diff --git a/simulator/e2e/README.md b/simulator/e2e/README.md new file mode 100644 index 0000000..b1d43d7 --- /dev/null +++ b/simulator/e2e/README.md @@ -0,0 +1,34 @@ +# Simulator E2E (Playwright) + +Browser end-to-end tests for the Human Experience Filter simulator. The ยง9 +pipeline requires an E2E browser tier for any UI surface; this is that tier. + +## What it covers + +`tests/altitude-lock.spec.ts` drives the real simulator and asserts the +altitude-morph behavior: + +- Zooming to a new altitude plays the morph that **matches the clip it lands on** + (forward zoom-in, and `.rev` zoom-out). +- After landing, the clip is **locked** โ it does not change while parked (no + secondary swap, no re-roll). + +The played morph paths are read from `window.__hefMorphs` (a diagnostic array +populated by `advance()` in `static/app.js`), which is reliable even when a morph +is served from the in-memory blob cache. The locked clip is read from the +Dev-Mode `#scale-name` readout. + +## Running + +Prerequisites: the project Python venv on PATH (the Playwright `webServer` boots +`uvicorn simulator.app:app` from the repo root), Node, and the Chromium browser. + +```bash +# from this directory (simulator/e2e) +npm install +npx playwright install chromium +npm test +``` + +`playwright.config.ts` starts uvicorn on port 8099 automatically (`reuseExistingServer` +locally) and points the tests at it. diff --git a/simulator/e2e/node_modules/.bin/playwright b/simulator/e2e/node_modules/.bin/playwright new file mode 120000 index 0000000..c30d07f --- /dev/null +++ b/simulator/e2e/node_modules/.bin/playwright @@ -0,0 +1 @@ +../@playwright/test/cli.js \ No newline at end of file diff --git a/simulator/e2e/node_modules/.bin/playwright-core b/simulator/e2e/node_modules/.bin/playwright-core new file mode 120000 index 0000000..08d6c28 --- /dev/null +++ b/simulator/e2e/node_modules/.bin/playwright-core @@ -0,0 +1 @@ +../playwright-core/cli.js \ No newline at end of file diff --git a/simulator/e2e/node_modules/.package-lock.json b/simulator/e2e/node_modules/.package-lock.json new file mode 100644 index 0000000..748e035 --- /dev/null +++ b/simulator/e2e/node_modules/.package-lock.json @@ -0,0 +1,55 @@ +{ + "name": "hef-e2e", + "lockfileVersion": 3, + "requires": true, + "packages": { + "node_modules/@playwright/test": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.61.1.tgz", + "integrity": "sha512-8nKv6+0RJSL9FE4jYOEGXnPeM/Hg12qZpmqzZjRh3qM0Y7c3z1mrOTfFLids72RDQYVh9WpLEfR5WdpNX4fkig==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.61.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.1.tgz", + "integrity": "sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.61.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.1.tgz", + "integrity": "sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + } + } +} diff --git a/simulator/e2e/node_modules/@playwright/test/LICENSE b/simulator/e2e/node_modules/@playwright/test/LICENSE new file mode 100644 index 0000000..df11237 --- /dev/null +++ b/simulator/e2e/node_modules/@playwright/test/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Portions Copyright (c) Microsoft Corporation. + Portions Copyright 2017 Google Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/simulator/e2e/node_modules/@playwright/test/NOTICE b/simulator/e2e/node_modules/@playwright/test/NOTICE new file mode 100644 index 0000000..814ec16 --- /dev/null +++ b/simulator/e2e/node_modules/@playwright/test/NOTICE @@ -0,0 +1,5 @@ +Playwright +Copyright (c) Microsoft Corporation + +This software contains code derived from the Puppeteer project (https://github.com/puppeteer/puppeteer), +available under the Apache 2.0 license (https://github.com/puppeteer/puppeteer/blob/master/LICENSE). diff --git a/simulator/e2e/node_modules/@playwright/test/README.md b/simulator/e2e/node_modules/@playwright/test/README.md new file mode 100644 index 0000000..d3abb8f --- /dev/null +++ b/simulator/e2e/node_modules/@playwright/test/README.md @@ -0,0 +1,318 @@ +# ๐ญ Playwright + +[](https://www.npmjs.com/package/playwright) [](https://www.chromium.org/Home) [](https://www.mozilla.org/en-US/firefox/new/) [](https://webkit.org/) [](https://aka.ms/playwright/discord) + +## [Documentation](https://playwright.dev) | [API reference](https://playwright.dev/docs/api/class-playwright) + +Playwright is a framework for web automation and testing. It drives Chromium, Firefox, and WebKit with a single API โ in your tests, in your scripts, and as a tool for AI agents. + +## Get Started + +Choose the path that fits your workflow: + +| | Best for | Install | +|---|---|---| +| **[Playwright Test](#playwright-test)** | End-to-end testing | `npm init playwright@latest` | +| **[Playwright CLI](#playwright-cli)** | Coding agents (Claude Code, Copilot) | `npm i -g @playwright/cli@latest` | +| **[Playwright MCP](#playwright-mcp)** | AI agents and LLM-driven automation | `npx @playwright/mcp@latest` | +| **[Playwright Library](#playwright-library)** | Browser automation scripts | `npm i playwright` | +| **[VS Code Extension](#vs-code-extension)** | Test authoring and debugging in VS Code | [Install from Marketplace](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright) | + +--- + +## Playwright Test + +Playwright Test is a full-featured test runner built for end-to-end testing. It runs tests across Chromium, Firefox, and WebKit with full browser isolation, auto-waiting, and web-first assertions. + +### Install + +```bash +npm init playwright@latest +``` + +Or add manually: + +```bash +npm i -D @playwright/test +npx playwright install +``` + +### Write a test + +```TypeScript +import { test, expect } from '@playwright/test'; + +test('has title', async ({ page }) => { + await page.goto('https://playwright.dev/'); + await expect(page).toHaveTitle(/Playwright/); +}); + +test('get started link', async ({ page }) => { + await page.goto('https://playwright.dev/'); + await page.getByRole('link', { name: 'Get started' }).click(); + await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible(); +}); +``` + +### Run tests + +```bash +npx playwright test +``` + +Tests run in parallel across all configured browsers, in headless mode by default. Each test gets a fresh browser context โ full isolation with near-zero overhead. + +### Key capabilities + +**Auto-wait and web-first assertions.** No artificial timeouts. Playwright waits for elements to be actionable, and assertions automatically retry until conditions are met. + +**Locators.** Find elements with resilient locators that mirror how users see the page: + +```TypeScript +page.getByRole('button', { name: 'Submit' }) +page.getByLabel('Email') +page.getByPlaceholder('Search...') +page.getByTestId('login-form') +``` + +**Test isolation.** Each test runs in its own browser context โ equivalent to a fresh browser profile. Save authentication state once and reuse it across tests: + +```TypeScript +// Save state after login +await page.context().storageState({ path: 'auth.json' }); + +// Reuse in other tests +test.use({ storageState: 'auth.json' }); +``` + +**Tracing.** Capture execution traces, screenshots, and videos on failure. Inspect every action, DOM snapshot, network request, and console message in the [Trace Viewer](https://playwright.dev/docs/trace-viewer): + +```TypeScript +// playwright.config.ts +export default defineConfig({ + use: { + trace: 'on-first-retry', + }, +}); +``` + +```bash +npx playwright show-trace trace.zip +``` + + + +**Parallelism.** Tests run in parallel by default across all configured browsers. + +[Full testing documentation](https://playwright.dev/docs/intro) + +--- + +## Playwright CLI + +[Playwright CLI](https://github.com/microsoft/playwright-cli) is a command-line interface for browser automation designed for coding agents. It's more token-efficient than MCP โ commands avoid loading large tool schemas and accessibility trees into the model context. + +### Install + +```bash +npm install -g @playwright/cli@latest +``` + +Optionally install skills for richer agent integration: + +```bash +playwright-cli install --skills +``` + +### Usage + +Point your coding agent at a task: + +``` +Test the "add todo" flow on https://demo.playwright.dev/todomvc using playwright-cli. +Take screenshots for all successful and failing scenarios. +``` + +Or run commands directly: + +```bash +playwright-cli open https://demo.playwright.dev/todomvc/ --headed +playwright-cli type "Buy groceries" +playwright-cli press Enter +playwright-cli screenshot +``` + +### Session monitoring + +Use `playwright-cli show` to open a visual dashboard with live screencast previews of all running browser sessions. Click any session to zoom in and take remote control. + +```bash +playwright-cli show +``` + + + +[Full CLI documentation](https://playwright.dev/agent-cli/introduction) | [GitHub](https://github.com/microsoft/playwright-cli) + +--- + +## Playwright MCP + +The [Playwright MCP server](https://github.com/microsoft/playwright-mcp) gives AI agents full browser control through the [Model Context Protocol](https://modelcontextprotocol.io). Agents interact with pages using structured accessibility snapshots โ no vision models or screenshots required. + +### Setup + +Add to your MCP client (VS Code, Cursor, Claude Desktop, Windsurf, etc.): + +```json +{ + "mcpServers": { + "playwright": { + "command": "npx", + "args": ["@playwright/mcp@latest"] + } + } +} +``` + +**One-click install for VS Code:** + +[](https://insiders.vscode.dev/redirect?url=vscode%3Amcp%2Finstall%3F%257B%2522name%2522%253A%2522playwright%2522%252C%2522command%2522%253A%2522npx%2522%252C%2522args%2522%253A%255B%2522%2540playwright%252Fmcp%2540latest%2522%255D%257D) + +**For Claude Code:** + +```bash +claude mcp add playwright npx @playwright/mcp@latest +``` + +### How it works + +Ask your AI assistant to interact with any web page: + +``` +Navigate to https://demo.playwright.dev/todomvc and add a few todo items. +``` + +The agent sees the page as a structured accessibility tree: + +``` +- heading "todos" [level=1] +- textbox "What needs to be done?" [ref=e5] +- listitem: + - checkbox "Toggle Todo" [ref=e10] + - text: "Buy groceries" +``` + +It uses element refs like `e5` and `e10` to click, type, and interact โ deterministically and without visual ambiguity. Tools cover navigation, form filling, screenshots, network mocking, storage management, and more. + +[Full MCP documentation](https://playwright.dev/mcp/introduction) | [GitHub](https://github.com/microsoft/playwright-mcp) + +--- + +## Playwright Library + +Use `playwright` as a library for browser automation scripts โ web scraping, PDF generation, screenshot capture, and any workflow that needs programmatic browser control without a test runner. + +### Install + +```bash +npm i playwright +``` + +### Examples + +**Take a screenshot:** + +```TypeScript +import { chromium } from 'playwright'; + +const browser = await chromium.launch(); +const page = await browser.newPage(); +await page.goto('https://playwright.dev/'); +await page.screenshot({ path: 'screenshot.png' }); +await browser.close(); +``` + +**Generate a PDF:** + +```TypeScript +import { chromium } from 'playwright'; + +const browser = await chromium.launch(); +const page = await browser.newPage(); +await page.goto('https://playwright.dev/'); +await page.pdf({ path: 'page.pdf', format: 'A4' }); +await browser.close(); +``` + +**Emulate a mobile device:** + +```TypeScript +import { chromium, devices } from 'playwright'; + +const browser = await chromium.launch(); +const context = await browser.newContext(devices['iPhone 15']); +const page = await context.newPage(); +await page.goto('https://playwright.dev/'); +await page.screenshot({ path: 'mobile.png' }); +await browser.close(); +``` + +**Intercept network requests:** + +```TypeScript +import { chromium } from 'playwright'; + +const browser = await chromium.launch(); +const page = await browser.newPage(); +await page.route('**/*.{png,jpg,jpeg}', route => route.abort()); +await page.goto('https://playwright.dev/'); +await browser.close(); +``` + +[Library documentation](https://playwright.dev/docs/library) | [API reference](https://playwright.dev/docs/api/class-playwright) + +--- + +## VS Code Extension + +The [Playwright VS Code extension](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright) brings test running, debugging, and code generation directly into your editor. + + + +**Run and debug tests** from the editor with a single click. Set breakpoints, inspect variables, and step through test execution with a live browser view. + +**Generate tests with CodeGen.** Click "Record new" to open a browser โ navigate and interact with your app while Playwright writes the test code for you. + +**Pick locators.** Hover over any element in the browser to see the best available locator, then click to copy it to your clipboard. + +**Trace Viewer integration.** Enable "Show Trace Viewer" in the sidebar to get a full execution trace after each test run โ DOM snapshots, network requests, console logs, and screenshots at every step. + +[Install the extension](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright) | [VS Code guide](https://playwright.dev/docs/getting-started-vscode) + +--- + +## Cross-Browser Support + +| | Linux | macOS | Windows | +| :--- | :---: | :---: | :---: | +| Chromium1 149.0.7827.55 | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| WebKit 26.5 | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| Firefox 151.0 | :white_check_mark: | :white_check_mark: | :white_check_mark: | + +Headless and headed execution on all platforms. 1 Uses [Chrome for Testing](https://developer.chrome.com/blog/chrome-for-testing) by default. + +## Other Languages + +Playwright is also available for [Python](https://playwright.dev/python/docs/intro), [.NET](https://playwright.dev/dotnet/docs/intro), and [Java](https://playwright.dev/java/docs/intro). + +## Resources + +* [Documentation](https://playwright.dev) +* [API reference](https://playwright.dev/docs/api/class-playwright) +* [MCP server](https://github.com/microsoft/playwright-mcp) +* [CLI for coding agents](https://github.com/microsoft/playwright-cli) +* [VS Code extension](https://github.com/microsoft/playwright-vscode) +* [Contribution guide](CONTRIBUTING.md) +* [Changelog](https://github.com/microsoft/playwright/releases) +* [Discord](https://aka.ms/playwright/discord) diff --git a/simulator/e2e/node_modules/@playwright/test/cli.js b/simulator/e2e/node_modules/@playwright/test/cli.js new file mode 100755 index 0000000..e42facb --- /dev/null +++ b/simulator/e2e/node_modules/@playwright/test/cli.js @@ -0,0 +1,19 @@ +#!/usr/bin/env node +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const { program } = require('playwright/lib/program'); +program.parse(process.argv); diff --git a/simulator/e2e/node_modules/@playwright/test/index.d.ts b/simulator/e2e/node_modules/@playwright/test/index.d.ts new file mode 100644 index 0000000..8d99c91 --- /dev/null +++ b/simulator/e2e/node_modules/@playwright/test/index.d.ts @@ -0,0 +1,18 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from 'playwright/test'; +export { default } from 'playwright/test'; diff --git a/simulator/e2e/node_modules/@playwright/test/index.js b/simulator/e2e/node_modules/@playwright/test/index.js new file mode 100644 index 0000000..8536f06 --- /dev/null +++ b/simulator/e2e/node_modules/@playwright/test/index.js @@ -0,0 +1,17 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module.exports = require('playwright/test'); diff --git a/simulator/e2e/node_modules/@playwright/test/index.mjs b/simulator/e2e/node_modules/@playwright/test/index.mjs new file mode 100644 index 0000000..8d99c91 --- /dev/null +++ b/simulator/e2e/node_modules/@playwright/test/index.mjs @@ -0,0 +1,18 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from 'playwright/test'; +export { default } from 'playwright/test'; diff --git a/simulator/e2e/node_modules/@playwright/test/package.json b/simulator/e2e/node_modules/@playwright/test/package.json new file mode 100644 index 0000000..385f2ef --- /dev/null +++ b/simulator/e2e/node_modules/@playwright/test/package.json @@ -0,0 +1,35 @@ +{ + "name": "@playwright/test", + "version": "1.61.1", + "description": "A high-level API to automate web browsers", + "repository": { + "type": "git", + "url": "git+https://github.com/microsoft/playwright.git" + }, + "homepage": "https://playwright.dev", + "engines": { + "node": ">=18" + }, + "author": { + "name": "Microsoft Corporation" + }, + "license": "Apache-2.0", + "exports": { + ".": { + "types": "./index.d.ts", + "import": "./index.mjs", + "require": "./index.js", + "default": "./index.js" + }, + "./cli": "./cli.js", + "./package.json": "./package.json", + "./reporter": "./reporter.js" + }, + "bin": { + "playwright": "cli.js" + }, + "scripts": {}, + "dependencies": { + "playwright": "1.61.1" + } +} diff --git a/simulator/e2e/node_modules/@playwright/test/reporter.d.ts b/simulator/e2e/node_modules/@playwright/test/reporter.d.ts new file mode 100644 index 0000000..806d13f --- /dev/null +++ b/simulator/e2e/node_modules/@playwright/test/reporter.d.ts @@ -0,0 +1,17 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from 'playwright/types/testReporter'; diff --git a/simulator/e2e/node_modules/@playwright/test/reporter.js b/simulator/e2e/node_modules/@playwright/test/reporter.js new file mode 100644 index 0000000..485e880 --- /dev/null +++ b/simulator/e2e/node_modules/@playwright/test/reporter.js @@ -0,0 +1,17 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// We only export types in reporter.d.ts. diff --git a/simulator/e2e/node_modules/@playwright/test/reporter.mjs b/simulator/e2e/node_modules/@playwright/test/reporter.mjs new file mode 100644 index 0000000..485e880 --- /dev/null +++ b/simulator/e2e/node_modules/@playwright/test/reporter.mjs @@ -0,0 +1,17 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// We only export types in reporter.d.ts. diff --git a/simulator/e2e/node_modules/playwright-core/LICENSE b/simulator/e2e/node_modules/playwright-core/LICENSE new file mode 100644 index 0000000..df11237 --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Portions Copyright (c) Microsoft Corporation. + Portions Copyright 2017 Google Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/simulator/e2e/node_modules/playwright-core/NOTICE b/simulator/e2e/node_modules/playwright-core/NOTICE new file mode 100644 index 0000000..814ec16 --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/NOTICE @@ -0,0 +1,5 @@ +Playwright +Copyright (c) Microsoft Corporation + +This software contains code derived from the Puppeteer project (https://github.com/puppeteer/puppeteer), +available under the Apache 2.0 license (https://github.com/puppeteer/puppeteer/blob/master/LICENSE). diff --git a/simulator/e2e/node_modules/playwright-core/README.md b/simulator/e2e/node_modules/playwright-core/README.md new file mode 100644 index 0000000..422b373 --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/README.md @@ -0,0 +1,3 @@ +# playwright-core + +This package contains the no-browser flavor of [Playwright](http://github.com/microsoft/playwright). diff --git a/simulator/e2e/node_modules/playwright-core/ThirdPartyNotices.txt b/simulator/e2e/node_modules/playwright-core/ThirdPartyNotices.txt new file mode 100644 index 0000000..369e202 --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/ThirdPartyNotices.txt @@ -0,0 +1,13 @@ +microsoft/playwright-core + +THIRD-PARTY SOFTWARE NOTICES AND INFORMATION + +This package bundles third-party software inside individual files under +`lib/`. Each bundled output has a sidecar `.js.LICENSE` file next +to it listing every npm package whose source was inlined into that +bundle, together with the full license text for each. + +For example: +- lib/utilsBundle.js.LICENSE + +This project incorporates components from the projects listed below. The original copyright notices and the licenses under which Microsoft received such components are set forth below. Microsoft reserves all rights not expressly granted herein, whether by implication, estoppel or otherwise. diff --git a/simulator/e2e/node_modules/playwright-core/bin/install_media_pack.ps1 b/simulator/e2e/node_modules/playwright-core/bin/install_media_pack.ps1 new file mode 100644 index 0000000..6170754 --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/bin/install_media_pack.ps1 @@ -0,0 +1,5 @@ +$osInfo = Get-WmiObject -Class Win32_OperatingSystem +# check if running on Windows Server +if ($osInfo.ProductType -eq 3) { + Install-WindowsFeature Server-Media-Foundation +} diff --git a/simulator/e2e/node_modules/playwright-core/bin/install_webkit_wsl.ps1 b/simulator/e2e/node_modules/playwright-core/bin/install_webkit_wsl.ps1 new file mode 100644 index 0000000..ccaaf15 --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/bin/install_webkit_wsl.ps1 @@ -0,0 +1,33 @@ +$ErrorActionPreference = 'Stop' + +# This script sets up a WSL distribution that will be used to run WebKit. + +$Distribution = "playwright" +$Username = "pwuser" + +$distributions = (wsl --list --quiet) -split "\r?\n" +if ($distributions -contains $Distribution) { + Write-Host "WSL distribution '$Distribution' already exists. Skipping installation." +} else { + Write-Host "Installing new WSL distribution '$Distribution'..." + $VhdSize = "10GB" + wsl --install -d Ubuntu-24.04 --name $Distribution --no-launch --vhd-size $VhdSize + wsl -d $Distribution -u root adduser --gecos GECOS --disabled-password $Username +} + +$pwshDirname = (Resolve-Path -Path $PSScriptRoot).Path; +$playwrightCoreRoot = Resolve-Path (Join-Path $pwshDirname "..") + +$initScript = @" +if [ ! -f "/home/$Username/node/bin/node" ]; then + mkdir -p /home/$Username/node + curl -fsSL https://nodejs.org/dist/v22.17.0/node-v22.17.0-linux-x64.tar.xz -o /home/$Username/node/node-v22.17.0-linux-x64.tar.xz + tar -xJf /home/$Username/node/node-v22.17.0-linux-x64.tar.xz -C /home/$Username/node --strip-components=1 + sudo -u $Username echo 'export PATH=/home/$Username/node/bin:\`$PATH' >> /home/$Username/.profile +fi +/home/$Username/node/bin/node cli.js install-deps webkit +sudo -u $Username PLAYWRIGHT_SKIP_BROWSER_GC=1 /home/$Username/node/bin/node cli.js install webkit +"@ -replace "\r\n", "`n" + +wsl -d $Distribution --cd $playwrightCoreRoot -u root -- bash -c "$initScript" +Write-Host "Done!" \ No newline at end of file diff --git a/simulator/e2e/node_modules/playwright-core/bin/reinstall_chrome_beta_linux.sh b/simulator/e2e/node_modules/playwright-core/bin/reinstall_chrome_beta_linux.sh new file mode 100755 index 0000000..13d55df --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/bin/reinstall_chrome_beta_linux.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +set -e +set -x + +if [[ $(arch) == "aarch64" ]]; then + echo "ERROR: not supported on Linux Arm64" + exit 1 +fi + +if [ -z "$PLAYWRIGHT_HOST_PLATFORM_OVERRIDE" ]; then + if [[ ! -f "/etc/os-release" ]]; then + echo "ERROR: cannot install on unknown linux distribution (/etc/os-release is missing)" + exit 1 + fi + + ID=$(bash -c 'source /etc/os-release && echo $ID') + if [[ "${ID}" != "ubuntu" && "${ID}" != "debian" ]]; then + echo "ERROR: cannot install on $ID distribution - only Ubuntu and Debian are supported" + exit 1 + fi +fi + +# 1. make sure to remove old beta if any. +if dpkg --get-selections | grep -q "^google-chrome-beta[[:space:]]*install$" >/dev/null; then + apt-get remove -y google-chrome-beta +fi + +# 2. Update apt lists (needed to install curl and chrome dependencies) +apt-get update + +# 3. Install curl to download chrome +if ! command -v curl >/dev/null; then + apt-get install -y curl +fi + +# 4. download chrome beta from dl.google.com and install it. +cd /tmp +curl -L -O https://dl.google.com/linux/direct/google-chrome-beta_current_amd64.deb +apt-get install -y ./google-chrome-beta_current_amd64.deb +rm -rf ./google-chrome-beta_current_amd64.deb +cd - +google-chrome-beta --version diff --git a/simulator/e2e/node_modules/playwright-core/bin/reinstall_chrome_beta_mac.sh b/simulator/e2e/node_modules/playwright-core/bin/reinstall_chrome_beta_mac.sh new file mode 100755 index 0000000..60b4b7f --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/bin/reinstall_chrome_beta_mac.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -e +set -x + +rm -rf "/Applications/Google Chrome Beta.app" +cd /tmp +curl -L --retry 3 -o ./googlechromebeta.dmg https://dl.google.com/chrome/mac/universal/beta/googlechromebeta.dmg +hdiutil attach -nobrowse -quiet -noautofsck -noautoopen -mountpoint /Volumes/googlechromebeta.dmg ./googlechromebeta.dmg +cp -pR "/Volumes/googlechromebeta.dmg/Google Chrome Beta.app" /Applications +hdiutil detach /Volumes/googlechromebeta.dmg +rm -rf /tmp/googlechromebeta.dmg + +/Applications/Google\ Chrome\ Beta.app/Contents/MacOS/Google\ Chrome\ Beta --version diff --git a/simulator/e2e/node_modules/playwright-core/bin/reinstall_chrome_beta_win.ps1 b/simulator/e2e/node_modules/playwright-core/bin/reinstall_chrome_beta_win.ps1 new file mode 100644 index 0000000..3fbe551 --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/bin/reinstall_chrome_beta_win.ps1 @@ -0,0 +1,24 @@ +$ErrorActionPreference = 'Stop' + +$url = 'https://dl.google.com/tag/s/dl/chrome/install/beta/googlechromebetastandaloneenterprise64.msi' + +Write-Host "Downloading Google Chrome Beta" +$wc = New-Object net.webclient +$msiInstaller = "$env:temp\google-chrome-beta.msi" +$wc.Downloadfile($url, $msiInstaller) + +Write-Host "Installing Google Chrome Beta" +$arguments = "/i `"$msiInstaller`" /quiet" +Start-Process msiexec.exe -ArgumentList $arguments -Wait +Remove-Item $msiInstaller + +$suffix = "\\Google\\Chrome Beta\\Application\\chrome.exe" +if (Test-Path "${env:ProgramFiles(x86)}$suffix") { + (Get-Item "${env:ProgramFiles(x86)}$suffix").VersionInfo +} elseif (Test-Path "${env:ProgramFiles}$suffix") { + (Get-Item "${env:ProgramFiles}$suffix").VersionInfo +} else { + Write-Host "ERROR: Failed to install Google Chrome Beta." + Write-Host "ERROR: This could be due to insufficient privileges, in which case re-running as Administrator may help." + exit 1 +} diff --git a/simulator/e2e/node_modules/playwright-core/bin/reinstall_chrome_stable_linux.sh b/simulator/e2e/node_modules/playwright-core/bin/reinstall_chrome_stable_linux.sh new file mode 100755 index 0000000..64bc139 --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/bin/reinstall_chrome_stable_linux.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +set -e +set -x + +if [[ $(arch) == "aarch64" ]]; then + echo "ERROR: not supported on Linux Arm64" + exit 1 +fi + +if [ -z "$PLAYWRIGHT_HOST_PLATFORM_OVERRIDE" ]; then + if [[ ! -f "/etc/os-release" ]]; then + echo "ERROR: cannot install on unknown linux distribution (/etc/os-release is missing)" + exit 1 + fi + + ID=$(bash -c 'source /etc/os-release && echo $ID') + if [[ "${ID}" != "ubuntu" && "${ID}" != "debian" ]]; then + echo "ERROR: cannot install on $ID distribution - only Ubuntu and Debian are supported" + exit 1 + fi +fi + +# 1. make sure to remove old stable if any. +if dpkg --get-selections | grep -q "^google-chrome[[:space:]]*install$" >/dev/null; then + apt-get remove -y google-chrome +fi + +# 2. Update apt lists (needed to install curl and chrome dependencies) +apt-get update + +# 3. Install curl to download chrome +if ! command -v curl >/dev/null; then + apt-get install -y curl +fi + +# 4. download chrome stable from dl.google.com and install it. +cd /tmp +curl -L -O https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb +apt-get install -y ./google-chrome-stable_current_amd64.deb +rm -rf ./google-chrome-stable_current_amd64.deb +cd - +google-chrome --version diff --git a/simulator/e2e/node_modules/playwright-core/bin/reinstall_chrome_stable_mac.sh b/simulator/e2e/node_modules/playwright-core/bin/reinstall_chrome_stable_mac.sh new file mode 100755 index 0000000..7db7a0f --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/bin/reinstall_chrome_stable_mac.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -e +set -x + +rm -rf "/Applications/Google Chrome.app" +cd /tmp +curl -L --retry 3 -o ./googlechrome.dmg https://dl.google.com/chrome/mac/universal/stable/GGRO/googlechrome.dmg +hdiutil attach -nobrowse -quiet -noautofsck -noautoopen -mountpoint /Volumes/googlechrome.dmg ./googlechrome.dmg +cp -pR "/Volumes/googlechrome.dmg/Google Chrome.app" /Applications +hdiutil detach /Volumes/googlechrome.dmg +rm -rf /tmp/googlechrome.dmg +/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --version diff --git a/simulator/e2e/node_modules/playwright-core/bin/reinstall_chrome_stable_win.ps1 b/simulator/e2e/node_modules/playwright-core/bin/reinstall_chrome_stable_win.ps1 new file mode 100644 index 0000000..7ca2dba --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/bin/reinstall_chrome_stable_win.ps1 @@ -0,0 +1,24 @@ +$ErrorActionPreference = 'Stop' +$url = 'https://dl.google.com/tag/s/dl/chrome/install/googlechromestandaloneenterprise64.msi' + +$wc = New-Object net.webclient +$msiInstaller = "$env:temp\google-chrome.msi" +Write-Host "Downloading Google Chrome" +$wc.Downloadfile($url, $msiInstaller) + +Write-Host "Installing Google Chrome" +$arguments = "/i `"$msiInstaller`" /quiet" +Start-Process msiexec.exe -ArgumentList $arguments -Wait +Remove-Item $msiInstaller + + +$suffix = "\\Google\\Chrome\\Application\\chrome.exe" +if (Test-Path "${env:ProgramFiles(x86)}$suffix") { + (Get-Item "${env:ProgramFiles(x86)}$suffix").VersionInfo +} elseif (Test-Path "${env:ProgramFiles}$suffix") { + (Get-Item "${env:ProgramFiles}$suffix").VersionInfo +} else { + Write-Host "ERROR: Failed to install Google Chrome." + Write-Host "ERROR: This could be due to insufficient privileges, in which case re-running as Administrator may help." + exit 1 +} diff --git a/simulator/e2e/node_modules/playwright-core/bin/reinstall_msedge_beta_linux.sh b/simulator/e2e/node_modules/playwright-core/bin/reinstall_msedge_beta_linux.sh new file mode 100755 index 0000000..e1f156a --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/bin/reinstall_msedge_beta_linux.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +set -e +set -x + +if [[ $(arch) == "aarch64" ]]; then + echo "ERROR: not supported on Linux Arm64" + exit 1 +fi + +if [ -z "$PLAYWRIGHT_HOST_PLATFORM_OVERRIDE" ]; then + if [[ ! -f "/etc/os-release" ]]; then + echo "ERROR: cannot install on unknown linux distribution (/etc/os-release is missing)" + exit 1 + fi + + ID=$(bash -c 'source /etc/os-release && echo $ID') + if [[ "${ID}" != "ubuntu" && "${ID}" != "debian" ]]; then + echo "ERROR: cannot install on $ID distribution - only Ubuntu and Debian are supported" + exit 1 + fi +fi + +# 1. make sure to remove old beta if any. +if dpkg --get-selections | grep -q "^microsoft-edge-beta[[:space:]]*install$" >/dev/null; then + apt-get remove -y microsoft-edge-beta +fi + +# 2. Install curl to download Microsoft gpg key +if ! command -v curl >/dev/null; then + apt-get update + apt-get install -y curl +fi + +# GnuPG is not preinstalled in slim images +if ! command -v gpg >/dev/null; then + apt-get update + apt-get install -y gpg +fi + +# 3. Add the GPG key, the apt repo, update the apt cache, and install the package +curl -L https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > /tmp/microsoft.gpg +install -o root -g root -m 644 /tmp/microsoft.gpg /etc/apt/trusted.gpg.d/ +sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/edge stable main" > /etc/apt/sources.list.d/microsoft-edge-dev.list' +rm /tmp/microsoft.gpg +apt-get update && apt-get install -y microsoft-edge-beta + +microsoft-edge-beta --version diff --git a/simulator/e2e/node_modules/playwright-core/bin/reinstall_msedge_beta_mac.sh b/simulator/e2e/node_modules/playwright-core/bin/reinstall_msedge_beta_mac.sh new file mode 100755 index 0000000..528a0e7 --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/bin/reinstall_msedge_beta_mac.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -e +set -x + +cd /tmp +curl -L --retry 3 -o ./msedge_beta.pkg "$1" +# Note: there's no way to uninstall previously installed MSEdge. +# However, running PKG again seems to update installation. +sudo installer -pkg /tmp/msedge_beta.pkg -target / +rm -rf /tmp/msedge_beta.pkg +/Applications/Microsoft\ Edge\ Beta.app/Contents/MacOS/Microsoft\ Edge\ Beta --version diff --git a/simulator/e2e/node_modules/playwright-core/bin/reinstall_msedge_beta_win.ps1 b/simulator/e2e/node_modules/playwright-core/bin/reinstall_msedge_beta_win.ps1 new file mode 100644 index 0000000..cce0d0b --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/bin/reinstall_msedge_beta_win.ps1 @@ -0,0 +1,23 @@ +$ErrorActionPreference = 'Stop' +$url = $args[0] + +Write-Host "Downloading Microsoft Edge Beta" +$wc = New-Object net.webclient +$msiInstaller = "$env:temp\microsoft-edge-beta.msi" +$wc.Downloadfile($url, $msiInstaller) + +Write-Host "Installing Microsoft Edge Beta" +$arguments = "/i `"$msiInstaller`" /quiet" +Start-Process msiexec.exe -ArgumentList $arguments -Wait +Remove-Item $msiInstaller + +$suffix = "\\Microsoft\\Edge Beta\\Application\\msedge.exe" +if (Test-Path "${env:ProgramFiles(x86)}$suffix") { + (Get-Item "${env:ProgramFiles(x86)}$suffix").VersionInfo +} elseif (Test-Path "${env:ProgramFiles}$suffix") { + (Get-Item "${env:ProgramFiles}$suffix").VersionInfo +} else { + Write-Host "ERROR: Failed to install Microsoft Edge Beta." + Write-Host "ERROR: This could be due to insufficient privileges, in which case re-running as Administrator may help." + exit 1 +} diff --git a/simulator/e2e/node_modules/playwright-core/bin/reinstall_msedge_dev_linux.sh b/simulator/e2e/node_modules/playwright-core/bin/reinstall_msedge_dev_linux.sh new file mode 100755 index 0000000..6c1a151 --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/bin/reinstall_msedge_dev_linux.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +set -e +set -x + +if [[ $(arch) == "aarch64" ]]; then + echo "ERROR: not supported on Linux Arm64" + exit 1 +fi + +if [ -z "$PLAYWRIGHT_HOST_PLATFORM_OVERRIDE" ]; then + if [[ ! -f "/etc/os-release" ]]; then + echo "ERROR: cannot install on unknown linux distribution (/etc/os-release is missing)" + exit 1 + fi + + ID=$(bash -c 'source /etc/os-release && echo $ID') + if [[ "${ID}" != "ubuntu" && "${ID}" != "debian" ]]; then + echo "ERROR: cannot install on $ID distribution - only Ubuntu and Debian are supported" + exit 1 + fi +fi + +# 1. make sure to remove old dev if any. +if dpkg --get-selections | grep -q "^microsoft-edge-dev[[:space:]]*install$" >/dev/null; then + apt-get remove -y microsoft-edge-dev +fi + +# 2. Install curl to download Microsoft gpg key +if ! command -v curl >/dev/null; then + apt-get update + apt-get install -y curl +fi + +# GnuPG is not preinstalled in slim images +if ! command -v gpg >/dev/null; then + apt-get update + apt-get install -y gpg +fi + +# 3. Add the GPG key, the apt repo, update the apt cache, and install the package +curl -L https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > /tmp/microsoft.gpg +install -o root -g root -m 644 /tmp/microsoft.gpg /etc/apt/trusted.gpg.d/ +sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/edge stable main" > /etc/apt/sources.list.d/microsoft-edge-dev.list' +rm /tmp/microsoft.gpg +apt-get update && apt-get install -y microsoft-edge-dev + +microsoft-edge-dev --version diff --git a/simulator/e2e/node_modules/playwright-core/bin/reinstall_msedge_dev_mac.sh b/simulator/e2e/node_modules/playwright-core/bin/reinstall_msedge_dev_mac.sh new file mode 100755 index 0000000..e35ea4e --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/bin/reinstall_msedge_dev_mac.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -e +set -x + +cd /tmp +curl -L --retry 3 -o ./msedge_dev.pkg "$1" +# Note: there's no way to uninstall previously installed MSEdge. +# However, running PKG again seems to update installation. +sudo installer -pkg /tmp/msedge_dev.pkg -target / +rm -rf /tmp/msedge_dev.pkg +/Applications/Microsoft\ Edge\ Dev.app/Contents/MacOS/Microsoft\ Edge\ Dev --version diff --git a/simulator/e2e/node_modules/playwright-core/bin/reinstall_msedge_dev_win.ps1 b/simulator/e2e/node_modules/playwright-core/bin/reinstall_msedge_dev_win.ps1 new file mode 100644 index 0000000..22e6db8 --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/bin/reinstall_msedge_dev_win.ps1 @@ -0,0 +1,23 @@ +$ErrorActionPreference = 'Stop' +$url = $args[0] + +Write-Host "Downloading Microsoft Edge Dev" +$wc = New-Object net.webclient +$msiInstaller = "$env:temp\microsoft-edge-dev.msi" +$wc.Downloadfile($url, $msiInstaller) + +Write-Host "Installing Microsoft Edge Dev" +$arguments = "/i `"$msiInstaller`" /quiet" +Start-Process msiexec.exe -ArgumentList $arguments -Wait +Remove-Item $msiInstaller + +$suffix = "\\Microsoft\\Edge Dev\\Application\\msedge.exe" +if (Test-Path "${env:ProgramFiles(x86)}$suffix") { + (Get-Item "${env:ProgramFiles(x86)}$suffix").VersionInfo +} elseif (Test-Path "${env:ProgramFiles}$suffix") { + (Get-Item "${env:ProgramFiles}$suffix").VersionInfo +} else { + Write-Host "ERROR: Failed to install Microsoft Edge Dev." + Write-Host "ERROR: This could be due to insufficient privileges, in which case re-running as Administrator may help." + exit 1 +} diff --git a/simulator/e2e/node_modules/playwright-core/bin/reinstall_msedge_stable_linux.sh b/simulator/e2e/node_modules/playwright-core/bin/reinstall_msedge_stable_linux.sh new file mode 100755 index 0000000..93bd766 --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/bin/reinstall_msedge_stable_linux.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +set -e +set -x + +if [[ $(arch) == "aarch64" ]]; then + echo "ERROR: not supported on Linux Arm64" + exit 1 +fi + +if [ -z "$PLAYWRIGHT_HOST_PLATFORM_OVERRIDE" ]; then + if [[ ! -f "/etc/os-release" ]]; then + echo "ERROR: cannot install on unknown linux distribution (/etc/os-release is missing)" + exit 1 + fi + + ID=$(bash -c 'source /etc/os-release && echo $ID') + if [[ "${ID}" != "ubuntu" && "${ID}" != "debian" ]]; then + echo "ERROR: cannot install on $ID distribution - only Ubuntu and Debian are supported" + exit 1 + fi +fi + +# 1. make sure to remove old stable if any. +if dpkg --get-selections | grep -q "^microsoft-edge-stable[[:space:]]*install$" >/dev/null; then + apt-get remove -y microsoft-edge-stable +fi + +# 2. Install curl to download Microsoft gpg key +if ! command -v curl >/dev/null; then + apt-get update + apt-get install -y curl +fi + +# GnuPG is not preinstalled in slim images +if ! command -v gpg >/dev/null; then + apt-get update + apt-get install -y gpg +fi + +# 3. Add the GPG key, the apt repo, update the apt cache, and install the package +curl -L https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > /tmp/microsoft.gpg +install -o root -g root -m 644 /tmp/microsoft.gpg /etc/apt/trusted.gpg.d/ +sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/edge stable main" > /etc/apt/sources.list.d/microsoft-edge-stable.list' +rm /tmp/microsoft.gpg +apt-get update && apt-get install -y microsoft-edge-stable + +microsoft-edge-stable --version diff --git a/simulator/e2e/node_modules/playwright-core/bin/reinstall_msedge_stable_mac.sh b/simulator/e2e/node_modules/playwright-core/bin/reinstall_msedge_stable_mac.sh new file mode 100755 index 0000000..44e5a56 --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/bin/reinstall_msedge_stable_mac.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -e +set -x + +cd /tmp +curl -L --retry 3 -o ./msedge_stable.pkg "$1" +# Note: there's no way to uninstall previously installed MSEdge. +# However, running PKG again seems to update installation. +sudo installer -pkg /tmp/msedge_stable.pkg -target / +rm -rf /tmp/msedge_stable.pkg +/Applications/Microsoft\ Edge.app/Contents/MacOS/Microsoft\ Edge --version diff --git a/simulator/e2e/node_modules/playwright-core/bin/reinstall_msedge_stable_win.ps1 b/simulator/e2e/node_modules/playwright-core/bin/reinstall_msedge_stable_win.ps1 new file mode 100644 index 0000000..31fdf51 --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/bin/reinstall_msedge_stable_win.ps1 @@ -0,0 +1,24 @@ +$ErrorActionPreference = 'Stop' + +$url = $args[0] + +Write-Host "Downloading Microsoft Edge" +$wc = New-Object net.webclient +$msiInstaller = "$env:temp\microsoft-edge-stable.msi" +$wc.Downloadfile($url, $msiInstaller) + +Write-Host "Installing Microsoft Edge" +$arguments = "/i `"$msiInstaller`" /quiet" +Start-Process msiexec.exe -ArgumentList $arguments -Wait +Remove-Item $msiInstaller + +$suffix = "\\Microsoft\\Edge\\Application\\msedge.exe" +if (Test-Path "${env:ProgramFiles(x86)}$suffix") { + (Get-Item "${env:ProgramFiles(x86)}$suffix").VersionInfo +} elseif (Test-Path "${env:ProgramFiles}$suffix") { + (Get-Item "${env:ProgramFiles}$suffix").VersionInfo +} else { + Write-Host "ERROR: Failed to install Microsoft Edge." + Write-Host "ERROR: This could be due to insufficient privileges, in which case re-running as Administrator may help." + exit 1 +} \ No newline at end of file diff --git a/simulator/e2e/node_modules/playwright-core/browsers.json b/simulator/e2e/node_modules/playwright-core/browsers.json new file mode 100644 index 0000000..a0651c7 --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/browsers.json @@ -0,0 +1,81 @@ +{ + "comment": "Do not edit this file, use utils/roll_browser.js", + "browsers": [ + { + "name": "chromium", + "revision": "1228", + "installByDefault": true, + "browserVersion": "149.0.7827.55", + "title": "Chrome for Testing" + }, + { + "name": "chromium-headless-shell", + "revision": "1228", + "installByDefault": true, + "browserVersion": "149.0.7827.55", + "title": "Chrome Headless Shell" + }, + { + "name": "chromium-tip-of-tree", + "revision": "1432", + "installByDefault": false, + "browserVersion": "151.0.7886.0", + "title": "Chrome Canary for Testing" + }, + { + "name": "chromium-tip-of-tree-headless-shell", + "revision": "1432", + "installByDefault": false, + "browserVersion": "151.0.7886.0", + "title": "Chrome Canary Headless Shell" + }, + { + "name": "firefox", + "revision": "1532", + "installByDefault": true, + "browserVersion": "151.0", + "title": "Firefox" + }, + { + "name": "firefox-beta", + "revision": "1526", + "installByDefault": false, + "browserVersion": "152.0b1", + "title": "Firefox Beta" + }, + { + "name": "webkit", + "revision": "2311", + "installByDefault": true, + "revisionOverrides": { + "mac14": "2251", + "mac14-arm64": "2251", + "debian11-x64": "2105", + "debian11-arm64": "2105", + "ubuntu20.04-x64": "2092", + "ubuntu20.04-arm64": "2092" + }, + "browserVersion": "26.5", + "title": "WebKit" + }, + { + "name": "ffmpeg", + "revision": "1011", + "installByDefault": true, + "revisionOverrides": { + "mac12": "1010", + "mac12-arm64": "1010" + } + }, + { + "name": "winldd", + "revision": "1007", + "installByDefault": false + }, + { + "name": "android", + "revision": "1001", + "installByDefault": false + } + ] +} diff --git a/simulator/e2e/node_modules/playwright-core/cli.js b/simulator/e2e/node_modules/playwright-core/cli.js new file mode 100755 index 0000000..f275a03 --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/cli.js @@ -0,0 +1,21 @@ +#!/usr/bin/env node +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const { libCli, libCliTestStub } = require('./lib/coreBundle'); +const { program } = require('./lib/utilsBundle'); +libCli.decorateProgram(program); +libCliTestStub.decorateProgram(program); +program.parse(process.argv); diff --git a/simulator/e2e/node_modules/playwright-core/index.d.ts b/simulator/e2e/node_modules/playwright-core/index.d.ts new file mode 100644 index 0000000..97c1493 --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/index.d.ts @@ -0,0 +1,17 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './types/types'; diff --git a/simulator/e2e/node_modules/playwright-core/index.js b/simulator/e2e/node_modules/playwright-core/index.js new file mode 100644 index 0000000..9fc51a4 --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/index.js @@ -0,0 +1,17 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +require('./lib/bootstrap'); +module.exports = require('./lib/coreBundle').inprocess.playwright; diff --git a/simulator/e2e/node_modules/playwright-core/index.mjs b/simulator/e2e/node_modules/playwright-core/index.mjs new file mode 100644 index 0000000..3b3c75b --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/index.mjs @@ -0,0 +1,28 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import playwright from './index.js'; + +export const chromium = playwright.chromium; +export const firefox = playwright.firefox; +export const webkit = playwright.webkit; +export const selectors = playwright.selectors; +export const devices = playwright.devices; +export const errors = playwright.errors; +export const request = playwright.request; +export const _electron = playwright._electron; +export const _android = playwright._android; +export default playwright; diff --git a/simulator/e2e/node_modules/playwright-core/lib/bootstrap.js b/simulator/e2e/node_modules/playwright-core/lib/bootstrap.js new file mode 100644 index 0000000..9825a9a --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/lib/bootstrap.js @@ -0,0 +1,88 @@ +"use strict"; +const minimumMajorNodeVersion = 18; +const currentNodeVersion = process.versions.node; +const major = +currentNodeVersion.split(".")[0]; +if (major < minimumMajorNodeVersion) { + console.error( + "You are running Node.js " + currentNodeVersion + `. +Playwright requires Node.js ${minimumMajorNodeVersion} or higher. +Please update your version of Node.js.` + ); + process.exit(1); +} +if (process.env.PW_INSTRUMENT_MODULES) { + const Module = require("module"); + const originalLoad = Module._load; + const root = { name: "", selfMs: 0, totalMs: 0, childrenMs: 0, children: [] }; + let current = root; + const stack = []; + Module._load = function(request, _parent, _isMain) { + const node = { name: request, selfMs: 0, totalMs: 0, childrenMs: 0, children: [] }; + current.children.push(node); + stack.push(current); + current = node; + const start = performance.now(); + let result; + try { + result = originalLoad.apply(this, arguments); + } catch (e) { + current = stack.pop(); + current.children.pop(); + throw e; + } + const duration = performance.now() - start; + node.totalMs = duration; + node.selfMs = Math.max(0, duration - node.childrenMs); + current = stack.pop(); + current.childrenMs += duration; + return result; + }; + process.on("exit", () => { + function printTree(node, prefix, isLast, lines2, depth) { + if (node.totalMs < 1 && depth > 0) + return; + const connector = depth === 0 ? "" : isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 "; + const time = `${node.totalMs.toFixed(1).padStart(8)}ms`; + const self = node.children.length ? ` (self: ${node.selfMs.toFixed(1)}ms)` : ""; + lines2.push(`${time} ${prefix}${connector}${node.name}${self}`); + const childPrefix = prefix + (depth === 0 ? "" : isLast ? " " : "\u2502 "); + const sorted2 = node.children.slice().sort((a, b) => b.totalMs - a.totalMs); + for (let i = 0; i < sorted2.length; i++) + printTree(sorted2[i], childPrefix, i === sorted2.length - 1, lines2, depth + 1); + } + let totalModules = 0; + function count(n) { + totalModules++; + n.children.forEach(count); + } + root.children.forEach(count); + const lines = []; + const sorted = root.children.slice().sort((a, b) => b.totalMs - a.totalMs); + for (let i = 0; i < sorted.length; i++) + printTree(sorted[i], "", i === sorted.length - 1, lines, 0); + const totalMs = root.children.reduce((s, c) => s + c.totalMs, 0); + process.stderr.write(` +--- Module load tree: ${totalModules} modules, ${totalMs.toFixed(0)}ms total --- +` + lines.join("\n") + "\n"); + const flat = /* @__PURE__ */ new Map(); + function gather(n) { + const existing = flat.get(n.name); + if (existing) { + existing.selfMs += n.selfMs; + existing.totalMs += n.totalMs; + existing.count++; + } else { + flat.set(n.name, { selfMs: n.selfMs, totalMs: n.totalMs, count: 1 }); + } + n.children.forEach(gather); + } + root.children.forEach(gather); + const top50 = [...flat.entries()].sort((a, b) => b[1].selfMs - a[1].selfMs).slice(0, 50); + const flatLines = top50.map( + ([mod, { selfMs, totalMs: totalMs2, count: count2 }]) => `${selfMs.toFixed(1).padStart(8)}ms self ${totalMs2.toFixed(1).padStart(8)}ms total (x${String(count2).padStart(3)}) ${mod}` + ); + process.stderr.write(` +--- Top 50 modules by self time --- +` + flatLines.join("\n") + "\n"); + }); +} diff --git a/simulator/e2e/node_modules/playwright-core/lib/coreBundle.js b/simulator/e2e/node_modules/playwright-core/lib/coreBundle.js new file mode 100644 index 0000000..7d8468f --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/lib/coreBundle.js @@ -0,0 +1,73386 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __esm = (fn, res) => function __init() { + return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; +}; +var __commonJS = (cb, mod) => function __require() { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; +}; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// packages/isomorphic/ariaSnapshot.ts +function ariaNodesEqual(a, b) { + if (a.role !== b.role || a.name !== b.name) + return false; + if (!ariaPropsEqual(a, b) || hasPointerCursor(a) !== hasPointerCursor(b)) + return false; + const aKeys = Object.keys(a.props); + const bKeys = Object.keys(b.props); + return aKeys.length === bKeys.length && aKeys.every((k) => a.props[k] === b.props[k]); +} +function hasPointerCursor(ariaNode) { + return ariaNode.box.cursor === "pointer"; +} +function ariaPropsEqual(a, b) { + return a.active === b.active && a.checked === b.checked && a.disabled === b.disabled && a.expanded === b.expanded && a.invalid === b.invalid && a.selected === b.selected && a.level === b.level && a.pressed === b.pressed; +} +function parseAriaSnapshotUnsafe(yaml3, text2, options = {}) { + const result2 = parseAriaSnapshot(yaml3, text2, options); + if (result2.errors.length) + throw new Error(result2.errors[0].message); + return result2.fragment; +} +function parseAriaSnapshot(yaml3, text2, options = {}) { + const lineCounter = new yaml3.LineCounter(); + const parseOptions = { + keepSourceTokens: true, + lineCounter, + ...options + }; + const yamlDoc = yaml3.parseDocument(text2, parseOptions); + const errors = []; + const convertRange = (range) => { + return [lineCounter.linePos(range[0]), lineCounter.linePos(range[1])]; + }; + const addError = (error) => { + errors.push({ + message: error.message, + range: [lineCounter.linePos(error.pos[0]), lineCounter.linePos(error.pos[1])] + }); + }; + const convertSeq = (container, seq) => { + for (const item of seq.items) { + const itemIsString = item instanceof yaml3.Scalar && typeof item.value === "string"; + if (itemIsString) { + const childNode = KeyParser.parse(item, parseOptions, errors); + if (childNode) { + container.children = container.children || []; + container.children.push(childNode); + } + continue; + } + const itemIsMap = item instanceof yaml3.YAMLMap; + if (itemIsMap) { + convertMap(container, item); + continue; + } + errors.push({ + message: "Sequence items should be strings or maps", + range: convertRange(item.range || seq.range) + }); + } + }; + const convertMap = (container, map) => { + for (const entry of map.items) { + container.children = container.children || []; + const keyIsString = entry.key instanceof yaml3.Scalar && typeof entry.key.value === "string"; + if (!keyIsString) { + errors.push({ + message: "Only string keys are supported", + range: convertRange(entry.key.range || map.range) + }); + continue; + } + const key = entry.key; + const value2 = entry.value; + if (key.value === "text") { + const valueIsString = value2 instanceof yaml3.Scalar && typeof value2.value === "string"; + if (!valueIsString) { + errors.push({ + message: "Text value should be a string", + range: convertRange(entry.value.range || map.range) + }); + continue; + } + container.children.push({ + kind: "text", + text: textValue(value2.value) + }); + continue; + } + if (key.value === "/children") { + const valueIsString = value2 instanceof yaml3.Scalar && typeof value2.value === "string"; + if (!valueIsString || value2.value !== "contain" && value2.value !== "equal" && value2.value !== "deep-equal") { + errors.push({ + message: 'Strict value should be "contain", "equal" or "deep-equal"', + range: convertRange(entry.value.range || map.range) + }); + continue; + } + container.containerMode = value2.value; + continue; + } + if (key.value.startsWith("/")) { + const valueIsString = value2 instanceof yaml3.Scalar && typeof value2.value === "string"; + if (!valueIsString) { + errors.push({ + message: "Property value should be a string", + range: convertRange(entry.value.range || map.range) + }); + continue; + } + container.props = container.props ?? {}; + container.props[key.value.slice(1)] = textValue(value2.value); + continue; + } + const childNode = KeyParser.parse(key, parseOptions, errors); + if (!childNode) + continue; + const valueIsScalar = value2 instanceof yaml3.Scalar; + if (valueIsScalar) { + const type3 = typeof value2.value; + if (type3 !== "string" && type3 !== "number" && type3 !== "boolean") { + errors.push({ + message: "Node value should be a string or a sequence", + range: convertRange(entry.value.range || map.range) + }); + continue; + } + container.children.push({ + ...childNode, + children: [{ + kind: "text", + text: textValue(String(value2.value)) + }] + }); + continue; + } + const valueIsSequence = value2 instanceof yaml3.YAMLSeq; + if (valueIsSequence) { + container.children.push(childNode); + convertSeq(childNode, value2); + continue; + } + errors.push({ + message: "Map values should be strings or sequences", + range: convertRange(entry.value.range || map.range) + }); + } + }; + const fragment = { kind: "role", role: "fragment" }; + yamlDoc.errors.forEach(addError); + if (errors.length) + return { errors, fragment }; + if (!(yamlDoc.contents instanceof yaml3.YAMLSeq)) { + errors.push({ + message: 'Aria snapshot must be a YAML sequence, elements starting with " -"', + range: yamlDoc.contents ? convertRange(yamlDoc.contents.range) : [{ line: 0, col: 0 }, { line: 0, col: 0 }] + }); + } + if (errors.length) + return { errors, fragment }; + convertSeq(fragment, yamlDoc.contents); + if (errors.length) + return { errors, fragment: emptyFragment }; + if (fragment.children?.length === 1 && (!fragment.containerMode || fragment.containerMode === "contain")) + return { fragment: fragment.children[0], errors: [] }; + return { fragment, errors: [] }; +} +function normalizeWhitespace(text2) { + return text2.replace(/[\u200b\u00ad]/g, "").replace(/[\r\n\s\t]+/g, " ").trim(); +} +function textValue(value2) { + return { + raw: value2, + normalized: normalizeWhitespace(value2) + }; +} +function findNewNode(from, to) { + function fillMap(root, map, position) { + let size = 1; + let childPosition = position + size; + for (const child of root.children || []) { + if (typeof child === "string") { + size++; + childPosition++; + } else { + size += fillMap(child, map, childPosition); + childPosition += size; + } + } + if (!["none", "presentation", "fragment", "iframe", "generic"].includes(root.role) && root.name) { + let byRole = map.get(root.role); + if (!byRole) { + byRole = /* @__PURE__ */ new Map(); + map.set(root.role, byRole); + } + const existing = byRole.get(root.name); + const sizeAndPosition = size * 100 - position; + if (!existing || existing.sizeAndPosition < sizeAndPosition) + byRole.set(root.name, { node: root, sizeAndPosition }); + } + return size; + } + const fromMap = /* @__PURE__ */ new Map(); + if (from) + fillMap(from, fromMap, 0); + const toMap = /* @__PURE__ */ new Map(); + fillMap(to, toMap, 0); + const result2 = []; + for (const [role, byRole] of toMap) { + for (const [name, byName] of byRole) { + const inFrom = fromMap.get(role)?.get(name); + if (!inFrom) + result2.push(byName); + } + } + result2.sort((a, b) => b.sizeAndPosition - a.sizeAndPosition); + return result2[0]?.node; +} +var emptyFragment, KeyParser, ParserError; +var init_ariaSnapshot = __esm({ + "packages/isomorphic/ariaSnapshot.ts"() { + "use strict"; + emptyFragment = { kind: "role", role: "fragment" }; + KeyParser = class _KeyParser { + static parse(text2, options, errors) { + try { + return new _KeyParser(text2.value)._parse(); + } catch (e) { + if (e instanceof ParserError) { + const message = options.prettyErrors === false ? e.message : e.message + ":\n\n" + text2.value + "\n" + " ".repeat(e.pos) + "^\n"; + errors.push({ + message, + range: [options.lineCounter.linePos(text2.range[0]), options.lineCounter.linePos(text2.range[0] + e.pos)] + }); + return null; + } + throw e; + } + } + constructor(input) { + this._input = input; + this._pos = 0; + this._length = input.length; + } + _peek() { + return this._input[this._pos] || ""; + } + _next() { + if (this._pos < this._length) + return this._input[this._pos++]; + return null; + } + _eof() { + return this._pos >= this._length; + } + _isWhitespace() { + return !this._eof() && /\s/.test(this._peek()); + } + _skipWhitespace() { + while (this._isWhitespace()) + this._pos++; + } + _readIdentifier(type3) { + if (this._eof()) + this._throwError(`Unexpected end of input when expecting ${type3}`); + const start3 = this._pos; + while (!this._eof() && /[a-zA-Z]/.test(this._peek())) + this._pos++; + return this._input.slice(start3, this._pos); + } + _readString() { + let result2 = ""; + let escaped2 = false; + while (!this._eof()) { + const ch = this._next(); + if (escaped2) { + result2 += ch; + escaped2 = false; + } else if (ch === "\\") { + escaped2 = true; + } else if (ch === '"') { + return result2; + } else { + result2 += ch; + } + } + this._throwError("Unterminated string"); + } + _throwError(message, offset = 0) { + throw new ParserError(message, offset || this._pos); + } + _readRegex() { + let result2 = ""; + let escaped2 = false; + let insideClass = false; + while (!this._eof()) { + const ch = this._next(); + if (escaped2) { + result2 += ch; + escaped2 = false; + } else if (ch === "\\") { + escaped2 = true; + result2 += ch; + } else if (ch === "/" && !insideClass) { + return { pattern: result2 }; + } else if (ch === "[") { + insideClass = true; + result2 += ch; + } else if (ch === "]" && insideClass) { + result2 += ch; + insideClass = false; + } else { + result2 += ch; + } + } + this._throwError("Unterminated regex"); + } + _readStringOrRegex() { + const ch = this._peek(); + if (ch === '"') { + this._next(); + return normalizeWhitespace(this._readString()); + } + if (ch === "/") { + this._next(); + return this._readRegex(); + } + return null; + } + _readAttributes(result2) { + let errorPos = this._pos; + while (true) { + this._skipWhitespace(); + if (this._peek() === "[") { + this._next(); + this._skipWhitespace(); + errorPos = this._pos; + const flagName = this._readIdentifier("attribute"); + this._skipWhitespace(); + let flagValue = ""; + if (this._peek() === "=") { + this._next(); + this._skipWhitespace(); + errorPos = this._pos; + while (this._peek() !== "]" && !this._isWhitespace() && !this._eof()) + flagValue += this._next(); + } + this._skipWhitespace(); + if (this._peek() !== "]") + this._throwError("Expected ]"); + this._next(); + this._applyAttribute(result2, flagName, flagValue || "true", errorPos); + } else { + break; + } + } + } + _parse() { + this._skipWhitespace(); + const role = this._readIdentifier("role"); + this._skipWhitespace(); + const name = this._readStringOrRegex() || ""; + const result2 = { kind: "role", role, name }; + this._readAttributes(result2); + this._skipWhitespace(); + if (!this._eof()) + this._throwError("Unexpected input"); + return result2; + } + _applyAttribute(node, key, value2, errorPos) { + if (key === "checked") { + this._assert(value2 === "true" || value2 === "false" || value2 === "mixed", 'Value of "checked" attribute must be a boolean or "mixed"', errorPos); + node.checked = value2 === "true" ? true : value2 === "false" ? false : "mixed"; + return; + } + if (key === "disabled") { + this._assert(value2 === "true" || value2 === "false", 'Value of "disabled" attribute must be a boolean', errorPos); + node.disabled = value2 === "true"; + return; + } + if (key === "expanded") { + this._assert(value2 === "true" || value2 === "false", 'Value of "expanded" attribute must be a boolean', errorPos); + node.expanded = value2 === "true"; + return; + } + if (key === "active") { + this._assert(value2 === "true" || value2 === "false", 'Value of "active" attribute must be a boolean', errorPos); + node.active = value2 === "true"; + return; + } + if (key === "invalid") { + this._assert(value2 === "true" || value2 === "false" || value2 === "grammar" || value2 === "spelling", 'Value of "invalid" attribute must be a boolean, "grammar" or "spelling"', errorPos); + node.invalid = value2 === "true" ? true : value2 === "false" ? false : value2; + return; + } + if (key === "level") { + this._assert(!isNaN(Number(value2)), 'Value of "level" attribute must be a number', errorPos); + node.level = Number(value2); + return; + } + if (key === "pressed") { + this._assert(value2 === "true" || value2 === "false" || value2 === "mixed", 'Value of "pressed" attribute must be a boolean or "mixed"', errorPos); + node.pressed = value2 === "true" ? true : value2 === "false" ? false : "mixed"; + return; + } + if (key === "selected") { + this._assert(value2 === "true" || value2 === "false", 'Value of "selected" attribute must be a boolean', errorPos); + node.selected = value2 === "true"; + return; + } + this._assert(false, `Unsupported attribute [${key}]`, errorPos); + } + _assert(value2, message, valuePos) { + if (!value2) + this._throwError(message || "Assertion error", valuePos); + } + }; + ParserError = class extends Error { + constructor(message, pos) { + super(message); + this.pos = pos; + } + }; + } +}); + +// packages/isomorphic/stringUtils.ts +function escapeWithQuotes(text2, char = "'") { + const stringified = JSON.stringify(text2); + const escapedText = stringified.substring(1, stringified.length - 1).replace(/\\"/g, '"'); + if (char === "'") + return char + escapedText.replace(/[']/g, "\\'") + char; + if (char === '"') + return char + escapedText.replace(/["]/g, '\\"') + char; + if (char === "`") + return char + escapedText.replace(/[`]/g, "\\`") + char; + throw new Error("Invalid escape char"); +} +function escapeTemplateString(text2) { + return text2.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${"); +} +function isString(obj) { + return typeof obj === "string" || obj instanceof String; +} +function toTitleCase(name) { + return name.charAt(0).toUpperCase() + name.substring(1); +} +function toSnakeCase(name) { + return name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([A-Z])([A-Z][a-z])/g, "$1_$2").toLowerCase(); +} +function formatObject(value2, indent = " ", mode = "multiline") { + if (typeof value2 === "string") + return escapeWithQuotes(value2, "'"); + if (Array.isArray(value2)) + return `[${value2.map((o) => formatObject(o)).join(", ")}]`; + if (typeof value2 === "object") { + const keys = Object.keys(value2).filter((key) => key !== "timeout" && value2[key] !== void 0).sort(); + if (!keys.length) + return "{}"; + const tokens = []; + for (const key of keys) + tokens.push(`${key}: ${formatObject(value2[key])}`); + if (mode === "multiline") + return `{ +${tokens.map((t) => indent + t).join(`, +`)} +}`; + return `{ ${tokens.join(", ")} }`; + } + return String(value2); +} +function formatObjectOrVoid(value2, indent = " ") { + const result2 = formatObject(value2, indent); + return result2 === "{}" ? "" : result2; +} +function quoteCSSAttributeValue(text2) { + return `"${text2.replace(/["\\]/g, (char) => "\\" + char)}"`; +} +function cacheNormalizedWhitespaces() { + normalizedWhitespaceCache = /* @__PURE__ */ new Map(); +} +function normalizeWhiteSpace(text2) { + let result2 = normalizedWhitespaceCache?.get(text2); + if (result2 === void 0) { + result2 = text2.replace(/[\u200b\u00ad]/g, "").trim().replace(/\s+/g, " "); + normalizedWhitespaceCache?.set(text2, result2); + } + return result2; +} +function normalizeEscapedRegexQuotes(source11) { + return source11.replace(/(^|[^\\])(\\\\)*\\(['"`])/g, "$1$2$3"); +} +function escapeRegexForSelector(re2) { + if (re2.unicode || re2.unicodeSets) + return String(re2); + return String(re2).replace(/(^|[^\\])(\\\\)*(["'`])/g, "$1$2\\$3").replace(/>>/g, "\\>\\>"); +} +function escapeForTextSelector(text2, exact) { + if (typeof text2 !== "string") + return escapeRegexForSelector(text2); + return `${JSON.stringify(text2)}${exact ? "s" : "i"}`; +} +function escapeForAttributeSelector(value2, exact) { + if (typeof value2 !== "string") + return escapeRegexForSelector(value2); + return `"${value2.replace(/\\/g, "\\\\").replace(/["]/g, '\\"')}"${exact ? "s" : "i"}`; +} +function trimString(input, cap, suffix = "") { + if (input.length <= cap) + return input; + const chars = [...input]; + if (chars.length > cap) + return chars.slice(0, cap - suffix.length).join("") + suffix; + return chars.join(""); +} +function trimStringWithEllipsis(input, cap) { + return trimString(input, cap, "\u2026"); +} +function escapeRegExp(s) { + return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); +} +function escapeHTMLAttribute(s) { + return s.replace(/[&<>"']/ug, (char) => escaped[char]); +} +function escapeHTML(s) { + return s.replace(/[&<]/ug, (char) => escaped[char]); +} +function longestCommonSubstring(s1, s2) { + const n = s1.length; + const m = s2.length; + let maxLen = 0; + let endingIndex = 0; + const dp = Array(n + 1).fill(null).map(() => Array(m + 1).fill(0)); + for (let i = 1; i <= n; i++) { + for (let j = 1; j <= m; j++) { + if (s1[i - 1] === s2[j - 1]) { + dp[i][j] = dp[i - 1][j - 1] + 1; + if (dp[i][j] > maxLen) { + maxLen = dp[i][j]; + endingIndex = i; + } + } + } + } + return s1.slice(endingIndex - maxLen, endingIndex); +} +function parseRegex(regex) { + if (regex[0] !== "/") + throw new Error(`Invalid regex, must start with '/': ${regex}`); + const lastSlash = regex.lastIndexOf("/"); + if (lastSlash <= 0) + throw new Error(`Invalid regex, must end with '/' followed by optional flags: ${regex}`); + const source11 = regex.slice(1, lastSlash); + const flags = regex.slice(lastSlash + 1); + return new RegExp(source11, flags); +} +function tomlBasicString(value2) { + return JSON.stringify(value2); +} +function tomlArray(values) { + return `[${values.map((value2) => tomlBasicString(value2)).join(", ")}]`; +} +function tomlMultilineBasicString(value2) { + const escaped2 = value2.replace(/\\/g, "\\\\").replace(/"""/g, '\\"\\"\\"'); + return `""" +${escaped2} +"""`; +} +function stripAnsiEscapes(str) { + return str.replace(ansiRegex, ""); +} +var normalizedWhitespaceCache, escaped, ansiRegex; +var init_stringUtils = __esm({ + "packages/isomorphic/stringUtils.ts"() { + "use strict"; + escaped = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }; + ansiRegex = new RegExp("([\\u001B\\u009B][[\\]()#?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)|(?:(?:\\d{0,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~])))", "g"); + } +}); + +// packages/isomorphic/rtti.ts +function isRegExp(obj) { + return obj instanceof RegExp || Object.prototype.toString.call(obj) === "[object RegExp]"; +} +function isRegexString(value2) { + try { + new RegExp(value2); + return true; + } catch { + return false; + } +} +function isObject(obj) { + return typeof obj === "object" && obj !== null; +} +function isError(obj) { + return obj instanceof Error || obj && Object.getPrototypeOf(obj)?.name === "Error"; +} +var init_rtti = __esm({ + "packages/isomorphic/rtti.ts"() { + "use strict"; + init_stringUtils(); + } +}); + +// packages/isomorphic/expectUtils.ts +function serializeExpectedTextValues(items, options = {}) { + return items.map((i) => ({ + string: isString(i) ? i : void 0, + regexSource: isRegExp(i) ? i.source : void 0, + regexFlags: isRegExp(i) ? i.flags : void 0, + matchSubstring: options.matchSubstring, + ignoreCase: options.ignoreCase, + normalizeWhiteSpace: options.normalizeWhiteSpace + })); +} +var init_expectUtils = __esm({ + "packages/isomorphic/expectUtils.ts"() { + "use strict"; + init_rtti(); + } +}); + +// packages/isomorphic/assert.ts +function assert(value2, message) { + if (!value2) + throw new Error(message || "Assertion error"); +} +var init_assert = __esm({ + "packages/isomorphic/assert.ts"() { + "use strict"; + } +}); + +// packages/isomorphic/base64.ts +function base64ByteLength(data) { + if (!data) + return 0; + const padding = data[data.length - 2] === "=" ? 2 : data[data.length - 1] === "=" ? 1 : 0; + return Math.max(0, Math.floor(data.length * 3 / 4) - padding); +} +var init_base64 = __esm({ + "packages/isomorphic/base64.ts"() { + "use strict"; + } +}); + +// packages/isomorphic/colors.ts +var webColors, noColors, applyStyle; +var init_colors = __esm({ + "packages/isomorphic/colors.ts"() { + "use strict"; + webColors = { + enabled: true, + reset: (text2) => applyStyle(0, 0, text2), + bold: (text2) => applyStyle(1, 22, text2), + dim: (text2) => applyStyle(2, 22, text2), + italic: (text2) => applyStyle(3, 23, text2), + underline: (text2) => applyStyle(4, 24, text2), + inverse: (text2) => applyStyle(7, 27, text2), + hidden: (text2) => applyStyle(8, 28, text2), + strikethrough: (text2) => applyStyle(9, 29, text2), + black: (text2) => applyStyle(30, 39, text2), + red: (text2) => applyStyle(31, 39, text2), + green: (text2) => applyStyle(32, 39, text2), + yellow: (text2) => applyStyle(33, 39, text2), + blue: (text2) => applyStyle(34, 39, text2), + magenta: (text2) => applyStyle(35, 39, text2), + cyan: (text2) => applyStyle(36, 39, text2), + white: (text2) => applyStyle(37, 39, text2), + gray: (text2) => applyStyle(90, 39, text2), + grey: (text2) => applyStyle(90, 39, text2) + }; + noColors = { + enabled: false, + reset: (t) => t, + bold: (t) => t, + dim: (t) => t, + italic: (t) => t, + underline: (t) => t, + inverse: (t) => t, + hidden: (t) => t, + strikethrough: (t) => t, + black: (t) => t, + red: (t) => t, + green: (t) => t, + yellow: (t) => t, + blue: (t) => t, + magenta: (t) => t, + cyan: (t) => t, + white: (t) => t, + gray: (t) => t, + grey: (t) => t + }; + applyStyle = (open5, close3, text2) => `\x1B[${open5}m${text2}\x1B[${close3}m`; + } +}); + +// packages/isomorphic/headers.ts +function headersObjectToArray(headers, separator, setCookieSeparator) { + if (!setCookieSeparator) + setCookieSeparator = separator; + const result2 = []; + for (const name in headers) { + const values = headers[name]; + if (values === void 0) + continue; + if (separator) { + const sep = name.toLowerCase() === "set-cookie" ? setCookieSeparator : separator; + for (const value2 of values.split(sep)) + result2.push({ name, value: value2.trim() }); + } else { + result2.push({ name, value: values }); + } + } + return result2; +} +function headersArrayToObject(headers, lowerCase) { + const result2 = {}; + for (const { name, value: value2 } of headers) + result2[lowerCase ? name.toLowerCase() : name] = value2; + return result2; +} +var init_headers = __esm({ + "packages/isomorphic/headers.ts"() { + "use strict"; + } +}); + +// packages/isomorphic/imageUtils.ts +function padImageToSize(image, size) { + if (image.width === size.width && image.height === size.height) + return image; + const buffer = new Uint8Array(size.width * size.height * 4); + for (let y = 0; y < size.height; y++) { + for (let x = 0; x < size.width; x++) { + const to = (y * size.width + x) * 4; + if (y < image.height && x < image.width) { + const from = (y * image.width + x) * 4; + buffer[to] = image.data[from]; + buffer[to + 1] = image.data[from + 1]; + buffer[to + 2] = image.data[from + 2]; + buffer[to + 3] = image.data[from + 3]; + } else { + buffer[to] = 0; + buffer[to + 1] = 0; + buffer[to + 2] = 0; + buffer[to + 3] = 0; + } + } + } + return { data: Buffer.from(buffer), width: size.width, height: size.height }; +} +function scaleImageToSize(image, size) { + const { data: src, width: w1, height: h1 } = image; + const w2 = Math.max(1, Math.floor(size.width)); + const h2 = Math.max(1, Math.floor(size.height)); + if (w1 === w2 && h1 === h2) + return image; + if (w1 <= 0 || h1 <= 0) + throw new Error("Invalid input image"); + if (size.width <= 0 || size.height <= 0 || !isFinite(size.width) || !isFinite(size.height)) + throw new Error("Invalid output dimensions"); + const clamp = (v, lo, hi) => v < lo ? lo : v > hi ? hi : v; + const weights = (t, o) => { + const t2 = t * t; + const t3 = t2 * t; + o[0] = -0.5 * t + 1 * t2 - 0.5 * t3; + o[1] = 1 - 2.5 * t2 + 1.5 * t3; + o[2] = 0.5 * t + 2 * t2 - 1.5 * t3; + o[3] = -0.5 * t2 + 0.5 * t3; + }; + const srcRowStride = w1 * 4; + const dstRowStride = w2 * 4; + const xOff = new Int32Array(w2 * 4); + const xW = new Float32Array(w2 * 4); + const wx = new Float32Array(4); + const xScale = w1 / w2; + for (let x = 0; x < w2; x++) { + const sx = (x + 0.5) * xScale - 0.5; + const sxi = Math.floor(sx); + const t = sx - sxi; + weights(t, wx); + const b = x * 4; + const i0 = clamp(sxi - 1, 0, w1 - 1); + const i1 = clamp(sxi + 0, 0, w1 - 1); + const i2 = clamp(sxi + 1, 0, w1 - 1); + const i3 = clamp(sxi + 2, 0, w1 - 1); + xOff[b + 0] = i0 << 2; + xOff[b + 1] = i1 << 2; + xOff[b + 2] = i2 << 2; + xOff[b + 3] = i3 << 2; + xW[b + 0] = wx[0]; + xW[b + 1] = wx[1]; + xW[b + 2] = wx[2]; + xW[b + 3] = wx[3]; + } + const yRow = new Int32Array(h2 * 4); + const yW = new Float32Array(h2 * 4); + const wy = new Float32Array(4); + const yScale = h1 / h2; + for (let y = 0; y < h2; y++) { + const sy = (y + 0.5) * yScale - 0.5; + const syi = Math.floor(sy); + const t = sy - syi; + weights(t, wy); + const b = y * 4; + const j0 = clamp(syi - 1, 0, h1 - 1); + const j1 = clamp(syi + 0, 0, h1 - 1); + const j2 = clamp(syi + 1, 0, h1 - 1); + const j3 = clamp(syi + 2, 0, h1 - 1); + yRow[b + 0] = j0 * srcRowStride; + yRow[b + 1] = j1 * srcRowStride; + yRow[b + 2] = j2 * srcRowStride; + yRow[b + 3] = j3 * srcRowStride; + yW[b + 0] = wy[0]; + yW[b + 1] = wy[1]; + yW[b + 2] = wy[2]; + yW[b + 3] = wy[3]; + } + const dst = new Uint8Array(w2 * h2 * 4); + for (let y = 0; y < h2; y++) { + const yb = y * 4; + const rb0 = yRow[yb + 0]; + const rb1 = yRow[yb + 1]; + const rb2 = yRow[yb + 2]; + const rb3 = yRow[yb + 3]; + const wy0 = yW[yb + 0]; + const wy1 = yW[yb + 1]; + const wy2 = yW[yb + 2]; + const wy3 = yW[yb + 3]; + const dstBase = y * dstRowStride; + for (let x = 0; x < w2; x++) { + const xb = x * 4; + const xo0 = xOff[xb + 0]; + const xo1 = xOff[xb + 1]; + const xo2 = xOff[xb + 2]; + const xo3 = xOff[xb + 3]; + const wx0 = xW[xb + 0]; + const wx1 = xW[xb + 1]; + const wx2 = xW[xb + 2]; + const wx3 = xW[xb + 3]; + const di = dstBase + (x << 2); + for (let c = 0; c < 4; c++) { + const r0 = src[rb0 + xo0 + c] * wx0 + src[rb0 + xo1 + c] * wx1 + src[rb0 + xo2 + c] * wx2 + src[rb0 + xo3 + c] * wx3; + const r1 = src[rb1 + xo0 + c] * wx0 + src[rb1 + xo1 + c] * wx1 + src[rb1 + xo2 + c] * wx2 + src[rb1 + xo3 + c] * wx3; + const r2 = src[rb2 + xo0 + c] * wx0 + src[rb2 + xo1 + c] * wx1 + src[rb2 + xo2 + c] * wx2 + src[rb2 + xo3 + c] * wx3; + const r3 = src[rb3 + xo0 + c] * wx0 + src[rb3 + xo1 + c] * wx1 + src[rb3 + xo2 + c] * wx2 + src[rb3 + xo3 + c] * wx3; + const v = r0 * wy0 + r1 * wy1 + r2 * wy2 + r3 * wy3; + dst[di + c] = v < 0 ? 0 : v > 255 ? 255 : v | 0; + } + } + } + return { data: Buffer.from(dst.buffer), width: w2, height: h2 }; +} +var init_imageUtils = __esm({ + "packages/isomorphic/imageUtils.ts"() { + "use strict"; + } +}); + +// packages/isomorphic/jsonSchema.ts +function validate(value2, schema, path59) { + const errors = []; + if (schema.oneOf) { + let bestErrors; + for (const variant of schema.oneOf) { + const variantErrors = validate(value2, variant, path59); + if (variantErrors.length === 0) + return []; + if (!bestErrors || variantErrors.length < bestErrors.length) + bestErrors = variantErrors; + } + if (bestErrors.length === 1 && bestErrors[0].startsWith(`${path59}: expected `)) + return [`${path59}: does not match any of the expected types`]; + return bestErrors; + } + if (schema.type === "string") { + if (typeof value2 !== "string") { + errors.push(`${path59}: expected string, got ${typeof value2}`); + return errors; + } + if (schema.pattern && !cachedRegex(schema.pattern).test(value2)) + errors.push(schema.patternError || `${path59}: must match pattern "${schema.pattern}"`); + return errors; + } + if (schema.type === "array") { + if (!Array.isArray(value2)) { + errors.push(`${path59}: expected array, got ${typeof value2}`); + return errors; + } + if (schema.items) { + for (let i = 0; i < value2.length; i++) + errors.push(...validate(value2[i], schema.items, `${path59}[${i}]`)); + } + return errors; + } + if (schema.type === "object") { + if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) { + errors.push(`${path59}: expected object, got ${Array.isArray(value2) ? "array" : typeof value2}`); + return errors; + } + const obj = value2; + for (const key of schema.required || []) { + if (obj[key] === void 0) + errors.push(`${path59}.${key}: required`); + } + for (const [key, propSchema] of Object.entries(schema.properties || {})) { + if (obj[key] !== void 0) + errors.push(...validate(obj[key], propSchema, `${path59}.${key}`)); + } + return errors; + } + return errors; +} +function cachedRegex(pattern) { + let regex = regexCache.get(pattern); + if (!regex) { + regex = new RegExp(pattern); + regexCache.set(pattern, regex); + } + return regex; +} +var regexCache; +var init_jsonSchema = __esm({ + "packages/isomorphic/jsonSchema.ts"() { + "use strict"; + regexCache = /* @__PURE__ */ new Map(); + } +}); + +// packages/isomorphic/cssTokenizer.ts +function digit(code) { + return between(code, 48, 57); +} +function hexdigit(code) { + return digit(code) || between(code, 65, 70) || between(code, 97, 102); +} +function uppercaseletter(code) { + return between(code, 65, 90); +} +function lowercaseletter(code) { + return between(code, 97, 122); +} +function letter(code) { + return uppercaseletter(code) || lowercaseletter(code); +} +function nonascii(code) { + return code >= 128; +} +function namestartchar(code) { + return letter(code) || nonascii(code) || code === 95; +} +function namechar(code) { + return namestartchar(code) || digit(code) || code === 45; +} +function nonprintable(code) { + return between(code, 0, 8) || code === 11 || between(code, 14, 31) || code === 127; +} +function newline(code) { + return code === 10; +} +function whitespace(code) { + return newline(code) || code === 9 || code === 32; +} +function preprocess(str) { + const codepoints = []; + for (let i = 0; i < str.length; i++) { + let code = str.charCodeAt(i); + if (code === 13 && str.charCodeAt(i + 1) === 10) { + code = 10; + i++; + } + if (code === 13 || code === 12) + code = 10; + if (code === 0) + code = 65533; + if (between(code, 55296, 56319) && between(str.charCodeAt(i + 1), 56320, 57343)) { + const lead = code - 55296; + const trail = str.charCodeAt(i + 1) - 56320; + code = Math.pow(2, 16) + lead * Math.pow(2, 10) + trail; + i++; + } + codepoints.push(code); + } + return codepoints; +} +function stringFromCode(code) { + if (code <= 65535) + return String.fromCharCode(code); + code -= Math.pow(2, 16); + const lead = Math.floor(code / Math.pow(2, 10)) + 55296; + const trail = code % Math.pow(2, 10) + 56320; + return String.fromCharCode(lead) + String.fromCharCode(trail); +} +function tokenize(str1) { + const str = preprocess(str1); + let i = -1; + const tokens = []; + let code; + let line = 0; + let column = 0; + let lastLineLength = 0; + const incrLineno = function() { + line += 1; + lastLineLength = column; + column = 0; + }; + const locStart = { line, column }; + const codepoint = function(i2) { + if (i2 >= str.length) + return -1; + return str[i2]; + }; + const next = function(num) { + if (num === void 0) + num = 1; + if (num > 3) + throw "Spec Error: no more than three codepoints of lookahead."; + return codepoint(i + num); + }; + const consume = function(num) { + if (num === void 0) + num = 1; + i += num; + code = codepoint(i); + if (newline(code)) + incrLineno(); + else + column += num; + return true; + }; + const reconsume = function() { + i -= 1; + if (newline(code)) { + line -= 1; + column = lastLineLength; + } else { + column -= 1; + } + locStart.line = line; + locStart.column = column; + return true; + }; + const eof = function(codepoint2) { + if (codepoint2 === void 0) + codepoint2 = code; + return codepoint2 === -1; + }; + const donothing = function() { + }; + const parseerror = function() { + }; + const consumeAToken = function() { + consumeComments(); + consume(); + if (whitespace(code)) { + while (whitespace(next())) + consume(); + return new WhitespaceToken(); + } else if (code === 34) { + return consumeAStringToken(); + } else if (code === 35) { + if (namechar(next()) || areAValidEscape(next(1), next(2))) { + const token = new HashToken(""); + if (wouldStartAnIdentifier(next(1), next(2), next(3))) + token.type = "id"; + token.value = consumeAName(); + return token; + } else { + return new DelimToken(code); + } + } else if (code === 36) { + if (next() === 61) { + consume(); + return new SuffixMatchToken(); + } else { + return new DelimToken(code); + } + } else if (code === 39) { + return consumeAStringToken(); + } else if (code === 40) { + return new OpenParenToken(); + } else if (code === 41) { + return new CloseParenToken(); + } else if (code === 42) { + if (next() === 61) { + consume(); + return new SubstringMatchToken(); + } else { + return new DelimToken(code); + } + } else if (code === 43) { + if (startsWithANumber()) { + reconsume(); + return consumeANumericToken(); + } else { + return new DelimToken(code); + } + } else if (code === 44) { + return new CommaToken(); + } else if (code === 45) { + if (startsWithANumber()) { + reconsume(); + return consumeANumericToken(); + } else if (next(1) === 45 && next(2) === 62) { + consume(2); + return new CDCToken(); + } else if (startsWithAnIdentifier()) { + reconsume(); + return consumeAnIdentlikeToken(); + } else { + return new DelimToken(code); + } + } else if (code === 46) { + if (startsWithANumber()) { + reconsume(); + return consumeANumericToken(); + } else { + return new DelimToken(code); + } + } else if (code === 58) { + return new ColonToken(); + } else if (code === 59) { + return new SemicolonToken(); + } else if (code === 60) { + if (next(1) === 33 && next(2) === 45 && next(3) === 45) { + consume(3); + return new CDOToken(); + } else { + return new DelimToken(code); + } + } else if (code === 64) { + if (wouldStartAnIdentifier(next(1), next(2), next(3))) + return new AtKeywordToken(consumeAName()); + else + return new DelimToken(code); + } else if (code === 91) { + return new OpenSquareToken(); + } else if (code === 92) { + if (startsWithAValidEscape()) { + reconsume(); + return consumeAnIdentlikeToken(); + } else { + parseerror(); + return new DelimToken(code); + } + } else if (code === 93) { + return new CloseSquareToken(); + } else if (code === 94) { + if (next() === 61) { + consume(); + return new PrefixMatchToken(); + } else { + return new DelimToken(code); + } + } else if (code === 123) { + return new OpenCurlyToken(); + } else if (code === 124) { + if (next() === 61) { + consume(); + return new DashMatchToken(); + } else if (next() === 124) { + consume(); + return new ColumnToken(); + } else { + return new DelimToken(code); + } + } else if (code === 125) { + return new CloseCurlyToken(); + } else if (code === 126) { + if (next() === 61) { + consume(); + return new IncludeMatchToken(); + } else { + return new DelimToken(code); + } + } else if (digit(code)) { + reconsume(); + return consumeANumericToken(); + } else if (namestartchar(code)) { + reconsume(); + return consumeAnIdentlikeToken(); + } else if (eof()) { + return new EOFToken(); + } else { + return new DelimToken(code); + } + }; + const consumeComments = function() { + while (next(1) === 47 && next(2) === 42) { + consume(2); + while (true) { + consume(); + if (code === 42 && next() === 47) { + consume(); + break; + } else if (eof()) { + parseerror(); + return; + } + } + } + }; + const consumeANumericToken = function() { + const num = consumeANumber(); + if (wouldStartAnIdentifier(next(1), next(2), next(3))) { + const token = new DimensionToken(); + token.value = num.value; + token.repr = num.repr; + token.type = num.type; + token.unit = consumeAName(); + return token; + } else if (next() === 37) { + consume(); + const token = new PercentageToken(); + token.value = num.value; + token.repr = num.repr; + return token; + } else { + const token = new NumberToken(); + token.value = num.value; + token.repr = num.repr; + token.type = num.type; + return token; + } + }; + const consumeAnIdentlikeToken = function() { + const str2 = consumeAName(); + if (str2.toLowerCase() === "url" && next() === 40) { + consume(); + while (whitespace(next(1)) && whitespace(next(2))) + consume(); + if (next() === 34 || next() === 39) + return new FunctionToken(str2); + else if (whitespace(next()) && (next(2) === 34 || next(2) === 39)) + return new FunctionToken(str2); + else + return consumeAURLToken(); + } else if (next() === 40) { + consume(); + return new FunctionToken(str2); + } else { + return new IdentToken(str2); + } + }; + const consumeAStringToken = function(endingCodePoint) { + if (endingCodePoint === void 0) + endingCodePoint = code; + let string = ""; + while (consume()) { + if (code === endingCodePoint || eof()) { + return new StringToken(string); + } else if (newline(code)) { + parseerror(); + reconsume(); + return new BadStringToken(); + } else if (code === 92) { + if (eof(next())) + donothing(); + else if (newline(next())) + consume(); + else + string += stringFromCode(consumeEscape()); + } else { + string += stringFromCode(code); + } + } + throw new Error("Internal error"); + }; + const consumeAURLToken = function() { + const token = new URLToken(""); + while (whitespace(next())) + consume(); + if (eof(next())) + return token; + while (consume()) { + if (code === 41 || eof()) { + return token; + } else if (whitespace(code)) { + while (whitespace(next())) + consume(); + if (next() === 41 || eof(next())) { + consume(); + return token; + } else { + consumeTheRemnantsOfABadURL(); + return new BadURLToken(); + } + } else if (code === 34 || code === 39 || code === 40 || nonprintable(code)) { + parseerror(); + consumeTheRemnantsOfABadURL(); + return new BadURLToken(); + } else if (code === 92) { + if (startsWithAValidEscape()) { + token.value += stringFromCode(consumeEscape()); + } else { + parseerror(); + consumeTheRemnantsOfABadURL(); + return new BadURLToken(); + } + } else { + token.value += stringFromCode(code); + } + } + throw new Error("Internal error"); + }; + const consumeEscape = function() { + consume(); + if (hexdigit(code)) { + const digits = [code]; + for (let total = 0; total < 5; total++) { + if (hexdigit(next())) { + consume(); + digits.push(code); + } else { + break; + } + } + if (whitespace(next())) + consume(); + let value2 = parseInt(digits.map(function(x) { + return String.fromCharCode(x); + }).join(""), 16); + if (value2 > maximumallowedcodepoint) + value2 = 65533; + return value2; + } else if (eof()) { + return 65533; + } else { + return code; + } + }; + const areAValidEscape = function(c1, c2) { + if (c1 !== 92) + return false; + if (newline(c2)) + return false; + return true; + }; + const startsWithAValidEscape = function() { + return areAValidEscape(code, next()); + }; + const wouldStartAnIdentifier = function(c1, c2, c3) { + if (c1 === 45) + return namestartchar(c2) || c2 === 45 || areAValidEscape(c2, c3); + else if (namestartchar(c1)) + return true; + else if (c1 === 92) + return areAValidEscape(c1, c2); + else + return false; + }; + const startsWithAnIdentifier = function() { + return wouldStartAnIdentifier(code, next(1), next(2)); + }; + const wouldStartANumber = function(c1, c2, c3) { + if (c1 === 43 || c1 === 45) { + if (digit(c2)) + return true; + if (c2 === 46 && digit(c3)) + return true; + return false; + } else if (c1 === 46) { + if (digit(c2)) + return true; + return false; + } else if (digit(c1)) { + return true; + } else { + return false; + } + }; + const startsWithANumber = function() { + return wouldStartANumber(code, next(1), next(2)); + }; + const consumeAName = function() { + let result2 = ""; + while (consume()) { + if (namechar(code)) { + result2 += stringFromCode(code); + } else if (startsWithAValidEscape()) { + result2 += stringFromCode(consumeEscape()); + } else { + reconsume(); + return result2; + } + } + throw new Error("Internal parse error"); + }; + const consumeANumber = function() { + let repr = ""; + let type3 = "integer"; + if (next() === 43 || next() === 45) { + consume(); + repr += stringFromCode(code); + } + while (digit(next())) { + consume(); + repr += stringFromCode(code); + } + if (next(1) === 46 && digit(next(2))) { + consume(); + repr += stringFromCode(code); + consume(); + repr += stringFromCode(code); + type3 = "number"; + while (digit(next())) { + consume(); + repr += stringFromCode(code); + } + } + const c1 = next(1); + const c2 = next(2); + const c3 = next(3); + if ((c1 === 69 || c1 === 101) && digit(c2)) { + consume(); + repr += stringFromCode(code); + consume(); + repr += stringFromCode(code); + type3 = "number"; + while (digit(next())) { + consume(); + repr += stringFromCode(code); + } + } else if ((c1 === 69 || c1 === 101) && (c2 === 43 || c2 === 45) && digit(c3)) { + consume(); + repr += stringFromCode(code); + consume(); + repr += stringFromCode(code); + consume(); + repr += stringFromCode(code); + type3 = "number"; + while (digit(next())) { + consume(); + repr += stringFromCode(code); + } + } + const value2 = convertAStringToANumber(repr); + return { type: type3, value: value2, repr }; + }; + const convertAStringToANumber = function(string) { + return +string; + }; + const consumeTheRemnantsOfABadURL = function() { + while (consume()) { + if (code === 41 || eof()) { + return; + } else if (startsWithAValidEscape()) { + consumeEscape(); + donothing(); + } else { + donothing(); + } + } + }; + let iterationCount = 0; + while (!eof(next())) { + tokens.push(consumeAToken()); + iterationCount++; + if (iterationCount > str.length * 2) + throw new Error("I'm infinite-looping!"); + } + return tokens; +} +function escapeIdent(string) { + string = "" + string; + let result2 = ""; + const firstcode = string.charCodeAt(0); + for (let i = 0; i < string.length; i++) { + const code = string.charCodeAt(i); + if (code === 0) + throw new InvalidCharacterError("Invalid character: the input contains U+0000."); + if (between(code, 1, 31) || code === 127 || i === 0 && between(code, 48, 57) || i === 1 && between(code, 48, 57) && firstcode === 45) + result2 += "\\" + code.toString(16) + " "; + else if (code >= 128 || code === 45 || code === 95 || between(code, 48, 57) || between(code, 65, 90) || between(code, 97, 122)) + result2 += string[i]; + else + result2 += "\\" + string[i]; + } + return result2; +} +function escapeHash(string) { + string = "" + string; + let result2 = ""; + for (let i = 0; i < string.length; i++) { + const code = string.charCodeAt(i); + if (code === 0) + throw new InvalidCharacterError("Invalid character: the input contains U+0000."); + if (code >= 128 || code === 45 || code === 95 || between(code, 48, 57) || between(code, 65, 90) || between(code, 97, 122)) + result2 += string[i]; + else + result2 += "\\" + code.toString(16) + " "; + } + return result2; +} +function escapeString(string) { + string = "" + string; + let result2 = ""; + for (let i = 0; i < string.length; i++) { + const code = string.charCodeAt(i); + if (code === 0) + throw new InvalidCharacterError("Invalid character: the input contains U+0000."); + if (between(code, 1, 31) || code === 127) + result2 += "\\" + code.toString(16) + " "; + else if (code === 34 || code === 92) + result2 += "\\" + string[i]; + else + result2 += string[i]; + } + return result2; +} +var between, maximumallowedcodepoint, InvalidCharacterError, CSSParserToken, BadStringToken, BadURLToken, WhitespaceToken, CDOToken, CDCToken, ColonToken, SemicolonToken, CommaToken, GroupingToken, OpenCurlyToken, CloseCurlyToken, OpenSquareToken, CloseSquareToken, OpenParenToken, CloseParenToken, IncludeMatchToken, DashMatchToken, PrefixMatchToken, SuffixMatchToken, SubstringMatchToken, ColumnToken, EOFToken, DelimToken, StringValuedToken, IdentToken, FunctionToken, AtKeywordToken, HashToken, StringToken, URLToken, NumberToken, PercentageToken, DimensionToken; +var init_cssTokenizer = __esm({ + "packages/isomorphic/cssTokenizer.ts"() { + "use strict"; + between = function(num, first, last) { + return num >= first && num <= last; + }; + maximumallowedcodepoint = 1114111; + InvalidCharacterError = class extends Error { + constructor(message) { + super(message); + this.name = "InvalidCharacterError"; + } + }; + CSSParserToken = class { + constructor() { + this.tokenType = ""; + } + toJSON() { + return { token: this.tokenType }; + } + toString() { + return this.tokenType; + } + toSource() { + return "" + this; + } + }; + BadStringToken = class extends CSSParserToken { + constructor() { + super(...arguments); + this.tokenType = "BADSTRING"; + } + }; + BadURLToken = class extends CSSParserToken { + constructor() { + super(...arguments); + this.tokenType = "BADURL"; + } + }; + WhitespaceToken = class extends CSSParserToken { + constructor() { + super(...arguments); + this.tokenType = "WHITESPACE"; + } + toString() { + return "WS"; + } + toSource() { + return " "; + } + }; + CDOToken = class extends CSSParserToken { + constructor() { + super(...arguments); + this.tokenType = "CDO"; + } + toSource() { + return ""; + } + }; + ColonToken = class extends CSSParserToken { + constructor() { + super(...arguments); + this.tokenType = ":"; + } + }; + SemicolonToken = class extends CSSParserToken { + constructor() { + super(...arguments); + this.tokenType = ";"; + } + }; + CommaToken = class extends CSSParserToken { + constructor() { + super(...arguments); + this.tokenType = ","; + } + }; + GroupingToken = class extends CSSParserToken { + constructor() { + super(...arguments); + this.value = ""; + this.mirror = ""; + } + }; + OpenCurlyToken = class extends GroupingToken { + constructor() { + super(); + this.tokenType = "{"; + this.value = "{"; + this.mirror = "}"; + } + }; + CloseCurlyToken = class extends GroupingToken { + constructor() { + super(); + this.tokenType = "}"; + this.value = "}"; + this.mirror = "{"; + } + }; + OpenSquareToken = class extends GroupingToken { + constructor() { + super(); + this.tokenType = "["; + this.value = "["; + this.mirror = "]"; + } + }; + CloseSquareToken = class extends GroupingToken { + constructor() { + super(); + this.tokenType = "]"; + this.value = "]"; + this.mirror = "["; + } + }; + OpenParenToken = class extends GroupingToken { + constructor() { + super(); + this.tokenType = "("; + this.value = "("; + this.mirror = ")"; + } + }; + CloseParenToken = class extends GroupingToken { + constructor() { + super(); + this.tokenType = ")"; + this.value = ")"; + this.mirror = "("; + } + }; + IncludeMatchToken = class extends CSSParserToken { + constructor() { + super(...arguments); + this.tokenType = "~="; + } + }; + DashMatchToken = class extends CSSParserToken { + constructor() { + super(...arguments); + this.tokenType = "|="; + } + }; + PrefixMatchToken = class extends CSSParserToken { + constructor() { + super(...arguments); + this.tokenType = "^="; + } + }; + SuffixMatchToken = class extends CSSParserToken { + constructor() { + super(...arguments); + this.tokenType = "$="; + } + }; + SubstringMatchToken = class extends CSSParserToken { + constructor() { + super(...arguments); + this.tokenType = "*="; + } + }; + ColumnToken = class extends CSSParserToken { + constructor() { + super(...arguments); + this.tokenType = "||"; + } + }; + EOFToken = class extends CSSParserToken { + constructor() { + super(...arguments); + this.tokenType = "EOF"; + } + toSource() { + return ""; + } + }; + DelimToken = class extends CSSParserToken { + constructor(code) { + super(); + this.tokenType = "DELIM"; + this.value = ""; + this.value = stringFromCode(code); + } + toString() { + return "DELIM(" + this.value + ")"; + } + toJSON() { + const json = this.constructor.prototype.constructor.prototype.toJSON.call(this); + json.value = this.value; + return json; + } + toSource() { + if (this.value === "\\") + return "\\\n"; + else + return this.value; + } + }; + StringValuedToken = class extends CSSParserToken { + constructor() { + super(...arguments); + this.value = ""; + } + ASCIIMatch(str) { + return this.value.toLowerCase() === str.toLowerCase(); + } + toJSON() { + const json = this.constructor.prototype.constructor.prototype.toJSON.call(this); + json.value = this.value; + return json; + } + }; + IdentToken = class extends StringValuedToken { + constructor(val) { + super(); + this.tokenType = "IDENT"; + this.value = val; + } + toString() { + return "IDENT(" + this.value + ")"; + } + toSource() { + return escapeIdent(this.value); + } + }; + FunctionToken = class extends StringValuedToken { + constructor(val) { + super(); + this.tokenType = "FUNCTION"; + this.value = val; + this.mirror = ")"; + } + toString() { + return "FUNCTION(" + this.value + ")"; + } + toSource() { + return escapeIdent(this.value) + "("; + } + }; + AtKeywordToken = class extends StringValuedToken { + constructor(val) { + super(); + this.tokenType = "AT-KEYWORD"; + this.value = val; + } + toString() { + return "AT(" + this.value + ")"; + } + toSource() { + return "@" + escapeIdent(this.value); + } + }; + HashToken = class extends StringValuedToken { + constructor(val) { + super(); + this.tokenType = "HASH"; + this.value = val; + this.type = "unrestricted"; + } + toString() { + return "HASH(" + this.value + ")"; + } + toJSON() { + const json = this.constructor.prototype.constructor.prototype.toJSON.call(this); + json.value = this.value; + json.type = this.type; + return json; + } + toSource() { + if (this.type === "id") + return "#" + escapeIdent(this.value); + else + return "#" + escapeHash(this.value); + } + }; + StringToken = class extends StringValuedToken { + constructor(val) { + super(); + this.tokenType = "STRING"; + this.value = val; + } + toString() { + return '"' + escapeString(this.value) + '"'; + } + }; + URLToken = class extends StringValuedToken { + constructor(val) { + super(); + this.tokenType = "URL"; + this.value = val; + } + toString() { + return "URL(" + this.value + ")"; + } + toSource() { + return 'url("' + escapeString(this.value) + '")'; + } + }; + NumberToken = class extends CSSParserToken { + constructor() { + super(); + this.tokenType = "NUMBER"; + this.type = "integer"; + this.repr = ""; + } + toString() { + if (this.type === "integer") + return "INT(" + this.value + ")"; + return "NUMBER(" + this.value + ")"; + } + toJSON() { + const json = super.toJSON(); + json.value = this.value; + json.type = this.type; + json.repr = this.repr; + return json; + } + toSource() { + return this.repr; + } + }; + PercentageToken = class extends CSSParserToken { + constructor() { + super(); + this.tokenType = "PERCENTAGE"; + this.repr = ""; + } + toString() { + return "PERCENTAGE(" + this.value + ")"; + } + toJSON() { + const json = this.constructor.prototype.constructor.prototype.toJSON.call(this); + json.value = this.value; + json.repr = this.repr; + return json; + } + toSource() { + return this.repr + "%"; + } + }; + DimensionToken = class extends CSSParserToken { + constructor() { + super(); + this.tokenType = "DIMENSION"; + this.type = "integer"; + this.repr = ""; + this.unit = ""; + } + toString() { + return "DIM(" + this.value + "," + this.unit + ")"; + } + toJSON() { + const json = this.constructor.prototype.constructor.prototype.toJSON.call(this); + json.value = this.value; + json.type = this.type; + json.repr = this.repr; + json.unit = this.unit; + return json; + } + toSource() { + const source11 = this.repr; + let unit = escapeIdent(this.unit); + if (unit[0].toLowerCase() === "e" && (unit[1] === "-" || between(unit.charCodeAt(1), 48, 57))) { + unit = "\\65 " + unit.slice(1, unit.length); + } + return source11 + unit; + } + }; + } +}); + +// packages/isomorphic/cssParser.ts +function isInvalidSelectorError(error) { + return error instanceof InvalidSelectorError; +} +function parseCSS(selector, customNames) { + let tokens; + try { + tokens = tokenize(selector); + if (!(tokens[tokens.length - 1] instanceof EOFToken)) + tokens.push(new EOFToken()); + } catch (e) { + const newMessage = e.message + ` while parsing css selector "${selector}". Did you mean to CSS.escape it?`; + const index = (e.stack || "").indexOf(e.message); + if (index !== -1) + e.stack = e.stack.substring(0, index) + newMessage + e.stack.substring(index + e.message.length); + e.message = newMessage; + throw e; + } + const unsupportedToken = tokens.find((token) => { + return token instanceof AtKeywordToken || token instanceof BadStringToken || token instanceof BadURLToken || token instanceof ColumnToken || token instanceof CDOToken || token instanceof CDCToken || token instanceof SemicolonToken || // TODO: Consider using these for something, e.g. to escape complex strings. + // For example :xpath{ (//div/bar[@attr="foo"])[2]/baz } + // Or this way :xpath( {complex-xpath-goes-here("hello")} ) + token instanceof OpenCurlyToken || token instanceof CloseCurlyToken || // TODO: Consider treating these as strings? + token instanceof URLToken || token instanceof PercentageToken; + }); + if (unsupportedToken) + throw new InvalidSelectorError(`Unsupported token "${unsupportedToken.toSource()}" while parsing css selector "${selector}". Did you mean to CSS.escape it?`); + let pos = 0; + const names = /* @__PURE__ */ new Set(); + function unexpected() { + return new InvalidSelectorError(`Unexpected token "${tokens[pos].toSource()}" while parsing css selector "${selector}". Did you mean to CSS.escape it?`); + } + function skipWhitespace() { + while (tokens[pos] instanceof WhitespaceToken) + pos++; + } + function isIdent(p = pos) { + return tokens[p] instanceof IdentToken; + } + function isString2(p = pos) { + return tokens[p] instanceof StringToken; + } + function isNumber(p = pos) { + return tokens[p] instanceof NumberToken; + } + function isComma(p = pos) { + return tokens[p] instanceof CommaToken; + } + function isOpenParen(p = pos) { + return tokens[p] instanceof OpenParenToken; + } + function isCloseParen(p = pos) { + return tokens[p] instanceof CloseParenToken; + } + function isFunction2(p = pos) { + return tokens[p] instanceof FunctionToken; + } + function isStar(p = pos) { + return tokens[p] instanceof DelimToken && tokens[p].value === "*"; + } + function isEOF(p = pos) { + return tokens[p] instanceof EOFToken; + } + function isClauseCombinator(p = pos) { + return tokens[p] instanceof DelimToken && [">", "+", "~"].includes(tokens[p].value); + } + function isSelectorClauseEnd(p = pos) { + return isComma(p) || isCloseParen(p) || isEOF(p) || isClauseCombinator(p) || tokens[p] instanceof WhitespaceToken; + } + function consumeFunctionArguments() { + const result3 = [consumeArgument()]; + while (true) { + skipWhitespace(); + if (!isComma()) + break; + pos++; + result3.push(consumeArgument()); + } + return result3; + } + function consumeArgument() { + skipWhitespace(); + if (isNumber()) + return tokens[pos++].value; + if (isString2()) + return tokens[pos++].value; + return consumeComplexSelector(); + } + function consumeComplexSelector() { + const result3 = { simples: [] }; + skipWhitespace(); + if (isClauseCombinator()) { + result3.simples.push({ selector: { functions: [{ name: "scope", args: [] }] }, combinator: "" }); + } else { + result3.simples.push({ selector: consumeSimpleSelector(), combinator: "" }); + } + while (true) { + skipWhitespace(); + if (isClauseCombinator()) { + result3.simples[result3.simples.length - 1].combinator = tokens[pos++].value; + skipWhitespace(); + } else if (isSelectorClauseEnd()) { + break; + } + result3.simples.push({ combinator: "", selector: consumeSimpleSelector() }); + } + return result3; + } + function consumeSimpleSelector() { + let rawCSSString = ""; + const functions = []; + while (!isSelectorClauseEnd()) { + if (isIdent() || isStar()) { + rawCSSString += tokens[pos++].toSource(); + } else if (tokens[pos] instanceof HashToken) { + rawCSSString += tokens[pos++].toSource(); + } else if (tokens[pos] instanceof DelimToken && tokens[pos].value === ".") { + pos++; + if (isIdent()) + rawCSSString += "." + tokens[pos++].toSource(); + else + throw unexpected(); + } else if (tokens[pos] instanceof ColonToken) { + pos++; + if (isIdent()) { + if (!customNames.has(tokens[pos].value.toLowerCase())) { + rawCSSString += ":" + tokens[pos++].toSource(); + } else { + const name = tokens[pos++].value.toLowerCase(); + functions.push({ name, args: [] }); + names.add(name); + } + } else if (isFunction2()) { + const name = tokens[pos++].value.toLowerCase(); + if (!customNames.has(name)) { + rawCSSString += `:${name}(${consumeBuiltinFunctionArguments()})`; + } else { + functions.push({ name, args: consumeFunctionArguments() }); + names.add(name); + } + skipWhitespace(); + if (!isCloseParen()) + throw unexpected(); + pos++; + } else { + throw unexpected(); + } + } else if (tokens[pos] instanceof OpenSquareToken) { + rawCSSString += "["; + pos++; + while (!(tokens[pos] instanceof CloseSquareToken) && !isEOF()) + rawCSSString += tokens[pos++].toSource(); + if (!(tokens[pos] instanceof CloseSquareToken)) + throw unexpected(); + rawCSSString += "]"; + pos++; + } else { + throw unexpected(); + } + } + if (!rawCSSString && !functions.length) + throw unexpected(); + return { css: rawCSSString || void 0, functions }; + } + function consumeBuiltinFunctionArguments() { + let s = ""; + let balance = 1; + while (!isEOF()) { + if (isOpenParen() || isFunction2()) + balance++; + if (isCloseParen()) + balance--; + if (!balance) + break; + s += tokens[pos++].toSource(); + } + return s; + } + const result2 = consumeFunctionArguments(); + if (!isEOF()) + throw unexpected(); + if (result2.some((arg) => typeof arg !== "object" || !("simples" in arg))) + throw new InvalidSelectorError(`Error while parsing css selector "${selector}". Did you mean to CSS.escape it?`); + return { selector: result2, names: Array.from(names) }; +} +function serializeSelector(args) { + return args.map((arg) => { + if (typeof arg === "string") + return `"${arg}"`; + if (typeof arg === "number") + return String(arg); + return arg.simples.map(({ selector, combinator }) => { + let s = selector.css || ""; + s = s + selector.functions.map((func) => `:${func.name}(${serializeSelector(func.args)})`).join(""); + if (combinator) + s += " " + combinator; + return s; + }).join(" "); + }).join(", "); +} +var InvalidSelectorError; +var init_cssParser = __esm({ + "packages/isomorphic/cssParser.ts"() { + "use strict"; + init_cssTokenizer(); + InvalidSelectorError = class extends Error { + }; + } +}); + +// packages/isomorphic/selectorParser.ts +function parseSelector(selector) { + const parsedStrings = parseSelectorString(selector); + const parts = []; + for (const part of parsedStrings.parts) { + if (part.name === "css" || part.name === "css:light") { + if (part.name === "css:light") + part.body = ":light(" + part.body + ")"; + const parsedCSS = parseCSS(part.body, customCSSNames); + parts.push({ + name: "css", + body: parsedCSS.selector, + source: part.body + }); + continue; + } + if (kNestedSelectorNames.has(part.name)) { + let innerSelector; + let distance; + try { + const unescaped = JSON.parse("[" + part.body + "]"); + if (!Array.isArray(unescaped) || unescaped.length < 1 || unescaped.length > 2 || typeof unescaped[0] !== "string") + throw new InvalidSelectorError(`Malformed selector: ${part.name}=` + part.body); + innerSelector = unescaped[0]; + if (unescaped.length === 2) { + if (typeof unescaped[1] !== "number" || !kNestedSelectorNamesWithDistance.has(part.name)) + throw new InvalidSelectorError(`Malformed selector: ${part.name}=` + part.body); + distance = unescaped[1]; + } + } catch (e) { + throw new InvalidSelectorError(`Malformed selector: ${part.name}=` + part.body); + } + const nested = { name: part.name, source: part.body, body: { parsed: parseSelector(innerSelector), distance } }; + const lastFrame = [...nested.body.parsed.parts].reverse().find((part2) => part2.name === "internal:control" && part2.body === "enter-frame"); + const lastFrameIndex = lastFrame ? nested.body.parsed.parts.indexOf(lastFrame) : -1; + if (lastFrameIndex !== -1 && selectorPartsEqual(nested.body.parsed.parts.slice(0, lastFrameIndex + 1), parts.slice(0, lastFrameIndex + 1))) + nested.body.parsed.parts.splice(0, lastFrameIndex + 1); + parts.push(nested); + continue; + } + parts.push({ ...part, source: part.body }); + } + if (kNestedSelectorNames.has(parts[0].name)) + throw new InvalidSelectorError(`"${parts[0].name}" selector cannot be first`); + return { + capture: parsedStrings.capture, + parts + }; +} +function splitSelectorByFrame(selectorText) { + const selector = parseSelector(selectorText); + const result2 = []; + let chunk = { + parts: [] + }; + let chunkStartIndex = 0; + for (let i = 0; i < selector.parts.length; ++i) { + const part = selector.parts[i]; + if (part.name === "internal:control" && part.body === "enter-frame") { + if (!chunk.parts.length) + throw new InvalidSelectorError("Selector cannot start with entering frame, select the iframe first"); + result2.push(chunk); + chunk = { parts: [] }; + chunkStartIndex = i + 1; + continue; + } + if (selector.capture === i) + chunk.capture = i - chunkStartIndex; + chunk.parts.push(part); + } + if (!chunk.parts.length) + throw new InvalidSelectorError(`Selector cannot end with entering frame, while parsing selector ${selectorText}`); + result2.push(chunk); + if (typeof selector.capture === "number" && typeof result2[result2.length - 1].capture !== "number") + throw new InvalidSelectorError(`Can not capture the selector before diving into the frame. Only use * after the last frame has been selected`); + return result2; +} +function selectorPartsEqual(list1, list2) { + return stringifySelector({ parts: list1 }) === stringifySelector({ parts: list2 }); +} +function stringifySelector(selector, forceEngineName) { + if (typeof selector === "string") + return selector; + return selector.parts.map((p, i) => { + let includeEngine = true; + if (!forceEngineName && i !== selector.capture) { + if (p.name === "css") + includeEngine = false; + else if (p.name === "xpath" && p.source.startsWith("//") || p.source.startsWith("..")) + includeEngine = false; + } + const prefix = includeEngine ? p.name + "=" : ""; + return `${i === selector.capture ? "*" : ""}${prefix}${p.source}`; + }).join(" >> "); +} +function visitAllSelectorParts(selector, visitor) { + const visit = (selector2, nested) => { + for (const part of selector2.parts) { + visitor(part, nested); + if (kNestedSelectorNames.has(part.name)) + visit(part.body.parsed, true); + } + }; + visit(selector, false); +} +function parseSelectorString(selector) { + let index = 0; + let quote5; + let start3 = 0; + const result2 = { parts: [] }; + const append = () => { + const part = selector.substring(start3, index).trim(); + const eqIndex = part.indexOf("="); + let name; + let body; + if (eqIndex !== -1 && part.substring(0, eqIndex).trim().match(/^[a-zA-Z_0-9-+:*]+$/)) { + name = part.substring(0, eqIndex).trim(); + body = part.substring(eqIndex + 1); + } else if (part.length > 1 && part[0] === '"' && part[part.length - 1] === '"') { + name = "text"; + body = part; + } else if (part.length > 1 && part[0] === "'" && part[part.length - 1] === "'") { + name = "text"; + body = part; + } else if (/^\(*\/\//.test(part) || part.startsWith("..")) { + name = "xpath"; + body = part; + } else { + name = "css"; + body = part; + } + let capture = false; + if (name[0] === "*") { + capture = true; + name = name.substring(1); + } + result2.parts.push({ name, body }); + if (capture) { + if (result2.capture !== void 0) + throw new InvalidSelectorError(`Only one of the selectors can capture using * modifier`); + result2.capture = result2.parts.length - 1; + } + }; + if (!selector.includes(">>")) { + index = selector.length; + append(); + return result2; + } + const shouldIgnoreTextSelectorQuote = () => { + const prefix = selector.substring(start3, index); + const match = prefix.match(/^\s*text\s*=(.*)$/); + return !!match && !!match[1]; + }; + while (index < selector.length) { + const c = selector[index]; + if (c === "\\" && index + 1 < selector.length) { + index += 2; + } else if (c === quote5) { + quote5 = void 0; + index++; + } else if (!quote5 && (c === '"' || c === "'" || c === "`") && !shouldIgnoreTextSelectorQuote()) { + quote5 = c; + index++; + } else if (!quote5 && c === ">" && selector[index + 1] === ">") { + append(); + index += 2; + start3 = index; + } else { + index++; + } + } + append(); + return result2; +} +function parseAttributeSelector(selector, allowUnquotedStrings) { + let wp = 0; + let EOL = selector.length === 0; + const next = () => selector[wp] || ""; + const eat1 = () => { + const result3 = next(); + ++wp; + EOL = wp >= selector.length; + return result3; + }; + const syntaxError = (stage) => { + if (EOL) + throw new InvalidSelectorError(`Unexpected end of selector while parsing selector \`${selector}\``); + throw new InvalidSelectorError(`Error while parsing selector \`${selector}\` - unexpected symbol "${next()}" at position ${wp}` + (stage ? " during " + stage : "")); + }; + function skipSpaces() { + while (!EOL && /\s/.test(next())) + eat1(); + } + function isCSSNameChar(char) { + return char >= "\x80" || char >= "0" && char <= "9" || char >= "A" && char <= "Z" || char >= "a" && char <= "z" || char >= "0" && char <= "9" || char === "_" || char === "-"; + } + function readIdentifier() { + let result3 = ""; + skipSpaces(); + while (!EOL && isCSSNameChar(next())) + result3 += eat1(); + return result3; + } + function readQuotedString(quote5) { + let result3 = eat1(); + if (result3 !== quote5) + syntaxError("parsing quoted string"); + while (!EOL && next() !== quote5) { + if (next() === "\\") + eat1(); + result3 += eat1(); + } + if (next() !== quote5) + syntaxError("parsing quoted string"); + result3 += eat1(); + return result3; + } + function readRegularExpression() { + if (eat1() !== "/") + syntaxError("parsing regular expression"); + let source11 = ""; + let inClass = false; + while (!EOL) { + if (next() === "\\") { + source11 += eat1(); + if (EOL) + syntaxError("parsing regular expression"); + } else if (inClass && next() === "]") { + inClass = false; + } else if (!inClass && next() === "[") { + inClass = true; + } else if (!inClass && next() === "/") { + break; + } + source11 += eat1(); + } + if (eat1() !== "/") + syntaxError("parsing regular expression"); + let flags = ""; + while (!EOL && next().match(/[dgimsuy]/)) + flags += eat1(); + try { + return new RegExp(source11, flags); + } catch (e) { + throw new InvalidSelectorError(`Error while parsing selector \`${selector}\`: ${e.message}`); + } + } + function readAttributeToken() { + let token = ""; + skipSpaces(); + if (next() === `'` || next() === `"`) + token = readQuotedString(next()).slice(1, -1); + else + token = readIdentifier(); + if (!token) + syntaxError("parsing property path"); + return token; + } + function readOperator() { + skipSpaces(); + let op = ""; + if (!EOL) + op += eat1(); + if (!EOL && op !== "=") + op += eat1(); + if (!["=", "*=", "^=", "$=", "|=", "~="].includes(op)) + syntaxError("parsing operator"); + return op; + } + function readAttribute() { + eat1(); + const jsonPath = []; + jsonPath.push(readAttributeToken()); + skipSpaces(); + while (next() === ".") { + eat1(); + jsonPath.push(readAttributeToken()); + skipSpaces(); + } + if (next() === "]") { + eat1(); + return { name: jsonPath.join("."), jsonPath, op: "", value: null, caseSensitive: false }; + } + const operator = readOperator(); + let value2 = void 0; + let caseSensitive = true; + skipSpaces(); + if (next() === "/") { + if (operator !== "=") + throw new InvalidSelectorError(`Error while parsing selector \`${selector}\` - cannot use ${operator} in attribute with regular expression`); + value2 = readRegularExpression(); + } else if (next() === `'` || next() === `"`) { + value2 = readQuotedString(next()).slice(1, -1); + skipSpaces(); + if (next() === "i" || next() === "I") { + caseSensitive = false; + eat1(); + } else if (next() === "s" || next() === "S") { + caseSensitive = true; + eat1(); + } + } else { + value2 = ""; + while (!EOL && (isCSSNameChar(next()) || next() === "+" || next() === ".")) + value2 += eat1(); + if (value2 === "true") { + value2 = true; + } else if (value2 === "false") { + value2 = false; + } else { + if (!allowUnquotedStrings) { + value2 = +value2; + if (Number.isNaN(value2)) + syntaxError("parsing attribute value"); + } + } + } + skipSpaces(); + if (next() !== "]") + syntaxError("parsing attribute value"); + eat1(); + if (operator !== "=" && typeof value2 !== "string") + throw new InvalidSelectorError(`Error while parsing selector \`${selector}\` - cannot use ${operator} in attribute with non-string matching value - ${value2}`); + return { name: jsonPath.join("."), jsonPath, op: operator, value: value2, caseSensitive }; + } + const result2 = { + name: "", + attributes: [] + }; + result2.name = readIdentifier(); + skipSpaces(); + while (next() === "[") { + result2.attributes.push(readAttribute()); + skipSpaces(); + } + if (!EOL) + syntaxError(void 0); + if (!result2.name && !result2.attributes.length) + throw new InvalidSelectorError(`Error while parsing selector \`${selector}\` - selector cannot be empty`); + return result2; +} +var kNestedSelectorNames, kNestedSelectorNamesWithDistance, customCSSNames; +var init_selectorParser = __esm({ + "packages/isomorphic/selectorParser.ts"() { + "use strict"; + init_cssParser(); + init_cssParser(); + kNestedSelectorNames = /* @__PURE__ */ new Set(["internal:has", "internal:has-not", "internal:and", "internal:or", "internal:chain", "left-of", "right-of", "above", "below", "near"]); + kNestedSelectorNamesWithDistance = /* @__PURE__ */ new Set(["left-of", "right-of", "above", "below", "near"]); + customCSSNames = /* @__PURE__ */ new Set(["not", "is", "where", "has", "scope", "light", "visible", "text", "text-matches", "text-is", "has-text", "above", "below", "right-of", "left-of", "near", "nth-match"]); + } +}); + +// packages/isomorphic/locatorGenerators.ts +function asLocatorDescription(lang, selector) { + try { + const parsed = parseSelector(selector); + const customDescription = parseCustomDescription(parsed); + if (customDescription) + return customDescription; + return innerAsLocators(new generators[lang](), parsed, false, 1)[0]; + } catch (e) { + return selector; + } +} +function locatorCustomDescription(selector) { + try { + const parsed = parseSelector(selector); + return parseCustomDescription(parsed); + } catch (e) { + return void 0; + } +} +function parseCustomDescription(parsed) { + const lastPart = parsed.parts[parsed.parts.length - 1]; + if (lastPart?.name === "internal:describe") { + const description = JSON.parse(lastPart.body); + if (typeof description === "string") + return description; + } + return void 0; +} +function asLocator(lang, selector, isFrameLocator = false) { + return asLocators(lang, selector, isFrameLocator, 1)[0]; +} +function asLocators(lang, selector, isFrameLocator = false, maxOutputSize = 20, preferredQuote) { + try { + return innerAsLocators(new generators[lang](preferredQuote), parseSelector(selector), isFrameLocator, maxOutputSize); + } catch (e) { + return [selector]; + } +} +function innerAsLocators(factory, parsed, isFrameLocator = false, maxOutputSize = 20) { + const parts = [...parsed.parts]; + const tokens = []; + let nextBase = isFrameLocator ? "frame-locator" : "page"; + for (let index = 0; index < parts.length; index++) { + const part = parts[index]; + const base = nextBase; + nextBase = "locator"; + if (part.name === "internal:describe") + continue; + if (part.name === "nth") { + if (part.body === "0") + tokens.push([factory.generateLocator(base, "first", ""), factory.generateLocator(base, "nth", "0")]); + else if (part.body === "-1") + tokens.push([factory.generateLocator(base, "last", ""), factory.generateLocator(base, "nth", "-1")]); + else + tokens.push([factory.generateLocator(base, "nth", part.body)]); + continue; + } + if (part.name === "visible") { + tokens.push([factory.generateLocator(base, "visible", part.body), factory.generateLocator(base, "default", `visible=${part.body}`)]); + continue; + } + if (part.name === "internal:text") { + const { exact, text: text2 } = detectExact(part.body); + tokens.push([factory.generateLocator(base, "text", text2, { exact })]); + continue; + } + if (part.name === "internal:has-text") { + const { exact, text: text2 } = detectExact(part.body); + if (!exact) { + tokens.push([factory.generateLocator(base, "has-text", text2, { exact })]); + continue; + } + } + if (part.name === "internal:has-not-text") { + const { exact, text: text2 } = detectExact(part.body); + if (!exact) { + tokens.push([factory.generateLocator(base, "has-not-text", text2, { exact })]); + continue; + } + } + if (part.name === "internal:has") { + const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize); + tokens.push(inners.map((inner) => factory.generateLocator(base, "has", inner))); + continue; + } + if (part.name === "internal:has-not") { + const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize); + tokens.push(inners.map((inner) => factory.generateLocator(base, "hasNot", inner))); + continue; + } + if (part.name === "internal:and") { + const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize); + tokens.push(inners.map((inner) => factory.generateLocator(base, "and", inner))); + continue; + } + if (part.name === "internal:or") { + const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize); + tokens.push(inners.map((inner) => factory.generateLocator(base, "or", inner))); + continue; + } + if (part.name === "internal:chain") { + const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize); + tokens.push(inners.map((inner) => factory.generateLocator(base, "chain", inner))); + continue; + } + if (part.name === "internal:label") { + const { exact, text: text2 } = detectExact(part.body); + tokens.push([factory.generateLocator(base, "label", text2, { exact })]); + continue; + } + if (part.name === "internal:role") { + const attrSelector = parseAttributeSelector(part.body, true); + const options = { attrs: [] }; + for (const attr of attrSelector.attributes) { + if (attr.name === "name") { + if (options.exact !== void 0 && options.exact !== attr.caseSensitive) + throw new Error(`Conflicting exactness in internal:role selector: ${stringifySelector({ parts: [part] })}`); + options.exact = attr.caseSensitive; + options.name = attr.value; + } else if (attr.name === "description") { + if (options.exact !== void 0 && options.exact !== attr.caseSensitive) + throw new Error(`Conflicting exactness in internal:role selector: ${stringifySelector({ parts: [part] })}`); + options.exact = attr.caseSensitive; + options.description = attr.value; + } else { + if (attr.name === "level" && typeof attr.value === "string") + attr.value = +attr.value; + options.attrs.push({ name: attr.name === "include-hidden" ? "includeHidden" : attr.name, value: attr.value }); + } + } + tokens.push([factory.generateLocator(base, "role", attrSelector.name, options)]); + continue; + } + if (part.name === "internal:testid") { + const attrSelector = parseAttributeSelector(part.body, true); + const { value: value2 } = attrSelector.attributes[0]; + tokens.push([factory.generateLocator(base, "test-id", value2)]); + continue; + } + if (part.name === "internal:attr") { + const attrSelector = parseAttributeSelector(part.body, true); + const { name, value: value2, caseSensitive } = attrSelector.attributes[0]; + const text2 = value2; + const exact = !!caseSensitive; + if (name === "placeholder") { + tokens.push([factory.generateLocator(base, "placeholder", text2, { exact })]); + continue; + } + if (name === "alt") { + tokens.push([factory.generateLocator(base, "alt", text2, { exact })]); + continue; + } + if (name === "title") { + tokens.push([factory.generateLocator(base, "title", text2, { exact })]); + continue; + } + } + if (part.name === "internal:control" && part.body === "enter-frame") { + const lastTokens = tokens[tokens.length - 1]; + const lastPart = parts[index - 1]; + const transformed = lastTokens.map((token) => factory.chainLocators([token, factory.generateLocator(base, "frame", "")])); + if (["xpath", "css"].includes(lastPart.name)) { + transformed.push( + factory.generateLocator(base, "frame-locator", stringifySelector({ parts: [lastPart] })), + factory.generateLocator(base, "frame-locator", stringifySelector({ parts: [lastPart] }, true)) + ); + } + lastTokens.splice(0, lastTokens.length, ...transformed); + nextBase = "frame-locator"; + continue; + } + const nextPart = parts[index + 1]; + const selectorPart = stringifySelector({ parts: [part] }); + const locatorPart = factory.generateLocator(base, "default", selectorPart); + if (nextPart && ["internal:has-text", "internal:has-not-text"].includes(nextPart.name)) { + const { exact, text: text2 } = detectExact(nextPart.body); + if (!exact) { + const nextLocatorPart = factory.generateLocator("locator", nextPart.name === "internal:has-text" ? "has-text" : "has-not-text", text2, { exact }); + const options = {}; + if (nextPart.name === "internal:has-text") + options.hasText = text2; + else + options.hasNotText = text2; + const combinedPart = factory.generateLocator(base, "default", selectorPart, options); + tokens.push([factory.chainLocators([locatorPart, nextLocatorPart]), combinedPart]); + index++; + continue; + } + } + let locatorPartWithEngine; + if (["xpath", "css"].includes(part.name)) { + const selectorPart2 = stringifySelector( + { parts: [part] }, + /* forceEngineName */ + true + ); + locatorPartWithEngine = factory.generateLocator(base, "default", selectorPart2); + } + tokens.push([locatorPart, locatorPartWithEngine].filter(Boolean)); + } + return combineTokens(factory, tokens, maxOutputSize); +} +function combineTokens(factory, tokens, maxOutputSize) { + const currentTokens = tokens.map(() => ""); + const result2 = []; + const visit = (index) => { + if (index === tokens.length) { + result2.push(factory.chainLocators(currentTokens)); + return result2.length < maxOutputSize; + } + for (const taken of tokens[index]) { + currentTokens[index] = taken; + if (!visit(index + 1)) + return false; + } + return true; + }; + visit(0); + return result2; +} +function detectExact(text2) { + let exact = false; + const match = text2.match(/^\/(.*)\/([igm]*)$/); + if (match) + return { text: new RegExp(match[1], match[2]) }; + if (text2.endsWith('"')) { + text2 = JSON.parse(text2); + exact = true; + } else if (text2.endsWith('"s')) { + text2 = JSON.parse(text2.substring(0, text2.length - 1)); + exact = true; + } else if (text2.endsWith('"i')) { + text2 = JSON.parse(text2.substring(0, text2.length - 1)); + exact = false; + } + return { exact, text: text2 }; +} +function isRegExp2(obj) { + return obj instanceof RegExp; +} +var JavaScriptLocatorFactory, PythonLocatorFactory, JavaLocatorFactory, CSharpLocatorFactory, JsonlLocatorFactory, generators; +var init_locatorGenerators = __esm({ + "packages/isomorphic/locatorGenerators.ts"() { + "use strict"; + init_selectorParser(); + init_stringUtils(); + JavaScriptLocatorFactory = class { + constructor(preferredQuote) { + this.preferredQuote = preferredQuote; + } + generateLocator(base, kind, body, options = {}) { + switch (kind) { + case "default": + if (options.hasText !== void 0) + return `locator(${this.quote(body)}, { hasText: ${this.toHasText(options.hasText)} })`; + if (options.hasNotText !== void 0) + return `locator(${this.quote(body)}, { hasNotText: ${this.toHasText(options.hasNotText)} })`; + return `locator(${this.quote(body)})`; + case "frame-locator": + return `frameLocator(${this.quote(body)})`; + case "frame": + return `contentFrame()`; + case "nth": + return `nth(${body})`; + case "first": + return `first()`; + case "last": + return `last()`; + case "visible": + return `filter({ visible: ${body === "true" ? "true" : "false"} })`; + case "role": + const attrs = []; + if (isRegExp2(options.name)) + attrs.push(`name: ${this.regexToSourceString(options.name)}`); + else if (typeof options.name === "string") + attrs.push(`name: ${this.quote(options.name)}`); + if (isRegExp2(options.description)) + attrs.push(`description: ${this.regexToSourceString(options.description)}`); + else if (typeof options.description === "string") + attrs.push(`description: ${this.quote(options.description)}`); + if (options.exact && (typeof options.name === "string" || typeof options.description === "string")) + attrs.push(`exact: true`); + for (const { name, value: value2 } of options.attrs) + attrs.push(`${name}: ${typeof value2 === "string" ? this.quote(value2) : value2}`); + const attrString = attrs.length ? `, { ${attrs.join(", ")} }` : ""; + return `getByRole(${this.quote(body)}${attrString})`; + case "has-text": + return `filter({ hasText: ${this.toHasText(body)} })`; + case "has-not-text": + return `filter({ hasNotText: ${this.toHasText(body)} })`; + case "has": + return `filter({ has: ${body} })`; + case "hasNot": + return `filter({ hasNot: ${body} })`; + case "and": + return `and(${body})`; + case "or": + return `or(${body})`; + case "chain": + return `locator(${body})`; + case "test-id": + return `getByTestId(${this.toTestIdValue(body)})`; + case "text": + return this.toCallWithExact("getByText", body, !!options.exact); + case "alt": + return this.toCallWithExact("getByAltText", body, !!options.exact); + case "placeholder": + return this.toCallWithExact("getByPlaceholder", body, !!options.exact); + case "label": + return this.toCallWithExact("getByLabel", body, !!options.exact); + case "title": + return this.toCallWithExact("getByTitle", body, !!options.exact); + default: + throw new Error("Unknown selector kind " + kind); + } + } + chainLocators(locators) { + return locators.join("."); + } + regexToSourceString(re2) { + return normalizeEscapedRegexQuotes(String(re2)); + } + toCallWithExact(method, body, exact) { + if (isRegExp2(body)) + return `${method}(${this.regexToSourceString(body)})`; + return exact ? `${method}(${this.quote(body)}, { exact: true })` : `${method}(${this.quote(body)})`; + } + toHasText(body) { + if (isRegExp2(body)) + return this.regexToSourceString(body); + return this.quote(body); + } + toTestIdValue(value2) { + if (isRegExp2(value2)) + return this.regexToSourceString(value2); + return this.quote(value2); + } + quote(text2) { + return escapeWithQuotes(text2, this.preferredQuote ?? "'"); + } + }; + PythonLocatorFactory = class { + generateLocator(base, kind, body, options = {}) { + switch (kind) { + case "default": + if (options.hasText !== void 0) + return `locator(${this.quote(body)}, has_text=${this.toHasText(options.hasText)})`; + if (options.hasNotText !== void 0) + return `locator(${this.quote(body)}, has_not_text=${this.toHasText(options.hasNotText)})`; + return `locator(${this.quote(body)})`; + case "frame-locator": + return `frame_locator(${this.quote(body)})`; + case "frame": + return `content_frame`; + case "nth": + return `nth(${body})`; + case "first": + return `first`; + case "last": + return `last`; + case "visible": + return `filter(visible=${body === "true" ? "True" : "False"})`; + case "role": + const attrs = []; + if (isRegExp2(options.name)) + attrs.push(`name=${this.regexToString(options.name)}`); + else if (typeof options.name === "string") + attrs.push(`name=${this.quote(options.name)}`); + if (isRegExp2(options.description)) + attrs.push(`description=${this.regexToString(options.description)}`); + else if (typeof options.description === "string") + attrs.push(`description=${this.quote(options.description)}`); + if (options.exact && (typeof options.name === "string" || typeof options.description === "string")) + attrs.push(`exact=True`); + for (const { name, value: value2 } of options.attrs) { + let valueString = typeof value2 === "string" ? this.quote(value2) : value2; + if (typeof value2 === "boolean") + valueString = value2 ? "True" : "False"; + attrs.push(`${toSnakeCase(name)}=${valueString}`); + } + const attrString = attrs.length ? `, ${attrs.join(", ")}` : ""; + return `get_by_role(${this.quote(body)}${attrString})`; + case "has-text": + return `filter(has_text=${this.toHasText(body)})`; + case "has-not-text": + return `filter(has_not_text=${this.toHasText(body)})`; + case "has": + return `filter(has=${body})`; + case "hasNot": + return `filter(has_not=${body})`; + case "and": + return `and_(${body})`; + case "or": + return `or_(${body})`; + case "chain": + return `locator(${body})`; + case "test-id": + return `get_by_test_id(${this.toTestIdValue(body)})`; + case "text": + return this.toCallWithExact("get_by_text", body, !!options.exact); + case "alt": + return this.toCallWithExact("get_by_alt_text", body, !!options.exact); + case "placeholder": + return this.toCallWithExact("get_by_placeholder", body, !!options.exact); + case "label": + return this.toCallWithExact("get_by_label", body, !!options.exact); + case "title": + return this.toCallWithExact("get_by_title", body, !!options.exact); + default: + throw new Error("Unknown selector kind " + kind); + } + } + chainLocators(locators) { + return locators.join("."); + } + regexToString(body) { + const suffix = body.flags.includes("i") ? ", re.IGNORECASE" : ""; + return `re.compile(r"${normalizeEscapedRegexQuotes(body.source).replace(/\\\//, "/").replace(/"/g, '\\"')}"${suffix})`; + } + toCallWithExact(method, body, exact) { + if (isRegExp2(body)) + return `${method}(${this.regexToString(body)})`; + if (exact) + return `${method}(${this.quote(body)}, exact=True)`; + return `${method}(${this.quote(body)})`; + } + toHasText(body) { + if (isRegExp2(body)) + return this.regexToString(body); + return `${this.quote(body)}`; + } + toTestIdValue(value2) { + if (isRegExp2(value2)) + return this.regexToString(value2); + return this.quote(value2); + } + quote(text2) { + return escapeWithQuotes(text2, '"'); + } + }; + JavaLocatorFactory = class { + generateLocator(base, kind, body, options = {}) { + let clazz; + switch (base) { + case "page": + clazz = "Page"; + break; + case "frame-locator": + clazz = "FrameLocator"; + break; + case "locator": + clazz = "Locator"; + break; + } + switch (kind) { + case "default": + if (options.hasText !== void 0) + return `locator(${this.quote(body)}, new ${clazz}.LocatorOptions().setHasText(${this.toHasText(options.hasText)}))`; + if (options.hasNotText !== void 0) + return `locator(${this.quote(body)}, new ${clazz}.LocatorOptions().setHasNotText(${this.toHasText(options.hasNotText)}))`; + return `locator(${this.quote(body)})`; + case "frame-locator": + return `frameLocator(${this.quote(body)})`; + case "frame": + return `contentFrame()`; + case "nth": + return `nth(${body})`; + case "first": + return `first()`; + case "last": + return `last()`; + case "visible": + return `filter(new ${clazz}.FilterOptions().setVisible(${body === "true" ? "true" : "false"}))`; + case "role": + const attrs = []; + if (isRegExp2(options.name)) + attrs.push(`.setName(${this.regexToString(options.name)})`); + else if (typeof options.name === "string") + attrs.push(`.setName(${this.quote(options.name)})`); + if (isRegExp2(options.description)) + attrs.push(`.setDescription(${this.regexToString(options.description)})`); + else if (typeof options.description === "string") + attrs.push(`.setDescription(${this.quote(options.description)})`); + if (options.exact && (typeof options.name === "string" || typeof options.description === "string")) + attrs.push(`.setExact(true)`); + for (const { name, value: value2 } of options.attrs) + attrs.push(`.set${toTitleCase(name)}(${typeof value2 === "string" ? this.quote(value2) : value2})`); + const attrString = attrs.length ? `, new ${clazz}.GetByRoleOptions()${attrs.join("")}` : ""; + return `getByRole(AriaRole.${toSnakeCase(body).toUpperCase()}${attrString})`; + case "has-text": + return `filter(new ${clazz}.FilterOptions().setHasText(${this.toHasText(body)}))`; + case "has-not-text": + return `filter(new ${clazz}.FilterOptions().setHasNotText(${this.toHasText(body)}))`; + case "has": + return `filter(new ${clazz}.FilterOptions().setHas(${body}))`; + case "hasNot": + return `filter(new ${clazz}.FilterOptions().setHasNot(${body}))`; + case "and": + return `and(${body})`; + case "or": + return `or(${body})`; + case "chain": + return `locator(${body})`; + case "test-id": + return `getByTestId(${this.toTestIdValue(body)})`; + case "text": + return this.toCallWithExact(clazz, "getByText", body, !!options.exact); + case "alt": + return this.toCallWithExact(clazz, "getByAltText", body, !!options.exact); + case "placeholder": + return this.toCallWithExact(clazz, "getByPlaceholder", body, !!options.exact); + case "label": + return this.toCallWithExact(clazz, "getByLabel", body, !!options.exact); + case "title": + return this.toCallWithExact(clazz, "getByTitle", body, !!options.exact); + default: + throw new Error("Unknown selector kind " + kind); + } + } + chainLocators(locators) { + return locators.join("."); + } + regexToString(body) { + const suffix = body.flags.includes("i") ? ", Pattern.CASE_INSENSITIVE" : ""; + return `Pattern.compile(${this.quote(normalizeEscapedRegexQuotes(body.source))}${suffix})`; + } + toCallWithExact(clazz, method, body, exact) { + if (isRegExp2(body)) + return `${method}(${this.regexToString(body)})`; + if (exact) + return `${method}(${this.quote(body)}, new ${clazz}.${toTitleCase(method)}Options().setExact(true))`; + return `${method}(${this.quote(body)})`; + } + toHasText(body) { + if (isRegExp2(body)) + return this.regexToString(body); + return this.quote(body); + } + toTestIdValue(value2) { + if (isRegExp2(value2)) + return this.regexToString(value2); + return this.quote(value2); + } + quote(text2) { + return escapeWithQuotes(text2, '"'); + } + }; + CSharpLocatorFactory = class { + generateLocator(base, kind, body, options = {}) { + switch (kind) { + case "default": + if (options.hasText !== void 0) + return `Locator(${this.quote(body)}, new() { ${this.toHasText(options.hasText)} })`; + if (options.hasNotText !== void 0) + return `Locator(${this.quote(body)}, new() { ${this.toHasNotText(options.hasNotText)} })`; + return `Locator(${this.quote(body)})`; + case "frame-locator": + return `FrameLocator(${this.quote(body)})`; + case "frame": + return `ContentFrame`; + case "nth": + return `Nth(${body})`; + case "first": + return `First`; + case "last": + return `Last`; + case "visible": + return `Filter(new() { Visible = ${body === "true" ? "true" : "false"} })`; + case "role": + const attrs = []; + if (isRegExp2(options.name)) + attrs.push(`NameRegex = ${this.regexToString(options.name)}`); + else if (typeof options.name === "string") + attrs.push(`Name = ${this.quote(options.name)}`); + if (isRegExp2(options.description)) + attrs.push(`DescriptionRegex = ${this.regexToString(options.description)}`); + else if (typeof options.description === "string") + attrs.push(`Description = ${this.quote(options.description)}`); + if (options.exact && (typeof options.name === "string" || typeof options.description === "string")) + attrs.push(`Exact = true`); + for (const { name, value: value2 } of options.attrs) + attrs.push(`${toTitleCase(name)} = ${typeof value2 === "string" ? this.quote(value2) : value2}`); + const attrString = attrs.length ? `, new() { ${attrs.join(", ")} }` : ""; + return `GetByRole(AriaRole.${toTitleCase(body)}${attrString})`; + case "has-text": + return `Filter(new() { ${this.toHasText(body)} })`; + case "has-not-text": + return `Filter(new() { ${this.toHasNotText(body)} })`; + case "has": + return `Filter(new() { Has = ${body} })`; + case "hasNot": + return `Filter(new() { HasNot = ${body} })`; + case "and": + return `And(${body})`; + case "or": + return `Or(${body})`; + case "chain": + return `Locator(${body})`; + case "test-id": + return `GetByTestId(${this.toTestIdValue(body)})`; + case "text": + return this.toCallWithExact("GetByText", body, !!options.exact); + case "alt": + return this.toCallWithExact("GetByAltText", body, !!options.exact); + case "placeholder": + return this.toCallWithExact("GetByPlaceholder", body, !!options.exact); + case "label": + return this.toCallWithExact("GetByLabel", body, !!options.exact); + case "title": + return this.toCallWithExact("GetByTitle", body, !!options.exact); + default: + throw new Error("Unknown selector kind " + kind); + } + } + chainLocators(locators) { + return locators.join("."); + } + regexToString(body) { + const suffix = body.flags.includes("i") ? ", RegexOptions.IgnoreCase" : ""; + return `new Regex(${this.quote(normalizeEscapedRegexQuotes(body.source))}${suffix})`; + } + toCallWithExact(method, body, exact) { + if (isRegExp2(body)) + return `${method}(${this.regexToString(body)})`; + if (exact) + return `${method}(${this.quote(body)}, new() { Exact = true })`; + return `${method}(${this.quote(body)})`; + } + toHasText(body) { + if (isRegExp2(body)) + return `HasTextRegex = ${this.regexToString(body)}`; + return `HasText = ${this.quote(body)}`; + } + toTestIdValue(value2) { + if (isRegExp2(value2)) + return this.regexToString(value2); + return this.quote(value2); + } + toHasNotText(body) { + if (isRegExp2(body)) + return `HasNotTextRegex = ${this.regexToString(body)}`; + return `HasNotText = ${this.quote(body)}`; + } + quote(text2) { + return escapeWithQuotes(text2, '"'); + } + }; + JsonlLocatorFactory = class { + generateLocator(base, kind, body, options = {}) { + return JSON.stringify({ + kind, + body, + options + }); + } + chainLocators(locators) { + const objects = locators.map((l) => JSON.parse(l)); + for (let i = 0; i < objects.length - 1; ++i) + objects[i].next = objects[i + 1]; + return JSON.stringify(objects[0]); + } + }; + generators = { + javascript: JavaScriptLocatorFactory, + python: PythonLocatorFactory, + java: JavaLocatorFactory, + csharp: CSharpLocatorFactory, + jsonl: JsonlLocatorFactory + }; + } +}); + +// packages/isomorphic/stackTrace.ts +function captureRawStack() { + const stackTraceLimit = Error.stackTraceLimit; + Error.stackTraceLimit = 50; + const error = new Error(); + const stack = error.stack || ""; + Error.stackTraceLimit = stackTraceLimit; + return stack.split("\n"); +} +function parseStackFrame(text2, pathSeparator, showInternalStackFrames) { + const match = text2 && text2.match(re); + if (!match) + return null; + let fname = match[2]; + let file = match[7]; + if (!file) + return null; + if (!showInternalStackFrames && (file.startsWith("internal") || file.startsWith("node:"))) + return null; + const line = match[8]; + const column = match[9]; + const closeParen = match[11] === ")"; + const frame = { + file: "", + line: 0, + column: 0 + }; + if (line) + frame.line = Number(line); + if (column) + frame.column = Number(column); + if (closeParen && file) { + let closes = 0; + for (let i = file.length - 1; i > 0; i--) { + if (file.charAt(i) === ")") { + closes++; + } else if (file.charAt(i) === "(" && file.charAt(i - 1) === " ") { + closes--; + if (closes === -1 && file.charAt(i - 1) === " ") { + const before = file.slice(0, i - 1); + const after = file.slice(i + 1); + file = after; + fname += ` (${before}`; + break; + } + } + } + } + if (fname) { + const methodMatch = fname.match(methodRe); + if (methodMatch) + fname = methodMatch[1]; + } + if (file) { + if (file.startsWith("file://")) + file = fileURLToPath(file, pathSeparator); + frame.file = file; + } + if (fname) + frame.function = fname; + return frame; +} +function rewriteErrorMessage(e, newMessage) { + const lines = (e.stack?.split("\n") || []).filter((l) => l.startsWith(" at ")); + e.message = newMessage; + const errorTitle = `${e.name}: ${e.message}`; + if (lines.length) + e.stack = `${errorTitle} +${lines.join("\n")}`; + return e; +} +function stringifyStackFrames(frames) { + const stackLines = []; + for (const frame of frames) { + if (frame.function) + stackLines.push(` at ${frame.function} (${frame.file}:${frame.line}:${frame.column})`); + else + stackLines.push(` at ${frame.file}:${frame.line}:${frame.column}`); + } + return stackLines; +} +function splitErrorMessage(message) { + const separationIdx = message.indexOf(":"); + return { + name: separationIdx !== -1 ? message.slice(0, separationIdx) : "", + message: separationIdx !== -1 && separationIdx + 2 <= message.length ? message.substring(separationIdx + 2) : message + }; +} +function parseErrorStack(stack, pathSeparator, showInternalStackFrames = false) { + const lines = stack.split("\n"); + let firstStackLine = lines.findIndex((line) => line.startsWith(" at ")); + if (firstStackLine === -1) + firstStackLine = lines.length; + const message = lines.slice(0, firstStackLine).join("\n"); + const stackLines = lines.slice(firstStackLine); + let location2; + for (const line of stackLines) { + const frame = parseStackFrame(line, pathSeparator, showInternalStackFrames); + if (!frame || !frame.file) + continue; + if (belongsToNodeModules(frame.file, pathSeparator)) + continue; + location2 = { file: frame.file, column: frame.column || 0, line: frame.line || 0 }; + break; + } + return { message, stackLines, location: location2 }; +} +function belongsToNodeModules(file, pathSeparator) { + return file.includes(`${pathSeparator}node_modules${pathSeparator}`); +} +function fileURLToPath(fileUrl, pathSeparator) { + if (!fileUrl.startsWith("file://")) + return fileUrl; + let path59 = decodeURIComponent(fileUrl.slice(7)); + if (path59.startsWith("/") && /^[a-zA-Z]:/.test(path59.slice(1))) + path59 = path59.slice(1); + return path59.replace(/\//g, pathSeparator); +} +var re, methodRe; +var init_stackTrace = __esm({ + "packages/isomorphic/stackTrace.ts"() { + "use strict"; + re = new RegExp( + "^(?:\\s*at )?(?:(new) )?(?:(.*?) \\()?(?:eval at ([^ ]+) \\((.+?):(\\d+):(\\d+)\\), )?(?:(.+?):(\\d+):(\\d+)|(native))(\\)?)$" + ); + methodRe = /^(.*?) \[as (.*?)\]$/; + } +}); + +// packages/isomorphic/manualPromise.ts +function signalToPromise(signal) { + if (signal.aborted) + return { promise: Promise.resolve(), dispose: () => { + } }; + let dispose; + const promise = new Promise((resolve) => { + const onAbort = () => resolve(); + signal.addEventListener("abort", onAbort, { once: true }); + dispose = () => signal.removeEventListener("abort", onAbort); + }); + return { promise, dispose }; +} +function cloneError(error, frames) { + const clone = new Error(); + clone.name = error.name; + clone.message = error.message; + clone.stack = [error.name + ":" + error.message, ...frames].join("\n"); + return clone; +} +var ManualPromise, LongStandingScope; +var init_manualPromise = __esm({ + "packages/isomorphic/manualPromise.ts"() { + "use strict"; + init_stackTrace(); + ManualPromise = class extends Promise { + constructor() { + let resolve; + let reject; + super((f, r) => { + resolve = f; + reject = r; + }); + this._isDone = false; + this._resolve = resolve; + this._reject = reject; + } + isDone() { + return this._isDone; + } + resolve(t) { + this._isDone = true; + this._resolve(t); + } + reject(e) { + this._isDone = true; + this._reject(e); + } + static get [Symbol.species]() { + return Promise; + } + get [Symbol.toStringTag]() { + return "ManualPromise"; + } + }; + LongStandingScope = class { + constructor() { + this._terminatePromises = /* @__PURE__ */ new Map(); + this._isClosed = false; + } + reject(error) { + this._isClosed = true; + this._terminateError = error; + for (const p of this._terminatePromises.keys()) + p.resolve(error); + } + close(error) { + this._isClosed = true; + this._closeError = error; + for (const [p, frames] of this._terminatePromises) + p.resolve(cloneError(error, frames)); + } + isClosed() { + return this._isClosed; + } + static async raceMultiple(scopes, promise) { + return Promise.race(scopes.map((s) => s.race(promise))); + } + async race(promise) { + return this._race(Array.isArray(promise) ? promise : [promise], false); + } + async safeRace(promise, defaultValue) { + return this._race([promise], true, defaultValue); + } + async _race(promises, safe, defaultValue) { + const terminatePromise = new ManualPromise(); + const frames = captureRawStack(); + if (this._terminateError) + terminatePromise.resolve(this._terminateError); + if (this._closeError) + terminatePromise.resolve(cloneError(this._closeError, frames)); + this._terminatePromises.set(terminatePromise, frames); + try { + return await Promise.race([ + terminatePromise.then((e) => safe ? defaultValue : Promise.reject(e)), + ...promises + ]); + } finally { + this._terminatePromises.delete(terminatePromise); + } + } + }; + } +}); + +// packages/isomorphic/mimeType.ts +function isJsonMimeType(mimeType) { + return !!mimeType.match(/^(application\/json|application\/.*?\+json|text\/(x-)?json)(;\s*charset=.*)?$/); +} +function isXmlMimeType(mimeType) { + return !!mimeType.match(/^(application\/xml|application\/.*?\+xml|text\/xml)(;\s*charset=.*)?$/); +} +function isTextualMimeType(mimeType) { + return !!mimeType.match(/^(text\/.*?|application\/(json|(x-)?javascript|xml.*?|ecmascript|graphql|x-www-form-urlencoded)|image\/svg(\+xml)?|application\/.*?(\+json|\+xml))(;\s*charset=.*)?$/); +} +function getMimeTypeForPath(path59) { + const dotIndex = path59.lastIndexOf("."); + if (dotIndex === -1) + return null; + const extension = path59.substring(dotIndex + 1); + return types.get(extension) || null; +} +function getExtensionForMimeType(contentType) { + const subtype = (contentType ?? "").split(";")[0].split("/")[1]?.trim().toLowerCase() ?? ""; + if (!subtype) + return "bin"; + const tail = subtype.includes("+") ? subtype.split("+").pop() : subtype; + if (tail === "plain") + return "txt"; + if (tail === "javascript" || tail === "ecmascript") + return "js"; + if (tail === "jpeg") + return "jpg"; + return tail.replace(/[^a-z0-9]/g, "") || "bin"; +} +var types; +var init_mimeType = __esm({ + "packages/isomorphic/mimeType.ts"() { + "use strict"; + types = /* @__PURE__ */ new Map([ + ["ez", "application/andrew-inset"], + ["aw", "application/applixware"], + ["atom", "application/atom+xml"], + ["atomcat", "application/atomcat+xml"], + ["atomdeleted", "application/atomdeleted+xml"], + ["atomsvc", "application/atomsvc+xml"], + ["dwd", "application/atsc-dwd+xml"], + ["held", "application/atsc-held+xml"], + ["rsat", "application/atsc-rsat+xml"], + ["bdoc", "application/bdoc"], + ["xcs", "application/calendar+xml"], + ["ccxml", "application/ccxml+xml"], + ["cdfx", "application/cdfx+xml"], + ["cdmia", "application/cdmi-capability"], + ["cdmic", "application/cdmi-container"], + ["cdmid", "application/cdmi-domain"], + ["cdmio", "application/cdmi-object"], + ["cdmiq", "application/cdmi-queue"], + ["cu", "application/cu-seeme"], + ["mpd", "application/dash+xml"], + ["davmount", "application/davmount+xml"], + ["dbk", "application/docbook+xml"], + ["dssc", "application/dssc+der"], + ["xdssc", "application/dssc+xml"], + ["ecma", "application/ecmascript"], + ["es", "application/ecmascript"], + ["emma", "application/emma+xml"], + ["emotionml", "application/emotionml+xml"], + ["epub", "application/epub+zip"], + ["exi", "application/exi"], + ["exp", "application/express"], + ["fdt", "application/fdt+xml"], + ["pfr", "application/font-tdpfr"], + ["geojson", "application/geo+json"], + ["gml", "application/gml+xml"], + ["gpx", "application/gpx+xml"], + ["gxf", "application/gxf"], + ["gz", "application/gzip"], + ["hjson", "application/hjson"], + ["stk", "application/hyperstudio"], + ["ink", "application/inkml+xml"], + ["inkml", "application/inkml+xml"], + ["ipfix", "application/ipfix"], + ["its", "application/its+xml"], + ["ear", "application/java-archive"], + ["jar", "application/java-archive"], + ["war", "application/java-archive"], + ["ser", "application/java-serialized-object"], + ["class", "application/java-vm"], + ["js", "application/javascript"], + ["mjs", "application/javascript"], + ["json", "application/json"], + ["map", "application/json"], + ["json5", "application/json5"], + ["jsonml", "application/jsonml+json"], + ["jsonld", "application/ld+json"], + ["lgr", "application/lgr+xml"], + ["lostxml", "application/lost+xml"], + ["hqx", "application/mac-binhex40"], + ["cpt", "application/mac-compactpro"], + ["mads", "application/mads+xml"], + ["webmanifest", "application/manifest+json"], + ["mrc", "application/marc"], + ["mrcx", "application/marcxml+xml"], + ["ma", "application/mathematica"], + ["mb", "application/mathematica"], + ["nb", "application/mathematica"], + ["mathml", "application/mathml+xml"], + ["mbox", "application/mbox"], + ["mscml", "application/mediaservercontrol+xml"], + ["metalink", "application/metalink+xml"], + ["meta4", "application/metalink4+xml"], + ["mets", "application/mets+xml"], + ["maei", "application/mmt-aei+xml"], + ["musd", "application/mmt-usd+xml"], + ["mods", "application/mods+xml"], + ["m21", "application/mp21"], + ["mp21", "application/mp21"], + ["m4p", "application/mp4"], + ["mp4s", "application/mp4"], + ["doc", "application/msword"], + ["dot", "application/msword"], + ["mxf", "application/mxf"], + ["nq", "application/n-quads"], + ["nt", "application/n-triples"], + ["cjs", "application/node"], + ["bin", "application/octet-stream"], + ["bpk", "application/octet-stream"], + ["buffer", "application/octet-stream"], + ["deb", "application/octet-stream"], + ["deploy", "application/octet-stream"], + ["dist", "application/octet-stream"], + ["distz", "application/octet-stream"], + ["dll", "application/octet-stream"], + ["dmg", "application/octet-stream"], + ["dms", "application/octet-stream"], + ["dump", "application/octet-stream"], + ["elc", "application/octet-stream"], + ["exe", "application/octet-stream"], + ["img", "application/octet-stream"], + ["iso", "application/octet-stream"], + ["lrf", "application/octet-stream"], + ["mar", "application/octet-stream"], + ["msi", "application/octet-stream"], + ["msm", "application/octet-stream"], + ["msp", "application/octet-stream"], + ["pkg", "application/octet-stream"], + ["so", "application/octet-stream"], + ["oda", "application/oda"], + ["opf", "application/oebps-package+xml"], + ["ogx", "application/ogg"], + ["omdoc", "application/omdoc+xml"], + ["onepkg", "application/onenote"], + ["onetmp", "application/onenote"], + ["onetoc", "application/onenote"], + ["onetoc2", "application/onenote"], + ["oxps", "application/oxps"], + ["relo", "application/p2p-overlay+xml"], + ["xer", "application/patch-ops-error+xml"], + ["pdf", "application/pdf"], + ["pgp", "application/pgp-encrypted"], + ["asc", "application/pgp-signature"], + ["sig", "application/pgp-signature"], + ["prf", "application/pics-rules"], + ["p10", "application/pkcs10"], + ["p7c", "application/pkcs7-mime"], + ["p7m", "application/pkcs7-mime"], + ["p7s", "application/pkcs7-signature"], + ["p8", "application/pkcs8"], + ["ac", "application/pkix-attr-cert"], + ["cer", "application/pkix-cert"], + ["crl", "application/pkix-crl"], + ["pkipath", "application/pkix-pkipath"], + ["pki", "application/pkixcmp"], + ["pls", "application/pls+xml"], + ["ai", "application/postscript"], + ["eps", "application/postscript"], + ["ps", "application/postscript"], + ["provx", "application/provenance+xml"], + ["pskcxml", "application/pskc+xml"], + ["raml", "application/raml+yaml"], + ["owl", "application/rdf+xml"], + ["rdf", "application/rdf+xml"], + ["rif", "application/reginfo+xml"], + ["rnc", "application/relax-ng-compact-syntax"], + ["rl", "application/resource-lists+xml"], + ["rld", "application/resource-lists-diff+xml"], + ["rs", "application/rls-services+xml"], + ["rapd", "application/route-apd+xml"], + ["sls", "application/route-s-tsid+xml"], + ["rusd", "application/route-usd+xml"], + ["gbr", "application/rpki-ghostbusters"], + ["mft", "application/rpki-manifest"], + ["roa", "application/rpki-roa"], + ["rsd", "application/rsd+xml"], + ["rss", "application/rss+xml"], + ["rtf", "application/rtf"], + ["sbml", "application/sbml+xml"], + ["scq", "application/scvp-cv-request"], + ["scs", "application/scvp-cv-response"], + ["spq", "application/scvp-vp-request"], + ["spp", "application/scvp-vp-response"], + ["sdp", "application/sdp"], + ["senmlx", "application/senml+xml"], + ["sensmlx", "application/sensml+xml"], + ["setpay", "application/set-payment-initiation"], + ["setreg", "application/set-registration-initiation"], + ["shf", "application/shf+xml"], + ["sieve", "application/sieve"], + ["siv", "application/sieve"], + ["smi", "application/smil+xml"], + ["smil", "application/smil+xml"], + ["rq", "application/sparql-query"], + ["srx", "application/sparql-results+xml"], + ["gram", "application/srgs"], + ["grxml", "application/srgs+xml"], + ["sru", "application/sru+xml"], + ["ssdl", "application/ssdl+xml"], + ["ssml", "application/ssml+xml"], + ["swidtag", "application/swid+xml"], + ["tei", "application/tei+xml"], + ["teicorpus", "application/tei+xml"], + ["tfi", "application/thraud+xml"], + ["tsd", "application/timestamped-data"], + ["toml", "application/toml"], + ["trig", "application/trig"], + ["ttml", "application/ttml+xml"], + ["ubj", "application/ubjson"], + ["rsheet", "application/urc-ressheet+xml"], + ["td", "application/urc-targetdesc+xml"], + ["vxml", "application/voicexml+xml"], + ["wasm", "application/wasm"], + ["wgt", "application/widget"], + ["hlp", "application/winhlp"], + ["wsdl", "application/wsdl+xml"], + ["wspolicy", "application/wspolicy+xml"], + ["xaml", "application/xaml+xml"], + ["xav", "application/xcap-att+xml"], + ["xca", "application/xcap-caps+xml"], + ["xdf", "application/xcap-diff+xml"], + ["xel", "application/xcap-el+xml"], + ["xns", "application/xcap-ns+xml"], + ["xenc", "application/xenc+xml"], + ["xht", "application/xhtml+xml"], + ["xhtml", "application/xhtml+xml"], + ["xlf", "application/xliff+xml"], + ["rng", "application/xml"], + ["xml", "application/xml"], + ["xsd", "application/xml"], + ["xsl", "application/xml"], + ["dtd", "application/xml-dtd"], + ["xop", "application/xop+xml"], + ["xpl", "application/xproc+xml"], + ["*xsl", "application/xslt+xml"], + ["xslt", "application/xslt+xml"], + ["xspf", "application/xspf+xml"], + ["mxml", "application/xv+xml"], + ["xhvml", "application/xv+xml"], + ["xvm", "application/xv+xml"], + ["xvml", "application/xv+xml"], + ["yang", "application/yang"], + ["yin", "application/yin+xml"], + ["zip", "application/zip"], + ["*3gpp", "audio/3gpp"], + ["adp", "audio/adpcm"], + ["amr", "audio/amr"], + ["au", "audio/basic"], + ["snd", "audio/basic"], + ["kar", "audio/midi"], + ["mid", "audio/midi"], + ["midi", "audio/midi"], + ["rmi", "audio/midi"], + ["mxmf", "audio/mobile-xmf"], + ["*mp3", "audio/mp3"], + ["m4a", "audio/mp4"], + ["mp4a", "audio/mp4"], + ["m2a", "audio/mpeg"], + ["m3a", "audio/mpeg"], + ["mp2", "audio/mpeg"], + ["mp2a", "audio/mpeg"], + ["mp3", "audio/mpeg"], + ["mpga", "audio/mpeg"], + ["oga", "audio/ogg"], + ["ogg", "audio/ogg"], + ["opus", "audio/ogg"], + ["spx", "audio/ogg"], + ["s3m", "audio/s3m"], + ["sil", "audio/silk"], + ["wav", "audio/wav"], + ["*wav", "audio/wave"], + ["weba", "audio/webm"], + ["xm", "audio/xm"], + ["ttc", "font/collection"], + ["otf", "font/otf"], + ["ttf", "font/ttf"], + ["woff", "font/woff"], + ["woff2", "font/woff2"], + ["exr", "image/aces"], + ["apng", "image/apng"], + ["avif", "image/avif"], + ["bmp", "image/bmp"], + ["cgm", "image/cgm"], + ["drle", "image/dicom-rle"], + ["emf", "image/emf"], + ["fits", "image/fits"], + ["g3", "image/g3fax"], + ["gif", "image/gif"], + ["heic", "image/heic"], + ["heics", "image/heic-sequence"], + ["heif", "image/heif"], + ["heifs", "image/heif-sequence"], + ["hej2", "image/hej2k"], + ["hsj2", "image/hsj2"], + ["ief", "image/ief"], + ["jls", "image/jls"], + ["jp2", "image/jp2"], + ["jpg2", "image/jp2"], + ["jpe", "image/jpeg"], + ["jpeg", "image/jpeg"], + ["jpg", "image/jpeg"], + ["jph", "image/jph"], + ["jhc", "image/jphc"], + ["jpm", "image/jpm"], + ["jpf", "image/jpx"], + ["jpx", "image/jpx"], + ["jxr", "image/jxr"], + ["jxra", "image/jxra"], + ["jxrs", "image/jxrs"], + ["jxs", "image/jxs"], + ["jxsc", "image/jxsc"], + ["jxsi", "image/jxsi"], + ["jxss", "image/jxss"], + ["ktx", "image/ktx"], + ["ktx2", "image/ktx2"], + ["png", "image/png"], + ["sgi", "image/sgi"], + ["svg", "image/svg+xml"], + ["svgz", "image/svg+xml"], + ["t38", "image/t38"], + ["tif", "image/tiff"], + ["tiff", "image/tiff"], + ["tfx", "image/tiff-fx"], + ["webp", "image/webp"], + ["wmf", "image/wmf"], + ["disposition-notification", "message/disposition-notification"], + ["u8msg", "message/global"], + ["u8dsn", "message/global-delivery-status"], + ["u8mdn", "message/global-disposition-notification"], + ["u8hdr", "message/global-headers"], + ["eml", "message/rfc822"], + ["mime", "message/rfc822"], + ["3mf", "model/3mf"], + ["gltf", "model/gltf+json"], + ["glb", "model/gltf-binary"], + ["iges", "model/iges"], + ["igs", "model/iges"], + ["mesh", "model/mesh"], + ["msh", "model/mesh"], + ["silo", "model/mesh"], + ["mtl", "model/mtl"], + ["obj", "model/obj"], + ["stpx", "model/step+xml"], + ["stpz", "model/step+zip"], + ["stpxz", "model/step-xml+zip"], + ["stl", "model/stl"], + ["vrml", "model/vrml"], + ["wrl", "model/vrml"], + ["*x3db", "model/x3d+binary"], + ["x3dbz", "model/x3d+binary"], + ["x3db", "model/x3d+fastinfoset"], + ["*x3dv", "model/x3d+vrml"], + ["x3dvz", "model/x3d+vrml"], + ["x3d", "model/x3d+xml"], + ["x3dz", "model/x3d+xml"], + ["x3dv", "model/x3d-vrml"], + ["appcache", "text/cache-manifest"], + ["manifest", "text/cache-manifest"], + ["ics", "text/calendar"], + ["ifb", "text/calendar"], + ["coffee", "text/coffeescript"], + ["litcoffee", "text/coffeescript"], + ["css", "text/css"], + ["csv", "text/csv"], + ["htm", "text/html"], + ["html", "text/html"], + ["shtml", "text/html"], + ["jade", "text/jade"], + ["jsx", "text/jsx"], + ["less", "text/less"], + ["markdown", "text/markdown"], + ["md", "text/markdown"], + ["mml", "text/mathml"], + ["mdx", "text/mdx"], + ["n3", "text/n3"], + ["conf", "text/plain"], + ["def", "text/plain"], + ["in", "text/plain"], + ["ini", "text/plain"], + ["list", "text/plain"], + ["log", "text/plain"], + ["text", "text/plain"], + ["txt", "text/plain"], + ["rtx", "text/richtext"], + ["*rtf", "text/rtf"], + ["sgm", "text/sgml"], + ["sgml", "text/sgml"], + ["shex", "text/shex"], + ["slim", "text/slim"], + ["slm", "text/slim"], + ["spdx", "text/spdx"], + ["styl", "text/stylus"], + ["stylus", "text/stylus"], + ["tsv", "text/tab-separated-values"], + ["man", "text/troff"], + ["me", "text/troff"], + ["ms", "text/troff"], + ["roff", "text/troff"], + ["t", "text/troff"], + ["tr", "text/troff"], + ["ttl", "text/turtle"], + ["uri", "text/uri-list"], + ["uris", "text/uri-list"], + ["urls", "text/uri-list"], + ["vcard", "text/vcard"], + ["vtt", "text/vtt"], + ["*xml", "text/xml"], + ["yaml", "text/yaml"], + ["yml", "text/yaml"], + ["3gp", "video/3gpp"], + ["3gpp", "video/3gpp"], + ["3g2", "video/3gpp2"], + ["h261", "video/h261"], + ["h263", "video/h263"], + ["h264", "video/h264"], + ["m4s", "video/iso.segment"], + ["jpgv", "video/jpeg"], + ["jpm", "video/jpm"], + ["jpgm", "video/jpm"], + ["mj2", "video/mj2"], + ["mjp2", "video/mj2"], + ["ts", "application/typescript"], + ["mp4", "video/mp4"], + ["mp4v", "video/mp4"], + ["mpg4", "video/mp4"], + ["m1v", "video/mpeg"], + ["m2v", "video/mpeg"], + ["mpe", "video/mpeg"], + ["mpeg", "video/mpeg"], + ["mpg", "video/mpeg"], + ["ogv", "video/ogg"], + ["mov", "video/quicktime"], + ["qt", "video/quicktime"], + ["webm", "video/webm"] + ]); + } +}); + +// packages/isomorphic/multimap.ts +var MultiMap; +var init_multimap = __esm({ + "packages/isomorphic/multimap.ts"() { + "use strict"; + MultiMap = class { + constructor() { + this._map = /* @__PURE__ */ new Map(); + } + set(key, value2) { + let values = this._map.get(key); + if (!values) { + values = []; + this._map.set(key, values); + } + values.push(value2); + } + get(key) { + return this._map.get(key) || []; + } + has(key) { + return this._map.has(key); + } + delete(key, value2) { + const values = this._map.get(key); + if (!values) + return; + if (values.includes(value2)) + this._map.set(key, values.filter((v) => value2 !== v)); + } + deleteAll(key) { + this._map.delete(key); + } + hasValue(key, value2) { + const values = this._map.get(key); + if (!values) + return false; + return values.includes(value2); + } + get size() { + return this._map.size; + } + [Symbol.iterator]() { + return this._map[Symbol.iterator](); + } + keys() { + return this._map.keys(); + } + values() { + const result2 = []; + for (const key of this.keys()) + result2.push(...this.get(key)); + return result2; + } + clear() { + this._map.clear(); + } + }; + } +}); + +// packages/isomorphic/protocolMetainfo.ts +function getMetainfo(metadata) { + return methodMetainfo.get(metadata.type + "." + metadata.method); +} +var methodMetainfo; +var init_protocolMetainfo = __esm({ + "packages/isomorphic/protocolMetainfo.ts"() { + "use strict"; + methodMetainfo = /* @__PURE__ */ new Map([ + ["Android.devices", { internal: true }], + ["AndroidSocket.write", { internal: true }], + ["AndroidSocket.close", { internal: true }], + ["AndroidDevice.wait", { title: "Wait" }], + ["AndroidDevice.fill", { title: 'Fill "{text}"' }], + ["AndroidDevice.tap", { title: "Tap" }], + ["AndroidDevice.drag", { title: "Drag" }], + ["AndroidDevice.fling", { title: "Fling" }], + ["AndroidDevice.longTap", { title: "Long tap" }], + ["AndroidDevice.pinchClose", { title: "Pinch close" }], + ["AndroidDevice.pinchOpen", { title: "Pinch open" }], + ["AndroidDevice.scroll", { title: "Scroll" }], + ["AndroidDevice.swipe", { title: "Swipe" }], + ["AndroidDevice.info", { internal: true }], + ["AndroidDevice.screenshot", { title: "Screenshot" }], + ["AndroidDevice.inputType", { title: "Type" }], + ["AndroidDevice.inputPress", { title: "Press" }], + ["AndroidDevice.inputTap", { title: "Tap" }], + ["AndroidDevice.inputSwipe", { title: "Swipe" }], + ["AndroidDevice.inputDrag", { title: "Drag" }], + ["AndroidDevice.launchBrowser", { title: "Launch browser" }], + ["AndroidDevice.open", { title: "Open app" }], + ["AndroidDevice.shell", { title: "Execute shell command", group: "configuration" }], + ["AndroidDevice.installApk", { title: "Install apk" }], + ["AndroidDevice.push", { title: "Push" }], + ["AndroidDevice.connectToWebView", { title: "Connect to Web View" }], + ["AndroidDevice.close", { internal: true }], + ["APIRequestContext.fetch", { title: '{method} "{url}"' }], + ["APIRequestContext.fetchResponseBody", { title: "Get response body", group: "getter" }], + ["APIRequestContext.fetchLog", { internal: true }], + ["APIRequestContext.storageState", { title: "Get storage state", group: "configuration" }], + ["APIRequestContext.disposeAPIResponse", { internal: true }], + ["APIRequestContext.dispose", { internal: true, potentiallyClosesScope: true }], + ["Artifact.pathAfterFinished", { internal: true }], + ["Artifact.saveAs", { internal: true }], + ["Artifact.saveAsStream", { internal: true }], + ["Artifact.failure", { internal: true }], + ["Artifact.stream", { internal: true }], + ["Artifact.cancel", { internal: true }], + ["Artifact.delete", { internal: true, potentiallyClosesScope: true }], + ["Stream.read", { internal: true }], + ["Stream.close", { internal: true }], + ["WritableStream.write", { internal: true }], + ["WritableStream.close", { internal: true }], + ["Browser.startServer", { title: "Start server" }], + ["Browser.stopServer", { title: "Stop server" }], + ["Browser.close", { title: "Close browser", pause: true, potentiallyClosesScope: true }], + ["Browser.killForTests", { internal: true, potentiallyClosesScope: true }], + ["Browser.defaultUserAgentForTest", { internal: true }], + ["Browser.newContext", { title: "Create context" }], + ["Browser.newContextForReuse", { internal: true }], + ["Browser.disconnectFromReusedContext", { internal: true }], + ["Browser.newBrowserCDPSession", { title: "Create CDP session", group: "configuration" }], + ["Browser.startTracing", { title: "Start browser tracing", group: "configuration" }], + ["Browser.stopTracing", { title: "Stop browser tracing", group: "configuration" }], + ["BrowserContext.addCookies", { title: "Add cookies", group: "configuration" }], + ["BrowserContext.addInitScript", { title: "Add init script", group: "configuration" }], + ["BrowserContext.clearCookies", { title: "Clear cookies", group: "configuration" }], + ["BrowserContext.clearPermissions", { title: "Clear permissions", group: "configuration" }], + ["BrowserContext.close", { title: "Close context", pause: true, potentiallyClosesScope: true }], + ["BrowserContext.cookies", { title: "Get cookies", group: "getter" }], + ["BrowserContext.exposeBinding", { title: "Expose binding", group: "configuration" }], + ["BrowserContext.grantPermissions", { title: "Grant permissions", group: "configuration" }], + ["BrowserContext.newPage", { title: "Create page" }], + ["BrowserContext.registerSelectorEngine", { internal: true }], + ["BrowserContext.setTestIdAttributeName", { internal: true }], + ["BrowserContext.setExtraHTTPHeaders", { title: "Set extra HTTP headers", group: "configuration" }], + ["BrowserContext.setGeolocation", { title: "Set geolocation", group: "configuration" }], + ["BrowserContext.setHTTPCredentials", { title: "Set HTTP credentials", group: "configuration" }], + ["BrowserContext.setNetworkInterceptionPatterns", { title: "Route requests", group: "route" }], + ["BrowserContext.setWebSocketInterceptionPatterns", { title: "Route WebSockets", group: "route" }], + ["BrowserContext.setOffline", { title: "Set offline mode" }], + ["BrowserContext.storageState", { title: "Get storage state", group: "configuration" }], + ["BrowserContext.setStorageState", { title: "Set storage state", group: "configuration" }], + ["BrowserContext.pause", { title: "Pause" }], + ["BrowserContext.enableRecorder", { internal: true }], + ["BrowserContext.disableRecorder", { internal: true }], + ["BrowserContext.exposeConsoleApi", { internal: true }], + ["BrowserContext.newCDPSession", { title: "Create CDP session", group: "configuration" }], + ["BrowserContext.createTempFiles", { internal: true }], + ["BrowserContext.updateSubscription", { internal: true }], + ["BrowserContext.clockFastForward", { title: 'Fast forward clock "{ticksNumber|ticksString}"' }], + ["BrowserContext.clockInstall", { title: 'Install clock "{timeNumber|timeString}"' }], + ["BrowserContext.clockPauseAt", { title: 'Pause clock "{timeNumber|timeString}"' }], + ["BrowserContext.clockResume", { title: "Resume clock" }], + ["BrowserContext.clockRunFor", { title: 'Run clock "{ticksNumber|ticksString}"' }], + ["BrowserContext.clockSetFixedTime", { title: 'Set fixed time "{timeNumber|timeString}"' }], + ["BrowserContext.clockSetSystemTime", { title: 'Set system time "{timeNumber|timeString}"' }], + ["BrowserContext.credentialsInstall", { title: "Install virtual WebAuthn authenticator", group: "configuration" }], + ["BrowserContext.credentialsCreate", { title: 'Create virtual credential for "{rpId}"', group: "configuration" }], + ["BrowserContext.credentialsGet", { title: "Get virtual credentials", group: "configuration" }], + ["BrowserContext.credentialsDelete", { title: "Delete virtual credential", group: "configuration" }], + ["BrowserType.launch", { title: "Launch browser" }], + ["BrowserType.launchPersistentContext", { title: "Launch persistent context" }], + ["BrowserType.connectOverCDP", { title: "Connect over CDP" }], + ["BrowserType.connectToWorker", { title: "Connect to worker" }], + ["Disposable.dispose", { internal: true, potentiallyClosesScope: true }], + ["Electron.launch", { title: "Launch electron" }], + ["ElectronApplication.browserWindow", { internal: true }], + ["ElectronApplication.evaluateExpression", { title: "Evaluate" }], + ["ElectronApplication.evaluateExpressionHandle", { title: "Evaluate" }], + ["ElectronApplication.updateSubscription", { internal: true }], + ["Frame.evalOnSelector", { title: "Evaluate", snapshot: true, pause: true }], + ["Frame.evalOnSelectorAll", { title: "Evaluate", snapshot: true, pause: true }], + ["Frame.addScriptTag", { title: "Add script tag", snapshot: true, pause: true }], + ["Frame.addStyleTag", { title: "Add style tag", snapshot: true, pause: true }], + ["Frame.ariaSnapshot", { title: "Aria snapshot", group: "getter" }], + ["Frame.blur", { title: "Blur", slowMo: true, snapshot: true, pause: true }], + ["Frame.check", { title: "Check", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }], + ["Frame.click", { title: "Click", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true, potentiallyClosesScope: true }], + ["Frame.content", { title: "Get content", snapshot: true, pause: true }], + ["Frame.dragAndDrop", { title: "Drag and drop", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }], + ["Frame.drop", { title: "Drop files or data onto an element", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }], + ["Frame.dblclick", { title: "Double click", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }], + ["Frame.dispatchEvent", { title: 'Dispatch "{type}"', slowMo: true, snapshot: true, pause: true }], + ["Frame.evaluateExpression", { title: "Evaluate", snapshot: true, pause: true }], + ["Frame.evaluateExpressionHandle", { title: "Evaluate", snapshot: true, pause: true }], + ["Frame.fill", { title: 'Fill "{value}"', slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }], + ["Frame.focus", { title: "Focus", slowMo: true, snapshot: true, pause: true }], + ["Frame.frameElement", { title: "Get frame element", group: "getter" }], + ["Frame.resolveSelector", { internal: true }], + ["Frame.highlight", { internal: true }], + ["Frame.hideHighlight", { internal: true }], + ["Frame.getAttribute", { title: 'Get attribute "{name}"', snapshot: true, pause: true, group: "getter" }], + ["Frame.goto", { title: 'Navigate to "{url}"', slowMo: true, snapshot: true, pause: true }], + ["Frame.hover", { title: "Hover", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }], + ["Frame.innerHTML", { title: "Get HTML", snapshot: true, pause: true, group: "getter" }], + ["Frame.innerText", { title: "Get inner text", snapshot: true, pause: true, group: "getter" }], + ["Frame.inputValue", { title: "Get input value", snapshot: true, pause: true, group: "getter" }], + ["Frame.isChecked", { title: "Is checked", snapshot: true, pause: true, group: "getter" }], + ["Frame.isDisabled", { title: "Is disabled", snapshot: true, pause: true, group: "getter" }], + ["Frame.isEnabled", { title: "Is enabled", snapshot: true, pause: true, group: "getter" }], + ["Frame.isHidden", { title: "Is hidden", snapshot: true, pause: true, group: "getter" }], + ["Frame.isVisible", { title: "Is visible", snapshot: true, pause: true, group: "getter" }], + ["Frame.isEditable", { title: "Is editable", snapshot: true, pause: true, group: "getter" }], + ["Frame.press", { title: 'Press "{key}"', slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }], + ["Frame.querySelector", { title: "Query selector", snapshot: true }], + ["Frame.querySelectorAll", { title: "Query selector all", snapshot: true }], + ["Frame.queryCount", { title: "Query count", snapshot: true, pause: true }], + ["Frame.selectOption", { title: "Select option", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }], + ["Frame.setContent", { title: "Set content", snapshot: true, pause: true }], + ["Frame.setInputFiles", { title: "Set input files", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }], + ["Frame.tap", { title: "Tap", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }], + ["Frame.textContent", { title: "Get text content", snapshot: true, pause: true, group: "getter" }], + ["Frame.title", { title: "Get page title", group: "getter" }], + ["Frame.type", { title: 'Type "{text}"', slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }], + ["Frame.uncheck", { title: "Uncheck", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }], + ["Frame.waitForTimeout", { title: "Wait for timeout", snapshot: true }], + ["Frame.waitForFunction", { title: "Wait for function", snapshot: true, pause: true }], + ["Frame.waitForSelector", { title: "Wait for selector", snapshot: true }], + ["Frame.expect", { title: 'Expect "{expression}"', snapshot: true, pause: true }], + ["JSHandle.dispose", { internal: true, potentiallyClosesScope: true }], + ["ElementHandle.dispose", { internal: true, potentiallyClosesScope: true }], + ["JSHandle.evaluateExpression", { title: "Evaluate", snapshot: true, pause: true }], + ["ElementHandle.evaluateExpression", { title: "Evaluate", snapshot: true, pause: true }], + ["JSHandle.evaluateExpressionHandle", { title: "Evaluate", snapshot: true, pause: true }], + ["ElementHandle.evaluateExpressionHandle", { title: "Evaluate", snapshot: true, pause: true }], + ["JSHandle.getPropertyList", { title: "Get property list", group: "getter" }], + ["ElementHandle.getPropertyList", { title: "Get property list", group: "getter" }], + ["JSHandle.getProperty", { title: "Get JS property", group: "getter" }], + ["ElementHandle.getProperty", { title: "Get JS property", group: "getter" }], + ["JSHandle.jsonValue", { title: "Get JSON value", group: "getter" }], + ["ElementHandle.jsonValue", { title: "Get JSON value", group: "getter" }], + ["ElementHandle.evalOnSelector", { title: "Evaluate", snapshot: true, pause: true }], + ["ElementHandle.evalOnSelectorAll", { title: "Evaluate", snapshot: true, pause: true }], + ["ElementHandle.boundingBox", { title: "Get bounding box", snapshot: true, pause: true }], + ["ElementHandle.check", { title: "Check", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }], + ["ElementHandle.click", { title: "Click", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }], + ["ElementHandle.contentFrame", { title: "Get content frame", group: "getter" }], + ["ElementHandle.dblclick", { title: "Double click", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }], + ["ElementHandle.dispatchEvent", { title: "Dispatch event", slowMo: true, snapshot: true, pause: true }], + ["ElementHandle.fill", { title: 'Fill "{value}"', slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }], + ["ElementHandle.focus", { title: "Focus", slowMo: true, snapshot: true, pause: true }], + ["ElementHandle.getAttribute", { title: "Get attribute", snapshot: true, pause: true, group: "getter" }], + ["ElementHandle.hover", { title: "Hover", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }], + ["ElementHandle.innerHTML", { title: "Get HTML", snapshot: true, pause: true, group: "getter" }], + ["ElementHandle.innerText", { title: "Get inner text", snapshot: true, pause: true, group: "getter" }], + ["ElementHandle.inputValue", { title: "Get input value", snapshot: true, pause: true, group: "getter" }], + ["ElementHandle.isChecked", { title: "Is checked", snapshot: true, pause: true, group: "getter" }], + ["ElementHandle.isDisabled", { title: "Is disabled", snapshot: true, pause: true, group: "getter" }], + ["ElementHandle.isEditable", { title: "Is editable", snapshot: true, pause: true, group: "getter" }], + ["ElementHandle.isEnabled", { title: "Is enabled", snapshot: true, pause: true, group: "getter" }], + ["ElementHandle.isHidden", { title: "Is hidden", snapshot: true, pause: true, group: "getter" }], + ["ElementHandle.isVisible", { title: "Is visible", snapshot: true, pause: true, group: "getter" }], + ["ElementHandle.ownerFrame", { title: "Get owner frame", group: "getter" }], + ["ElementHandle.press", { title: 'Press "{key}"', slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }], + ["ElementHandle.querySelector", { title: "Query selector", snapshot: true }], + ["ElementHandle.querySelectorAll", { title: "Query selector all", snapshot: true }], + ["ElementHandle.screenshot", { title: "Screenshot", snapshot: true, pause: true }], + ["ElementHandle.scrollIntoViewIfNeeded", { title: "Scroll into view", slowMo: true, snapshot: true, pause: true }], + ["ElementHandle.selectOption", { title: "Select option", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }], + ["ElementHandle.selectText", { title: "Select text", slowMo: true, snapshot: true, pause: true }], + ["ElementHandle.setInputFiles", { title: "Set input files", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }], + ["ElementHandle.tap", { title: "Tap", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }], + ["ElementHandle.textContent", { title: "Get text content", snapshot: true, pause: true, group: "getter" }], + ["ElementHandle.type", { title: "Type", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }], + ["ElementHandle.uncheck", { title: "Uncheck", slowMo: true, snapshot: true, pause: true, input: true, isAutoWaiting: true }], + ["ElementHandle.waitForElementState", { title: "Wait for state", snapshot: true, pause: true }], + ["ElementHandle.waitForSelector", { title: "Wait for selector", snapshot: true }], + ["LocalUtils.zip", { internal: true }], + ["LocalUtils.harOpen", { internal: true }], + ["LocalUtils.harLookup", { internal: true }], + ["LocalUtils.harClose", { internal: true }], + ["LocalUtils.harUnzip", { internal: true }], + ["LocalUtils.connect", { internal: true }], + ["LocalUtils.tracingStarted", { internal: true }], + ["LocalUtils.addStackToTracingNoReply", { internal: true }], + ["LocalUtils.traceDiscarded", { internal: true }], + ["LocalUtils.globToRegex", { internal: true }], + ["Request.response", { internal: true }], + ["Request.rawRequestHeaders", { internal: true }], + ["Route.redirectNavigationRequest", { internal: true }], + ["Route.abort", { title: "Abort request", group: "route" }], + ["Route.continue", { title: "Continue request", group: "route" }], + ["Route.fulfill", { title: "Fulfill request", group: "route" }], + ["WebSocketRoute.connect", { title: "Connect WebSocket to server", group: "route" }], + ["WebSocketRoute.ensureOpened", { internal: true }], + ["WebSocketRoute.sendToPage", { title: "Send WebSocket message", group: "route" }], + ["WebSocketRoute.sendToServer", { title: "Send WebSocket message", group: "route" }], + ["WebSocketRoute.closePage", { internal: true }], + ["WebSocketRoute.closeServer", { internal: true }], + ["Response.body", { title: "Get response body", group: "getter" }], + ["Response.securityDetails", { internal: true }], + ["Response.serverAddr", { internal: true }], + ["Response.rawResponseHeaders", { internal: true }], + ["Response.httpVersion", { internal: true }], + ["Response.sizes", { internal: true }], + ["Page.addInitScript", { title: "Add init script", group: "configuration" }], + ["Page.close", { title: "Close page", pause: true, potentiallyClosesScope: true }], + ["Page.runBeforeUnload", { title: "Run beforeunload", pause: true }], + ["Page.clearConsoleMessages", { title: "Clear console messages" }], + ["Page.consoleMessages", { title: "Get console messages", group: "getter" }], + ["Page.emulateMedia", { title: "Emulate media", snapshot: true, pause: true }], + ["Page.exposeBinding", { title: "Expose binding", group: "configuration" }], + ["Page.goBack", { title: "Go back", slowMo: true, snapshot: true, pause: true }], + ["Page.goForward", { title: "Go forward", slowMo: true, snapshot: true, pause: true }], + ["Page.requestGC", { title: "Request garbage collection", group: "configuration" }], + ["Page.registerLocatorHandler", { title: "Register locator handler" }], + ["Page.resolveLocatorHandlerNoReply", { internal: true }], + ["Page.unregisterLocatorHandler", { title: "Unregister locator handler" }], + ["Page.reload", { title: "Reload", slowMo: true, snapshot: true, pause: true }], + ["Page.expectScreenshot", { title: "Expect screenshot", snapshot: true, pause: true }], + ["Page.screenshot", { title: "Screenshot", snapshot: true, pause: true }], + ["Page.setExtraHTTPHeaders", { title: "Set extra HTTP headers", group: "configuration" }], + ["Page.setNetworkInterceptionPatterns", { title: "Route requests", group: "route" }], + ["Page.setWebSocketInterceptionPatterns", { title: "Route WebSockets", group: "route" }], + ["Page.setViewportSize", { title: "Set viewport size", snapshot: true, pause: true }], + ["Page.keyboardDown", { title: 'Key down "{key}"', slowMo: true, snapshot: true, pause: true, input: true }], + ["Page.keyboardUp", { title: 'Key up "{key}"', slowMo: true, snapshot: true, pause: true, input: true }], + ["Page.keyboardInsertText", { title: 'Insert "{text}"', slowMo: true, snapshot: true, pause: true, input: true }], + ["Page.keyboardType", { title: 'Type "{text}"', slowMo: true, snapshot: true, pause: true, input: true }], + ["Page.keyboardPress", { title: 'Press "{key}"', slowMo: true, snapshot: true, pause: true, input: true }], + ["Page.mouseMove", { title: "Mouse move", slowMo: true, snapshot: true, pause: true, input: true }], + ["Page.mouseDown", { title: "Mouse down", slowMo: true, snapshot: true, pause: true, input: true }], + ["Page.mouseUp", { title: "Mouse up", slowMo: true, snapshot: true, pause: true, input: true }], + ["Page.mouseClick", { title: "Click", slowMo: true, snapshot: true, pause: true, input: true }], + ["Page.mouseWheel", { title: "Mouse wheel", slowMo: true, snapshot: true, pause: true, input: true }], + ["Page.touchscreenTap", { title: "Tap", slowMo: true, snapshot: true, pause: true, input: true }], + ["Page.clearPageErrors", { title: "Clear page errors" }], + ["Page.pageErrors", { title: "Get page errors", group: "getter" }], + ["Page.pdf", { title: "PDF" }], + ["Page.requests", { title: "Get network requests", group: "getter" }], + ["Page.startJSCoverage", { title: "Start JS coverage", group: "configuration" }], + ["Page.stopJSCoverage", { title: "Stop JS coverage", group: "configuration" }], + ["Page.startCSSCoverage", { title: "Start CSS coverage", group: "configuration" }], + ["Page.stopCSSCoverage", { title: "Stop CSS coverage", group: "configuration" }], + ["Page.bringToFront", { title: "Bring to front" }], + ["Page.pickLocator", { title: "Pick locator", group: "configuration" }], + ["Page.cancelPickLocator", { title: "Cancel pick locator", group: "configuration" }], + ["Page.hideHighlight", { title: "Hide all element highlights", group: "configuration" }], + ["Page.screencastShowOverlay", { title: "Show overlay", group: "configuration" }], + ["Page.screencastRemoveOverlay", { title: "Remove overlay", group: "configuration" }], + ["Page.screencastChapter", { title: "Show chapter overlay", group: "configuration" }], + ["Page.screencastSetOverlayVisible", { title: "Set overlay visibility", group: "configuration" }], + ["Page.screencastShowActions", { title: "Show actions", group: "configuration" }], + ["Page.screencastHideActions", { title: "Remove actions", group: "configuration" }], + ["Page.screencastStart", { title: "Start screencast", group: "configuration" }], + ["Page.screencastStop", { title: "Stop screencast", group: "configuration" }], + ["Page.updateSubscription", { internal: true }], + ["Page.setDockTile", { internal: true }], + ["Page.webStorageItems", { title: "Get WebStorage items", group: "getter" }], + ["Page.webStorageGetItem", { title: "Get WebStorage item", group: "getter" }], + ["Page.webStorageSetItem", { title: "Set WebStorage item", group: "configuration" }], + ["Page.webStorageRemoveItem", { title: "Remove WebStorage item", group: "configuration" }], + ["Page.webStorageClear", { title: "Clear WebStorage", group: "configuration" }], + ["Root.initialize", { internal: true }], + ["Playwright.newRequest", { title: "Create request context" }], + ["DebugController.initialize", { internal: true }], + ["DebugController.setReportStateChanged", { internal: true }], + ["DebugController.setRecorderMode", { internal: true }], + ["DebugController.highlight", { internal: true }], + ["DebugController.hideHighlight", { internal: true }], + ["DebugController.resume", { internal: true }], + ["DebugController.kill", { internal: true }], + ["SocksSupport.socksConnected", { internal: true }], + ["SocksSupport.socksFailed", { internal: true }], + ["SocksSupport.socksData", { internal: true }], + ["SocksSupport.socksError", { internal: true }], + ["SocksSupport.socksEnd", { internal: true }], + ["JsonPipe.send", { internal: true }], + ["JsonPipe.close", { internal: true }], + ["CDPSession.send", { title: "Send CDP command", group: "configuration" }], + ["CDPSession.detach", { title: "Detach CDP session", potentiallyClosesScope: true, group: "configuration" }], + ["BindingCall.reject", { internal: true }], + ["BindingCall.resolve", { internal: true }], + ["Debugger.requestPause", { title: "Pause on next call", group: "configuration" }], + ["Debugger.resume", { title: "Resume", group: "configuration" }], + ["Debugger.next", { title: "Step to next call", group: "configuration" }], + ["Debugger.runTo", { title: "Run to location", group: "configuration" }], + ["Dialog.accept", { title: "Accept dialog" }], + ["Dialog.dismiss", { title: "Dismiss dialog" }], + ["Tracing.tracingStart", { title: "Start tracing", group: "configuration" }], + ["Tracing.tracingStartChunk", { title: "Start tracing", group: "configuration" }], + ["Tracing.tracingGroup", { title: 'Trace "{name}"' }], + ["Tracing.tracingGroupEnd", { title: "Group end" }], + ["Tracing.tracingStopChunk", { title: "Stop tracing", group: "configuration" }], + ["Tracing.tracingStop", { title: "Stop tracing", group: "configuration" }], + ["Tracing.harStart", { internal: true }], + ["Tracing.harExport", { internal: true }], + ["Worker.disconnect", { title: "Disconnect from worker", potentiallyClosesScope: true }], + ["Worker.evaluateExpression", { title: "Evaluate" }], + ["Worker.evaluateExpressionHandle", { title: "Evaluate" }], + ["Worker.updateSubscription", { internal: true }] + ]); + } +}); + +// packages/isomorphic/protocolFormatter.ts +function formatProtocolParam(params2, alternatives) { + return _formatProtocolParam(params2, alternatives)?.replaceAll("\n", "\\n"); +} +function _formatProtocolParam(params2, alternatives) { + if (!params2) + return void 0; + for (const name of alternatives.split("|")) { + if (name === "url") { + try { + const urlObject = new URL(params2[name]); + if (urlObject.protocol === "data:") + return urlObject.protocol; + if (["about:", "chrome:", "edge:"].includes(urlObject.protocol)) + return params2[name]; + return urlObject.pathname + urlObject.search; + } catch (error) { + if (params2[name] !== void 0) + return params2[name]; + } + } + if (name === "timeNumber" && params2[name] !== void 0) { + return new Date(params2[name]).toString(); + } + const value2 = deepParam(params2, name); + if (value2 !== void 0) + return value2; + } +} +function deepParam(params2, name) { + const tokens = name.split("."); + let current = params2; + for (const token of tokens) { + if (typeof current !== "object" || current === null) + return void 0; + current = current[token]; + } + if (current === void 0) + return void 0; + return String(current); +} +function renderTitleForCall(metadata) { + const titleFormat = metadata.title ?? getMetainfo(metadata)?.title ?? metadata.method; + return titleFormat.replace(/\{([^}]+)\}/g, (fullMatch, p1) => { + return formatProtocolParam(metadata.params, p1) ?? fullMatch; + }); +} +function getActionGroup(metadata) { + return getMetainfo(metadata)?.group; +} +var init_protocolFormatter = __esm({ + "packages/isomorphic/protocolFormatter.ts"() { + "use strict"; + init_protocolMetainfo(); + } +}); + +// packages/isomorphic/semaphore.ts +var Semaphore; +var init_semaphore = __esm({ + "packages/isomorphic/semaphore.ts"() { + "use strict"; + init_manualPromise(); + Semaphore = class { + constructor(max) { + this._acquired = 0; + this._queue = []; + this._max = max; + } + setMax(max) { + this._max = max; + } + acquire() { + const lock2 = new ManualPromise(); + this._queue.push(lock2); + this._flush(); + return lock2; + } + release() { + --this._acquired; + this._flush(); + } + _flush() { + while (this._acquired < this._max && this._queue.length) { + ++this._acquired; + this._queue.shift().resolve(); + } + } + }; + } +}); + +// packages/isomorphic/formatUtils.ts +function msToString(ms) { + if (ms < 0 || !isFinite(ms)) + return "-"; + if (ms === 0) + return "0ms"; + if (ms < 1e3) + return ms.toFixed(0) + "ms"; + const seconds = ms / 1e3; + if (seconds < 60) + return seconds.toFixed(1) + "s"; + const minutes = seconds / 60; + if (minutes < 60) + return minutes.toFixed(1) + "m"; + const hours = minutes / 60; + if (hours < 24) + return hours.toFixed(1) + "h"; + const days = hours / 24; + return days.toFixed(1) + "d"; +} +function bytesToString(bytes) { + if (bytes < 0 || !isFinite(bytes)) + return "-"; + if (bytes === 0) + return "0"; + if (bytes < 1e3) + return bytes.toFixed(0); + const kb = bytes / 1024; + if (kb < 1e3) + return kb.toFixed(1) + "K"; + const mb = kb / 1024; + if (mb < 1e3) + return mb.toFixed(1) + "M"; + const gb = mb / 1024; + return gb.toFixed(1) + "G"; +} +var init_formatUtils = __esm({ + "packages/isomorphic/formatUtils.ts"() { + "use strict"; + } +}); + +// packages/isomorphic/time.ts +function setTimeOrigin(origin) { + _timeOrigin = origin; + _timeShift = performance.timeOrigin - origin; +} +function timeOrigin() { + return _timeOrigin; +} +function monotonicTime() { + return Math.floor((performance.now() + _timeShift) * 1e3) / 1e3; +} +var _timeOrigin, _timeShift, DEFAULT_PLAYWRIGHT_TIMEOUT, DEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT; +var init_time = __esm({ + "packages/isomorphic/time.ts"() { + "use strict"; + _timeOrigin = performance.timeOrigin; + _timeShift = 0; + DEFAULT_PLAYWRIGHT_TIMEOUT = 3e4; + DEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT = 3 * 60 * 1e3; + } +}); + +// packages/isomorphic/timeoutRunner.ts +async function raceAgainstDeadline(cb, deadline) { + let timer; + return await Promise.race([ + cb().then((result2) => { + return { result: result2, timedOut: false }; + }), + new Promise((resolve) => { + if (!deadline) + return; + timer = setTimeout(() => resolve({ timedOut: true }), deadline - monotonicTime()); + }) + ]).finally(() => { + clearTimeout(timer); + }); +} +async function pollAgainstDeadline(callback, deadline, pollIntervals = [100, 250, 500, 1e3]) { + const lastPollInterval = pollIntervals.pop() ?? 1e3; + let lastResult; + const wrappedCallback = () => Promise.resolve().then(callback); + while (true) { + const time = monotonicTime(); + if (deadline && time >= deadline) + break; + const received = await raceAgainstDeadline(wrappedCallback, deadline); + if (received.timedOut) + break; + lastResult = received.result.result; + if (!received.result.continuePolling) + return { result: lastResult, timedOut: false }; + const interval = pollIntervals.shift() ?? lastPollInterval; + if (deadline && deadline <= monotonicTime() + interval) + break; + await new Promise((x) => setTimeout(x, interval)); + } + return { timedOut: true, result: lastResult }; +} +var init_timeoutRunner = __esm({ + "packages/isomorphic/timeoutRunner.ts"() { + "use strict"; + init_time(); + } +}); + +// packages/isomorphic/trace/snapshotServer.ts +function removeHash(url2) { + try { + const u = new URL(url2); + u.hash = ""; + return u.toString(); + } catch (e) { + return url2; + } +} +var SnapshotServer; +var init_snapshotServer = __esm({ + "packages/isomorphic/trace/snapshotServer.ts"() { + "use strict"; + SnapshotServer = class { + constructor(snapshotStorage, resourceLoader) { + this._snapshotIds = /* @__PURE__ */ new Map(); + this._snapshotStorage = snapshotStorage; + this._resourceLoader = resourceLoader; + } + serveSnapshot(pageOrFrameId, searchParams, snapshotUrl) { + const snapshot3 = this._snapshot(pageOrFrameId, searchParams); + if (!snapshot3) + return new Response(null, { status: 404 }); + const renderedSnapshot = snapshot3.render(); + this._snapshotIds.set(snapshotUrl, snapshot3); + return new Response(renderedSnapshot.html, { status: 200, headers: { "Content-Type": "text/html; charset=utf-8" } }); + } + async serveClosestScreenshot(pageOrFrameId, searchParams) { + const snapshot3 = this._snapshot(pageOrFrameId, searchParams); + const sha1 = snapshot3?.closestScreenshot(); + if (!sha1) + return new Response(null, { status: 404 }); + return new Response(await this._resourceLoader(sha1)); + } + serveSnapshotInfo(pageOrFrameId, searchParams) { + const snapshot3 = this._snapshot(pageOrFrameId, searchParams); + return this._respondWithJson(snapshot3 ? { + viewport: snapshot3.viewport(), + url: snapshot3.snapshot().frameUrl, + timestamp: snapshot3.snapshot().timestamp, + wallTime: snapshot3.snapshot().wallTime + } : { + error: "No snapshot found" + }); + } + _snapshot(pageOrFrameId, params2) { + const name = params2.get("name"); + return this._snapshotStorage.snapshotByName(pageOrFrameId, name); + } + _respondWithJson(object) { + return new Response(JSON.stringify(object), { + status: 200, + headers: { + "Cache-Control": "public, max-age=31536000", + "Content-Type": "application/json" + } + }); + } + async serveResource(requestUrlAlternatives, method, snapshotUrl) { + let resource; + const snapshot3 = this._snapshotIds.get(snapshotUrl); + for (const requestUrl of requestUrlAlternatives) { + resource = snapshot3?.resourceByUrl(removeHash(requestUrl), method); + if (resource) + break; + } + if (!resource) + return new Response(null, { status: 404 }); + const sha1 = resource.response.content._sha1; + const content = sha1 ? await this._resourceLoader(sha1) || new Blob([]) : new Blob([]); + let contentType = resource.response.content.mimeType; + const isTextEncoding = /^text\/|^application\/(javascript|json)/.test(contentType); + if (isTextEncoding && !contentType.includes("charset")) + contentType = `${contentType}; charset=utf-8`; + const headers = new Headers(); + if (contentType !== "x-unknown") + headers.set("Content-Type", contentType); + for (const { name, value: value2 } of resource.response.headers) + headers.set(name, value2); + headers.delete("Content-Encoding"); + headers.delete("Access-Control-Allow-Origin"); + headers.set("Access-Control-Allow-Origin", "*"); + headers.delete("Content-Length"); + headers.set("Content-Length", String(content.size)); + if (this._snapshotStorage.hasResourceOverride(resource.request.url)) + headers.set("Cache-Control", "no-store, no-cache, max-age=0"); + else + headers.set("Cache-Control", "public, max-age=31536000"); + const { status } = resource.response; + const isNullBodyStatus = status === 101 || status === 204 || status === 205 || status === 304; + return new Response(isNullBodyStatus ? null : content, { + headers, + status: resource.response.status, + statusText: resource.response.statusText + }); + } + }; + } +}); + +// packages/isomorphic/urlMatch.ts +function isHttpUrl(url2, base) { + try { + return ["http:", "https:"].includes(new URL(url2, base).protocol); + } catch { + return false; + } +} +function globToRegexPattern(glob) { + const tokens = ["^"]; + let inGroup = false; + for (let i = 0; i < glob.length; ++i) { + const c = glob[i]; + if (c === "\\" && i + 1 < glob.length) { + const char = glob[++i]; + tokens.push(escapedChars.has(char) ? "\\" + char : char); + continue; + } + if (c === "*") { + const charBefore = glob[i - 1]; + let starCount = 1; + while (glob[i + 1] === "*") { + starCount++; + i++; + } + if (starCount > 1) { + const charAfter = glob[i + 1]; + if (charAfter === "/") { + if (charBefore === "/") + tokens.push("((.+/)|)"); + else + tokens.push("(.*/)"); + ++i; + } else { + tokens.push("(.*)"); + } + } else { + tokens.push("([^/]*)"); + } + continue; + } + switch (c) { + case "{": + if (inGroup) + throw new Error(`Invalid glob pattern ${JSON.stringify(glob)}: nested '{' is not supported`); + inGroup = true; + tokens.push("("); + break; + case "}": + if (!inGroup) + throw new Error(`Invalid glob pattern ${JSON.stringify(glob)}: unmatched '}'`); + inGroup = false; + tokens.push(")"); + break; + case ",": + if (inGroup) { + tokens.push("|"); + break; + } + tokens.push("\\" + c); + break; + default: + tokens.push(escapedChars.has(c) ? "\\" + c : c); + } + } + if (inGroup) + throw new Error(`Invalid glob pattern ${JSON.stringify(glob)}: unmatched '{'`); + tokens.push("$"); + return tokens.join(""); +} +function isRegExp3(obj) { + return obj instanceof RegExp || Object.prototype.toString.call(obj) === "[object RegExp]"; +} +function serializeURLPattern(v) { + return { + hash: v.hash, + hostname: v.hostname, + password: v.password, + pathname: v.pathname, + port: v.port, + protocol: v.protocol, + search: v.search, + username: v.username + }; +} +function serializeURLMatch(match) { + if (isString(match)) + return { glob: match }; + if (isRegExp3(match)) + return { regexSource: match.source, regexFlags: match.flags }; + if (isURLPattern(match)) + return { urlPattern: serializeURLPattern(match) }; + return void 0; +} +function deserializeURLPattern(v) { + if (typeof globalThis.URLPattern !== "function") + return () => true; + return new globalThis.URLPattern({ + hash: v.hash, + hostname: v.hostname, + password: v.password, + pathname: v.pathname, + port: v.port, + protocol: v.protocol, + search: v.search, + username: v.username + }); +} +function deserializeURLMatch(match) { + if (match.regexSource) + return new RegExp(match.regexSource, match.regexFlags); + if (match.urlPattern) + return deserializeURLPattern(match.urlPattern); + return match.glob; +} +function urlMatchesEqual(match1, match2) { + if (isRegExp3(match1) && isRegExp3(match2)) + return match1.source === match2.source && match1.flags === match2.flags; + return match1 === match2; +} +function urlMatches(baseURL, urlString, match, webSocketUrl) { + if (match === void 0 || match === "") + return true; + if (isString(match)) + match = new RegExp(resolveGlobToRegexPattern(baseURL, match, webSocketUrl)); + if (isRegExp3(match)) { + const r = match.test(urlString); + return r; + } + const url2 = parseURL(urlString); + if (!url2) + return false; + if (isURLPattern(match)) + return match.test(url2.href); + if (typeof match !== "function") + throw new Error("url parameter should be string, RegExp, URLPattern or function"); + return match(url2); +} +function resolveGlobToRegexPattern(baseURL, glob, webSocketUrl) { + if (webSocketUrl) + baseURL = toWebSocketBaseUrl(baseURL); + glob = resolveGlobBase(baseURL, glob); + return globToRegexPattern(glob); +} +function toWebSocketBaseUrl(baseURL) { + if (baseURL && /^https?:\/\//.test(baseURL)) + baseURL = baseURL.replace(/^http/, "ws"); + return baseURL; +} +function resolveGlobBase(baseURL, match) { + if (!match.startsWith("*")) { + let mapToken2 = function(original, replacement) { + if (original.length === 0) + return ""; + tokenMap.set(replacement, original); + return replacement; + }; + var mapToken = mapToken2; + const tokenMap = /* @__PURE__ */ new Map(); + match = match.replaceAll(/\\\\\?/g, "?"); + if (match.startsWith("about:") || match.startsWith("data:") || match.startsWith("chrome:") || match.startsWith("edge:") || match.startsWith("file:")) + return match; + const relativePath = match.split("/").map((token, index) => { + if (token === "." || token === ".." || token === "") + return token; + if (index === 0 && token.endsWith(":")) { + if (token.indexOf("*") !== -1 || token.indexOf("{") !== -1) + return mapToken2(token, "http:"); + return token; + } + const questionIndex = token.indexOf("?"); + if (questionIndex === -1) + return mapToken2(token, `$_${index}_$`); + const newPrefix = mapToken2(token.substring(0, questionIndex), `$_${index}_$`); + const newSuffix = mapToken2(token.substring(questionIndex), `?$_${index}_$`); + return newPrefix + newSuffix; + }).join("/"); + const result2 = resolveBaseURL(baseURL, relativePath); + let resolved = result2.resolved; + for (const [token, original] of tokenMap) { + const normalize = result2.caseInsensitivePart?.includes(token); + resolved = resolved.replace(token, normalize ? original.toLowerCase() : original); + } + match = resolved; + } + return match; +} +function parseURL(url2) { + try { + return new URL(url2); + } catch (e) { + return null; + } +} +function constructURLBasedOnBaseURL(baseURL, givenURL) { + try { + return resolveBaseURL(baseURL, givenURL).resolved; + } catch (e) { + return givenURL; + } +} +function resolveBaseURL(baseURL, givenURL) { + try { + const url2 = new URL(givenURL, baseURL); + const resolved = url2.toString(); + const caseInsensitivePrefix = url2.origin; + return { resolved, caseInsensitivePart: caseInsensitivePrefix }; + } catch (e) { + return { resolved: givenURL }; + } +} +var escapedChars, isURLPattern; +var init_urlMatch = __esm({ + "packages/isomorphic/urlMatch.ts"() { + "use strict"; + init_stringUtils(); + escapedChars = /* @__PURE__ */ new Set(["$", "^", "+", ".", "*", "(", ")", "|", "\\", "?", "{", "}", "[", "]"]); + isURLPattern = (v) => typeof globalThis.URLPattern === "function" && v instanceof globalThis.URLPattern; + } +}); + +// packages/isomorphic/locatorUtils.ts +function getByAttributeTextSelector(attrName, text2, options) { + return `internal:attr=[${attrName}=${escapeForAttributeSelector(text2, options?.exact || false)}]`; +} +function encodeTestIdAttributeName(testIdAttributeName2) { + return testIdAttributeName2.includes(",") ? JSON.stringify(testIdAttributeName2) : testIdAttributeName2; +} +function getByTestIdSelector(testIdAttributeName2, testId) { + return `internal:testid=[${encodeTestIdAttributeName(testIdAttributeName2)}=${escapeForAttributeSelector(testId, true)}]`; +} +function getByLabelSelector(text2, options) { + return "internal:label=" + escapeForTextSelector(text2, !!options?.exact); +} +function getByAltTextSelector(text2, options) { + return getByAttributeTextSelector("alt", text2, options); +} +function getByTitleSelector(text2, options) { + return getByAttributeTextSelector("title", text2, options); +} +function getByPlaceholderSelector(text2, options) { + return getByAttributeTextSelector("placeholder", text2, options); +} +function getByTextSelector(text2, options) { + return "internal:text=" + escapeForTextSelector(text2, !!options?.exact); +} +function getByRoleSelector(role, options = {}) { + const props = []; + if (options.checked !== void 0) + props.push(["checked", String(options.checked)]); + if (options.disabled !== void 0) + props.push(["disabled", String(options.disabled)]); + if (options.selected !== void 0) + props.push(["selected", String(options.selected)]); + if (options.expanded !== void 0) + props.push(["expanded", String(options.expanded)]); + if (options.includeHidden !== void 0) + props.push(["include-hidden", String(options.includeHidden)]); + if (options.level !== void 0) + props.push(["level", String(options.level)]); + if (options.name !== void 0) + props.push(["name", escapeForAttributeSelector(options.name, !!options.exact)]); + if (options.description !== void 0) + props.push(["description", escapeForAttributeSelector(options.description, !!options.exact)]); + if (options.pressed !== void 0) + props.push(["pressed", String(options.pressed)]); + return `internal:role=${role}${props.map(([n, v]) => `[${n}=${v}]`).join("")}`; +} +var init_locatorUtils = __esm({ + "packages/isomorphic/locatorUtils.ts"() { + "use strict"; + init_stringUtils(); + } +}); + +// packages/isomorphic/locatorParser.ts +function parseLocator(locator2, testIdAttributeName2) { + locator2 = locator2.replace(/AriaRole\s*\.\s*([\w]+)/g, (_, group) => group.toLowerCase()).replace(/(get_by_role|getByRole)\s*\(\s*(?:["'`])([^'"`]+)['"`]/g, (_, group1, group2) => `${group1}(${group2.toLowerCase()}`); + const params2 = []; + let template = ""; + for (let i = 0; i < locator2.length; ++i) { + const quote5 = locator2[i]; + if (quote5 !== '"' && quote5 !== "'" && quote5 !== "`" && quote5 !== "/") { + template += quote5; + continue; + } + const isRegexEscaping = locator2[i - 1] === "r" || locator2[i] === "/"; + ++i; + let text2 = ""; + while (i < locator2.length) { + if (locator2[i] === "\\") { + if (isRegexEscaping) { + if (locator2[i + 1] !== quote5) + text2 += locator2[i]; + ++i; + text2 += locator2[i]; + } else { + ++i; + if (locator2[i] === "n") + text2 += "\n"; + else if (locator2[i] === "r") + text2 += "\r"; + else if (locator2[i] === "t") + text2 += " "; + else + text2 += locator2[i]; + } + ++i; + continue; + } + if (locator2[i] !== quote5) { + text2 += locator2[i++]; + continue; + } + break; + } + params2.push({ quote: quote5, text: text2 }); + template += (quote5 === "/" ? "r" : "") + "$" + params2.length; + } + template = template.toLowerCase().replace(/get_by_alt_text/g, "getbyalttext").replace(/get_by_test_id/g, "getbytestid").replace(/get_by_([\w]+)/g, "getby$1").replace(/has_not_text/g, "hasnottext").replace(/has_text/g, "hastext").replace(/has_not/g, "hasnot").replace(/frame_locator/g, "framelocator").replace(/content_frame/g, "contentframe").replace(/[{}\s]/g, "").replace(/new\(\)/g, "").replace(/new[\w]+\.[\w]+options\(\)/g, "").replace(/\.set/g, ",set").replace(/\.or_\(/g, "or(").replace(/\.and_\(/g, "and(").replace(/:/g, "=").replace(/,re\.ignorecase/g, "i").replace(/,pattern.case_insensitive/g, "i").replace(/,regexoptions.ignorecase/g, "i").replace(/re.compile\(([^)]+)\)/g, "$1").replace(/pattern.compile\(([^)]+)\)/g, "r$1").replace(/newregex\(([^)]+)\)/g, "r$1").replace(/string=/g, "=").replace(/regex=/g, "=").replace(/,,/g, ",").replace(/,\)/g, ")"); + const preferredQuote = params2.map((p) => p.quote).filter((quote5) => "'\"`".includes(quote5))[0]; + return { selector: transform(template, params2, testIdAttributeName2), preferredQuote }; +} +function countParams(template) { + return [...template.matchAll(/\$\d+/g)].length; +} +function shiftParams(template, sub) { + return template.replace(/\$(\d+)/g, (_, ordinal) => `$${ordinal - sub}`); +} +function transform(template, params2, testIdAttributeName2) { + while (true) { + const hasMatch = template.match(/filter\(,?(has=|hasnot=|sethas\(|sethasnot\()/); + if (!hasMatch) + break; + const start3 = hasMatch.index + hasMatch[0].length; + let balance = 0; + let end = start3; + for (; end < template.length; end++) { + if (template[end] === "(") + balance++; + else if (template[end] === ")") + balance--; + if (balance < 0) + break; + } + let prefix = template.substring(0, start3); + let extraSymbol = 0; + if (["sethas(", "sethasnot("].includes(hasMatch[1])) { + extraSymbol = 1; + prefix = prefix.replace(/sethas\($/, "has=").replace(/sethasnot\($/, "hasnot="); + } + const paramsCountBeforeHas = countParams(template.substring(0, start3)); + const hasTemplate = shiftParams(template.substring(start3, end), paramsCountBeforeHas); + const paramsCountInHas = countParams(hasTemplate); + const hasParams = params2.slice(paramsCountBeforeHas, paramsCountBeforeHas + paramsCountInHas); + const hasSelector = JSON.stringify(transform(hasTemplate, hasParams, testIdAttributeName2)); + template = prefix.replace(/=$/, "2=") + `$${paramsCountBeforeHas + 1}` + shiftParams(template.substring(end + extraSymbol), paramsCountInHas - 1); + const paramsBeforeHas = params2.slice(0, paramsCountBeforeHas); + const paramsAfterHas = params2.slice(paramsCountBeforeHas + paramsCountInHas); + params2 = paramsBeforeHas.concat([{ quote: '"', text: hasSelector }]).concat(paramsAfterHas); + } + template = template.replace(/\,set([\w]+)\(([^)]+)\)/g, (_, group1, group2) => "," + group1.toLowerCase() + "=" + group2.toLowerCase()).replace(/framelocator\(([^)]+)\)/g, "$1.internal:control=enter-frame").replace(/contentframe(\(\))?/g, "internal:control=enter-frame").replace(/locator\(([^)]+),hastext=([^),]+)\)/g, "locator($1).internal:has-text=$2").replace(/locator\(([^)]+),hasnottext=([^),]+)\)/g, "locator($1).internal:has-not-text=$2").replace(/locator\(([^)]+),hastext=([^),]+)\)/g, "locator($1).internal:has-text=$2").replace(/locator\(([^)]+)\)/g, "$1").replace(/getbyrole\(([^)]+)\)/g, "internal:role=$1").replace(/getbytext\(([^)]+)\)/g, "internal:text=$1").replace(/getbylabel\(([^)]+)\)/g, "internal:label=$1").replace(/getbytestid\(([^)]+)\)/g, `internal:testid=[${encodeTestIdAttributeName(testIdAttributeName2)}=$1]`).replace(/getby(placeholder|alt|title)(?:text)?\(([^)]+)\)/g, "internal:attr=[$1=$2]").replace(/first(\(\))?/g, "nth=0").replace(/last(\(\))?/g, "nth=-1").replace(/nth\(([^)]+)\)/g, "nth=$1").replace(/filter\(,?visible=true\)/g, "visible=true").replace(/filter\(,?visible=false\)/g, "visible=false").replace(/filter\(,?hastext=([^)]+)\)/g, "internal:has-text=$1").replace(/filter\(,?hasnottext=([^)]+)\)/g, "internal:has-not-text=$1").replace(/filter\(,?has2=([^)]+)\)/g, "internal:has=$1").replace(/filter\(,?hasnot2=([^)]+)\)/g, "internal:has-not=$1").replace(/,exact=false/g, "").replace(/(,name=\$\d+)(,description=\$\d+),exact=true/g, "$1s$2s").replace(/,exact=true/g, "s").replace(/,includehidden=/g, ",include-hidden=").replace(/\,/g, "]["); + const parts = template.split("."); + for (let index = 0; index < parts.length - 1; index++) { + if (parts[index] === "internal:control=enter-frame" && parts[index + 1].startsWith("nth=")) { + const [nth] = parts.splice(index, 1); + parts.splice(index + 1, 0, nth); + } + } + return parts.map((t) => { + if (!t.startsWith("internal:") || t === "internal:control") + return t.replace(/\$(\d+)/g, (_, ordinal) => { + const param = params2[+ordinal - 1]; + return param.text; + }); + t = t.includes("[") ? t.replace(/\]/, "") + "]" : t; + t = t.replace(/(?:r)\$(\d+)(i)?/g, (_, ordinal, suffix) => { + const param = params2[+ordinal - 1]; + if (t.startsWith("internal:attr") || t.startsWith("internal:testid") || t.startsWith("internal:role")) + return escapeForAttributeSelector(new RegExp(param.text), false) + (suffix || ""); + return escapeForTextSelector(new RegExp(param.text, suffix), false); + }).replace(/\$(\d+)(i|s)?/g, (_, ordinal, suffix) => { + const param = params2[+ordinal - 1]; + if (t.startsWith("internal:has=") || t.startsWith("internal:has-not=")) + return param.text; + if (t.startsWith("internal:testid")) + return escapeForAttributeSelector(param.text, true); + if (t.startsWith("internal:attr") || t.startsWith("internal:role")) + return escapeForAttributeSelector(param.text, suffix === "s"); + return escapeForTextSelector(param.text, suffix === "s"); + }); + return t; + }).join(" >> "); +} +function locatorOrSelectorAsSelector(language, locator2, testIdAttributeName2 = "data-testid") { + try { + return unsafeLocatorOrSelectorAsSelector(language, locator2, testIdAttributeName2); + } catch (e) { + return ""; + } +} +function unsafeLocatorOrSelectorAsSelector(language, locator2, testIdAttributeName2 = "data-testid") { + try { + parseSelector(locator2); + return locator2; + } catch (e) { + } + const { selector, preferredQuote } = parseLocator(locator2, testIdAttributeName2); + const locators = asLocators(language, selector, void 0, void 0, preferredQuote); + const digest = digestForComparison(language, locator2); + if (locators.some((candidate) => digestForComparison(language, candidate) === digest)) + return selector; + return ""; +} +function digestForComparison(language, locator2) { + locator2 = locator2.replace(/\s/g, ""); + if (language === "javascript") + locator2 = locator2.replace(/\\?["`]/g, "'").replace(/,{}/g, ""); + return locator2; +} +var init_locatorParser = __esm({ + "packages/isomorphic/locatorParser.ts"() { + "use strict"; + init_locatorGenerators(); + init_locatorUtils(); + init_selectorParser(); + init_stringUtils(); + } +}); + +// packages/isomorphic/trace/snapshotRenderer.ts +function findClosest(items, metric, target) { + return items.find((item, index) => { + if (index === items.length - 1) + return true; + const next = items[index + 1]; + return Math.abs(metric(item) - target) < Math.abs(metric(next) - target); + }); +} +function isNodeNameAttributesChildNodesSnapshot(n) { + return Array.isArray(n) && typeof n[0] === "string"; +} +function isSubtreeReferenceSnapshot(n) { + return Array.isArray(n) && Array.isArray(n[0]); +} +function snapshotNodes(snapshot3) { + if (!snapshot3._nodes) { + const nodes = []; + const visit = (n) => { + if (typeof n === "string") { + nodes.push(n); + } else if (isNodeNameAttributesChildNodesSnapshot(n)) { + const [, , ...children] = n; + for (const child of children) + visit(child); + nodes.push(n); + } + }; + visit(snapshot3.html); + snapshot3._nodes = nodes; + } + return snapshot3._nodes; +} +function snapshotScript(viewport, ...targetIds) { + function applyPlaywrightAttributes(blankSnapshotUrl2, viewport2, ...targetIds2) { + const win = window; + const searchParams = new URLSearchParams(win.location.search); + const shouldPopulateCanvasFromScreenshot = searchParams.has("shouldPopulateCanvasFromScreenshot"); + const isUnderTest2 = searchParams.has("isUnderTest"); + const frameBoundingRectsInfo = { + viewport: viewport2, + frames: /* @__PURE__ */ new WeakMap() + }; + win["__playwright_frame_bounding_rects__"] = frameBoundingRectsInfo; + const kPointerWarningTitle = "Recorded click position in absolute coordinates did not match the center of the clicked element. This is either due to the use of provided offset, or due to a difference between the test runner and the trace viewer operating systems."; + const scrollTops = []; + const scrollLefts = []; + const targetElements = []; + const canvasElements = []; + let topSnapshotWindow = win; + while (topSnapshotWindow !== topSnapshotWindow.parent && !topSnapshotWindow.location.pathname.match(/\/page@[a-z0-9]+$/)) + topSnapshotWindow = topSnapshotWindow.parent; + const visit = (root) => { + for (const e of root.querySelectorAll(`[__playwright_scroll_top_]`)) + scrollTops.push(e); + for (const e of root.querySelectorAll(`[__playwright_scroll_left_]`)) + scrollLefts.push(e); + for (const element2 of root.querySelectorAll(`[__playwright_value_]`)) { + const inputElement = element2; + if (inputElement.type !== "file") + inputElement.value = inputElement.getAttribute("__playwright_value_"); + element2.removeAttribute("__playwright_value_"); + } + for (const element2 of root.querySelectorAll(`[__playwright_checked_]`)) { + element2.checked = element2.getAttribute("__playwright_checked_") === "true"; + element2.removeAttribute("__playwright_checked_"); + } + for (const element2 of root.querySelectorAll(`[__playwright_selected_]`)) { + element2.selected = element2.getAttribute("__playwright_selected_") === "true"; + element2.removeAttribute("__playwright_selected_"); + } + for (const element2 of root.querySelectorAll(`[__playwright_popover_open_]`)) { + try { + element2.showPopover(); + } catch { + } + element2.removeAttribute("__playwright_popover_open_"); + } + for (const element2 of root.querySelectorAll(`[__playwright_dialog_open_]`)) { + try { + if (element2.getAttribute("__playwright_dialog_open_") === "modal") + element2.showModal(); + else + element2.show(); + } catch { + } + element2.removeAttribute("__playwright_dialog_open_"); + } + const highlightTarget = (target) => { + const style = target.style; + style.outline = "2px solid #006ab1"; + style.backgroundColor = "#6fa8dc7f"; + targetElements.push(target); + }; + for (const target of root.querySelectorAll(`[__playwright_target__=""]`)) + highlightTarget(target); + for (const targetId of targetIds2) { + if (!targetId) + continue; + for (const target of root.querySelectorAll(`[__playwright_target__="${targetId}"]`)) + highlightTarget(target); + } + for (const iframe of root.querySelectorAll("iframe, frame")) { + const boundingRectJson = iframe.getAttribute("__playwright_bounding_rect__"); + iframe.removeAttribute("__playwright_bounding_rect__"); + const boundingRect = boundingRectJson ? JSON.parse(boundingRectJson) : void 0; + if (boundingRect) + frameBoundingRectsInfo.frames.set(iframe, { boundingRect, scrollLeft: 0, scrollTop: 0 }); + const src = iframe.getAttribute("__playwright_src__"); + if (!src) { + iframe.setAttribute("src", blankSnapshotUrl2); + } else { + const url2 = new URL(win.location.href); + const index = url2.pathname.lastIndexOf("/snapshot/"); + if (index !== -1) + url2.pathname = url2.pathname.substring(0, index + 1); + url2.pathname += src.substring(1); + iframe.setAttribute("src", url2.toString()); + } + } + { + const body = root.querySelector(`body[__playwright_custom_elements__]`); + if (body && win.customElements) { + const customElements = (body.getAttribute("__playwright_custom_elements__") || "").split(","); + for (const elementName of customElements) + win.customElements.define(elementName, class extends HTMLElement { + }); + } + } + for (const element2 of root.querySelectorAll(`template[__playwright_shadow_root_]`)) { + const template = element2; + const shadowRoot = template.parentElement.attachShadow({ mode: "open" }); + shadowRoot.appendChild(template.content); + template.remove(); + visit(shadowRoot); + } + for (const element2 of root.querySelectorAll("a")) + element2.addEventListener("click", (event) => { + event.preventDefault(); + }); + if ("adoptedStyleSheets" in root) { + const adoptedSheets = [...root.adoptedStyleSheets]; + for (const element2 of root.querySelectorAll(`template[__playwright_style_sheet_]`)) { + const template = element2; + const sheet = new CSSStyleSheet(); + sheet.replaceSync(template.getAttribute("__playwright_style_sheet_")); + adoptedSheets.push(sheet); + } + root.adoptedStyleSheets = adoptedSheets; + } + canvasElements.push(...root.querySelectorAll("canvas")); + }; + const onLoad = () => { + win.removeEventListener("load", onLoad); + for (const element2 of scrollTops) { + element2.scrollTop = +element2.getAttribute("__playwright_scroll_top_"); + element2.removeAttribute("__playwright_scroll_top_"); + if (frameBoundingRectsInfo.frames.has(element2)) + frameBoundingRectsInfo.frames.get(element2).scrollTop = element2.scrollTop; + } + for (const element2 of scrollLefts) { + element2.scrollLeft = +element2.getAttribute("__playwright_scroll_left_"); + element2.removeAttribute("__playwright_scroll_left_"); + if (frameBoundingRectsInfo.frames.has(element2)) + frameBoundingRectsInfo.frames.get(element2).scrollLeft = element2.scrollLeft; + } + win.document.styleSheets[0].disabled = true; + const search = new URL(win.location.href).searchParams; + const isTopFrame = win === topSnapshotWindow; + if (isTopFrame && search.get("pointX") && search.get("pointY")) { + const pointX = +search.get("pointX"); + const pointY = +search.get("pointY"); + const pointElement = win.document.createElement("x-pw-pointer"); + pointElement.style.position = "fixed"; + pointElement.style.backgroundColor = "#f44336"; + pointElement.style.width = "20px"; + pointElement.style.height = "20px"; + pointElement.style.borderRadius = "10px"; + pointElement.style.margin = "-10px 0 0 -10px"; + pointElement.style.zIndex = "2147483646"; + pointElement.style.display = "flex"; + pointElement.style.alignItems = "center"; + pointElement.style.justifyContent = "center"; + const target = targetElements[0]; + const targetBox = target?.getBoundingClientRect(); + const targetCenter = target ? { x: targetBox.left + targetBox.width / 2, y: targetBox.top + targetBox.height / 2 } : null; + pointElement.style.left = (targetCenter?.x ?? pointX) + "px"; + pointElement.style.top = (targetCenter?.y ?? pointY) + "px"; + const isAligned = !targetCenter || Math.abs(targetCenter.x - pointX) <= 10 && Math.abs(targetCenter.y - pointY) <= 10; + if (!isAligned) { + const warningElement = win.document.createElement("x-pw-pointer-warning"); + warningElement.textContent = "\u26A0"; + warningElement.style.fontSize = "19px"; + warningElement.style.color = "white"; + warningElement.style.marginTop = "-3.5px"; + warningElement.style.userSelect = "none"; + pointElement.appendChild(warningElement); + pointElement.setAttribute("title", kPointerWarningTitle); + } + win.document.documentElement.appendChild(pointElement); + } + if (canvasElements.length > 0) { + let drawCheckerboard2 = function(context2, canvas) { + function createCheckerboardPattern() { + const pattern = win.document.createElement("canvas"); + pattern.width = pattern.width / Math.floor(pattern.width / 24); + pattern.height = pattern.height / Math.floor(pattern.height / 24); + const context3 = pattern.getContext("2d"); + context3.fillStyle = "lightgray"; + context3.fillRect(0, 0, pattern.width, pattern.height); + context3.fillStyle = "white"; + context3.fillRect(0, 0, pattern.width / 2, pattern.height / 2); + context3.fillRect(pattern.width / 2, pattern.height / 2, pattern.width, pattern.height); + return context3.createPattern(pattern, "repeat"); + } + context2.fillStyle = createCheckerboardPattern(); + context2.fillRect(0, 0, canvas.width, canvas.height); + }; + var drawCheckerboard = drawCheckerboard2; + const img = new Image(); + img.onload = () => { + for (const canvas of canvasElements) { + const context2 = canvas.getContext("2d"); + const boundingRectAttribute = canvas.getAttribute("__playwright_bounding_rect__"); + canvas.removeAttribute("__playwright_bounding_rect__"); + if (!boundingRectAttribute) + continue; + let boundingRect; + try { + boundingRect = JSON.parse(boundingRectAttribute); + } catch (e) { + continue; + } + let currWindow = win; + while (currWindow !== topSnapshotWindow) { + const iframe = currWindow.frameElement; + currWindow = currWindow.parent; + const iframeInfo = currWindow["__playwright_frame_bounding_rects__"]?.frames.get(iframe); + if (!iframeInfo?.boundingRect) + break; + const leftOffset = iframeInfo.boundingRect.left - iframeInfo.scrollLeft; + const topOffset = iframeInfo.boundingRect.top - iframeInfo.scrollTop; + boundingRect.left += leftOffset; + boundingRect.top += topOffset; + boundingRect.right += leftOffset; + boundingRect.bottom += topOffset; + } + const { width, height } = topSnapshotWindow["__playwright_frame_bounding_rects__"].viewport; + boundingRect.left = boundingRect.left / width; + boundingRect.top = boundingRect.top / height; + boundingRect.right = boundingRect.right / width; + boundingRect.bottom = boundingRect.bottom / height; + const partiallyUncaptured = boundingRect.right > 1 || boundingRect.bottom > 1; + const fullyUncaptured = boundingRect.left > 1 || boundingRect.top > 1; + if (fullyUncaptured) { + canvas.title = `Playwright couldn't capture canvas contents because it's located outside the viewport.`; + continue; + } + drawCheckerboard2(context2, canvas); + if (shouldPopulateCanvasFromScreenshot) { + context2.drawImage(img, boundingRect.left * img.width, boundingRect.top * img.height, (boundingRect.right - boundingRect.left) * img.width, (boundingRect.bottom - boundingRect.top) * img.height, 0, 0, canvas.width, canvas.height); + if (partiallyUncaptured) + canvas.title = `Playwright couldn't capture full canvas contents because it's located partially outside the viewport.`; + else + canvas.title = `Canvas contents are displayed on a best-effort basis based on viewport screenshots taken during test execution.`; + } else { + canvas.title = "Canvas content display is disabled."; + } + if (isUnderTest2) + console.log(`canvas drawn:`, JSON.stringify([boundingRect.left, boundingRect.top, boundingRect.right - boundingRect.left, boundingRect.bottom - boundingRect.top].map((v) => Math.floor(v * 100)))); + } + }; + img.onerror = () => { + for (const canvas of canvasElements) { + const context2 = canvas.getContext("2d"); + drawCheckerboard2(context2, canvas); + canvas.title = `Playwright couldn't show canvas contents because the screenshot failed to load.`; + } + }; + img.src = location.href.replace("/snapshot", "/closest-screenshot"); + } + }; + const onDOMContentLoaded = () => visit(win.document); + win.addEventListener("load", onLoad); + win.addEventListener("DOMContentLoaded", onDOMContentLoaded); + } + const safe = (value2) => JSON.stringify(value2).replace(/ `, ${safe(String(id))}`).join("")})`; +} +function rewriteURLForCustomProtocol(href) { + if (href.startsWith(kLegacyBlobPrefix)) + href = href.substring(kLegacyBlobPrefix.length); + try { + const url2 = new URL(href); + if (url2.protocol === "javascript:" || url2.protocol === "vbscript:") + return "javascript:void(0)"; + const isBlob = url2.protocol === "blob:"; + const isFile = url2.protocol === "file:"; + if (!isBlob && !isFile && schemas.includes(url2.protocol)) + return href; + const prefix = "pw-" + url2.protocol.slice(0, url2.protocol.length - 1); + if (!isFile) + url2.protocol = "https:"; + url2.hostname = url2.hostname ? `${prefix}--${url2.hostname}` : prefix; + if (isFile) { + url2.protocol = "https:"; + } + return url2.toString(); + } catch { + return href; + } +} +function rewriteURLsInStyleSheetForCustomProtocol(text2) { + return text2.replace(urlInCSSRegex, (match, protocol) => { + const isBlob = protocol === "blob:"; + const isFile = protocol === "file:"; + if (!isBlob && !isFile && schemas.includes(protocol)) + return match; + return match.replace(protocol + "//", `https://pw-${protocol.slice(0, -1)}--`); + }); +} +function escapeURLsInStyleSheet(text2) { + const replacer = (match, url2) => { + if (url2.includes("")) + return match.replace(url2, encodeURI(url2)); + return match; + }; + return text2.replace(urlToEscapeRegex1, replacer).replace(urlToEscapeRegex2, replacer); +} +var SnapshotRenderer, autoClosing, kAllowedMetaHttpEquivs, schemas, kLegacyBlobPrefix, urlInCSSRegex, urlToEscapeRegex1, urlToEscapeRegex2, blankSnapshotUrl; +var init_snapshotRenderer = __esm({ + "packages/isomorphic/trace/snapshotRenderer.ts"() { + "use strict"; + init_stringUtils(); + SnapshotRenderer = class { + constructor(htmlCache, resources, snapshots, screencastFrames, index) { + this._htmlCache = htmlCache; + this._resources = resources; + this._snapshots = snapshots; + this._index = index; + this._snapshot = snapshots[index]; + this._callId = snapshots[index].callId; + this._screencastFrames = screencastFrames; + this.snapshotName = snapshots[index].snapshotName; + } + snapshot() { + return this._snapshots[this._index]; + } + viewport() { + return this._snapshots[this._index].viewport; + } + closestScreenshot() { + const { wallTime, timestamp } = this.snapshot(); + const closestFrame = wallTime && this._screencastFrames[0]?.frameSwapWallTime ? findClosest(this._screencastFrames, (frame) => frame.frameSwapWallTime, wallTime) : findClosest(this._screencastFrames, (frame) => frame.timestamp, timestamp); + return closestFrame?.sha1; + } + render() { + const result2 = []; + const visit = (n, snapshotIndex, parentTag, parentAttrs) => { + if (typeof n === "string") { + if (parentTag === "STYLE" || parentTag === "style") + result2.push(escapeURLsInStyleSheet(rewriteURLsInStyleSheetForCustomProtocol(n))); + else + result2.push(escapeHTML(n)); + return; + } + if (isSubtreeReferenceSnapshot(n)) { + const referenceIndex = snapshotIndex - n[0][0]; + if (referenceIndex >= 0 && referenceIndex <= snapshotIndex) { + const nodes = snapshotNodes(this._snapshots[referenceIndex]); + const nodeIndex = n[0][1]; + if (nodeIndex >= 0 && nodeIndex < nodes.length) + return visit(nodes[nodeIndex], referenceIndex, parentTag, parentAttrs); + } + } else if (isNodeNameAttributesChildNodesSnapshot(n)) { + const [name, nodeAttrs, ...children] = n; + if (name.toUpperCase() === "SCRIPT") + return; + const upperName = name.toUpperCase(); + const nodeName = upperName === "NOSCRIPT" ? "X-NOSCRIPT" : name; + const attrs = Object.entries(nodeAttrs || {}); + result2.push("<", nodeName); + const kCurrentSrcAttribute = "__playwright_current_src__"; + const isFrame = upperName === "IFRAME" || upperName === "FRAME"; + const isAnchor = upperName === "A"; + const isImg = upperName === "IMG"; + const isMeta = upperName === "META"; + const isImgWithCurrentSrc = isImg && attrs.some((a) => a[0] === kCurrentSrcAttribute); + const isSourceInsidePictureWithCurrentSrc = upperName === "SOURCE" && parentTag === "PICTURE" && parentAttrs?.some((a) => a[0] === kCurrentSrcAttribute); + const hasUnsafeHttpEquiv = isMeta && attrs.some((a) => a[0].toLowerCase() === "http-equiv" && !kAllowedMetaHttpEquivs.has(a[1].trim().toLowerCase())); + for (const [attr, value2] of attrs) { + let attrName = attr; + if (attr.toLowerCase().startsWith("on")) + continue; + if (isFrame && attr.toLowerCase() === "src") { + attrName = "__playwright_src__"; + } + if (isFrame && (attr.toLowerCase() === "srcdoc" || attr.toLowerCase() === "sandbox")) { + attrName = "__playwright_" + attr.toLowerCase() + "__"; + } + if (upperName === "OBJECT" && attr.toLowerCase() === "data") + attrName = "__playwright_data__"; + if (upperName === "EMBED" && attr.toLowerCase() === "src") + attrName = "__playwright_src__"; + if (isImg && attr === kCurrentSrcAttribute) { + attrName = "src"; + } + if (["src", "srcset"].includes(attr.toLowerCase()) && (isImgWithCurrentSrc || isSourceInsidePictureWithCurrentSrc)) { + attrName = "_" + attrName; + } + if (hasUnsafeHttpEquiv && (attr.toLowerCase() === "http-equiv" || attr.toLowerCase() === "content")) { + attrName = "_" + attr; + } + let attrValue = value2; + if (!isAnchor && (attr.toLowerCase() === "href" || attr.toLowerCase() === "src" || attr === kCurrentSrcAttribute)) + attrValue = rewriteURLForCustomProtocol(value2); + result2.push(" ", attrName, '="', escapeHTMLAttribute(attrValue), '"'); + } + result2.push(">"); + for (const child of children) + visit(child, snapshotIndex, nodeName, attrs); + if (!autoClosing.has(nodeName)) + result2.push("", nodeName, ">"); + return; + } else { + return; + } + }; + const snapshot3 = this._snapshot; + const html = this._htmlCache.getOrCompute(this, () => { + visit(snapshot3.html, this._index, void 0, void 0); + const safeDoctype = snapshot3.doctype?.replace(/[^a-zA-Z0-9]/g, ""); + const prefix = safeDoctype ? `` : ""; + const html2 = prefix + [ + // Hide the document in order to prevent flickering. We will unhide once script has processed shadow. + "", + `` + ].join("") + result2.join(""); + return { value: html2, size: html2.length }; + }); + return { html, pageId: snapshot3.pageId, frameId: snapshot3.frameId, index: this._index }; + } + resourceByUrl(url2, method) { + const snapshot3 = this._snapshot; + let sameFrameResource; + let otherFrameResource; + for (const resource of this._resources) { + if (typeof resource._monotonicTime === "number" && resource._monotonicTime >= snapshot3.timestamp) + break; + if (resource.response.status === 304) { + continue; + } + if (resource.request.url === url2 && resource.request.method === method) { + if (resource._frameref === snapshot3.frameId) + sameFrameResource = resource; + else + otherFrameResource = resource; + } + } + let result2 = sameFrameResource ?? otherFrameResource; + if (result2 && method.toUpperCase() === "GET") { + let override = snapshot3.resourceOverrides.find((o) => o.url === url2); + if (override?.ref) { + const index = this._index - override.ref; + if (index >= 0 && index < this._snapshots.length) + override = this._snapshots[index].resourceOverrides.find((o) => o.url === url2); + } + if (override?.sha1) { + result2 = { + ...result2, + response: { + ...result2.response, + content: { + ...result2.response.content, + _sha1: override.sha1 + } + } + }; + } + } + return result2; + } + }; + autoClosing = /* @__PURE__ */ new Set(["AREA", "BASE", "BR", "COL", "COMMAND", "EMBED", "HR", "IMG", "INPUT", "KEYGEN", "LINK", "MENUITEM", "META", "PARAM", "SOURCE", "TRACK", "WBR"]); + kAllowedMetaHttpEquivs = /* @__PURE__ */ new Set(["content-type", "content-language", "default-style", "x-ua-compatible"]); + schemas = ["about:", "blob:", "data:", "file:", "ftp:", "http:", "https:", "mailto:", "sftp:", "ws:", "wss:"]; + kLegacyBlobPrefix = "http://playwright.bloburl/#"; + urlInCSSRegex = /url\(['"]?([\w-]+:)\/\//ig; + urlToEscapeRegex1 = /url\(\s*'([^']*)'\s*\)/ig; + urlToEscapeRegex2 = /url\(\s*"([^"]*)"\s*\)/ig; + blankSnapshotUrl = "data:text/html;base64," + btoa(``); + } +}); + +// packages/isomorphic/lruCache.ts +var LRUCache; +var init_lruCache = __esm({ + "packages/isomorphic/lruCache.ts"() { + "use strict"; + LRUCache = class { + constructor(maxSize) { + this._maxSize = maxSize; + this._map = /* @__PURE__ */ new Map(); + this._size = 0; + } + getOrCompute(key, compute) { + if (this._map.has(key)) { + const result3 = this._map.get(key); + this._map.delete(key); + this._map.set(key, result3); + return result3.value; + } + const result2 = compute(); + while (this._map.size && this._size + result2.size > this._maxSize) { + const [firstKey, firstValue] = this._map.entries().next().value; + this._size -= firstValue.size; + this._map.delete(firstKey); + } + this._map.set(key, result2); + this._size += result2.size; + return result2.value; + } + }; + } +}); + +// packages/isomorphic/trace/snapshotStorage.ts +var SnapshotStorage; +var init_snapshotStorage = __esm({ + "packages/isomorphic/trace/snapshotStorage.ts"() { + "use strict"; + init_snapshotRenderer(); + init_lruCache(); + SnapshotStorage = class { + constructor() { + this._frameSnapshots = /* @__PURE__ */ new Map(); + this._cache = new LRUCache(1e8); + // 100MB per each trace + this._contextToResources = /* @__PURE__ */ new Map(); + this._resourceUrlsWithOverrides = /* @__PURE__ */ new Set(); + } + addResource(contextId4, resource) { + resource.request.url = rewriteURLForCustomProtocol(resource.request.url); + this._ensureResourcesForContext(contextId4).push(resource); + } + addFrameSnapshot(contextId4, snapshot3, screencastFrames) { + for (const override of snapshot3.resourceOverrides) + override.url = rewriteURLForCustomProtocol(override.url); + let frameSnapshots = this._frameSnapshots.get(snapshot3.frameId); + if (!frameSnapshots) { + frameSnapshots = { + raw: [], + renderers: [] + }; + this._frameSnapshots.set(snapshot3.frameId, frameSnapshots); + if (snapshot3.isMainFrame) + this._frameSnapshots.set(snapshot3.pageId, frameSnapshots); + } + frameSnapshots.raw.push(snapshot3); + const resources = this._ensureResourcesForContext(contextId4); + const renderer = new SnapshotRenderer(this._cache, resources, frameSnapshots.raw, screencastFrames, frameSnapshots.raw.length - 1); + frameSnapshots.renderers.push(renderer); + return renderer; + } + snapshotByName(pageOrFrameId, snapshotName) { + const snapshot3 = this._frameSnapshots.get(pageOrFrameId); + return snapshot3?.renderers.find((r) => r.snapshotName === snapshotName); + } + snapshotsForTest() { + return [...this._frameSnapshots.keys()]; + } + finalize() { + for (const resources of this._contextToResources.values()) + resources.sort((a, b) => (a._monotonicTime || 0) - (b._monotonicTime || 0)); + for (const frameSnapshots of this._frameSnapshots.values()) { + for (const snapshot3 of frameSnapshots.raw) { + for (const override of snapshot3.resourceOverrides) + this._resourceUrlsWithOverrides.add(override.url); + } + } + } + hasResourceOverride(url2) { + return this._resourceUrlsWithOverrides.has(url2); + } + _ensureResourcesForContext(contextId4) { + let resources = this._contextToResources.get(contextId4); + if (!resources) { + resources = []; + this._contextToResources.set(contextId4, resources); + } + return resources; + } + }; + } +}); + +// packages/isomorphic/trace/traceUtils.ts +function parseClientSideCallMetadata(data) { + const result2 = /* @__PURE__ */ new Map(); + const { files, stacks } = data; + for (const s of stacks) { + const [id, ff] = s; + result2.set(`call@${id}`, ff.map((f) => ({ file: files[f[0]], line: f[1], column: f[2], function: f[3] }))); + } + return result2; +} +function serializeClientSideCallMetadata(metadatas) { + const fileNames = /* @__PURE__ */ new Map(); + const stacks = []; + for (const m of metadatas) { + if (!m.stack || !m.stack.length) + continue; + const stack = []; + for (const frame of m.stack) { + let ordinal = fileNames.get(frame.file); + if (typeof ordinal !== "number") { + ordinal = fileNames.size; + fileNames.set(frame.file, ordinal); + } + const stackFrame = [ordinal, frame.line || 0, frame.column || 0, frame.function || ""]; + stack.push(stackFrame); + } + stacks.push([m.id, stack]); + } + return { files: [...fileNames.keys()], stacks }; +} +var init_traceUtils = __esm({ + "packages/isomorphic/trace/traceUtils.ts"() { + "use strict"; + } +}); + +// packages/isomorphic/trace/traceModernizer.ts +var TraceVersionError, latestVersion, TraceModernizer; +var init_traceModernizer = __esm({ + "packages/isomorphic/trace/traceModernizer.ts"() { + "use strict"; + TraceVersionError = class extends Error { + constructor(message) { + super(message); + this.name = "TraceVersionError"; + } + }; + latestVersion = 8; + TraceModernizer = class { + constructor(contextEntry, snapshotStorage) { + this._actionMap = /* @__PURE__ */ new Map(); + this._pageEntries = /* @__PURE__ */ new Map(); + this._jsHandles = /* @__PURE__ */ new Map(); + this._consoleObjects = /* @__PURE__ */ new Map(); + this._contextEntry = contextEntry; + this._snapshotStorage = snapshotStorage; + } + appendTrace(trace) { + for (const line of trace.split("\n")) + this._appendEvent(line); + } + actions() { + return [...this._actionMap.values()]; + } + _pageEntry(pageId4) { + let pageEntry = this._pageEntries.get(pageId4); + if (!pageEntry) { + pageEntry = { + pageId: pageId4, + screencastFrames: [] + }; + this._pageEntries.set(pageId4, pageEntry); + this._contextEntry.pages.push(pageEntry); + } + return pageEntry; + } + _appendEvent(line) { + if (!line) + return; + const events = this._modernize(JSON.parse(line)); + for (const event of events) + this._innerAppendEvent(event); + } + _innerAppendEvent(event) { + const contextEntry = this._contextEntry; + switch (event.type) { + case "context-options": { + if (event.version > latestVersion) + throw new TraceVersionError("The trace was created by a newer version of Playwright and is not supported by this version of the viewer. Please use latest Playwright to open the trace."); + this._version = event.version; + contextEntry.origin = event.origin; + contextEntry.browserName = event.browserName; + contextEntry.channel = event.channel; + contextEntry.title = event.title; + contextEntry.platform = event.platform; + contextEntry.playwrightVersion = event.playwrightVersion; + contextEntry.wallTime = event.wallTime; + contextEntry.startTime = event.monotonicTime; + contextEntry.sdkLanguage = event.sdkLanguage; + contextEntry.options = event.options; + contextEntry.testIdAttributeName = event.testIdAttributeName; + contextEntry.contextId = event.contextId ?? ""; + contextEntry.testTimeout = event.testTimeout; + break; + } + case "screencast-frame": { + this._pageEntry(event.pageId).screencastFrames.push(event); + break; + } + case "before": { + this._actionMap.set(event.callId, { ...event, type: "action", endTime: 0, log: [] }); + break; + } + case "input": { + const existing = this._actionMap.get(event.callId); + existing.inputSnapshot = event.inputSnapshot; + existing.point = event.point; + break; + } + case "log": { + const existing = this._actionMap.get(event.callId); + if (!existing) + return; + existing.log.push({ + time: event.time, + message: event.message + }); + break; + } + case "after": { + const existing = this._actionMap.get(event.callId); + existing.afterSnapshot = event.afterSnapshot; + existing.endTime = event.endTime; + existing.result = event.result; + existing.error = event.error; + existing.attachments = event.attachments; + existing.annotations = event.annotations; + if (event.point) + existing.point = event.point; + break; + } + case "action": { + this._actionMap.set(event.callId, { ...event, log: [] }); + break; + } + case "event": { + contextEntry.events.push(event); + break; + } + case "stdout": { + contextEntry.stdio.push(event); + break; + } + case "stderr": { + contextEntry.stdio.push(event); + break; + } + case "error": { + contextEntry.errors.push(event); + break; + } + case "console": { + contextEntry.events.push(event); + break; + } + case "resource-snapshot": + this._snapshotStorage.addResource(this._contextEntry.contextId, event.snapshot); + contextEntry.resources.push(event.snapshot); + break; + case "frame-snapshot": + this._snapshotStorage.addFrameSnapshot(this._contextEntry.contextId, event.snapshot, this._pageEntry(event.snapshot.pageId).screencastFrames); + break; + } + if ("pageId" in event && event.pageId) + this._pageEntry(event.pageId); + if (event.type === "action" || event.type === "before") + contextEntry.startTime = Math.min(contextEntry.startTime, event.startTime); + if (event.type === "action" || event.type === "after") + contextEntry.endTime = Math.max(contextEntry.endTime, event.endTime); + if (event.type === "event") { + contextEntry.startTime = Math.min(contextEntry.startTime, event.time); + contextEntry.endTime = Math.max(contextEntry.endTime, event.time); + } + if (event.type === "screencast-frame") { + contextEntry.startTime = Math.min(contextEntry.startTime, event.timestamp); + contextEntry.endTime = Math.max(contextEntry.endTime, event.timestamp); + } + } + _processedContextCreatedEvent() { + return this._version !== void 0; + } + _modernize(event) { + let version3 = this._version ?? event.version ?? 6; + let events = [event]; + for (; version3 < latestVersion; ++version3) + events = this[`_modernize_${version3}_to_${version3 + 1}`].call(this, events); + return events; + } + _modernize_0_to_1(events) { + for (const event of events) { + if (event.type !== "action") + continue; + if (typeof event.metadata.error === "string") + event.metadata.error = { error: { name: "Error", message: event.metadata.error } }; + } + return events; + } + _modernize_1_to_2(events) { + for (const event of events) { + if (event.type !== "frame-snapshot" || !event.snapshot.isMainFrame) + continue; + event.snapshot.viewport = this._contextEntry.options?.viewport || { width: 1280, height: 720 }; + } + return events; + } + _modernize_2_to_3(events) { + for (const event of events) { + if (event.type !== "resource-snapshot" || event.snapshot.request) + continue; + const resource = event.snapshot; + event.snapshot = { + _frameref: resource.frameId, + request: { + url: resource.url, + method: resource.method, + headers: resource.requestHeaders, + postData: resource.requestSha1 ? { _sha1: resource.requestSha1 } : void 0 + }, + response: { + status: resource.status, + headers: resource.responseHeaders, + content: { + mimeType: resource.contentType, + _sha1: resource.responseSha1 + } + }, + _monotonicTime: resource.timestamp + }; + } + return events; + } + _modernize_3_to_4(events) { + const result2 = []; + for (const event of events) { + const e = this._modernize_event_3_to_4(event); + if (e) + result2.push(e); + } + return result2; + } + _modernize_event_3_to_4(event) { + if (event.type !== "action" && event.type !== "event") { + return event; + } + const metadata = event.metadata; + if (metadata.internal || metadata.method.startsWith("tracing")) + return null; + if (event.type === "event") { + if (metadata.method === "__create__" && metadata.type === "ConsoleMessage") { + return { + type: "object", + class: metadata.type, + guid: metadata.params.guid, + initializer: metadata.params.initializer + }; + } + return { + type: "event", + time: metadata.startTime, + class: metadata.type, + method: metadata.method, + params: metadata.params, + pageId: metadata.pageId + }; + } + return { + type: "action", + callId: metadata.id, + startTime: metadata.startTime, + endTime: metadata.endTime, + apiName: metadata.apiName || metadata.type + "." + metadata.method, + class: metadata.type, + method: metadata.method, + params: metadata.params, + // eslint-disable-next-line no-restricted-globals + wallTime: metadata.wallTime || Date.now(), + log: metadata.log, + beforeSnapshot: metadata.snapshots.find((s) => s.title === "before")?.snapshotName, + inputSnapshot: metadata.snapshots.find((s) => s.title === "input")?.snapshotName, + afterSnapshot: metadata.snapshots.find((s) => s.title === "after")?.snapshotName, + error: metadata.error?.error, + result: metadata.result, + point: metadata.point, + pageId: metadata.pageId + }; + } + _modernize_4_to_5(events) { + const result2 = []; + for (const event of events) { + const e = this._modernize_event_4_to_5(event); + if (e) + result2.push(e); + } + return result2; + } + _modernize_event_4_to_5(event) { + if (event.type === "event" && event.method === "__create__" && event.class === "JSHandle") + this._jsHandles.set(event.params.guid, event.params.initializer); + if (event.type === "object") { + if (event.class !== "ConsoleMessage") + return null; + const args = event.initializer.args?.map((arg) => { + if (arg.guid) { + const handle = this._jsHandles.get(arg.guid); + return { preview: handle?.preview || "", value: "" }; + } + return { preview: arg.preview || "", value: arg.value || "" }; + }); + this._consoleObjects.set(event.guid, { + type: event.initializer.type, + text: event.initializer.text, + location: event.initializer.location, + args + }); + return null; + } + if (event.type === "event" && event.method === "console") { + const consoleMessage = this._consoleObjects.get(event.params.message?.guid || ""); + if (!consoleMessage) + return null; + return { + type: "console", + time: event.time, + pageId: event.pageId, + messageType: consoleMessage.type, + text: consoleMessage.text, + args: consoleMessage.args, + location: consoleMessage.location + }; + } + return event; + } + _modernize_5_to_6(events) { + const result2 = []; + for (const event of events) { + result2.push(event); + if (event.type !== "after" || !event.log.length) + continue; + for (const log2 of event.log) { + result2.push({ + type: "log", + callId: event.callId, + message: log2, + time: -1 + }); + } + } + return result2; + } + _modernize_6_to_7(events) { + const result2 = []; + if (!this._processedContextCreatedEvent() && events[0].type !== "context-options") { + const event = { + type: "context-options", + origin: "testRunner", + version: 6, + browserName: "", + options: {}, + platform: "unknown", + wallTime: 0, + monotonicTime: 0, + sdkLanguage: "javascript", + contextId: "" + }; + result2.push(event); + } + for (const event of events) { + if (event.type === "context-options") { + result2.push({ ...event, monotonicTime: 0, origin: "library", contextId: "" }); + continue; + } + if (event.type === "before" || event.type === "action") { + if (!this._contextEntry.wallTime) + this._contextEntry.wallTime = event.wallTime; + const eventAsV6 = event; + const eventAsV7 = event; + eventAsV7.stepId = `${eventAsV6.apiName}@${eventAsV6.wallTime}`; + result2.push(eventAsV7); + } else { + result2.push(event); + } + } + return result2; + } + _modernize_7_to_8(events) { + const result2 = []; + for (const event of events) { + if (event.type === "before" || event.type === "action") { + const eventAsV7 = event; + const eventAsV8 = event; + if (eventAsV7.apiName) { + eventAsV8.title = eventAsV7.apiName; + delete eventAsV8.apiName; + } + eventAsV8.stepId = eventAsV7.stepId ?? eventAsV7.callId; + result2.push(eventAsV8); + } else { + result2.push(event); + } + } + return result2; + } + }; + } +}); + +// packages/isomorphic/trace/traceLoader.ts +function stripEncodingFromContentType(contentType) { + const charset = contentType.match(/^(.*);\s*charset=.*$/); + if (charset) + return charset[1]; + return contentType; +} +function createEmptyContext() { + return { + origin: "testRunner", + startTime: Number.MAX_SAFE_INTEGER, + wallTime: Number.MAX_SAFE_INTEGER, + endTime: 0, + browserName: "", + options: { + deviceScaleFactor: 1, + isMobile: false, + viewport: { width: 1280, height: 800 } + }, + pages: [], + resources: [], + actions: [], + events: [], + errors: [], + stdio: [], + hasSource: false, + contextId: "" + }; +} +var TraceLoader; +var init_traceLoader = __esm({ + "packages/isomorphic/trace/traceLoader.ts"() { + "use strict"; + init_traceUtils(); + init_snapshotStorage(); + init_traceModernizer(); + TraceLoader = class { + constructor() { + this.contextEntries = []; + this._resourceToContentType = /* @__PURE__ */ new Map(); + } + async load(backend, traceFile, unzipProgress) { + this._backend = backend; + const prefix = traceFile?.match(/(.+)\.trace$/)?.[1]; + const prefixes = []; + let hasSource = false; + for (const entryName of await this._backend.entryNames()) { + const match = entryName.match(/(.+)\.trace$/); + if (match && (!prefix || prefix === match[1])) + prefixes.push(match[1] || ""); + if (entryName.includes("src@")) + hasSource = true; + } + if (!prefixes.length) + throw new Error("Cannot find .trace file"); + this._snapshotStorage = new SnapshotStorage(); + const total = prefixes.length * 3; + let done = 0; + for (const prefix2 of prefixes) { + const contextEntry = createEmptyContext(); + contextEntry.hasSource = hasSource; + const modernizer = new TraceModernizer(contextEntry, this._snapshotStorage); + const trace = await this._backend.readText(prefix2 + ".trace") || ""; + modernizer.appendTrace(trace); + unzipProgress?.(++done, total); + const network = await this._backend.readText(prefix2 + ".network") || ""; + modernizer.appendTrace(network); + unzipProgress?.(++done, total); + contextEntry.actions = modernizer.actions().sort((a1, a2) => a1.startTime - a2.startTime); + if (!backend.isLive()) { + for (const action of contextEntry.actions.slice().reverse()) { + if (!action.endTime && !action.error) { + for (const a of contextEntry.actions) { + if (a.parentId === action.callId && action.endTime < a.endTime) + action.endTime = a.endTime; + } + } + } + } + const stacks = await this._backend.readText(prefix2 + ".stacks"); + if (stacks) { + const callMetadata = parseClientSideCallMetadata(JSON.parse(stacks)); + for (const action of contextEntry.actions) + action.stack = action.stack || callMetadata.get(action.callId); + } + unzipProgress?.(++done, total); + for (const resource of contextEntry.resources) { + if (resource.request.postData?._sha1) + this._resourceToContentType.set(resource.request.postData._sha1, stripEncodingFromContentType(resource.request.postData.mimeType)); + if (resource.response.content?._sha1) + this._resourceToContentType.set(resource.response.content._sha1, stripEncodingFromContentType(resource.response.content.mimeType)); + } + this.contextEntries.push(contextEntry); + } + this._snapshotStorage.finalize(); + } + async hasEntry(filename) { + return this._backend.hasEntry(filename); + } + async resourceForSha1(sha1) { + const blob = await this._backend.readBlob("resources/" + sha1); + const contentType = this._resourceToContentType.get(sha1); + if (!blob || contentType === void 0 || contentType === "x-unknown") + return blob; + return new Blob([blob], { type: contentType }); + } + storage() { + return this._snapshotStorage; + } + }; + } +}); + +// packages/isomorphic/trace/traceModel.ts +function indexModel(context2) { + for (const page of context2.pages) + page[contextSymbol] = context2; + for (let i = 0; i < context2.actions.length; ++i) { + const action = context2.actions[i]; + action[contextSymbol] = context2; + } + let lastNonRouteAction = void 0; + for (let i = context2.actions.length - 1; i >= 0; i--) { + const action = context2.actions[i]; + action[nextInContextSymbol] = lastNonRouteAction; + if (action.class !== "Route") + lastNonRouteAction = action; + } + for (const event of context2.events) + event[contextSymbol] = context2; + for (const resource of context2.resources) + resource[contextSymbol] = context2; +} +function mergeActionsAndUpdateTiming(contexts) { + const result2 = []; + const actions = mergeActionsAndUpdateTimingSameTrace(contexts); + result2.push(...actions); + result2.sort((a1, a2) => { + if (a2.parentId === a1.callId) + return 1; + if (a1.parentId === a2.callId) + return -1; + return a1.endTime - a2.endTime; + }); + for (let i = 1; i < result2.length; ++i) + result2[i][prevByEndTimeSymbol] = result2[i - 1]; + result2.sort((a1, a2) => { + if (a2.parentId === a1.callId) + return -1; + if (a1.parentId === a2.callId) + return 1; + return a1.startTime - a2.startTime; + }); + for (let i = 0; i + 1 < result2.length; ++i) + result2[i][nextByStartTimeSymbol] = result2[i + 1]; + return result2; +} +function mergeActionsAndUpdateTimingSameTrace(contexts) { + const map = /* @__PURE__ */ new Map(); + const libraryContexts = contexts.filter((context2) => context2.origin === "library"); + const testRunnerContexts = contexts.filter((context2) => context2.origin === "testRunner"); + if (!testRunnerContexts.length || !libraryContexts.length) { + return contexts.map((context2) => { + return context2.actions.map((action) => ({ ...action, context: context2 })); + }).flat(); + } + for (const context2 of libraryContexts) { + for (const action of context2.actions) { + map.set(action.stepId || `tmp-step@${++lastTmpStepId}`, { ...action, context: context2 }); + } + } + const delta = monotonicTimeDeltaBetweenLibraryAndRunner(testRunnerContexts, map); + if (delta) + adjustMonotonicTime(libraryContexts, delta); + const nonPrimaryIdToPrimaryId = /* @__PURE__ */ new Map(); + for (const context2 of testRunnerContexts) { + for (const action of context2.actions) { + const existing = action.stepId && map.get(action.stepId); + if (existing) { + nonPrimaryIdToPrimaryId.set(action.callId, existing.callId); + if (action.error) + existing.error = action.error; + if (action.attachments) + existing.attachments = action.attachments; + if (action.annotations) + existing.annotations = action.annotations; + if (action.parentId) + existing.parentId = nonPrimaryIdToPrimaryId.get(action.parentId) ?? action.parentId; + if (action.group) + existing.group = action.group; + existing.startTime = action.startTime; + existing.endTime = action.endTime; + continue; + } + if (action.parentId) + action.parentId = nonPrimaryIdToPrimaryId.get(action.parentId) ?? action.parentId; + map.set(action.stepId || `tmp-step@${++lastTmpStepId}`, { ...action, context: context2 }); + } + } + return [...map.values()]; +} +function adjustMonotonicTime(contexts, monotonicTimeDelta) { + for (const context2 of contexts) { + context2.startTime += monotonicTimeDelta; + context2.endTime += monotonicTimeDelta; + for (const action of context2.actions) { + if (action.startTime) + action.startTime += monotonicTimeDelta; + if (action.endTime) + action.endTime += monotonicTimeDelta; + } + for (const event of context2.events) + event.time += monotonicTimeDelta; + for (const event of context2.stdio) + event.timestamp += monotonicTimeDelta; + for (const page of context2.pages) { + for (const frame of page.screencastFrames) + frame.timestamp += monotonicTimeDelta; + } + for (const resource of context2.resources) { + if (resource._monotonicTime) + resource._monotonicTime += monotonicTimeDelta; + } + } +} +function monotonicTimeDeltaBetweenLibraryAndRunner(nonPrimaryContexts, libraryActions) { + for (const context2 of nonPrimaryContexts) { + for (const action of context2.actions) { + if (!action.startTime) + continue; + const libraryAction = action.stepId ? libraryActions.get(action.stepId) : void 0; + if (libraryAction) + return action.startTime - libraryAction.startTime; + } + } + return 0; +} +function buildActionTree(actions) { + const itemMap = /* @__PURE__ */ new Map(); + for (const action of actions) { + itemMap.set(action.callId, { + id: action.callId, + parent: void 0, + children: [], + action + }); + } + const rootItem = { action: { ...kFakeRootAction }, id: "", parent: void 0, children: [] }; + for (const item of itemMap.values()) { + rootItem.action.startTime = Math.min(rootItem.action.startTime, item.action.startTime); + rootItem.action.endTime = Math.max(rootItem.action.endTime, item.action.endTime); + const parent = item.action.parentId ? itemMap.get(item.action.parentId) || rootItem : rootItem; + parent.children.push(item); + item.parent = parent; + } + const inheritStack = (item) => { + for (const child of item.children) { + child.action.stack = child.action.stack ?? item.action.stack; + inheritStack(child); + } + }; + inheritStack(rootItem); + return { rootItem, itemMap }; +} +function context(action) { + return action[contextSymbol]; +} +function nextInContext(action) { + return action[nextInContextSymbol]; +} +function previousActionByEndTime(action) { + return action[prevByEndTimeSymbol]; +} +function nextActionByStartTime(action) { + return action[nextByStartTimeSymbol]; +} +function stats(action) { + let errors = 0; + let warnings = 0; + for (const event of eventsForAction(action)) { + if (event.type === "console") { + const type3 = event.messageType; + if (type3 === "warning") + ++warnings; + else if (type3 === "error") + ++errors; + } + if (event.type === "event" && event.method === "pageError") + ++errors; + } + return { errors, warnings }; +} +function eventsForAction(action) { + let result2 = action[eventsSymbol]; + if (result2) + return result2; + const nextAction = nextInContext(action); + result2 = context(action).events.filter((event) => { + return event.time >= action.startTime && (!nextAction || event.time < nextAction.startTime); + }); + action[eventsSymbol] = result2; + return result2; +} +function collectSources(actions, errorDescriptors) { + const result2 = /* @__PURE__ */ new Map(); + for (const action of actions) { + for (const frame of action.stack || []) { + let source11 = result2.get(frame.file); + if (!source11) { + source11 = { errors: [], content: void 0 }; + result2.set(frame.file, source11); + } + } + } + for (const error of errorDescriptors) { + const { action, stack, message } = error; + if (!action || !stack) + continue; + result2.get(stack[0].file)?.errors.push({ + line: stack[0].line || 0, + message + }); + } + return result2; +} +var contextSymbol, nextInContextSymbol, prevByEndTimeSymbol, nextByStartTimeSymbol, eventsSymbol, TraceModel, lastTmpStepId, kFakeRootAction; +var init_traceModel = __esm({ + "packages/isomorphic/trace/traceModel.ts"() { + "use strict"; + init_protocolFormatter(); + contextSymbol = Symbol("context"); + nextInContextSymbol = Symbol("nextInContext"); + prevByEndTimeSymbol = Symbol("prevByEndTime"); + nextByStartTimeSymbol = Symbol("nextByStartTime"); + eventsSymbol = Symbol("events"); + TraceModel = class { + constructor(traceUri, contexts) { + contexts.forEach((contextEntry) => indexModel(contextEntry)); + const libraryContext = contexts.find((context2) => context2.origin === "library"); + this.traceUri = traceUri; + this.browserName = libraryContext?.browserName || ""; + this.sdkLanguage = libraryContext?.sdkLanguage; + this.channel = libraryContext?.channel; + this.testIdAttributeName = libraryContext?.testIdAttributeName; + this.platform = libraryContext?.platform || ""; + this.playwrightVersion = contexts.find((c) => c.playwrightVersion)?.playwrightVersion; + this.title = libraryContext?.title || ""; + this.options = libraryContext?.options || {}; + this.testTimeout = contexts.find((c) => c.origin === "testRunner")?.testTimeout; + this.actions = mergeActionsAndUpdateTiming(contexts); + this.pages = [].concat(...contexts.map((c) => c.pages)); + this.wallTime = contexts.map((c) => c.wallTime).reduce((prev, cur) => Math.min(prev || Number.MAX_VALUE, cur), Number.MAX_VALUE); + this.startTime = contexts.map((c) => c.startTime).reduce((prev, cur) => Math.min(prev, cur), Number.MAX_VALUE); + this.endTime = contexts.map((c) => c.endTime).reduce((prev, cur) => Math.max(prev, cur), Number.MIN_VALUE); + this.events = [].concat(...contexts.map((c) => c.events)); + this.stdio = [].concat(...contexts.map((c) => c.stdio)); + this.errors = [].concat(...contexts.map((c) => c.errors)); + this.hasSource = contexts.some((c) => c.hasSource); + this.hasStepData = contexts.some((context2) => context2.origin === "testRunner"); + this.resources = [...contexts.map((c) => c.resources)].flat().map((entry) => ({ ...entry, id: `${entry.pageref}-${entry.startedDateTime}-${entry.request.url}` })); + this.attachments = this.actions.flatMap((action) => action.attachments?.map((attachment) => ({ ...attachment, callId: action.callId, traceUri })) ?? []); + this.visibleAttachments = this.attachments.filter((attachment) => !attachment.name.startsWith("_")); + this.events.sort((a1, a2) => a1.time - a2.time); + this.resources.sort((a1, a2) => a1._monotonicTime - a2._monotonicTime); + this.errorDescriptors = this.hasStepData ? this._errorDescriptorsFromTestRunner() : this._errorDescriptorsFromActions(); + this.sources = collectSources(this.actions, this.errorDescriptors); + this.actionCounters = /* @__PURE__ */ new Map(); + for (const action of this.actions) { + action.group = action.group ?? getActionGroup({ type: action.class, method: action.method }); + if (action.group) + this.actionCounters.set(action.group, 1 + (this.actionCounters.get(action.group) || 0)); + } + } + createRelativeUrl(path59) { + const url2 = new URL("http://localhost/" + path59); + url2.searchParams.set("trace", this.traceUri); + return url2.toString().substring("http://localhost/".length); + } + failedAction() { + return this.actions.findLast((a) => a.error); + } + filteredActions(actionsFilter) { + const filter = new Set(actionsFilter); + return this.actions.filter((action) => !action.group || filter.has(action.group)); + } + renderActionTree(filter) { + const actions = this.filteredActions(filter ?? []); + const { rootItem } = buildActionTree(actions); + const actionTree = []; + const visit = (actionItem, indent) => { + const title = renderTitleForCall({ ...actionItem.action, type: actionItem.action.class }); + actionTree.push(`${indent}${title || actionItem.id}`); + for (const child of actionItem.children) + visit(child, indent + " "); + }; + rootItem.children.forEach((a) => visit(a, "")); + return actionTree; + } + _errorDescriptorsFromActions() { + const errors = []; + for (const action of this.actions || []) { + if (!action.error?.message) + continue; + errors.push({ + action, + stack: action.stack, + message: action.error.message + }); + } + return errors; + } + _errorDescriptorsFromTestRunner() { + return this.errors.filter((e) => !!e.message).map((error, i) => ({ + stack: error.stack, + message: error.message + })); + } + }; + lastTmpStepId = 0; + kFakeRootAction = { + type: "action", + callId: "", + startTime: 0, + endTime: 0, + class: "", + method: "", + params: {}, + log: [], + context: { + origin: "library", + startTime: 0, + endTime: 0, + browserName: "", + wallTime: 0, + options: {}, + pages: [], + resources: [], + actions: [], + events: [], + stdio: [], + errors: [], + hasSource: false, + contextId: "" + } + }; + } +}); + +// packages/isomorphic/yaml.ts +function yamlEscapeKeyIfNeeded(str) { + if (!yamlStringNeedsQuotes(str)) + return str; + return `'` + str.replace(/'/g, `''`) + `'`; +} +function yamlEscapeValueIfNeeded(str) { + if (!yamlStringNeedsQuotes(str)) + return str; + return '"' + str.replace(/[\\"\x00-\x1f\x7f-\x9f]/g, (c) => { + switch (c) { + case "\\": + return "\\\\"; + case '"': + return '\\"'; + case "\b": + return "\\b"; + case "\f": + return "\\f"; + case "\n": + return "\\n"; + case "\r": + return "\\r"; + case " ": + return "\\t"; + default: + const code = c.charCodeAt(0); + return "\\x" + code.toString(16).padStart(2, "0"); + } + }) + '"'; +} +function yamlStringNeedsQuotes(str) { + if (str.length === 0) + return true; + if (/^\s|\s$/.test(str)) + return true; + if (/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f]/.test(str)) + return true; + if (/^-/.test(str)) + return true; + if (/[\n:](\s|$)/.test(str)) + return true; + if (/\s#/.test(str)) + return true; + if (/[\n\r]/.test(str)) + return true; + if (/^[&*\],?!>|@"'#%]/.test(str)) + return true; + if (/[{}`]/.test(str)) + return true; + if (/^\[/.test(str)) + return true; + if (!isNaN(Number(str)) || ["y", "n", "yes", "no", "true", "false", "on", "off", "null"].includes(str.toLowerCase())) + return true; + return false; +} +var init_yaml = __esm({ + "packages/isomorphic/yaml.ts"() { + "use strict"; + } +}); + +// packages/isomorphic/index.ts +var isomorphic_exports = {}; +__export(isomorphic_exports, { + CSharpLocatorFactory: () => CSharpLocatorFactory, + DEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT: () => DEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT, + DEFAULT_PLAYWRIGHT_TIMEOUT: () => DEFAULT_PLAYWRIGHT_TIMEOUT, + InvalidSelectorError: () => InvalidSelectorError, + JavaLocatorFactory: () => JavaLocatorFactory, + JavaScriptLocatorFactory: () => JavaScriptLocatorFactory, + JsonlLocatorFactory: () => JsonlLocatorFactory, + KeyParser: () => KeyParser, + LongStandingScope: () => LongStandingScope, + ManualPromise: () => ManualPromise, + MultiMap: () => MultiMap, + ParserError: () => ParserError, + PythonLocatorFactory: () => PythonLocatorFactory, + Semaphore: () => Semaphore, + SnapshotServer: () => SnapshotServer, + SnapshotStorage: () => SnapshotStorage, + TraceLoader: () => TraceLoader, + TraceModel: () => TraceModel, + ansiRegex: () => ansiRegex, + ariaNodesEqual: () => ariaNodesEqual, + asLocator: () => asLocator, + asLocatorDescription: () => asLocatorDescription, + asLocators: () => asLocators, + assert: () => assert, + base64ByteLength: () => base64ByteLength, + buildActionTree: () => buildActionTree, + bytesToString: () => bytesToString, + cacheNormalizedWhitespaces: () => cacheNormalizedWhitespaces, + captureRawStack: () => captureRawStack, + constructURLBasedOnBaseURL: () => constructURLBasedOnBaseURL, + context: () => context, + customCSSNames: () => customCSSNames, + deserializeURLMatch: () => deserializeURLMatch, + escapeForAttributeSelector: () => escapeForAttributeSelector, + escapeForTextSelector: () => escapeForTextSelector, + escapeHTML: () => escapeHTML, + escapeHTMLAttribute: () => escapeHTMLAttribute, + escapeRegExp: () => escapeRegExp, + escapeTemplateString: () => escapeTemplateString, + escapeWithQuotes: () => escapeWithQuotes, + eventsForAction: () => eventsForAction, + findNewNode: () => findNewNode, + formatObject: () => formatObject, + formatObjectOrVoid: () => formatObjectOrVoid, + formatProtocolParam: () => formatProtocolParam, + getActionGroup: () => getActionGroup, + getExtensionForMimeType: () => getExtensionForMimeType, + getMetainfo: () => getMetainfo, + getMimeTypeForPath: () => getMimeTypeForPath, + globToRegexPattern: () => globToRegexPattern, + hasPointerCursor: () => hasPointerCursor, + headersArrayToObject: () => headersArrayToObject, + headersObjectToArray: () => headersObjectToArray, + isError: () => isError, + isHttpUrl: () => isHttpUrl, + isInvalidSelectorError: () => isInvalidSelectorError, + isJsonMimeType: () => isJsonMimeType, + isObject: () => isObject, + isRegExp: () => isRegExp, + isRegexString: () => isRegexString, + isString: () => isString, + isTextualMimeType: () => isTextualMimeType, + isURLPattern: () => isURLPattern, + isXmlMimeType: () => isXmlMimeType, + locatorCustomDescription: () => locatorCustomDescription, + locatorOrSelectorAsSelector: () => locatorOrSelectorAsSelector, + longestCommonSubstring: () => longestCommonSubstring, + methodMetainfo: () => methodMetainfo, + monotonicTime: () => monotonicTime, + msToString: () => msToString, + nextActionByStartTime: () => nextActionByStartTime, + noColors: () => noColors, + normalizeEscapedRegexQuotes: () => normalizeEscapedRegexQuotes, + normalizeWhiteSpace: () => normalizeWhiteSpace, + padImageToSize: () => padImageToSize, + parseAriaSnapshot: () => parseAriaSnapshot, + parseAriaSnapshotUnsafe: () => parseAriaSnapshotUnsafe, + parseAttributeSelector: () => parseAttributeSelector, + parseCSS: () => parseCSS, + parseClientSideCallMetadata: () => parseClientSideCallMetadata, + parseErrorStack: () => parseErrorStack, + parseRegex: () => parseRegex, + parseSelector: () => parseSelector, + parseStackFrame: () => parseStackFrame, + pollAgainstDeadline: () => pollAgainstDeadline, + previousActionByEndTime: () => previousActionByEndTime, + quoteCSSAttributeValue: () => quoteCSSAttributeValue, + raceAgainstDeadline: () => raceAgainstDeadline, + renderTitleForCall: () => renderTitleForCall, + resolveGlobToRegexPattern: () => resolveGlobToRegexPattern, + rewriteErrorMessage: () => rewriteErrorMessage, + scaleImageToSize: () => scaleImageToSize, + serializeClientSideCallMetadata: () => serializeClientSideCallMetadata, + serializeExpectedTextValues: () => serializeExpectedTextValues, + serializeSelector: () => serializeSelector, + serializeURLMatch: () => serializeURLMatch, + serializeURLPattern: () => serializeURLPattern, + setTimeOrigin: () => setTimeOrigin, + signalToPromise: () => signalToPromise, + splitErrorMessage: () => splitErrorMessage, + splitSelectorByFrame: () => splitSelectorByFrame, + stats: () => stats, + stringifySelector: () => stringifySelector, + stringifyStackFrames: () => stringifyStackFrames, + stripAnsiEscapes: () => stripAnsiEscapes, + textValue: () => textValue, + timeOrigin: () => timeOrigin, + toSnakeCase: () => toSnakeCase, + toTitleCase: () => toTitleCase, + tomlArray: () => tomlArray, + tomlBasicString: () => tomlBasicString, + tomlMultilineBasicString: () => tomlMultilineBasicString, + trimString: () => trimString, + trimStringWithEllipsis: () => trimStringWithEllipsis, + unsafeLocatorOrSelectorAsSelector: () => unsafeLocatorOrSelectorAsSelector, + urlMatches: () => urlMatches, + urlMatchesEqual: () => urlMatchesEqual, + validate: () => validate, + visitAllSelectorParts: () => visitAllSelectorParts, + webColors: () => webColors, + yamlEscapeKeyIfNeeded: () => yamlEscapeKeyIfNeeded, + yamlEscapeValueIfNeeded: () => yamlEscapeValueIfNeeded +}); +var init_isomorphic = __esm({ + "packages/isomorphic/index.ts"() { + "use strict"; + init_ariaSnapshot(); + init_expectUtils(); + init_assert(); + init_base64(); + init_colors(); + init_headers(); + init_imageUtils(); + init_jsonSchema(); + init_locatorGenerators(); + init_manualPromise(); + init_mimeType(); + init_multimap(); + init_protocolFormatter(); + init_protocolMetainfo(); + init_rtti(); + init_semaphore(); + init_stackTrace(); + init_stringUtils(); + init_formatUtils(); + init_time(); + init_timeoutRunner(); + init_snapshotServer(); + init_urlMatch(); + init_cssParser(); + init_locatorParser(); + init_selectorParser(); + init_snapshotStorage(); + init_traceLoader(); + init_traceModel(); + init_traceUtils(); + init_yaml(); + } +}); + +// packages/utils/ascii.ts +function wrapInASCIIBox(text2, padding = 0) { + const lines = text2.split("\n"); + const maxLength = Math.max(...lines.map((line) => line.length)); + return [ + "\u2554" + "\u2550".repeat(maxLength + padding * 2) + "\u2557", + ...lines.map((line) => "\u2551" + " ".repeat(padding) + line + " ".repeat(maxLength - line.length + padding) + "\u2551"), + "\u255A" + "\u2550".repeat(maxLength + padding * 2) + "\u255D" + ].join("\n"); +} +function jsonStringifyForceASCII(object) { + return JSON.stringify(object).replace( + /[\u007f-\uffff]/g, + (c) => "\\u" + ("0000" + c.charCodeAt(0).toString(16)).slice(-4) + ); +} +var init_ascii = __esm({ + "packages/utils/ascii.ts"() { + "use strict"; + } +}); + +// packages/utils/chromiumChannels.ts +function defaultUserDataDirForChannel(channel) { + return channelToDefaultUserDataDir.get(channel)?.[process.platform]; +} +function isChromiumChannelName(channel) { + return channelToDefaultUserDataDir.has(channel); +} +var import_os, import_path, channelToDefaultUserDataDir; +var init_chromiumChannels = __esm({ + "packages/utils/chromiumChannels.ts"() { + "use strict"; + import_os = __toESM(require("os")); + import_path = __toESM(require("path")); + channelToDefaultUserDataDir = /* @__PURE__ */ new Map([ + ["chrome", { + "linux": import_path.default.join(import_os.default.homedir(), ".config", "google-chrome"), + "darwin": import_path.default.join(import_os.default.homedir(), "Library", "Application Support", "Google", "Chrome"), + "win32": import_path.default.join(process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local"), "Google", "Chrome", "User Data") + }], + ["chrome-beta", { + "linux": import_path.default.join(import_os.default.homedir(), ".config", "google-chrome-beta"), + "darwin": import_path.default.join(import_os.default.homedir(), "Library", "Application Support", "Google", "Chrome Beta"), + "win32": import_path.default.join(process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local"), "Google", "Chrome Beta", "User Data") + }], + ["chrome-dev", { + "linux": import_path.default.join(import_os.default.homedir(), ".config", "google-chrome-unstable"), + "darwin": import_path.default.join(import_os.default.homedir(), "Library", "Application Support", "Google", "Chrome Dev"), + "win32": import_path.default.join(process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local"), "Google", "Chrome Dev", "User Data") + }], + ["chrome-canary", { + "linux": import_path.default.join(import_os.default.homedir(), ".config", "google-chrome-canary"), + "darwin": import_path.default.join(import_os.default.homedir(), "Library", "Application Support", "Google", "Chrome Canary"), + "win32": import_path.default.join(process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local"), "Google", "Chrome SxS", "User Data") + }], + ["msedge", { + "linux": import_path.default.join(import_os.default.homedir(), ".config", "microsoft-edge"), + "darwin": import_path.default.join(import_os.default.homedir(), "Library", "Application Support", "Microsoft Edge"), + "win32": import_path.default.join(process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local"), "Microsoft", "Edge", "User Data") + }], + ["msedge-beta", { + "linux": import_path.default.join(import_os.default.homedir(), ".config", "microsoft-edge-beta"), + "darwin": import_path.default.join(import_os.default.homedir(), "Library", "Application Support", "Microsoft Edge Beta"), + "win32": import_path.default.join(process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local"), "Microsoft", "Edge Beta", "User Data") + }], + ["msedge-dev", { + "linux": import_path.default.join(import_os.default.homedir(), ".config", "microsoft-edge-dev"), + "darwin": import_path.default.join(import_os.default.homedir(), "Library", "Application Support", "Microsoft Edge Dev"), + "win32": import_path.default.join(process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local"), "Microsoft", "Edge Dev", "User Data") + }], + ["msedge-canary", { + "linux": import_path.default.join(import_os.default.homedir(), ".config", "microsoft-edge-canary"), + "darwin": import_path.default.join(import_os.default.homedir(), "Library", "Application Support", "Microsoft Edge Canary"), + "win32": import_path.default.join(process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local"), "Microsoft", "Edge SxS", "User Data") + }] + ]); + } +}); + +// packages/utils/third_party/pixelmatch.js +var require_pixelmatch = __commonJS({ + "packages/utils/third_party/pixelmatch.js"(exports2, module2) { + "use strict"; + module2.exports = pixelmatch2; + var defaultOptions = { + threshold: 0.1, + // matching threshold (0 to 1); smaller is more sensitive + includeAA: false, + // whether to skip anti-aliasing detection + alpha: 0.1, + // opacity of original image in diff output + aaColor: [255, 255, 0], + // color of anti-aliased pixels in diff output + diffColor: [255, 0, 0], + // color of different pixels in diff output + diffColorAlt: null, + // whether to detect dark on light differences between img1 and img2 and set an alternative color to differentiate between the two + diffMask: false + // draw the diff over a transparent background (a mask) + }; + function pixelmatch2(img1, img2, output, width, height, options) { + if (!isPixelData(img1) || !isPixelData(img2) || output && !isPixelData(output)) + throw new Error("Image data: Uint8Array, Uint8ClampedArray or Buffer expected."); + if (img1.length !== img2.length || output && output.length !== img1.length) + throw new Error("Image sizes do not match."); + if (img1.length !== width * height * 4) throw new Error("Image data size does not match width/height."); + options = Object.assign({}, defaultOptions, options); + const len = width * height; + const a32 = new Uint32Array(img1.buffer, img1.byteOffset, len); + const b32 = new Uint32Array(img2.buffer, img2.byteOffset, len); + let identical = true; + for (let i = 0; i < len; i++) { + if (a32[i] !== b32[i]) { + identical = false; + break; + } + } + if (identical) { + if (output && !options.diffMask) { + for (let i = 0; i < len; i++) drawGrayPixel(img1, 4 * i, options.alpha, output); + } + return 0; + } + const maxDelta = 35215 * options.threshold * options.threshold; + let diff2 = 0; + for (let y = 0; y < height; y++) { + for (let x = 0; x < width; x++) { + const pos = (y * width + x) * 4; + const delta = colorDelta(img1, img2, pos, pos); + if (Math.abs(delta) > maxDelta) { + if (!options.includeAA && (antialiased(img1, x, y, width, height, img2) || antialiased(img2, x, y, width, height, img1))) { + if (output && !options.diffMask) drawPixel2(output, pos, ...options.aaColor); + } else { + if (output) { + drawPixel2(output, pos, ...delta < 0 && options.diffColorAlt || options.diffColor); + } + diff2++; + } + } else if (output) { + if (!options.diffMask) drawGrayPixel(img1, pos, options.alpha, output); + } + } + } + return diff2; + } + function isPixelData(arr) { + return ArrayBuffer.isView(arr) && arr.constructor.BYTES_PER_ELEMENT === 1; + } + function antialiased(img, x1, y1, width, height, img2) { + const x0 = Math.max(x1 - 1, 0); + const y0 = Math.max(y1 - 1, 0); + const x2 = Math.min(x1 + 1, width - 1); + const y2 = Math.min(y1 + 1, height - 1); + const pos = (y1 * width + x1) * 4; + let zeroes = x1 === x0 || x1 === x2 || y1 === y0 || y1 === y2 ? 1 : 0; + let min = 0; + let max = 0; + let minX, minY, maxX, maxY; + for (let x = x0; x <= x2; x++) { + for (let y = y0; y <= y2; y++) { + if (x === x1 && y === y1) continue; + const delta = colorDelta(img, img, pos, (y * width + x) * 4, true); + if (delta === 0) { + zeroes++; + if (zeroes > 2) return false; + } else if (delta < min) { + min = delta; + minX = x; + minY = y; + } else if (delta > max) { + max = delta; + maxX = x; + maxY = y; + } + } + } + if (min === 0 || max === 0) return false; + return hasManySiblings(img, minX, minY, width, height) && hasManySiblings(img2, minX, minY, width, height) || hasManySiblings(img, maxX, maxY, width, height) && hasManySiblings(img2, maxX, maxY, width, height); + } + function hasManySiblings(img, x1, y1, width, height) { + const x0 = Math.max(x1 - 1, 0); + const y0 = Math.max(y1 - 1, 0); + const x2 = Math.min(x1 + 1, width - 1); + const y2 = Math.min(y1 + 1, height - 1); + const pos = (y1 * width + x1) * 4; + let zeroes = x1 === x0 || x1 === x2 || y1 === y0 || y1 === y2 ? 1 : 0; + for (let x = x0; x <= x2; x++) { + for (let y = y0; y <= y2; y++) { + if (x === x1 && y === y1) continue; + const pos2 = (y * width + x) * 4; + if (img[pos] === img[pos2] && img[pos + 1] === img[pos2 + 1] && img[pos + 2] === img[pos2 + 2] && img[pos + 3] === img[pos2 + 3]) zeroes++; + if (zeroes > 2) return true; + } + } + return false; + } + function colorDelta(img1, img2, k, m, yOnly) { + let r1 = img1[k + 0]; + let g1 = img1[k + 1]; + let b1 = img1[k + 2]; + let a1 = img1[k + 3]; + let r2 = img2[m + 0]; + let g2 = img2[m + 1]; + let b2 = img2[m + 2]; + let a2 = img2[m + 3]; + if (a1 === a2 && r1 === r2 && g1 === g2 && b1 === b2) return 0; + if (a1 < 255) { + a1 /= 255; + r1 = blend(r1, a1); + g1 = blend(g1, a1); + b1 = blend(b1, a1); + } + if (a2 < 255) { + a2 /= 255; + r2 = blend(r2, a2); + g2 = blend(g2, a2); + b2 = blend(b2, a2); + } + const y1 = rgb2y(r1, g1, b1); + const y2 = rgb2y(r2, g2, b2); + const y = y1 - y2; + if (yOnly) return y; + const i = rgb2i(r1, g1, b1) - rgb2i(r2, g2, b2); + const q = rgb2q(r1, g1, b1) - rgb2q(r2, g2, b2); + const delta = 0.5053 * y * y + 0.299 * i * i + 0.1957 * q * q; + return y1 > y2 ? -delta : delta; + } + function rgb2y(r, g, b) { + return r * 0.29889531 + g * 0.58662247 + b * 0.11448223; + } + function rgb2i(r, g, b) { + return r * 0.59597799 - g * 0.2741761 - b * 0.32180189; + } + function rgb2q(r, g, b) { + return r * 0.21147017 - g * 0.52261711 + b * 0.31114694; + } + function blend(c, a) { + return 255 + (c - 255) * a; + } + function drawPixel2(output, pos, r, g, b) { + output[pos + 0] = r; + output[pos + 1] = g; + output[pos + 2] = b; + output[pos + 3] = 255; + } + function drawGrayPixel(img, i, alpha, output) { + const r = img[i + 0]; + const g = img[i + 1]; + const b = img[i + 2]; + const val = blend(rgb2y(r, g, b), alpha * img[i + 3] / 255); + drawPixel2(output, i, val, val, val); + } + } +}); + +// packages/utils/image_tools/colorUtils.ts +function blendWithWhite(c, a) { + return 255 + (c - 255) * a; +} +function rgb2gray(r, g, b) { + return 77 * r + 150 * g + 29 * b + 128 >> 8; +} +function colorDeltaE94(rgb1, rgb2) { + const [l1, a1, b1] = xyz2lab(srgb2xyz(rgb1)); + const [l2, a2, b2] = xyz2lab(srgb2xyz(rgb2)); + const deltaL = l1 - l2; + const deltaA = a1 - a2; + const deltaB = b1 - b2; + const c1 = Math.sqrt(a1 ** 2 + b1 ** 2); + const c2 = Math.sqrt(a2 ** 2 + b2 ** 2); + const deltaC = c1 - c2; + let deltaH = deltaA ** 2 + deltaB ** 2 - deltaC ** 2; + deltaH = deltaH < 0 ? 0 : Math.sqrt(deltaH); + const k1 = 0.045; + const k2 = 0.015; + const kL = 1; + const kC = 1; + const kH = 1; + const sC = 1 + k1 * c1; + const sH = 1 + k2 * c1; + const sL = 1; + return Math.sqrt((deltaL / sL / kL) ** 2 + (deltaC / sC / kC) ** 2 + (deltaH / sH / kH) ** 2); +} +function srgb2xyz(rgb) { + let r = rgb[0] / 255; + let g = rgb[1] / 255; + let b = rgb[2] / 255; + r = r > 0.04045 ? Math.pow((r + 0.055) / 1.055, 2.4) : r / 12.92; + g = g > 0.04045 ? Math.pow((g + 0.055) / 1.055, 2.4) : g / 12.92; + b = b > 0.04045 ? Math.pow((b + 0.055) / 1.055, 2.4) : b / 12.92; + return [ + r * 0.4124 + g * 0.3576 + b * 0.1805, + r * 0.2126 + g * 0.7152 + b * 0.0722, + r * 0.0193 + g * 0.1192 + b * 0.9505 + ]; +} +function xyz2lab(xyz) { + const x = xyz[0] / 0.950489; + const y = xyz[1]; + const z31 = xyz[2] / 1.08884; + const fx = x > sigma_pow3 ? x ** (1 / 3) : x / 3 / sigma_pow2 + 4 / 29; + const fy = y > sigma_pow3 ? y ** (1 / 3) : y / 3 / sigma_pow2 + 4 / 29; + const fz = z31 > sigma_pow3 ? z31 ** (1 / 3) : z31 / 3 / sigma_pow2 + 4 / 29; + const l = 116 * fy - 16; + const a = 500 * (fx - fy); + const b = 200 * (fy - fz); + return [l, a, b]; +} +var sigma_pow2, sigma_pow3; +var init_colorUtils = __esm({ + "packages/utils/image_tools/colorUtils.ts"() { + "use strict"; + sigma_pow2 = 6 * 6 / 29 / 29; + sigma_pow3 = 6 * 6 * 6 / 29 / 29 / 29; + } +}); + +// packages/utils/image_tools/imageChannel.ts +var ImageChannel; +var init_imageChannel = __esm({ + "packages/utils/image_tools/imageChannel.ts"() { + "use strict"; + init_colorUtils(); + ImageChannel = class _ImageChannel { + static intoRGB(width, height, data, options = {}) { + const { + paddingSize = 0, + paddingColorOdd = [255, 0, 255], + paddingColorEven = [0, 255, 0] + } = options; + const newWidth = width + 2 * paddingSize; + const newHeight = height + 2 * paddingSize; + const r = new Uint8Array(newWidth * newHeight); + const g = new Uint8Array(newWidth * newHeight); + const b = new Uint8Array(newWidth * newHeight); + for (let y = 0; y < newHeight; ++y) { + for (let x = 0; x < newWidth; ++x) { + const index = y * newWidth + x; + if (y >= paddingSize && y < newHeight - paddingSize && x >= paddingSize && x < newWidth - paddingSize) { + const offset = ((y - paddingSize) * width + (x - paddingSize)) * 4; + const alpha = data[offset + 3] === 255 ? 1 : data[offset + 3] / 255; + r[index] = blendWithWhite(data[offset], alpha); + g[index] = blendWithWhite(data[offset + 1], alpha); + b[index] = blendWithWhite(data[offset + 2], alpha); + } else { + const color = (y + x) % 2 === 0 ? paddingColorEven : paddingColorOdd; + r[index] = color[0]; + g[index] = color[1]; + b[index] = color[2]; + } + } + } + return [ + new _ImageChannel(newWidth, newHeight, r), + new _ImageChannel(newWidth, newHeight, g), + new _ImageChannel(newWidth, newHeight, b) + ]; + } + constructor(width, height, data) { + this.data = data; + this.width = width; + this.height = height; + } + get(x, y) { + return this.data[y * this.width + x]; + } + boundXY(x, y) { + return [ + Math.min(Math.max(x, 0), this.width - 1), + Math.min(Math.max(y, 0), this.height - 1) + ]; + } + }; + } +}); + +// packages/utils/image_tools/stats.ts +function ssim(stats2, x1, y1, x2, y2) { + const mean1 = stats2.meanC1(x1, y1, x2, y2); + const mean2 = stats2.meanC2(x1, y1, x2, y2); + const var1 = stats2.varianceC1(x1, y1, x2, y2); + const var2 = stats2.varianceC2(x1, y1, x2, y2); + const cov = stats2.covariance(x1, y1, x2, y2); + const c1 = (0.01 * DYNAMIC_RANGE) ** 2; + const c2 = (0.03 * DYNAMIC_RANGE) ** 2; + return (2 * mean1 * mean2 + c1) * (2 * cov + c2) / (mean1 ** 2 + mean2 ** 2 + c1) / (var1 + var2 + c2); +} +var DYNAMIC_RANGE, FastStats; +var init_stats = __esm({ + "packages/utils/image_tools/stats.ts"() { + "use strict"; + DYNAMIC_RANGE = 2 ** 8 - 1; + FastStats = class { + constructor(c1, c2) { + this.c1 = c1; + this.c2 = c2; + const { width, height } = c1; + this._partialSumC1 = new Array(width * height); + this._partialSumC2 = new Array(width * height); + this._partialSumSq1 = new Array(width * height); + this._partialSumSq2 = new Array(width * height); + this._partialSumMult = new Array(width * height); + const recalc = (mx, idx, initial, x, y) => { + mx[idx] = initial; + if (y > 0) + mx[idx] += mx[(y - 1) * width + x]; + if (x > 0) + mx[idx] += mx[y * width + x - 1]; + if (x > 0 && y > 0) + mx[idx] -= mx[(y - 1) * width + x - 1]; + }; + for (let y = 0; y < height; ++y) { + for (let x = 0; x < width; ++x) { + const idx = y * width + x; + recalc(this._partialSumC1, idx, this.c1.data[idx], x, y); + recalc(this._partialSumC2, idx, this.c2.data[idx], x, y); + recalc(this._partialSumSq1, idx, this.c1.data[idx] * this.c1.data[idx], x, y); + recalc(this._partialSumSq2, idx, this.c2.data[idx] * this.c2.data[idx], x, y); + recalc(this._partialSumMult, idx, this.c1.data[idx] * this.c2.data[idx], x, y); + } + } + } + _sum(partialSum, x1, y1, x2, y2) { + const width = this.c1.width; + let result2 = partialSum[y2 * width + x2]; + if (y1 > 0) + result2 -= partialSum[(y1 - 1) * width + x2]; + if (x1 > 0) + result2 -= partialSum[y2 * width + x1 - 1]; + if (x1 > 0 && y1 > 0) + result2 += partialSum[(y1 - 1) * width + x1 - 1]; + return result2; + } + meanC1(x1, y1, x2, y2) { + const N = (y2 - y1 + 1) * (x2 - x1 + 1); + return this._sum(this._partialSumC1, x1, y1, x2, y2) / N; + } + meanC2(x1, y1, x2, y2) { + const N = (y2 - y1 + 1) * (x2 - x1 + 1); + return this._sum(this._partialSumC2, x1, y1, x2, y2) / N; + } + varianceC1(x1, y1, x2, y2) { + const N = (y2 - y1 + 1) * (x2 - x1 + 1); + return (this._sum(this._partialSumSq1, x1, y1, x2, y2) - this._sum(this._partialSumC1, x1, y1, x2, y2) ** 2 / N) / N; + } + varianceC2(x1, y1, x2, y2) { + const N = (y2 - y1 + 1) * (x2 - x1 + 1); + return (this._sum(this._partialSumSq2, x1, y1, x2, y2) - this._sum(this._partialSumC2, x1, y1, x2, y2) ** 2 / N) / N; + } + covariance(x1, y1, x2, y2) { + const N = (y2 - y1 + 1) * (x2 - x1 + 1); + return (this._sum(this._partialSumMult, x1, y1, x2, y2) - this._sum(this._partialSumC1, x1, y1, x2, y2) * this._sum(this._partialSumC2, x1, y1, x2, y2) / N) / N; + } + }; + } +}); + +// packages/utils/image_tools/compare.ts +function drawPixel(width, data, x, y, r, g, b) { + const idx = (y * width + x) * 4; + data[idx + 0] = r; + data[idx + 1] = g; + data[idx + 2] = b; + data[idx + 3] = 255; +} +function compare(actual, expected, diff2, width, height, options = {}) { + const { + maxColorDeltaE94 = 1 + } = options; + const paddingSize = Math.max(VARIANCE_WINDOW_RADIUS, SSIM_WINDOW_RADIUS); + const paddingColorEven = [255, 0, 255]; + const paddingColorOdd = [0, 255, 0]; + const [r1, g1, b1] = ImageChannel.intoRGB(width, height, expected, { + paddingSize, + paddingColorEven, + paddingColorOdd + }); + const [r2, g2, b2] = ImageChannel.intoRGB(width, height, actual, { + paddingSize, + paddingColorEven, + paddingColorOdd + }); + const noop = (x, y) => { + }; + const drawRedPixel = diff2 ? (x, y) => drawPixel(width, diff2, x - paddingSize, y - paddingSize, 255, 0, 0) : noop; + const drawYellowPixel = diff2 ? (x, y) => drawPixel(width, diff2, x - paddingSize, y - paddingSize, 255, 255, 0) : noop; + const drawGrayPixel = diff2 ? (x, y) => { + const gray = rgb2gray(r1.get(x, y), g1.get(x, y), b1.get(x, y)); + const value2 = blendWithWhite(gray, 0.1); + drawPixel(width, diff2, x - paddingSize, y - paddingSize, value2, value2, value2); + } : noop; + let fastR; + let fastG; + let fastB; + let diffCount = 0; + for (let y = paddingSize; y < r1.height - paddingSize; ++y) { + for (let x = paddingSize; x < r1.width - paddingSize; ++x) { + if (r1.get(x, y) === r2.get(x, y) && g1.get(x, y) === g2.get(x, y) && b1.get(x, y) === b2.get(x, y)) { + drawGrayPixel(x, y); + continue; + } + const delta = colorDeltaE94( + [r1.get(x, y), g1.get(x, y), b1.get(x, y)], + [r2.get(x, y), g2.get(x, y), b2.get(x, y)] + ); + if (delta <= maxColorDeltaE94) { + drawGrayPixel(x, y); + continue; + } + if (!fastR || !fastG || !fastB) { + fastR = new FastStats(r1, r2); + fastG = new FastStats(g1, g2); + fastB = new FastStats(b1, b2); + } + const [varX1, varY1] = r1.boundXY(x - VARIANCE_WINDOW_RADIUS, y - VARIANCE_WINDOW_RADIUS); + const [varX2, varY2] = r1.boundXY(x + VARIANCE_WINDOW_RADIUS, y + VARIANCE_WINDOW_RADIUS); + const var1 = fastR.varianceC1(varX1, varY1, varX2, varY2) + fastG.varianceC1(varX1, varY1, varX2, varY2) + fastB.varianceC1(varX1, varY1, varX2, varY2); + const var2 = fastR.varianceC2(varX1, varY1, varX2, varY2) + fastG.varianceC2(varX1, varY1, varX2, varY2) + fastB.varianceC2(varX1, varY1, varX2, varY2); + if (var1 === 0 || var2 === 0) { + drawRedPixel(x, y); + ++diffCount; + continue; + } + const [ssimX1, ssimY1] = r1.boundXY(x - SSIM_WINDOW_RADIUS, y - SSIM_WINDOW_RADIUS); + const [ssimX2, ssimY2] = r1.boundXY(x + SSIM_WINDOW_RADIUS, y + SSIM_WINDOW_RADIUS); + const ssimRGB = (ssim(fastR, ssimX1, ssimY1, ssimX2, ssimY2) + ssim(fastG, ssimX1, ssimY1, ssimX2, ssimY2) + ssim(fastB, ssimX1, ssimY1, ssimX2, ssimY2)) / 3; + const isAntialiased = ssimRGB >= 0.99; + if (isAntialiased) { + drawYellowPixel(x, y); + } else { + drawRedPixel(x, y); + ++diffCount; + } + } + } + return diffCount; +} +var SSIM_WINDOW_RADIUS, VARIANCE_WINDOW_RADIUS; +var init_compare = __esm({ + "packages/utils/image_tools/compare.ts"() { + "use strict"; + init_colorUtils(); + init_imageChannel(); + init_stats(); + SSIM_WINDOW_RADIUS = 15; + VARIANCE_WINDOW_RADIUS = 1; + } +}); + +// packages/utils/comparators.ts +function getComparator(mimeType) { + if (mimeType === "image/png") + return compareImages.bind(null, "image/png"); + if (mimeType === "image/jpeg") + return compareImages.bind(null, "image/jpeg"); + if (mimeType === "text/plain") + return compareText; + return compareBuffersOrStrings; +} +function compareBuffersOrStrings(actualBuffer, expectedBuffer) { + if (typeof actualBuffer === "string") + return compareText(actualBuffer, expectedBuffer); + if (!actualBuffer || !(actualBuffer instanceof Buffer)) + return { errorMessage: "Actual result should be a Buffer or a string." }; + if (Buffer.compare(actualBuffer, expectedBuffer)) + return { errorMessage: "Buffers differ" }; + return null; +} +function compareImages(mimeType, actualBuffer, expectedBuffer, options = {}) { + if (!actualBuffer || !(actualBuffer instanceof Buffer)) + return { errorMessage: "Actual result should be a Buffer." }; + validateBuffer(expectedBuffer, mimeType); + let actual = mimeType === "image/png" ? PNG.sync.read(actualBuffer) : jpegjs.decode(actualBuffer, { maxMemoryUsageInMB: JPEG_JS_MAX_BUFFER_SIZE_IN_MB }); + let expected = mimeType === "image/png" ? PNG.sync.read(expectedBuffer) : jpegjs.decode(expectedBuffer, { maxMemoryUsageInMB: JPEG_JS_MAX_BUFFER_SIZE_IN_MB }); + const size = { width: Math.max(expected.width, actual.width), height: Math.max(expected.height, actual.height) }; + let sizesMismatchError = ""; + if (expected.width !== actual.width || expected.height !== actual.height) { + sizesMismatchError = `Expected an image ${expected.width}px by ${expected.height}px, received ${actual.width}px by ${actual.height}px. `; + actual = padImageToSize(actual, size); + expected = padImageToSize(expected, size); + } + const diff2 = new PNG({ width: size.width, height: size.height }); + let count; + if (options.comparator === "ssim-cie94") { + count = compare(expected.data, actual.data, diff2.data, size.width, size.height, { + // All ฮE* formulae are originally designed to have the difference of 1.0 stand for a "just noticeable difference" (JND). + // See https://en.wikipedia.org/wiki/Color_difference#CIELAB_%CE%94E* + maxColorDeltaE94: 1 + }); + } else if ((options.comparator ?? "pixelmatch") === "pixelmatch") { + count = (0, import_pixelmatch.default)(expected.data, actual.data, diff2.data, size.width, size.height, { + threshold: options.threshold ?? 0.2 + }); + } else { + throw new Error(`Configuration specifies unknown comparator "${options.comparator}"`); + } + const maxDiffPixels1 = options.maxDiffPixels; + const maxDiffPixels2 = options.maxDiffPixelRatio !== void 0 ? expected.width * expected.height * options.maxDiffPixelRatio : void 0; + let maxDiffPixels; + if (maxDiffPixels1 !== void 0 && maxDiffPixels2 !== void 0) + maxDiffPixels = Math.min(maxDiffPixels1, maxDiffPixels2); + else + maxDiffPixels = maxDiffPixels1 ?? maxDiffPixels2 ?? 0; + const ratio = Math.ceil(count / (expected.width * expected.height) * 100) / 100; + const pixelsMismatchError = count > maxDiffPixels ? `${count} pixels (ratio ${ratio.toFixed(2)} of all image pixels) are different.` : ""; + if (pixelsMismatchError || sizesMismatchError) + return { errorMessage: sizesMismatchError + pixelsMismatchError, diff: PNG.sync.write(diff2) }; + return null; +} +function validateBuffer(buffer, mimeType) { + if (mimeType === "image/png") { + const pngMagicNumber = [137, 80, 78, 71, 13, 10, 26, 10]; + if (buffer.length < pngMagicNumber.length || !pngMagicNumber.every((byte, index) => buffer[index] === byte)) + throw new Error("Could not decode expected image as PNG."); + } else if (mimeType === "image/jpeg") { + const jpegMagicNumber = [255, 216]; + if (buffer.length < jpegMagicNumber.length || !jpegMagicNumber.every((byte, index) => buffer[index] === byte)) + throw new Error("Could not decode expected image as JPEG."); + } +} +function compareText(actual, expectedBuffer) { + if (typeof actual !== "string") + return { errorMessage: "Actual result should be a string" }; + let expected = expectedBuffer.toString("utf-8"); + if (expected === actual) + return null; + if (!actual.endsWith("\n")) + actual += "\n"; + if (!expected.endsWith("\n")) + expected += "\n"; + const lines = diff.createPatch("file", expected, actual, void 0, void 0, { context: 5 }).split("\n"); + const coloredLines = lines.slice(4).map((line) => { + if (line.startsWith("-")) + return colors.green(line); + if (line.startsWith("+")) + return colors.red(line); + if (line.startsWith("@@")) + return colors.dim(line); + return line; + }); + const errorMessage = coloredLines.join("\n"); + return { errorMessage }; +} +var import_pixelmatch, jpegjs, colors, diff, PNG, JPEG_JS_MAX_BUFFER_SIZE_IN_MB; +var init_comparators = __esm({ + "packages/utils/comparators.ts"() { + "use strict"; + init_imageUtils(); + import_pixelmatch = __toESM(require_pixelmatch()); + init_compare(); + jpegjs = require("./utilsBundle").jpegjs; + colors = require("./utilsBundle").colors; + diff = require("./utilsBundle").diff; + ({ PNG } = require("./utilsBundle")); + JPEG_JS_MAX_BUFFER_SIZE_IN_MB = 5 * 1024; + } +}); + +// packages/utils/crypto.ts +function createGuid() { + return import_crypto.default.randomBytes(16).toString("hex"); +} +function calculateSha1(buffer) { + const hash = import_crypto.default.createHash("sha1"); + hash.update(buffer); + return hash.digest("hex"); +} +function encodeBase128(value2) { + const bytes = []; + do { + let byte = value2 & 127; + value2 >>>= 7; + if (bytes.length > 0) + byte |= 128; + bytes.push(byte); + } while (value2 > 0); + return Buffer.from(bytes.reverse()); +} +function generateSelfSignedCertificate() { + const { privateKey, publicKey } = import_crypto.default.generateKeyPairSync("rsa", { modulusLength: 2048 }); + const publicKeyDer = publicKey.export({ type: "pkcs1", format: "der" }); + const oneYearInMilliseconds = 365 * 24 * 60 * 60 * 1e3; + const notBefore = new Date((/* @__PURE__ */ new Date()).getTime() - oneYearInMilliseconds); + const notAfter = new Date((/* @__PURE__ */ new Date()).getTime() + oneYearInMilliseconds); + const tbsCertificate = DER.encodeSequence([ + DER.encodeExplicitContextDependent(0, DER.encodeInteger(1)), + // version + DER.encodeInteger(1), + // serialNumber + DER.encodeSequence([ + DER.encodeObjectIdentifier("1.2.840.113549.1.1.11"), + // sha256WithRSAEncryption PKCS #1 + DER.encodeNull() + ]), + // signature + DER.encodeSequence([ + DER.encodeSet([ + DER.encodeSequence([ + DER.encodeObjectIdentifier("2.5.4.3"), + // commonName X.520 DN component + DER.encodePrintableString("localhost") + ]) + ]), + DER.encodeSet([ + DER.encodeSequence([ + DER.encodeObjectIdentifier("2.5.4.10"), + // organizationName X.520 DN component + DER.encodePrintableString("Playwright Client Certificate Support") + ]) + ]) + ]), + // issuer + DER.encodeSequence([ + DER.encodeDate(notBefore), + // notBefore + DER.encodeDate(notAfter) + // notAfter + ]), + // validity + DER.encodeSequence([ + DER.encodeSet([ + DER.encodeSequence([ + DER.encodeObjectIdentifier("2.5.4.3"), + // commonName X.520 DN component + DER.encodePrintableString("localhost") + ]) + ]), + DER.encodeSet([ + DER.encodeSequence([ + DER.encodeObjectIdentifier("2.5.4.10"), + // organizationName X.520 DN component + DER.encodePrintableString("Playwright Client Certificate Support") + ]) + ]) + ]), + // subject + DER.encodeSequence([ + DER.encodeSequence([ + DER.encodeObjectIdentifier("1.2.840.113549.1.1.1"), + // rsaEncryption PKCS #1 + DER.encodeNull() + ]), + DER.encodeBitString(publicKeyDer) + ]) + // SubjectPublicKeyInfo + ]); + const signature = import_crypto.default.sign("sha256", tbsCertificate, privateKey); + const certificate = DER.encodeSequence([ + tbsCertificate, + DER.encodeSequence([ + DER.encodeObjectIdentifier("1.2.840.113549.1.1.11"), + // sha256WithRSAEncryption PKCS #1 + DER.encodeNull() + ]), + DER.encodeBitString(signature) + ]); + const certPem = [ + "-----BEGIN CERTIFICATE-----", + // Split the base64 string into lines of 64 characters + certificate.toString("base64").match(/.{1,64}/g).join("\n"), + "-----END CERTIFICATE-----" + ].join("\n"); + return { + cert: certPem, + key: privateKey.export({ type: "pkcs1", format: "pem" }) + }; +} +var import_crypto, DER; +var init_crypto = __esm({ + "packages/utils/crypto.ts"() { + "use strict"; + import_crypto = __toESM(require("crypto")); + init_assert(); + DER = class { + static encodeSequence(data) { + return this._encode(48, Buffer.concat(data)); + } + static encodeInteger(data) { + assert(data >= -128 && data <= 127); + return this._encode(2, Buffer.from([data])); + } + static encodeObjectIdentifier(oid) { + const parts = oid.split(".").map((v) => Number(v)); + const output = [encodeBase128(40 * parts[0] + parts[1])]; + for (let i = 2; i < parts.length; i++) + output.push(encodeBase128(parts[i])); + return this._encode(6, Buffer.concat(output)); + } + static encodeNull() { + return Buffer.from([5, 0]); + } + static encodeSet(data) { + assert(data.length === 1, "Only one item in the set is supported. We'd need to sort the data to support more."); + return this._encode(49, Buffer.concat(data)); + } + static encodeExplicitContextDependent(tag, data) { + return this._encode(160 + tag, data); + } + static encodePrintableString(data) { + return this._encode(19, Buffer.from(data)); + } + static encodeBitString(data) { + const unusedBits = 0; + const content = Buffer.concat([Buffer.from([unusedBits]), data]); + return this._encode(3, content); + } + static encodeDate(date) { + const year = date.getUTCFullYear(); + const isGeneralizedTime = year >= 2050; + const parts = [ + isGeneralizedTime ? year.toString() : year.toString().slice(-2), + (date.getUTCMonth() + 1).toString().padStart(2, "0"), + date.getUTCDate().toString().padStart(2, "0"), + date.getUTCHours().toString().padStart(2, "0"), + date.getUTCMinutes().toString().padStart(2, "0"), + date.getUTCSeconds().toString().padStart(2, "0") + ]; + const encodedDate = parts.join("") + "Z"; + const tag = isGeneralizedTime ? 24 : 23; + return this._encode(tag, Buffer.from(encodedDate)); + } + static _encode(tag, data) { + const lengthBytes = this._encodeLength(data.length); + return Buffer.concat([Buffer.from([tag]), lengthBytes, data]); + } + static _encodeLength(length) { + if (length < 128) { + return Buffer.from([length]); + } else { + const lengthBytes = []; + while (length > 0) { + lengthBytes.unshift(length & 255); + length >>= 8; + } + return Buffer.from([128 | lengthBytes.length, ...lengthBytes]); + } + } + }; + } +}); + +// packages/utils/env.ts +function getFromENV(name) { + let value2 = process.env[name]; + value2 = value2 === void 0 ? process.env[`npm_config_${name.toLowerCase()}`] : value2; + value2 = value2 === void 0 ? process.env[`npm_package_config_${name.toLowerCase()}`] : value2; + return value2; +} +function getAsBooleanFromENV(name, defaultValue) { + const value2 = getFromENV(name); + if (value2 === "false" || value2 === "0") + return false; + if (value2) + return true; + return !!defaultValue; +} +function getPackageManager() { + const env = process.env.npm_config_user_agent || ""; + if (env.includes("yarn")) + return "yarn"; + if (env.includes("pnpm")) + return "pnpm"; + return "npm"; +} +function getPackageManagerExecCommand() { + const packageManager = getPackageManager(); + if (packageManager === "yarn") + return "yarn"; + if (packageManager === "pnpm") + return "pnpm exec"; + return "npx"; +} +function isLikelyNpxGlobal() { + return process.argv.length >= 2 && process.argv[1].includes("_npx"); +} +function setPlaywrightTestProcessEnv() { + return process.env["PLAYWRIGHT_TEST"] = "1"; +} +function guessClientName() { + if (process.env.CLAUDECODE) + return "Claude Code"; + if (process.env.COPILOT_CLI) + return "GitHub Copilot"; + return "playwright-cli"; +} +function isCodingAgent() { + return !!process.env.CLAUDECODE || !!process.env.COPILOT_CLI; +} +var init_env = __esm({ + "packages/utils/env.ts"() { + "use strict"; + } +}); + +// packages/utils/debug.ts +function debugMode() { + if (_debugMode === "console") + return "console"; + if (_debugMode === "0" || _debugMode === "false") + return ""; + return _debugMode ? "inspector" : ""; +} +function isUnderTest() { + return _isUnderTest; +} +var _debugMode, _isUnderTest; +var init_debug = __esm({ + "packages/utils/debug.ts"() { + "use strict"; + init_env(); + _debugMode = getFromENV("PWDEBUG") || ""; + _isUnderTest = getAsBooleanFromENV("PWTEST_UNDER_TEST"); + } +}); + +// packages/utils/debugLogger.ts +var import_fs, debug, debugLoggerColorMap, DebugLogger, debugLogger, kLogCount, RecentLogsCollector; +var init_debugLogger = __esm({ + "packages/utils/debugLogger.ts"() { + "use strict"; + import_fs = __toESM(require("fs")); + debug = require("./utilsBundle").debug; + debugLoggerColorMap = { + "api": 45, + // cyan + "protocol": 34, + // green + "install": 34, + // green + "download": 34, + // green + "browser": 0, + // reset + "socks": 92, + // purple + "client-certificates": 92, + // purple + "error": 160, + // red, + "channel": 33, + // blue + "server": 45, + // cyan + "server:channel": 34, + // green + "server:metadata": 33, + // blue, + "recorder": 45 + // cyan + }; + DebugLogger = class { + constructor() { + this._debuggers = /* @__PURE__ */ new Map(); + if (process.env.DEBUG_FILE) { + const ansiRegex2 = new RegExp([ + "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)", + "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))" + ].join("|"), "g"); + const stream3 = import_fs.default.createWriteStream(process.env.DEBUG_FILE); + debug.log = (data) => { + stream3.write(data.replace(ansiRegex2, "")); + stream3.write("\n"); + }; + } + } + log(name, message) { + let cachedDebugger = this._debuggers.get(name); + if (!cachedDebugger) { + cachedDebugger = debug(`pw:${name}`); + this._debuggers.set(name, cachedDebugger); + cachedDebugger.color = debugLoggerColorMap[name] || 0; + } + cachedDebugger(message); + } + isEnabled(name) { + return debug.enabled(`pw:${name}`); + } + }; + debugLogger = new DebugLogger(); + kLogCount = 150; + RecentLogsCollector = class { + constructor() { + this._logs = []; + this._listeners = []; + } + log(message) { + this._logs.push(message); + if (this._logs.length === kLogCount * 2) + this._logs.splice(0, kLogCount); + for (const listener of this._listeners) + listener(message); + } + recentLogs() { + if (this._logs.length > kLogCount) + return this._logs.slice(-kLogCount); + return this._logs; + } + onMessage(listener) { + for (const message of this._logs) + listener(message); + this._listeners.push(listener); + } + }; + } +}); + +// packages/utils/eventsHelper.ts +var EventsHelper, eventsHelper; +var init_eventsHelper = __esm({ + "packages/utils/eventsHelper.ts"() { + "use strict"; + EventsHelper = class { + static addEventListener(emitter, eventName, handler) { + emitter.on(eventName, handler); + return { emitter, eventName, handler, dispose: async () => { + emitter.removeListener(eventName, handler); + } }; + } + static removeEventListeners(listeners) { + for (const listener of listeners) + listener.emitter.removeListener(listener.eventName, listener.handler); + listeners.splice(0, listeners.length); + } + }; + eventsHelper = EventsHelper; + } +}); + +// packages/utils/fileUtils.ts +async function mkdirIfNeeded(filePath) { + await import_fs2.default.promises.mkdir(import_path2.default.dirname(filePath), { recursive: true }).catch(() => { + }); +} +async function removeFolders(dirs) { + return await Promise.all(dirs.map( + (dir) => import_fs2.default.promises.rm(dir, { recursive: true, force: true, maxRetries: 10 }).catch((e) => e) + )); +} +function canAccessFile(file) { + if (!file) + return false; + try { + import_fs2.default.accessSync(file); + return true; + } catch (e) { + return false; + } +} +function isWritable(file) { + try { + import_fs2.default.accessSync(file, import_fs2.default.constants.W_OK); + return true; + } catch { + return false; + } +} +function isSystemDirectory(dir) { + const resolved = import_path2.default.resolve(dir); + if (process.platform === "win32") { + const systemRoot = import_path2.default.resolve(process.env.SystemRoot || "C:\\Windows"); + return isPathInside(systemRoot.toLowerCase(), resolved.toLowerCase()); + } + return resolved === "/"; +} +async function copyFileAndMakeWritable(from, to) { + await import_fs2.default.promises.copyFile(from, to); + await import_fs2.default.promises.chmod(to, 436); +} +function addSuffixToFilePath(filePath, suffix) { + const ext = import_path2.default.extname(filePath); + const base = filePath.substring(0, filePath.length - ext.length); + return base + suffix + ext; +} +function sanitizeForFilePath(s) { + return s.replace(/[\x00-\x2C\x2E-\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]+/g, "-"); +} +function trimLongString(s, length = 100) { + if (s.length <= length) + return s; + const hash = calculateSha1(s); + const middle = `-${hash.substring(0, 5)}-`; + const start3 = Math.floor((length - middle.length) / 2); + const end = length - middle.length - start3; + return s.substring(0, start3) + middle + s.slice(-end); +} +function isPathInside(root, candidate) { + const resolvedRoot = import_path2.default.resolve(root); + const resolvedCandidate = import_path2.default.resolve(candidate); + if (resolvedCandidate === resolvedRoot) + return true; + return resolvedCandidate.startsWith(resolvedRoot + import_path2.default.sep); +} +function resolveWithinRoot(root, fileName) { + if (import_path2.default.isAbsolute(fileName)) + return null; + const resolvedFile = import_path2.default.resolve(root, fileName); + return isPathInside(root, resolvedFile) ? resolvedFile : null; +} +function throwingResolveWithinRoot(root, fileName) { + const resolved = resolveWithinRoot(root, fileName); + if (!resolved) + throw new Error(`Path '${fileName}' escapes root directory`); + return resolved; +} +function toPosixPath(aPath) { + return aPath.split(import_path2.default.sep).join(import_path2.default.posix.sep); +} +function makeSocketPath(domain, name) { + const userNameHash = calculateSha1(process.env.USERNAME || process.env.USER || "default").slice(0, 8); + if (process.platform === "win32") { + const socketsDir = process.env.PWTEST_SOCKETS_DIR; + const suffix2 = socketsDir ? `-${calculateSha1(socketsDir).slice(0, 8)}` : ""; + return `\\\\.\\pipe\\pw-${userNameHash}-${domain}-${name}${suffix2}`; + } + const baseDir = process.env.PWTEST_SOCKETS_DIR || import_path2.default.join(import_os2.default.tmpdir(), `pw-${userNameHash}`); + const dir = import_path2.default.join(baseDir, domain); + const suffix = ".sock"; + const maxNameLength = UNIX_SOCKET_PATH_MAX - dir.length - import_path2.default.sep.length - suffix.length; + if (maxNameLength < 1) + throw new Error(`Socket directory path is too long (${dir.length} chars); set PWTEST_SOCKETS_DIR to a shorter location.`); + const fsFriendlyName = trimLongString(sanitizeForFilePath(name), maxNameLength); + const result2 = import_path2.default.join(dir, `${fsFriendlyName}${suffix}`); + import_fs2.default.mkdirSync(dir, { recursive: true }); + return result2; +} +var import_fs2, import_os2, import_path2, existsAsync, UNIX_SOCKET_PATH_MAX; +var init_fileUtils = __esm({ + "packages/utils/fileUtils.ts"() { + "use strict"; + import_fs2 = __toESM(require("fs")); + import_os2 = __toESM(require("os")); + import_path2 = __toESM(require("path")); + init_crypto(); + existsAsync = (path59) => new Promise((resolve) => import_fs2.default.stat(path59, (err) => resolve(!err))); + UNIX_SOCKET_PATH_MAX = 103; + } +}); + +// packages/utils/linuxUtils.ts +function getLinuxDistributionInfoSync() { + if (process.platform !== "linux") + return void 0; + if (!osRelease && !didFailToReadOSRelease) { + try { + const osReleaseText = import_fs3.default.readFileSync("/etc/os-release", "utf8"); + const fields = parseOSReleaseText(osReleaseText); + osRelease = { + id: fields.get("id") ?? "", + version: fields.get("version_id") ?? "" + }; + } catch (e) { + didFailToReadOSRelease = true; + } + } + return osRelease; +} +function parseOSReleaseText(osReleaseText) { + const fields = /* @__PURE__ */ new Map(); + for (const line of osReleaseText.split("\n")) { + const tokens = line.split("="); + const name = tokens.shift(); + let value2 = tokens.join("=").trim(); + if (value2.startsWith('"') && value2.endsWith('"')) + value2 = value2.substring(1, value2.length - 1); + if (!name) + continue; + fields.set(name.toLowerCase(), value2); + } + return fields; +} +var import_fs3, didFailToReadOSRelease, osRelease; +var init_linuxUtils = __esm({ + "packages/utils/linuxUtils.ts"() { + "use strict"; + import_fs3 = __toESM(require("fs")); + didFailToReadOSRelease = false; + } +}); + +// packages/utils/hostPlatform.ts +function calculatePlatform() { + if (process.env.PLAYWRIGHT_HOST_PLATFORM_OVERRIDE) { + return { + hostPlatform: process.env.PLAYWRIGHT_HOST_PLATFORM_OVERRIDE, + isOfficiallySupportedPlatform: false + }; + } + const platform = import_os3.default.platform(); + if (platform === "darwin") { + const ver = import_os3.default.release().split(".").map((a) => parseInt(a, 10)); + let macVersion = ""; + if (ver[0] < 18) { + macVersion = "mac10.13"; + } else if (ver[0] === 18) { + macVersion = "mac10.14"; + } else if (ver[0] === 19) { + macVersion = "mac10.15"; + } else if (ver[0] < 25) { + macVersion = "mac" + (ver[0] - 9); + if (import_os3.default.cpus().some((cpu) => cpu.model.includes("Apple"))) + macVersion += "-arm64"; + } else { + const LAST_STABLE_MACOS_MAJOR_VERSION = 26; + macVersion = "mac" + Math.min(ver[0] + 1, LAST_STABLE_MACOS_MAJOR_VERSION); + if (import_os3.default.cpus().some((cpu) => cpu.model.includes("Apple"))) + macVersion += "-arm64"; + } + return { hostPlatform: macVersion, isOfficiallySupportedPlatform: true }; + } + if (platform === "linux") { + if (!["x64", "arm64"].includes(import_os3.default.arch())) + return { hostPlatform: "", isOfficiallySupportedPlatform: false }; + const archSuffix = "-" + import_os3.default.arch(); + const distroInfo = getLinuxDistributionInfoSync(); + if (distroInfo?.id === "ubuntu" || distroInfo?.id === "pop" || distroInfo?.id === "neon" || distroInfo?.id === "tuxedo") { + const isUbuntu = distroInfo?.id === "ubuntu"; + const version3 = distroInfo?.version; + const major2 = parseInt(distroInfo.version, 10); + if (major2 < 20) + return { hostPlatform: "ubuntu18.04" + archSuffix, isOfficiallySupportedPlatform: false }; + if (major2 < 22) + return { hostPlatform: "ubuntu20.04" + archSuffix, isOfficiallySupportedPlatform: isUbuntu && version3 === "20.04" }; + if (major2 < 24) + return { hostPlatform: "ubuntu22.04" + archSuffix, isOfficiallySupportedPlatform: isUbuntu && version3 === "22.04" }; + if (major2 < 26) + return { hostPlatform: "ubuntu24.04" + archSuffix, isOfficiallySupportedPlatform: isUbuntu && version3 === "24.04" }; + if (major2 < 28) + return { hostPlatform: "ubuntu26.04" + archSuffix, isOfficiallySupportedPlatform: isUbuntu && version3 === "26.04" }; + return { hostPlatform: "ubuntu" + distroInfo.version + archSuffix, isOfficiallySupportedPlatform: false }; + } + if (distroInfo?.id === "linuxmint") { + const mintMajor = parseInt(distroInfo.version, 10); + if (mintMajor <= 20) + return { hostPlatform: "ubuntu20.04" + archSuffix, isOfficiallySupportedPlatform: false }; + if (mintMajor === 21) + return { hostPlatform: "ubuntu22.04" + archSuffix, isOfficiallySupportedPlatform: false }; + return { hostPlatform: "ubuntu24.04" + archSuffix, isOfficiallySupportedPlatform: false }; + } + if (distroInfo?.id === "debian" || distroInfo?.id === "raspbian") { + const isOfficiallySupportedPlatform2 = distroInfo?.id === "debian"; + if (distroInfo?.version === "11") + return { hostPlatform: "debian11" + archSuffix, isOfficiallySupportedPlatform: isOfficiallySupportedPlatform2 }; + if (distroInfo?.version === "12") + return { hostPlatform: "debian12" + archSuffix, isOfficiallySupportedPlatform: isOfficiallySupportedPlatform2 }; + if (distroInfo?.version === "13") + return { hostPlatform: "debian13" + archSuffix, isOfficiallySupportedPlatform: isOfficiallySupportedPlatform2 }; + if (distroInfo?.version === "") + return { hostPlatform: "debian13" + archSuffix, isOfficiallySupportedPlatform: isOfficiallySupportedPlatform2 }; + } + return { hostPlatform: "ubuntu24.04" + archSuffix, isOfficiallySupportedPlatform: false }; + } + if (platform === "win32") + return { hostPlatform: "win64", isOfficiallySupportedPlatform: true }; + return { hostPlatform: "", isOfficiallySupportedPlatform: false }; +} +function toShortPlatform(hostPlatform2) { + if (hostPlatform2 === "") + return ""; + if (hostPlatform2 === "win64") + return "win-x64"; + if (hostPlatform2.startsWith("mac")) + return hostPlatform2.endsWith("arm64") ? "mac-arm64" : "mac-x64"; + return hostPlatform2.endsWith("arm64") ? "linux-arm64" : "linux-x64"; +} +var import_os3, hostPlatform, isOfficiallySupportedPlatform, shortPlatform; +var init_hostPlatform = __esm({ + "packages/utils/hostPlatform.ts"() { + "use strict"; + import_os3 = __toESM(require("os")); + init_linuxUtils(); + ({ hostPlatform, isOfficiallySupportedPlatform } = calculatePlatform()); + shortPlatform = toShortPlatform(hostPlatform); + } +}); + +// packages/utils/happyEyeballs.ts +async function createSocket(host, port) { + return new Promise((resolve, reject) => { + if (import_net.default.isIP(host)) { + const socket = import_net.default.createConnection({ host, port }); + socket.on("connect", () => resolve(socket)); + socket.on("error", (error) => reject(error)); + } else { + createConnectionAsync( + { host, port }, + (err, socket) => { + if (err) + reject(err); + if (socket) + resolve(socket); + }, + /* useTLS */ + false + ).catch((err) => reject(err)); + } + }); +} +async function createConnectionAsync(options, oncreate, useTLS) { + const lookup = options.__testHookLookup || lookupAddresses; + const hostname = clientRequestArgsToHostName(options); + const addresses = await lookup(hostname); + const dnsLookupAt = monotonicTime(); + const sockets = /* @__PURE__ */ new Set(); + let firstError; + let errorCount = 0; + const handleError = (socket, err) => { + if (!sockets.delete(socket)) + return; + ++errorCount; + firstError ??= err; + if (errorCount === addresses.length) + oncreate?.(firstError); + }; + const connected = new ManualPromise(); + for (const { address } of addresses) { + const socket = useTLS ? import_tls.default.connect({ + ...options, + port: options.port, + host: address, + servername: hostname + }) : import_net.default.createConnection({ + ...options, + port: options.port, + host: address + }); + socket[kDNSLookupAt] = dnsLookupAt; + socket.on("connect", () => { + socket[kTCPConnectionAt] = monotonicTime(); + connected.resolve(); + oncreate?.(null, socket); + sockets.delete(socket); + for (const s of sockets) + s.destroy(); + sockets.clear(); + }); + socket.on("timeout", () => { + socket.destroy(); + handleError(socket, new Error("Connection timeout")); + }); + socket.on("error", (e) => handleError(socket, e)); + sockets.add(socket); + await Promise.race([ + connected, + new Promise((f) => setTimeout(f, connectionAttemptDelayMs)) + ]); + if (connected.isDone()) + break; + } +} +async function lookupAddresses(hostname) { + const [v4Result, v6Result] = await Promise.allSettled([ + import_dns.default.promises.lookup(hostname, { all: true, family: 4 }), + import_dns.default.promises.lookup(hostname, { all: true, family: 6 }) + ]); + const v4Addresses = v4Result.status === "fulfilled" ? v4Result.value : []; + const v6Addresses = v6Result.status === "fulfilled" ? v6Result.value : []; + if (!v4Addresses.length && !v6Addresses.length) { + if (v4Result.status === "rejected") + throw v4Result.reason; + throw v6Result.reason; + } + const result2 = []; + for (let i = 0; i < Math.max(v4Addresses.length, v6Addresses.length); i++) { + if (v6Addresses[i]) + result2.push(v6Addresses[i]); + if (v4Addresses[i]) + result2.push(v4Addresses[i]); + } + return result2; +} +function clientRequestArgsToHostName(options) { + if (options.hostname) + return options.hostname; + if (options.host) + return options.host; + throw new Error("Either options.hostname or options.host must be provided"); +} +function timingForSocket(socket) { + return { + dnsLookupAt: socket[kDNSLookupAt], + tcpConnectionAt: socket[kTCPConnectionAt] + }; +} +var import_dns, import_http, import_https, import_net, import_tls, connectionAttemptDelayMs, kDNSLookupAt, kTCPConnectionAt, HttpHappyEyeballsAgent, HttpsHappyEyeballsAgent, httpsHappyEyeballsAgent, httpHappyEyeballsAgent; +var init_happyEyeballs = __esm({ + "packages/utils/happyEyeballs.ts"() { + "use strict"; + import_dns = __toESM(require("dns")); + import_http = __toESM(require("http")); + import_https = __toESM(require("https")); + import_net = __toESM(require("net")); + import_tls = __toESM(require("tls")); + init_assert(); + init_manualPromise(); + init_time(); + connectionAttemptDelayMs = 300; + kDNSLookupAt = Symbol("kDNSLookupAt"); + kTCPConnectionAt = Symbol("kTCPConnectionAt"); + HttpHappyEyeballsAgent = class extends import_http.default.Agent { + createConnection(options, oncreate) { + if (import_net.default.isIP(clientRequestArgsToHostName(options))) + return import_net.default.createConnection(options); + createConnectionAsync( + options, + oncreate, + /* useTLS */ + false + ).catch((err) => oncreate?.(err)); + } + }; + HttpsHappyEyeballsAgent = class extends import_https.default.Agent { + createConnection(options, oncreate) { + if (import_net.default.isIP(clientRequestArgsToHostName(options))) + return import_tls.default.connect(options); + createConnectionAsync( + options, + oncreate, + /* useTLS */ + true + ).catch((err) => oncreate?.(err)); + } + }; + httpsHappyEyeballsAgent = new HttpsHappyEyeballsAgent({ keepAlive: true }); + httpHappyEyeballsAgent = new HttpHappyEyeballsAgent({ keepAlive: true }); + } +}); + +// packages/utils/network.ts +function httpRequest(params2, onResponse, onError) { + let url2 = new URL(params2.url); + const options = { + method: params2.method || "GET", + headers: params2.headers + }; + if (params2.rejectUnauthorized !== void 0) + options.rejectUnauthorized = params2.rejectUnauthorized; + const proxyURL = getProxyForUrl(params2.url); + if (proxyURL) { + const parsedProxyURL = normalizeProxyURL(proxyURL); + if (params2.url.startsWith("http:")) { + options.path = url2.toString(); + const headers = options.headers || {}; + if (!Object.keys(headers).some((header) => header.toLowerCase() === "host")) + headers.host = url2.host; + options.headers = headers; + url2 = parsedProxyURL; + } else { + options.agent = new HttpsProxyAgent(parsedProxyURL); + } + } + options.agent ??= url2.protocol === "https:" ? httpsHappyEyeballsAgent : httpHappyEyeballsAgent; + let cancelRequest; + const requestCallback = (res) => { + const statusCode = res.statusCode || 0; + if (statusCode >= 300 && statusCode < 400 && res.headers.location) { + request2.destroy(); + cancelRequest = httpRequest({ ...params2, url: new URL(res.headers.location, params2.url).toString() }, onResponse, onError).cancel; + } else { + onResponse(res); + } + }; + const request2 = url2.protocol === "https:" ? import_https2.default.request(url2, options, requestCallback) : import_http2.default.request(url2, options, requestCallback); + request2.on("error", onError); + if (params2.socketTimeout !== void 0) { + request2.setTimeout(params2.socketTimeout, () => { + onError(new Error(`Request to ${params2.url} timed out after ${params2.socketTimeout}ms`)); + request2.abort(); + }); + } + cancelRequest = (e) => { + try { + request2.destroy(e); + } catch { + } + }; + request2.end(params2.data); + return { cancel: (e) => cancelRequest(e) }; +} +function shouldBypassProxy(url2, bypass) { + if (!bypass) + return false; + const domains = bypass.split(",").map((s) => { + s = s.trim(); + if (!s.startsWith(".")) + s = "." + s; + return s; + }); + const domain = "." + url2.hostname; + return domains.some((d) => domain.endsWith(d)); +} +function normalizeProxyURL(proxy) { + proxy = proxy.trim(); + if (!/^\w+:\/\//.test(proxy)) + proxy = "http://" + proxy; + return new URL(proxy); +} +function createProxyAgent(proxy, forUrl) { + if (!proxy) + return; + if (forUrl && proxy.bypass && shouldBypassProxy(forUrl, proxy.bypass)) + return; + const proxyURL = normalizeProxyURL(proxy.server); + if (proxyURL.protocol?.startsWith("socks")) { + if (proxyURL.protocol === "socks5:") + proxyURL.protocol = "socks5h:"; + else if (proxyURL.protocol === "socks4:") + proxyURL.protocol = "socks4a:"; + return new SocksProxyAgent(proxyURL); + } + if (proxy.username) { + proxyURL.username = proxy.username; + proxyURL.password = proxy.password || ""; + } + if (forUrl && ["ws:", "wss:"].includes(forUrl.protocol)) { + return new HttpsProxyAgent(proxyURL); + } + return new HttpsProxyAgent(proxyURL); +} +function createHttpServer(...args) { + const server = import_http2.default.createServer(...args); + decorateServer(server); + return server; +} +function createHttpsServer(...args) { + const server = import_https2.default.createServer(...args); + decorateServer(server); + return server; +} +function createHttp2Server(...args) { + const server = import_http22.default.createSecureServer(...args); + decorateServer(server); + return server; +} +async function startHttpServer(server, options) { + const { host = "localhost", port = 0 } = options; + const errorPromise = new ManualPromise(); + const errorListener = (error) => errorPromise.reject(error); + server.on("error", errorListener); + try { + server.listen(port, host); + await Promise.race([ + new Promise((cb) => server.once("listening", cb)), + errorPromise + ]); + } finally { + server.removeListener("error", errorListener); + } +} +async function isURLAvailable(url2, ignoreHTTPSErrors, onLog, onStdErr) { + let statusCode = await httpStatusCode(url2, ignoreHTTPSErrors, onLog, onStdErr); + if (statusCode === 404 && url2.pathname === "/") { + const indexUrl = new URL(url2); + indexUrl.pathname = "/index.html"; + statusCode = await httpStatusCode(indexUrl, ignoreHTTPSErrors, onLog, onStdErr); + } + return statusCode >= 200 && statusCode < 404; +} +async function httpStatusCode(url2, ignoreHTTPSErrors, onLog, onStdErr) { + return new Promise((resolve) => { + onLog?.(`HTTP GET: ${url2}`); + httpRequest({ + url: url2.toString(), + headers: { Accept: "*/*" }, + rejectUnauthorized: !ignoreHTTPSErrors + }, (res) => { + res.resume(); + const statusCode = res.statusCode ?? 0; + onLog?.(`HTTP Status: ${statusCode}`); + resolve(statusCode); + }, (error) => { + if (error.code === "DEPTH_ZERO_SELF_SIGNED_CERT") + onStdErr?.(`[WebServer] Self-signed certificate detected. Try adding ignoreHTTPSErrors: true to config.webServer.`); + onLog?.(`Error while checking if ${url2} is available: ${error.message}`); + resolve(0); + }); + }); +} +function decorateServer(server) { + const sockets = /* @__PURE__ */ new Set(); + server.on("connection", (socket) => { + sockets.add(socket); + socket.once("close", () => sockets.delete(socket)); + }); + const close3 = server.close; + server.close = (callback) => { + for (const socket of sockets) + socket.destroy(); + sockets.clear(); + return close3.call(server, callback); + }; +} +var import_http2, import_http22, import_https2, HttpsProxyAgent, SocksProxyAgent, getProxyForUrl, NET_DEFAULT_TIMEOUT; +var init_network = __esm({ + "packages/utils/network.ts"() { + "use strict"; + import_http2 = __toESM(require("http")); + import_http22 = __toESM(require("http2")); + import_https2 = __toESM(require("https")); + init_manualPromise(); + init_happyEyeballs(); + ({ HttpsProxyAgent } = require("./utilsBundle")); + ({ SocksProxyAgent } = require("./utilsBundle")); + ({ getProxyForUrl } = require("./utilsBundle")); + NET_DEFAULT_TIMEOUT = 3e4; + } +}); + +// packages/utils/httpServer.ts +function computeAllowedHosts(requested, bound) { + const loopback = /* @__PURE__ */ new Set(["127.0.0.1", "::1", "localhost"]); + const isLoopback = (h) => h !== void 0 && loopback.has(h.toLowerCase()); + if (!isLoopback(requested) && requested !== void 0) + return null; + if (!isLoopback(bound) && requested === void 0) + return null; + return /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]"]); +} +function urlHostFromAddress(address) { + return address.family === "IPv6" ? `[${address.address}]` : address.address; +} +function hostnameFromHostHeader(host) { + if (host.startsWith("[")) { + const end = host.indexOf("]"); + return end < 0 ? host : host.substring(0, end + 1); + } + const colon = host.indexOf(":"); + return colon < 0 ? host : host.substring(0, colon); +} +function serveFolder(folder) { + const server = new HttpServer(folder); + server.routePrefix("/", (request2, response2) => { + let relativePath = new URL("http://localhost" + request2.url).pathname; + if (relativePath.startsWith("/trace/file")) { + const url2 = new URL("http://localhost" + request2.url); + const requested = url2.searchParams.get("path"); + if (!requested) + return false; + const resolved = import_path3.default.resolve(requested); + try { + return server.serveFile(request2, response2, resolved); + } catch (e) { + return false; + } + } + if (relativePath === "/") + relativePath = "/index.html"; + const absolutePath = import_path3.default.join(folder, ...relativePath.split("/")); + return server.serveFile(request2, response2, absolutePath); + }); + return server; +} +var import_fs4, import_path3, mime, wsServer, HttpServer; +var init_httpServer = __esm({ + "packages/utils/httpServer.ts"() { + "use strict"; + import_fs4 = __toESM(require("fs")); + import_path3 = __toESM(require("path")); + init_assert(); + init_crypto(); + init_fileUtils(); + init_network(); + mime = require("./utilsBundle").mime; + ({ wsServer } = require("./utilsBundle")); + HttpServer = class { + constructor(staticRoot) { + this._urlPrefixPrecise = ""; + this._urlPrefixHumanReadable = ""; + this._port = 0; + this._started = false; + this._routes = []; + // Allowed Host headers; null disables the check (host bound to a public address). + this._allowedHosts = null; + this._server = createHttpServer(this._onRequest.bind(this)); + this._staticRoot = staticRoot ? import_path3.default.resolve(staticRoot) : void 0; + } + server() { + return this._server; + } + routePrefix(prefix, handler) { + this._routes.push({ prefix, handler }); + } + routePath(path59, handler) { + this._routes.push({ exact: path59, handler }); + } + port() { + return this._port; + } + createWebSocket(transportFactory, guid) { + assert(!this._wsGuid, "can only create one main websocket transport per server"); + this._wsGuid = guid || createGuid(); + const wsPath = "/" + this._wsGuid; + const wss = new wsServer({ noServer: true }); + this._server.on("upgrade", (request2, socket, head) => { + const pathname = new URL(request2.url ?? "/", "http://localhost").pathname; + if (pathname !== wsPath) + return; + wss.handleUpgrade(request2, socket, head, (ws4) => wss.emit("connection", ws4, request2)); + }); + wss.on("connection", (ws4, request2) => { + const url2 = new URL(request2.url ?? "/", "http://localhost"); + const transport = transportFactory(url2); + transport.sendEvent = (method, params2) => ws4.send(JSON.stringify({ method, params: params2 })); + transport.close = () => ws4.close(); + transport.onconnect(); + ws4.on("message", async (message) => { + const { id, method, params: params2 } = JSON.parse(String(message)); + try { + const result2 = await transport.dispatch(method, params2); + ws4.send(JSON.stringify({ id, result: result2 })); + } catch (e) { + ws4.send(JSON.stringify({ id, error: String(e) })); + } + }); + ws4.on("close", () => transport.onclose()); + ws4.on("error", () => transport.onclose()); + }); + } + wsGuid() { + return this._wsGuid; + } + async createViteDevServer(options) { + const loadVite = new Function('return import("vite")'); + const vite = await loadVite(); + return await vite.createServer({ + root: options.root, + base: options.base, + server: { + middlewareMode: true, + // Dedicated path so Vite's HMR websocket does not collide with any + // websocket HttpServer owns via createWebSocket(). + hmr: { path: "/__vite_hmr", server: this._server } + }, + appType: "spa", + clearScreen: false + }); + } + // HMR end + // Vite's middleware `next` callback for the "no middleware matched" case; + // emits a 404 only if nothing downstream already responded. + static notFoundFallback(response2) { + return () => { + if (!response2.headersSent) { + response2.statusCode = 404; + response2.end(); + } + }; + } + async start(options = {}) { + assert(!this._started, "server already started"); + this._started = true; + const host = options.host; + if (options.preferredPort) { + try { + await startHttpServer(this._server, { port: options.preferredPort, host }); + } catch (e) { + if (!e || !e.message || !e.message.includes("EADDRINUSE")) + throw e; + await startHttpServer(this._server, { host }); + } + } else { + await startHttpServer(this._server, { port: options.port, host }); + } + const address = this._server.address(); + assert(address, "Could not bind server socket"); + if (typeof address === "string") { + this._urlPrefixPrecise = address; + this._urlPrefixHumanReadable = address; + } else { + this._port = address.port; + this._urlPrefixPrecise = `http://${urlHostFromAddress(address)}:${address.port}`; + this._urlPrefixHumanReadable = `http://${host ?? "localhost"}:${address.port}`; + this._allowedHosts = computeAllowedHosts(host, address.address); + } + } + async stop() { + await new Promise((cb) => this._server.close(cb)); + } + urlPrefix(purpose) { + return purpose === "human-readable" ? this._urlPrefixHumanReadable : this._urlPrefixPrecise; + } + serveFile(request2, response2, absoluteFilePath, headers, options) { + if (this._staticRoot && !options?.skipRootCheck && !isPathInside(this._staticRoot, absoluteFilePath)) { + response2.statusCode = 403; + response2.end(); + return true; + } + try { + for (const [name, value2] of Object.entries(headers || {})) + response2.setHeader(name, value2); + if (request2.headers.range) + this._serveRangeFile(request2, response2, absoluteFilePath); + else + this._serveFile(response2, absoluteFilePath); + return true; + } catch (e) { + return false; + } + } + _serveFile(response2, absoluteFilePath) { + const content = import_fs4.default.readFileSync(absoluteFilePath); + response2.statusCode = 200; + const contentType = mime.getType(import_path3.default.extname(absoluteFilePath)) || "application/octet-stream"; + response2.setHeader("Content-Type", contentType); + response2.setHeader("Content-Length", content.byteLength); + response2.end(content); + } + _serveRangeFile(request2, response2, absoluteFilePath) { + const range = request2.headers.range; + if (!range || !range.startsWith("bytes=") || range.includes(", ") || [...range].filter((char) => char === "-").length !== 1) { + response2.statusCode = 400; + return response2.end("Bad request"); + } + const [startStr, endStr] = range.replace(/bytes=/, "").split("-"); + let start3; + let end; + const size = import_fs4.default.statSync(absoluteFilePath).size; + if (startStr !== "" && endStr === "") { + start3 = +startStr; + end = size - 1; + } else if (startStr === "" && endStr !== "") { + start3 = size - +endStr; + end = size - 1; + } else { + start3 = +startStr; + end = +endStr; + } + if (Number.isNaN(start3) || Number.isNaN(end) || start3 >= size || end >= size || start3 > end) { + response2.writeHead(416, { + "Content-Range": `bytes */${size}` + }); + return response2.end(); + } + response2.writeHead(206, { + "Content-Range": `bytes ${start3}-${end}/${size}`, + "Accept-Ranges": "bytes", + "Content-Length": end - start3 + 1, + "Content-Type": mime.getType(import_path3.default.extname(absoluteFilePath)) + }); + const readable = import_fs4.default.createReadStream(absoluteFilePath, { start: start3, end }); + readable.pipe(response2); + } + _onRequest(request2, response2) { + if (request2.method === "OPTIONS") { + response2.writeHead(200); + response2.end(); + return; + } + if (this._allowedHosts) { + const host = request2.headers.host?.toLowerCase(); + const hostname = host ? hostnameFromHostHeader(host) : void 0; + if (!hostname || !this._allowedHosts.has(hostname)) { + response2.statusCode = 403; + response2.end(); + return; + } + } + request2.on("error", () => response2.end()); + try { + if (!request2.url) { + response2.end(); + return; + } + const url2 = new URL("http://localhost" + request2.url); + for (const route2 of this._routes) { + if (route2.exact && url2.pathname === route2.exact && route2.handler(request2, response2)) + return; + if (route2.prefix && url2.pathname.startsWith(route2.prefix) && route2.handler(request2, response2)) + return; + } + response2.statusCode = 404; + response2.end(); + } catch (e) { + response2.end(); + } + } + }; + } +}); + +// packages/utils/zones.ts +function currentZone() { + return asyncLocalStorage.getStore() ?? emptyZone; +} +var import_async_hooks, asyncLocalStorage, Zone, emptyZone; +var init_zones = __esm({ + "packages/utils/zones.ts"() { + "use strict"; + import_async_hooks = require("async_hooks"); + asyncLocalStorage = new import_async_hooks.AsyncLocalStorage(); + Zone = class _Zone { + constructor(asyncLocalStorage2, store) { + this._asyncLocalStorage = asyncLocalStorage2; + this._data = store; + } + with(type3, data) { + return new _Zone(this._asyncLocalStorage, new Map(this._data).set(type3, data)); + } + without(type3) { + const data = type3 ? new Map(this._data) : /* @__PURE__ */ new Map(); + data.delete(type3); + return new _Zone(this._asyncLocalStorage, data); + } + run(func) { + return this._asyncLocalStorage.run(this, func); + } + data(type3) { + return this._data.get(type3); + } + }; + emptyZone = new Zone(asyncLocalStorage, /* @__PURE__ */ new Map()); + } +}); + +// packages/utils/nodePlatform.ts +function setBoxedStackPrefixes(prefixes) { + boxedStackPrefixes = prefixes; +} +var import_crypto4, import_fs5, import_path4, util, import_stream, import_events, colors2, pipelineAsync, NodeZone, boxedStackPrefixes, nodePlatform, ReadableStreamImpl, WritableStreamImpl; +var init_nodePlatform = __esm({ + "packages/utils/nodePlatform.ts"() { + "use strict"; + import_crypto4 = __toESM(require("crypto")); + import_fs5 = __toESM(require("fs")); + import_path4 = __toESM(require("path")); + util = __toESM(require("util")); + import_stream = require("stream"); + import_events = require("events"); + init_debugLogger(); + init_zones(); + init_debug(); + colors2 = require("./utilsBundle").colors; + pipelineAsync = util.promisify(import_stream.pipeline); + NodeZone = class _NodeZone { + constructor(zone) { + this._zone = zone; + } + push(data) { + return new _NodeZone(this._zone.with("apiZone", data)); + } + pop() { + return new _NodeZone(this._zone.without("apiZone")); + } + run(func) { + return this._zone.run(func); + } + data() { + return this._zone.data("apiZone"); + } + }; + boxedStackPrefixes = []; + nodePlatform = (coreDir) => ({ + name: "node", + boxedStackPrefixes: () => { + if (process.env.PWDEBUGIMPL) + return []; + return [coreDir, ...boxedStackPrefixes]; + }, + calculateSha1: (text2) => { + const sha1 = import_crypto4.default.createHash("sha1"); + sha1.update(text2); + return Promise.resolve(sha1.digest("hex")); + }, + colors: colors2, + coreDir, + createGuid: () => import_crypto4.default.randomBytes(16).toString("hex"), + defaultMaxListeners: () => import_events.EventEmitter.defaultMaxListeners, + fs: () => import_fs5.default, + env: process.env, + inspectCustom: util.inspect.custom, + isDebugMode: () => debugMode() === "inspector", + isJSDebuggerAttached: () => !!require("inspector").url(), + isLogEnabled(name) { + return debugLogger.isEnabled(name); + }, + isUnderTest: () => isUnderTest(), + log(name, message) { + debugLogger.log(name, message); + }, + path: () => import_path4.default, + pathSeparator: import_path4.default.sep, + showInternalStackFrames: () => !!process.env.PWDEBUGIMPL, + async streamFile(path59, stream3) { + await pipelineAsync(import_fs5.default.createReadStream(path59), stream3); + }, + streamReadable: (channel) => { + return new ReadableStreamImpl(channel); + }, + streamWritable: (channel) => { + return new WritableStreamImpl(channel); + }, + zones: { + current: () => new NodeZone(currentZone()), + empty: new NodeZone(emptyZone) + } + }); + ReadableStreamImpl = class extends import_stream.Readable { + constructor(channel) { + super(); + this._channel = channel; + } + async _read() { + const result2 = await this._channel.read({ size: 1024 * 1024 }); + if (result2.binary.byteLength) + this.push(result2.binary); + else + this.push(null); + } + _destroy(error, callback) { + this._channel.close().catch((e) => null); + super._destroy(error, callback); + } + }; + WritableStreamImpl = class extends import_stream.Writable { + constructor(channel) { + super(); + this._channel = channel; + } + async _write(chunk, encoding, callback) { + const error = await this._channel.write({ binary: typeof chunk === "string" ? Buffer.from(chunk) : chunk }).catch((e) => e); + callback(error || null); + } + async _final(callback) { + const error = await this._channel.close().catch((e) => e); + callback(error || null); + } + }; + } +}); + +// packages/utils/processLauncher.ts +async function gracefullyCloseAll() { + await Promise.all(Array.from(gracefullyCloseSet).map((gracefullyClose) => gracefullyClose().catch((e) => { + }))); +} +function gracefullyProcessExitDoNotHang(code, onExit2) { + const beforeExit = onExit2 ? () => onExit2().catch(() => { + }) : () => Promise.resolve(); + const callback = () => beforeExit().then(() => process.exit(code)); + setTimeout(callback, 3e4); + gracefullyCloseAll().then(callback); +} +function exitHandler() { + for (const kill of killSet) + kill(); +} +function sigintHandler() { + const exitWithCode130 = () => { + if (isUnderTest()) { + setTimeout(() => process.exit(130), 1e3); + } else { + process.exit(130); + } + }; + if (sigintHandlerCalled) { + process.off("SIGINT", sigintHandler); + for (const kill of killSet) + kill(); + exitWithCode130(); + } else { + sigintHandlerCalled = true; + gracefullyCloseAll().then(() => exitWithCode130()); + } +} +function sigtermHandler() { + gracefullyCloseAll(); +} +function sighupHandler() { + gracefullyCloseAll(); +} +function addProcessHandlerIfNeeded(name) { + if (!installedHandlers.has(name)) { + installedHandlers.add(name); + process.on(name, processHandlers[name]); + } +} +function removeProcessHandlersIfNeeded() { + if (killSet.size) + return; + for (const handler of installedHandlers) + process.off(handler, processHandlers[handler]); + installedHandlers.clear(); +} +async function launchProcess(options) { + const stdio = options.stdio === "pipe" ? ["ignore", "pipe", "pipe", "pipe", "pipe"] : ["pipe", "pipe", "pipe"]; + options.log(` ${options.command} ${options.args ? options.args.join(" ") : ""}`); + const spawnOptions = { + // On non-windows platforms, `detached: true` makes child process a leader of a new + // process group, making it possible to kill child process tree with `.kill(-pid)` command. + // @see https://nodejs.org/api/child_process.html#child_process_options_detached + detached: process.platform !== "win32", + env: options.env, + cwd: options.cwd, + shell: options.shell, + stdio + }; + const spawnedProcess = childProcess.spawn(options.command, options.args || [], spawnOptions); + const cleanup = async () => { + options.log(`[pid=${spawnedProcess.pid || "N/A"}] starting temporary directories cleanup`); + const errors = await removeFolders(options.tempDirectories); + for (let i = 0; i < options.tempDirectories.length; ++i) { + if (errors[i]) + options.log(`[pid=${spawnedProcess.pid || "N/A"}] exception while removing ${options.tempDirectories[i]}: ${errors[i]}`); + } + options.log(`[pid=${spawnedProcess.pid || "N/A"}] finished temporary directories cleanup`); + }; + spawnedProcess.on("error", () => { + }); + if (!spawnedProcess.pid) { + let failed; + const failedPromise = new Promise((f, r) => failed = f); + spawnedProcess.once("error", (error) => { + failed(new Error("Failed to launch: " + error)); + }); + return failedPromise.then(async (error) => { + await cleanup(); + throw error; + }); + } + options.log(` pid=${spawnedProcess.pid}`); + const stdout = readline.createInterface({ input: spawnedProcess.stdout }); + stdout.on("line", (data) => { + options.log(`[pid=${spawnedProcess.pid}][out] ` + data); + }); + const stderr = readline.createInterface({ input: spawnedProcess.stderr }); + stderr.on("line", (data) => { + options.log(`[pid=${spawnedProcess.pid}][err] ` + data); + }); + let processClosed = false; + let fulfillCleanup = () => { + }; + const waitForCleanup = new Promise((f) => fulfillCleanup = f); + spawnedProcess.once("close", (exitCode, signal) => { + options.log(`[pid=${spawnedProcess.pid}] `); + processClosed = true; + gracefullyCloseSet.delete(gracefullyClose); + killSet.delete(killProcessAndCleanup); + removeProcessHandlersIfNeeded(); + options.onExit(exitCode, signal); + cleanup().then(fulfillCleanup); + }); + addProcessHandlerIfNeeded("exit"); + if (options.handleSIGINT) + addProcessHandlerIfNeeded("SIGINT"); + if (options.handleSIGTERM) + addProcessHandlerIfNeeded("SIGTERM"); + if (options.handleSIGHUP) + addProcessHandlerIfNeeded("SIGHUP"); + gracefullyCloseSet.add(gracefullyClose); + killSet.add(killProcessAndCleanup); + let gracefullyClosing = false; + async function gracefullyClose() { + if (gracefullyClosing) { + options.log(`[pid=${spawnedProcess.pid}] `); + killProcess(); + await waitForCleanup; + return; + } + gracefullyClosing = true; + options.log(`[pid=${spawnedProcess.pid}] `); + await options.attemptToGracefullyClose().catch(() => killProcess()); + await waitForCleanup; + options.log(`[pid=${spawnedProcess.pid}] `); + } + function killProcess() { + gracefullyCloseSet.delete(gracefullyClose); + killSet.delete(killProcessAndCleanup); + removeProcessHandlersIfNeeded(); + options.log(`[pid=${spawnedProcess.pid}] `); + if (spawnedProcess.pid && !spawnedProcess.killed && !processClosed) { + options.log(`[pid=${spawnedProcess.pid}] `); + try { + if (process.platform === "win32") { + const taskkillProcess = childProcess.spawnSync(`taskkill /pid ${spawnedProcess.pid} /T /F`, { shell: true }); + const [stdout2, stderr2] = [taskkillProcess.stdout.toString(), taskkillProcess.stderr.toString()]; + if (stdout2) + options.log(`[pid=${spawnedProcess.pid}] taskkill stdout: ${stdout2}`); + if (stderr2) + options.log(`[pid=${spawnedProcess.pid}] taskkill stderr: ${stderr2}`); + } else { + process.kill(-spawnedProcess.pid, "SIGKILL"); + } + } catch (e) { + options.log(`[pid=${spawnedProcess.pid}] exception while trying to kill process: ${e}`); + } + } else { + options.log(`[pid=${spawnedProcess.pid}] `); + } + } + function killProcessAndCleanup() { + killProcess(); + options.log(`[pid=${spawnedProcess.pid || "N/A"}] starting temporary directories cleanup`); + for (const dir of options.tempDirectories) { + try { + import_fs6.default.rmSync(dir, { force: true, recursive: true, maxRetries: 5 }); + } catch (e) { + options.log(`[pid=${spawnedProcess.pid || "N/A"}] exception while removing ${dir}: ${e}`); + } + } + options.log(`[pid=${spawnedProcess.pid || "N/A"}] finished temporary directories cleanup`); + } + function killAndWait() { + killProcess(); + return waitForCleanup; + } + return { launchedProcess: spawnedProcess, gracefullyClose, kill: killAndWait }; +} +function envArrayToObject(env) { + const result2 = {}; + for (const { name, value: value2 } of env) + result2[name] = value2; + return result2; +} +var childProcess, import_fs6, readline, gracefullyCloseSet, killSet, sigintHandlerCalled, installedHandlers, processHandlers; +var init_processLauncher = __esm({ + "packages/utils/processLauncher.ts"() { + "use strict"; + childProcess = __toESM(require("child_process")); + import_fs6 = __toESM(require("fs")); + readline = __toESM(require("readline")); + init_fileUtils(); + init_debug(); + gracefullyCloseSet = /* @__PURE__ */ new Set(); + killSet = /* @__PURE__ */ new Set(); + sigintHandlerCalled = false; + installedHandlers = /* @__PURE__ */ new Set(); + processHandlers = { + exit: exitHandler, + SIGINT: sigintHandler, + SIGTERM: sigtermHandler, + SIGHUP: sighupHandler + }; + } +}); + +// packages/utils/profiler.ts +async function startProfiling() { + if (!profileDir) + return; + session = new (require("inspector")).Session(); + session.connect(); + await new Promise((f) => { + session.post("Profiler.enable", () => { + session.post("Profiler.start", f); + }); + }); +} +async function stopProfiling(profileName) { + if (!profileDir) + return; + await new Promise((f) => session.post("Profiler.stop", (err, { profile }) => { + if (!err) { + import_fs7.default.mkdirSync(profileDir, { recursive: true }); + import_fs7.default.writeFileSync(import_path5.default.join(profileDir, profileName + ".json"), JSON.stringify(profile)); + } + f(); + })); +} +var import_fs7, import_path5, profileDir, session; +var init_profiler = __esm({ + "packages/utils/profiler.ts"() { + "use strict"; + import_fs7 = __toESM(require("fs")); + import_path5 = __toESM(require("path")); + profileDir = process.env.PWTEST_PROFILE_DIR || ""; + } +}); + +// packages/utils/serializedFS.ts +var import_fs8, yazl, APPEND_CHUNK_SIZE, SerializedFS; +var init_serializedFS = __esm({ + "packages/utils/serializedFS.ts"() { + "use strict"; + import_fs8 = __toESM(require("fs")); + init_manualPromise(); + yazl = require("./utilsBundle").yazl; + APPEND_CHUNK_SIZE = 64 * 1024; + SerializedFS = class { + constructor() { + this._buffers = /* @__PURE__ */ new Map(); + this._operations = []; + this._operationsDone = new ManualPromise(); + this._operationsDone.resolve(); + } + mkdir(dir) { + this._appendOperation({ op: "mkdir", dir }); + } + writeFile(file, content, skipIfExists) { + this._buffers.delete(file); + this._appendOperation({ op: "writeFile", file, content, skipIfExists }); + } + appendFile(file, text2, flush) { + if (!this._buffers.has(file)) + this._buffers.set(file, []); + const buffer = this._buffers.get(file); + buffer.push(text2); + let size = 0; + for (const chunk of buffer) + size += chunk.length; + if (flush || size >= APPEND_CHUNK_SIZE) + this._flushFile(file); + } + _flushFile(file) { + const buffer = this._buffers.get(file); + if (buffer === void 0) + return; + const content = buffer.join(""); + this._buffers.delete(file); + this._appendOperation({ op: "appendFile", file, content }); + } + copyFile(from, to) { + this._flushFile(from); + this._buffers.delete(to); + this._appendOperation({ op: "copyFile", from, to }); + } + async syncAndGetError() { + for (const file of this._buffers.keys()) + this._flushFile(file); + await this._operationsDone; + return this._error; + } + zip(entries, zipFileName) { + for (const file of this._buffers.keys()) + this._flushFile(file); + this._appendOperation({ op: "zip", entries, zipFileName }); + } + // This method serializes all writes to the trace. + _appendOperation(op) { + const last = this._operations[this._operations.length - 1]; + if (last?.op === "appendFile" && op.op === "appendFile" && last.file === op.file && last.content.length < APPEND_CHUNK_SIZE) { + last.content += op.content; + return; + } + this._operations.push(op); + if (this._operationsDone.isDone()) + this._performOperations(); + } + async _performOperations() { + this._operationsDone = new ManualPromise(); + while (this._operations.length) { + const op = this._operations.shift(); + if (this._error) + continue; + try { + await this._performOperation(op); + } catch (e) { + this._error = e; + } + } + this._operationsDone.resolve(); + } + async _performOperation(op) { + switch (op.op) { + case "mkdir": { + await import_fs8.default.promises.mkdir(op.dir, { recursive: true }); + return; + } + case "writeFile": { + if (op.skipIfExists) + await import_fs8.default.promises.writeFile(op.file, op.content, { flag: "wx" }).catch(() => { + }); + else + await import_fs8.default.promises.writeFile(op.file, op.content); + return; + } + case "copyFile": { + await import_fs8.default.promises.copyFile(op.from, op.to); + return; + } + case "appendFile": { + await import_fs8.default.promises.appendFile(op.file, op.content); + return; + } + case "zip": { + const zipFile = new yazl.ZipFile(); + const result2 = new ManualPromise(); + zipFile.on("error", (error) => result2.reject(error)); + for (const entry of op.entries) + zipFile.addFile(entry.value, entry.name); + zipFile.end(); + zipFile.outputStream.pipe(import_fs8.default.createWriteStream(op.zipFileName)).on("close", () => result2.resolve()).on("error", (error) => result2.reject(error)); + await result2; + return; + } + } + } + }; + } +}); + +// packages/utils/socksProxy.ts +function hexToNumber(hex) { + return [...hex].reduce((value2, digit2) => { + const code = digit2.charCodeAt(0); + if (code >= 48 && code <= 57) + return value2 + code; + if (code >= 97 && code <= 102) + return value2 + (code - 97) + 10; + if (code >= 65 && code <= 70) + return value2 + (code - 65) + 10; + throw new Error("Invalid IPv6 token " + hex); + }, 0); +} +function ipToSocksAddress(address) { + const ipv4Mapped = /^::ffff:(\d+\.\d+\.\d+\.\d+)$/i.exec(address); + if (ipv4Mapped) + address = ipv4Mapped[1]; + if (import_net2.default.isIPv4(address)) { + return [ + 1, + // IPv4 + ...address.split(".", 4).map((t) => +t & 255) + // Address + ]; + } + if (import_net2.default.isIPv6(address)) { + const result2 = [4]; + const tokens = address.split(":", 8); + while (tokens.length < 8) + tokens.unshift(""); + for (const token of tokens) { + const value2 = hexToNumber(token); + result2.push(value2 >> 8 & 255, value2 & 255); + } + return result2; + } + throw new Error("Only IPv4 and IPv6 addresses are supported"); +} +function starMatchToRegex(pattern) { + const source11 = pattern.split("*").map((s) => { + return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + }).join(".*"); + return new RegExp("^" + source11 + "$"); +} +function parsePattern(pattern) { + if (!pattern) + return () => false; + const matchers = pattern.split(",").map((token) => { + const match = token.match(/^(.*?)(?::(\d+))?$/); + if (!match) + throw new Error(`Unsupported token "${token}" in pattern "${pattern}"`); + const tokenPort = match[2] ? +match[2] : void 0; + const portMatches = (port) => tokenPort === void 0 || tokenPort === port; + let tokenHost = match[1]; + if (tokenHost === "") { + return (host, port) => { + if (!portMatches(port)) + return false; + return host === "localhost" || host.endsWith(".localhost") || host === "127.0.0.1" || host === "[::1]"; + }; + } + if (tokenHost === "*") + return (host, port) => portMatches(port); + if (import_net2.default.isIPv4(tokenHost) || import_net2.default.isIPv6(tokenHost)) + return (host, port) => host === tokenHost && portMatches(port); + if (tokenHost[0] === ".") + tokenHost = "*" + tokenHost; + const tokenRegex = starMatchToRegex(tokenHost); + return (host, port) => { + if (!portMatches(port)) + return false; + if (import_net2.default.isIPv4(host) || import_net2.default.isIPv6(host)) + return false; + return !!host.match(tokenRegex); + }; + }); + return (host, port) => matchers.some((matcher) => matcher(host, port)); +} +var import_events2, import_net2, SocksConnection, SocksProxy, SocksProxyHandler; +var init_socksProxy = __esm({ + "packages/utils/socksProxy.ts"() { + "use strict"; + import_events2 = __toESM(require("events")); + import_net2 = __toESM(require("net")); + init_assert(); + init_crypto(); + init_debugLogger(); + init_happyEyeballs(); + SocksConnection = class { + constructor(uid, socket, client) { + this._buffer = Buffer.from([]); + this._offset = 0; + this._fence = 0; + this._uid = uid; + this._socket = socket; + this._client = client; + this._boundOnData = this._onData.bind(this); + socket.on("data", this._boundOnData); + socket.on("close", () => this._onClose()); + socket.on("end", () => this._onClose()); + socket.on("error", () => this._onClose()); + this._run().catch(() => this._socket.end()); + } + async _run() { + assert(await this._authenticate()); + const { command, host, port } = await this._parseRequest(); + if (command !== 1 /* CONNECT */) { + this._writeBytes(Buffer.from([ + 5, + 7 /* CommandNotSupported */, + 0, + // RSV + 1, + // IPv4 + 0, + 0, + 0, + 0, + // Address + 0, + 0 + // Port + ])); + return; + } + this._socket.off("data", this._boundOnData); + this._client.onSocketRequested({ uid: this._uid, host, port }); + } + async _authenticate() { + const version3 = await this._readByte(); + assert(version3 === 5, "The VER field must be set to x05 for this version of the protocol, was " + version3); + const nMethods = await this._readByte(); + assert(nMethods, "No authentication methods specified"); + const methods = await this._readBytes(nMethods); + for (const method of methods) { + if (method === 0) { + this._writeBytes(Buffer.from([version3, method])); + return true; + } + } + this._writeBytes(Buffer.from([version3, 255 /* NO_ACCEPTABLE_METHODS */])); + return false; + } + async _parseRequest() { + const version3 = await this._readByte(); + assert(version3 === 5, "The VER field must be set to x05 for this version of the protocol, was " + version3); + const command = await this._readByte(); + await this._readByte(); + const addressType = await this._readByte(); + let host = ""; + switch (addressType) { + case 1 /* IPv4 */: + host = (await this._readBytes(4)).join("."); + break; + case 3 /* FqName */: + const length = await this._readByte(); + host = (await this._readBytes(length)).toString(); + break; + case 4 /* IPv6 */: + const bytes = await this._readBytes(16); + const tokens = []; + for (let i = 0; i < 8; ++i) + tokens.push(bytes.readUInt16BE(i * 2).toString(16)); + host = tokens.join(":"); + break; + } + const port = (await this._readBytes(2)).readUInt16BE(0); + this._buffer = Buffer.from([]); + this._offset = 0; + this._fence = 0; + return { + command, + host, + port + }; + } + async _readByte() { + const buffer = await this._readBytes(1); + return buffer[0]; + } + async _readBytes(length) { + this._fence = this._offset + length; + if (!this._buffer || this._buffer.length < this._fence) + await new Promise((f) => this._fenceCallback = f); + this._offset += length; + return this._buffer.slice(this._offset - length, this._offset); + } + _writeBytes(buffer) { + if (this._socket.writable) + this._socket.write(buffer); + } + _onClose() { + this._client.onSocketClosed({ uid: this._uid }); + } + _onData(buffer) { + this._buffer = Buffer.concat([this._buffer, buffer]); + if (this._fenceCallback && this._buffer.length >= this._fence) { + const callback = this._fenceCallback; + this._fenceCallback = void 0; + callback(); + } + } + socketConnected(host, port) { + this._writeBytes(Buffer.from([ + 5, + 0 /* Succeeded */, + 0, + // RSV + ...ipToSocksAddress(host), + // ATYP, Address + port >> 8, + port & 255 + // Port + ])); + this._socket.on("data", (data) => this._client.onSocketData({ uid: this._uid, data })); + } + socketFailed(errorCode) { + const buffer = Buffer.from([ + 5, + 0, + 0, + // RSV + ...ipToSocksAddress("0.0.0.0"), + // ATYP, Address + 0, + 0 + // Port + ]); + switch (errorCode) { + case "ENOENT": + case "ENOTFOUND": + case "ETIMEDOUT": + case "EHOSTUNREACH": + buffer[1] = 4 /* HostUnreachable */; + break; + case "ENETUNREACH": + buffer[1] = 3 /* NetworkUnreachable */; + break; + case "ECONNREFUSED": + buffer[1] = 5 /* ConnectionRefused */; + break; + case "ERULESET": + buffer[1] = 2 /* NotAllowedByRuleSet */; + break; + } + this._writeBytes(buffer); + this._socket.end(); + } + sendData(data) { + this._socket.write(data); + } + end() { + this._socket.end(); + } + error(error) { + this._socket.destroy(new Error(error)); + } + }; + SocksProxy = class _SocksProxy extends import_events2.default { + constructor() { + super(); + this._connections = /* @__PURE__ */ new Map(); + this._sockets = /* @__PURE__ */ new Set(); + this._closed = false; + this._patternMatcher = () => false; + this._directSockets = /* @__PURE__ */ new Map(); + this._server = new import_net2.default.Server((socket) => { + const uid = createGuid(); + const connection = new SocksConnection(uid, socket, this); + this._connections.set(uid, connection); + }); + this._server.on("connection", (socket) => { + if (this._closed) { + socket.destroy(); + return; + } + this._sockets.add(socket); + socket.once("close", () => this._sockets.delete(socket)); + }); + } + static { + this.Events = { + SocksRequested: "socksRequested", + SocksData: "socksData", + SocksClosed: "socksClosed" + }; + } + setPattern(pattern) { + try { + this._patternMatcher = parsePattern(pattern); + } catch (e) { + this._patternMatcher = () => false; + } + } + async _handleDirect(request2) { + try { + const socket = await createSocket(request2.host, request2.port); + socket.on("data", (data) => this._connections.get(request2.uid)?.sendData(data)); + socket.on("error", (error) => { + this._connections.get(request2.uid)?.error(error.message); + this._directSockets.delete(request2.uid); + }); + socket.on("end", () => { + this._connections.get(request2.uid)?.end(); + this._directSockets.delete(request2.uid); + }); + const localAddress = socket.localAddress; + const localPort = socket.localPort; + this._directSockets.set(request2.uid, socket); + this._connections.get(request2.uid)?.socketConnected(localAddress, localPort); + } catch (error) { + this._connections.get(request2.uid)?.socketFailed(error.code); + } + } + port() { + return this._port; + } + async listen(port, hostname) { + return new Promise((f) => { + this._server.listen(port, hostname, () => { + const port2 = this._server.address().port; + this._port = port2; + f(port2); + }); + }); + } + async close() { + if (this._closed) + return; + this._closed = true; + for (const socket of this._sockets) + socket.destroy(); + this._sockets.clear(); + await new Promise((f) => this._server.close(f)); + } + onSocketRequested(payload) { + if (!this._patternMatcher(payload.host, payload.port)) { + this._handleDirect(payload); + return; + } + this.emit(_SocksProxy.Events.SocksRequested, payload); + } + onSocketData(payload) { + const direct = this._directSockets.get(payload.uid); + if (direct) { + direct.write(payload.data); + return; + } + this.emit(_SocksProxy.Events.SocksData, payload); + } + onSocketClosed(payload) { + const direct = this._directSockets.get(payload.uid); + if (direct) { + direct.destroy(); + this._directSockets.delete(payload.uid); + return; + } + this.emit(_SocksProxy.Events.SocksClosed, payload); + } + socketConnected({ uid, host, port }) { + this._connections.get(uid)?.socketConnected(host, port); + } + socketFailed({ uid, errorCode }) { + this._connections.get(uid)?.socketFailed(errorCode); + } + sendSocketData({ uid, data }) { + this._connections.get(uid)?.sendData(data); + } + sendSocketEnd({ uid }) { + this._connections.get(uid)?.end(); + } + sendSocketError({ uid, error }) { + this._connections.get(uid)?.error(error); + } + }; + SocksProxyHandler = class _SocksProxyHandler extends import_events2.default { + constructor(pattern, redirectPortForTest) { + super(); + this._sockets = /* @__PURE__ */ new Map(); + this._patternMatcher = () => false; + this._patternMatcher = parsePattern(pattern); + this._redirectPortForTest = redirectPortForTest; + } + static { + this.Events = { + SocksConnected: "socksConnected", + SocksData: "socksData", + SocksError: "socksError", + SocksFailed: "socksFailed", + SocksEnd: "socksEnd" + }; + } + cleanup() { + for (const uid of this._sockets.keys()) + this.socketClosed({ uid }); + } + async socketRequested({ uid, host, port }) { + debugLogger.log("socks", `[${uid}] => request ${host}:${port}`); + if (!this._patternMatcher(host, port)) { + const payload = { uid, errorCode: "ERULESET" }; + debugLogger.log("socks", `[${uid}] <= pattern error ${payload.errorCode}`); + this.emit(_SocksProxyHandler.Events.SocksFailed, payload); + return; + } + if (host === "local.playwright") + host = "localhost"; + try { + if (this._redirectPortForTest) + port = this._redirectPortForTest; + const socket = await createSocket(host, port); + socket.on("data", (data) => { + const payload2 = { uid, data }; + this.emit(_SocksProxyHandler.Events.SocksData, payload2); + }); + socket.on("error", (error) => { + const payload2 = { uid, error: error.message }; + debugLogger.log("socks", `[${uid}] <= network socket error ${payload2.error}`); + this.emit(_SocksProxyHandler.Events.SocksError, payload2); + this._sockets.delete(uid); + }); + socket.on("end", () => { + const payload2 = { uid }; + debugLogger.log("socks", `[${uid}] <= network socket closed`); + this.emit(_SocksProxyHandler.Events.SocksEnd, payload2); + this._sockets.delete(uid); + }); + const localAddress = socket.localAddress; + const localPort = socket.localPort; + this._sockets.set(uid, socket); + const payload = { uid, host: localAddress, port: localPort }; + debugLogger.log("socks", `[${uid}] <= connected to network ${payload.host}:${payload.port}`); + this.emit(_SocksProxyHandler.Events.SocksConnected, payload); + } catch (error) { + const payload = { uid, errorCode: error.code }; + debugLogger.log("socks", `[${uid}] <= connect error ${payload.errorCode}`); + this.emit(_SocksProxyHandler.Events.SocksFailed, payload); + } + } + sendSocketData({ uid, data }) { + this._sockets.get(uid)?.write(data); + } + socketClosed({ uid }) { + debugLogger.log("socks", `[${uid}] <= browser socket closed`); + this._sockets.get(uid)?.destroy(); + this._sockets.delete(uid); + } + }; + } +}); + +// packages/utils/spawnAsync.ts +function spawnAsync(cmd, args, options = {}) { + const process2 = (0, import_child_process.spawn)(cmd, args, Object.assign({ windowsHide: true }, options)); + return new Promise((resolve) => { + let stdout = ""; + let stderr = ""; + if (process2.stdout) + process2.stdout.on("data", (data) => stdout += data.toString()); + if (process2.stderr) + process2.stderr.on("data", (data) => stderr += data.toString()); + process2.on("close", (code) => resolve({ stdout, stderr, code })); + process2.on("error", (error) => resolve({ stdout, stderr, code: 0, error })); + }); +} +var import_child_process; +var init_spawnAsync = __esm({ + "packages/utils/spawnAsync.ts"() { + "use strict"; + import_child_process = require("child_process"); + } +}); + +// packages/utils/stringWidth.ts +function characterWidth(c) { + return getEastAsianWidth.eastAsianWidth(c.codePointAt(0)); +} +function stringWidth(v) { + let width = 0; + for (const { segment } of new Intl.Segmenter(void 0, { granularity: "grapheme" }).segment(v)) + width += characterWidth(segment); + return width; +} +function suffixOfWidth(v, width) { + const segments = [...new Intl.Segmenter(void 0, { granularity: "grapheme" }).segment(v)]; + let suffixBegin = v.length; + for (const { segment, index } of segments.reverse()) { + const segmentWidth = stringWidth(segment); + if (segmentWidth > width) + break; + width -= segmentWidth; + suffixBegin = index; + } + return v.substring(suffixBegin); +} +function fitToWidth(line, width, prefix) { + const prefixLength = prefix ? stripAnsiEscapes(prefix).length : 0; + width -= prefixLength; + if (stringWidth(line) <= width) + return line; + const parts = line.split(ansiRegex); + const taken = []; + for (let i = parts.length - 1; i >= 0; i--) { + if (i % 2) { + taken.push(parts[i]); + } else { + let part = suffixOfWidth(parts[i], width); + const wasTruncated = part.length < parts[i].length; + if (wasTruncated && parts[i].length > 0) { + part = "\u2026" + suffixOfWidth(parts[i], width - 1); + } + taken.push(part); + width -= stringWidth(part); + } + } + return taken.reverse().join(""); +} +var getEastAsianWidth; +var init_stringWidth = __esm({ + "packages/utils/stringWidth.ts"() { + "use strict"; + init_stringUtils(); + getEastAsianWidth = require("./utilsBundle").getEastAsianWidth; + } +}); + +// packages/utils/task.ts +function makeWaitForNextTask() { + if (process.versions.electron) + return (callback) => setTimeout(callback, 0); + if (parseInt(process.versions.node, 10) >= 11) + return setImmediate; + let spinning = false; + const callbacks = []; + const loop = () => { + const callback = callbacks.shift(); + if (!callback) { + spinning = false; + return; + } + setImmediate(loop); + callback(); + }; + return (callback) => { + callbacks.push(callback); + if (!spinning) { + spinning = true; + setImmediate(loop); + } + }; +} +var init_task = __esm({ + "packages/utils/task.ts"() { + "use strict"; + } +}); + +// packages/utils/wsServer.ts +var wsServer2, lastConnectionId, kConnectionSymbol, perMessageDeflate, WSServer; +var init_wsServer = __esm({ + "packages/utils/wsServer.ts"() { + "use strict"; + init_httpServer(); + init_network(); + init_debugLogger(); + ({ wsServer: wsServer2 } = require("./utilsBundle")); + lastConnectionId = 0; + kConnectionSymbol = Symbol("kConnection"); + perMessageDeflate = { + serverNoContextTakeover: true, + zlibDeflateOptions: { + level: 3 + }, + zlibInflateOptions: { + chunkSize: 10 * 1024 + }, + threshold: 10 * 1024 + }; + WSServer = class { + constructor(delegate) { + // Allowed Host headers for HTTP requests. null disables the check (server bound to a public address). + this._allowedHosts = null; + this._delegate = delegate; + } + async listen(port = 0, hostname, path59) { + debugLogger.log("server", `Server started at ${/* @__PURE__ */ new Date()}`); + hostname ??= "localhost"; + const server = createHttpServer((request2, response2) => this._onRequest(request2, response2)); + server.on("error", (error) => debugLogger.log("server", String(error))); + this.server = server; + const wsEndpoint = await new Promise((resolve, reject) => { + server.listen(port, hostname, () => { + const address = server.address(); + if (!address) { + reject(new Error("Could not bind server socket")); + return; + } + if (typeof address === "string") { + resolve(`${address}${path59}`); + return; + } + this._allowedHosts = computeAllowedHosts(hostname, address.address); + resolve(`ws://${urlHostFromAddress(address)}:${address.port}${path59}`); + }).on("error", reject); + }); + debugLogger.log("server", "Listening at " + wsEndpoint); + this._wsServer = new wsServer2({ + noServer: true, + perMessageDeflate + }); + this._wsServer.on("headers", (headers) => this._delegate.onHeaders(headers)); + server.on("upgrade", (request2, socket, head) => { + const pathname = new URL("http://localhost" + request2.url).pathname; + if (pathname !== path59) { + socket.write(`HTTP/${request2.httpVersion} 400 Bad Request\r +\r +`); + socket.destroy(); + return; + } + if (this._allowedHosts && !this._isAllowedOrigin(request2.headers.origin)) { + socket.write(`HTTP/${request2.httpVersion} 403 Forbidden\r +\r +`); + socket.destroy(); + return; + } + const upgradeResult = this._delegate.onUpgrade(request2, socket); + if (upgradeResult) { + socket.write(upgradeResult.error); + socket.destroy(); + return; + } + this._wsServer.handleUpgrade(request2, socket, head, (ws4) => this._wsServer.emit("connection", ws4, request2)); + }); + this._wsServer.on("connection", (ws4, request2) => { + debugLogger.log("server", "Connected client ws.extension=" + ws4.extensions); + const url2 = new URL("http://localhost" + (request2.url || "")); + const id = String(++lastConnectionId); + debugLogger.log("server", `[${id}] serving connection: ${request2.url}`); + const connection = this._delegate.onConnection(request2, url2, ws4, id); + ws4[kConnectionSymbol] = connection; + }); + return wsEndpoint; + } + _onRequest(request2, response2) { + if (this._allowedHosts) { + const host = request2.headers.host?.toLowerCase(); + const hostname = host ? hostnameFromHostHeader(host) : void 0; + if (!hostname || !this._allowedHosts.has(hostname)) { + response2.statusCode = 403; + response2.end(); + return; + } + } + this._delegate.onRequest(request2, response2); + } + _isAllowedOrigin(origin) { + if (!origin) + return true; + try { + const hostname = new URL(origin).hostname.toLowerCase(); + const bracketed = hostname.includes(":") ? `[${hostname}]` : hostname; + return this._allowedHosts.has(hostname) || this._allowedHosts.has(bracketed); + } catch { + return false; + } + } + async close() { + const server = this._wsServer; + if (!server) + return; + debugLogger.log("server", "closing websocket server"); + const waitForClose = new Promise((f) => server.close(f)); + await Promise.all(Array.from(server.clients).map(async (ws4) => { + const connection = ws4[kConnectionSymbol]; + if (connection) + await connection.close(); + try { + ws4.terminate(); + } catch (e) { + } + })); + await waitForClose; + debugLogger.log("server", "closing http server"); + if (this.server) + await new Promise((f) => this.server.close(f)); + this._wsServer = void 0; + this.server = void 0; + debugLogger.log("server", "closed server"); + } + }; + } +}); + +// packages/utils/zipFile.ts +var yauzl, ZipFile; +var init_zipFile = __esm({ + "packages/utils/zipFile.ts"() { + "use strict"; + yauzl = require("./utilsBundle").yauzl; + ZipFile = class { + constructor(fileName) { + this._entries = /* @__PURE__ */ new Map(); + this._fileName = fileName; + this._openedPromise = this._open(); + } + async _open() { + await new Promise((fulfill, reject) => { + yauzl.open(this._fileName, { autoClose: false }, (e, z31) => { + if (e) { + reject(e); + return; + } + this._zipFile = z31; + this._zipFile.on("entry", (entry) => { + this._entries.set(entry.fileName, entry); + }); + this._zipFile.on("end", fulfill); + }); + }); + } + async entries() { + await this._openedPromise; + return [...this._entries.keys()]; + } + async read(entryPath) { + await this._openedPromise; + const entry = this._entries.get(entryPath); + if (!entry) + throw new Error(`${entryPath} not found in file ${this._fileName}`); + return new Promise((resolve, reject) => { + this._zipFile.openReadStream(entry, (error, readStream) => { + if (error || !readStream) { + reject(error || "Entry not found"); + return; + } + const buffers = []; + readStream.on("data", (data) => buffers.push(data)); + readStream.on("end", () => resolve(Buffer.concat(buffers))); + }); + }); + } + close() { + this._zipFile?.close(); + } + }; + } +}); + +// packages/utils/third_party/extractZip.ts +async function extractZip(zipPath, opts) { + debug2("creating target directory", opts.dir); + if (!import_path6.default.isAbsolute(opts.dir)) + throw new Error("Target directory is expected to be absolute"); + await import_fs9.promises.mkdir(opts.dir, { recursive: true }); + opts.dir = await import_fs9.promises.realpath(opts.dir); + return new Extractor(zipPath, opts).extract(); +} +var import_fs9, import_path6, import_stream2, import_util, debugPkg, getStream, yauzl2, debug2, openZip, pipeline2, Extractor; +var init_extractZip = __esm({ + "packages/utils/third_party/extractZip.ts"() { + "use strict"; + import_fs9 = require("fs"); + import_path6 = __toESM(require("path")); + import_stream2 = __toESM(require("stream")); + import_util = require("util"); + debugPkg = require("./utilsBundle").debug; + getStream = require("./utilsBundle").getStream; + yauzl2 = require("./utilsBundle").yauzl; + debug2 = debugPkg("extract-zip"); + openZip = (0, import_util.promisify)(yauzl2.open); + pipeline2 = (0, import_util.promisify)(import_stream2.default.pipeline); + Extractor = class { + constructor(zipPath, opts) { + this.canceled = false; + this.zipPath = zipPath; + this.opts = opts; + } + async extract() { + debug2("opening", this.zipPath, "with opts", this.opts); + this.zipfile = await openZip(this.zipPath, { lazyEntries: true }); + this.canceled = false; + return new Promise((resolve, reject) => { + this.zipfile.on("error", (err) => { + this.canceled = true; + reject(err); + }); + this.zipfile.readEntry(); + this.zipfile.on("close", () => { + if (!this.canceled) { + debug2("zip extraction complete"); + resolve(); + } + }); + this.zipfile.on("entry", async (entry) => { + if (this.canceled) { + debug2("skipping entry", entry.fileName, { cancelled: this.canceled }); + return; + } + debug2("zipfile entry", entry.fileName); + if (entry.fileName.startsWith("__MACOSX/")) { + this.zipfile.readEntry(); + return; + } + const destDir = import_path6.default.dirname(import_path6.default.join(this.opts.dir, entry.fileName)); + try { + await import_fs9.promises.mkdir(destDir, { recursive: true }); + const canonicalDestDir = await import_fs9.promises.realpath(destDir); + const relativeDestDir = import_path6.default.relative(this.opts.dir, canonicalDestDir); + if (relativeDestDir.split(import_path6.default.sep).includes("..")) + throw new Error(`Out of bound path "${canonicalDestDir}" found while processing file ${entry.fileName}`); + await this.extractEntry(entry); + debug2("finished processing", entry.fileName); + this.zipfile.readEntry(); + } catch (err) { + this.canceled = true; + this.zipfile.close(); + reject(err); + } + }); + }); + } + async extractEntry(entry) { + if (this.canceled) { + debug2("skipping entry extraction", entry.fileName, { cancelled: this.canceled }); + return; + } + if (this.opts.onEntry) + this.opts.onEntry(entry, this.zipfile); + const dest = import_path6.default.join(this.opts.dir, entry.fileName); + const mode = entry.externalFileAttributes >> 16 & 65535; + const IFMT = 61440; + const IFDIR = 16384; + const IFLNK = 40960; + const symlink = (mode & IFMT) === IFLNK; + let isDir = (mode & IFMT) === IFDIR; + if (!isDir && entry.fileName.endsWith("/")) + isDir = true; + const madeBy = entry.versionMadeBy >> 8; + if (!isDir) + isDir = madeBy === 0 && entry.externalFileAttributes === 16; + debug2("extracting entry", { filename: entry.fileName, isDir, isSymlink: symlink }); + const procMode = this.getExtractedMode(mode, isDir) & 511; + const destDir = isDir ? dest : import_path6.default.dirname(dest); + const mkdirOptions = { recursive: true }; + if (isDir) + mkdirOptions.mode = procMode; + debug2("mkdir", { dir: destDir, ...mkdirOptions }); + await import_fs9.promises.mkdir(destDir, mkdirOptions); + if (isDir) + return; + debug2("opening read stream", dest); + const readStream = await (0, import_util.promisify)(this.zipfile.openReadStream.bind(this.zipfile))(entry); + if (symlink) { + const link = await getStream(readStream); + debug2("creating symlink", link, dest); + await import_fs9.promises.symlink(link, dest); + } else { + await pipeline2(readStream, (0, import_fs9.createWriteStream)(dest, { mode: procMode })); + } + } + getExtractedMode(entryMode, isDir) { + let mode = entryMode; + if (mode === 0) { + if (isDir) { + if (this.opts.defaultDirMode) + mode = Number(this.opts.defaultDirMode); + if (!mode) + mode = 493; + } else { + if (this.opts.defaultFileMode) + mode = Number(this.opts.defaultFileMode); + if (!mode) + mode = 420; + } + } + return mode; + } + }; + } +}); + +// packages/utils/third_party/lockfile.ts +function probe(file, fs61, callback) { + const cachedPrecision = fs61[cacheSymbol]; + if (cachedPrecision) { + return fs61.stat(file, (err, stat) => { + if (err) + return callback(err); + callback(null, stat.mtime, cachedPrecision); + }); + } + const mtime = new Date(Math.ceil(Date.now() / 1e3) * 1e3 + 5); + fs61.utimes(file, mtime, mtime, (err) => { + if (err) + return callback(err); + fs61.stat(file, (err2, stat) => { + if (err2) + return callback(err2); + const precision = stat.mtime.getTime() % 1e3 === 0 ? "s" : "ms"; + Object.defineProperty(fs61, cacheSymbol, { value: precision }); + callback(null, stat.mtime, precision); + }); + }); +} +function getMtime(precision) { + let now = Date.now(); + if (precision === "s") + now = Math.ceil(now / 1e3) * 1e3; + return new Date(now); +} +function getLockFile(file, options) { + return options.lockfilePath || `${file}.lock`; +} +function resolveCanonicalPath(file, options, callback) { + if (!options.realpath) + return callback(null, import_path7.default.resolve(file)); + options.fs.realpath(file, callback); +} +function acquireLock(file, options, callback) { + const lockfilePath = getLockFile(file, options); + options.fs.mkdir(lockfilePath, (err) => { + if (!err) { + return probe(lockfilePath, options.fs, (err2, mtime, mtimePrecision) => { + if (err2) { + options.fs.rmdir(lockfilePath, () => { + }); + return callback(err2); + } + callback(null, mtime, mtimePrecision); + }); + } + if (err.code !== "EEXIST") + return callback(err); + if (options.stale <= 0) + return callback(Object.assign(new Error("Lock file is already being held"), { code: "ELOCKED", file })); + options.fs.stat(lockfilePath, (err2, stat) => { + if (err2) { + if (err2.code === "ENOENT") + return acquireLock(file, { ...options, stale: 0 }, callback); + return callback(err2); + } + if (!isLockStale(stat, options)) + return callback(Object.assign(new Error("Lock file is already being held"), { code: "ELOCKED", file })); + removeLock(file, options, (err3) => { + if (err3) + return callback(err3); + acquireLock(file, { ...options, stale: 0 }, callback); + }); + }); + }); +} +function isLockStale(stat, options) { + return stat.mtime.getTime() < Date.now() - options.stale; +} +function removeLock(file, options, callback) { + options.fs.rmdir(getLockFile(file, options), (err) => { + if (err && err.code !== "ENOENT") + return callback(err); + callback(null); + }); +} +function updateLock(file, options) { + const lock2 = locks[file]; + if (lock2.updateTimeout) + return; + lock2.updateDelay = lock2.updateDelay || options.update; + lock2.updateTimeout = setTimeout(() => { + lock2.updateTimeout = null; + options.fs.stat(lock2.lockfilePath, (err, stat) => { + const isOverThreshold = lock2.lastUpdate + options.stale < Date.now(); + if (err) { + if (err.code === "ENOENT" || isOverThreshold) + return setLockAsCompromised(file, lock2, Object.assign(err, { code: "ECOMPROMISED" })); + lock2.updateDelay = 1e3; + return updateLock(file, options); + } + const isMtimeOurs = lock2.mtime.getTime() === stat.mtime.getTime(); + if (!isMtimeOurs) { + return setLockAsCompromised( + file, + lock2, + Object.assign( + new Error("Unable to update lock within the stale threshold"), + { code: "ECOMPROMISED" } + ) + ); + } + const mtime = getMtime(lock2.mtimePrecision); + options.fs.utimes(lock2.lockfilePath, mtime, mtime, (err2) => { + const isOverThreshold2 = lock2.lastUpdate + options.stale < Date.now(); + if (lock2.released) + return; + if (err2) { + if (err2.code === "ENOENT" || isOverThreshold2) + return setLockAsCompromised(file, lock2, Object.assign(err2, { code: "ECOMPROMISED" })); + lock2.updateDelay = 1e3; + return updateLock(file, options); + } + lock2.mtime = mtime; + lock2.lastUpdate = Date.now(); + lock2.updateDelay = null; + updateLock(file, options); + }); + }); + }, lock2.updateDelay); + if (lock2.updateTimeout && lock2.updateTimeout.unref) + lock2.updateTimeout.unref(); +} +function setLockAsCompromised(file, lock2, err) { + lock2.released = true; + if (lock2.updateTimeout) + clearTimeout(lock2.updateTimeout); + if (locks[file] === lock2) + delete locks[file]; + lock2.options.onCompromised(err); +} +function lockImpl(file, options, callback) { + const resolvedOptions = { + stale: 1e4, + update: null, + realpath: true, + retries: 0, + fs: gracefulFs, + onCompromised: (err) => { + throw err; + }, + ...options + }; + resolvedOptions.retries = resolvedOptions.retries || 0; + resolvedOptions.retries = typeof resolvedOptions.retries === "number" ? { retries: resolvedOptions.retries } : resolvedOptions.retries; + resolvedOptions.stale = Math.max(resolvedOptions.stale || 0, 2e3); + resolvedOptions.update = resolvedOptions.update === null || resolvedOptions.update === void 0 ? resolvedOptions.stale / 2 : resolvedOptions.update || 0; + resolvedOptions.update = Math.max(Math.min(resolvedOptions.update, resolvedOptions.stale / 2), 1e3); + resolveCanonicalPath(file, resolvedOptions, (err, resolvedFile) => { + if (err) + return callback(err); + const canonicalFile = resolvedFile; + const operation = retry.operation(resolvedOptions.retries); + operation.attempt(() => { + acquireLock(canonicalFile, resolvedOptions, (err2, mtime, mtimePrecision) => { + if (operation.retry(err2 || void 0)) + return; + if (err2) + return callback(operation.mainError()); + const lock2 = locks[canonicalFile] = { + lockfilePath: getLockFile(canonicalFile, resolvedOptions), + mtime, + mtimePrecision, + options: resolvedOptions, + lastUpdate: Date.now() + }; + updateLock(canonicalFile, resolvedOptions); + callback(null, (releasedCallback) => { + if (lock2.released) { + return releasedCallback && releasedCallback(Object.assign(new Error("Lock is already released"), { code: "ERELEASED" })); + } + unlock(canonicalFile, { ...resolvedOptions, realpath: false }, releasedCallback || (() => { + })); + }); + }); + }); + }); +} +function unlock(file, options, callback) { + const resolvedOptions = { + stale: 1e4, + update: null, + realpath: true, + retries: 0, + fs: gracefulFs, + onCompromised: (err) => { + throw err; + }, + ...options + }; + resolveCanonicalPath(file, resolvedOptions, (err, resolvedFile) => { + if (err) + return callback(err); + const canonicalFile = resolvedFile; + const lock2 = locks[canonicalFile]; + if (!lock2) + return callback(Object.assign(new Error("Lock is not acquired/owned by you"), { code: "ENOTACQUIRED" })); + if (lock2.updateTimeout) + clearTimeout(lock2.updateTimeout); + lock2.released = true; + delete locks[canonicalFile]; + removeLock(canonicalFile, resolvedOptions, callback); + }); +} +function toPromise(method) { + return (...args) => new Promise((resolve, reject) => { + args.push((err, result2) => { + if (err) + reject(err); + else + resolve(result2); + }); + method(...args); + }); +} +function ensureCleanup() { + if (cleanupInitialized) + return; + cleanupInitialized = true; + onExit(() => { + for (const file in locks) { + const options = locks[file].options; + try { + options.fs.rmdirSync(getLockFile(file, options)); + } catch (e) { + } + } + }); +} +async function lock(file, options) { + ensureCleanup(); + const release = await toPromise(lockImpl)(file, options || {}); + return toPromise(release); +} +var import_path7, gracefulFs, retry, onExit, locks, cacheSymbol, cleanupInitialized; +var init_lockfile = __esm({ + "packages/utils/third_party/lockfile.ts"() { + "use strict"; + import_path7 = __toESM(require("path")); + gracefulFs = require("./utilsBundle").gracefulFs; + retry = require("./utilsBundle").retry; + onExit = require("./utilsBundle").onExit; + locks = {}; + cacheSymbol = Symbol(); + cleanupInitialized = false; + } +}); + +// packages/utils/index.ts +var utils_exports = {}; +__export(utils_exports, { + FastStats: () => FastStats, + HttpServer: () => HttpServer, + ImageChannel: () => ImageChannel, + NET_DEFAULT_TIMEOUT: () => NET_DEFAULT_TIMEOUT, + RecentLogsCollector: () => RecentLogsCollector, + SerializedFS: () => SerializedFS, + SocksProxy: () => SocksProxy, + SocksProxyHandler: () => SocksProxyHandler, + WSServer: () => WSServer, + ZipFile: () => ZipFile, + Zone: () => Zone, + addSuffixToFilePath: () => addSuffixToFilePath, + blendWithWhite: () => blendWithWhite, + calculateSha1: () => calculateSha1, + canAccessFile: () => canAccessFile, + colorDeltaE94: () => colorDeltaE94, + compare: () => compare, + compareBuffersOrStrings: () => compareBuffersOrStrings, + computeAllowedHosts: () => computeAllowedHosts, + copyFileAndMakeWritable: () => copyFileAndMakeWritable, + createGuid: () => createGuid, + createHttp2Server: () => createHttp2Server, + createHttpServer: () => createHttpServer, + createHttpsServer: () => createHttpsServer, + createProxyAgent: () => createProxyAgent, + currentZone: () => currentZone, + debugLogger: () => debugLogger, + debugMode: () => debugMode, + decorateServer: () => decorateServer, + defaultUserDataDirForChannel: () => defaultUserDataDirForChannel, + emptyZone: () => emptyZone, + envArrayToObject: () => envArrayToObject, + eventsHelper: () => eventsHelper, + existsAsync: () => existsAsync, + extractZip: () => extractZip, + fitToWidth: () => fitToWidth, + generateSelfSignedCertificate: () => generateSelfSignedCertificate, + getAsBooleanFromENV: () => getAsBooleanFromENV, + getComparator: () => getComparator, + getFromENV: () => getFromENV, + getPackageManager: () => getPackageManager, + getPackageManagerExecCommand: () => getPackageManagerExecCommand, + gracefullyCloseAll: () => gracefullyCloseAll, + gracefullyCloseSet: () => gracefullyCloseSet, + gracefullyProcessExitDoNotHang: () => gracefullyProcessExitDoNotHang, + guessClientName: () => guessClientName, + hostPlatform: () => hostPlatform, + hostnameFromHostHeader: () => hostnameFromHostHeader, + httpRequest: () => httpRequest, + isChromiumChannelName: () => isChromiumChannelName, + isCodingAgent: () => isCodingAgent, + isLikelyNpxGlobal: () => isLikelyNpxGlobal, + isOfficiallySupportedPlatform: () => isOfficiallySupportedPlatform, + isPathInside: () => isPathInside, + isSystemDirectory: () => isSystemDirectory, + isURLAvailable: () => isURLAvailable, + isUnderTest: () => isUnderTest, + isWritable: () => isWritable, + jsonStringifyForceASCII: () => jsonStringifyForceASCII, + launchProcess: () => launchProcess, + lock: () => lock, + makeSocketPath: () => makeSocketPath, + makeWaitForNextTask: () => makeWaitForNextTask, + mkdirIfNeeded: () => mkdirIfNeeded, + nodePlatform: () => nodePlatform, + parsePattern: () => parsePattern, + perMessageDeflate: () => perMessageDeflate, + removeFolders: () => removeFolders, + resolveWithinRoot: () => resolveWithinRoot, + rgb2gray: () => rgb2gray, + sanitizeForFilePath: () => sanitizeForFilePath, + serveFolder: () => serveFolder, + setBoxedStackPrefixes: () => setBoxedStackPrefixes, + setPlaywrightTestProcessEnv: () => setPlaywrightTestProcessEnv, + shortPlatform: () => shortPlatform, + spawnAsync: () => spawnAsync, + srgb2xyz: () => srgb2xyz, + ssim: () => ssim, + startHttpServer: () => startHttpServer, + startProfiling: () => startProfiling, + stopProfiling: () => stopProfiling, + stringWidth: () => stringWidth, + throwingResolveWithinRoot: () => throwingResolveWithinRoot, + toPosixPath: () => toPosixPath, + trimLongString: () => trimLongString, + urlHostFromAddress: () => urlHostFromAddress, + wrapInASCIIBox: () => wrapInASCIIBox, + xyz2lab: () => xyz2lab +}); +var init_utils = __esm({ + "packages/utils/index.ts"() { + "use strict"; + init_ascii(); + init_chromiumChannels(); + init_comparators(); + init_crypto(); + init_debug(); + init_debugLogger(); + init_env(); + init_eventsHelper(); + init_fileUtils(); + init_hostPlatform(); + init_httpServer(); + init_network(); + init_nodePlatform(); + init_processLauncher(); + init_profiler(); + init_serializedFS(); + init_socksProxy(); + init_spawnAsync(); + init_stringWidth(); + init_task(); + init_wsServer(); + init_zipFile(); + init_zones(); + init_extractZip(); + init_lockfile(); + init_colorUtils(); + init_compare(); + init_imageChannel(); + init_stats(); + } +}); + +// packages/playwright-core/src/client/eventEmitter.ts +function checkListener(listener) { + if (typeof listener !== "function") + throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener); +} +function unwrapListener(l) { + return wrappedListener(l) ?? l; +} +function unwrapListeners(arr) { + return arr.map((l) => wrappedListener(l) ?? l); +} +function wrappedListener(l) { + return l.listener; +} +var EventEmitter3, OnceWrapper; +var init_eventEmitter = __esm({ + "packages/playwright-core/src/client/eventEmitter.ts"() { + "use strict"; + EventEmitter3 = class { + constructor(platform) { + this._events = void 0; + this._eventsCount = 0; + this._maxListeners = void 0; + this._pendingHandlers = /* @__PURE__ */ new Map(); + this._platform = platform; + if (this._events === void 0 || this._events === Object.getPrototypeOf(this)._events) { + this._events = /* @__PURE__ */ Object.create(null); + this._eventsCount = 0; + } + this._maxListeners = this._maxListeners || void 0; + this.on = this.addListener; + this.off = this.removeListener; + } + setMaxListeners(n) { + if (typeof n !== "number" || n < 0 || Number.isNaN(n)) + throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + "."); + this._maxListeners = n; + return this; + } + getMaxListeners() { + return this._maxListeners === void 0 ? this._platform.defaultMaxListeners() : this._maxListeners; + } + emit(type3, ...args) { + const events = this._events; + if (events === void 0) + return false; + const handler = events?.[type3]; + if (handler === void 0) + return false; + if (typeof handler === "function") { + this._callHandler(type3, handler, args); + } else { + const len = handler.length; + const listeners = handler.slice(); + for (let i = 0; i < len; ++i) + this._callHandler(type3, listeners[i], args); + } + return true; + } + _callHandler(type3, handler, args) { + const promise = Reflect.apply(handler, this, args); + if (!(promise instanceof Promise)) + return; + let set = this._pendingHandlers.get(type3); + if (!set) { + set = /* @__PURE__ */ new Set(); + this._pendingHandlers.set(type3, set); + } + set.add(promise); + promise.catch((e) => { + if (this._rejectionHandler) + this._rejectionHandler(e); + else + throw e; + }).finally(() => set.delete(promise)); + } + addListener(type3, listener) { + return this._addListener(type3, listener, false); + } + on(type3, listener) { + return this._addListener(type3, listener, false); + } + _addListener(type3, listener, prepend) { + checkListener(listener); + let events = this._events; + let existing; + if (events === void 0) { + events = this._events = /* @__PURE__ */ Object.create(null); + this._eventsCount = 0; + } else { + if (events.newListener !== void 0) { + this.emit("newListener", type3, unwrapListener(listener)); + events = this._events; + } + existing = events[type3]; + } + if (existing === void 0) { + existing = events[type3] = listener; + ++this._eventsCount; + } else { + if (typeof existing === "function") { + existing = events[type3] = prepend ? [listener, existing] : [existing, listener]; + } else if (prepend) { + existing.unshift(listener); + } else { + existing.push(listener); + } + const m = this.getMaxListeners(); + if (m > 0 && existing.length > m && !existing.warned) { + existing.warned = true; + const w = new Error("Possible EventEmitter memory leak detected. " + existing.length + " " + String(type3) + " listeners added. Use emitter.setMaxListeners() to increase limit"); + w.name = "MaxListenersExceededWarning"; + w.emitter = this; + w.type = type3; + w.count = existing.length; + if (!this._platform.isUnderTest()) { + console.warn(w); + } + } + } + return this; + } + prependListener(type3, listener) { + return this._addListener(type3, listener, true); + } + once(type3, listener) { + checkListener(listener); + this.on(type3, new OnceWrapper(this, type3, listener).wrapperFunction); + return this; + } + prependOnceListener(type3, listener) { + checkListener(listener); + this.prependListener(type3, new OnceWrapper(this, type3, listener).wrapperFunction); + return this; + } + removeListener(type3, listener) { + checkListener(listener); + const events = this._events; + if (events === void 0) + return this; + const list = events[type3]; + if (list === void 0) + return this; + if (list === listener || list.listener === listener) { + if (--this._eventsCount === 0) { + this._events = /* @__PURE__ */ Object.create(null); + } else { + delete events[type3]; + if (events.removeListener) + this.emit("removeListener", type3, list.listener ?? listener); + } + } else if (typeof list !== "function") { + let position = -1; + let originalListener; + for (let i = list.length - 1; i >= 0; i--) { + if (list[i] === listener || wrappedListener(list[i]) === listener) { + originalListener = wrappedListener(list[i]); + position = i; + break; + } + } + if (position < 0) + return this; + if (position === 0) + list.shift(); + else + list.splice(position, 1); + if (list.length === 1) + events[type3] = list[0]; + if (events.removeListener !== void 0) + this.emit("removeListener", type3, originalListener || listener); + } + return this; + } + off(type3, listener) { + return this.removeListener(type3, listener); + } + removeAllListeners(type3, options) { + this._removeAllListeners(type3); + if (!options) + return this; + if (options.behavior === "wait") { + const errors = []; + this._rejectionHandler = (error) => errors.push(error); + return this._waitFor(type3).then(() => { + if (errors.length) + throw errors[0]; + }); + } + if (options.behavior === "ignoreErrors") + this._rejectionHandler = () => { + }; + return Promise.resolve(); + } + _removeAllListeners(type3) { + const events = this._events; + if (!events) + return; + if (!events.removeListener) { + if (type3 === void 0) { + this._events = /* @__PURE__ */ Object.create(null); + this._eventsCount = 0; + } else if (events[type3] !== void 0) { + if (--this._eventsCount === 0) + this._events = /* @__PURE__ */ Object.create(null); + else + delete events[type3]; + } + return; + } + if (type3 === void 0) { + const keys = Object.keys(events); + let key; + for (let i = 0; i < keys.length; ++i) { + key = keys[i]; + if (key === "removeListener") + continue; + this._removeAllListeners(key); + } + this._removeAllListeners("removeListener"); + this._events = /* @__PURE__ */ Object.create(null); + this._eventsCount = 0; + return; + } + const listeners = events[type3]; + if (typeof listeners === "function") { + this.removeListener(type3, listeners); + } else if (listeners !== void 0) { + for (let i = listeners.length - 1; i >= 0; i--) + this.removeListener(type3, listeners[i]); + } + } + listeners(type3) { + return this._listeners(this, type3, true); + } + rawListeners(type3) { + return this._listeners(this, type3, false); + } + listenerCount(type3) { + const events = this._events; + if (events !== void 0) { + const listener = events[type3]; + if (typeof listener === "function") + return 1; + if (listener !== void 0) + return listener.length; + } + return 0; + } + eventNames() { + return this._eventsCount > 0 && this._events ? Reflect.ownKeys(this._events) : []; + } + async _waitFor(type3) { + let promises = []; + if (type3) { + promises = [...this._pendingHandlers.get(type3) || []]; + } else { + promises = []; + for (const [, pending] of this._pendingHandlers) + promises.push(...pending); + } + await Promise.all(promises); + } + _listeners(target, type3, unwrap) { + const events = target._events; + if (events === void 0) + return []; + const listener = events[type3]; + if (listener === void 0) + return []; + if (typeof listener === "function") + return unwrap ? [unwrapListener(listener)] : [listener]; + return unwrap ? unwrapListeners(listener) : listener.slice(); + } + }; + OnceWrapper = class { + constructor(eventEmitter, eventType, listener) { + this._fired = false; + this._eventEmitter = eventEmitter; + this._eventType = eventType; + this._listener = listener; + this.wrapperFunction = this._handle.bind(this); + this.wrapperFunction.listener = listener; + } + _handle(...args) { + if (this._fired) + return; + this._fired = true; + this._eventEmitter.removeListener(this._eventType, this.wrapperFunction); + return this._listener.apply(this._eventEmitter, args); + } + }; + } +}); + +// packages/playwright-core/src/bootstrap.ts +var minimumMajorNodeVersion, currentNodeVersion, major; +var init_bootstrap = __esm({ + "packages/playwright-core/src/bootstrap.ts"() { + "use strict"; + minimumMajorNodeVersion = 18; + currentNodeVersion = process.versions.node; + major = +currentNodeVersion.split(".")[0]; + if (major < minimumMajorNodeVersion) { + console.error( + "You are running Node.js " + currentNodeVersion + `. +Playwright requires Node.js ${minimumMajorNodeVersion} or higher. +Please update your version of Node.js.` + ); + process.exit(1); + } + if (process.env.PW_INSTRUMENT_MODULES) { + const Module = require("module"); + const originalLoad = Module._load; + const root = { name: "", selfMs: 0, totalMs: 0, childrenMs: 0, children: [] }; + let current = root; + const stack = []; + Module._load = function(request2, _parent, _isMain) { + const node = { name: request2, selfMs: 0, totalMs: 0, childrenMs: 0, children: [] }; + current.children.push(node); + stack.push(current); + current = node; + const start3 = performance.now(); + let result2; + try { + result2 = originalLoad.apply(this, arguments); + } catch (e) { + current = stack.pop(); + current.children.pop(); + throw e; + } + const duration = performance.now() - start3; + node.totalMs = duration; + node.selfMs = Math.max(0, duration - node.childrenMs); + current = stack.pop(); + current.childrenMs += duration; + return result2; + }; + process.on("exit", () => { + function printTree(node, prefix, isLast, lines2, depth) { + if (node.totalMs < 1 && depth > 0) + return; + const connector = depth === 0 ? "" : isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 "; + const time = `${node.totalMs.toFixed(1).padStart(8)}ms`; + const self2 = node.children.length ? ` (self: ${node.selfMs.toFixed(1)}ms)` : ""; + lines2.push(`${time} ${prefix}${connector}${node.name}${self2}`); + const childPrefix = prefix + (depth === 0 ? "" : isLast ? " " : "\u2502 "); + const sorted2 = node.children.slice().sort((a, b) => b.totalMs - a.totalMs); + for (let i = 0; i < sorted2.length; i++) + printTree(sorted2[i], childPrefix, i === sorted2.length - 1, lines2, depth + 1); + } + let totalModules = 0; + function count(n) { + totalModules++; + n.children.forEach(count); + } + root.children.forEach(count); + const lines = []; + const sorted = root.children.slice().sort((a, b) => b.totalMs - a.totalMs); + for (let i = 0; i < sorted.length; i++) + printTree(sorted[i], "", i === sorted.length - 1, lines, 0); + const totalMs = root.children.reduce((s, c) => s + c.totalMs, 0); + process.stderr.write(` +--- Module load tree: ${totalModules} modules, ${totalMs.toFixed(0)}ms total --- +` + lines.join("\n") + "\n"); + const flat = /* @__PURE__ */ new Map(); + function gather(n) { + const existing = flat.get(n.name); + if (existing) { + existing.selfMs += n.selfMs; + existing.totalMs += n.totalMs; + existing.count++; + } else { + flat.set(n.name, { selfMs: n.selfMs, totalMs: n.totalMs, count: 1 }); + } + n.children.forEach(gather); + } + root.children.forEach(gather); + const top50 = [...flat.entries()].sort((a, b) => b[1].selfMs - a[1].selfMs).slice(0, 50); + const flatLines = top50.map( + ([mod, { selfMs, totalMs: totalMs2, count: count2 }]) => `${selfMs.toFixed(1).padStart(8)}ms self ${totalMs2.toFixed(1).padStart(8)}ms total (x${String(count2).padStart(3)}) ${mod}` + ); + process.stderr.write(` +--- Top 50 modules by self time --- +` + flatLines.join("\n") + "\n"); + }); + } + } +}); + +// packages/playwright-core/src/package.ts +function libPath(...parts) { + return import_path8.default.join(packageRoot, "lib", ...parts); +} +var import_path8, packageRoot, packageJSON, binPath; +var init_package = __esm({ + "packages/playwright-core/src/package.ts"() { + "use strict"; + import_path8 = __toESM(require("path")); + packageRoot = import_path8.default.join(__dirname, ".."); + packageJSON = require(import_path8.default.join(packageRoot, "package.json")); + binPath = import_path8.default.join(packageRoot, "bin"); + } +}); + +// packages/playwright-core/src/tools/trace/traceParser.ts +async function extractTrace(traceFile, outDir) { + const zipFile = new ZipFile(traceFile); + try { + const entries = await zipFile.entries(); + for (const entry of entries) { + const outPath = resolveWithinRoot(outDir, entry); + if (!outPath) + throw new Error(`Trace entry '${entry}' escapes output directory`); + await import_fs10.default.promises.mkdir(import_path9.default.dirname(outPath), { recursive: true }); + const buffer = await zipFile.read(entry); + await import_fs10.default.promises.writeFile(outPath, buffer); + } + } finally { + zipFile.close(); + } +} +var import_fs10, import_path9, DirTraceLoaderBackend; +var init_traceParser = __esm({ + "packages/playwright-core/src/tools/trace/traceParser.ts"() { + "use strict"; + import_fs10 = __toESM(require("fs")); + import_path9 = __toESM(require("path")); + init_fileUtils(); + init_zipFile(); + DirTraceLoaderBackend = class { + constructor(dir) { + this._dir = dir; + } + isLive() { + return false; + } + async entryNames() { + const entries = []; + const walk = async (dir, prefix) => { + const items = await import_fs10.default.promises.readdir(dir, { withFileTypes: true }); + for (const item of items) { + if (item.isDirectory()) + await walk(import_path9.default.join(dir, item.name), prefix ? `${prefix}/${item.name}` : item.name); + else + entries.push(prefix ? `${prefix}/${item.name}` : item.name); + } + }; + await walk(this._dir, ""); + return entries; + } + async hasEntry(entryName) { + const resolved = resolveWithinRoot(this._dir, entryName); + if (!resolved) + return false; + try { + await import_fs10.default.promises.access(resolved); + return true; + } catch { + return false; + } + } + async readText(entryName) { + const resolved = resolveWithinRoot(this._dir, entryName); + if (!resolved) + return; + try { + return await import_fs10.default.promises.readFile(resolved, "utf-8"); + } catch { + } + } + async readBlob(entryName) { + const resolved = resolveWithinRoot(this._dir, entryName); + if (!resolved) + return; + try { + const buffer = await import_fs10.default.promises.readFile(resolved); + return new Blob([new Uint8Array(buffer)]); + } catch { + } + } + }; + } +}); + +// packages/playwright-core/src/tools/trace/traceUtils.ts +function ensureTraceOpen() { + if (!import_fs11.default.existsSync(traceDir)) + throw new Error(`No trace opened. Run 'npx playwright trace open ' first.`); + return traceDir; +} +async function closeTrace() { + if (import_fs11.default.existsSync(traceDir)) + await import_fs11.default.promises.rm(traceDir, { recursive: true }); +} +async function openTrace(traceFile) { + const filePath = import_path10.default.resolve(traceFile); + if (!import_fs11.default.existsSync(filePath)) + throw new Error(`Trace file not found: ${filePath}`); + await closeTrace(); + await import_fs11.default.promises.mkdir(traceDir, { recursive: true }); + if (filePath.endsWith(".zip")) + await extractTrace(filePath, traceDir); + else + await import_fs11.default.promises.writeFile(import_path10.default.join(traceDir, ".link"), filePath, "utf-8"); +} +async function loadTrace() { + const dir = ensureTraceOpen(); + const linkFile = import_path10.default.join(dir, ".link"); + let traceDir2; + let traceFile; + if (import_fs11.default.existsSync(linkFile)) { + const tracePath = await import_fs11.default.promises.readFile(linkFile, "utf-8"); + traceDir2 = import_path10.default.dirname(tracePath); + traceFile = import_path10.default.basename(tracePath); + } else { + traceDir2 = dir; + } + const backend = new DirTraceLoaderBackend(traceDir2); + const loader = new TraceLoader(); + await loader.load(backend, traceFile); + const model = new TraceModel(traceDir2, loader.contextEntries); + return new LoadedTrace(model, loader, buildOrdinalMap(model)); +} +function formatTimestamp(ms, base) { + const relative = ms - base; + if (relative < 0) + return "0:00.000"; + const totalMs = Math.floor(relative); + const minutes = Math.floor(totalMs / 6e4); + const seconds = Math.floor(totalMs % 6e4 / 1e3); + const millis = totalMs % 1e3; + return `${minutes}:${seconds.toString().padStart(2, "0")}.${millis.toString().padStart(3, "0")}`; +} +function actionTitle(action) { + return renderTitleForCall({ ...action, type: action.class }) || `${action.class}.${action.method}`; +} +async function saveOutputFile(fileName, content, explicitOutput) { + let outFile; + if (explicitOutput) { + outFile = explicitOutput; + } else { + const resolved = resolveWithinRoot(cliOutputDir, fileName); + if (!resolved) + throw new Error(`Attachment name '${fileName}' escapes output directory`); + await import_fs11.default.promises.mkdir(import_path10.default.dirname(resolved), { recursive: true }); + outFile = resolved; + } + await import_fs11.default.promises.writeFile(outFile, content); + return outFile; +} +function buildOrdinalMap(model) { + const actions = model.actions.filter((a) => a.group !== "configuration"); + const { rootItem } = buildActionTree(actions); + const ordinalToCallId = /* @__PURE__ */ new Map(); + const callIdToOrdinal = /* @__PURE__ */ new Map(); + let ordinal = 1; + const visit = (item) => { + ordinalToCallId.set(ordinal, item.action.callId); + callIdToOrdinal.set(item.action.callId, ordinal); + ordinal++; + for (const child of item.children) + visit(child); + }; + for (const child of rootItem.children) + visit(child); + return { ordinalToCallId, callIdToOrdinal }; +} +var import_fs11, import_path10, traceDir, cliOutputDir, LoadedTrace; +var init_traceUtils2 = __esm({ + "packages/playwright-core/src/tools/trace/traceUtils.ts"() { + "use strict"; + import_fs11 = __toESM(require("fs")); + import_path10 = __toESM(require("path")); + init_traceModel(); + init_traceLoader(); + init_protocolFormatter(); + init_fileUtils(); + init_traceParser(); + traceDir = import_path10.default.join(".playwright-cli", "trace"); + cliOutputDir = ".playwright-cli"; + LoadedTrace = class { + constructor(model, loader, ordinals) { + this.model = model; + this.loader = loader; + this.ordinalToCallId = ordinals.ordinalToCallId; + this.callIdToOrdinal = ordinals.callIdToOrdinal; + } + resolveActionId(actionId) { + const ordinal = parseInt(actionId, 10); + if (!isNaN(ordinal)) { + const callId = this.ordinalToCallId.get(ordinal); + if (callId) + return this.model.actions.find((a) => a.callId === callId); + } + return this.model.actions.find((a) => a.callId === actionId); + } + }; + } +}); + +// packages/playwright-core/src/tools/trace/traceOpen.ts +async function traceOpen(traceFile) { + await openTrace(traceFile); + await traceInfo(); +} +async function traceInfo() { + const trace = await loadTrace(); + const model = trace.model; + const info = { + browser: model.browserName || "unknown", + platform: model.platform || "unknown", + playwrightVersion: model.playwrightVersion || "unknown", + title: model.title || "", + duration: msToString(model.endTime - model.startTime), + durationMs: model.endTime - model.startTime, + startTime: model.wallTime ? new Date(model.wallTime).toISOString() : "unknown", + viewport: model.options.viewport ? `${model.options.viewport.width}x${model.options.viewport.height}` : "default", + actions: model.actions.length, + pages: model.pages.length, + network: model.resources.length, + errors: model.errorDescriptors.length, + attachments: model.attachments.length, + consoleMessages: model.events.filter((e) => e.type === "console").length + }; + console.log(""); + console.log(` Browser: ${info.browser}`); + console.log(` Platform: ${info.platform}`); + console.log(` Playwright: ${info.playwrightVersion}`); + if (info.title) + console.log(` Title: ${info.title}`); + console.log(` Duration: ${info.duration}`); + console.log(` Start time: ${info.startTime}`); + console.log(` Viewport: ${info.viewport}`); + console.log(` Actions: ${info.actions}`); + console.log(` Pages: ${info.pages}`); + console.log(` Network: ${info.network} requests`); + console.log(` Errors: ${info.errors}`); + console.log(` Attachments: ${info.attachments}`); + console.log(` Console: ${info.consoleMessages} messages`); + console.log(""); +} +var init_traceOpen = __esm({ + "packages/playwright-core/src/tools/trace/traceOpen.ts"() { + "use strict"; + init_formatUtils(); + init_traceUtils2(); + } +}); + +// packages/playwright-core/src/tools/trace/traceActions.ts +async function traceActions(options) { + const trace = await loadTrace(); + const actions = filterActions(trace.model.actions, options); + const { rootItem } = buildActionTree(actions); + console.log(` ${"#".padStart(4)} ${"Time".padEnd(9)} ${"Action".padEnd(55)} ${"Duration".padStart(8)}`); + console.log(` ${"\u2500".repeat(4)} ${"\u2500".repeat(9)} ${"\u2500".repeat(55)} ${"\u2500".repeat(8)}`); + const visit = (item, indent) => { + const action = item.action; + const ordinal = trace.callIdToOrdinal.get(action.callId) ?? "?"; + const ts = formatTimestamp(action.startTime, trace.model.startTime); + const duration = action.endTime ? msToString(action.endTime - action.startTime) : "running"; + const title = actionTitle(action); + const locator2 = actionLocator(action); + const error = action.error ? " \u2717" : ""; + const prefix = ` ${(ordinal + ".").padStart(4)} ${ts} ${indent}`; + console.log(`${prefix}${title.padEnd(Math.max(1, 55 - indent.length))} ${duration.padStart(8)}${error}`); + if (locator2) + console.log(`${" ".repeat(prefix.length)}${locator2}`); + for (const child of item.children) + visit(child, indent + " "); + }; + for (const child of rootItem.children) + visit(child, ""); +} +function filterActions(actions, options) { + let result2 = actions.filter((a) => a.group !== "configuration"); + if (options.grep) { + const pattern = new RegExp(options.grep, "i"); + result2 = result2.filter((a) => pattern.test(actionTitle(a)) || pattern.test(actionLocator(a) || "")); + } + if (options.errorsOnly) + result2 = result2.filter((a) => !!a.error); + return result2; +} +function actionLocator(action, sdkLanguage) { + return action.params.selector ? asLocatorDescription(sdkLanguage || "javascript", action.params.selector) : void 0; +} +async function traceAction(actionId) { + const trace = await loadTrace(); + const action = trace.resolveActionId(actionId); + if (!action) { + console.error(`Action '${actionId}' not found. Use 'trace actions' to see available action IDs.`); + process.exitCode = 1; + return; + } + const title = actionTitle(action); + console.log(` + ${title} +`); + console.log(" Time"); + console.log(` start: ${formatTimestamp(action.startTime, trace.model.startTime)}`); + const duration = action.endTime ? msToString(action.endTime - action.startTime) : action.error ? "Timed Out" : "Running"; + console.log(` duration: ${duration}`); + const paramKeys = Object.keys(action.params).filter((name) => name !== "info"); + if (paramKeys.length) { + console.log("\n Parameters"); + for (const key of paramKeys) { + const value2 = formatParamValue(action.params[key]); + console.log(` ${key}: ${value2}`); + } + } + if (action.result) { + console.log("\n Return value"); + for (const [key, value2] of Object.entries(action.result)) + console.log(` ${key}: ${formatParamValue(value2)}`); + } + if (action.error) { + console.log("\n Error"); + console.log(` ${action.error.message}`); + } + if (action.log.length) { + console.log("\n Log"); + for (const entry of action.log) { + const time = entry.time !== -1 ? formatTimestamp(entry.time, trace.model.startTime) : ""; + console.log(` ${time.padEnd(12)} ${entry.message}`); + } + } + if (action.stack?.length) { + console.log("\n Source"); + for (const frame of action.stack.slice(0, 5)) { + const file = frame.file.replace(/.*[/\\](.*)/, "$1"); + console.log(` ${file}:${frame.line}:${frame.column}`); + } + } + const snapshots = []; + if (action.beforeSnapshot) + snapshots.push("before"); + if (action.inputSnapshot) + snapshots.push("input"); + if (action.afterSnapshot) + snapshots.push("after"); + if (snapshots.length) { + console.log("\n Snapshots"); + console.log(` available: ${snapshots.join(", ")}`); + console.log(` usage: npx playwright trace snapshot ${actionId} --name <${snapshots.join("|")}>`); + } + console.log(""); +} +function formatParamValue(value2) { + if (value2 === void 0 || value2 === null) + return String(value2); + if (typeof value2 === "string") + return `"${value2}"`; + if (typeof value2 !== "object") + return String(value2); + if (value2.guid) + return ""; + return JSON.stringify(value2).slice(0, 1e3); +} +var init_traceActions = __esm({ + "packages/playwright-core/src/tools/trace/traceActions.ts"() { + "use strict"; + init_traceModel(); + init_locatorGenerators(); + init_formatUtils(); + init_traceUtils2(); + } +}); + +// packages/playwright-core/src/tools/trace/traceRequests.ts +async function traceRequests(options) { + const trace = await loadTrace(); + const model = trace.model; + let indexed = model.resources.map((r, i) => ({ resource: r, ordinal: i + 1 })); + if (options.grep) { + const pattern = new RegExp(options.grep, "i"); + indexed = indexed.filter(({ resource: r }) => pattern.test(r.request.url)); + } + if (options.method) + indexed = indexed.filter(({ resource: r }) => r.request.method.toLowerCase() === options.method.toLowerCase()); + if (options.status) { + const code = parseInt(options.status, 10); + indexed = indexed.filter(({ resource: r }) => r.response.status === code); + } + if (options.failed) + indexed = indexed.filter(({ resource: r }) => r.response.status >= 400 || r.response.status === -1); + if (!indexed.length) { + console.log(" No network requests"); + return; + } + console.log(` ${"#".padStart(4)} ${"Method".padEnd(8)} ${"Status".padEnd(8)} ${"Name".padEnd(45)} ${"Duration".padStart(10)} ${"Size".padStart(8)} ${"Route".padEnd(10)}`); + console.log(` ${"\u2500".repeat(4)} ${"\u2500".repeat(8)} ${"\u2500".repeat(8)} ${"\u2500".repeat(45)} ${"\u2500".repeat(10)} ${"\u2500".repeat(8)} ${"\u2500".repeat(10)}`); + for (const { resource: r, ordinal } of indexed) { + let name; + try { + const url2 = new URL(r.request.url); + name = url2.pathname.substring(url2.pathname.lastIndexOf("/") + 1); + if (!name) + name = url2.host; + if (url2.search) + name += url2.search; + } catch { + name = r.request.url; + } + if (name.length > 45) + name = name.substring(0, 42) + "..."; + const status = r.response.status > 0 ? String(r.response.status) : "ERR"; + const size = r.response._transferSize > 0 ? r.response._transferSize : r.response.bodySize; + const route2 = formatRouteStatus(r); + console.log(` ${(ordinal + ".").padStart(4)} ${r.request.method.padEnd(8)} ${status.padEnd(8)} ${name.padEnd(45)} ${msToString(r.time).padStart(10)} ${bytesToString2(size).padStart(8)} ${route2.padEnd(10)}`); + } +} +async function traceRequest(requestId) { + const trace = await loadTrace(); + const model = trace.model; + const ordinal = parseInt(requestId, 10); + const resource = !isNaN(ordinal) && ordinal >= 1 && ordinal <= model.resources.length ? model.resources[ordinal - 1] : void 0; + if (!resource) { + console.error(`Request '${requestId}' not found. Use 'trace requests' to see available request IDs.`); + process.exitCode = 1; + return; + } + const r = resource; + const status = r.response.status > 0 ? `${r.response.status} ${r.response.statusText}` : "ERR"; + const size = r.response._transferSize > 0 ? r.response._transferSize : r.response.bodySize; + console.log(` + ${r.request.method} ${r.request.url} +`); + console.log(" General"); + console.log(` status: ${status}`); + console.log(` duration: ${msToString(r.time)}`); + console.log(` size: ${bytesToString2(size)}`); + if (r.response.content.mimeType) + console.log(` type: ${r.response.content.mimeType}`); + const route2 = formatRouteStatus(r); + if (route2) + console.log(` route: ${route2}`); + if (r.serverIPAddress) + console.log(` server: ${r.serverIPAddress}${r._serverPort ? ":" + r._serverPort : ""}`); + if (r.response._failureText) + console.log(` error: ${r.response._failureText}`); + if (r.request.headers.length) { + console.log("\n Request headers"); + for (const h of r.request.headers) + console.log(` ${h.name}: ${h.value}`); + } + if (r.request.postData) { + console.log("\n Request body"); + const resource2 = r.request.postData._sha1 ?? r.request.postData._file; + if (resource2) { + console.log(` ${import_path11.default.relative(process.cwd(), import_path11.default.join(trace.model.traceUri, "resources", resource2))}`); + } else { + const text2 = r.request.postData.text.length > 2e3 ? r.request.postData.text.substring(0, 2e3) + "..." : r.request.postData.text; + console.log(` ${text2}`); + } + } + if (r.response.headers.length) { + console.log("\n Response headers"); + for (const h of r.response.headers) + console.log(` ${h.name}: ${h.value}`); + } + if (r.response.bodySize > 0) { + const resource2 = r.response.content._sha1 ?? r.response.content._file; + if (resource2) { + console.log("\n Response body"); + console.log(` ${import_path11.default.relative(process.cwd(), import_path11.default.join(trace.model.traceUri, "resources", resource2))}`); + } else if (r.response.content.text) { + const text2 = r.response.content.text.length > 2e3 ? r.response.content.text.substring(0, 2e3) + "..." : r.response.content.text; + console.log("\n Response body"); + console.log(` ${text2}`); + } + } + if (r._securityDetails) { + console.log("\n Security"); + if (r._securityDetails.protocol) + console.log(` protocol: ${r._securityDetails.protocol}`); + if (r._securityDetails.subjectName) + console.log(` subject: ${r._securityDetails.subjectName}`); + if (r._securityDetails.issuer) + console.log(` issuer: ${r._securityDetails.issuer}`); + } + console.log(""); +} +function bytesToString2(bytes) { + if (bytes < 0 || !isFinite(bytes)) + return "-"; + if (bytes === 0) + return "0"; + if (bytes < 1e3) + return bytes.toFixed(0); + const kb = bytes / 1024; + if (kb < 1e3) + return kb.toFixed(1) + "K"; + const mb = kb / 1024; + if (mb < 1e3) + return mb.toFixed(1) + "M"; + const gb = mb / 1024; + return gb.toFixed(1) + "G"; +} +function formatRouteStatus(r) { + if (r._wasAborted) + return "aborted"; + if (r._wasContinued) + return "continued"; + if (r._wasFulfilled) + return "fulfilled"; + if (r._apiRequest) + return "api"; + return ""; +} +var import_path11; +var init_traceRequests = __esm({ + "packages/playwright-core/src/tools/trace/traceRequests.ts"() { + "use strict"; + import_path11 = __toESM(require("path")); + init_formatUtils(); + init_traceUtils2(); + } +}); + +// packages/playwright-core/src/tools/trace/traceConsole.ts +async function traceConsole(options) { + const trace = await loadTrace(); + const model = trace.model; + const items = []; + for (const event of model.events) { + if (event.type === "console") { + if (options.stdio) + continue; + const level = event.messageType; + if (options.errorsOnly && level !== "error") + continue; + if (options.warnings && level !== "error" && level !== "warning") + continue; + const url2 = event.location.url; + const filename = url2 ? url2.substring(url2.lastIndexOf("/") + 1) : ""; + items.push({ + type: "browser", + level, + text: event.text, + location: `${filename}:${event.location.lineNumber}`, + timestamp: event.time + }); + } + if (event.type === "event" && event.method === "pageError") { + if (options.stdio) + continue; + const error = event.params.error; + items.push({ + type: "browser", + level: "error", + text: error?.error?.message || String(error?.value || ""), + timestamp: event.time + }); + } + } + for (const event of model.stdio) { + if (options.browser) + continue; + if (options.errorsOnly && event.type !== "stderr") + continue; + if (options.warnings && event.type !== "stderr") + continue; + let text2 = ""; + if (event.text) + text2 = event.text.trim(); + if (event.base64) + text2 = Buffer.from(event.base64, "base64").toString("utf-8").trim(); + if (!text2) + continue; + items.push({ + type: event.type, + level: event.type === "stderr" ? "error" : "info", + text: text2, + timestamp: event.timestamp + }); + } + items.sort((a, b) => a.timestamp - b.timestamp); + if (!items.length) { + console.log(" No console entries"); + return; + } + for (const item of items) { + const ts = formatTimestamp(item.timestamp, model.startTime); + const source11 = item.type === "browser" ? "[browser]" : `[${item.type}]`; + const level = item.level.padEnd(8); + const location2 = item.location ? ` ${item.location}` : ""; + console.log(` ${ts} ${source11.padEnd(10)} ${level} ${item.text}${location2}`); + } +} +var init_traceConsole = __esm({ + "packages/playwright-core/src/tools/trace/traceConsole.ts"() { + "use strict"; + init_traceUtils2(); + } +}); + +// packages/playwright-core/src/tools/trace/traceErrors.ts +async function traceErrors() { + const trace = await loadTrace(); + const model = trace.model; + if (!model.errorDescriptors.length) { + console.log(" No errors"); + return; + } + for (const error of model.errorDescriptors) { + if (error.action) { + const title = actionTitle(error.action); + console.log(` + \u2717 ${title}`); + } else { + console.log(` + \u2717 Error`); + } + if (error.stack?.length) { + const frame = error.stack[0]; + const file = frame.file.replace(/.*[/\\](.*)/, "$1"); + console.log(` at ${file}:${frame.line}:${frame.column}`); + } + console.log(""); + const indented = error.message.split("\n").map((l) => ` ${l}`).join("\n"); + console.log(indented); + } + console.log(""); +} +var init_traceErrors = __esm({ + "packages/playwright-core/src/tools/trace/traceErrors.ts"() { + "use strict"; + init_traceUtils2(); + } +}); + +// packages/isomorphic/disposable.ts +async function disposeAll(disposables) { + const copy = [...disposables]; + disposables.length = 0; + await Promise.all(copy.map((d) => d.dispose())); +} +var init_disposable = __esm({ + "packages/isomorphic/disposable.ts"() { + "use strict"; + } +}); + +// packages/playwright-core/src/server/userAgent.ts +function getUserAgent() { + if (cachedUserAgent) + return cachedUserAgent; + try { + cachedUserAgent = determineUserAgent(); + } catch (e) { + cachedUserAgent = "Playwright/unknown"; + } + return cachedUserAgent; +} +function determineUserAgent() { + let osIdentifier = "unknown"; + let osVersion = "unknown"; + if (process.platform === "win32") { + const version3 = import_os4.default.release().split("."); + osIdentifier = "windows"; + osVersion = `${version3[0]}.${version3[1]}`; + } else if (process.platform === "darwin") { + const version3 = (0, import_child_process2.execSync)("sw_vers -productVersion", { stdio: ["ignore", "pipe", "ignore"] }).toString().trim().split("."); + osIdentifier = "macOS"; + osVersion = `${version3[0]}.${version3[1]}`; + } else if (process.platform === "linux") { + const distroInfo = getLinuxDistributionInfoSync(); + if (distroInfo) { + osIdentifier = distroInfo.id || "linux"; + osVersion = distroInfo.version || "unknown"; + } else { + osIdentifier = "linux"; + } + } + const additionalTokens = []; + if (process.env.CI) + additionalTokens.push("CI/1"); + const serializedTokens = additionalTokens.length ? " " + additionalTokens.join(" ") : ""; + const { embedderName, embedderVersion } = getEmbedderName(); + return `Playwright/${getPlaywrightVersion()} (${import_os4.default.arch()}; ${osIdentifier} ${osVersion}) ${embedderName}/${embedderVersion}${serializedTokens}`; +} +function getEmbedderName() { + let embedderName = "unknown"; + let embedderVersion = "unknown"; + if (!process.env.PW_LANG_NAME) { + embedderName = "node"; + embedderVersion = process.version.substring(1).split(".").slice(0, 2).join("."); + } else if (["node", "python", "java", "csharp"].includes(process.env.PW_LANG_NAME)) { + embedderName = process.env.PW_LANG_NAME; + embedderVersion = process.env.PW_LANG_NAME_VERSION ?? "unknown"; + } + return { embedderName, embedderVersion }; +} +function getPlaywrightVersion(majorMinorOnly = false) { + const version3 = process.env.PW_VERSION_OVERRIDE || packageJSON.version; + return majorMinorOnly ? version3.split(".").slice(0, 2).join(".") : version3; +} +var import_child_process2, import_os4, cachedUserAgent; +var init_userAgent = __esm({ + "packages/playwright-core/src/server/userAgent.ts"() { + "use strict"; + import_child_process2 = require("child_process"); + import_os4 = __toESM(require("os")); + init_linuxUtils(); + init_package(); + } +}); + +// packages/playwright-core/src/generated/clockSource.ts +var source; +var init_clockSource = __esm({ + "packages/playwright-core/src/generated/clockSource.ts"() { + "use strict"; + source = '\nvar __commonJS = obj => {\n let required = false;\n let result;\n return function __require() {\n if (!required) {\n required = true;\n let fn;\n for (const name in obj) { fn = obj[name]; break; }\n const module = { exports: {} };\n fn(module.exports, module);\n result = module.exports;\n }\n return result;\n }\n};\nvar __export = (target, all) => {for (var name in all) target[name] = all[name];};\nvar __toESM = mod => ({ ...mod, \'default\': mod });\nvar __toCommonJS = mod => ({ ...mod, __esModule: true });\n\n\n// packages/injected/src/clock.ts\nvar clock_exports = {};\n__export(clock_exports, {\n ClockController: () => ClockController,\n createClock: () => createClock,\n inject: () => inject,\n install: () => install\n});\nmodule.exports = __toCommonJS(clock_exports);\nvar ClockController = class {\n constructor(embedder) {\n this._duringTick = false;\n this._uniqueTimerId = idCounterStart;\n this.disposables = [];\n this._log = [];\n this._timers = /* @__PURE__ */ new Map();\n this._now = { time: asWallTime(0), isFixedTime: false, ticks: 0, origin: asWallTime(-1) };\n this._embedder = embedder;\n }\n uninstall() {\n this.disposables.forEach((dispose) => dispose());\n this.disposables.length = 0;\n }\n now() {\n this._replayLogOnce();\n this._syncRealTime();\n return this._now.time;\n }\n install(time) {\n this._replayLogOnce();\n this._innerInstall(asWallTime(time));\n }\n setSystemTime(time) {\n this._replayLogOnce();\n this._innerSetTime(asWallTime(time));\n }\n setFixedTime(time) {\n this._replayLogOnce();\n this._innerSetFixedTime(asWallTime(time));\n }\n performanceNow() {\n this._replayLogOnce();\n this._syncRealTime();\n return this._now.ticks;\n }\n _syncRealTime() {\n if (!this._realTime)\n return;\n const now = this._embedder.performanceNow();\n const sinceLastSync = now - this._realTime.lastSyncTicks;\n if (sinceLastSync > 0) {\n this._advanceNow(shiftTicks(this._now.ticks, sinceLastSync));\n this._realTime.lastSyncTicks = now;\n }\n }\n _innerSetTime(time) {\n this._now.time = time;\n this._now.isFixedTime = false;\n if (this._now.origin < 0)\n this._now.origin = this._now.time;\n }\n _innerInstall(time) {\n if (this._now.origin < 0)\n this._now.ticks = 0;\n this._innerSetTime(time);\n }\n _innerSetFixedTime(time) {\n this._innerSetTime(time);\n this._now.isFixedTime = true;\n }\n _advanceNow(to) {\n if (this._now.ticks > to) {\n return;\n }\n if (!this._now.isFixedTime)\n this._now.time = asWallTime(this._now.time + to - this._now.ticks);\n this._now.ticks = to;\n }\n async log(type, time, param) {\n this._log.push({ type, time, param });\n }\n async runFor(ticks) {\n this._replayLogOnce();\n if (ticks < 0)\n throw new TypeError("Negative ticks are not supported");\n await this._runWithDisabledRealTimeSync(async () => {\n await this._runTo(shiftTicks(this._now.ticks, ticks));\n });\n }\n async _runTo(to) {\n to = Math.ceil(to);\n if (this._now.ticks > to)\n return;\n let firstException;\n while (true) {\n const result = await this._callFirstTimer(to);\n if (!result.timerFound)\n break;\n firstException = firstException || result.error;\n }\n this._advanceNow(to);\n if (firstException)\n throw firstException;\n }\n async pauseAt(time) {\n this._replayLogOnce();\n await this._innerPause();\n const toConsume = time - this._now.time;\n await this._innerFastForwardTo(shiftTicks(this._now.ticks, toConsume));\n return toConsume;\n }\n async _innerPause() {\n var _a;\n this._realTime = void 0;\n await ((_a = this._currentRealTimeTimer) == null ? void 0 : _a.dispose());\n this._currentRealTimeTimer = void 0;\n }\n resume() {\n this._replayLogOnce();\n this._innerResume();\n }\n _innerResume() {\n const now = this._embedder.performanceNow();\n this._realTime = { startTicks: now, lastSyncTicks: now };\n this._updateRealTimeTimer();\n }\n _updateRealTimeTimer() {\n var _a;\n if ((_a = this._currentRealTimeTimer) == null ? void 0 : _a.promise) {\n return;\n }\n const firstTimer = this._firstTimer();\n const nextTick = Math.min(firstTimer ? firstTimer.callAt : this._now.ticks + maxTimeout, this._now.ticks + 100);\n const callAt = this._currentRealTimeTimer ? Math.min(this._currentRealTimeTimer.callAt, nextTick) : nextTick;\n if (this._currentRealTimeTimer) {\n this._currentRealTimeTimer.cancel();\n this._currentRealTimeTimer = void 0;\n }\n const realTimeTimer = {\n callAt,\n promise: void 0,\n cancel: this._embedder.setTimeout(() => {\n this._syncRealTime();\n realTimeTimer.promise = this._runTo(this._now.ticks).catch((e) => console.error(e));\n void realTimeTimer.promise.then(() => {\n this._currentRealTimeTimer = void 0;\n if (this._realTime)\n this._updateRealTimeTimer();\n });\n }, callAt - this._now.ticks),\n dispose: async () => {\n realTimeTimer.cancel();\n await realTimeTimer.promise;\n }\n };\n this._currentRealTimeTimer = realTimeTimer;\n }\n async _runWithDisabledRealTimeSync(fn) {\n if (!this._realTime) {\n await fn();\n return;\n }\n await this._innerPause();\n try {\n await fn();\n } finally {\n this._innerResume();\n }\n }\n async fastForward(ticks) {\n this._replayLogOnce();\n await this._runWithDisabledRealTimeSync(async () => {\n await this._innerFastForwardTo(shiftTicks(this._now.ticks, ticks | 0));\n });\n }\n async _innerFastForwardTo(to) {\n if (to < this._now.ticks)\n throw new Error("Cannot fast-forward to the past");\n for (const timer of this._timers.values()) {\n if (to > timer.callAt)\n timer.callAt = to;\n }\n await this._runTo(to);\n }\n addTimer(options) {\n this._replayLogOnce();\n if (options.type === "AnimationFrame" /* AnimationFrame */ && !options.func)\n throw new Error("Callback must be provided to requestAnimationFrame calls");\n if (options.type === "IdleCallback" /* IdleCallback */ && !options.func)\n throw new Error("Callback must be provided to requestIdleCallback calls");\n if (["Timeout" /* Timeout */, "Interval" /* Interval */].includes(options.type) && !options.func && options.delay === void 0)\n throw new Error("Callback must be provided to timer calls");\n let delay = options.delay ? +options.delay : 0;\n if (!Number.isFinite(delay))\n delay = 0;\n delay = delay > maxTimeout ? 1 : delay;\n delay = Math.max(0, delay);\n const timer = {\n type: options.type,\n func: options.func,\n args: options.args || [],\n delay,\n callAt: shiftTicks(this._now.ticks, delay || (this._duringTick ? 1 : 0)),\n createdAt: this._now.ticks,\n id: this._uniqueTimerId++,\n error: new Error()\n };\n this._timers.set(timer.id, timer);\n if (this._realTime)\n this._updateRealTimeTimer();\n return timer.id;\n }\n countTimers() {\n return this._timers.size;\n }\n _firstTimer(beforeTick) {\n let firstTimer = null;\n for (const timer of this._timers.values()) {\n const isInRange = beforeTick === void 0 || timer.callAt <= beforeTick;\n if (isInRange && (!firstTimer || compareTimers(firstTimer, timer) === 1))\n firstTimer = timer;\n }\n return firstTimer;\n }\n _takeFirstTimer(beforeTick) {\n const timer = this._firstTimer(beforeTick);\n if (!timer)\n return null;\n this._advanceNow(timer.callAt);\n if (timer.type === "Interval" /* Interval */)\n timer.callAt = shiftTicks(timer.callAt, timer.delay);\n else\n this._timers.delete(timer.id);\n return timer;\n }\n async _callFirstTimer(beforeTick) {\n const timer = this._takeFirstTimer(beforeTick);\n if (!timer)\n return { timerFound: false };\n this._duringTick = true;\n try {\n if (typeof timer.func !== "function") {\n let error2;\n try {\n (() => {\n globalThis.eval(timer.func);\n })();\n } catch (e) {\n error2 = e;\n }\n await new Promise((f) => this._embedder.setTimeout(f));\n return { timerFound: true, error: error2 };\n }\n let args = timer.args;\n if (timer.type === "AnimationFrame" /* AnimationFrame */)\n args = [this._now.ticks];\n else if (timer.type === "IdleCallback" /* IdleCallback */)\n args = [{ didTimeout: false, timeRemaining: () => 0 }];\n let error;\n try {\n timer.func.apply(null, args);\n } catch (e) {\n error = e;\n }\n await new Promise((f) => this._embedder.setTimeout(f));\n return { timerFound: true, error };\n } finally {\n this._duringTick = false;\n }\n }\n getTimeToNextFrame() {\n this._replayLogOnce();\n return 16 - this._now.ticks % 16;\n }\n clearTimer(timerId, type) {\n this._replayLogOnce();\n if (!timerId) {\n return;\n }\n const id = Number(timerId);\n if (Number.isNaN(id) || id < idCounterStart) {\n const handlerName = getClearHandler(type);\n new Error(`Clock: ${handlerName} was invoked to clear a native timer instead of one created by the clock library.`);\n }\n const timer = this._timers.get(id);\n if (timer) {\n if (timer.type === type || timer.type === "Timeout" && type === "Interval" || timer.type === "Interval" && type === "Timeout") {\n this._timers.delete(id);\n } else {\n const clear = getClearHandler(type);\n const schedule = getScheduleHandler(timer.type);\n throw new Error(\n `Cannot clear timer: timer created with ${schedule}() but cleared with ${clear}()`\n );\n }\n }\n }\n _replayLogOnce() {\n if (!this._log.length)\n return;\n let lastLogTime = -1;\n let isPaused = false;\n for (const { type, time, param } of this._log) {\n if (!isPaused && lastLogTime !== -1)\n this._advanceNow(shiftTicks(this._now.ticks, time - lastLogTime));\n lastLogTime = time;\n if (type === "install") {\n this._innerInstall(asWallTime(param));\n } else if (type === "fastForward" || type === "runFor") {\n this._advanceNow(shiftTicks(this._now.ticks, param));\n } else if (type === "pauseAt") {\n isPaused = true;\n this._innerSetTime(asWallTime(param));\n } else if (type === "resume") {\n isPaused = false;\n } else if (type === "setFixedTime") {\n this._innerSetFixedTime(asWallTime(param));\n } else if (type === "setSystemTime") {\n this._innerSetTime(asWallTime(param));\n }\n }\n if (!isPaused) {\n if (lastLogTime > 0)\n this._advanceNow(shiftTicks(this._now.ticks, this._embedder.dateNow() - lastLogTime));\n this._innerResume();\n } else {\n this._realTime = void 0;\n }\n this._log.length = 0;\n }\n};\nfunction mirrorDateProperties(target, source) {\n for (const prop in source) {\n if (source.hasOwnProperty(prop))\n target[prop] = source[prop];\n }\n target.toString = () => source.toString();\n target.prototype = source.prototype;\n target.parse = source.parse;\n target.UTC = source.UTC;\n target.prototype.toUTCString = source.prototype.toUTCString;\n target.isFake = true;\n return target;\n}\nfunction createDate(clock, NativeDate) {\n function ClockDate(year, month, date, hour, minute, second, ms) {\n if (!(this instanceof ClockDate))\n return new NativeDate(clock.now()).toString();\n switch (arguments.length) {\n case 0:\n return new NativeDate(clock.now());\n case 1:\n return new NativeDate(year);\n case 2:\n return new NativeDate(year, month);\n case 3:\n return new NativeDate(year, month, date);\n case 4:\n return new NativeDate(year, month, date, hour);\n case 5:\n return new NativeDate(year, month, date, hour, minute);\n case 6:\n return new NativeDate(\n year,\n month,\n date,\n hour,\n minute,\n second\n );\n default:\n return new NativeDate(\n year,\n month,\n date,\n hour,\n minute,\n second,\n ms\n );\n }\n }\n ClockDate.now = () => clock.now();\n return mirrorDateProperties(ClockDate, NativeDate);\n}\nfunction createIntl(clock, NativeIntl) {\n const ClockIntl = {};\n for (const key of Object.getOwnPropertyNames(NativeIntl))\n ClockIntl[key] = NativeIntl[key];\n ClockIntl.DateTimeFormat = function(...args) {\n const realFormatter = new NativeIntl.DateTimeFormat(...args);\n const formatter = {\n formatRange: realFormatter.formatRange.bind(realFormatter),\n formatRangeToParts: realFormatter.formatRangeToParts.bind(realFormatter),\n resolvedOptions: realFormatter.resolvedOptions.bind(realFormatter),\n format: (date) => realFormatter.format(date || clock.now()),\n formatToParts: (date) => realFormatter.formatToParts(date || clock.now())\n };\n return formatter;\n };\n ClockIntl.DateTimeFormat.prototype = Object.create(\n NativeIntl.DateTimeFormat.prototype\n );\n ClockIntl.DateTimeFormat.supportedLocalesOf = NativeIntl.DateTimeFormat.supportedLocalesOf;\n return ClockIntl;\n}\nfunction compareTimers(a, b) {\n if (a.callAt < b.callAt)\n return -1;\n if (a.callAt > b.callAt)\n return 1;\n if (a.type === "Immediate" /* Immediate */ && b.type !== "Immediate" /* Immediate */)\n return -1;\n if (a.type !== "Immediate" /* Immediate */ && b.type === "Immediate" /* Immediate */)\n return 1;\n if (a.createdAt < b.createdAt)\n return -1;\n if (a.createdAt > b.createdAt)\n return 1;\n if (a.id < b.id)\n return -1;\n if (a.id > b.id)\n return 1;\n}\nvar maxTimeout = Math.pow(2, 31) - 1;\nvar idCounterStart = 1e12;\nfunction platformOriginals(globalObject) {\n const raw = {\n setTimeout: globalObject.setTimeout,\n clearTimeout: globalObject.clearTimeout,\n setInterval: globalObject.setInterval,\n clearInterval: globalObject.clearInterval,\n requestAnimationFrame: globalObject.requestAnimationFrame ? globalObject.requestAnimationFrame : void 0,\n cancelAnimationFrame: globalObject.cancelAnimationFrame ? globalObject.cancelAnimationFrame : void 0,\n requestIdleCallback: globalObject.requestIdleCallback ? globalObject.requestIdleCallback : void 0,\n cancelIdleCallback: globalObject.cancelIdleCallback ? globalObject.cancelIdleCallback : void 0,\n Date: globalObject.Date,\n performance: globalObject.performance,\n Intl: globalObject.Intl,\n AbortSignal: globalObject.AbortSignal\n };\n const bound = { ...raw };\n for (const key of Object.keys(bound)) {\n if (key !== "Date" && key !== "AbortSignal" && typeof bound[key] === "function")\n bound[key] = bound[key].bind(globalObject);\n }\n return { raw, bound };\n}\nfunction getScheduleHandler(type) {\n if (type === "IdleCallback" || type === "AnimationFrame")\n return `request${type}`;\n return `set${type}`;\n}\nfunction createApi(clock, originals, browserName) {\n return {\n setTimeout: (func, timeout, ...args) => {\n const delay = timeout ? +timeout : timeout;\n return clock.addTimer({\n type: "Timeout" /* Timeout */,\n func,\n args,\n delay\n });\n },\n clearTimeout: (timerId) => {\n if (timerId)\n clock.clearTimer(timerId, "Timeout" /* Timeout */);\n },\n setInterval: (func, timeout, ...args) => {\n const delay = timeout ? +timeout : timeout;\n return clock.addTimer({\n type: "Interval" /* Interval */,\n func,\n args,\n delay\n });\n },\n clearInterval: (timerId) => {\n if (timerId)\n return clock.clearTimer(timerId, "Interval" /* Interval */);\n },\n requestAnimationFrame: (callback) => {\n return clock.addTimer({\n type: "AnimationFrame" /* AnimationFrame */,\n func: callback,\n delay: clock.getTimeToNextFrame()\n });\n },\n cancelAnimationFrame: (timerId) => {\n if (timerId)\n return clock.clearTimer(timerId, "AnimationFrame" /* AnimationFrame */);\n },\n requestIdleCallback: (callback, options) => {\n let timeToNextIdlePeriod = 0;\n if (clock.countTimers() > 0)\n timeToNextIdlePeriod = 50;\n return clock.addTimer({\n type: "IdleCallback" /* IdleCallback */,\n func: callback,\n delay: (options == null ? void 0 : options.timeout) ? Math.min(options == null ? void 0 : options.timeout, timeToNextIdlePeriod) : timeToNextIdlePeriod\n });\n },\n cancelIdleCallback: (timerId) => {\n if (timerId)\n return clock.clearTimer(timerId, "IdleCallback" /* IdleCallback */);\n },\n Intl: originals.Intl ? createIntl(clock, originals.Intl) : void 0,\n Date: createDate(clock, originals.Date),\n performance: originals.performance ? fakePerformance(clock, originals.performance) : void 0,\n AbortSignal: originals.AbortSignal ? fakeAbortSignal(clock, originals.AbortSignal, browserName) : void 0\n };\n}\nfunction getClearHandler(type) {\n if (type === "IdleCallback" || type === "AnimationFrame")\n return `cancel${type}`;\n return `clear${type}`;\n}\nvar FakePerformanceEntry = class {\n constructor(name, entryType, startTime, duration) {\n this.name = name;\n this.entryType = entryType;\n this.startTime = startTime;\n this.duration = duration;\n }\n toJSON() {\n return JSON.stringify({ ...this });\n }\n};\nfunction fakePerformance(clock, performance) {\n const result = {\n now: () => clock.performanceNow()\n };\n result.__defineGetter__("timeOrigin", () => clock._now.origin || 0);\n for (const key of Object.keys(performance.__proto__)) {\n if (key === "now" || key === "timeOrigin")\n continue;\n if (key === "getEntries" || key === "getEntriesByName" || key === "getEntriesByType")\n result[key] = () => [];\n else if (key === "mark")\n result[key] = (name) => new FakePerformanceEntry(name, "mark", 0, 0);\n else if (key === "measure")\n result[key] = (name) => new FakePerformanceEntry(name, "measure", 0, 50);\n else\n result[key] = () => {\n };\n }\n return result;\n}\nfunction fakeAbortSignal(clock, abortSignal, browserName) {\n Object.defineProperty(abortSignal, "timeout", {\n value(ms) {\n const controller = new AbortController();\n clock.addTimer({\n delay: ms,\n type: "Timeout" /* Timeout */,\n func: () => controller.abort(\n new DOMException(\n browserName === "chromium" ? "signal timed out" : "The operation timed out.",\n "TimeoutError"\n )\n )\n });\n return controller.signal;\n }\n });\n return abortSignal;\n}\nfunction createClock(globalObject, config = {}) {\n const originals = platformOriginals(globalObject);\n const embedder = {\n dateNow: () => originals.raw.Date.now(),\n performanceNow: () => Math.ceil(originals.raw.performance.now()),\n setTimeout: (task, timeout) => {\n const timerId = originals.bound.setTimeout(task, timeout);\n return () => originals.bound.clearTimeout(timerId);\n },\n setInterval: (task, delay) => {\n const intervalId = originals.bound.setInterval(task, delay);\n return () => originals.bound.clearInterval(intervalId);\n }\n };\n const clock = new ClockController(embedder);\n const api = createApi(clock, originals.bound, config.browserName);\n return { clock, api, originals: originals.raw };\n}\nfunction install(globalObject, config = {}) {\n var _a, _b;\n if ((_a = globalObject.Date) == null ? void 0 : _a.isFake) {\n throw new TypeError(`Can\'t install fake timers twice on the same global object.`);\n }\n const { clock, api, originals } = createClock(globalObject, config);\n const toFake = ((_b = config.toFake) == null ? void 0 : _b.length) ? config.toFake : Object.keys(originals);\n for (const method of toFake) {\n if (method === "Date") {\n globalObject.Date = mirrorDateProperties(api.Date, globalObject.Date);\n } else if (method === "Intl") {\n globalObject.Intl = api[method];\n } else if (method === "AbortSignal") {\n globalObject.AbortSignal = api[method];\n } else if (method === "performance") {\n globalObject.performance = api[method];\n const kEventTimeStamp = Symbol("playwrightEventTimeStamp");\n Object.defineProperty(Event.prototype, "timeStamp", {\n get() {\n var _a2;\n if (!this[kEventTimeStamp])\n this[kEventTimeStamp] = (_a2 = api.performance) == null ? void 0 : _a2.now();\n return this[kEventTimeStamp];\n }\n });\n } else {\n globalObject[method] = (...args) => {\n return api[method].apply(api, args);\n };\n }\n clock.disposables.push(() => {\n globalObject[method] = originals[method];\n });\n }\n return { clock, api, originals };\n}\nfunction inject(globalObject, browserName) {\n const builtins = platformOriginals(globalObject).bound;\n const { clock: controller } = install(globalObject, { browserName });\n controller.resume();\n return {\n controller,\n builtins\n };\n}\nfunction asWallTime(n) {\n return n;\n}\nfunction shiftTicks(ticks, ms) {\n return ticks + ms;\n}\n'; + } +}); + +// packages/playwright-core/src/protocol/serializers.ts +function parseSerializedValue(value2, handles) { + return innerParseSerializedValue(value2, handles, /* @__PURE__ */ new Map(), []); +} +function innerParseSerializedValue(value2, handles, refs, accessChain) { + if (value2.ref !== void 0) + return refs.get(value2.ref); + if (value2.n !== void 0) + return value2.n; + if (value2.s !== void 0) + return value2.s; + if (value2.b !== void 0) + return value2.b; + if (value2.v !== void 0) { + if (value2.v === "undefined") + return void 0; + if (value2.v === "null") + return null; + if (value2.v === "NaN") + return NaN; + if (value2.v === "Infinity") + return Infinity; + if (value2.v === "-Infinity") + return -Infinity; + if (value2.v === "-0") + return -0; + } + if (value2.d !== void 0) + return new Date(value2.d); + if (value2.u !== void 0) + return new URL(value2.u); + if (value2.bi !== void 0) + return BigInt(value2.bi); + if (value2.e !== void 0) { + const error = new Error(value2.e.m); + error.name = value2.e.n; + error.stack = value2.e.s; + return error; + } + if (value2.r !== void 0) + return new RegExp(value2.r.p, value2.r.f); + if (value2.ta !== void 0) { + const ctor = typedArrayKindToConstructor[value2.ta.k]; + return new ctor(value2.ta.b.buffer, value2.ta.b.byteOffset, value2.ta.b.length / ctor.BYTES_PER_ELEMENT); + } + if (value2.a !== void 0) { + const result2 = []; + refs.set(value2.id, result2); + for (let i = 0; i < value2.a.length; i++) + result2.push(innerParseSerializedValue(value2.a[i], handles, refs, [...accessChain, i])); + return result2; + } + if (value2.o !== void 0) { + const result2 = {}; + refs.set(value2.id, result2); + for (const { k, v } of value2.o) + result2[k] = innerParseSerializedValue(v, handles, refs, [...accessChain, k]); + return result2; + } + if (value2.h !== void 0) { + if (handles === void 0) + throw new Error("Unexpected handle"); + return handles[value2.h]; + } + throw new Error(`Attempting to deserialize unexpected value${accessChainToDisplayString(accessChain)}: ${value2}`); +} +function serializeValue(value2, handleSerializer) { + return innerSerializeValue(value2, handleSerializer, { lastId: 0, visited: /* @__PURE__ */ new Map() }, []); +} +function innerSerializeValue(value2, handleSerializer, visitorInfo, accessChain) { + const handle = handleSerializer(value2); + if ("fallThrough" in handle) + value2 = handle.fallThrough; + else + return handle; + if (typeof value2 === "symbol") + return { v: "undefined" }; + if (Object.is(value2, void 0)) + return { v: "undefined" }; + if (Object.is(value2, null)) + return { v: "null" }; + if (Object.is(value2, NaN)) + return { v: "NaN" }; + if (Object.is(value2, Infinity)) + return { v: "Infinity" }; + if (Object.is(value2, -Infinity)) + return { v: "-Infinity" }; + if (Object.is(value2, -0)) + return { v: "-0" }; + if (typeof value2 === "boolean") + return { b: value2 }; + if (typeof value2 === "number") + return { n: value2 }; + if (typeof value2 === "string") + return { s: value2 }; + if (typeof value2 === "bigint") + return { bi: value2.toString() }; + if (isError2(value2)) + return { e: { n: value2.name, m: value2.message, s: value2.stack || "" } }; + if (isDate(value2)) + return { d: value2.toJSON() }; + if (isURL(value2)) + return { u: value2.toJSON() }; + if (isRegExp4(value2)) + return { r: { p: value2.source, f: value2.flags } }; + const typedArrayKind = constructorToTypedArrayKind.get(value2.constructor); + if (typedArrayKind) + return { ta: { b: Buffer.from(value2.buffer, value2.byteOffset, value2.byteLength), k: typedArrayKind } }; + const id = visitorInfo.visited.get(value2); + if (id) + return { ref: id }; + if (Array.isArray(value2)) { + const a = []; + const id2 = ++visitorInfo.lastId; + visitorInfo.visited.set(value2, id2); + for (let i = 0; i < value2.length; ++i) + a.push(innerSerializeValue(value2[i], handleSerializer, visitorInfo, [...accessChain, i])); + return { a, id: id2 }; + } + if (typeof value2 === "object") { + const o = []; + const id2 = ++visitorInfo.lastId; + visitorInfo.visited.set(value2, id2); + for (const name of Object.keys(value2)) + o.push({ k: name, v: innerSerializeValue(value2[name], handleSerializer, visitorInfo, [...accessChain, name]) }); + return { o, id: id2 }; + } + throw new Error(`Attempting to serialize unexpected value${accessChainToDisplayString(accessChain)}: ${value2}`); +} +function accessChainToDisplayString(accessChain) { + const chainString = accessChain.map((accessor, i) => { + if (typeof accessor === "string") + return i ? `.${accessor}` : accessor; + return `[${accessor}]`; + }).join(""); + return chainString.length > 0 ? ` at position "${chainString}"` : ""; +} +function isRegExp4(obj) { + return obj instanceof RegExp || Object.prototype.toString.call(obj) === "[object RegExp]"; +} +function isDate(obj) { + return obj instanceof Date || Object.prototype.toString.call(obj) === "[object Date]"; +} +function isURL(obj) { + return obj instanceof URL || Object.prototype.toString.call(obj) === "[object URL]"; +} +function isError2(obj) { + const proto = obj ? Object.getPrototypeOf(obj) : null; + return obj instanceof Error || proto?.name === "Error" || proto && isError2(proto); +} +var typedArrayKindToConstructor, constructorToTypedArrayKind; +var init_serializers = __esm({ + "packages/playwright-core/src/protocol/serializers.ts"() { + "use strict"; + typedArrayKindToConstructor = { + i8: Int8Array, + ui8: Uint8Array, + ui8c: Uint8ClampedArray, + i16: Int16Array, + ui16: Uint16Array, + i32: Int32Array, + ui32: Uint32Array, + f32: Float32Array, + f64: Float64Array, + bi64: BigInt64Array, + bui64: BigUint64Array + }; + constructorToTypedArrayKind = new Map(Object.entries(typedArrayKindToConstructor).map(([k, v]) => [v, k])); + } +}); + +// packages/playwright-core/src/server/errors.ts +function isTargetClosedError(error) { + return error instanceof TargetClosedError || error.name === "TargetClosedError"; +} +function serializeError(e) { + if (isError(e)) + return { error: { message: e.message, stack: e.stack, name: e.name } }; + return { value: serializeValue(e, (value2) => ({ fallThrough: value2 })) }; +} +function parseError(error) { + if (!error.error) { + if (error.value === void 0) + throw new Error("Serialized error must have either an error or a value"); + return parseSerializedValue(error.value, void 0); + } + const e = new Error(error.error.message); + e.stack = error.error.stack || ""; + e.name = error.error.name; + return e; +} +var CustomError, TimeoutError, TargetClosedError; +var init_errors = __esm({ + "packages/playwright-core/src/server/errors.ts"() { + "use strict"; + init_rtti(); + init_serializers(); + CustomError = class extends Error { + constructor(message) { + super(message); + this.name = this.constructor.name; + } + }; + TimeoutError = class extends CustomError { + }; + TargetClosedError = class extends CustomError { + constructor(cause, logs) { + super((cause || "Target page, context or browser has been closed") + (logs || "")); + } + }; + } +}); + +// packages/playwright-core/src/server/progress.ts +function isAbortError(error) { + return error instanceof TimeoutError || !!error[kAbortErrorSymbol]; +} +async function raceUncancellableOperationWithCleanup(progress2, run, cleanup) { + let aborted = false; + try { + return await progress2.race(run().then(async (t) => { + if (aborted) + await cleanup(t); + return t; + })); + } catch (error) { + aborted = true; + throw error; + } +} +var ProgressController, kAbortErrorSymbol, nullProgress; +var init_progress = __esm({ + "packages/playwright-core/src/server/progress.ts"() { + "use strict"; + init_manualPromise(); + init_assert(); + init_time(); + init_debugLogger(); + init_errors(); + ProgressController = class _ProgressController { + constructor(metadata, onCallLog) { + this._forceAbortPromise = new ManualPromise(); + this._donePromise = new ManualPromise(); + this._state = "before"; + this.metadata = metadata || { id: "", startTime: 0, endTime: 0, type: "Internal", method: "", params: {}, log: [], internal: true }; + this._onCallLog = onCallLog; + this._forceAbortPromise.catch((e) => null); + this._controller = new AbortController(); + } + static createForSdkObject(sdkObject, callMetadata) { + const logName = sdkObject.logName || "api"; + return new _ProgressController(callMetadata, (message) => { + if (logName === "api" && sdkObject.attribution.playwright?.options.isInternalPlaywright) + return; + debugLogger.log(logName, message); + sdkObject.instrumentation.onCallLog(sdkObject, callMetadata, logName, message); + }); + } + async abort(error) { + if (this._state === "running") { + error[kAbortErrorSymbol] = true; + this._state = { error }; + this._forceAbortPromise.reject(error); + this._controller.abort(error); + } + await this._donePromise; + } + async run(task, timeout) { + const deadline = timeout ? monotonicTime() + timeout : 0; + assert(this._state === "before"); + this._state = "running"; + let timer; + let outerProgress; + let allowConcurrent = false; + const progress2 = { + timeout: timeout ?? 0, + deadline, + disableTimeout: () => { + clearTimeout(timer); + }, + log: (message) => { + if (this._state === "running") + this.metadata.log.push(message); + this._onCallLog?.(message); + }, + metadata: this.metadata, + setAllowConcurrentOrNestedRaces: (allow) => { + allowConcurrent = allow; + }, + race: (promise) => { + if (process.env.PW_DETECT_NESTED_PROGRESS) { + const innerProgress = new Error().stack; + if (outerProgress && !allowConcurrent && outerProgress !== innerProgress) { + console.error("Cannot call race() inside another race()"); + console.error("<<<<>>>>:", outerProgress); + console.error("<<<<>>>>:", innerProgress); + } + outerProgress = innerProgress; + } + const promises = Array.isArray(promise) ? promise : [promise]; + if (!promises.length) + return Promise.resolve(); + return Promise.race([...promises, this._forceAbortPromise]).finally(() => outerProgress = void 0); + }, + wait: async (timeout2) => { + let timer2; + const promise = new Promise((f) => timer2 = setTimeout(f, timeout2)); + return progress2.race(promise).finally(() => clearTimeout(timer2)); + }, + signal: this._controller.signal + }; + if (deadline) { + const timeoutError = new TimeoutError(`Timeout ${timeout}ms exceeded.`); + timer = setTimeout(() => { + if (this.metadata.pauseStartTime && !this.metadata.pauseEndTime) + return; + if (this._state === "running") { + this._state = { error: timeoutError }; + this._forceAbortPromise.reject(timeoutError); + this._controller.abort(timeoutError); + } + }, deadline - monotonicTime()); + } + try { + const result2 = await task(progress2); + this._state = "finished"; + return result2; + } catch (error) { + this._state = { error }; + throw error; + } finally { + clearTimeout(timer); + this._donePromise.resolve(); + } + } + }; + kAbortErrorSymbol = Symbol("kAbortError"); + nullProgress = { + timeout: 0, + deadline: 0, + disableTimeout() { + }, + log() { + }, + race(promise) { + const promises = Array.isArray(promise) ? promise : [promise]; + return Promise.race(promises); + }, + wait: async (timeout) => await new Promise((f) => setTimeout(f, timeout)), + signal: new AbortController().signal, + metadata: { + id: "", + startTime: 0, + endTime: 0, + type: "", + method: "", + params: {}, + log: [], + internal: true + }, + setAllowConcurrentOrNestedRaces() { + } + }; + } +}); + +// packages/playwright-core/src/server/clock.ts +function parseTicks(value2) { + if (typeof value2 === "number") + return value2; + if (!value2) + return 0; + const str = value2; + const strings = str.split(":"); + const l = strings.length; + let i = l; + let ms = 0; + let parsed; + if (l > 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) { + throw new Error( + `Clock only understands numbers, 'mm:ss' and 'hh:mm:ss'` + ); + } + while (i--) { + parsed = parseInt(strings[i], 10); + if (parsed >= 60) + throw new Error(`Invalid time ${str}`); + ms += parsed * Math.pow(60, l - i - 1); + } + return ms * 1e3; +} +function parseTime(epoch) { + if (!epoch) + return 0; + if (typeof epoch === "number") + return epoch; + const parsed = new Date(epoch); + if (!isFinite(parsed.getTime())) + throw new Error(`Invalid date: ${epoch}`); + return parsed.getTime(); +} +var Clock; +var init_clock = __esm({ + "packages/playwright-core/src/server/clock.ts"() { + "use strict"; + init_clockSource(); + init_progress(); + Clock = class { + constructor(browserContext) { + this._initScripts = []; + this._browserContext = browserContext; + } + async uninstall(progress2) { + await progress2.race(Promise.all(this._initScripts.map((script) => script.dispose()))); + this._initScripts = []; + } + async fastForward(ticks) { + await this._installIfNeeded(); + const ticksMillis = parseTicks(ticks); + this._initScripts.push(await this._browserContext.addInitScript(nullProgress, `globalThis.__pwClock.controller.log('fastForward', ${Date.now()}, ${ticksMillis})`)); + await this._evaluateInFrames(`globalThis.__pwClock.controller.fastForward(${ticksMillis})`); + } + async install(time) { + await this._installIfNeeded(); + const timeMillis = time !== void 0 ? parseTime(time) : Date.now(); + this._initScripts.push(await this._browserContext.addInitScript(nullProgress, `globalThis.__pwClock.controller.log('install', ${Date.now()}, ${timeMillis})`)); + await this._evaluateInFrames(`globalThis.__pwClock.controller.install(${timeMillis})`); + } + async pauseAt(ticks) { + await this._installIfNeeded(); + const timeMillis = parseTime(ticks); + this._initScripts.push(await this._browserContext.addInitScript(nullProgress, `globalThis.__pwClock.controller.log('pauseAt', ${Date.now()}, ${timeMillis})`)); + await this._evaluateInFrames(`globalThis.__pwClock.controller.pauseAt(${timeMillis})`); + } + resumeNoReply() { + if (!this._initScripts.length) + return; + const doResume = async () => { + this._initScripts.push(await this._browserContext.addInitScript(nullProgress, `globalThis.__pwClock.controller.log('resume', ${Date.now()})`)); + await this._evaluateInFrames(`globalThis.__pwClock.controller.resume()`); + }; + doResume().catch(() => { + }); + } + async resume(progress2) { + await progress2.race(this._installIfNeeded()); + this._initScripts.push(await this._browserContext.addInitScript(nullProgress, `globalThis.__pwClock.controller.log('resume', ${Date.now()})`)); + await progress2.race(this._evaluateInFrames(`globalThis.__pwClock.controller.resume()`)); + } + async setFixedTime(time) { + await this._installIfNeeded(); + const timeMillis = parseTime(time); + this._initScripts.push(await this._browserContext.addInitScript(nullProgress, `globalThis.__pwClock.controller.log('setFixedTime', ${Date.now()}, ${timeMillis})`)); + await this._evaluateInFrames(`globalThis.__pwClock.controller.setFixedTime(${timeMillis})`); + } + async setSystemTime(time) { + await this._installIfNeeded(); + const timeMillis = parseTime(time); + this._initScripts.push(await this._browserContext.addInitScript(nullProgress, `globalThis.__pwClock.controller.log('setSystemTime', ${Date.now()}, ${timeMillis})`)); + await this._evaluateInFrames(`globalThis.__pwClock.controller.setSystemTime(${timeMillis})`); + } + async runFor(ticks) { + await this._installIfNeeded(); + const ticksMillis = parseTicks(ticks); + this._initScripts.push(await this._browserContext.addInitScript(nullProgress, `globalThis.__pwClock.controller.log('runFor', ${Date.now()}, ${ticksMillis})`)); + await this._evaluateInFrames(`globalThis.__pwClock.controller.runFor(${ticksMillis})`); + } + async _installIfNeeded() { + if (this._initScripts.length) + return; + const script = `(() => { + const module = {}; + ${source} + if (!globalThis.__pwClock) + globalThis.__pwClock = (module.exports.inject())(globalThis, ${JSON.stringify(this._browserContext._browser.options.name)}); + })();`; + const initScript = await this._browserContext.addInitScript(nullProgress, script); + await this._evaluateInFrames(script); + this._initScripts.push(initScript); + } + async _evaluateInFrames(script) { + await this._browserContext.safeNonStallingEvaluateInAllFrames(script, "main", { throwOnJSErrors: true }); + } + }; + } +}); + +// packages/playwright-core/src/generated/webAuthnSource.ts +var source2; +var init_webAuthnSource = __esm({ + "packages/playwright-core/src/generated/webAuthnSource.ts"() { + "use strict"; + source2 = ` +var __commonJS = obj => { + let required = false; + let result; + return function __require() { + if (!required) { + required = true; + let fn; + for (const name in obj) { fn = obj[name]; break; } + const module = { exports: {} }; + fn(module.exports, module); + result = module.exports; + } + return result; + } +}; +var __export = (target, all) => {for (var name in all) target[name] = all[name];}; +var __toESM = mod => ({ ...mod, 'default': mod }); +var __toCommonJS = mod => ({ ...mod, __esModule: true }); + + +// packages/injected/src/webAuthn.ts +var webAuthn_exports = {}; +__export(webAuthn_exports, { + inject: () => inject +}); +module.exports = __toCommonJS(webAuthn_exports); +function inject(globalThis) { + if (globalThis.__pwWebAuthnInstalled) + return; + globalThis.__pwWebAuthnInstalled = true; + const binding = globalThis.__pwWebAuthnBinding; + if (!binding || !globalThis.navigator) + return; + if (!globalThis.navigator.credentials) { + Object.defineProperty(globalThis.navigator, "credentials", { + value: { create: async () => null, get: async () => null }, + writable: true, + configurable: true + }); + } + function toBase64Url(buf) { + const bytes = buf instanceof ArrayBuffer ? new Uint8Array(buf) : new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength); + let s = ""; + for (let i = 0; i < bytes.length; i++) + s += String.fromCharCode(bytes[i]); + return globalThis.btoa(s).replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", ""); + } + function fromBase64Url(s) { + let str = s.replaceAll("-", "+").replaceAll("_", "/"); + while (str.length % 4) + str += "="; + const bin = globalThis.atob(str); + const out = new Uint8Array(bin.length); + for (let i = 0; i < bin.length; i++) + out[i] = bin.charCodeAt(i); + return out.buffer; + } + const PublicKeyCredentialCtor = globalThis.PublicKeyCredential; + const AuthAttestationResponseCtor = globalThis.AuthenticatorAttestationResponse; + const AuthAssertionResponseCtor = globalThis.AuthenticatorAssertionResponse; + function defineReadonly(target, props) { + for (const k of Object.keys(props)) + Object.defineProperty(target, k, { value: props[k], enumerable: true, configurable: true }); + } + function makeAttestationResponse(clientDataJSON, attestationObject) { + const proto = (AuthAttestationResponseCtor == null ? void 0 : AuthAttestationResponseCtor.prototype) || Object.prototype; + const r = Object.create(proto); + defineReadonly(r, { clientDataJSON, attestationObject }); + r.getTransports = () => ["internal"]; + r.getAuthenticatorData = () => { + return attestationObject; + }; + r.getPublicKey = () => null; + r.getPublicKeyAlgorithm = () => -7; + return r; + } + function makeAssertionResponse(clientDataJSON, authenticatorData, signature, userHandle) { + const proto = (AuthAssertionResponseCtor == null ? void 0 : AuthAssertionResponseCtor.prototype) || Object.prototype; + const r = Object.create(proto); + defineReadonly(r, { clientDataJSON, authenticatorData, signature, userHandle }); + return r; + } + function makePublicKeyCredential(id, response) { + const proto = (PublicKeyCredentialCtor == null ? void 0 : PublicKeyCredentialCtor.prototype) || Object.prototype; + const cred = Object.create(proto); + defineReadonly(cred, { + id, + rawId: fromBase64Url(id), + type: "public-key", + authenticatorAttachment: "platform", + response + }); + cred.getClientExtensionResults = () => ({}); + cred.toJSON = () => ({ id, rawId: id, type: "public-key", response: {} }); + return cred; + } + function toBuf(x) { + if (!x) + return new ArrayBuffer(0); + if (x instanceof ArrayBuffer) + return x; + const v = x; + const out = new Uint8Array(v.byteLength); + out.set(new Uint8Array(v.buffer, v.byteOffset, v.byteLength)); + return out.buffer; + } + function failure(name, message) { + const Ctor = globalThis.DOMException || Error; + throw new Ctor(message, name); + } + const origCreate = globalThis.navigator.credentials.create.bind(globalThis.navigator.credentials); + const origGet = globalThis.navigator.credentials.get.bind(globalThis.navigator.credentials); + globalThis.navigator.credentials.create = async function(options) { + var _a, _b, _c, _d, _e, _f, _g; + if (!options || !options.publicKey) + return origCreate(options); + const pk = options.publicKey; + const req = { + type: "create", + origin: globalThis.location.origin, + challenge: toBase64Url(toBuf(pk.challenge)), + rp: { id: (_a = pk.rp) == null ? void 0 : _a.id, name: ((_b = pk.rp) == null ? void 0 : _b.name) || "" }, + user: { + id: toBase64Url(toBuf((_c = pk.user) == null ? void 0 : _c.id)), + name: ((_d = pk.user) == null ? void 0 : _d.name) || "", + displayName: ((_e = pk.user) == null ? void 0 : _e.displayName) || "" + }, + pubKeyCredParams: (pk.pubKeyCredParams || []).map((p) => ({ type: p.type, alg: p.alg })), + excludeCredentials: (pk.excludeCredentials || []).map((c) => ({ type: c.type, id: toBase64Url(toBuf(c.id)) })), + userVerification: (_f = pk.authenticatorSelection) == null ? void 0 : _f.userVerification, + residentKey: (_g = pk.authenticatorSelection) == null ? void 0 : _g.residentKey + }; + const result = await binding(req); + if (!result.ok) + failure(result.name, result.message); + const resp = makeAttestationResponse(fromBase64Url(result.clientDataJSON), fromBase64Url(result.attestationObject)); + return makePublicKeyCredential(result.id, resp); + }; + globalThis.navigator.credentials.get = async function(options) { + if (!options || !options.publicKey) + return origGet(options); + const pk = options.publicKey; + const req = { + type: "get", + origin: globalThis.location.origin, + challenge: toBase64Url(toBuf(pk.challenge)), + rpId: pk.rpId || new URL(globalThis.location.origin).hostname, + allowCredentials: (pk.allowCredentials || []).map((c) => ({ type: c.type, id: toBase64Url(toBuf(c.id)) })), + userVerification: pk.userVerification + }; + const result = await binding(req); + if (!result.ok) + failure(result.name, result.message); + const resp = makeAssertionResponse( + fromBase64Url(result.clientDataJSON), + fromBase64Url(result.authenticatorData), + fromBase64Url(result.signature), + result.userHandle ? fromBase64Url(result.userHandle) : null + ); + return makePublicKeyCredential(result.id, resp); + }; + if (PublicKeyCredentialCtor) { + PublicKeyCredentialCtor.isUserVerifyingPlatformAuthenticatorAvailable = async () => true; + PublicKeyCredentialCtor.isConditionalMediationAvailable = async () => true; + } +} +`; + } +}); + +// packages/playwright-core/src/server/credentials.ts +function toPublic(r) { + return { id: r.id, rpId: r.rpId, userHandle: r.userHandle, privateKey: r.privateKey, publicKey: r.publicKey }; +} +function randomBase64Url(bytes) { + return bufToB64Url(import_crypto6.default.randomBytes(bytes)); +} +function bufToB64Url(b) { + return b.toString("base64").replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", ""); +} +function b64UrlToBuf(s) { + return Buffer.from(s, "base64url"); +} +function u32ToBytes(n) { + const b = Buffer.alloc(4); + b.writeUInt32BE(n >>> 0, 0); + return b; +} +function cborHead(major2, value2) { + const m = major2 << 5; + if (value2 < 24) + return Buffer.from([m | value2]); + if (value2 < 256) + return Buffer.from([m | 24, value2]); + if (value2 < 65536) + return Buffer.from([m | 25, value2 >> 8 & 255, value2 & 255]); + return Buffer.from([m | 26, value2 >>> 24 & 255, value2 >> 16 & 255, value2 >> 8 & 255, value2 & 255]); +} +function cborUint(v) { + return cborHead(0, v); +} +function cborNint(v) { + return cborHead(1, -1 - v); +} +function cborBytes(b) { + return Buffer.concat([cborHead(2, b.length), b]); +} +function cborText(s) { + const b = Buffer.from(s, "utf8"); + return Buffer.concat([cborHead(3, b.length), b]); +} +function cborMap(entries) { + return Buffer.concat([cborHead(5, entries.length), ...entries.flatMap(([k, v]) => [k, v])]); +} +function encodeCoseEs256PublicKey(publicKey) { + const jwk = publicKey.export({ format: "jwk" }); + const x = Buffer.from(jwk.x, "base64url"); + const y = Buffer.from(jwk.y, "base64url"); + return cborMap([ + [cborUint(1), cborUint(2)], + // kty = EC2 + [cborUint(3), cborNint(-7)], + // alg = ES256 + [cborNint(-1), cborUint(1)], + // crv = P-256 + [cborNint(-2), cborBytes(x)], + [cborNint(-3), cborBytes(y)] + ]); +} +function encodeAttestationObjectNone(authData) { + return cborMap([ + [cborText("fmt"), cborText("none")], + [cborText("attStmt"), cborMap([])], + [cborText("authData"), cborBytes(authData)] + ]); +} +var import_crypto6, kBindingName, kAuthenticatorAAGUID, Credentials; +var init_credentials = __esm({ + "packages/playwright-core/src/server/credentials.ts"() { + "use strict"; + import_crypto6 = __toESM(require("crypto")); + init_webAuthnSource(); + init_progress(); + kBindingName = "__pwWebAuthnBinding"; + kAuthenticatorAAGUID = Buffer.alloc(16); + Credentials = class { + constructor(browserContext) { + this._initScripts = []; + this._installed = false; + this._registry = /* @__PURE__ */ new Map(); + this._browserContext = browserContext; + } + async create(options) { + let privateKey = options.privateKey; + let publicKey = options.publicKey; + if (!privateKey || !publicKey) { + const pair = import_crypto6.default.generateKeyPairSync("ec", { namedCurve: "P-256" }); + privateKey = bufToB64Url(pair.privateKey.export({ format: "der", type: "pkcs8" })); + publicKey = bufToB64Url(pair.publicKey.export({ format: "der", type: "spki" })); + } + const record = { + id: options.id || randomBase64Url(16), + rpId: options.rpId, + userHandle: options.userHandle || randomBase64Url(16), + privateKey, + publicKey, + signCount: 0, + isResident: true + }; + this._registry.set(record.id, record); + return toPublic(record); + } + async get(filter) { + return [...this._registry.values()].filter((c) => { + if (filter?.rpId && c.rpId !== filter.rpId) + return false; + if (filter?.id && c.id !== filter.id) + return false; + return true; + }).map(toPublic); + } + async delete(id) { + this._registry.delete(id); + } + async dispose(progress2) { + await progress2.race(Promise.all(this._initScripts.map((s) => s.dispose()))); + this._initScripts = []; + this._installed = false; + this._registry.clear(); + } + async install(progress2) { + if (this._installed) + return; + this._installed = true; + await this._browserContext.exposeBinding(progress2, kBindingName, async (_source, payload) => { + try { + if (payload?.type === "create") + return await this._handleCreate(payload); + if (payload?.type === "get") + return this._handleGet(payload); + } catch (e) { + return { ok: false, name: "NotAllowedError", message: e.message }; + } + return { ok: false, name: "NotAllowedError", message: "Unknown WebAuthn request" }; + }); + const script = `(() => { + const module = {}; + ${source2} + module.exports.inject()(globalThis); + })();`; + const initScript = await this._browserContext.addInitScript(nullProgress, script); + this._initScripts.push(initScript); + await progress2.race(this._browserContext.safeNonStallingEvaluateInAllFrames(script, "main", { throwOnJSErrors: false })); + } + async _handleCreate(req) { + const rpId = req.rp?.id || new URL(req.origin).hostname; + const userHandle = req.user.id; + if (req.excludeCredentials?.length) { + for (const desc of req.excludeCredentials) { + if (this._registry.has(desc.id)) + return { ok: false, name: "InvalidStateError", message: "Credential excluded" }; + } + } + const pair = import_crypto6.default.generateKeyPairSync("ec", { namedCurve: "P-256" }); + const privateKey = bufToB64Url(pair.privateKey.export({ format: "der", type: "pkcs8" })); + const publicKey = bufToB64Url(pair.publicKey.export({ format: "der", type: "spki" })); + const credentialId = import_crypto6.default.randomBytes(16); + const credentialIdB64 = bufToB64Url(credentialId); + const record = { + id: credentialIdB64, + rpId, + userHandle, + privateKey, + publicKey, + signCount: 0, + isResident: req.residentKey === "required" || req.residentKey === "preferred" + }; + this._registry.set(credentialIdB64, record); + const clientDataJSON = Buffer.from(JSON.stringify({ + type: "webauthn.create", + challenge: req.challenge, + origin: req.origin, + crossOrigin: false + })); + const rpIdHash = import_crypto6.default.createHash("sha256").update(rpId).digest(); + const flags = 1 | 4 | 64; + const signCountBuf = u32ToBytes(record.signCount); + const cosePublicKey = encodeCoseEs256PublicKey(pair.publicKey); + const credIdLenBuf = Buffer.from([credentialId.length >> 8 & 255, credentialId.length & 255]); + const attestedCredentialData = Buffer.concat([kAuthenticatorAAGUID, credIdLenBuf, credentialId, cosePublicKey]); + const authData = Buffer.concat([rpIdHash, Buffer.from([flags]), signCountBuf, attestedCredentialData]); + const attestationObject = encodeAttestationObjectNone(authData); + return { + ok: true, + id: credentialIdB64, + clientDataJSON: bufToB64Url(clientDataJSON), + attestationObject: bufToB64Url(attestationObject) + }; + } + _handleGet(req) { + const rpId = req.rpId || new URL(req.origin).hostname; + let candidate; + if (req.allowCredentials?.length) { + for (const desc of req.allowCredentials) { + const c = this._registry.get(desc.id); + if (c && c.rpId === rpId) { + candidate = c; + break; + } + } + } else { + for (const c of this._registry.values()) { + if (c.rpId === rpId && c.isResident) { + candidate = c; + break; + } + } + } + if (!candidate) + return { ok: false, name: "NotAllowedError", message: "No matching credential" }; + const clientDataJSON = Buffer.from(JSON.stringify({ + type: "webauthn.get", + challenge: req.challenge, + origin: req.origin, + crossOrigin: false + })); + const rpIdHash = import_crypto6.default.createHash("sha256").update(rpId).digest(); + const flags = 1 | 4; + candidate.signCount += 1; + const signCountBuf = u32ToBytes(candidate.signCount); + const authData = Buffer.concat([rpIdHash, Buffer.from([flags]), signCountBuf]); + const clientDataHash = import_crypto6.default.createHash("sha256").update(clientDataJSON).digest(); + const toSign = Buffer.concat([authData, clientDataHash]); + const privateKey = import_crypto6.default.createPrivateKey({ key: b64UrlToBuf(candidate.privateKey), format: "der", type: "pkcs8" }); + const signature = import_crypto6.default.sign("sha256", toSign, privateKey); + return { + ok: true, + id: candidate.id, + clientDataJSON: bufToB64Url(clientDataJSON), + authenticatorData: bufToB64Url(authData), + signature: bufToB64Url(signature), + userHandle: candidate.userHandle || null + }; + } + }; + } +}); + +// packages/playwright-core/src/server/instrumentation.ts +function createRootSdkObject() { + const fakeParent = { attribution: {}, instrumentation: createInstrumentation() }; + const root = new SdkObject(fakeParent); + root.guid = ""; + return root; +} +function createInstrumentation() { + const listeners = /* @__PURE__ */ new Map(); + const lastListeners = /* @__PURE__ */ new Map(); + return new Proxy({}, { + get: (obj, prop) => { + if (typeof prop !== "string") + return obj[prop]; + if (prop === "addListener") { + return (listener, context2, options) => { + if (options?.order === "last") + lastListeners.set(listener, context2); + else + listeners.set(listener, context2); + }; + } + if (prop === "removeListener") { + return (listener) => { + listeners.delete(listener); + lastListeners.delete(listener); + }; + } + if (!prop.startsWith("on")) + return obj[prop]; + return async (sdkObject, ...params2) => { + for (const [listener, context2] of listeners) { + if (!context2 || sdkObject.attribution.context === context2) + await listener[prop]?.(sdkObject, ...params2); + } + for (const [listener, context2] of lastListeners) { + if (!context2 || sdkObject.attribution.context === context2) + await listener[prop]?.(sdkObject, ...params2); + } + }; + } + }); +} +var import_events3, SdkObject; +var init_instrumentation = __esm({ + "packages/playwright-core/src/server/instrumentation.ts"() { + "use strict"; + import_events3 = require("events"); + init_crypto(); + init_debugLogger(); + SdkObject = class extends import_events3.EventEmitter { + constructor(parent, guidPrefix, guid) { + super(); + this.guid = guid || `${guidPrefix || ""}@${createGuid()}`; + this.setMaxListeners(0); + this.attribution = { ...parent.attribution }; + this.instrumentation = parent.instrumentation; + } + apiLog(message) { + if (!this.attribution.playwright.options.isInternalPlaywright) + debugLogger.log("api", message); + } + closeReason() { + return this.attribution.worker?._closeReason || this.attribution.page?._closeReason || this.attribution.context?._closeReason || this.attribution.browser?._closeReason; + } + }; + } +}); + +// packages/playwright-core/src/server/debugger.ts +function matchesLocation(metadata, location2) { + return !!metadata.location?.file.includes(location2.file) && (location2.line === void 0 || metadata.location.line === location2.line) && (location2.column === void 0 || metadata.location.column === location2.column); +} +var symbol, Debugger; +var init_debugger = __esm({ + "packages/playwright-core/src/server/debugger.ts"() { + "use strict"; + init_protocolMetainfo(); + init_time(); + init_instrumentation(); + init_browserContext(); + symbol = Symbol("Debugger"); + Debugger = class _Debugger extends SdkObject { + constructor(context2) { + super(context2, "debugger"); + this._pauseAt = {}; + this._enabled = false; + this._pauseBeforeWaitingActions = false; + this._muted = false; + this._context = context2; + this._context[symbol] = this; + context2.instrumentation.addListener(this, context2, { order: "last" }); + this._context.once(BrowserContext.Events.Close, () => { + this._context.instrumentation.removeListener(this); + }); + } + static { + this.Events = { + PausedStateChanged: "pausedstatechanged" + }; + } + requestPause(progress2) { + if (this.isPaused()) + throw new Error("Debugger is already paused"); + this.setPauseBeforeWaitingActions(); + this.setPauseAt({ next: true }); + } + doResume(progress2) { + if (!this.isPaused()) + throw new Error("Debugger is not paused"); + this.resume(); + } + next(progress2) { + if (!this.isPaused()) + throw new Error("Debugger is not paused"); + this.setPauseBeforeWaitingActions(); + this.setPauseAt({ next: true }); + this.resume(); + } + runTo(progress2, location2) { + if (!this.isPaused()) + throw new Error("Debugger is not paused"); + this.setPauseBeforeWaitingActions(); + this.setPauseAt({ location: location2 }); + this.resume(); + } + async setMuted(muted) { + this._muted = muted; + } + async onBeforeCall(sdkObject, metadata) { + if (this._muted || metadata.internal) + return; + const metainfo = getMetainfo(metadata); + const pauseOnPauseCall = this._enabled && metadata.type === "BrowserContext" && metadata.method === "pause"; + const pauseBeforeAction = !!this._pauseAt.next && !!metainfo?.pause && (this._pauseBeforeWaitingActions || !metainfo?.isAutoWaiting); + const pauseOnLocation = !!this._pauseAt.location && matchesLocation(metadata, this._pauseAt.location); + if (pauseOnPauseCall || pauseBeforeAction || pauseOnLocation) + await this._pause(sdkObject, metadata); + } + async onBeforeInputAction(sdkObject, metadata) { + if (this._muted || metadata.internal) + return; + const metainfo = getMetainfo(metadata); + const pauseBeforeInput = !!this._pauseAt.next && !!metainfo?.pause && !!metainfo?.isAutoWaiting && !this._pauseBeforeWaitingActions; + if (pauseBeforeInput) + await this._pause(sdkObject, metadata); + } + async _pause(sdkObject, metadata) { + if (this._muted || metadata.internal) + return; + if (this._pausedCall) + return; + this._pauseAt = {}; + metadata.pauseStartTime = monotonicTime(); + const result2 = new Promise((resolve) => { + this._pausedCall = { metadata, sdkObject, resolve }; + }); + this.emit(_Debugger.Events.PausedStateChanged); + return result2; + } + resume() { + if (!this._pausedCall) + return; + this._pausedCall.metadata.pauseEndTime = monotonicTime(); + this._pausedCall.resolve(); + this._pausedCall = void 0; + this.emit(_Debugger.Events.PausedStateChanged); + } + setPauseBeforeWaitingActions() { + this._pauseBeforeWaitingActions = true; + } + setPauseAt(at = {}) { + this._enabled = true; + this._pauseAt = at; + } + isPaused(metadata) { + if (metadata) + return this._pausedCall?.metadata === metadata; + return !!this._pausedCall; + } + pausedDetails() { + return this._pausedCall; + } + }; + } +}); + +// packages/playwright-core/src/server/dialog.ts +var Dialog, DialogManager; +var init_dialog = __esm({ + "packages/playwright-core/src/server/dialog.ts"() { + "use strict"; + init_assert(); + init_instrumentation(); + Dialog = class extends SdkObject { + constructor(page, type3, message, onHandle, defaultValue) { + super(page, "dialog"); + this._handled = false; + this._page = page; + this._type = type3; + this._message = message; + this._onHandle = onHandle; + this._defaultValue = defaultValue || ""; + } + async accept(progress2, promptText) { + await progress2.race(this._accept(promptText)); + } + async dismiss(progress2) { + await progress2.race(this._dismiss()); + } + page() { + return this._page; + } + type() { + return this._type; + } + message() { + return this._message; + } + defaultValue() { + return this._defaultValue; + } + async _accept(promptText) { + assert(!this._handled, "Cannot accept dialog which is already handled!"); + this._handled = true; + this._page.browserContext.dialogManager._dialogWillClose(this); + await this._onHandle(true, promptText); + } + async _dismiss() { + assert(!this._handled, "Cannot dismiss dialog which is already handled!"); + this._handled = true; + this._page.browserContext.dialogManager._dialogWillClose(this); + await this._onHandle(false); + } + async _close() { + if (this._type === "beforeunload") + await this._accept(); + else + await this._dismiss(); + } + }; + DialogManager = class { + constructor(instrumentation) { + this._dialogHandlers = /* @__PURE__ */ new Set(); + this._openedDialogs = /* @__PURE__ */ new Set(); + this._instrumentation = instrumentation; + } + dialogDidOpen(dialog) { + for (const frame of dialog.page().frameManager.frames()) + frame.invalidateNonStallingEvaluations("JavaScript dialog interrupted evaluation"); + this._openedDialogs.add(dialog); + this._instrumentation.onDialog(dialog); + let hasHandlers = false; + for (const handler of this._dialogHandlers) { + if (handler(dialog)) + hasHandlers = true; + } + if (!hasHandlers) + dialog._close().then(() => { + }); + } + _dialogWillClose(dialog) { + this._openedDialogs.delete(dialog); + } + addDialogHandler(handler) { + this._dialogHandlers.add(handler); + } + removeDialogHandler(handler) { + this._dialogHandlers.delete(handler); + if (!this._dialogHandlers.size) { + for (const dialog of this._openedDialogs) + dialog._close().catch(() => { + }); + } + } + hasOpenDialogsForPage(page) { + return [...this._openedDialogs].some((dialog) => dialog.page() === page); + } + async closeBeforeUnloadDialogs() { + await Promise.all([...this._openedDialogs].map(async (dialog) => { + if (dialog.type() === "beforeunload") + await dialog._dismiss(); + })); + } + }; + } +}); + +// packages/playwright-core/src/server/network.ts +function filterCookies(cookies, urls) { + const parsedURLs = urls.map((s) => new URL(s)); + return cookies.filter((c) => { + if (!parsedURLs.length) + return true; + for (const parsedURL of parsedURLs) { + let domain = c.domain; + if (!domain.startsWith(".")) + domain = "." + domain; + if (!("." + parsedURL.hostname).endsWith(domain)) + continue; + if (!parsedURL.pathname.startsWith(c.path)) + continue; + if (parsedURL.protocol !== "https:" && !isLocalHostname(parsedURL.hostname) && c.secure) + continue; + return true; + } + return false; + }); +} +function isLocalHostname(hostname) { + return hostname === "localhost" || hostname.endsWith(".localhost"); +} +function isForbiddenHeader(name, value2) { + const lowerName = name.toLowerCase(); + if (FORBIDDEN_HEADER_NAMES.has(lowerName)) + return true; + if (lowerName.startsWith("proxy-")) + return true; + if (lowerName.startsWith("sec-")) + return true; + if (lowerName === "x-http-method" || lowerName === "x-http-method-override" || lowerName === "x-method-override") { + if (value2 && FORBIDDEN_METHODS.has(value2.toUpperCase())) + return true; + } + return false; +} +function applyHeadersOverrides(original, overrides) { + const forbiddenHeaders = original.filter((header) => isForbiddenHeader(header.name, header.value)); + const allowedHeaders = overrides.filter((header) => !isForbiddenHeader(header.name, header.value)); + return mergeHeaders([allowedHeaders, forbiddenHeaders]); +} +function rewriteCookies(cookies) { + return cookies.map((c) => { + assert(c.url || c.domain && c.path, "Cookie should have a url or a domain/path pair"); + assert(!(c.url && c.domain), "Cookie should have either url or domain"); + assert(!(c.url && c.path), "Cookie should have either url or path"); + assert(!(c.expires && c.expires < 0 && c.expires !== -1), "Cookie should have a valid expires, only -1 or a positive number for the unix timestamp in seconds is allowed"); + assert(!(c.expires && c.expires > 0 && c.expires > kMaxCookieExpiresDateInSeconds), "Cookie should have a valid expires, only -1 or a positive number for the unix timestamp in seconds is allowed"); + const copy = { ...c }; + if (copy.url) { + assert(copy.url !== "about:blank", `Blank page can not have cookie "${c.name}"`); + assert(!copy.url.startsWith("data:"), `Data URL page can not have cookie "${c.name}"`); + const url2 = new URL(copy.url); + copy.domain = url2.hostname; + copy.path = url2.pathname.substring(0, url2.pathname.lastIndexOf("/") + 1); + copy.secure = url2.protocol === "https:"; + } + return copy; + }); +} +function parseURL2(url2) { + try { + return new URL(url2); + } catch (e) { + return null; + } +} +function stripFragmentFromUrl(url2) { + if (!url2.includes("#")) + return url2; + return url2.substring(0, url2.indexOf("#")); +} +function statusText(status) { + return STATUS_TEXTS[String(status)] || "Unknown"; +} +function singleHeader(name, value2) { + return [{ name, value: value2 }]; +} +function mergeHeaders(headers) { + const lowerCaseToValue = /* @__PURE__ */ new Map(); + const lowerCaseToOriginalCase = /* @__PURE__ */ new Map(); + for (const h of headers) { + if (!h) + continue; + for (const { name, value: value2 } of h) { + const lower = name.toLowerCase(); + lowerCaseToOriginalCase.set(lower, name); + lowerCaseToValue.set(lower, value2); + } + } + const result2 = []; + for (const [lower, value2] of lowerCaseToValue) + result2.push({ name: lowerCaseToOriginalCase.get(lower), value: value2 }); + return result2; +} +function headersSize(headers) { + let result2 = 0; + for (const header of headers) + result2 += header.name.length + header.value.length + 4; + return result2; +} +function requestHeadersSize(headers, url2, method) { + let result2 = 4; + result2 += method.length; + result2 += new URL(url2).pathname.length; + result2 += 8; + result2 += headersSize(headers); + return result2; +} +function responseHeadersSize(headers, statusText2) { + let result2 = 4; + result2 += 8; + result2 += 3; + result2 += statusText2.length; + result2 += headersSize(headers); + result2 += 2; + return result2; +} +var FORBIDDEN_HEADER_NAMES, FORBIDDEN_METHODS, kMaxCookieExpiresDateInSeconds, Request, Route, Response2, WebSocket, STATUS_TEXTS; +var init_network2 = __esm({ + "packages/playwright-core/src/server/network.ts"() { + "use strict"; + init_manualPromise(); + init_assert(); + init_browserContext(); + init_fetch(); + init_instrumentation(); + FORBIDDEN_HEADER_NAMES = /* @__PURE__ */ new Set([ + "accept-charset", + "accept-encoding", + "access-control-request-headers", + "access-control-request-method", + "connection", + "content-length", + "cookie", + "date", + "dnt", + "expect", + "host", + "keep-alive", + "origin", + "referer", + "set-cookie", + "te", + "trailer", + "transfer-encoding", + "upgrade", + "via" + ]); + FORBIDDEN_METHODS = /* @__PURE__ */ new Set(["CONNECT", "TRACE", "TRACK"]); + kMaxCookieExpiresDateInSeconds = 253402300799; + Request = class _Request extends SdkObject { + constructor(context2, frame, serviceWorker, redirectedFrom, documentId, url2, resourceType, method, postData, headers, wallTimeMs) { + super(frame || context2, "request"); + this._response = null; + this._redirectedTo = null; + this._failureText = null; + this._frame = null; + this._serviceWorker = null; + this._rawRequestHeadersPromise = new ManualPromise(); + this._waitForResponsePromise = new ManualPromise(); + this._responseEndTiming = -1; + assert(!url2.startsWith("data:"), "Data urls should not fire requests"); + this._context = context2; + this._frame = frame; + this._serviceWorker = serviceWorker; + this._redirectedFrom = redirectedFrom; + if (redirectedFrom) + redirectedFrom._redirectedTo = this; + this._documentId = documentId; + this._url = stripFragmentFromUrl(url2); + this._resourceType = resourceType; + this._method = method; + this._postData = postData; + this._headers = headers; + this._wallTimeMs = wallTimeMs; + this._isFavicon = url2.endsWith("/favicon.ico") || !!redirectedFrom?._isFavicon; + } + static { + this.Events = { + Response: "response" + }; + } + wallTimeMs() { + return this._wallTimeMs; + } + async raceWithPageClosure(progress2, promise) { + const scope = this._serviceWorker?.openScope ?? this._frame?._page.openScope; + if (scope) + return await progress2.race(scope.race(promise)); + return await progress2.race(promise); + } + async rawRequestHeaders(progress2) { + return await this.raceWithPageClosure(progress2, this.internalRawRequestHeaders()); + } + async response(progress2) { + return await this.raceWithPageClosure(progress2, this._waitForResponse()); + } + _setFailureText(failureText) { + this._failureText = failureText; + this._waitForResponsePromise.resolve(null); + } + _applyOverrides(overrides) { + this._overrides = { ...this._overrides, ...overrides }; + return this._overrides; + } + overrides() { + return this._overrides; + } + url() { + return this._overrides?.url || this._url; + } + resourceType() { + return this._resourceType; + } + method() { + return this._overrides?.method || this._method; + } + postDataBuffer() { + return this._overrides?.postData || this._postData; + } + headers() { + return this._overrides?.headers || this._headers; + } + headerValue(name) { + const lowerCaseName = name.toLowerCase(); + return this.headers().find((h) => h.name.toLowerCase() === lowerCaseName)?.value; + } + // "null" means no raw headers available - we'll use provisional headers as raw headers. + setRawRequestHeaders(headers) { + if (!this._rawRequestHeadersPromise.isDone()) + this._rawRequestHeadersPromise.resolve(headers || this._headers); + } + async internalRawRequestHeaders() { + return this._overrides?.headers || this._rawRequestHeadersPromise; + } + _waitForResponse() { + return this._waitForResponsePromise; + } + _existingResponse() { + return this._response; + } + _setResponse(response2) { + this._response = response2; + this._waitForResponsePromise.resolve(response2); + this.emit(_Request.Events.Response, response2); + } + _finalRequest() { + return this._redirectedTo ? this._redirectedTo._finalRequest() : this; + } + frame() { + return this._frame; + } + serviceWorker() { + return this._serviceWorker; + } + isNavigationRequest() { + return !!this._documentId; + } + redirectedFrom() { + return this._redirectedFrom; + } + failure() { + if (this._failureText === null) + return null; + return { + errorText: this._failureText + }; + } + // TODO(bidi): remove once post body is available. + _setBodySize(size) { + this._bodySize = size; + } + bodySize() { + return this._bodySize || this.postDataBuffer()?.length || 0; + } + async _requestHeadersSize() { + return requestHeadersSize(await this.internalRawRequestHeaders(), this.url(), this.method()); + } + }; + Route = class extends SdkObject { + constructor(request2, delegate) { + super(request2._frame || request2._context, "route"); + this._handled = false; + this._futureHandlers = []; + this._request = request2; + this._delegate = delegate; + this._request._context.addRouteInFlight(this); + } + handle(handlers) { + this._futureHandlers = [...handlers]; + this.continue({ isFallback: true }).catch(() => { + }); + } + async removeHandler(handler) { + this._futureHandlers = this._futureHandlers.filter((h) => h !== handler); + if (handler === this._currentHandler) { + await this.continue({ isFallback: true }).catch(() => { + }); + return; + } + } + request() { + return this._request; + } + async abort(errorCode = "failed") { + this._startHandling(); + this._request._context.emit(BrowserContext.Events.RequestAborted, this._request); + await this._delegate.abort(errorCode); + this._endHandling(); + } + redirectNavigationRequest(url2) { + this._startHandling(); + assert(this._request.isNavigationRequest()); + this._request.frame().redirectNavigation(url2, this._request._documentId, this._request.headerValue("referer")); + this._endHandling(); + } + async fulfill(overrides) { + this._startHandling(); + let body = overrides.body; + let isBase64 = overrides.isBase64 || false; + if (body === void 0) { + if (overrides.fetchResponseUid) { + const buffer = this._request._context.fetchRequest.fetchResponses.get(overrides.fetchResponseUid) || APIRequestContext.findResponseBody(overrides.fetchResponseUid); + assert(buffer, "Fetch response has been disposed"); + body = buffer.toString("base64"); + isBase64 = true; + } else { + body = ""; + isBase64 = false; + } + } else if (!overrides.status || overrides.status < 200 || overrides.status >= 400) { + this._request._responseBodyOverride = { body, isBase64 }; + } + const headers = [...overrides.headers || []]; + this._maybeAddCorsHeaders(headers); + this._request._context.emit(BrowserContext.Events.RequestFulfilled, this._request); + await this._delegate.fulfill({ + status: overrides.status || 200, + headers, + body, + isBase64 + }); + this._endHandling(); + } + // See https://github.com/microsoft/playwright/issues/12929 + _maybeAddCorsHeaders(headers) { + const origin = this._request.headerValue("origin"); + if (!origin) + return; + const requestUrl = new URL(this._request.url()); + if (!requestUrl.protocol.startsWith("http")) + return; + if (requestUrl.origin === origin.trim()) + return; + const corsHeader = headers.find(({ name }) => name === "access-control-allow-origin"); + if (corsHeader) + return; + headers.push({ name: "access-control-allow-origin", value: origin }); + headers.push({ name: "access-control-allow-credentials", value: "true" }); + headers.push({ name: "vary", value: "Origin" }); + } + async continue(overrides) { + if (overrides.url) { + const newUrl = new URL(overrides.url); + const oldUrl = new URL(this._request.url()); + if (oldUrl.protocol !== newUrl.protocol) + throw new Error("New URL must have same protocol as overridden URL"); + } + if (overrides.headers) { + overrides.headers = applyHeadersOverrides(this._request._headers, overrides.headers); + } + overrides = this._request._applyOverrides(overrides); + const nextHandler = this._futureHandlers.shift(); + if (nextHandler) { + this._currentHandler = nextHandler; + nextHandler(this, this._request); + return; + } + if (!overrides.isFallback) + this._request._context.emit(BrowserContext.Events.RequestContinued, this._request); + this._startHandling(); + await this._delegate.continue(overrides); + this._endHandling(); + } + _startHandling() { + assert(!this._handled, "Route is already handled!"); + this._handled = true; + this._currentHandler = void 0; + } + _endHandling() { + this._futureHandlers = []; + this._currentHandler = void 0; + this._request._context.removeRouteInFlight(this); + } + }; + Response2 = class extends SdkObject { + constructor(request2, status, statusText2, headers, timing, getResponseBodyCallback, fromServiceWorker) { + super(request2.frame() || request2._context, "response"); + this._contentPromise = null; + this._finishedPromise = new ManualPromise(); + this._headersMap = /* @__PURE__ */ new Map(); + this._serverAddrPromise = new ManualPromise(); + this._securityDetailsPromise = new ManualPromise(); + this._rawResponseHeadersPromise = new ManualPromise(); + this._httpVersionPromise = new ManualPromise(); + this._encodedBodySizePromise = new ManualPromise(); + this._transferSizePromise = new ManualPromise(); + this._responseHeadersSizePromise = new ManualPromise(); + this._request = request2; + this._timing = timing; + this._status = status; + this._statusText = statusText2; + this._url = request2.url(); + this._headers = headers; + for (const { name, value: value2 } of this._headers) + this._headersMap.set(name.toLowerCase(), value2); + this._getResponseBodyCallback = getResponseBodyCallback; + this._request._setResponse(this); + this._fromServiceWorker = fromServiceWorker; + } + async body(progress2) { + return await this._request.raceWithPageClosure(progress2, this.internalBody()); + } + async securityDetails(progress2) { + return await this._request.raceWithPageClosure(progress2, this.internalSecurityDetails()); + } + async serverAddr(progress2) { + return await this._request.raceWithPageClosure(progress2, this.internalServerAddr()); + } + async rawResponseHeaders(progress2) { + return await this._request.raceWithPageClosure(progress2, this.internalRawResponseHeaders()); + } + async httpVersion(progress2) { + return await this._request.raceWithPageClosure(progress2, this.internalHttpVersion()); + } + async sizes(progress2) { + return await this._request.raceWithPageClosure(progress2, this.internalSizes()); + } + _serverAddrFinished(addr) { + this._serverAddrPromise.resolve(addr); + } + _securityDetailsFinished(securityDetails) { + this._securityDetailsPromise.resolve(securityDetails); + } + _requestFinished(responseEndTiming) { + this._request._responseEndTiming = Math.max(responseEndTiming, this._timing.responseStart); + if (this._timing.requestStart === -1) + this._timing.requestStart = this._request._responseEndTiming; + this._finishedPromise.resolve(); + } + _setHttpVersion(httpVersion) { + this._httpVersionPromise.resolve(httpVersion); + } + url() { + return this._url; + } + status() { + return this._status; + } + statusText() { + return this._statusText; + } + headers() { + return this._headers; + } + headerValue(name) { + return this._headersMap.get(name); + } + // "null" means no raw headers available - we'll use provisional headers as raw headers. + setRawResponseHeaders(headers) { + if (!this._rawResponseHeadersPromise.isDone()) + this._rawResponseHeadersPromise.resolve(headers || this._headers); + } + setTransferSize(size) { + this._transferSizePromise.resolve(size); + } + setEncodedBodySize(size) { + this._encodedBodySizePromise.resolve(size); + } + setResponseHeadersSize(size) { + this._responseHeadersSizePromise.resolve(size); + } + timing() { + return this._timing; + } + async internalSecurityDetails() { + return await this._securityDetailsPromise || null; + } + async internalServerAddr() { + return await this._serverAddrPromise || null; + } + async internalRawResponseHeaders() { + return await this._rawResponseHeadersPromise; + } + internalBody() { + if (!this._contentPromise) { + this._contentPromise = this._finishedPromise.then(async () => { + if (this._status >= 300 && this._status <= 399) + throw new Error("Response body is unavailable for redirect responses"); + if (this._request._responseBodyOverride) { + const { body, isBase64 } = this._request._responseBodyOverride; + return Buffer.from(body, isBase64 ? "base64" : "utf-8"); + } + return this._getResponseBodyCallback(); + }); + } + return this._contentPromise; + } + request() { + return this._request; + } + finished() { + return this._finishedPromise; + } + frame() { + return this._request.frame(); + } + async internalHttpVersion() { + const httpVersion = await this._httpVersionPromise || null; + if (!httpVersion) + return "HTTP/1.1"; + if (httpVersion === "http/1.1") + return "HTTP/1.1"; + if (httpVersion === "h2") + return "HTTP/2.0"; + return httpVersion; + } + fromServiceWorker() { + return this._fromServiceWorker; + } + async responseHeadersSize() { + const availableSize = await this._responseHeadersSizePromise; + if (availableSize !== null) + return availableSize; + return responseHeadersSize(await this._rawResponseHeadersPromise, this.statusText()); + } + async internalSizes() { + const requestHeadersSize2 = await this._request._requestHeadersSize(); + const responseHeadersSize2 = await this.responseHeadersSize(); + let encodedBodySize = await this._encodedBodySizePromise; + if (encodedBodySize === null) { + const headers = await this._rawResponseHeadersPromise; + const contentLength = headers.find((h) => h.name.toLowerCase() === "content-length")?.value; + encodedBodySize = contentLength ? +contentLength : 0; + } + let transferSize = await this._transferSizePromise; + if (transferSize === null) { + transferSize = responseHeadersSize2 + encodedBodySize; + } + return { + requestBodySize: this._request.bodySize(), + requestHeadersSize: requestHeadersSize2, + responseBodySize: encodedBodySize, + responseHeadersSize: responseHeadersSize2, + transferSize + }; + } + }; + WebSocket = class _WebSocket extends SdkObject { + constructor(parent, url2) { + super(parent, "ws"); + this._notified = false; + this._url = stripFragmentFromUrl(url2); + } + static { + this.Events = { + Close: "close", + SocketError: "socketerror", + FrameReceived: "framereceived", + FrameSent: "framesent", + Request: "request", + Response: "response" + }; + } + markAsNotified() { + if (this._notified) + return false; + this._notified = true; + return true; + } + url() { + return this._url; + } + wallTimeMs() { + return this._wallTimeMs; + } + setWallTimeMs(wallTimeMs) { + this._wallTimeMs = wallTimeMs; + } + requestSent(headers) { + this.emit(_WebSocket.Events.Request, { headers }); + } + responseReceived(status, statusText2, headers) { + this.emit(_WebSocket.Events.Response, { status, statusText: statusText2, headers }); + } + frameSent(opcode, data, wallTimeMs) { + this.emit(_WebSocket.Events.FrameSent, { opcode, data, wallTimeMs }); + } + frameReceived(opcode, data, wallTimeMs) { + this.emit(_WebSocket.Events.FrameReceived, { opcode, data, wallTimeMs }); + } + error(errorMessage) { + this.emit(_WebSocket.Events.SocketError, errorMessage); + } + closed() { + this.emit(_WebSocket.Events.Close); + } + }; + STATUS_TEXTS = { + "100": "Continue", + "101": "Switching Protocols", + "102": "Processing", + "103": "Early Hints", + "200": "OK", + "201": "Created", + "202": "Accepted", + "203": "Non-Authoritative Information", + "204": "No Content", + "205": "Reset Content", + "206": "Partial Content", + "207": "Multi-Status", + "208": "Already Reported", + "226": "IM Used", + "300": "Multiple Choices", + "301": "Moved Permanently", + "302": "Found", + "303": "See Other", + "304": "Not Modified", + "305": "Use Proxy", + "306": "Switch Proxy", + "307": "Temporary Redirect", + "308": "Permanent Redirect", + "400": "Bad Request", + "401": "Unauthorized", + "402": "Payment Required", + "403": "Forbidden", + "404": "Not Found", + "405": "Method Not Allowed", + "406": "Not Acceptable", + "407": "Proxy Authentication Required", + "408": "Request Timeout", + "409": "Conflict", + "410": "Gone", + "411": "Length Required", + "412": "Precondition Failed", + "413": "Payload Too Large", + "414": "URI Too Long", + "415": "Unsupported Media Type", + "416": "Range Not Satisfiable", + "417": "Expectation Failed", + "418": "I'm a teapot", + "421": "Misdirected Request", + "422": "Unprocessable Entity", + "423": "Locked", + "424": "Failed Dependency", + "425": "Too Early", + "426": "Upgrade Required", + "428": "Precondition Required", + "429": "Too Many Requests", + "431": "Request Header Fields Too Large", + "451": "Unavailable For Legal Reasons", + "500": "Internal Server Error", + "501": "Not Implemented", + "502": "Bad Gateway", + "503": "Service Unavailable", + "504": "Gateway Timeout", + "505": "HTTP Version Not Supported", + "506": "Variant Also Negotiates", + "507": "Insufficient Storage", + "508": "Loop Detected", + "510": "Not Extended", + "511": "Network Authentication Required" + }; + } +}); + +// packages/playwright-core/src/server/cookieStore.ts +function parseRawCookie(header) { + const pairs = header.split(";").filter((s) => s.trim().length > 0).map((p) => { + let key = ""; + let value3 = ""; + const separatorPos = p.indexOf("="); + if (separatorPos === -1) { + key = p.trim(); + } else { + key = p.slice(0, separatorPos).trim(); + value3 = p.slice(separatorPos + 1).trim(); + } + return [key, value3]; + }); + if (!pairs.length) + return null; + const [name, value2] = pairs[0]; + const cookie = { + name, + value: value2 + }; + for (let i = 1; i < pairs.length; i++) { + const [name2, value3] = pairs[i]; + switch (name2.toLowerCase()) { + case "expires": + const expiresMs = +new Date(value3); + if (isFinite(expiresMs)) { + if (expiresMs <= 0) + cookie.expires = 0; + else + cookie.expires = Math.min(expiresMs / 1e3, kMaxCookieExpiresDateInSeconds); + } + break; + case "max-age": + const maxAgeSec = parseInt(value3, 10); + if (isFinite(maxAgeSec)) { + if (maxAgeSec <= 0) + cookie.expires = 0; + else + cookie.expires = Math.min(Date.now() / 1e3 + maxAgeSec, kMaxCookieExpiresDateInSeconds); + } + break; + case "domain": + cookie.domain = value3.toLocaleLowerCase() || ""; + if (cookie.domain && !cookie.domain.startsWith(".") && cookie.domain.includes(".")) + cookie.domain = "." + cookie.domain; + break; + case "path": + cookie.path = value3 || ""; + break; + case "secure": + cookie.secure = true; + break; + case "httponly": + cookie.httpOnly = true; + break; + case "samesite": + switch (value3.toLowerCase()) { + case "none": + cookie.sameSite = "None"; + break; + case "lax": + cookie.sameSite = "Lax"; + break; + case "strict": + cookie.sameSite = "Strict"; + break; + } + break; + } + } + return cookie; +} +function domainMatches(value2, domain) { + if (value2 === domain) + return true; + if (!domain.startsWith(".")) + return false; + value2 = "." + value2; + return value2.endsWith(domain); +} +function pathMatches(value2, path59) { + if (value2 === path59) + return true; + if (!value2.endsWith("/")) + value2 = value2 + "/"; + if (!path59.endsWith("/")) + path59 = path59 + "/"; + return value2.startsWith(path59); +} +var Cookie, CookieStore; +var init_cookieStore = __esm({ + "packages/playwright-core/src/server/cookieStore.ts"() { + "use strict"; + init_network2(); + Cookie = class { + constructor(data) { + this._raw = data; + } + _name() { + return this._raw.name; + } + // https://datatracker.ietf.org/doc/html/rfc6265#section-5.4 + matches(url2) { + if (this._raw.secure && (url2.protocol !== "https:" && !isLocalHostname(url2.hostname))) + return false; + if (!domainMatches(url2.hostname, this._raw.domain)) + return false; + if (!pathMatches(url2.pathname, this._raw.path)) + return false; + return true; + } + _equals(other) { + return this._raw.name === other._raw.name && this._raw.domain === other._raw.domain && this._raw.path === other._raw.path; + } + _networkCookie() { + return this._raw; + } + _updateExpiresFrom(other) { + this._raw.expires = other._raw.expires; + } + _expired() { + if (this._raw.expires === -1) + return false; + return this._raw.expires * 1e3 < Date.now(); + } + }; + CookieStore = class _CookieStore { + constructor() { + this._nameToCookies = /* @__PURE__ */ new Map(); + } + addCookies(cookies) { + for (const cookie of cookies) + this._addCookie(new Cookie(cookie)); + } + cookies(url2) { + const result2 = []; + for (const cookie of this._cookiesIterator()) { + if (cookie.matches(url2)) + result2.push(cookie._networkCookie()); + } + return result2; + } + allCookies() { + const result2 = []; + for (const cookie of this._cookiesIterator()) + result2.push(cookie._networkCookie()); + return result2; + } + _addCookie(cookie) { + let set = this._nameToCookies.get(cookie._name()); + if (!set) { + set = /* @__PURE__ */ new Set(); + this._nameToCookies.set(cookie._name(), set); + } + for (const other of set) { + if (other._equals(cookie)) + set.delete(other); + } + set.add(cookie); + _CookieStore.pruneExpired(set); + } + *_cookiesIterator() { + for (const [name, cookies] of this._nameToCookies) { + _CookieStore.pruneExpired(cookies); + for (const cookie of cookies) + yield cookie; + if (cookies.size === 0) + this._nameToCookies.delete(name); + } + } + static pruneExpired(cookies) { + for (const cookie of cookies) { + if (cookie._expired()) + cookies.delete(cookie); + } + } + }; + } +}); + +// packages/playwright-core/src/server/formData.ts +function generateUniqueBoundaryString() { + const charCodes = []; + for (let i = 0; i < 16; i++) + charCodes.push(alphaNumericEncodingMap[Math.floor(Math.random() * alphaNumericEncodingMap.length)]); + return "----WebKitFormBoundary" + String.fromCharCode(...charCodes); +} +var mime2, MultipartFormData, alphaNumericEncodingMap; +var init_formData = __esm({ + "packages/playwright-core/src/server/formData.ts"() { + "use strict"; + mime2 = require("./utilsBundle").mime; + MultipartFormData = class { + constructor() { + this._chunks = []; + this._boundary = generateUniqueBoundaryString(); + } + contentTypeHeader() { + return `multipart/form-data; boundary=${this._boundary}`; + } + addField(name, value2) { + this._beginMultiPartHeader(name); + this._finishMultiPartHeader(); + this._chunks.push(Buffer.from(value2)); + this._finishMultiPartField(); + } + addFileField(name, value2) { + this._beginMultiPartHeader(name); + this._chunks.push(Buffer.from(`; filename="${value2.name}"`)); + this._chunks.push(Buffer.from(`\r +content-type: ${value2.mimeType || mime2.getType(value2.name) || "application/octet-stream"}`)); + this._finishMultiPartHeader(); + this._chunks.push(value2.buffer); + this._finishMultiPartField(); + } + finish() { + this._addBoundary(true); + return Buffer.concat(this._chunks); + } + _beginMultiPartHeader(name) { + this._addBoundary(); + this._chunks.push(Buffer.from(`content-disposition: form-data; name="${name}"`)); + } + _finishMultiPartHeader() { + this._chunks.push(Buffer.from(`\r +\r +`)); + } + _finishMultiPartField() { + this._chunks.push(Buffer.from(`\r +`)); + } + _addBoundary(isLastBoundary) { + this._chunks.push(Buffer.from("--" + this._boundary)); + if (isLastBoundary) + this._chunks.push(Buffer.from("--")); + this._chunks.push(Buffer.from("\r\n")); + } + }; + alphaNumericEncodingMap = [ + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 65, + 66 + ]; + } +}); + +// packages/playwright-core/src/server/socksClientCertificatesInterceptor.ts +function loadDummyServerCertsIfNeeded() { + if (dummyServerTlsOptions) + return; + const { cert, key } = generateSelfSignedCertificate(); + dummyServerTlsOptions = { key, cert }; +} +function normalizeOrigin(origin) { + try { + return new URL(origin).origin; + } catch (error) { + return origin; + } +} +function convertClientCertificatesToTLSOptions(clientCertificates) { + if (!clientCertificates || !clientCertificates.length) + return; + const tlsOptions = { + pfx: [], + key: [], + cert: [] + }; + for (const cert of clientCertificates) { + if (cert.cert) + tlsOptions.cert.push(cert.cert); + if (cert.key) + tlsOptions.key.push({ pem: cert.key, passphrase: cert.passphrase }); + if (cert.pfx) + tlsOptions.pfx.push({ buf: cert.pfx, passphrase: cert.passphrase }); + } + return tlsOptions; +} +function getMatchingTLSOptionsForOrigin(clientCertificates, origin) { + const matchingCerts = clientCertificates?.filter( + (c) => normalizeOrigin(c.origin) === origin + ); + return convertClientCertificatesToTLSOptions(matchingCerts); +} +function rewriteToLocalhostIfNeeded(host) { + return host === "local.playwright" ? "localhost" : host; +} +function rewriteOpenSSLErrorIfNeeded(error) { + if (error.message !== "unsupported" && error.code !== "ERR_CRYPTO_UNSUPPORTED_OPERATION") + return error; + return rewriteErrorMessage(error, [ + "Unsupported TLS certificate.", + "Most likely, the security algorithm of the given certificate was deprecated by OpenSSL.", + "For more details, see https://github.com/openssl/openssl/blob/master/README-PROVIDERS.md#the-legacy-provider", + "You could probably modernize the certificate by following the steps at https://github.com/nodejs/node/issues/40672#issuecomment-1243648223" + ].join("\n")); +} +function parseALPNFromClientHello(buffer) { + if (buffer.length < 6) + return null; + if (buffer[0] !== 22) + return null; + let offset = 5; + if (buffer[offset] !== 1) + return null; + offset += 4; + offset += 2; + offset += 32; + if (offset >= buffer.length) + return null; + const sessionIdLength = buffer[offset]; + offset += 1 + sessionIdLength; + if (offset + 2 > buffer.length) + return null; + const cipherSuitesLength = buffer.readUInt16BE(offset); + offset += 2 + cipherSuitesLength; + if (offset >= buffer.length) + return null; + const compressionMethodsLength = buffer[offset]; + offset += 1 + compressionMethodsLength; + if (offset + 2 > buffer.length) + return null; + const extensionsLength = buffer.readUInt16BE(offset); + offset += 2; + const extensionsEnd = offset + extensionsLength; + if (extensionsEnd > buffer.length) + return null; + while (offset + 4 <= extensionsEnd) { + const extensionType = buffer.readUInt16BE(offset); + offset += 2; + const extensionLength = buffer.readUInt16BE(offset); + offset += 2; + if (offset + extensionLength > extensionsEnd) + return null; + if (extensionType === 16) + return parseALPNExtension(buffer.subarray(offset, offset + extensionLength)); + offset += extensionLength; + } + return null; +} +function parseALPNExtension(buffer) { + if (buffer.length < 2) + return null; + const listLength = buffer.readUInt16BE(0); + if (listLength !== buffer.length - 2) + return null; + const protocols = []; + let offset = 2; + while (offset < buffer.length) { + const protocolLength = buffer[offset]; + offset += 1; + if (offset + protocolLength > buffer.length) + break; + const protocol = buffer.subarray(offset, offset + protocolLength).toString("utf8"); + protocols.push(protocol); + offset += protocolLength; + } + return protocols.length > 0 ? protocols : null; +} +var import_events4, import_http23, import_net3, import_stream3, import_tls2, getProxyForUrl2, dummyServerTlsOptions, SocksProxyConnection, ClientCertificatesProxy; +var init_socksClientCertificatesInterceptor = __esm({ + "packages/playwright-core/src/server/socksClientCertificatesInterceptor.ts"() { + "use strict"; + import_events4 = require("events"); + import_http23 = __toESM(require("http2")); + import_net3 = __toESM(require("net")); + import_stream3 = __toESM(require("stream")); + import_tls2 = __toESM(require("tls")); + init_socksProxy(); + init_debugLogger(); + init_happyEyeballs(); + init_stringUtils(); + init_crypto(); + init_stackTrace(); + init_network(); + init_browserContext(); + ({ getProxyForUrl: getProxyForUrl2 } = require("./utilsBundle")); + dummyServerTlsOptions = void 0; + SocksProxyConnection = class { + constructor(socksProxy, uid, host, port) { + this._firstPackageReceived = false; + this._closed = false; + this.socksProxy = socksProxy; + this.uid = uid; + this.host = host; + this.port = port; + this._serverCloseEventListener = () => { + this._browserEncrypted.destroy(); + }; + this._browserEncrypted = new import_stream3.default.Duplex({ + read: () => { + }, + write: (data, encoding, callback) => { + this.socksProxy._socksProxy.sendSocketData({ uid: this.uid, data }); + callback(); + }, + destroy: (error, callback) => { + if (error) + socksProxy._socksProxy.sendSocketError({ uid: this.uid, error: error.message }); + else + socksProxy._socksProxy.sendSocketEnd({ uid: this.uid }); + callback(); + } + }); + } + async connect() { + const proxyAgent = this.socksProxy._getProxyAgent(this.host, this.port); + if (proxyAgent) + this._serverEncrypted = await proxyAgent.connect(new import_events4.EventEmitter(), { host: rewriteToLocalhostIfNeeded(this.host), port: this.port, secureEndpoint: false }); + else + this._serverEncrypted = await createSocket(rewriteToLocalhostIfNeeded(this.host), this.port); + this._serverEncrypted.once("close", this._serverCloseEventListener); + this._serverEncrypted.once("error", (error) => this._browserEncrypted.destroy(error)); + if (this._closed) { + this._serverEncrypted.destroy(); + return; + } + this.socksProxy._socksProxy.socketConnected({ + uid: this.uid, + host: this._serverEncrypted.localAddress, + port: this._serverEncrypted.localPort + }); + } + onClose() { + this._serverEncrypted.destroy(); + this._browserEncrypted.destroy(); + this._closed = true; + } + onData(data) { + if (!this._firstPackageReceived) { + this._firstPackageReceived = true; + const secureContext = data[0] === 22 ? this.socksProxy.secureContextMap.get(normalizeOrigin(`https://${this.host}:${this.port}`)) : void 0; + if (secureContext) + this._establishTlsTunnel(this._browserEncrypted, data, secureContext); + else + this._establishPlaintextTunnel(this._browserEncrypted); + } + this._browserEncrypted.push(data); + } + _establishPlaintextTunnel(browserEncrypted) { + browserEncrypted.pipe(this._serverEncrypted); + this._serverEncrypted.pipe(browserEncrypted); + } + _establishTlsTunnel(browserEncrypted, clientHello, secureContext) { + const browserALPNProtocols = parseALPNFromClientHello(clientHello) || ["http/1.1"]; + debugLogger.log("client-certificates", `Browser->Proxy ${this.host}:${this.port} offers ALPN ${browserALPNProtocols}`); + const rejectUnauthorized = !this.socksProxy.ignoreHTTPSErrors; + const serverDecrypted = import_tls2.default.connect({ + socket: this._serverEncrypted, + host: this.host, + port: this.port, + rejectUnauthorized, + ALPNProtocols: browserALPNProtocols, + servername: !import_net3.default.isIP(this.host) ? this.host : void 0, + secureContext + }, async () => { + const browserDecrypted = await this._upgradeToTLSIfNeeded(browserEncrypted, serverDecrypted.alpnProtocol); + debugLogger.log("client-certificates", `Proxy->Server ${this.host}:${this.port} chooses ALPN ${browserDecrypted.alpnProtocol}`); + browserDecrypted.pipe(serverDecrypted); + serverDecrypted.pipe(browserDecrypted); + const cleanup = (error) => this._serverEncrypted.destroy(error); + browserDecrypted.once("error", cleanup); + serverDecrypted.once("error", cleanup); + browserDecrypted.once("close", cleanup); + serverDecrypted.once("close", cleanup); + if (this._closed) + serverDecrypted.destroy(); + }); + serverDecrypted.once("error", async (error) => { + debugLogger.log("client-certificates", `error when connecting to server: ${error.message.replaceAll("\n", " ")}`); + this._serverEncrypted.removeListener("close", this._serverCloseEventListener); + this._serverEncrypted.destroy(); + const browserDecrypted = await this._upgradeToTLSIfNeeded(this._browserEncrypted, serverDecrypted.alpnProtocol); + const responseBody = escapeHTML("Playwright client-certificate error: " + error.message).replaceAll("\n", " "); + if (browserDecrypted.alpnProtocol === "h2") { + if ("performServerHandshake" in import_http23.default) { + const session2 = import_http23.default.performServerHandshake(browserDecrypted); + session2.on("error", (error2) => { + this._browserEncrypted.destroy(error2); + }); + session2.once("stream", (stream3) => { + const cleanup = (error2) => { + session2.close(); + this._browserEncrypted.destroy(error2); + }; + stream3.once("end", cleanup); + stream3.once("error", cleanup); + stream3.respond({ + [import_http23.default.constants.HTTP2_HEADER_CONTENT_TYPE]: "text/html", + [import_http23.default.constants.HTTP2_HEADER_STATUS]: 503 + }); + stream3.end(responseBody); + }); + } else { + this._browserEncrypted.destroy(error); + } + } else { + browserDecrypted.end([ + "HTTP/1.1 503 Internal Server Error", + "Content-Type: text/html; charset=utf-8", + "Content-Length: " + Buffer.byteLength(responseBody), + "", + responseBody + ].join("\r\n")); + } + }); + } + async _upgradeToTLSIfNeeded(socket, alpnProtocol) { + this._brorwserDecrypted ??= new Promise((resolve, reject) => { + const dummyServer = import_tls2.default.createServer({ + ...dummyServerTlsOptions, + ALPNProtocols: [alpnProtocol || "http/1.1"] + }); + dummyServer.emit("connection", socket); + dummyServer.once("secureConnection", (tlsSocket) => { + dummyServer.close(); + resolve(tlsSocket); + }); + dummyServer.once("error", (error) => { + dummyServer.close(); + reject(error); + }); + }); + return this._brorwserDecrypted; + } + }; + ClientCertificatesProxy = class _ClientCertificatesProxy { + constructor(contextOptions) { + this._connections = /* @__PURE__ */ new Map(); + this.secureContextMap = /* @__PURE__ */ new Map(); + verifyClientCertificates(contextOptions.clientCertificates); + this.ignoreHTTPSErrors = contextOptions.ignoreHTTPSErrors; + this._proxy = contextOptions.proxy; + this._initSecureContexts(contextOptions.clientCertificates); + this._socksProxy = new SocksProxy(); + this._socksProxy.setPattern("*"); + this._socksProxy.addListener(SocksProxy.Events.SocksRequested, async (payload) => { + try { + const connection = new SocksProxyConnection(this, payload.uid, payload.host, payload.port); + await connection.connect(); + this._connections.set(payload.uid, connection); + } catch (error) { + debugLogger.log("client-certificates", `Failed to connect to ${payload.host}:${payload.port}: ${error.message}`); + this._socksProxy.socketFailed({ uid: payload.uid, errorCode: error.code }); + } + }); + this._socksProxy.addListener(SocksProxy.Events.SocksData, (payload) => { + this._connections.get(payload.uid)?.onData(payload.data); + }); + this._socksProxy.addListener(SocksProxy.Events.SocksClosed, (payload) => { + this._connections.get(payload.uid)?.onClose(); + this._connections.delete(payload.uid); + }); + loadDummyServerCertsIfNeeded(); + } + _getProxyAgent(host, port) { + const proxyFromOptions = createProxyAgent(this._proxy); + if (proxyFromOptions) + return proxyFromOptions; + const proxyFromEnv = getProxyForUrl2(`https://${host}:${port}`); + if (proxyFromEnv) + return createProxyAgent({ server: proxyFromEnv }); + } + _initSecureContexts(clientCertificates) { + const origin2certs = /* @__PURE__ */ new Map(); + for (const cert of clientCertificates || []) { + const origin = normalizeOrigin(cert.origin); + const certs = origin2certs.get(origin) || []; + certs.push(cert); + origin2certs.set(origin, certs); + } + for (const [origin, certs] of origin2certs) { + try { + this.secureContextMap.set(origin, import_tls2.default.createSecureContext(convertClientCertificatesToTLSOptions(certs))); + } catch (error) { + error = rewriteOpenSSLErrorIfNeeded(error); + throw rewriteErrorMessage(error, `Failed to load client certificate: ${error.message}`); + } + } + } + static async create(progress2, contextOptions) { + const proxy = new _ClientCertificatesProxy(contextOptions); + try { + await progress2.race(proxy._socksProxy.listen(0, "127.0.0.1")); + return proxy; + } catch (error) { + await progress2.race(proxy.close()); + throw error; + } + } + proxySettings() { + return { server: `socks5://127.0.0.1:${this._socksProxy.port()}` }; + } + async close() { + await this._socksProxy.close(); + } + }; + } +}); + +// packages/playwright-core/src/server/trace/recorder/snapshotterInjected.ts +function frameSnapshotStreamer(snapshotStreamer, removeNoScript) { + if (window[snapshotStreamer]) + return; + const kShadowAttribute = "__playwright_shadow_root_"; + const kValueAttribute = "__playwright_value_"; + const kCheckedAttribute = "__playwright_checked_"; + const kSelectedAttribute = "__playwright_selected_"; + const kScrollTopAttribute = "__playwright_scroll_top_"; + const kScrollLeftAttribute = "__playwright_scroll_left_"; + const kStyleSheetAttribute = "__playwright_style_sheet_"; + const kTargetAttribute = "__playwright_target__"; + const kCustomElementsAttribute = "__playwright_custom_elements__"; + const kCurrentSrcAttribute = "__playwright_current_src__"; + const kBoundingRectAttribute = "__playwright_bounding_rect__"; + const kPopoverOpenAttribute = "__playwright_popover_open_"; + const kDialogOpenAttribute = "__playwright_dialog_open_"; + const kSnapshotFrameId = Symbol("__playwright_snapshot_frameid_"); + const kCachedData = Symbol("__playwright_snapshot_cache_"); + const kEndOfList = Symbol("__playwright_end_of_list_"); + function resetCachedData(obj) { + delete obj[kCachedData]; + } + function ensureCachedData(obj) { + if (!obj[kCachedData]) + obj[kCachedData] = {}; + return obj[kCachedData]; + } + const kObserverConfig = { attributes: true, subtree: true }; + function removeHash2(url2) { + try { + const u = new URL(url2); + u.hash = ""; + return u.toString(); + } catch (e) { + return url2; + } + } + class Streamer { + constructor() { + this._lastSnapshotNumber = 0; + this._staleStyleSheets = /* @__PURE__ */ new Set(); + this._modifiedStyleSheets = /* @__PURE__ */ new Set(); + this._readingStyleSheet = false; + this._targetGeneration = 0; + const invalidateCSSGroupingRule = (rule) => { + if (rule.parentStyleSheet) + this._invalidateStyleSheet(rule.parentStyleSheet); + }; + this._interceptNativeMethod(window.CSSStyleSheet.prototype, "insertRule", (sheet) => this._invalidateStyleSheet(sheet)); + this._interceptNativeMethod(window.CSSStyleSheet.prototype, "deleteRule", (sheet) => this._invalidateStyleSheet(sheet)); + this._interceptNativeMethod(window.CSSStyleSheet.prototype, "addRule", (sheet) => this._invalidateStyleSheet(sheet)); + this._interceptNativeMethod(window.CSSStyleSheet.prototype, "removeRule", (sheet) => this._invalidateStyleSheet(sheet)); + this._interceptNativeGetter(window.CSSStyleSheet.prototype, "rules", (sheet) => this._invalidateStyleSheet(sheet)); + this._interceptNativeGetter(window.CSSStyleSheet.prototype, "cssRules", (sheet) => this._invalidateStyleSheet(sheet)); + this._interceptNativeMethod(window.CSSStyleSheet.prototype, "replaceSync", (sheet) => this._invalidateStyleSheet(sheet)); + this._interceptNativeMethod(window.CSSGroupingRule.prototype, "insertRule", invalidateCSSGroupingRule); + this._interceptNativeMethod(window.CSSGroupingRule.prototype, "deleteRule", invalidateCSSGroupingRule); + this._interceptNativeGetter(window.CSSGroupingRule.prototype, "cssRules", invalidateCSSGroupingRule); + this._interceptNativeSetter(window.StyleSheet.prototype, "disabled", (sheet) => { + if (sheet instanceof CSSStyleSheet) + this._invalidateStyleSheet(sheet); + }); + this._interceptNativeAsyncMethod(window.CSSStyleSheet.prototype, "replace", (sheet) => this._invalidateStyleSheet(sheet)); + this._fakeBase = document.createElement("base"); + this._observer = new MutationObserver((list) => this._handleMutations(list)); + this._ensureObservingCurrentDocument(); + } + _refreshListenersWhenNeeded() { + this._refreshListeners(); + const customEventName = "__playwright_snapshotter_global_listeners_check__"; + let seenEvent = false; + const handleCustomEvent = () => seenEvent = true; + window.addEventListener(customEventName, handleCustomEvent); + const observer = new MutationObserver((entries) => { + const newDocumentElement = entries.some((entry) => Array.from(entry.addedNodes).includes(document.documentElement)); + if (newDocumentElement) { + seenEvent = false; + window.dispatchEvent(new CustomEvent(customEventName)); + if (!seenEvent) { + window.addEventListener(customEventName, handleCustomEvent); + this._refreshListeners(); + } + } + }); + observer.observe(document, { childList: true }); + } + _refreshListeners() { + document.addEventListener("__playwright_mark_target__", (event) => { + const target = event.composedPath()[0]; + if (target?.nodeType !== Node.ELEMENT_NODE) + return; + target.__playwright_target__ = this._targetGeneration; + }); + document.addEventListener("__playwright_reset_targets__", () => { + ++this._targetGeneration; + }); + } + _interceptNativeMethod(obj, method, cb) { + const native = obj[method]; + if (!native) + return; + obj[method] = function(...args) { + const result2 = native.call(this, ...args); + cb(this, result2); + return result2; + }; + } + _interceptNativeAsyncMethod(obj, method, cb) { + const native = obj[method]; + if (!native) + return; + obj[method] = async function(...args) { + const result2 = await native.call(this, ...args); + cb(this, result2); + return result2; + }; + } + _interceptNativeGetter(obj, prop, cb) { + const descriptor = Object.getOwnPropertyDescriptor(obj, prop); + Object.defineProperty(obj, prop, { + ...descriptor, + get: function() { + const result2 = descriptor.get.call(this); + cb(this, result2); + return result2; + } + }); + } + _interceptNativeSetter(obj, prop, cb) { + const descriptor = Object.getOwnPropertyDescriptor(obj, prop); + Object.defineProperty(obj, prop, { + ...descriptor, + set: function(value2) { + const result2 = descriptor.set.call(this, value2); + cb(this, value2); + return result2; + } + }); + } + _handleMutations(list) { + for (const mutation of list) + ensureCachedData(mutation.target).attributesCached = void 0; + } + _ensureObservingCurrentDocument() { + if (this._observedDocument === document) + return; + this._observedDocument = document; + this._observer.disconnect(); + this._observer.observe(document, kObserverConfig); + this._refreshListenersWhenNeeded(); + } + _invalidateStyleSheet(sheet) { + if (this._readingStyleSheet) + return; + this._staleStyleSheets.add(sheet); + if (sheet.href !== null) + this._modifiedStyleSheets.add(sheet); + } + _updateStyleElementStyleSheetTextIfNeeded(sheet, forceText) { + const data = ensureCachedData(sheet); + if (this._staleStyleSheets.has(sheet) || forceText && data.cssText === void 0) { + this._staleStyleSheets.delete(sheet); + try { + data.cssText = this._getSheetText(sheet); + } catch (e) { + data.cssText = ""; + } + } + return data.cssText; + } + // Returns either content, ref, or no override. + _updateLinkStyleSheetTextIfNeeded(sheet, snapshotNumber) { + const data = ensureCachedData(sheet); + if (this._staleStyleSheets.has(sheet)) { + this._staleStyleSheets.delete(sheet); + try { + data.cssText = this._getSheetText(sheet); + data.cssRef = snapshotNumber; + return data.cssText; + } catch (e) { + } + } + return data.cssRef === void 0 ? void 0 : snapshotNumber - data.cssRef; + } + markIframe(iframeElement, frameId) { + iframeElement[kSnapshotFrameId] = frameId; + } + resetHistory() { + this._staleStyleSheets.clear(); + const visitNode = (node) => { + resetCachedData(node); + if (node.nodeType === Node.ELEMENT_NODE) { + const element2 = node; + if (element2.shadowRoot) + visitNode(element2.shadowRoot); + } + for (let child = node.firstChild; child; child = child.nextSibling) + visitNode(child); + }; + visitNode(document.documentElement); + visitNode(this._fakeBase); + } + __sanitizeMetaAttribute(name, value2, httpEquiv) { + if (name === "charset") + return "utf-8"; + if (httpEquiv.toLowerCase() !== "content-type" || name !== "content") + return value2; + const [type3, ...params2] = value2.split(";"); + if (type3 !== "text/html" || params2.length <= 0) + return value2; + const charsetParamIdx = params2.findIndex((param) => param.trim().startsWith("charset=")); + if (charsetParamIdx > -1) + params2[charsetParamIdx] = "charset=utf-8"; + return `${type3}; ${params2.join("; ")}`; + } + _sanitizeUrl(url2) { + if (url2.startsWith("javascript:") || url2.startsWith("vbscript:")) + return ""; + return url2; + } + _sanitizeSrcSet(srcset) { + return srcset.split(",").map((src) => { + src = src.trim(); + const spaceIndex = src.lastIndexOf(" "); + if (spaceIndex === -1) + return this._sanitizeUrl(src); + return this._sanitizeUrl(src.substring(0, spaceIndex).trim()) + src.substring(spaceIndex); + }).join(", "); + } + _resolveUrl(base, url2) { + if (url2 === "") + return ""; + try { + return new URL(url2, base).href; + } catch (e) { + return url2; + } + } + _getSheetBase(sheet) { + let rootSheet = sheet; + while (rootSheet.parentStyleSheet) + rootSheet = rootSheet.parentStyleSheet; + if (rootSheet.ownerNode) + return rootSheet.ownerNode.baseURI; + return document.baseURI; + } + _getSheetText(sheet) { + this._readingStyleSheet = true; + try { + if (sheet.disabled) + return ""; + const rules = []; + for (const rule of sheet.cssRules) + rules.push(rule.cssText); + return rules.join("\n"); + } finally { + this._readingStyleSheet = false; + } + } + captureSnapshot(reset) { + const timestamp = performance.now(); + const snapshotNumber = ++this._lastSnapshotNumber; + if (reset === "history") + this.resetHistory(); + if (reset) + ++this._targetGeneration; + let nodeCounter = 0; + let shadowDomNesting = 0; + let headNesting = 0; + this._ensureObservingCurrentDocument(); + this._handleMutations(this._observer.takeRecords()); + const definedCustomElements = /* @__PURE__ */ new Set(); + const visitNode = (node) => { + const nodeType = node.nodeType; + const nodeName = nodeType === Node.DOCUMENT_FRAGMENT_NODE ? "template" : node.nodeName; + if (nodeType !== Node.ELEMENT_NODE && nodeType !== Node.DOCUMENT_FRAGMENT_NODE && nodeType !== Node.TEXT_NODE) + return; + if (nodeName === "SCRIPT") + return; + if (nodeName === "LINK" && nodeType === Node.ELEMENT_NODE) { + const rel = node.getAttribute("rel")?.toLowerCase(); + if (rel === "preload" || rel === "prefetch") + return; + } + if (removeNoScript && nodeName === "NOSCRIPT") + return; + if (nodeName === "META") { + const httpEquiv = node.httpEquiv.toLowerCase(); + if (httpEquiv === "content-security-policy" || httpEquiv === "refresh" || httpEquiv === "set-cookie") + return; + } + if ((nodeName === "IFRAME" || nodeName === "FRAME") && headNesting) + return; + const data = ensureCachedData(node); + const values = []; + let equals = !!data.cached; + let extraNodes = 0; + const expectValue = (value2) => { + equals = equals && data.cached[values.length] === value2; + values.push(value2); + }; + const checkAndReturn = (n) => { + data.attributesCached = true; + if (equals) + return { equals: true, n: [[snapshotNumber - data.ref[0], data.ref[1]]] }; + nodeCounter += extraNodes; + data.ref = [snapshotNumber, nodeCounter++]; + data.cached = values; + return { equals: false, n }; + }; + if (nodeType === Node.TEXT_NODE) { + const value2 = node.nodeValue || ""; + expectValue(value2); + return checkAndReturn(value2); + } + if (nodeName === "STYLE") { + const sheet = node.sheet; + let cssText; + if (sheet) + cssText = this._updateStyleElementStyleSheetTextIfNeeded(sheet); + cssText = cssText || node.textContent || ""; + expectValue(cssText); + extraNodes++; + return checkAndReturn([nodeName, {}, cssText]); + } + const attrs = {}; + const result3 = [nodeName, attrs]; + const visitChild = (child) => { + const snapshot3 = visitNode(child); + if (snapshot3) { + result3.push(snapshot3.n); + expectValue(child); + equals = equals && snapshot3.equals; + } + }; + const visitChildStyleSheet = (child) => { + const snapshot3 = visitStyleSheet(child); + if (snapshot3) { + result3.push(snapshot3.n); + expectValue(child); + equals = equals && snapshot3.equals; + } + }; + if (nodeType === Node.DOCUMENT_FRAGMENT_NODE) + attrs[kShadowAttribute] = "open"; + if (nodeType === Node.ELEMENT_NODE) { + const element2 = node; + if (element2.localName.includes("-") && window.customElements?.get(element2.localName)) + definedCustomElements.add(element2.localName); + if (nodeName === "INPUT" || nodeName === "TEXTAREA") { + const value2 = element2.value; + expectValue(kValueAttribute); + expectValue(value2); + attrs[kValueAttribute] = value2; + } + if (nodeName === "INPUT" && ["checkbox", "radio"].includes(element2.type)) { + const value2 = element2.checked ? "true" : "false"; + expectValue(kCheckedAttribute); + expectValue(value2); + attrs[kCheckedAttribute] = value2; + } + if (nodeName === "OPTION") { + const value2 = element2.selected ? "true" : "false"; + expectValue(kSelectedAttribute); + expectValue(value2); + attrs[kSelectedAttribute] = value2; + } + if (nodeName === "CANVAS" || nodeName === "IFRAME" || nodeName === "FRAME") { + const boundingRect = element2.getBoundingClientRect(); + const value2 = JSON.stringify({ + left: boundingRect.left, + top: boundingRect.top, + right: boundingRect.right, + bottom: boundingRect.bottom + }); + expectValue(kBoundingRectAttribute); + expectValue(value2); + attrs[kBoundingRectAttribute] = value2; + } + if (element2.popover && element2.matches && element2.matches(":popover-open")) { + const value2 = "true"; + expectValue(kPopoverOpenAttribute); + expectValue(value2); + attrs[kPopoverOpenAttribute] = value2; + } + if (nodeName === "DIALOG" && element2.open) { + const value2 = element2.matches(":modal") ? "modal" : "true"; + expectValue(kDialogOpenAttribute); + expectValue(value2); + attrs[kDialogOpenAttribute] = value2; + } + if (element2.scrollTop) { + expectValue(kScrollTopAttribute); + expectValue(element2.scrollTop); + attrs[kScrollTopAttribute] = "" + element2.scrollTop; + } + if (element2.scrollLeft) { + expectValue(kScrollLeftAttribute); + expectValue(element2.scrollLeft); + attrs[kScrollLeftAttribute] = "" + element2.scrollLeft; + } + if (element2.shadowRoot) { + ++shadowDomNesting; + visitChild(element2.shadowRoot); + --shadowDomNesting; + } + if (element2.__playwright_target__ === this._targetGeneration) { + expectValue(kTargetAttribute); + attrs[kTargetAttribute] = ""; + } + } + if (nodeName === "HEAD") { + ++headNesting; + this._fakeBase.setAttribute("href", document.baseURI); + visitChild(this._fakeBase); + } + for (let child = node.firstChild; child; child = child.nextSibling) + visitChild(child); + if (nodeName === "HEAD") + --headNesting; + expectValue(kEndOfList); + let documentOrShadowRoot = null; + if (node.ownerDocument.documentElement === node) + documentOrShadowRoot = node.ownerDocument; + else if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) + documentOrShadowRoot = node; + if (documentOrShadowRoot) { + for (const sheet of documentOrShadowRoot.adoptedStyleSheets || []) + visitChildStyleSheet(sheet); + expectValue(kEndOfList); + } + if (nodeName === "IFRAME" || nodeName === "FRAME") { + const element2 = node; + const frameId = element2[kSnapshotFrameId]; + const name = "src"; + const value2 = frameId ? `/snapshot/${frameId}` : ""; + expectValue(name); + expectValue(value2); + attrs[name] = value2; + } + if (nodeName === "BODY" && definedCustomElements.size) { + const value2 = [...definedCustomElements].join(","); + expectValue(kCustomElementsAttribute); + expectValue(value2); + attrs[kCustomElementsAttribute] = value2; + } + if (nodeName === "IMG" || nodeName === "PICTURE") { + const value2 = nodeName === "PICTURE" ? "" : this._sanitizeUrl(node.currentSrc); + expectValue(kCurrentSrcAttribute); + expectValue(value2); + attrs[kCurrentSrcAttribute] = value2; + } + if (equals && data.attributesCached && !shadowDomNesting) + return checkAndReturn(result3); + if (nodeType === Node.ELEMENT_NODE) { + const element2 = node; + for (let i = 0; i < element2.attributes.length; i++) { + const name = element2.attributes[i].name; + if (nodeName === "LINK" && name === "integrity") + continue; + if (nodeName === "IFRAME" && (name === "src" || name === "srcdoc" || name === "sandbox")) + continue; + if (nodeName === "FRAME" && name === "src") + continue; + if (nodeName === "DIALOG" && name === "open") + continue; + let value2 = element2.attributes[i].value; + if (nodeName === "META") + value2 = this.__sanitizeMetaAttribute(name, value2, node.httpEquiv); + else if (name === "src" && nodeName === "IMG") + value2 = this._sanitizeUrl(value2); + else if (name === "srcset" && nodeName === "IMG") + value2 = this._sanitizeSrcSet(value2); + else if (name === "srcset" && nodeName === "SOURCE") + value2 = this._sanitizeSrcSet(value2); + else if (name === "href" && nodeName === "LINK") + value2 = this._sanitizeUrl(value2); + else if (name.startsWith("on")) + value2 = ""; + expectValue(name); + expectValue(value2); + attrs[name] = value2; + } + expectValue(kEndOfList); + } + if (result3.length === 2 && !Object.keys(attrs).length) + result3.pop(); + return checkAndReturn(result3); + }; + const visitStyleSheet = (sheet) => { + const data = ensureCachedData(sheet); + const oldCSSText = data.cssText; + const cssText = this._updateStyleElementStyleSheetTextIfNeeded( + sheet, + true + /* forceText */ + ); + if (cssText === oldCSSText) + return { equals: true, n: [[snapshotNumber - data.ref[0], data.ref[1]]] }; + data.ref = [snapshotNumber, nodeCounter++]; + return { + equals: false, + n: ["template", { + [kStyleSheetAttribute]: cssText + }] + }; + }; + let html; + if (document.documentElement) { + const { n } = visitNode(document.documentElement); + html = n; + } else { + html = ["html"]; + } + const result2 = { + html, + doctype: document.doctype ? document.doctype.name : void 0, + resourceOverrides: [], + viewport: { + width: window.innerWidth, + height: window.innerHeight + }, + url: location.href, + wallTime: Date.now(), + collectionTime: 0 + }; + for (const sheet of this._modifiedStyleSheets) { + if (sheet.href === null) + continue; + const content = this._updateLinkStyleSheetTextIfNeeded(sheet, snapshotNumber); + if (content === void 0) { + continue; + } + const base = this._getSheetBase(sheet); + const url2 = removeHash2(this._resolveUrl(base, sheet.href)); + result2.resourceOverrides.push({ url: url2, content, contentType: "text/css" }); + } + result2.collectionTime = performance.now() - timestamp; + return result2; + } + } + window[snapshotStreamer] = new Streamer(); +} +var init_snapshotterInjected = __esm({ + "packages/playwright-core/src/server/trace/recorder/snapshotterInjected.ts"() { + "use strict"; + } +}); + +// packages/playwright-core/src/server/trace/recorder/snapshotter.ts +var mime3, Snapshotter, kNeedsResetSymbol; +var init_snapshotter = __esm({ + "packages/playwright-core/src/server/trace/recorder/snapshotter.ts"() { + "use strict"; + init_time(); + init_crypto(); + init_debugLogger(); + init_eventsHelper(); + init_snapshotterInjected(); + init_browserContext(); + init_progress(); + mime3 = require("./utilsBundle").mime; + Snapshotter = class { + constructor(context2, delegate) { + this._eventListeners = []; + this._started = false; + this._context = context2; + this._delegate = delegate; + const guid = createGuid(); + this._snapshotStreamer = "__playwright_snapshot_streamer_" + guid; + } + started() { + return this._started; + } + async start(progress2) { + this._started = true; + if (!this._initScript) + await this._initialize(progress2); + await progress2.race(this.reset()); + } + async reset() { + if (this._started) + await this._context.safeNonStallingEvaluateInAllFrames(`window["${this._snapshotStreamer}"].resetHistory()`, "main"); + } + stop() { + this._started = false; + } + async resetForReuse() { + if (this._initScript) { + eventsHelper.removeEventListeners(this._eventListeners); + await this._initScript.dispose(); + this._initScript = void 0; + } + } + async _initialize(progress2) { + for (const page of this._context.pages()) + this._onPage(page); + this._eventListeners = [ + eventsHelper.addEventListener(this._context, BrowserContext.Events.Page, this._onPage.bind(this)), + eventsHelper.addEventListener(this._context, BrowserContext.Events.FrameAttached, (frame) => this._annotateFrameHierarchy(frame)) + ]; + const { javaScriptEnabled } = this._context._options; + const initScriptSource = `(${frameSnapshotStreamer})("${this._snapshotStreamer}", ${javaScriptEnabled || javaScriptEnabled === void 0})`; + this._initScript = await this._context.addInitScript(progress2, initScriptSource); + await progress2.race(this._context.safeNonStallingEvaluateInAllFrames(initScriptSource, "main")); + } + dispose() { + eventsHelper.removeEventListeners(this._eventListeners); + } + async _captureFrameSnapshot(frame, resetTargets) { + const needsHistoryReset = !!frame[kNeedsResetSymbol]; + frame[kNeedsResetSymbol] = false; + const reset = needsHistoryReset ? "history" : resetTargets ? "targets" : void 0; + const expression2 = `window["${this._snapshotStreamer}"].captureSnapshot(${JSON.stringify(reset)})`; + try { + return await frame.nonStallingRawEvaluateInExistingMainContext(expression2); + } catch (e) { + frame[kNeedsResetSymbol] = true; + debugLogger.log("error", e); + } + } + async captureSnapshot(page, callId, snapshotName, resetTargets) { + const snapshots = page.frames().map(async (frame) => { + const data = await this._captureFrameSnapshot(frame, resetTargets); + if (!data || !this._started) + return; + const snapshot3 = { + callId, + snapshotName, + pageId: page.guid, + frameId: frame.guid, + frameUrl: data.url, + doctype: data.doctype, + html: data.html, + viewport: data.viewport, + timestamp: monotonicTime(), + wallTime: data.wallTime, + collectionTime: data.collectionTime, + resourceOverrides: [], + isMainFrame: page.mainFrame() === frame + }; + for (const { url: url2, content, contentType } of data.resourceOverrides) { + if (typeof content === "string") { + const buffer = Buffer.from(content); + const sha1 = calculateSha1(buffer) + "." + (mime3.getExtension(contentType) || "dat"); + this._delegate.onSnapshotterBlob({ sha1, buffer }); + snapshot3.resourceOverrides.push({ url: url2, sha1 }); + } else { + snapshot3.resourceOverrides.push({ url: url2, ref: content }); + } + } + this._delegate.onFrameSnapshot(snapshot3); + }); + await Promise.all(snapshots); + } + _onPage(page) { + for (const frame of page.frames()) + this._annotateFrameHierarchy(frame); + } + _annotateFrameHierarchy(frame) { + (async () => { + const frameElement = await frame.frameElement(nullProgress); + const parent = frame.parentFrame(); + if (!parent) + return; + const context2 = await parent.mainContext(); + await context2?.evaluate(({ snapshotStreamer, frameElement: frameElement2, frameId }) => { + window[snapshotStreamer].markIframe(frameElement2, frameId); + }, { snapshotStreamer: this._snapshotStreamer, frameElement, frameId: frame.guid }); + frameElement.dispose(); + })().catch(() => { + }); + } + }; + kNeedsResetSymbol = Symbol("kNeedsReset"); + } +}); + +// packages/playwright-core/src/server/artifact.ts +var import_fs12, Artifact; +var init_artifact = __esm({ + "packages/playwright-core/src/server/artifact.ts"() { + "use strict"; + import_fs12 = __toESM(require("fs")); + init_manualPromise(); + init_assert(); + init_errors(); + init_instrumentation(); + Artifact = class extends SdkObject { + constructor(parent, localPath, unaccessibleErrorMessage, cancelCallback) { + super(parent, "artifact"); + this._finishedPromise = new ManualPromise(); + this._saveCallbacks = []; + this._finished = false; + this._deleted = false; + this._localPath = localPath; + this._unaccessibleErrorMessage = unaccessibleErrorMessage; + this._cancelCallback = cancelCallback; + } + async localPathAfterFinished(progress2) { + return await progress2.race(this._localPathAfterFinished()); + } + async failureError(progress2) { + return await progress2.race(this._failureError()); + } + async cancel(progress2) { + return await progress2.race(this._cancel()); + } + async delete(progress2) { + return await progress2.race(this._delete()); + } + localPath() { + return this._localPath; + } + async _localPathAfterFinished() { + if (this._unaccessibleErrorMessage) + throw new Error(this._unaccessibleErrorMessage); + await this._finishedPromise; + if (this._failureErrorValue) + throw this._failureErrorValue; + return this._localPath; + } + saveAs(progress2, saveCallback) { + if (this._unaccessibleErrorMessage) + throw new Error(this._unaccessibleErrorMessage); + if (this._deleted) + throw new Error(`File already deleted. Save before deleting.`); + if (this._failureErrorValue) + throw this._failureErrorValue; + if (this._finished) { + saveCallback(this._localPath).catch(() => { + }); + return; + } + this._saveCallbacks.push(saveCallback); + } + async _failureError() { + if (this._unaccessibleErrorMessage) + return this._unaccessibleErrorMessage; + await this._finishedPromise; + return this._failureErrorValue?.message || null; + } + async _cancel() { + assert(this._cancelCallback !== void 0); + return this._cancelCallback(); + } + async _delete() { + if (this._unaccessibleErrorMessage) + return; + const fileName = await this._localPathAfterFinished(); + if (this._deleted) + return; + this._deleted = true; + if (fileName) + await import_fs12.default.promises.unlink(fileName).catch((e) => { + }); + } + async deleteOnContextClose() { + if (this._deleted) + return; + this._deleted = true; + if (!this._unaccessibleErrorMessage) + await import_fs12.default.promises.unlink(this._localPath).catch((e) => { + }); + await this.reportFinished(new TargetClosedError(this.closeReason())); + } + async reportFinished(error) { + if (this._finished) + return; + this._finished = true; + this._failureErrorValue = error; + if (error) { + for (const callback of this._saveCallbacks) + await callback("", error); + } else { + for (const callback of this._saveCallbacks) + await callback(this._localPath); + } + this._saveCallbacks = []; + this._finishedPromise.resolve(); + } + }; + } +}); + +// packages/playwright-core/src/protocol/validatorPrimitives.ts +function findValidator(type3, method, kind) { + const validator = maybeFindValidator(type3, method, kind); + if (!validator) + throw new ValidationError(`Unknown scheme for ${kind}: ${type3}.${method}`); + return validator; +} +function maybeFindValidator(type3, method, kind) { + const schemeName = type3 + (kind === "Initializer" ? "" : method[0].toUpperCase() + method.substring(1)) + kind; + return scheme[schemeName]; +} +function createMetadataValidator() { + return tOptional(scheme["Metadata"]); +} +function createWaitInfoValidator() { + return scheme["WaitInfo"]; +} +var ValidationError, scheme, tFloat, tInt, tBoolean, tString, tBinary, tAny, tOptional, tArray, tObject, tEnum, tChannel, tType; +var init_validatorPrimitives = __esm({ + "packages/playwright-core/src/protocol/validatorPrimitives.ts"() { + "use strict"; + ValidationError = class extends Error { + }; + scheme = {}; + tFloat = (arg, path59, context2) => { + if (arg instanceof Number) + return arg.valueOf(); + if (typeof arg === "number") + return arg; + throw new ValidationError(`${path59}: expected float, got ${typeof arg}`); + }; + tInt = (arg, path59, context2) => { + let value2; + if (arg instanceof Number) + value2 = arg.valueOf(); + else if (typeof arg === "number") + value2 = arg; + else + throw new ValidationError(`${path59}: expected integer, got ${typeof arg}`); + if (!Number.isInteger(value2)) + throw new ValidationError(`${path59}: expected integer, got float ${value2}`); + return value2; + }; + tBoolean = (arg, path59, context2) => { + if (arg instanceof Boolean) + return arg.valueOf(); + if (typeof arg === "boolean") + return arg; + throw new ValidationError(`${path59}: expected boolean, got ${typeof arg}`); + }; + tString = (arg, path59, context2) => { + if (arg instanceof String) + return arg.valueOf(); + if (typeof arg === "string") + return arg; + throw new ValidationError(`${path59}: expected string, got ${typeof arg}`); + }; + tBinary = (arg, path59, context2) => { + if (context2.binary === "fromBase64") { + if (arg instanceof String) + return Buffer.from(arg.valueOf(), "base64"); + if (typeof arg === "string") + return Buffer.from(arg, "base64"); + throw new ValidationError(`${path59}: expected base64-encoded buffer, got ${typeof arg}`); + } + if (context2.binary === "toBase64") { + if (!(arg instanceof Buffer)) + throw new ValidationError(`${path59}: expected Buffer, got ${typeof arg}`); + return arg.toString("base64"); + } + if (context2.binary === "buffer") { + if (!(arg instanceof Buffer) && !(arg instanceof Object)) + throw new ValidationError(`${path59}: expected Buffer, got ${typeof arg}`); + return arg; + } + throw new ValidationError(`Unsupported binary behavior "${context2.binary}"`); + }; + tAny = (arg, path59, context2) => { + return arg; + }; + tOptional = (v) => { + return (arg, path59, context2) => { + if (Object.is(arg, void 0)) + return arg; + return v(arg, path59, context2); + }; + }; + tArray = (v) => { + return (arg, path59, context2) => { + if (!Array.isArray(arg)) + throw new ValidationError(`${path59}: expected array, got ${typeof arg}`); + return arg.map((x, index) => v(x, path59 + "[" + index + "]", context2)); + }; + }; + tObject = (s) => { + return (arg, path59, context2) => { + if (Object.is(arg, null)) + throw new ValidationError(`${path59}: expected object, got null`); + if (typeof arg !== "object") + throw new ValidationError(`${path59}: expected object, got ${typeof arg}`); + const result2 = {}; + for (const [key, v] of Object.entries(s)) { + const value2 = v(arg[key], path59 ? path59 + "." + key : key, context2); + if (!Object.is(value2, void 0)) + result2[key] = value2; + } + if (context2.isUnderTest()) { + for (const [key, value2] of Object.entries(arg)) { + if (key.startsWith("__testHook")) + result2[key] = value2; + } + } + return result2; + }; + }; + tEnum = (e) => { + return (arg, path59, context2) => { + if (!e.includes(arg)) + throw new ValidationError(`${path59}: expected one of (${e.join("|")})`); + return arg; + }; + }; + tChannel = (names) => { + return (arg, path59, context2) => { + return context2.tChannelImpl(names, arg, path59, context2); + }; + }; + tType = (name) => { + return (arg, path59, context2) => { + const v = scheme[name]; + if (!v) + throw new ValidationError(path59 + ': unknown type "' + name + '"'); + return v(arg, path59, context2); + }; + }; + } +}); + +// packages/playwright-core/src/protocol/validator.ts +var init_validator = __esm({ + "packages/playwright-core/src/protocol/validator.ts"() { + "use strict"; + init_validatorPrimitives(); + init_validatorPrimitives(); + scheme.AndroidInitializer = tOptional(tObject({})); + scheme.AndroidDevicesParams = tObject({ + host: tOptional(tString), + port: tOptional(tInt), + omitDriverInstall: tOptional(tBoolean) + }); + scheme.AndroidDevicesResult = tObject({ + devices: tArray(tChannel(["AndroidDevice"])) + }); + scheme.AndroidSocketInitializer = tOptional(tObject({})); + scheme.AndroidSocketDataEvent = tObject({ + data: tBinary + }); + scheme.AndroidSocketCloseEvent = tOptional(tObject({})); + scheme.AndroidSocketWriteParams = tObject({ + data: tBinary + }); + scheme.AndroidSocketWriteResult = tOptional(tObject({})); + scheme.AndroidSocketCloseParams = tOptional(tObject({})); + scheme.AndroidSocketCloseResult = tOptional(tObject({})); + scheme.AndroidDeviceInitializer = tObject({ + model: tString, + serial: tString + }); + scheme.AndroidDeviceCloseEvent = tOptional(tObject({})); + scheme.AndroidDeviceWebViewAddedEvent = tObject({ + webView: tType("AndroidWebView") + }); + scheme.AndroidDeviceWebViewRemovedEvent = tObject({ + socketName: tString + }); + scheme.AndroidDeviceWaitParams = tObject({ + androidSelector: tType("AndroidSelector"), + state: tOptional(tEnum(["gone"])), + timeout: tFloat + }); + scheme.AndroidDeviceWaitResult = tOptional(tObject({})); + scheme.AndroidDeviceFillParams = tObject({ + androidSelector: tType("AndroidSelector"), + text: tString, + timeout: tFloat + }); + scheme.AndroidDeviceFillResult = tOptional(tObject({})); + scheme.AndroidDeviceTapParams = tObject({ + androidSelector: tType("AndroidSelector"), + duration: tOptional(tFloat), + timeout: tFloat + }); + scheme.AndroidDeviceTapResult = tOptional(tObject({})); + scheme.AndroidDeviceDragParams = tObject({ + androidSelector: tType("AndroidSelector"), + dest: tType("Point"), + speed: tOptional(tFloat), + timeout: tFloat + }); + scheme.AndroidDeviceDragResult = tOptional(tObject({})); + scheme.AndroidDeviceFlingParams = tObject({ + androidSelector: tType("AndroidSelector"), + direction: tEnum(["up", "down", "left", "right"]), + speed: tOptional(tFloat), + timeout: tFloat + }); + scheme.AndroidDeviceFlingResult = tOptional(tObject({})); + scheme.AndroidDeviceLongTapParams = tObject({ + androidSelector: tType("AndroidSelector"), + timeout: tFloat + }); + scheme.AndroidDeviceLongTapResult = tOptional(tObject({})); + scheme.AndroidDevicePinchCloseParams = tObject({ + androidSelector: tType("AndroidSelector"), + percent: tFloat, + speed: tOptional(tFloat), + timeout: tFloat + }); + scheme.AndroidDevicePinchCloseResult = tOptional(tObject({})); + scheme.AndroidDevicePinchOpenParams = tObject({ + androidSelector: tType("AndroidSelector"), + percent: tFloat, + speed: tOptional(tFloat), + timeout: tFloat + }); + scheme.AndroidDevicePinchOpenResult = tOptional(tObject({})); + scheme.AndroidDeviceScrollParams = tObject({ + androidSelector: tType("AndroidSelector"), + direction: tEnum(["up", "down", "left", "right"]), + percent: tFloat, + speed: tOptional(tFloat), + timeout: tFloat + }); + scheme.AndroidDeviceScrollResult = tOptional(tObject({})); + scheme.AndroidDeviceSwipeParams = tObject({ + androidSelector: tType("AndroidSelector"), + direction: tEnum(["up", "down", "left", "right"]), + percent: tFloat, + speed: tOptional(tFloat), + timeout: tFloat + }); + scheme.AndroidDeviceSwipeResult = tOptional(tObject({})); + scheme.AndroidDeviceInfoParams = tObject({ + androidSelector: tType("AndroidSelector") + }); + scheme.AndroidDeviceInfoResult = tObject({ + info: tType("AndroidElementInfo") + }); + scheme.AndroidDeviceScreenshotParams = tOptional(tObject({})); + scheme.AndroidDeviceScreenshotResult = tObject({ + binary: tBinary + }); + scheme.AndroidDeviceInputTypeParams = tObject({ + text: tString + }); + scheme.AndroidDeviceInputTypeResult = tOptional(tObject({})); + scheme.AndroidDeviceInputPressParams = tObject({ + key: tString + }); + scheme.AndroidDeviceInputPressResult = tOptional(tObject({})); + scheme.AndroidDeviceInputTapParams = tObject({ + point: tType("Point") + }); + scheme.AndroidDeviceInputTapResult = tOptional(tObject({})); + scheme.AndroidDeviceInputSwipeParams = tObject({ + segments: tArray(tType("Point")), + steps: tInt + }); + scheme.AndroidDeviceInputSwipeResult = tOptional(tObject({})); + scheme.AndroidDeviceInputDragParams = tObject({ + from: tType("Point"), + to: tType("Point"), + steps: tInt + }); + scheme.AndroidDeviceInputDragResult = tOptional(tObject({})); + scheme.AndroidDeviceLaunchBrowserParams = tObject({ + noDefaultViewport: tOptional(tBoolean), + viewport: tOptional(tObject({ + width: tInt, + height: tInt + })), + screen: tOptional(tObject({ + width: tInt, + height: tInt + })), + ignoreHTTPSErrors: tOptional(tBoolean), + clientCertificates: tOptional(tArray(tObject({ + origin: tString, + cert: tOptional(tBinary), + key: tOptional(tBinary), + passphrase: tOptional(tString), + pfx: tOptional(tBinary) + }))), + javaScriptEnabled: tOptional(tBoolean), + bypassCSP: tOptional(tBoolean), + userAgent: tOptional(tString), + locale: tOptional(tString), + timezoneId: tOptional(tString), + geolocation: tOptional(tObject({ + longitude: tFloat, + latitude: tFloat, + accuracy: tOptional(tFloat) + })), + permissions: tOptional(tArray(tString)), + extraHTTPHeaders: tOptional(tArray(tType("NameValue"))), + offline: tOptional(tBoolean), + httpCredentials: tOptional(tObject({ + username: tString, + password: tString, + origin: tOptional(tString), + send: tOptional(tEnum(["always", "unauthorized"])) + })), + deviceScaleFactor: tOptional(tFloat), + isMobile: tOptional(tBoolean), + hasTouch: tOptional(tBoolean), + colorScheme: tOptional(tEnum(["dark", "light", "no-preference", "no-override"])), + reducedMotion: tOptional(tEnum(["reduce", "no-preference", "no-override"])), + forcedColors: tOptional(tEnum(["active", "none", "no-override"])), + acceptDownloads: tOptional(tEnum(["accept", "deny", "internal-browser-default"])), + contrast: tOptional(tEnum(["no-preference", "more", "no-override"])), + baseURL: tOptional(tString), + recordVideo: tOptional(tObject({ + dir: tOptional(tString), + size: tOptional(tObject({ + width: tInt, + height: tInt + })), + showActions: tOptional(tObject({ + duration: tOptional(tFloat), + position: tOptional(tEnum(["top-left", "top", "top-right", "bottom-left", "bottom", "bottom-right"])), + fontSize: tOptional(tInt), + cursor: tOptional(tEnum(["none", "pointer"])) + })) + })), + strictSelectors: tOptional(tBoolean), + serviceWorkers: tOptional(tEnum(["allow", "block"])), + selectorEngines: tOptional(tArray(tType("SelectorEngine"))), + testIdAttributeName: tOptional(tString), + pkg: tOptional(tString), + args: tOptional(tArray(tString)), + proxy: tOptional(tObject({ + server: tString, + bypass: tOptional(tString), + username: tOptional(tString), + password: tOptional(tString) + })) + }); + scheme.AndroidDeviceLaunchBrowserResult = tObject({ + context: tChannel(["BrowserContext"]) + }); + scheme.AndroidDeviceOpenParams = tObject({ + command: tString + }); + scheme.AndroidDeviceOpenResult = tObject({ + socket: tChannel(["AndroidSocket"]) + }); + scheme.AndroidDeviceShellParams = tObject({ + command: tString + }); + scheme.AndroidDeviceShellResult = tObject({ + result: tBinary + }); + scheme.AndroidDeviceInstallApkParams = tObject({ + file: tBinary, + args: tOptional(tArray(tString)) + }); + scheme.AndroidDeviceInstallApkResult = tOptional(tObject({})); + scheme.AndroidDevicePushParams = tObject({ + file: tBinary, + path: tString, + mode: tOptional(tInt) + }); + scheme.AndroidDevicePushResult = tOptional(tObject({})); + scheme.AndroidDeviceConnectToWebViewParams = tObject({ + socketName: tString + }); + scheme.AndroidDeviceConnectToWebViewResult = tObject({ + context: tChannel(["BrowserContext"]) + }); + scheme.AndroidDeviceCloseParams = tOptional(tObject({})); + scheme.AndroidDeviceCloseResult = tOptional(tObject({})); + scheme.AndroidWebView = tObject({ + pid: tInt, + pkg: tString, + socketName: tString + }); + scheme.AndroidSelector = tObject({ + checkable: tOptional(tBoolean), + checked: tOptional(tBoolean), + clazz: tOptional(tString), + clickable: tOptional(tBoolean), + depth: tOptional(tInt), + desc: tOptional(tString), + enabled: tOptional(tBoolean), + focusable: tOptional(tBoolean), + focused: tOptional(tBoolean), + hasChild: tOptional(tObject({ + androidSelector: tType("AndroidSelector") + })), + hasDescendant: tOptional(tObject({ + androidSelector: tType("AndroidSelector"), + maxDepth: tOptional(tInt) + })), + longClickable: tOptional(tBoolean), + pkg: tOptional(tString), + res: tOptional(tString), + scrollable: tOptional(tBoolean), + selected: tOptional(tBoolean), + text: tOptional(tString) + }); + scheme.AndroidElementInfo = tObject({ + children: tOptional(tArray(tType("AndroidElementInfo"))), + clazz: tString, + desc: tString, + res: tString, + pkg: tString, + text: tString, + bounds: tType("Rect"), + checkable: tBoolean, + checked: tBoolean, + clickable: tBoolean, + enabled: tBoolean, + focusable: tBoolean, + focused: tBoolean, + longClickable: tBoolean, + scrollable: tBoolean, + selected: tBoolean + }); + scheme.APIRequestContextInitializer = tObject({ + tracing: tChannel(["Tracing"]) + }); + scheme.APIRequestContextFetchParams = tObject({ + url: tString, + encodedParams: tOptional(tString), + params: tOptional(tArray(tType("NameValue"))), + method: tOptional(tString), + headers: tOptional(tArray(tType("NameValue"))), + postData: tOptional(tBinary), + jsonData: tOptional(tString), + formData: tOptional(tArray(tType("NameValue"))), + multipartData: tOptional(tArray(tType("FormField"))), + timeout: tFloat, + failOnStatusCode: tOptional(tBoolean), + ignoreHTTPSErrors: tOptional(tBoolean), + maxRedirects: tOptional(tInt), + maxRetries: tOptional(tInt) + }); + scheme.APIRequestContextFetchResult = tObject({ + response: tType("APIResponse") + }); + scheme.APIRequestContextFetchResponseBodyParams = tObject({ + fetchUid: tString + }); + scheme.APIRequestContextFetchResponseBodyResult = tObject({ + binary: tOptional(tBinary) + }); + scheme.APIRequestContextFetchLogParams = tObject({ + fetchUid: tString + }); + scheme.APIRequestContextFetchLogResult = tObject({ + log: tArray(tString) + }); + scheme.APIRequestContextStorageStateParams = tObject({ + indexedDB: tOptional(tBoolean) + }); + scheme.APIRequestContextStorageStateResult = tObject({ + cookies: tArray(tType("NetworkCookie")), + origins: tArray(tType("OriginStorage")) + }); + scheme.APIRequestContextDisposeAPIResponseParams = tObject({ + fetchUid: tString + }); + scheme.APIRequestContextDisposeAPIResponseResult = tOptional(tObject({})); + scheme.APIRequestContextDisposeParams = tObject({ + reason: tOptional(tString) + }); + scheme.APIRequestContextDisposeResult = tOptional(tObject({})); + scheme.APIResponse = tObject({ + fetchUid: tString, + url: tString, + status: tInt, + statusText: tString, + headers: tArray(tType("NameValue")), + securityDetails: tOptional(tType("SecurityDetails")), + serverAddr: tOptional(tType("RemoteAddr")) + }); + scheme.ArtifactInitializer = tObject({ + absolutePath: tString + }); + scheme.ArtifactPathAfterFinishedParams = tOptional(tObject({})); + scheme.ArtifactPathAfterFinishedResult = tObject({ + value: tString + }); + scheme.ArtifactSaveAsParams = tObject({ + path: tString + }); + scheme.ArtifactSaveAsResult = tOptional(tObject({})); + scheme.ArtifactSaveAsStreamParams = tOptional(tObject({})); + scheme.ArtifactSaveAsStreamResult = tObject({ + stream: tChannel(["Stream"]) + }); + scheme.ArtifactFailureParams = tOptional(tObject({})); + scheme.ArtifactFailureResult = tObject({ + error: tOptional(tString) + }); + scheme.ArtifactStreamParams = tOptional(tObject({})); + scheme.ArtifactStreamResult = tObject({ + stream: tChannel(["Stream"]) + }); + scheme.ArtifactCancelParams = tOptional(tObject({})); + scheme.ArtifactCancelResult = tOptional(tObject({})); + scheme.ArtifactDeleteParams = tOptional(tObject({})); + scheme.ArtifactDeleteResult = tOptional(tObject({})); + scheme.StreamInitializer = tOptional(tObject({})); + scheme.StreamReadParams = tObject({ + size: tOptional(tInt) + }); + scheme.StreamReadResult = tObject({ + binary: tBinary + }); + scheme.StreamCloseParams = tOptional(tObject({})); + scheme.StreamCloseResult = tOptional(tObject({})); + scheme.WritableStreamInitializer = tOptional(tObject({})); + scheme.WritableStreamWriteParams = tObject({ + binary: tBinary + }); + scheme.WritableStreamWriteResult = tOptional(tObject({})); + scheme.WritableStreamCloseParams = tOptional(tObject({})); + scheme.WritableStreamCloseResult = tOptional(tObject({})); + scheme.BrowserInitializer = tObject({ + version: tString, + name: tString, + browserName: tEnum(["chromium", "firefox", "webkit"]) + }); + scheme.BrowserContextEvent = tObject({ + context: tChannel(["BrowserContext"]) + }); + scheme.BrowserCloseEvent = tOptional(tObject({})); + scheme.BrowserStartServerParams = tObject({ + title: tString, + workspaceDir: tOptional(tString), + metadata: tOptional(tAny), + host: tOptional(tString), + port: tOptional(tInt) + }); + scheme.BrowserStartServerResult = tObject({ + endpoint: tString + }); + scheme.BrowserStopServerParams = tOptional(tObject({})); + scheme.BrowserStopServerResult = tOptional(tObject({})); + scheme.BrowserCloseParams = tObject({ + reason: tOptional(tString) + }); + scheme.BrowserCloseResult = tOptional(tObject({})); + scheme.BrowserKillForTestsParams = tOptional(tObject({})); + scheme.BrowserKillForTestsResult = tOptional(tObject({})); + scheme.BrowserDefaultUserAgentForTestParams = tOptional(tObject({})); + scheme.BrowserDefaultUserAgentForTestResult = tObject({ + userAgent: tString + }); + scheme.BrowserNewContextParams = tObject({ + noDefaultViewport: tOptional(tBoolean), + viewport: tOptional(tObject({ + width: tInt, + height: tInt + })), + screen: tOptional(tObject({ + width: tInt, + height: tInt + })), + ignoreHTTPSErrors: tOptional(tBoolean), + clientCertificates: tOptional(tArray(tObject({ + origin: tString, + cert: tOptional(tBinary), + key: tOptional(tBinary), + passphrase: tOptional(tString), + pfx: tOptional(tBinary) + }))), + javaScriptEnabled: tOptional(tBoolean), + bypassCSP: tOptional(tBoolean), + userAgent: tOptional(tString), + locale: tOptional(tString), + timezoneId: tOptional(tString), + geolocation: tOptional(tObject({ + longitude: tFloat, + latitude: tFloat, + accuracy: tOptional(tFloat) + })), + permissions: tOptional(tArray(tString)), + extraHTTPHeaders: tOptional(tArray(tType("NameValue"))), + offline: tOptional(tBoolean), + httpCredentials: tOptional(tObject({ + username: tString, + password: tString, + origin: tOptional(tString), + send: tOptional(tEnum(["always", "unauthorized"])) + })), + deviceScaleFactor: tOptional(tFloat), + isMobile: tOptional(tBoolean), + hasTouch: tOptional(tBoolean), + colorScheme: tOptional(tEnum(["dark", "light", "no-preference", "no-override"])), + reducedMotion: tOptional(tEnum(["reduce", "no-preference", "no-override"])), + forcedColors: tOptional(tEnum(["active", "none", "no-override"])), + acceptDownloads: tOptional(tEnum(["accept", "deny", "internal-browser-default"])), + contrast: tOptional(tEnum(["no-preference", "more", "no-override"])), + baseURL: tOptional(tString), + recordVideo: tOptional(tObject({ + dir: tOptional(tString), + size: tOptional(tObject({ + width: tInt, + height: tInt + })), + showActions: tOptional(tObject({ + duration: tOptional(tFloat), + position: tOptional(tEnum(["top-left", "top", "top-right", "bottom-left", "bottom", "bottom-right"])), + fontSize: tOptional(tInt), + cursor: tOptional(tEnum(["none", "pointer"])) + })) + })), + strictSelectors: tOptional(tBoolean), + serviceWorkers: tOptional(tEnum(["allow", "block"])), + selectorEngines: tOptional(tArray(tType("SelectorEngine"))), + testIdAttributeName: tOptional(tString), + proxy: tOptional(tObject({ + server: tString, + bypass: tOptional(tString), + username: tOptional(tString), + password: tOptional(tString) + })), + storageState: tOptional(tObject({ + cookies: tOptional(tArray(tType("SetNetworkCookie"))), + origins: tOptional(tArray(tType("SetOriginStorage"))) + })) + }); + scheme.BrowserNewContextResult = tObject({ + context: tChannel(["BrowserContext"]) + }); + scheme.BrowserNewContextForReuseParams = tObject({ + noDefaultViewport: tOptional(tBoolean), + viewport: tOptional(tObject({ + width: tInt, + height: tInt + })), + screen: tOptional(tObject({ + width: tInt, + height: tInt + })), + ignoreHTTPSErrors: tOptional(tBoolean), + clientCertificates: tOptional(tArray(tObject({ + origin: tString, + cert: tOptional(tBinary), + key: tOptional(tBinary), + passphrase: tOptional(tString), + pfx: tOptional(tBinary) + }))), + javaScriptEnabled: tOptional(tBoolean), + bypassCSP: tOptional(tBoolean), + userAgent: tOptional(tString), + locale: tOptional(tString), + timezoneId: tOptional(tString), + geolocation: tOptional(tObject({ + longitude: tFloat, + latitude: tFloat, + accuracy: tOptional(tFloat) + })), + permissions: tOptional(tArray(tString)), + extraHTTPHeaders: tOptional(tArray(tType("NameValue"))), + offline: tOptional(tBoolean), + httpCredentials: tOptional(tObject({ + username: tString, + password: tString, + origin: tOptional(tString), + send: tOptional(tEnum(["always", "unauthorized"])) + })), + deviceScaleFactor: tOptional(tFloat), + isMobile: tOptional(tBoolean), + hasTouch: tOptional(tBoolean), + colorScheme: tOptional(tEnum(["dark", "light", "no-preference", "no-override"])), + reducedMotion: tOptional(tEnum(["reduce", "no-preference", "no-override"])), + forcedColors: tOptional(tEnum(["active", "none", "no-override"])), + acceptDownloads: tOptional(tEnum(["accept", "deny", "internal-browser-default"])), + contrast: tOptional(tEnum(["no-preference", "more", "no-override"])), + baseURL: tOptional(tString), + recordVideo: tOptional(tObject({ + dir: tOptional(tString), + size: tOptional(tObject({ + width: tInt, + height: tInt + })), + showActions: tOptional(tObject({ + duration: tOptional(tFloat), + position: tOptional(tEnum(["top-left", "top", "top-right", "bottom-left", "bottom", "bottom-right"])), + fontSize: tOptional(tInt), + cursor: tOptional(tEnum(["none", "pointer"])) + })) + })), + strictSelectors: tOptional(tBoolean), + serviceWorkers: tOptional(tEnum(["allow", "block"])), + selectorEngines: tOptional(tArray(tType("SelectorEngine"))), + testIdAttributeName: tOptional(tString), + proxy: tOptional(tObject({ + server: tString, + bypass: tOptional(tString), + username: tOptional(tString), + password: tOptional(tString) + })), + storageState: tOptional(tObject({ + cookies: tOptional(tArray(tType("SetNetworkCookie"))), + origins: tOptional(tArray(tType("SetOriginStorage"))) + })) + }); + scheme.BrowserNewContextForReuseResult = tObject({ + context: tChannel(["BrowserContext"]) + }); + scheme.BrowserDisconnectFromReusedContextParams = tObject({ + reason: tString + }); + scheme.BrowserDisconnectFromReusedContextResult = tOptional(tObject({})); + scheme.BrowserNewBrowserCDPSessionParams = tOptional(tObject({})); + scheme.BrowserNewBrowserCDPSessionResult = tObject({ + session: tChannel(["CDPSession"]) + }); + scheme.BrowserStartTracingParams = tObject({ + page: tOptional(tChannel(["Page"])), + screenshots: tOptional(tBoolean), + categories: tOptional(tArray(tString)) + }); + scheme.BrowserStartTracingResult = tOptional(tObject({})); + scheme.BrowserStopTracingParams = tOptional(tObject({})); + scheme.BrowserStopTracingResult = tObject({ + artifact: tChannel(["Artifact"]) + }); + scheme.BrowserContextInitializer = tObject({ + debugger: tChannel(["Debugger"]), + requestContext: tChannel(["APIRequestContext"]), + tracing: tChannel(["Tracing"]), + options: tObject({ + noDefaultViewport: tOptional(tBoolean), + viewport: tOptional(tObject({ + width: tInt, + height: tInt + })), + screen: tOptional(tObject({ + width: tInt, + height: tInt + })), + ignoreHTTPSErrors: tOptional(tBoolean), + clientCertificates: tOptional(tArray(tObject({ + origin: tString, + cert: tOptional(tBinary), + key: tOptional(tBinary), + passphrase: tOptional(tString), + pfx: tOptional(tBinary) + }))), + javaScriptEnabled: tOptional(tBoolean), + bypassCSP: tOptional(tBoolean), + userAgent: tOptional(tString), + locale: tOptional(tString), + timezoneId: tOptional(tString), + geolocation: tOptional(tObject({ + longitude: tFloat, + latitude: tFloat, + accuracy: tOptional(tFloat) + })), + permissions: tOptional(tArray(tString)), + extraHTTPHeaders: tOptional(tArray(tType("NameValue"))), + offline: tOptional(tBoolean), + httpCredentials: tOptional(tObject({ + username: tString, + password: tString, + origin: tOptional(tString), + send: tOptional(tEnum(["always", "unauthorized"])) + })), + deviceScaleFactor: tOptional(tFloat), + isMobile: tOptional(tBoolean), + hasTouch: tOptional(tBoolean), + colorScheme: tOptional(tEnum(["dark", "light", "no-preference", "no-override"])), + reducedMotion: tOptional(tEnum(["reduce", "no-preference", "no-override"])), + forcedColors: tOptional(tEnum(["active", "none", "no-override"])), + acceptDownloads: tOptional(tEnum(["accept", "deny", "internal-browser-default"])), + contrast: tOptional(tEnum(["no-preference", "more", "no-override"])), + baseURL: tOptional(tString), + recordVideo: tOptional(tObject({ + dir: tOptional(tString), + size: tOptional(tObject({ + width: tInt, + height: tInt + })), + showActions: tOptional(tObject({ + duration: tOptional(tFloat), + position: tOptional(tEnum(["top-left", "top", "top-right", "bottom-left", "bottom", "bottom-right"])), + fontSize: tOptional(tInt), + cursor: tOptional(tEnum(["none", "pointer"])) + })) + })), + strictSelectors: tOptional(tBoolean), + serviceWorkers: tOptional(tEnum(["allow", "block"])), + selectorEngines: tOptional(tArray(tType("SelectorEngine"))), + testIdAttributeName: tOptional(tString) + }) + }); + scheme.BrowserContextBindingCallEvent = tObject({ + binding: tChannel(["BindingCall"]) + }); + scheme.BrowserContextConsoleEvent = tObject({ + type: tString, + text: tString, + args: tArray(tChannel(["ElementHandle", "JSHandle"])), + location: tObject({ + url: tString, + lineNumber: tInt, + columnNumber: tInt + }), + timestamp: tFloat, + page: tOptional(tChannel(["Page"])), + worker: tOptional(tChannel(["Worker"])) + }); + scheme.BrowserContextCloseEvent = tOptional(tObject({})); + scheme.BrowserContextDialogEvent = tObject({ + dialog: tChannel(["Dialog"]) + }); + scheme.BrowserContextPageEvent = tObject({ + page: tChannel(["Page"]) + }); + scheme.BrowserContextPageErrorEvent = tObject({ + error: tType("SerializedError"), + page: tChannel(["Page"]), + location: tObject({ + url: tString, + line: tInt, + column: tInt + }) + }); + scheme.BrowserContextRouteEvent = tObject({ + route: tChannel(["Route"]) + }); + scheme.BrowserContextWebSocketRouteEvent = tObject({ + webSocketRoute: tChannel(["WebSocketRoute"]) + }); + scheme.BrowserContextServiceWorkerEvent = tObject({ + worker: tChannel(["Worker"]) + }); + scheme.BrowserContextRequestEvent = tObject({ + request: tChannel(["Request"]), + page: tOptional(tChannel(["Page"])) + }); + scheme.BrowserContextRequestFailedEvent = tObject({ + request: tChannel(["Request"]), + failureText: tOptional(tString), + responseEndTiming: tFloat, + page: tOptional(tChannel(["Page"])) + }); + scheme.BrowserContextRequestFinishedEvent = tObject({ + request: tChannel(["Request"]), + response: tOptional(tChannel(["Response"])), + responseEndTiming: tFloat, + page: tOptional(tChannel(["Page"])) + }); + scheme.BrowserContextResponseEvent = tObject({ + response: tChannel(["Response"]), + page: tOptional(tChannel(["Page"])) + }); + scheme.BrowserContextRecorderEventEvent = tObject({ + event: tEnum(["actionAdded", "actionUpdated", "signalAdded"]), + data: tAny, + page: tChannel(["Page"]), + code: tString + }); + scheme.BrowserContextAddCookiesParams = tObject({ + cookies: tArray(tType("SetNetworkCookie")) + }); + scheme.BrowserContextAddCookiesResult = tOptional(tObject({})); + scheme.BrowserContextAddInitScriptParams = tObject({ + source: tString + }); + scheme.BrowserContextAddInitScriptResult = tObject({ + disposable: tChannel(["Disposable"]) + }); + scheme.BrowserContextClearCookiesParams = tObject({ + name: tOptional(tString), + nameRegexSource: tOptional(tString), + nameRegexFlags: tOptional(tString), + domain: tOptional(tString), + domainRegexSource: tOptional(tString), + domainRegexFlags: tOptional(tString), + path: tOptional(tString), + pathRegexSource: tOptional(tString), + pathRegexFlags: tOptional(tString) + }); + scheme.BrowserContextClearCookiesResult = tOptional(tObject({})); + scheme.BrowserContextClearPermissionsParams = tOptional(tObject({})); + scheme.BrowserContextClearPermissionsResult = tOptional(tObject({})); + scheme.BrowserContextCloseParams = tObject({ + reason: tOptional(tString) + }); + scheme.BrowserContextCloseResult = tOptional(tObject({})); + scheme.BrowserContextCookiesParams = tObject({ + urls: tArray(tString) + }); + scheme.BrowserContextCookiesResult = tObject({ + cookies: tArray(tType("NetworkCookie")) + }); + scheme.BrowserContextExposeBindingParams = tObject({ + name: tString + }); + scheme.BrowserContextExposeBindingResult = tObject({ + disposable: tChannel(["Disposable"]) + }); + scheme.BrowserContextGrantPermissionsParams = tObject({ + permissions: tArray(tString), + origin: tOptional(tString) + }); + scheme.BrowserContextGrantPermissionsResult = tOptional(tObject({})); + scheme.BrowserContextNewPageParams = tOptional(tObject({})); + scheme.BrowserContextNewPageResult = tObject({ + page: tChannel(["Page"]) + }); + scheme.BrowserContextRegisterSelectorEngineParams = tObject({ + selectorEngine: tType("SelectorEngine") + }); + scheme.BrowserContextRegisterSelectorEngineResult = tOptional(tObject({})); + scheme.BrowserContextSetTestIdAttributeNameParams = tObject({ + testIdAttributeName: tString + }); + scheme.BrowserContextSetTestIdAttributeNameResult = tOptional(tObject({})); + scheme.BrowserContextSetExtraHTTPHeadersParams = tObject({ + headers: tArray(tType("NameValue")) + }); + scheme.BrowserContextSetExtraHTTPHeadersResult = tOptional(tObject({})); + scheme.BrowserContextSetGeolocationParams = tObject({ + geolocation: tOptional(tObject({ + longitude: tFloat, + latitude: tFloat, + accuracy: tOptional(tFloat) + })) + }); + scheme.BrowserContextSetGeolocationResult = tOptional(tObject({})); + scheme.BrowserContextSetHTTPCredentialsParams = tObject({ + httpCredentials: tOptional(tObject({ + username: tString, + password: tString, + origin: tOptional(tString) + })) + }); + scheme.BrowserContextSetHTTPCredentialsResult = tOptional(tObject({})); + scheme.BrowserContextSetNetworkInterceptionPatternsParams = tObject({ + patterns: tArray(tObject({ + glob: tOptional(tString), + regexSource: tOptional(tString), + regexFlags: tOptional(tString), + urlPattern: tOptional(tType("URLPattern")) + })) + }); + scheme.BrowserContextSetNetworkInterceptionPatternsResult = tOptional(tObject({})); + scheme.BrowserContextSetWebSocketInterceptionPatternsParams = tObject({ + patterns: tArray(tObject({ + glob: tOptional(tString), + regexSource: tOptional(tString), + regexFlags: tOptional(tString), + urlPattern: tOptional(tType("URLPattern")) + })) + }); + scheme.BrowserContextSetWebSocketInterceptionPatternsResult = tOptional(tObject({})); + scheme.BrowserContextSetOfflineParams = tObject({ + offline: tBoolean + }); + scheme.BrowserContextSetOfflineResult = tOptional(tObject({})); + scheme.BrowserContextStorageStateParams = tObject({ + indexedDB: tOptional(tBoolean) + }); + scheme.BrowserContextStorageStateResult = tObject({ + cookies: tArray(tType("NetworkCookie")), + origins: tArray(tType("OriginStorage")) + }); + scheme.BrowserContextSetStorageStateParams = tObject({ + storageState: tOptional(tObject({ + cookies: tOptional(tArray(tType("SetNetworkCookie"))), + origins: tOptional(tArray(tType("SetOriginStorage"))) + })) + }); + scheme.BrowserContextSetStorageStateResult = tOptional(tObject({})); + scheme.BrowserContextPauseParams = tOptional(tObject({})); + scheme.BrowserContextPauseResult = tOptional(tObject({})); + scheme.BrowserContextEnableRecorderParams = tObject({ + language: tOptional(tString), + mode: tOptional(tEnum(["inspecting", "recording"])), + recorderMode: tOptional(tEnum(["default", "api"])), + pauseOnNextStatement: tOptional(tBoolean), + testIdAttributeName: tOptional(tString), + launchOptions: tOptional(tAny), + contextOptions: tOptional(tAny), + device: tOptional(tString), + saveStorage: tOptional(tString), + outputFile: tOptional(tString), + handleSIGINT: tOptional(tBoolean), + omitCallTracking: tOptional(tBoolean) + }); + scheme.BrowserContextEnableRecorderResult = tOptional(tObject({})); + scheme.BrowserContextDisableRecorderParams = tOptional(tObject({})); + scheme.BrowserContextDisableRecorderResult = tOptional(tObject({})); + scheme.BrowserContextExposeConsoleApiParams = tOptional(tObject({})); + scheme.BrowserContextExposeConsoleApiResult = tOptional(tObject({})); + scheme.BrowserContextNewCDPSessionParams = tObject({ + page: tOptional(tChannel(["Page"])), + frame: tOptional(tChannel(["Frame"])) + }); + scheme.BrowserContextNewCDPSessionResult = tObject({ + session: tChannel(["CDPSession"]) + }); + scheme.BrowserContextCreateTempFilesParams = tObject({ + rootDirName: tOptional(tString), + items: tArray(tObject({ + name: tString, + lastModifiedMs: tOptional(tFloat) + })) + }); + scheme.BrowserContextCreateTempFilesResult = tObject({ + rootDir: tOptional(tChannel(["WritableStream"])), + writableStreams: tArray(tChannel(["WritableStream"])) + }); + scheme.BrowserContextUpdateSubscriptionParams = tObject({ + event: tEnum(["console", "dialog", "request", "response", "requestFinished", "requestFailed"]), + enabled: tBoolean + }); + scheme.BrowserContextUpdateSubscriptionResult = tOptional(tObject({})); + scheme.BrowserContextClockFastForwardParams = tObject({ + ticksNumber: tOptional(tFloat), + ticksString: tOptional(tString) + }); + scheme.BrowserContextClockFastForwardResult = tOptional(tObject({})); + scheme.BrowserContextClockInstallParams = tObject({ + timeNumber: tOptional(tFloat), + timeString: tOptional(tString) + }); + scheme.BrowserContextClockInstallResult = tOptional(tObject({})); + scheme.BrowserContextClockPauseAtParams = tObject({ + timeNumber: tOptional(tFloat), + timeString: tOptional(tString) + }); + scheme.BrowserContextClockPauseAtResult = tOptional(tObject({})); + scheme.BrowserContextClockResumeParams = tOptional(tObject({})); + scheme.BrowserContextClockResumeResult = tOptional(tObject({})); + scheme.BrowserContextClockRunForParams = tObject({ + ticksNumber: tOptional(tFloat), + ticksString: tOptional(tString) + }); + scheme.BrowserContextClockRunForResult = tOptional(tObject({})); + scheme.BrowserContextClockSetFixedTimeParams = tObject({ + timeNumber: tOptional(tFloat), + timeString: tOptional(tString) + }); + scheme.BrowserContextClockSetFixedTimeResult = tOptional(tObject({})); + scheme.BrowserContextClockSetSystemTimeParams = tObject({ + timeNumber: tOptional(tFloat), + timeString: tOptional(tString) + }); + scheme.BrowserContextClockSetSystemTimeResult = tOptional(tObject({})); + scheme.BrowserContextCredentialsInstallParams = tOptional(tObject({})); + scheme.BrowserContextCredentialsInstallResult = tOptional(tObject({})); + scheme.BrowserContextCredentialsCreateParams = tObject({ + rpId: tString, + id: tOptional(tString), + userHandle: tOptional(tString), + privateKey: tOptional(tString), + publicKey: tOptional(tString) + }); + scheme.BrowserContextCredentialsCreateResult = tObject({ + credential: tType("VirtualCredential") + }); + scheme.BrowserContextCredentialsGetParams = tObject({ + rpId: tOptional(tString), + id: tOptional(tString) + }); + scheme.BrowserContextCredentialsGetResult = tObject({ + credentials: tArray(tType("VirtualCredential")) + }); + scheme.BrowserContextCredentialsDeleteParams = tObject({ + id: tString + }); + scheme.BrowserContextCredentialsDeleteResult = tOptional(tObject({})); + scheme.BrowserTypeInitializer = tObject({ + executablePath: tString, + name: tString + }); + scheme.BrowserTypeLaunchParams = tObject({ + channel: tOptional(tString), + executablePath: tOptional(tString), + args: tOptional(tArray(tString)), + ignoreAllDefaultArgs: tOptional(tBoolean), + ignoreDefaultArgs: tOptional(tArray(tString)), + handleSIGINT: tOptional(tBoolean), + handleSIGTERM: tOptional(tBoolean), + handleSIGHUP: tOptional(tBoolean), + timeout: tFloat, + env: tOptional(tArray(tType("NameValue"))), + headless: tOptional(tBoolean), + proxy: tOptional(tObject({ + server: tString, + bypass: tOptional(tString), + username: tOptional(tString), + password: tOptional(tString) + })), + downloadsPath: tOptional(tString), + tracesDir: tOptional(tString), + artifactsDir: tOptional(tString), + chromiumSandbox: tOptional(tBoolean), + firefoxUserPrefs: tOptional(tAny), + cdpPort: tOptional(tInt), + slowMo: tOptional(tFloat) + }); + scheme.BrowserTypeLaunchResult = tObject({ + browser: tChannel(["Browser"]) + }); + scheme.BrowserTypeLaunchPersistentContextParams = tObject({ + channel: tOptional(tString), + executablePath: tOptional(tString), + args: tOptional(tArray(tString)), + ignoreAllDefaultArgs: tOptional(tBoolean), + ignoreDefaultArgs: tOptional(tArray(tString)), + handleSIGINT: tOptional(tBoolean), + handleSIGTERM: tOptional(tBoolean), + handleSIGHUP: tOptional(tBoolean), + timeout: tFloat, + env: tOptional(tArray(tType("NameValue"))), + headless: tOptional(tBoolean), + proxy: tOptional(tObject({ + server: tString, + bypass: tOptional(tString), + username: tOptional(tString), + password: tOptional(tString) + })), + downloadsPath: tOptional(tString), + tracesDir: tOptional(tString), + artifactsDir: tOptional(tString), + chromiumSandbox: tOptional(tBoolean), + firefoxUserPrefs: tOptional(tAny), + cdpPort: tOptional(tInt), + noDefaultViewport: tOptional(tBoolean), + viewport: tOptional(tObject({ + width: tInt, + height: tInt + })), + screen: tOptional(tObject({ + width: tInt, + height: tInt + })), + ignoreHTTPSErrors: tOptional(tBoolean), + clientCertificates: tOptional(tArray(tObject({ + origin: tString, + cert: tOptional(tBinary), + key: tOptional(tBinary), + passphrase: tOptional(tString), + pfx: tOptional(tBinary) + }))), + javaScriptEnabled: tOptional(tBoolean), + bypassCSP: tOptional(tBoolean), + userAgent: tOptional(tString), + locale: tOptional(tString), + timezoneId: tOptional(tString), + geolocation: tOptional(tObject({ + longitude: tFloat, + latitude: tFloat, + accuracy: tOptional(tFloat) + })), + permissions: tOptional(tArray(tString)), + extraHTTPHeaders: tOptional(tArray(tType("NameValue"))), + offline: tOptional(tBoolean), + httpCredentials: tOptional(tObject({ + username: tString, + password: tString, + origin: tOptional(tString), + send: tOptional(tEnum(["always", "unauthorized"])) + })), + deviceScaleFactor: tOptional(tFloat), + isMobile: tOptional(tBoolean), + hasTouch: tOptional(tBoolean), + colorScheme: tOptional(tEnum(["dark", "light", "no-preference", "no-override"])), + reducedMotion: tOptional(tEnum(["reduce", "no-preference", "no-override"])), + forcedColors: tOptional(tEnum(["active", "none", "no-override"])), + acceptDownloads: tOptional(tEnum(["accept", "deny", "internal-browser-default"])), + contrast: tOptional(tEnum(["no-preference", "more", "no-override"])), + baseURL: tOptional(tString), + recordVideo: tOptional(tObject({ + dir: tOptional(tString), + size: tOptional(tObject({ + width: tInt, + height: tInt + })), + showActions: tOptional(tObject({ + duration: tOptional(tFloat), + position: tOptional(tEnum(["top-left", "top", "top-right", "bottom-left", "bottom", "bottom-right"])), + fontSize: tOptional(tInt), + cursor: tOptional(tEnum(["none", "pointer"])) + })) + })), + strictSelectors: tOptional(tBoolean), + serviceWorkers: tOptional(tEnum(["allow", "block"])), + selectorEngines: tOptional(tArray(tType("SelectorEngine"))), + testIdAttributeName: tOptional(tString), + userDataDir: tString, + slowMo: tOptional(tFloat) + }); + scheme.BrowserTypeLaunchPersistentContextResult = tObject({ + browser: tChannel(["Browser"]), + context: tChannel(["BrowserContext"]) + }); + scheme.BrowserTypeConnectOverCDPParams = tObject({ + endpointURL: tOptional(tString), + headers: tOptional(tArray(tType("NameValue"))), + slowMo: tOptional(tFloat), + timeout: tFloat, + isLocal: tOptional(tBoolean), + noDefaults: tOptional(tBoolean), + artifactsDir: tOptional(tString), + transport: tOptional(tBinary) + }); + scheme.BrowserTypeConnectOverCDPResult = tObject({ + browser: tChannel(["Browser"]), + defaultContext: tOptional(tChannel(["BrowserContext"])) + }); + scheme.BrowserTypeConnectToWorkerParams = tObject({ + endpoint: tString, + timeout: tFloat + }); + scheme.BrowserTypeConnectToWorkerResult = tObject({ + worker: tChannel(["Worker"]) + }); + scheme.Metadata = tObject({ + location: tOptional(tObject({ + file: tString, + line: tOptional(tInt), + column: tOptional(tInt) + })), + title: tOptional(tString), + internal: tOptional(tBoolean), + stepId: tOptional(tString) + }); + scheme.ClientSideCallMetadata = tObject({ + id: tInt, + stack: tOptional(tArray(tType("StackFrame"))) + }); + scheme.SDKLanguage = tEnum(["javascript", "python", "java", "csharp"]); + scheme.DisposableInitializer = tOptional(tObject({})); + scheme.DisposableDisposeParams = tOptional(tObject({})); + scheme.DisposableDisposeResult = tOptional(tObject({})); + scheme.WaitInfo = tObject({ + waitId: tString, + phase: tEnum(["before", "after", "log"]), + event: tOptional(tString), + message: tOptional(tString), + error: tOptional(tString) + }); + scheme.ElectronInitializer = tOptional(tObject({})); + scheme.ElectronLaunchParams = tObject({ + executablePath: tOptional(tString), + args: tOptional(tArray(tString)), + chromiumSandbox: tOptional(tBoolean), + cwd: tOptional(tString), + env: tOptional(tArray(tType("NameValue"))), + timeout: tFloat, + acceptDownloads: tOptional(tEnum(["accept", "deny", "internal-browser-default"])), + bypassCSP: tOptional(tBoolean), + colorScheme: tOptional(tEnum(["dark", "light", "no-preference", "no-override"])), + extraHTTPHeaders: tOptional(tArray(tType("NameValue"))), + geolocation: tOptional(tObject({ + longitude: tFloat, + latitude: tFloat, + accuracy: tOptional(tFloat) + })), + httpCredentials: tOptional(tObject({ + username: tString, + password: tString, + origin: tOptional(tString) + })), + ignoreHTTPSErrors: tOptional(tBoolean), + locale: tOptional(tString), + offline: tOptional(tBoolean), + recordVideo: tOptional(tObject({ + dir: tOptional(tString), + size: tOptional(tObject({ + width: tInt, + height: tInt + })), + showActions: tOptional(tObject({ + duration: tOptional(tFloat), + position: tOptional(tEnum(["top-left", "top", "top-right", "bottom-left", "bottom", "bottom-right"])), + fontSize: tOptional(tInt), + cursor: tOptional(tEnum(["none", "pointer"])) + })) + })), + strictSelectors: tOptional(tBoolean), + timezoneId: tOptional(tString), + tracesDir: tOptional(tString), + artifactsDir: tOptional(tString), + selectorEngines: tOptional(tArray(tType("SelectorEngine"))), + testIdAttributeName: tOptional(tString) + }); + scheme.ElectronLaunchResult = tObject({ + electronApplication: tChannel(["ElectronApplication"]) + }); + scheme.ElectronApplicationInitializer = tObject({ + context: tChannel(["BrowserContext"]) + }); + scheme.ElectronApplicationCloseEvent = tOptional(tObject({})); + scheme.ElectronApplicationConsoleEvent = tObject({ + type: tString, + text: tString, + args: tArray(tChannel(["ElementHandle", "JSHandle"])), + location: tObject({ + url: tString, + lineNumber: tInt, + columnNumber: tInt + }), + timestamp: tFloat + }); + scheme.ElectronApplicationBrowserWindowParams = tObject({ + page: tChannel(["Page"]) + }); + scheme.ElectronApplicationBrowserWindowResult = tObject({ + handle: tChannel(["ElementHandle", "JSHandle"]) + }); + scheme.ElectronApplicationEvaluateExpressionParams = tObject({ + expression: tString, + isFunction: tOptional(tBoolean), + arg: tType("SerializedArgument") + }); + scheme.ElectronApplicationEvaluateExpressionResult = tObject({ + value: tType("SerializedValue") + }); + scheme.ElectronApplicationEvaluateExpressionHandleParams = tObject({ + expression: tString, + isFunction: tOptional(tBoolean), + arg: tType("SerializedArgument") + }); + scheme.ElectronApplicationEvaluateExpressionHandleResult = tObject({ + handle: tChannel(["ElementHandle", "JSHandle"]) + }); + scheme.ElectronApplicationUpdateSubscriptionParams = tObject({ + event: tEnum(["console"]), + enabled: tBoolean + }); + scheme.ElectronApplicationUpdateSubscriptionResult = tOptional(tObject({})); + scheme.FrameInitializer = tObject({ + url: tString, + name: tString, + parentFrame: tOptional(tChannel(["Frame"])), + loadStates: tArray(tType("LifecycleEvent")) + }); + scheme.FrameLoadstateEvent = tObject({ + add: tOptional(tType("LifecycleEvent")), + remove: tOptional(tType("LifecycleEvent")) + }); + scheme.FrameNavigatedEvent = tObject({ + url: tString, + name: tString, + newDocument: tOptional(tObject({ + request: tOptional(tChannel(["Request"])) + })), + error: tOptional(tString) + }); + scheme.FrameEvalOnSelectorParams = tObject({ + selector: tString, + strict: tOptional(tBoolean), + expression: tString, + isFunction: tOptional(tBoolean), + arg: tType("SerializedArgument") + }); + scheme.FrameEvalOnSelectorResult = tObject({ + value: tType("SerializedValue") + }); + scheme.FrameEvalOnSelectorAllParams = tObject({ + selector: tString, + expression: tString, + isFunction: tOptional(tBoolean), + arg: tType("SerializedArgument") + }); + scheme.FrameEvalOnSelectorAllResult = tObject({ + value: tType("SerializedValue") + }); + scheme.FrameAddScriptTagParams = tObject({ + url: tOptional(tString), + content: tOptional(tString), + type: tOptional(tString) + }); + scheme.FrameAddScriptTagResult = tObject({ + element: tChannel(["ElementHandle"]) + }); + scheme.FrameAddStyleTagParams = tObject({ + url: tOptional(tString), + content: tOptional(tString) + }); + scheme.FrameAddStyleTagResult = tObject({ + element: tChannel(["ElementHandle"]) + }); + scheme.FrameAriaSnapshotParams = tObject({ + mode: tOptional(tEnum(["ai", "default"])), + track: tOptional(tString), + selector: tOptional(tString), + depth: tOptional(tInt), + boxes: tOptional(tBoolean), + timeout: tFloat + }); + scheme.FrameAriaSnapshotResult = tObject({ + snapshot: tString + }); + scheme.FrameBlurParams = tObject({ + selector: tString, + strict: tOptional(tBoolean), + timeout: tFloat + }); + scheme.FrameBlurResult = tOptional(tObject({})); + scheme.FrameCheckParams = tObject({ + selector: tString, + strict: tOptional(tBoolean), + force: tOptional(tBoolean), + position: tOptional(tType("Point")), + timeout: tFloat, + trial: tOptional(tBoolean) + }); + scheme.FrameCheckResult = tOptional(tObject({})); + scheme.FrameClickParams = tObject({ + selector: tString, + strict: tOptional(tBoolean), + force: tOptional(tBoolean), + noWaitAfter: tOptional(tBoolean), + modifiers: tOptional(tArray(tEnum(["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]))), + position: tOptional(tType("Point")), + delay: tOptional(tFloat), + button: tOptional(tEnum(["left", "right", "middle"])), + clickCount: tOptional(tInt), + timeout: tFloat, + trial: tOptional(tBoolean), + steps: tOptional(tInt) + }); + scheme.FrameClickResult = tOptional(tObject({})); + scheme.FrameContentParams = tOptional(tObject({})); + scheme.FrameContentResult = tObject({ + value: tString + }); + scheme.FrameDragAndDropParams = tObject({ + source: tString, + target: tString, + force: tOptional(tBoolean), + timeout: tFloat, + trial: tOptional(tBoolean), + sourcePosition: tOptional(tType("Point")), + targetPosition: tOptional(tType("Point")), + strict: tOptional(tBoolean), + steps: tOptional(tInt) + }); + scheme.FrameDragAndDropResult = tOptional(tObject({})); + scheme.FrameDropParams = tObject({ + selector: tString, + strict: tOptional(tBoolean), + position: tOptional(tType("Point")), + payloads: tOptional(tArray(tObject({ + name: tString, + mimeType: tOptional(tString), + buffer: tBinary + }))), + localPaths: tOptional(tArray(tString)), + streams: tOptional(tArray(tChannel(["WritableStream"]))), + data: tOptional(tArray(tObject({ + mimeType: tString, + value: tString + }))), + timeout: tFloat + }); + scheme.FrameDropResult = tOptional(tObject({})); + scheme.FrameDblclickParams = tObject({ + selector: tString, + strict: tOptional(tBoolean), + force: tOptional(tBoolean), + modifiers: tOptional(tArray(tEnum(["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]))), + position: tOptional(tType("Point")), + delay: tOptional(tFloat), + button: tOptional(tEnum(["left", "right", "middle"])), + timeout: tFloat, + trial: tOptional(tBoolean), + steps: tOptional(tInt) + }); + scheme.FrameDblclickResult = tOptional(tObject({})); + scheme.FrameDispatchEventParams = tObject({ + selector: tString, + strict: tOptional(tBoolean), + type: tString, + eventInit: tType("SerializedArgument"), + timeout: tFloat + }); + scheme.FrameDispatchEventResult = tOptional(tObject({})); + scheme.FrameEvaluateExpressionParams = tObject({ + expression: tString, + isFunction: tOptional(tBoolean), + arg: tType("SerializedArgument") + }); + scheme.FrameEvaluateExpressionResult = tObject({ + value: tType("SerializedValue") + }); + scheme.FrameEvaluateExpressionHandleParams = tObject({ + expression: tString, + isFunction: tOptional(tBoolean), + arg: tType("SerializedArgument") + }); + scheme.FrameEvaluateExpressionHandleResult = tObject({ + handle: tChannel(["ElementHandle", "JSHandle"]) + }); + scheme.FrameFillParams = tObject({ + selector: tString, + strict: tOptional(tBoolean), + value: tString, + force: tOptional(tBoolean), + timeout: tFloat + }); + scheme.FrameFillResult = tOptional(tObject({})); + scheme.FrameFocusParams = tObject({ + selector: tString, + strict: tOptional(tBoolean), + timeout: tFloat + }); + scheme.FrameFocusResult = tOptional(tObject({})); + scheme.FrameFrameElementParams = tOptional(tObject({})); + scheme.FrameFrameElementResult = tObject({ + element: tChannel(["ElementHandle"]) + }); + scheme.FrameResolveSelectorParams = tObject({ + selector: tString + }); + scheme.FrameResolveSelectorResult = tObject({ + resolvedSelector: tString + }); + scheme.FrameHighlightParams = tObject({ + selector: tString, + style: tOptional(tString) + }); + scheme.FrameHighlightResult = tOptional(tObject({})); + scheme.FrameHideHighlightParams = tObject({ + selector: tString + }); + scheme.FrameHideHighlightResult = tOptional(tObject({})); + scheme.FrameGetAttributeParams = tObject({ + selector: tString, + strict: tOptional(tBoolean), + name: tString, + timeout: tFloat + }); + scheme.FrameGetAttributeResult = tObject({ + value: tOptional(tString) + }); + scheme.FrameGotoParams = tObject({ + url: tString, + timeout: tFloat, + waitUntil: tOptional(tType("LifecycleEvent")), + referer: tOptional(tString) + }); + scheme.FrameGotoResult = tObject({ + response: tOptional(tChannel(["Response"])) + }); + scheme.FrameHoverParams = tObject({ + selector: tString, + strict: tOptional(tBoolean), + force: tOptional(tBoolean), + modifiers: tOptional(tArray(tEnum(["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]))), + position: tOptional(tType("Point")), + timeout: tFloat, + trial: tOptional(tBoolean) + }); + scheme.FrameHoverResult = tOptional(tObject({})); + scheme.FrameInnerHTMLParams = tObject({ + selector: tString, + strict: tOptional(tBoolean), + timeout: tFloat + }); + scheme.FrameInnerHTMLResult = tObject({ + value: tString + }); + scheme.FrameInnerTextParams = tObject({ + selector: tString, + strict: tOptional(tBoolean), + timeout: tFloat + }); + scheme.FrameInnerTextResult = tObject({ + value: tString + }); + scheme.FrameInputValueParams = tObject({ + selector: tString, + strict: tOptional(tBoolean), + timeout: tFloat + }); + scheme.FrameInputValueResult = tObject({ + value: tString + }); + scheme.FrameIsCheckedParams = tObject({ + selector: tString, + strict: tOptional(tBoolean), + timeout: tFloat + }); + scheme.FrameIsCheckedResult = tObject({ + value: tBoolean + }); + scheme.FrameIsDisabledParams = tObject({ + selector: tString, + strict: tOptional(tBoolean), + timeout: tFloat + }); + scheme.FrameIsDisabledResult = tObject({ + value: tBoolean + }); + scheme.FrameIsEnabledParams = tObject({ + selector: tString, + strict: tOptional(tBoolean), + timeout: tFloat + }); + scheme.FrameIsEnabledResult = tObject({ + value: tBoolean + }); + scheme.FrameIsHiddenParams = tObject({ + selector: tString, + strict: tOptional(tBoolean) + }); + scheme.FrameIsHiddenResult = tObject({ + value: tBoolean + }); + scheme.FrameIsVisibleParams = tObject({ + selector: tString, + strict: tOptional(tBoolean) + }); + scheme.FrameIsVisibleResult = tObject({ + value: tBoolean + }); + scheme.FrameIsEditableParams = tObject({ + selector: tString, + strict: tOptional(tBoolean), + timeout: tFloat + }); + scheme.FrameIsEditableResult = tObject({ + value: tBoolean + }); + scheme.FramePressParams = tObject({ + selector: tString, + strict: tOptional(tBoolean), + key: tString, + delay: tOptional(tFloat), + noWaitAfter: tOptional(tBoolean), + timeout: tFloat + }); + scheme.FramePressResult = tOptional(tObject({})); + scheme.FrameQuerySelectorParams = tObject({ + selector: tString, + strict: tOptional(tBoolean) + }); + scheme.FrameQuerySelectorResult = tObject({ + element: tOptional(tChannel(["ElementHandle"])) + }); + scheme.FrameQuerySelectorAllParams = tObject({ + selector: tString + }); + scheme.FrameQuerySelectorAllResult = tObject({ + elements: tArray(tChannel(["ElementHandle"])) + }); + scheme.FrameQueryCountParams = tObject({ + selector: tString + }); + scheme.FrameQueryCountResult = tObject({ + value: tInt + }); + scheme.FrameSelectOptionParams = tObject({ + selector: tString, + strict: tOptional(tBoolean), + elements: tOptional(tArray(tChannel(["ElementHandle"]))), + options: tOptional(tArray(tObject({ + valueOrLabel: tOptional(tString), + value: tOptional(tString), + label: tOptional(tString), + index: tOptional(tInt) + }))), + force: tOptional(tBoolean), + timeout: tFloat + }); + scheme.FrameSelectOptionResult = tObject({ + values: tArray(tString) + }); + scheme.FrameSetContentParams = tObject({ + html: tString, + timeout: tFloat, + waitUntil: tOptional(tType("LifecycleEvent")) + }); + scheme.FrameSetContentResult = tOptional(tObject({})); + scheme.FrameSetInputFilesParams = tObject({ + selector: tString, + strict: tOptional(tBoolean), + payloads: tOptional(tArray(tObject({ + name: tString, + mimeType: tOptional(tString), + buffer: tBinary + }))), + localDirectory: tOptional(tString), + directoryStream: tOptional(tChannel(["WritableStream"])), + localPaths: tOptional(tArray(tString)), + streams: tOptional(tArray(tChannel(["WritableStream"]))), + timeout: tFloat + }); + scheme.FrameSetInputFilesResult = tOptional(tObject({})); + scheme.FrameTapParams = tObject({ + selector: tString, + strict: tOptional(tBoolean), + force: tOptional(tBoolean), + modifiers: tOptional(tArray(tEnum(["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]))), + position: tOptional(tType("Point")), + timeout: tFloat, + trial: tOptional(tBoolean) + }); + scheme.FrameTapResult = tOptional(tObject({})); + scheme.FrameTextContentParams = tObject({ + selector: tString, + strict: tOptional(tBoolean), + timeout: tFloat + }); + scheme.FrameTextContentResult = tObject({ + value: tOptional(tString) + }); + scheme.FrameTitleParams = tOptional(tObject({})); + scheme.FrameTitleResult = tObject({ + value: tString + }); + scheme.FrameTypeParams = tObject({ + selector: tString, + strict: tOptional(tBoolean), + text: tString, + delay: tOptional(tFloat), + timeout: tFloat + }); + scheme.FrameTypeResult = tOptional(tObject({})); + scheme.FrameUncheckParams = tObject({ + selector: tString, + strict: tOptional(tBoolean), + force: tOptional(tBoolean), + position: tOptional(tType("Point")), + timeout: tFloat, + trial: tOptional(tBoolean) + }); + scheme.FrameUncheckResult = tOptional(tObject({})); + scheme.FrameWaitForTimeoutParams = tObject({ + waitTimeout: tFloat + }); + scheme.FrameWaitForTimeoutResult = tOptional(tObject({})); + scheme.FrameWaitForFunctionParams = tObject({ + expression: tString, + isFunction: tOptional(tBoolean), + arg: tType("SerializedArgument"), + timeout: tFloat, + pollingInterval: tOptional(tFloat) + }); + scheme.FrameWaitForFunctionResult = tObject({ + handle: tChannel(["ElementHandle", "JSHandle"]) + }); + scheme.FrameWaitForSelectorParams = tObject({ + selector: tString, + strict: tOptional(tBoolean), + timeout: tFloat, + state: tOptional(tEnum(["attached", "detached", "visible", "hidden"])), + omitReturnValue: tOptional(tBoolean) + }); + scheme.FrameWaitForSelectorResult = tObject({ + element: tOptional(tChannel(["ElementHandle"])) + }); + scheme.FrameExpectParams = tObject({ + selector: tOptional(tString), + expression: tString, + expressionArg: tOptional(tAny), + pseudo: tOptional(tEnum(["before", "after"])), + expectedText: tOptional(tArray(tType("ExpectedTextValue"))), + expectedNumber: tOptional(tFloat), + expectedValue: tOptional(tType("SerializedArgument")), + useInnerText: tOptional(tBoolean), + isNot: tBoolean, + timeout: tFloat + }); + scheme.FrameExpectResult = tOptional(tObject({})); + scheme.FrameExpectErrorDetails = tObject({ + received: tOptional(tObject({ + value: tOptional(tType("SerializedValue")), + ariaSnapshot: tOptional(tString) + })), + timedOut: tOptional(tBoolean), + customErrorMessage: tOptional(tString) + }); + scheme.JSHandleInitializer = tObject({ + preview: tString + }); + scheme.JSHandlePreviewUpdatedEvent = tObject({ + preview: tString + }); + scheme.ElementHandlePreviewUpdatedEvent = tType("JSHandlePreviewUpdatedEvent"); + scheme.JSHandleDisposeParams = tOptional(tObject({})); + scheme.ElementHandleDisposeParams = tType("JSHandleDisposeParams"); + scheme.JSHandleDisposeResult = tOptional(tObject({})); + scheme.ElementHandleDisposeResult = tType("JSHandleDisposeResult"); + scheme.JSHandleEvaluateExpressionParams = tObject({ + expression: tString, + isFunction: tOptional(tBoolean), + arg: tType("SerializedArgument") + }); + scheme.ElementHandleEvaluateExpressionParams = tType("JSHandleEvaluateExpressionParams"); + scheme.JSHandleEvaluateExpressionResult = tObject({ + value: tType("SerializedValue") + }); + scheme.ElementHandleEvaluateExpressionResult = tType("JSHandleEvaluateExpressionResult"); + scheme.JSHandleEvaluateExpressionHandleParams = tObject({ + expression: tString, + isFunction: tOptional(tBoolean), + arg: tType("SerializedArgument") + }); + scheme.ElementHandleEvaluateExpressionHandleParams = tType("JSHandleEvaluateExpressionHandleParams"); + scheme.JSHandleEvaluateExpressionHandleResult = tObject({ + handle: tChannel(["ElementHandle", "JSHandle"]) + }); + scheme.ElementHandleEvaluateExpressionHandleResult = tType("JSHandleEvaluateExpressionHandleResult"); + scheme.JSHandleGetPropertyListParams = tOptional(tObject({})); + scheme.ElementHandleGetPropertyListParams = tType("JSHandleGetPropertyListParams"); + scheme.JSHandleGetPropertyListResult = tObject({ + properties: tArray(tObject({ + name: tString, + value: tChannel(["ElementHandle", "JSHandle"]) + })) + }); + scheme.ElementHandleGetPropertyListResult = tType("JSHandleGetPropertyListResult"); + scheme.JSHandleGetPropertyParams = tObject({ + name: tString + }); + scheme.ElementHandleGetPropertyParams = tType("JSHandleGetPropertyParams"); + scheme.JSHandleGetPropertyResult = tObject({ + handle: tChannel(["ElementHandle", "JSHandle"]) + }); + scheme.ElementHandleGetPropertyResult = tType("JSHandleGetPropertyResult"); + scheme.JSHandleJsonValueParams = tOptional(tObject({})); + scheme.ElementHandleJsonValueParams = tType("JSHandleJsonValueParams"); + scheme.JSHandleJsonValueResult = tObject({ + value: tType("SerializedValue") + }); + scheme.ElementHandleJsonValueResult = tType("JSHandleJsonValueResult"); + scheme.ElementHandleInitializer = tObject({ + preview: tString + }); + scheme.ElementHandleEvalOnSelectorParams = tObject({ + selector: tString, + strict: tOptional(tBoolean), + expression: tString, + isFunction: tOptional(tBoolean), + arg: tType("SerializedArgument") + }); + scheme.ElementHandleEvalOnSelectorResult = tObject({ + value: tType("SerializedValue") + }); + scheme.ElementHandleEvalOnSelectorAllParams = tObject({ + selector: tString, + expression: tString, + isFunction: tOptional(tBoolean), + arg: tType("SerializedArgument") + }); + scheme.ElementHandleEvalOnSelectorAllResult = tObject({ + value: tType("SerializedValue") + }); + scheme.ElementHandleBoundingBoxParams = tOptional(tObject({})); + scheme.ElementHandleBoundingBoxResult = tObject({ + value: tOptional(tType("Rect")) + }); + scheme.ElementHandleCheckParams = tObject({ + force: tOptional(tBoolean), + position: tOptional(tType("Point")), + timeout: tFloat, + trial: tOptional(tBoolean) + }); + scheme.ElementHandleCheckResult = tOptional(tObject({})); + scheme.ElementHandleClickParams = tObject({ + force: tOptional(tBoolean), + noWaitAfter: tOptional(tBoolean), + modifiers: tOptional(tArray(tEnum(["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]))), + position: tOptional(tType("Point")), + delay: tOptional(tFloat), + button: tOptional(tEnum(["left", "right", "middle"])), + clickCount: tOptional(tInt), + timeout: tFloat, + trial: tOptional(tBoolean), + steps: tOptional(tInt) + }); + scheme.ElementHandleClickResult = tOptional(tObject({})); + scheme.ElementHandleContentFrameParams = tOptional(tObject({})); + scheme.ElementHandleContentFrameResult = tObject({ + frame: tOptional(tChannel(["Frame"])) + }); + scheme.ElementHandleDblclickParams = tObject({ + force: tOptional(tBoolean), + modifiers: tOptional(tArray(tEnum(["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]))), + position: tOptional(tType("Point")), + delay: tOptional(tFloat), + button: tOptional(tEnum(["left", "right", "middle"])), + timeout: tFloat, + trial: tOptional(tBoolean), + steps: tOptional(tInt) + }); + scheme.ElementHandleDblclickResult = tOptional(tObject({})); + scheme.ElementHandleDispatchEventParams = tObject({ + type: tString, + eventInit: tType("SerializedArgument") + }); + scheme.ElementHandleDispatchEventResult = tOptional(tObject({})); + scheme.ElementHandleFillParams = tObject({ + value: tString, + force: tOptional(tBoolean), + timeout: tFloat + }); + scheme.ElementHandleFillResult = tOptional(tObject({})); + scheme.ElementHandleFocusParams = tOptional(tObject({})); + scheme.ElementHandleFocusResult = tOptional(tObject({})); + scheme.ElementHandleGetAttributeParams = tObject({ + name: tString + }); + scheme.ElementHandleGetAttributeResult = tObject({ + value: tOptional(tString) + }); + scheme.ElementHandleHoverParams = tObject({ + force: tOptional(tBoolean), + modifiers: tOptional(tArray(tEnum(["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]))), + position: tOptional(tType("Point")), + timeout: tFloat, + trial: tOptional(tBoolean) + }); + scheme.ElementHandleHoverResult = tOptional(tObject({})); + scheme.ElementHandleInnerHTMLParams = tOptional(tObject({})); + scheme.ElementHandleInnerHTMLResult = tObject({ + value: tString + }); + scheme.ElementHandleInnerTextParams = tOptional(tObject({})); + scheme.ElementHandleInnerTextResult = tObject({ + value: tString + }); + scheme.ElementHandleInputValueParams = tOptional(tObject({})); + scheme.ElementHandleInputValueResult = tObject({ + value: tString + }); + scheme.ElementHandleIsCheckedParams = tOptional(tObject({})); + scheme.ElementHandleIsCheckedResult = tObject({ + value: tBoolean + }); + scheme.ElementHandleIsDisabledParams = tOptional(tObject({})); + scheme.ElementHandleIsDisabledResult = tObject({ + value: tBoolean + }); + scheme.ElementHandleIsEditableParams = tOptional(tObject({})); + scheme.ElementHandleIsEditableResult = tObject({ + value: tBoolean + }); + scheme.ElementHandleIsEnabledParams = tOptional(tObject({})); + scheme.ElementHandleIsEnabledResult = tObject({ + value: tBoolean + }); + scheme.ElementHandleIsHiddenParams = tOptional(tObject({})); + scheme.ElementHandleIsHiddenResult = tObject({ + value: tBoolean + }); + scheme.ElementHandleIsVisibleParams = tOptional(tObject({})); + scheme.ElementHandleIsVisibleResult = tObject({ + value: tBoolean + }); + scheme.ElementHandleOwnerFrameParams = tOptional(tObject({})); + scheme.ElementHandleOwnerFrameResult = tObject({ + frame: tOptional(tChannel(["Frame"])) + }); + scheme.ElementHandlePressParams = tObject({ + key: tString, + delay: tOptional(tFloat), + timeout: tFloat, + noWaitAfter: tOptional(tBoolean) + }); + scheme.ElementHandlePressResult = tOptional(tObject({})); + scheme.ElementHandleQuerySelectorParams = tObject({ + selector: tString, + strict: tOptional(tBoolean) + }); + scheme.ElementHandleQuerySelectorResult = tObject({ + element: tOptional(tChannel(["ElementHandle"])) + }); + scheme.ElementHandleQuerySelectorAllParams = tObject({ + selector: tString + }); + scheme.ElementHandleQuerySelectorAllResult = tObject({ + elements: tArray(tChannel(["ElementHandle"])) + }); + scheme.ElementHandleScreenshotParams = tObject({ + timeout: tFloat, + type: tOptional(tEnum(["png", "jpeg"])), + quality: tOptional(tInt), + omitBackground: tOptional(tBoolean), + caret: tOptional(tEnum(["hide", "initial"])), + animations: tOptional(tEnum(["disabled", "allow"])), + scale: tOptional(tEnum(["css", "device"])), + mask: tOptional(tArray(tObject({ + frame: tChannel(["Frame"]), + selector: tString + }))), + maskColor: tOptional(tString), + style: tOptional(tString) + }); + scheme.ElementHandleScreenshotResult = tObject({ + binary: tBinary + }); + scheme.ElementHandleScrollIntoViewIfNeededParams = tObject({ + timeout: tFloat + }); + scheme.ElementHandleScrollIntoViewIfNeededResult = tOptional(tObject({})); + scheme.ElementHandleSelectOptionParams = tObject({ + elements: tOptional(tArray(tChannel(["ElementHandle"]))), + options: tOptional(tArray(tObject({ + valueOrLabel: tOptional(tString), + value: tOptional(tString), + label: tOptional(tString), + index: tOptional(tInt) + }))), + force: tOptional(tBoolean), + timeout: tFloat + }); + scheme.ElementHandleSelectOptionResult = tObject({ + values: tArray(tString) + }); + scheme.ElementHandleSelectTextParams = tObject({ + force: tOptional(tBoolean), + timeout: tFloat + }); + scheme.ElementHandleSelectTextResult = tOptional(tObject({})); + scheme.ElementHandleSetInputFilesParams = tObject({ + payloads: tOptional(tArray(tObject({ + name: tString, + mimeType: tOptional(tString), + buffer: tBinary + }))), + localDirectory: tOptional(tString), + directoryStream: tOptional(tChannel(["WritableStream"])), + localPaths: tOptional(tArray(tString)), + streams: tOptional(tArray(tChannel(["WritableStream"]))), + timeout: tFloat + }); + scheme.ElementHandleSetInputFilesResult = tOptional(tObject({})); + scheme.ElementHandleTapParams = tObject({ + force: tOptional(tBoolean), + modifiers: tOptional(tArray(tEnum(["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]))), + position: tOptional(tType("Point")), + timeout: tFloat, + trial: tOptional(tBoolean) + }); + scheme.ElementHandleTapResult = tOptional(tObject({})); + scheme.ElementHandleTextContentParams = tOptional(tObject({})); + scheme.ElementHandleTextContentResult = tObject({ + value: tOptional(tString) + }); + scheme.ElementHandleTypeParams = tObject({ + text: tString, + delay: tOptional(tFloat), + timeout: tFloat + }); + scheme.ElementHandleTypeResult = tOptional(tObject({})); + scheme.ElementHandleUncheckParams = tObject({ + force: tOptional(tBoolean), + position: tOptional(tType("Point")), + timeout: tFloat, + trial: tOptional(tBoolean) + }); + scheme.ElementHandleUncheckResult = tOptional(tObject({})); + scheme.ElementHandleWaitForElementStateParams = tObject({ + state: tEnum(["visible", "hidden", "stable", "enabled", "disabled", "editable"]), + timeout: tFloat + }); + scheme.ElementHandleWaitForElementStateResult = tOptional(tObject({})); + scheme.ElementHandleWaitForSelectorParams = tObject({ + selector: tString, + strict: tOptional(tBoolean), + timeout: tFloat, + state: tOptional(tEnum(["attached", "detached", "visible", "hidden"])) + }); + scheme.ElementHandleWaitForSelectorResult = tObject({ + element: tOptional(tChannel(["ElementHandle"])) + }); + scheme.LocalUtilsInitializer = tObject({ + deviceDescriptors: tArray(tObject({ + name: tString, + descriptor: tObject({ + userAgent: tString, + viewport: tObject({ + width: tInt, + height: tInt + }), + screen: tOptional(tObject({ + width: tInt, + height: tInt + })), + deviceScaleFactor: tFloat, + isMobile: tBoolean, + hasTouch: tBoolean, + defaultBrowserType: tEnum(["chromium", "firefox", "webkit"]) + }) + })) + }); + scheme.LocalUtilsZipParams = tObject({ + zipFile: tString, + entries: tArray(tType("NameValue")), + stacksId: tOptional(tString), + mode: tEnum(["write", "append"]), + includeSources: tBoolean, + additionalSources: tOptional(tArray(tString)) + }); + scheme.LocalUtilsZipResult = tOptional(tObject({})); + scheme.LocalUtilsHarOpenParams = tObject({ + file: tString + }); + scheme.LocalUtilsHarOpenResult = tObject({ + harId: tOptional(tString), + error: tOptional(tString) + }); + scheme.LocalUtilsHarLookupParams = tObject({ + harId: tString, + url: tString, + method: tString, + headers: tArray(tType("NameValue")), + postData: tOptional(tBinary), + isNavigationRequest: tBoolean + }); + scheme.LocalUtilsHarLookupResult = tObject({ + action: tEnum(["error", "redirect", "fulfill", "noentry"]), + message: tOptional(tString), + redirectURL: tOptional(tString), + status: tOptional(tInt), + headers: tOptional(tArray(tType("NameValue"))), + body: tOptional(tBinary) + }); + scheme.LocalUtilsHarCloseParams = tObject({ + harId: tString + }); + scheme.LocalUtilsHarCloseResult = tOptional(tObject({})); + scheme.LocalUtilsHarUnzipParams = tObject({ + zipFile: tString, + harFile: tString, + resourcesDir: tOptional(tString) + }); + scheme.LocalUtilsHarUnzipResult = tOptional(tObject({})); + scheme.LocalUtilsConnectParams = tObject({ + endpoint: tString, + headers: tOptional(tAny), + exposeNetwork: tOptional(tString), + slowMo: tOptional(tFloat), + timeout: tFloat, + socksProxyRedirectPortForTest: tOptional(tInt) + }); + scheme.LocalUtilsConnectResult = tObject({ + pipe: tChannel(["JsonPipe"]), + headers: tArray(tType("NameValue")) + }); + scheme.LocalUtilsTracingStartedParams = tObject({ + tracesDir: tOptional(tString), + traceName: tString, + live: tOptional(tBoolean) + }); + scheme.LocalUtilsTracingStartedResult = tObject({ + stacksId: tString + }); + scheme.LocalUtilsAddStackToTracingNoReplyParams = tObject({ + callData: tType("ClientSideCallMetadata") + }); + scheme.LocalUtilsAddStackToTracingNoReplyResult = tOptional(tObject({})); + scheme.LocalUtilsTraceDiscardedParams = tObject({ + stacksId: tString + }); + scheme.LocalUtilsTraceDiscardedResult = tOptional(tObject({})); + scheme.LocalUtilsGlobToRegexParams = tObject({ + glob: tString, + baseURL: tOptional(tString), + webSocketUrl: tOptional(tBoolean) + }); + scheme.LocalUtilsGlobToRegexResult = tObject({ + regex: tString + }); + scheme.SetNetworkCookie = tObject({ + name: tString, + value: tString, + url: tOptional(tString), + domain: tOptional(tString), + path: tOptional(tString), + expires: tOptional(tFloat), + httpOnly: tOptional(tBoolean), + secure: tOptional(tBoolean), + sameSite: tOptional(tEnum(["Strict", "Lax", "None"])), + partitionKey: tOptional(tString), + _crHasCrossSiteAncestor: tOptional(tBoolean) + }); + scheme.NetworkCookie = tObject({ + name: tString, + value: tString, + domain: tString, + path: tString, + expires: tFloat, + httpOnly: tBoolean, + secure: tBoolean, + sameSite: tEnum(["Strict", "Lax", "None"]), + partitionKey: tOptional(tString), + _crHasCrossSiteAncestor: tOptional(tBoolean) + }); + scheme.RequestInitializer = tObject({ + frame: tOptional(tChannel(["Frame"])), + serviceWorker: tOptional(tChannel(["Worker"])), + url: tString, + resourceType: tString, + method: tString, + postData: tOptional(tBinary), + headers: tArray(tType("NameValue")), + isNavigationRequest: tBoolean, + redirectedFrom: tOptional(tChannel(["Request"])) + }); + scheme.RequestResponseParams = tOptional(tObject({})); + scheme.RequestResponseResult = tObject({ + response: tOptional(tChannel(["Response"])) + }); + scheme.RequestRawRequestHeadersParams = tOptional(tObject({})); + scheme.RequestRawRequestHeadersResult = tObject({ + headers: tArray(tType("NameValue")) + }); + scheme.RouteInitializer = tObject({ + request: tChannel(["Request"]) + }); + scheme.RouteRedirectNavigationRequestParams = tObject({ + url: tString + }); + scheme.RouteRedirectNavigationRequestResult = tOptional(tObject({})); + scheme.RouteAbortParams = tObject({ + errorCode: tOptional(tString) + }); + scheme.RouteAbortResult = tOptional(tObject({})); + scheme.RouteContinueParams = tObject({ + url: tOptional(tString), + method: tOptional(tString), + headers: tOptional(tArray(tType("NameValue"))), + postData: tOptional(tBinary), + isFallback: tBoolean + }); + scheme.RouteContinueResult = tOptional(tObject({})); + scheme.RouteFulfillParams = tObject({ + status: tOptional(tInt), + headers: tOptional(tArray(tType("NameValue"))), + body: tOptional(tString), + isBase64: tOptional(tBoolean), + fetchResponseUid: tOptional(tString) + }); + scheme.RouteFulfillResult = tOptional(tObject({})); + scheme.WebSocketRouteInitializer = tObject({ + url: tString, + protocols: tArray(tString) + }); + scheme.WebSocketRouteMessageFromPageEvent = tObject({ + message: tString, + isBase64: tBoolean + }); + scheme.WebSocketRouteMessageFromServerEvent = tObject({ + message: tString, + isBase64: tBoolean + }); + scheme.WebSocketRouteClosePageEvent = tObject({ + code: tOptional(tInt), + reason: tOptional(tString), + wasClean: tBoolean + }); + scheme.WebSocketRouteCloseServerEvent = tObject({ + code: tOptional(tInt), + reason: tOptional(tString), + wasClean: tBoolean + }); + scheme.WebSocketRouteConnectParams = tOptional(tObject({})); + scheme.WebSocketRouteConnectResult = tOptional(tObject({})); + scheme.WebSocketRouteEnsureOpenedParams = tOptional(tObject({})); + scheme.WebSocketRouteEnsureOpenedResult = tOptional(tObject({})); + scheme.WebSocketRouteSendToPageParams = tObject({ + message: tString, + isBase64: tBoolean + }); + scheme.WebSocketRouteSendToPageResult = tOptional(tObject({})); + scheme.WebSocketRouteSendToServerParams = tObject({ + message: tString, + isBase64: tBoolean + }); + scheme.WebSocketRouteSendToServerResult = tOptional(tObject({})); + scheme.WebSocketRouteClosePageParams = tObject({ + code: tOptional(tInt), + reason: tOptional(tString), + wasClean: tBoolean + }); + scheme.WebSocketRouteClosePageResult = tOptional(tObject({})); + scheme.WebSocketRouteCloseServerParams = tObject({ + code: tOptional(tInt), + reason: tOptional(tString), + wasClean: tBoolean + }); + scheme.WebSocketRouteCloseServerResult = tOptional(tObject({})); + scheme.ResourceTiming = tObject({ + startTime: tFloat, + domainLookupStart: tFloat, + domainLookupEnd: tFloat, + connectStart: tFloat, + secureConnectionStart: tFloat, + connectEnd: tFloat, + requestStart: tFloat, + responseStart: tFloat + }); + scheme.ResponseInitializer = tObject({ + request: tChannel(["Request"]), + url: tString, + status: tInt, + statusText: tString, + headers: tArray(tType("NameValue")), + timing: tType("ResourceTiming"), + fromServiceWorker: tBoolean + }); + scheme.ResponseBodyParams = tOptional(tObject({})); + scheme.ResponseBodyResult = tObject({ + binary: tBinary + }); + scheme.ResponseSecurityDetailsParams = tOptional(tObject({})); + scheme.ResponseSecurityDetailsResult = tObject({ + value: tOptional(tType("SecurityDetails")) + }); + scheme.ResponseServerAddrParams = tOptional(tObject({})); + scheme.ResponseServerAddrResult = tObject({ + value: tOptional(tType("RemoteAddr")) + }); + scheme.ResponseRawResponseHeadersParams = tOptional(tObject({})); + scheme.ResponseRawResponseHeadersResult = tObject({ + headers: tArray(tType("NameValue")) + }); + scheme.ResponseHttpVersionParams = tOptional(tObject({})); + scheme.ResponseHttpVersionResult = tObject({ + value: tString + }); + scheme.ResponseSizesParams = tOptional(tObject({})); + scheme.ResponseSizesResult = tObject({ + sizes: tType("RequestSizes") + }); + scheme.SecurityDetails = tObject({ + issuer: tOptional(tString), + protocol: tOptional(tString), + subjectName: tOptional(tString), + validFrom: tOptional(tFloat), + validTo: tOptional(tFloat) + }); + scheme.RequestSizes = tObject({ + requestBodySize: tInt, + requestHeadersSize: tInt, + responseBodySize: tInt, + responseHeadersSize: tInt + }); + scheme.RemoteAddr = tObject({ + ipAddress: tString, + port: tInt + }); + scheme.WebSocketInitializer = tObject({ + url: tString + }); + scheme.WebSocketOpenEvent = tOptional(tObject({})); + scheme.WebSocketFrameSentEvent = tObject({ + opcode: tInt, + data: tString + }); + scheme.WebSocketFrameReceivedEvent = tObject({ + opcode: tInt, + data: tString + }); + scheme.WebSocketSocketErrorEvent = tObject({ + error: tString + }); + scheme.WebSocketCloseEvent = tOptional(tObject({})); + scheme.PageInitializer = tObject({ + mainFrame: tChannel(["Frame"]), + viewportSize: tOptional(tObject({ + width: tInt, + height: tInt + })), + isClosed: tBoolean, + opener: tOptional(tChannel(["Page"])), + video: tOptional(tChannel(["Artifact"])) + }); + scheme.PageBindingCallEvent = tObject({ + binding: tChannel(["BindingCall"]) + }); + scheme.PageCloseEvent = tOptional(tObject({})); + scheme.PageCrashEvent = tOptional(tObject({})); + scheme.PageDownloadEvent = tObject({ + url: tString, + suggestedFilename: tString, + artifact: tChannel(["Artifact"]) + }); + scheme.PageViewportSizeChangedEvent = tObject({ + viewportSize: tOptional(tObject({ + width: tInt, + height: tInt + })) + }); + scheme.PageFileChooserEvent = tObject({ + element: tChannel(["ElementHandle"]), + isMultiple: tBoolean + }); + scheme.PageFrameAttachedEvent = tObject({ + frame: tChannel(["Frame"]) + }); + scheme.PageFrameDetachedEvent = tObject({ + frame: tChannel(["Frame"]) + }); + scheme.PageLocatorHandlerTriggeredEvent = tObject({ + uid: tInt + }); + scheme.PageRouteEvent = tObject({ + route: tChannel(["Route"]) + }); + scheme.PageScreencastFrameEvent = tObject({ + data: tBinary, + timestamp: tFloat, + viewportWidth: tInt, + viewportHeight: tInt + }); + scheme.PageWebSocketRouteEvent = tObject({ + webSocketRoute: tChannel(["WebSocketRoute"]) + }); + scheme.PageWebSocketEvent = tObject({ + webSocket: tChannel(["WebSocket"]) + }); + scheme.PageWorkerEvent = tObject({ + worker: tChannel(["Worker"]) + }); + scheme.PageAddInitScriptParams = tObject({ + source: tString + }); + scheme.PageAddInitScriptResult = tObject({ + disposable: tChannel(["Disposable"]) + }); + scheme.PageCloseParams = tObject({ + reason: tOptional(tString) + }); + scheme.PageCloseResult = tOptional(tObject({})); + scheme.PageRunBeforeUnloadParams = tOptional(tObject({})); + scheme.PageRunBeforeUnloadResult = tOptional(tObject({})); + scheme.PageClearConsoleMessagesParams = tOptional(tObject({})); + scheme.PageClearConsoleMessagesResult = tOptional(tObject({})); + scheme.PageConsoleMessagesParams = tObject({ + filter: tOptional(tType("ConsoleMessagesFilter")) + }); + scheme.PageConsoleMessagesResult = tObject({ + messages: tArray(tObject({ + type: tString, + text: tString, + args: tArray(tChannel(["ElementHandle", "JSHandle"])), + location: tObject({ + url: tString, + lineNumber: tInt, + columnNumber: tInt + }), + timestamp: tFloat + })) + }); + scheme.PageEmulateMediaParams = tObject({ + media: tOptional(tEnum(["screen", "print", "no-override"])), + colorScheme: tOptional(tEnum(["dark", "light", "no-preference", "no-override"])), + reducedMotion: tOptional(tEnum(["reduce", "no-preference", "no-override"])), + forcedColors: tOptional(tEnum(["active", "none", "no-override"])), + contrast: tOptional(tEnum(["no-preference", "more", "no-override"])) + }); + scheme.PageEmulateMediaResult = tOptional(tObject({})); + scheme.PageExposeBindingParams = tObject({ + name: tString + }); + scheme.PageExposeBindingResult = tObject({ + disposable: tChannel(["Disposable"]) + }); + scheme.PageGoBackParams = tObject({ + timeout: tFloat, + waitUntil: tOptional(tType("LifecycleEvent")) + }); + scheme.PageGoBackResult = tObject({ + response: tOptional(tChannel(["Response"])) + }); + scheme.PageGoForwardParams = tObject({ + timeout: tFloat, + waitUntil: tOptional(tType("LifecycleEvent")) + }); + scheme.PageGoForwardResult = tObject({ + response: tOptional(tChannel(["Response"])) + }); + scheme.PageRequestGCParams = tOptional(tObject({})); + scheme.PageRequestGCResult = tOptional(tObject({})); + scheme.PageRegisterLocatorHandlerParams = tObject({ + selector: tString, + noWaitAfter: tOptional(tBoolean) + }); + scheme.PageRegisterLocatorHandlerResult = tObject({ + uid: tInt + }); + scheme.PageResolveLocatorHandlerNoReplyParams = tObject({ + uid: tInt, + remove: tOptional(tBoolean) + }); + scheme.PageResolveLocatorHandlerNoReplyResult = tOptional(tObject({})); + scheme.PageUnregisterLocatorHandlerParams = tObject({ + uid: tInt + }); + scheme.PageUnregisterLocatorHandlerResult = tOptional(tObject({})); + scheme.PageReloadParams = tObject({ + timeout: tFloat, + waitUntil: tOptional(tType("LifecycleEvent")) + }); + scheme.PageReloadResult = tObject({ + response: tOptional(tChannel(["Response"])) + }); + scheme.PageExpectScreenshotParams = tObject({ + expected: tOptional(tBinary), + timeout: tFloat, + isNot: tBoolean, + locator: tOptional(tObject({ + frame: tChannel(["Frame"]), + selector: tString + })), + comparator: tOptional(tString), + maxDiffPixels: tOptional(tInt), + maxDiffPixelRatio: tOptional(tFloat), + threshold: tOptional(tFloat), + fullPage: tOptional(tBoolean), + clip: tOptional(tType("Rect")), + omitBackground: tOptional(tBoolean), + caret: tOptional(tEnum(["hide", "initial"])), + animations: tOptional(tEnum(["disabled", "allow"])), + scale: tOptional(tEnum(["css", "device"])), + mask: tOptional(tArray(tObject({ + frame: tChannel(["Frame"]), + selector: tString + }))), + maskColor: tOptional(tString), + style: tOptional(tString) + }); + scheme.PageExpectScreenshotResult = tObject({ + actual: tOptional(tBinary) + }); + scheme.PageExpectScreenshotErrorDetails = tObject({ + diff: tOptional(tBinary), + customErrorMessage: tOptional(tString), + actual: tOptional(tBinary), + previous: tOptional(tBinary), + timedOut: tOptional(tBoolean), + log: tOptional(tArray(tString)) + }); + scheme.PageScreenshotParams = tObject({ + timeout: tFloat, + type: tOptional(tEnum(["png", "jpeg"])), + quality: tOptional(tInt), + fullPage: tOptional(tBoolean), + clip: tOptional(tType("Rect")), + omitBackground: tOptional(tBoolean), + caret: tOptional(tEnum(["hide", "initial"])), + animations: tOptional(tEnum(["disabled", "allow"])), + scale: tOptional(tEnum(["css", "device"])), + mask: tOptional(tArray(tObject({ + frame: tChannel(["Frame"]), + selector: tString + }))), + maskColor: tOptional(tString), + style: tOptional(tString) + }); + scheme.PageScreenshotResult = tObject({ + binary: tBinary + }); + scheme.PageSetExtraHTTPHeadersParams = tObject({ + headers: tArray(tType("NameValue")) + }); + scheme.PageSetExtraHTTPHeadersResult = tOptional(tObject({})); + scheme.PageSetNetworkInterceptionPatternsParams = tObject({ + patterns: tArray(tObject({ + glob: tOptional(tString), + regexSource: tOptional(tString), + regexFlags: tOptional(tString), + urlPattern: tOptional(tType("URLPattern")) + })) + }); + scheme.PageSetNetworkInterceptionPatternsResult = tOptional(tObject({})); + scheme.PageSetWebSocketInterceptionPatternsParams = tObject({ + patterns: tArray(tObject({ + glob: tOptional(tString), + regexSource: tOptional(tString), + regexFlags: tOptional(tString), + urlPattern: tOptional(tType("URLPattern")) + })) + }); + scheme.PageSetWebSocketInterceptionPatternsResult = tOptional(tObject({})); + scheme.PageSetViewportSizeParams = tObject({ + viewportSize: tObject({ + width: tInt, + height: tInt + }) + }); + scheme.PageSetViewportSizeResult = tOptional(tObject({})); + scheme.PageKeyboardDownParams = tObject({ + key: tString + }); + scheme.PageKeyboardDownResult = tOptional(tObject({})); + scheme.PageKeyboardUpParams = tObject({ + key: tString + }); + scheme.PageKeyboardUpResult = tOptional(tObject({})); + scheme.PageKeyboardInsertTextParams = tObject({ + text: tString + }); + scheme.PageKeyboardInsertTextResult = tOptional(tObject({})); + scheme.PageKeyboardTypeParams = tObject({ + text: tString, + delay: tOptional(tFloat) + }); + scheme.PageKeyboardTypeResult = tOptional(tObject({})); + scheme.PageKeyboardPressParams = tObject({ + key: tString, + delay: tOptional(tFloat) + }); + scheme.PageKeyboardPressResult = tOptional(tObject({})); + scheme.PageMouseMoveParams = tObject({ + x: tFloat, + y: tFloat, + steps: tOptional(tInt) + }); + scheme.PageMouseMoveResult = tOptional(tObject({})); + scheme.PageMouseDownParams = tObject({ + button: tOptional(tEnum(["left", "right", "middle"])), + clickCount: tOptional(tInt) + }); + scheme.PageMouseDownResult = tOptional(tObject({})); + scheme.PageMouseUpParams = tObject({ + button: tOptional(tEnum(["left", "right", "middle"])), + clickCount: tOptional(tInt) + }); + scheme.PageMouseUpResult = tOptional(tObject({})); + scheme.PageMouseClickParams = tObject({ + x: tFloat, + y: tFloat, + delay: tOptional(tFloat), + button: tOptional(tEnum(["left", "right", "middle"])), + clickCount: tOptional(tInt) + }); + scheme.PageMouseClickResult = tOptional(tObject({})); + scheme.PageMouseWheelParams = tObject({ + deltaX: tFloat, + deltaY: tFloat + }); + scheme.PageMouseWheelResult = tOptional(tObject({})); + scheme.PageTouchscreenTapParams = tObject({ + x: tFloat, + y: tFloat + }); + scheme.PageTouchscreenTapResult = tOptional(tObject({})); + scheme.PageClearPageErrorsParams = tOptional(tObject({})); + scheme.PageClearPageErrorsResult = tOptional(tObject({})); + scheme.PagePageErrorsParams = tObject({ + filter: tOptional(tType("ConsoleMessagesFilter")) + }); + scheme.PagePageErrorsResult = tObject({ + errors: tArray(tType("SerializedError")) + }); + scheme.PagePdfParams = tObject({ + scale: tOptional(tFloat), + displayHeaderFooter: tOptional(tBoolean), + headerTemplate: tOptional(tString), + footerTemplate: tOptional(tString), + printBackground: tOptional(tBoolean), + landscape: tOptional(tBoolean), + pageRanges: tOptional(tString), + format: tOptional(tString), + width: tOptional(tString), + height: tOptional(tString), + preferCSSPageSize: tOptional(tBoolean), + margin: tOptional(tObject({ + top: tOptional(tString), + bottom: tOptional(tString), + left: tOptional(tString), + right: tOptional(tString) + })), + tagged: tOptional(tBoolean), + outline: tOptional(tBoolean) + }); + scheme.PagePdfResult = tObject({ + pdf: tBinary + }); + scheme.PageRequestsParams = tOptional(tObject({})); + scheme.PageRequestsResult = tObject({ + requests: tArray(tChannel(["Request"])) + }); + scheme.PageStartJSCoverageParams = tObject({ + resetOnNavigation: tOptional(tBoolean), + reportAnonymousScripts: tOptional(tBoolean) + }); + scheme.PageStartJSCoverageResult = tOptional(tObject({})); + scheme.PageStopJSCoverageParams = tOptional(tObject({})); + scheme.PageStopJSCoverageResult = tObject({ + entries: tArray(tObject({ + url: tString, + scriptId: tString, + source: tOptional(tString), + functions: tArray(tObject({ + functionName: tString, + isBlockCoverage: tBoolean, + ranges: tArray(tObject({ + startOffset: tInt, + endOffset: tInt, + count: tInt + })) + })) + })) + }); + scheme.PageStartCSSCoverageParams = tObject({ + resetOnNavigation: tOptional(tBoolean) + }); + scheme.PageStartCSSCoverageResult = tOptional(tObject({})); + scheme.PageStopCSSCoverageParams = tOptional(tObject({})); + scheme.PageStopCSSCoverageResult = tObject({ + entries: tArray(tObject({ + url: tString, + text: tOptional(tString), + ranges: tArray(tObject({ + start: tInt, + end: tInt + })) + })) + }); + scheme.PageBringToFrontParams = tOptional(tObject({})); + scheme.PageBringToFrontResult = tOptional(tObject({})); + scheme.PagePickLocatorParams = tOptional(tObject({})); + scheme.PagePickLocatorResult = tObject({ + selector: tString + }); + scheme.PageCancelPickLocatorParams = tOptional(tObject({})); + scheme.PageCancelPickLocatorResult = tOptional(tObject({})); + scheme.PageHideHighlightParams = tOptional(tObject({})); + scheme.PageHideHighlightResult = tOptional(tObject({})); + scheme.PageScreencastShowOverlayParams = tObject({ + html: tString, + duration: tOptional(tFloat) + }); + scheme.PageScreencastShowOverlayResult = tObject({ + id: tString + }); + scheme.PageScreencastRemoveOverlayParams = tObject({ + id: tString + }); + scheme.PageScreencastRemoveOverlayResult = tOptional(tObject({})); + scheme.PageScreencastChapterParams = tObject({ + title: tString, + description: tOptional(tString), + duration: tOptional(tFloat) + }); + scheme.PageScreencastChapterResult = tOptional(tObject({})); + scheme.PageScreencastSetOverlayVisibleParams = tObject({ + visible: tBoolean + }); + scheme.PageScreencastSetOverlayVisibleResult = tOptional(tObject({})); + scheme.PageScreencastShowActionsParams = tObject({ + duration: tOptional(tFloat), + position: tOptional(tEnum(["top-left", "top", "top-right", "bottom-left", "bottom", "bottom-right"])), + fontSize: tOptional(tInt), + cursor: tOptional(tEnum(["none", "pointer"])) + }); + scheme.PageScreencastShowActionsResult = tOptional(tObject({})); + scheme.PageScreencastHideActionsParams = tOptional(tObject({})); + scheme.PageScreencastHideActionsResult = tOptional(tObject({})); + scheme.PageScreencastStartParams = tObject({ + size: tOptional(tObject({ + width: tInt, + height: tInt + })), + quality: tOptional(tInt), + sendFrames: tOptional(tBoolean), + record: tOptional(tBoolean) + }); + scheme.PageScreencastStartResult = tObject({ + artifact: tOptional(tChannel(["Artifact"])) + }); + scheme.PageScreencastStopParams = tOptional(tObject({})); + scheme.PageScreencastStopResult = tOptional(tObject({})); + scheme.PageUpdateSubscriptionParams = tObject({ + event: tEnum(["console", "dialog", "fileChooser", "request", "response", "requestFinished", "requestFailed"]), + enabled: tBoolean + }); + scheme.PageUpdateSubscriptionResult = tOptional(tObject({})); + scheme.PageSetDockTileParams = tObject({ + image: tBinary + }); + scheme.PageSetDockTileResult = tOptional(tObject({})); + scheme.PageWebStorageItemsParams = tObject({ + kind: tEnum(["local", "session"]) + }); + scheme.PageWebStorageItemsResult = tObject({ + items: tArray(tType("NameValue")) + }); + scheme.PageWebStorageGetItemParams = tObject({ + kind: tEnum(["local", "session"]), + name: tString + }); + scheme.PageWebStorageGetItemResult = tObject({ + value: tOptional(tString) + }); + scheme.PageWebStorageSetItemParams = tObject({ + kind: tEnum(["local", "session"]), + name: tString, + value: tString + }); + scheme.PageWebStorageSetItemResult = tOptional(tObject({})); + scheme.PageWebStorageRemoveItemParams = tObject({ + kind: tEnum(["local", "session"]), + name: tString + }); + scheme.PageWebStorageRemoveItemResult = tOptional(tObject({})); + scheme.PageWebStorageClearParams = tObject({ + kind: tEnum(["local", "session"]) + }); + scheme.PageWebStorageClearResult = tOptional(tObject({})); + scheme.RootInitializer = tOptional(tObject({})); + scheme.RootInitializeParams = tObject({ + sdkLanguage: tType("SDKLanguage") + }); + scheme.RootInitializeResult = tObject({ + playwright: tChannel(["Playwright"]) + }); + scheme.PlaywrightInitializer = tObject({ + chromium: tChannel(["BrowserType"]), + firefox: tChannel(["BrowserType"]), + webkit: tChannel(["BrowserType"]), + android: tChannel(["Android"]), + electron: tChannel(["Electron"]), + utils: tOptional(tChannel(["LocalUtils"])), + preLaunchedBrowser: tOptional(tChannel(["Browser"])), + preConnectedAndroidDevice: tOptional(tChannel(["AndroidDevice"])), + socksSupport: tOptional(tChannel(["SocksSupport"])) + }); + scheme.PlaywrightNewRequestParams = tObject({ + baseURL: tOptional(tString), + userAgent: tOptional(tString), + ignoreHTTPSErrors: tOptional(tBoolean), + extraHTTPHeaders: tOptional(tArray(tType("NameValue"))), + failOnStatusCode: tOptional(tBoolean), + clientCertificates: tOptional(tArray(tObject({ + origin: tString, + cert: tOptional(tBinary), + key: tOptional(tBinary), + passphrase: tOptional(tString), + pfx: tOptional(tBinary) + }))), + maxRedirects: tOptional(tInt), + httpCredentials: tOptional(tObject({ + username: tString, + password: tString, + origin: tOptional(tString), + send: tOptional(tEnum(["always", "unauthorized"])) + })), + proxy: tOptional(tObject({ + server: tString, + bypass: tOptional(tString), + username: tOptional(tString), + password: tOptional(tString) + })), + storageState: tOptional(tObject({ + cookies: tOptional(tArray(tType("NetworkCookie"))), + origins: tOptional(tArray(tType("SetOriginStorage"))) + })), + tracesDir: tOptional(tString) + }); + scheme.PlaywrightNewRequestResult = tObject({ + request: tChannel(["APIRequestContext"]) + }); + scheme.DebugControllerInitializer = tOptional(tObject({})); + scheme.DebugControllerInspectRequestedEvent = tObject({ + selector: tString, + locator: tString, + ariaSnapshot: tString + }); + scheme.DebugControllerSetModeRequestedEvent = tObject({ + mode: tString + }); + scheme.DebugControllerStateChangedEvent = tObject({ + pageCount: tInt + }); + scheme.DebugControllerSourceChangedEvent = tObject({ + text: tString, + header: tOptional(tString), + footer: tOptional(tString), + actions: tOptional(tArray(tString)) + }); + scheme.DebugControllerPausedEvent = tObject({ + paused: tBoolean + }); + scheme.DebugControllerInitializeParams = tObject({ + codegenId: tString, + sdkLanguage: tType("SDKLanguage") + }); + scheme.DebugControllerInitializeResult = tOptional(tObject({})); + scheme.DebugControllerSetReportStateChangedParams = tObject({ + enabled: tBoolean + }); + scheme.DebugControllerSetReportStateChangedResult = tOptional(tObject({})); + scheme.DebugControllerSetRecorderModeParams = tObject({ + mode: tEnum(["inspecting", "recording", "none"]), + testIdAttributeName: tOptional(tString), + generateAutoExpect: tOptional(tBoolean) + }); + scheme.DebugControllerSetRecorderModeResult = tOptional(tObject({})); + scheme.DebugControllerHighlightParams = tObject({ + selector: tOptional(tString), + ariaTemplate: tOptional(tString) + }); + scheme.DebugControllerHighlightResult = tOptional(tObject({})); + scheme.DebugControllerHideHighlightParams = tOptional(tObject({})); + scheme.DebugControllerHideHighlightResult = tOptional(tObject({})); + scheme.DebugControllerResumeParams = tOptional(tObject({})); + scheme.DebugControllerResumeResult = tOptional(tObject({})); + scheme.DebugControllerKillParams = tOptional(tObject({})); + scheme.DebugControllerKillResult = tOptional(tObject({})); + scheme.SocksSupportInitializer = tOptional(tObject({})); + scheme.SocksSupportSocksRequestedEvent = tObject({ + uid: tString, + host: tString, + port: tInt + }); + scheme.SocksSupportSocksDataEvent = tObject({ + uid: tString, + data: tBinary + }); + scheme.SocksSupportSocksClosedEvent = tObject({ + uid: tString + }); + scheme.SocksSupportSocksConnectedParams = tObject({ + uid: tString, + host: tString, + port: tInt + }); + scheme.SocksSupportSocksConnectedResult = tOptional(tObject({})); + scheme.SocksSupportSocksFailedParams = tObject({ + uid: tString, + errorCode: tString + }); + scheme.SocksSupportSocksFailedResult = tOptional(tObject({})); + scheme.SocksSupportSocksDataParams = tObject({ + uid: tString, + data: tBinary + }); + scheme.SocksSupportSocksDataResult = tOptional(tObject({})); + scheme.SocksSupportSocksErrorParams = tObject({ + uid: tString, + error: tString + }); + scheme.SocksSupportSocksErrorResult = tOptional(tObject({})); + scheme.SocksSupportSocksEndParams = tObject({ + uid: tString + }); + scheme.SocksSupportSocksEndResult = tOptional(tObject({})); + scheme.JsonPipeInitializer = tOptional(tObject({})); + scheme.JsonPipeMessageEvent = tObject({ + message: tAny + }); + scheme.JsonPipeClosedEvent = tObject({ + reason: tOptional(tString) + }); + scheme.JsonPipeSendParams = tObject({ + message: tAny + }); + scheme.JsonPipeSendResult = tOptional(tObject({})); + scheme.JsonPipeCloseParams = tOptional(tObject({})); + scheme.JsonPipeCloseResult = tOptional(tObject({})); + scheme.ExpectedTextValue = tObject({ + string: tOptional(tString), + regexSource: tOptional(tString), + regexFlags: tOptional(tString), + matchSubstring: tOptional(tBoolean), + ignoreCase: tOptional(tBoolean), + normalizeWhiteSpace: tOptional(tBoolean) + }); + scheme.SelectorEngine = tObject({ + name: tString, + source: tString, + contentScript: tOptional(tBoolean) + }); + scheme.FormField = tObject({ + name: tString, + value: tOptional(tString), + file: tOptional(tObject({ + name: tString, + mimeType: tOptional(tString), + buffer: tBinary + })) + }); + scheme.LifecycleEvent = tEnum(["load", "domcontentloaded", "networkidle", "commit"]); + scheme.ConsoleMessagesFilter = tEnum(["all", "since-navigation"]); + scheme.RecorderSource = tObject({ + isRecorded: tBoolean, + id: tString, + label: tString, + text: tString, + language: tString, + highlight: tArray(tObject({ + line: tInt, + type: tString + })), + revealLine: tOptional(tInt), + group: tOptional(tString) + }); + scheme.IndexedDBDatabase = tObject({ + name: tString, + version: tInt, + stores: tArray(tObject({ + name: tString, + autoIncrement: tBoolean, + keyPath: tOptional(tString), + keyPathArray: tOptional(tArray(tString)), + records: tArray(tObject({ + key: tOptional(tAny), + keyEncoded: tOptional(tAny), + value: tOptional(tAny), + valueEncoded: tOptional(tAny) + })), + indexes: tArray(tObject({ + name: tString, + keyPath: tOptional(tString), + keyPathArray: tOptional(tArray(tString)), + multiEntry: tBoolean, + unique: tBoolean + })) + })) + }); + scheme.SetOriginStorage = tObject({ + origin: tString, + localStorage: tArray(tType("NameValue")), + indexedDB: tOptional(tArray(tType("IndexedDBDatabase"))) + }); + scheme.OriginStorage = tObject({ + origin: tString, + localStorage: tArray(tType("NameValue")), + indexedDB: tOptional(tArray(tType("IndexedDBDatabase"))) + }); + scheme.RecordHarOptions = tObject({ + content: tOptional(tEnum(["embed", "attach", "omit"])), + mode: tOptional(tEnum(["full", "minimal"])), + urlGlob: tOptional(tString), + urlRegexSource: tOptional(tString), + urlRegexFlags: tOptional(tString), + harPath: tOptional(tString), + resourcesDir: tOptional(tString) + }); + scheme.CDPSessionInitializer = tOptional(tObject({})); + scheme.CDPSessionEventEvent = tObject({ + method: tString, + params: tOptional(tAny) + }); + scheme.CDPSessionCloseEvent = tOptional(tObject({})); + scheme.CDPSessionSendParams = tObject({ + method: tString, + params: tOptional(tAny) + }); + scheme.CDPSessionSendResult = tObject({ + result: tAny + }); + scheme.CDPSessionDetachParams = tOptional(tObject({})); + scheme.CDPSessionDetachResult = tOptional(tObject({})); + scheme.BindingCallInitializer = tObject({ + frame: tChannel(["Frame"]), + name: tString, + args: tArray(tType("SerializedValue")) + }); + scheme.BindingCallRejectParams = tObject({ + error: tType("SerializedError") + }); + scheme.BindingCallRejectResult = tOptional(tObject({})); + scheme.BindingCallResolveParams = tObject({ + result: tType("SerializedArgument") + }); + scheme.BindingCallResolveResult = tOptional(tObject({})); + scheme.DebuggerInitializer = tOptional(tObject({})); + scheme.DebuggerPausedStateChangedEvent = tObject({ + pausedDetails: tOptional(tObject({ + location: tObject({ + file: tString, + line: tOptional(tInt), + column: tOptional(tInt) + }), + title: tString, + stack: tOptional(tString) + })) + }); + scheme.DebuggerRequestPauseParams = tOptional(tObject({})); + scheme.DebuggerRequestPauseResult = tOptional(tObject({})); + scheme.DebuggerResumeParams = tOptional(tObject({})); + scheme.DebuggerResumeResult = tOptional(tObject({})); + scheme.DebuggerNextParams = tOptional(tObject({})); + scheme.DebuggerNextResult = tOptional(tObject({})); + scheme.DebuggerRunToParams = tObject({ + location: tObject({ + file: tString, + line: tOptional(tInt), + column: tOptional(tInt) + }) + }); + scheme.DebuggerRunToResult = tOptional(tObject({})); + scheme.DialogInitializer = tObject({ + page: tOptional(tChannel(["Page"])), + type: tString, + message: tString, + defaultValue: tString + }); + scheme.DialogAcceptParams = tObject({ + promptText: tOptional(tString) + }); + scheme.DialogAcceptResult = tOptional(tObject({})); + scheme.DialogDismissParams = tOptional(tObject({})); + scheme.DialogDismissResult = tOptional(tObject({})); + scheme.SerializedValue = tObject({ + n: tOptional(tFloat), + b: tOptional(tBoolean), + s: tOptional(tString), + v: tOptional(tEnum(["null", "undefined", "NaN", "Infinity", "-Infinity", "-0"])), + d: tOptional(tString), + u: tOptional(tString), + bi: tOptional(tString), + ta: tOptional(tObject({ + b: tBinary, + k: tEnum(["i8", "ui8", "ui8c", "i16", "ui16", "i32", "ui32", "f32", "f64", "bi64", "bui64"]) + })), + e: tOptional(tObject({ + m: tString, + n: tString, + s: tString + })), + r: tOptional(tObject({ + p: tString, + f: tString + })), + a: tOptional(tArray(tType("SerializedValue"))), + o: tOptional(tArray(tObject({ + k: tString, + v: tType("SerializedValue") + }))), + h: tOptional(tInt), + id: tOptional(tInt), + ref: tOptional(tInt) + }); + scheme.SerializedArgument = tObject({ + value: tType("SerializedValue"), + handles: tArray(tChannel("*")) + }); + scheme.SerializedError = tObject({ + error: tOptional(tObject({ + message: tString, + name: tString, + stack: tOptional(tString) + })), + value: tOptional(tType("SerializedValue")) + }); + scheme.StackFrame = tObject({ + file: tString, + line: tInt, + column: tInt, + function: tOptional(tString) + }); + scheme.VirtualCredential = tObject({ + id: tString, + rpId: tString, + userHandle: tString, + privateKey: tString, + publicKey: tString + }); + scheme.Point = tObject({ + x: tFloat, + y: tFloat + }); + scheme.Rect = tObject({ + x: tFloat, + y: tFloat, + width: tFloat, + height: tFloat + }); + scheme.URLPattern = tObject({ + hash: tString, + hostname: tString, + password: tString, + pathname: tString, + port: tString, + protocol: tString, + search: tString, + username: tString + }); + scheme.NameValue = tObject({ + name: tString, + value: tString + }); + scheme.TracingInitializer = tOptional(tObject({})); + scheme.TracingTracingStartParams = tObject({ + name: tOptional(tString), + snapshots: tOptional(tBoolean), + screenshots: tOptional(tBoolean), + live: tOptional(tBoolean) + }); + scheme.TracingTracingStartResult = tOptional(tObject({})); + scheme.TracingTracingStartChunkParams = tObject({ + name: tOptional(tString), + title: tOptional(tString) + }); + scheme.TracingTracingStartChunkResult = tObject({ + traceName: tString + }); + scheme.TracingTracingGroupParams = tObject({ + name: tString, + location: tOptional(tObject({ + file: tString, + line: tOptional(tInt), + column: tOptional(tInt) + })) + }); + scheme.TracingTracingGroupResult = tOptional(tObject({})); + scheme.TracingTracingGroupEndParams = tOptional(tObject({})); + scheme.TracingTracingGroupEndResult = tOptional(tObject({})); + scheme.TracingTracingStopChunkParams = tObject({ + mode: tEnum(["archive", "discard", "entries"]) + }); + scheme.TracingTracingStopChunkResult = tObject({ + artifact: tOptional(tChannel(["Artifact"])), + entries: tOptional(tArray(tType("NameValue"))) + }); + scheme.TracingTracingStopParams = tOptional(tObject({})); + scheme.TracingTracingStopResult = tOptional(tObject({})); + scheme.TracingHarStartParams = tObject({ + page: tOptional(tChannel(["Page"])), + options: tType("RecordHarOptions") + }); + scheme.TracingHarStartResult = tObject({ + harId: tString + }); + scheme.TracingHarExportParams = tObject({ + harId: tOptional(tString), + mode: tEnum(["archive", "entries"]) + }); + scheme.TracingHarExportResult = tObject({ + artifact: tOptional(tChannel(["Artifact"])), + entries: tOptional(tArray(tType("NameValue"))) + }); + scheme.WorkerInitializer = tObject({ + url: tString + }); + scheme.WorkerConsoleEvent = tObject({ + type: tString, + text: tString, + args: tArray(tChannel(["ElementHandle", "JSHandle"])), + location: tObject({ + url: tString, + lineNumber: tInt, + columnNumber: tInt + }), + timestamp: tFloat + }); + scheme.WorkerCloseEvent = tOptional(tObject({})); + scheme.WorkerDisconnectParams = tObject({ + reason: tOptional(tString) + }); + scheme.WorkerDisconnectResult = tOptional(tObject({})); + scheme.WorkerEvaluateExpressionParams = tObject({ + expression: tString, + isFunction: tOptional(tBoolean), + arg: tType("SerializedArgument") + }); + scheme.WorkerEvaluateExpressionResult = tObject({ + value: tType("SerializedValue") + }); + scheme.WorkerEvaluateExpressionHandleParams = tObject({ + expression: tString, + isFunction: tOptional(tBoolean), + arg: tType("SerializedArgument") + }); + scheme.WorkerEvaluateExpressionHandleResult = tObject({ + handle: tChannel(["ElementHandle", "JSHandle"]) + }); + scheme.WorkerUpdateSubscriptionParams = tObject({ + event: tEnum(["console"]), + enabled: tBoolean + }); + scheme.WorkerUpdateSubscriptionResult = tOptional(tObject({})); + } +}); + +// packages/playwright-core/src/server/protocolError.ts +function isProtocolError(e) { + return e instanceof ProtocolError; +} +function isSessionClosedError(e) { + return e instanceof ProtocolError && (e.type === "closed" || e.type === "crashed"); +} +var ProtocolError; +var init_protocolError = __esm({ + "packages/playwright-core/src/server/protocolError.ts"() { + "use strict"; + init_stackTrace(); + ProtocolError = class extends Error { + constructor(type3, method, logs) { + super(); + this.type = type3; + this.method = method; + this.logs = logs; + } + setMessage(message) { + rewriteErrorMessage(this, `Protocol error (${this.method}): ${message}`); + } + browserLogMessage() { + return this.logs ? "\nBrowser logs:\n" + this.logs : ""; + } + }; + } +}); + +// packages/playwright-core/src/server/callLog.ts +function compressCallLog(log2) { + const lines = []; + for (const block of findRepeatedSubsequences(log2)) { + for (let i = 0; i < block.sequence.length; i++) { + const line = block.sequence[i]; + const leadingWhitespace = line.match(/^\s*/); + const whitespacePrefix = " " + leadingWhitespace?.[0] || ""; + const countPrefix = `${block.count} \xD7 `; + if (block.count > 1 && i === 0) + lines.push(whitespacePrefix + countPrefix + line.trim()); + else if (block.count > 1) + lines.push(whitespacePrefix + " ".repeat(countPrefix.length - 2) + "- " + line.trim()); + else + lines.push(whitespacePrefix + "- " + line.trim()); + } + } + return lines; +} +function findRepeatedSubsequences(s) { + const n = s.length; + const result2 = []; + let i = 0; + const arraysEqual = (a1, a2) => { + if (a1.length !== a2.length) + return false; + for (let j = 0; j < a1.length; j++) { + if (a1[j] !== a2[j]) + return false; + } + return true; + }; + while (i < n) { + let maxRepeatCount = 1; + let maxRepeatSubstr = [s[i]]; + let maxRepeatLength = 1; + for (let p = 1; p <= n - i; p++) { + const substr = s.slice(i, i + p); + let k = 1; + while (i + p * k <= n && arraysEqual(s.slice(i + p * (k - 1), i + p * k), substr)) + k += 1; + k -= 1; + if (k > 1 && k * p > maxRepeatCount * maxRepeatLength) { + maxRepeatCount = k; + maxRepeatSubstr = substr; + maxRepeatLength = p; + } + } + result2.push({ sequence: maxRepeatSubstr, count: maxRepeatCount }); + i += maxRepeatLength * maxRepeatCount; + } + return result2; +} +var findRepeatedSubsequencesForTest; +var init_callLog = __esm({ + "packages/playwright-core/src/server/callLog.ts"() { + "use strict"; + findRepeatedSubsequencesForTest = findRepeatedSubsequences; + } +}); + +// packages/playwright-core/src/server/dispatchers/dispatcher.ts +function setMaxDispatchersForTest(value2) { + maxDispatchersOverride = value2; +} +function maxDispatchersForBucket(gcBucket) { + return maxDispatchersOverride ?? { + "JSHandle": 1e5, + "ElementHandle": 1e5 + }[gcBucket] ?? 1e4; +} +var import_events5, metadataValidator, waitInfoValidator, maxDispatchersOverride, Dispatcher, RootDispatcher, DispatcherConnection; +var init_dispatcher = __esm({ + "packages/playwright-core/src/server/dispatchers/dispatcher.ts"() { + "use strict"; + import_events5 = require("events"); + init_protocolMetainfo(); + init_eventsHelper(); + init_debug(); + init_assert(); + init_time(); + init_stackTrace(); + init_validator(); + init_errors(); + init_instrumentation(); + init_protocolError(); + init_callLog(); + init_progress(); + metadataValidator = createMetadataValidator(); + waitInfoValidator = createWaitInfoValidator(); + Dispatcher = class extends import_events5.EventEmitter { + constructor(parent, object, type3, initializer, gcBucket) { + super(); + this._dispatchers = /* @__PURE__ */ new Map(); + this._disposed = false; + this._eventListeners = []; + this._activeProgressControllers = /* @__PURE__ */ new Set(); + this.connection = parent instanceof DispatcherConnection ? parent : parent.connection; + this._parent = parent instanceof DispatcherConnection ? void 0 : parent; + const guid = object.guid; + this._guid = guid; + this._type = type3; + this._object = object; + this._gcBucket = gcBucket ?? type3; + this.connection.registerDispatcher(this); + if (this._parent) { + assert(!this._parent._dispatchers.has(guid)); + this._parent._dispatchers.set(guid, this); + } + if (this._parent) + this.connection.sendCreate(this._parent, type3, guid, initializer); + this.connection.maybeDisposeStaleDispatchers(this._gcBucket); + } + parentScope() { + return this._parent; + } + addObjectListener(eventName, handler) { + this._eventListeners.push(eventsHelper.addEventListener(this._object, eventName, handler)); + } + adopt(child) { + if (child._parent === this) + return; + const oldParent = child._parent; + oldParent._dispatchers.delete(child._guid); + this._dispatchers.set(child._guid, child); + child._parent = this; + this.connection.sendAdopt(this, child); + } + async _runCommand(callMetadata, method, validParams) { + const controller = ProgressController.createForSdkObject(this._object, callMetadata); + this._activeProgressControllers.add(controller); + try { + return await controller.run((progress2) => this[method](validParams, progress2), validParams?.timeout); + } finally { + this._activeProgressControllers.delete(controller); + } + } + _dispatchEvent(method, params2) { + if (this._disposed) { + if (isUnderTest()) + throw new Error(`${this._guid} is sending "${String(method)}" event after being disposed`); + return; + } + this.connection.sendEvent(this, method, params2); + } + _dispose(reason) { + this._disposeRecursively(new TargetClosedError(this._object.closeReason())); + this.connection.sendDispose(this, reason); + } + _onDispose() { + } + async stopPendingOperations(error) { + const controllers = []; + const collect = (dispatcher) => { + controllers.push(...dispatcher._activeProgressControllers); + for (const child of [...dispatcher._dispatchers.values()]) + collect(child); + }; + collect(this); + await Promise.all(controllers.map((controller) => controller.abort(error))); + } + _disposeRecursively(error) { + assert(!this._disposed, `${this._guid} is disposed more than once`); + this._onDispose(); + this._disposed = true; + eventsHelper.removeEventListeners(this._eventListeners); + this._parent?._dispatchers.delete(this._guid); + const list = this.connection._dispatchersByBucket.get(this._gcBucket); + list?.delete(this._guid); + this.connection._dispatcherByGuid.delete(this._guid); + this.connection._dispatcherByObject.delete(this._object); + for (const dispatcher of [...this._dispatchers.values()]) + dispatcher._disposeRecursively(error); + this._dispatchers.clear(); + } + _debugScopeState() { + return { + _guid: this._guid, + objects: Array.from(this._dispatchers.values()).map((o) => o._debugScopeState()) + }; + } + }; + RootDispatcher = class extends Dispatcher { + constructor(connection, createPlaywright2) { + super(connection, createRootSdkObject(), "Root", {}); + this.createPlaywright = createPlaywright2; + this._initialized = false; + } + async initialize(params2, progress2) { + assert(this.createPlaywright); + assert(!this._initialized); + this._initialized = true; + return { + playwright: await progress2.race(this.createPlaywright(this, params2)) + }; + } + }; + DispatcherConnection = class { + constructor(isInProcess) { + this._dispatcherByGuid = /* @__PURE__ */ new Map(); + this._dispatcherByObject = /* @__PURE__ */ new Map(); + this._dispatchersByBucket = /* @__PURE__ */ new Map(); + this.onmessage = (message) => { + }; + this._waitOperations = /* @__PURE__ */ new Map(); + this._isInProcess = !!isInProcess; + } + sendEvent(dispatcher, event, params2) { + const validator = findValidator(dispatcher._type, event, "Event"); + params2 = validator(params2, "", this._validatorToWireContext()); + this.onmessage({ guid: dispatcher._guid, method: event, params: params2 }); + } + sendCreate(parent, type3, guid, initializer) { + const validator = findValidator(type3, "", "Initializer"); + initializer = validator(initializer, "", this._validatorToWireContext()); + this.onmessage({ guid: parent._guid, method: "__create__", params: { type: type3, initializer, guid } }); + } + sendAdopt(parent, dispatcher) { + this.onmessage({ guid: parent._guid, method: "__adopt__", params: { guid: dispatcher._guid } }); + } + sendDispose(dispatcher, reason) { + this.onmessage({ guid: dispatcher._guid, method: "__dispose__", params: { reason } }); + } + _validatorToWireContext() { + return { + tChannelImpl: this._tChannelImplToWire.bind(this), + binary: this._isInProcess ? "buffer" : "toBase64", + isUnderTest + }; + } + _validatorFromWireContext() { + return { + tChannelImpl: this._tChannelImplFromWire.bind(this), + binary: this._isInProcess ? "buffer" : "fromBase64", + isUnderTest + }; + } + _tChannelImplFromWire(names, arg, path59, context2) { + if (arg && typeof arg === "object" && typeof arg.guid === "string") { + const guid = arg.guid; + const dispatcher = this._dispatcherByGuid.get(guid); + if (!dispatcher) + throw new ValidationError(`${path59}: no object with guid ${guid}`); + if (names !== "*" && !names.includes(dispatcher._type)) + throw new ValidationError(`${path59}: object with guid ${guid} has type ${dispatcher._type}, expected ${names.toString()}`); + return dispatcher; + } + throw new ValidationError(`${path59}: expected guid for ${names.toString()}`); + } + _tChannelImplToWire(names, arg, path59, context2) { + if (arg instanceof Dispatcher) { + if (names !== "*" && !names.includes(arg._type)) + throw new ValidationError(`${path59}: dispatcher with guid ${arg._guid} has type ${arg._type}, expected ${names.toString()}`); + return { guid: arg._guid }; + } + throw new ValidationError(`${path59}: expected dispatcher ${names.toString()}`); + } + existingDispatcher(object) { + return this._dispatcherByObject.get(object); + } + registerDispatcher(dispatcher) { + assert(!this._dispatcherByGuid.has(dispatcher._guid)); + this._dispatcherByGuid.set(dispatcher._guid, dispatcher); + this._dispatcherByObject.set(dispatcher._object, dispatcher); + let list = this._dispatchersByBucket.get(dispatcher._gcBucket); + if (!list) { + list = /* @__PURE__ */ new Set(); + this._dispatchersByBucket.set(dispatcher._gcBucket, list); + } + list.add(dispatcher._guid); + } + maybeDisposeStaleDispatchers(gcBucket) { + const maxDispatchers = maxDispatchersForBucket(gcBucket); + const list = this._dispatchersByBucket.get(gcBucket); + if (!list || list.size <= maxDispatchers) + return; + const dispatchersArray = [...list]; + const disposeCount = maxDispatchers / 10 | 0; + this._dispatchersByBucket.set(gcBucket, new Set(dispatchersArray.slice(disposeCount))); + for (let i = 0; i < disposeCount; ++i) { + const d = this._dispatcherByGuid.get(dispatchersArray[i]); + if (!d) + continue; + d._dispose("gc"); + } + } + async dispatch(message) { + const { id, guid, method, params: params2, metadata } = message; + const dispatcher = this._dispatcherByGuid.get(guid); + if (method === "__waitInfo__") { + if (dispatcher) + await this._dispatchWaitInfo(id, dispatcher, params2, metadata); + return; + } + if (!dispatcher) { + this.onmessage({ id, error: serializeError(new TargetClosedError(void 0)) }); + return; + } + let validParams; + let validMetadata; + try { + const validator = findValidator(dispatcher._type, method, "Params"); + const validatorContext = this._validatorFromWireContext(); + validParams = validator(params2, "", validatorContext); + validMetadata = metadataValidator(metadata, "", validatorContext); + if (typeof dispatcher[method] !== "function") + throw new Error(`Mismatching dispatcher: "${dispatcher._type}" does not implement "${method}"`); + } catch (e) { + this.onmessage({ id, error: serializeError(e) }); + return; + } + const metainfo = getMetainfo({ type: dispatcher._type, method }); + if (metainfo?.internal) { + validMetadata.internal = true; + } + const sdkObject = dispatcher._object; + const callMetadata = { + id: `call@${id}`, + location: validMetadata.location, + title: validMetadata.title, + internal: validMetadata.internal, + stepId: validMetadata.stepId, + objectId: sdkObject.guid, + pageId: sdkObject.attribution?.page?.guid, + frameId: sdkObject.attribution?.frame?.guid, + startTime: monotonicTime(), + endTime: 0, + type: dispatcher._type, + method, + params: params2 || {}, + log: [] + }; + await sdkObject.instrumentation.onBeforeCall(sdkObject, callMetadata); + const response2 = { id }; + try { + if (this._dispatcherByGuid.get(guid) !== dispatcher) + throw new TargetClosedError(sdkObject.closeReason()); + const result2 = await dispatcher._runCommand(callMetadata, method, validParams); + const validator = findValidator(dispatcher._type, method, "Result"); + response2.result = validator(result2, "", this._validatorToWireContext()); + callMetadata.result = result2; + } catch (e) { + if (isTargetClosedError(e)) { + const reason = sdkObject.closeReason(); + if (reason) + rewriteErrorMessage(e, reason); + } else if (isProtocolError(e)) { + if (e.type === "closed") + e = new TargetClosedError(sdkObject.closeReason(), e.browserLogMessage()); + else if (e.type === "crashed") + rewriteErrorMessage(e, "Target crashed " + e.browserLogMessage()); + } + response2.error = serializeError(e); + const detailsValidator = maybeFindValidator(dispatcher._type, method, "ErrorDetails"); + if (detailsValidator) + response2.errorDetails = detailsValidator(e?.details ?? {}, "", this._validatorToWireContext()); + callMetadata.error = response2.error; + } finally { + callMetadata.endTime = monotonicTime(); + await sdkObject.instrumentation.onAfterCall(sdkObject, callMetadata); + if (metainfo?.slowMo) + await this._doSlowMo(sdkObject); + } + if (response2.error) + response2.log = compressCallLog(callMetadata.log); + this.onmessage(response2); + } + async _doSlowMo(sdkObject) { + const slowMo = sdkObject.attribution.browser?.options.slowMo; + if (slowMo) + await new Promise((f) => setTimeout(f, slowMo)); + } + async _dispatchWaitInfo(id, dispatcher, params2, metadata) { + let info; + let validMetadata; + try { + const validatorContext = this._validatorFromWireContext(); + info = waitInfoValidator(params2, "", validatorContext); + validMetadata = metadataValidator(metadata, "", validatorContext); + } catch { + return; + } + const sdkObject = dispatcher._object; + if (info.phase === "before") { + const callMetadata = { + id: `call@${id}`, + location: validMetadata.location, + title: validMetadata.title, + internal: validMetadata.internal, + stepId: validMetadata.stepId, + objectId: sdkObject.guid, + pageId: sdkObject.attribution?.page?.guid, + frameId: sdkObject.attribution?.frame?.guid, + startTime: monotonicTime(), + endTime: 0, + type: dispatcher._type, + method: "__waitInfo__", + params: params2 || {}, + log: [] + }; + this._waitOperations.set(info.waitId, callMetadata); + await sdkObject.instrumentation.onBeforeCall(sdkObject, callMetadata).catch(() => { + }); + return; + } + const originalMetadata = this._waitOperations.get(info.waitId); + if (!originalMetadata) + return; + if (info.phase === "log" && info.message) { + originalMetadata.log.push(info.message); + sdkObject.instrumentation.onCallLog(sdkObject, originalMetadata, "api", info.message); + return; + } + if (info.phase === "after") { + originalMetadata.endTime = monotonicTime(); + originalMetadata.error = info.error ? { error: { name: "Error", message: info.error } } : void 0; + this._waitOperations.delete(info.waitId); + await sdkObject.instrumentation.onAfterCall(sdkObject, originalMetadata).catch(() => { + }); + } + } + }; + } +}); + +// packages/isomorphic/utilityScriptSerializers.ts +function isRegExp5(obj) { + try { + return obj instanceof RegExp || Object.prototype.toString.call(obj) === "[object RegExp]"; + } catch (error) { + return false; + } +} +function isDate2(obj) { + try { + return obj instanceof Date || Object.prototype.toString.call(obj) === "[object Date]"; + } catch (error) { + return false; + } +} +function isURL2(obj) { + try { + return obj instanceof URL || Object.prototype.toString.call(obj) === "[object URL]"; + } catch (error) { + return false; + } +} +function isError3(obj) { + try { + return obj instanceof Error || obj && Object.getPrototypeOf(obj)?.name === "Error"; + } catch (error) { + return false; + } +} +function isTypedArray(obj, constructor) { + try { + return obj instanceof constructor || Object.prototype.toString.call(obj) === `[object ${constructor.name}]`; + } catch (error) { + return false; + } +} +function isArrayBuffer(obj) { + try { + return obj instanceof ArrayBuffer || Object.prototype.toString.call(obj) === "[object ArrayBuffer]"; + } catch (error) { + return false; + } +} +function typedArrayToBase64(array) { + if ("toBase64" in array) + return array.toBase64(); + const binary = Array.from(new Uint8Array(array.buffer, array.byteOffset, array.byteLength)).map((b) => String.fromCharCode(b)).join(""); + return btoa(binary); +} +function base64ToTypedArray(base64, TypedArrayConstructor) { + const binary = atob(base64); + const bytes = new Uint8Array(binary.length); + for (let i = 0; i < binary.length; i++) + bytes[i] = binary.charCodeAt(i); + return new TypedArrayConstructor(bytes.buffer); +} +function parseEvaluationResultValue(value2, handles = [], refs = /* @__PURE__ */ new Map()) { + if (Object.is(value2, void 0)) + return void 0; + if (typeof value2 === "object" && value2) { + if ("ref" in value2) + return refs.get(value2.ref); + if ("v" in value2) { + if (value2.v === "undefined") + return void 0; + if (value2.v === "null") + return null; + if (value2.v === "NaN") + return NaN; + if (value2.v === "Infinity") + return Infinity; + if (value2.v === "-Infinity") + return -Infinity; + if (value2.v === "-0") + return -0; + return void 0; + } + if ("d" in value2) { + return new Date(value2.d); + } + if ("u" in value2) + return new URL(value2.u); + if ("bi" in value2) + return BigInt(value2.bi); + if ("e" in value2) { + const error = new Error(value2.e.m); + error.name = value2.e.n; + error.stack = value2.e.s; + return error; + } + if ("r" in value2) + return new RegExp(value2.r.p, value2.r.f); + if ("a" in value2) { + const result2 = []; + refs.set(value2.id, result2); + for (const a of value2.a) + result2.push(parseEvaluationResultValue(a, handles, refs)); + return result2; + } + if ("o" in value2) { + const result2 = {}; + refs.set(value2.id, result2); + for (const { k, v } of value2.o) { + if (k === "__proto__") + continue; + result2[k] = parseEvaluationResultValue(v, handles, refs); + } + return result2; + } + if ("h" in value2) + return handles[value2.h]; + if ("ta" in value2) + return base64ToTypedArray(value2.ta.b, typedArrayConstructors[value2.ta.k]); + if ("ab" in value2) + return base64ToTypedArray(value2.ab.b, Uint8Array).buffer; + } + return value2; +} +function serializeAsCallArgument(value2, handleSerializer) { + return serialize(value2, handleSerializer, { visited: /* @__PURE__ */ new Map(), lastId: 0 }); +} +function serialize(value2, handleSerializer, visitorInfo) { + if (value2 && typeof value2 === "object") { + if (typeof globalThis.Window === "function" && value2 instanceof globalThis.Window) + return "ref: "; + if (typeof globalThis.Document === "function" && value2 instanceof globalThis.Document) + return "ref: "; + if (typeof globalThis.Node === "function" && value2 instanceof globalThis.Node) + return "ref: "; + } + return innerSerialize(value2, handleSerializer, visitorInfo); +} +function innerSerialize(value2, handleSerializer, visitorInfo) { + const result2 = handleSerializer(value2); + if ("fallThrough" in result2) + value2 = result2.fallThrough; + else + return result2; + if (typeof value2 === "symbol") + return { v: "undefined" }; + if (Object.is(value2, void 0)) + return { v: "undefined" }; + if (Object.is(value2, null)) + return { v: "null" }; + if (Object.is(value2, NaN)) + return { v: "NaN" }; + if (Object.is(value2, Infinity)) + return { v: "Infinity" }; + if (Object.is(value2, -Infinity)) + return { v: "-Infinity" }; + if (Object.is(value2, -0)) + return { v: "-0" }; + if (typeof value2 === "boolean") + return value2; + if (typeof value2 === "number") + return value2; + if (typeof value2 === "string") + return value2; + if (typeof value2 === "bigint") + return { bi: value2.toString() }; + if (isError3(value2)) { + let stack; + if (value2.stack?.startsWith(value2.name + ": " + value2.message)) { + stack = value2.stack; + } else { + stack = `${value2.name}: ${value2.message} +${value2.stack}`; + } + return { e: { n: value2.name, m: value2.message, s: stack } }; + } + if (isDate2(value2)) + return { d: value2.toJSON() }; + if (isURL2(value2)) + return { u: value2.toJSON() }; + if (isRegExp5(value2)) + return { r: { p: value2.source, f: value2.flags } }; + for (const [k, ctor] of Object.entries(typedArrayConstructors)) { + if (isTypedArray(value2, ctor)) + return { ta: { b: typedArrayToBase64(value2), k } }; + } + if (isArrayBuffer(value2)) + return { ab: { b: typedArrayToBase64(new Uint8Array(value2)) } }; + const id = visitorInfo.visited.get(value2); + if (id) + return { ref: id }; + if (Array.isArray(value2)) { + const a = []; + const id2 = ++visitorInfo.lastId; + visitorInfo.visited.set(value2, id2); + for (let i = 0; i < value2.length; ++i) + a.push(serialize(value2[i], handleSerializer, visitorInfo)); + return { a, id: id2 }; + } + if (typeof value2 === "object") { + const o = []; + const id2 = ++visitorInfo.lastId; + visitorInfo.visited.set(value2, id2); + for (const name of Object.keys(value2)) { + let item; + try { + item = value2[name]; + } catch (e) { + continue; + } + if (name === "toJSON" && typeof item === "function") + o.push({ k: name, v: { o: [], id: 0 } }); + else + o.push({ k: name, v: serialize(item, handleSerializer, visitorInfo) }); + } + let jsonWrapper; + try { + if (o.length === 0 && value2.toJSON && typeof value2.toJSON === "function") + jsonWrapper = { value: value2.toJSON() }; + } catch (e) { + } + if (jsonWrapper) + return innerSerialize(jsonWrapper.value, handleSerializer, visitorInfo); + return { o, id: id2 }; + } +} +var typedArrayConstructors; +var init_utilityScriptSerializers = __esm({ + "packages/isomorphic/utilityScriptSerializers.ts"() { + "use strict"; + typedArrayConstructors = { + i8: Int8Array, + ui8: Uint8Array, + ui8c: Uint8ClampedArray, + i16: Int16Array, + ui16: Uint16Array, + i32: Int32Array, + ui32: Uint32Array, + // TODO: add Float16Array once it's in baseline + f32: Float32Array, + f64: Float64Array, + bi64: BigInt64Array, + bui64: BigUint64Array + }; + } +}); + +// packages/playwright-core/src/generated/utilityScriptSource.ts +var source3; +var init_utilityScriptSource = __esm({ + "packages/playwright-core/src/generated/utilityScriptSource.ts"() { + "use strict"; + source3 = '\nvar __commonJS = obj => {\n let required = false;\n let result;\n return function __require() {\n if (!required) {\n required = true;\n let fn;\n for (const name in obj) { fn = obj[name]; break; }\n const module = { exports: {} };\n fn(module.exports, module);\n result = module.exports;\n }\n return result;\n }\n};\nvar __export = (target, all) => {for (var name in all) target[name] = all[name];};\nvar __toESM = mod => ({ ...mod, \'default\': mod });\nvar __toCommonJS = mod => ({ ...mod, __esModule: true });\n\n\n// packages/injected/src/utilityScript.ts\nvar utilityScript_exports = {};\n__export(utilityScript_exports, {\n UtilityScript: () => UtilityScript\n});\nmodule.exports = __toCommonJS(utilityScript_exports);\n\n// packages/isomorphic/utilityScriptSerializers.ts\nfunction isRegExp(obj) {\n try {\n return obj instanceof RegExp || Object.prototype.toString.call(obj) === "[object RegExp]";\n } catch (error) {\n return false;\n }\n}\nfunction isDate(obj) {\n try {\n return obj instanceof Date || Object.prototype.toString.call(obj) === "[object Date]";\n } catch (error) {\n return false;\n }\n}\nfunction isURL(obj) {\n try {\n return obj instanceof URL || Object.prototype.toString.call(obj) === "[object URL]";\n } catch (error) {\n return false;\n }\n}\nfunction isError(obj) {\n var _a;\n try {\n return obj instanceof Error || obj && ((_a = Object.getPrototypeOf(obj)) == null ? void 0 : _a.name) === "Error";\n } catch (error) {\n return false;\n }\n}\nfunction isTypedArray(obj, constructor) {\n try {\n return obj instanceof constructor || Object.prototype.toString.call(obj) === `[object ${constructor.name}]`;\n } catch (error) {\n return false;\n }\n}\nfunction isArrayBuffer(obj) {\n try {\n return obj instanceof ArrayBuffer || Object.prototype.toString.call(obj) === "[object ArrayBuffer]";\n } catch (error) {\n return false;\n }\n}\nvar typedArrayConstructors = {\n i8: Int8Array,\n ui8: Uint8Array,\n ui8c: Uint8ClampedArray,\n i16: Int16Array,\n ui16: Uint16Array,\n i32: Int32Array,\n ui32: Uint32Array,\n // TODO: add Float16Array once it\'s in baseline\n f32: Float32Array,\n f64: Float64Array,\n bi64: BigInt64Array,\n bui64: BigUint64Array\n};\nfunction typedArrayToBase64(array) {\n if ("toBase64" in array)\n return array.toBase64();\n const binary = Array.from(new Uint8Array(array.buffer, array.byteOffset, array.byteLength)).map((b) => String.fromCharCode(b)).join("");\n return btoa(binary);\n}\nfunction base64ToTypedArray(base64, TypedArrayConstructor) {\n const binary = atob(base64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++)\n bytes[i] = binary.charCodeAt(i);\n return new TypedArrayConstructor(bytes.buffer);\n}\nfunction parseEvaluationResultValue(value, handles = [], refs = /* @__PURE__ */ new Map()) {\n if (Object.is(value, void 0))\n return void 0;\n if (typeof value === "object" && value) {\n if ("ref" in value)\n return refs.get(value.ref);\n if ("v" in value) {\n if (value.v === "undefined")\n return void 0;\n if (value.v === "null")\n return null;\n if (value.v === "NaN")\n return NaN;\n if (value.v === "Infinity")\n return Infinity;\n if (value.v === "-Infinity")\n return -Infinity;\n if (value.v === "-0")\n return -0;\n return void 0;\n }\n if ("d" in value) {\n return new Date(value.d);\n }\n if ("u" in value)\n return new URL(value.u);\n if ("bi" in value)\n return BigInt(value.bi);\n if ("e" in value) {\n const error = new Error(value.e.m);\n error.name = value.e.n;\n error.stack = value.e.s;\n return error;\n }\n if ("r" in value)\n return new RegExp(value.r.p, value.r.f);\n if ("a" in value) {\n const result = [];\n refs.set(value.id, result);\n for (const a of value.a)\n result.push(parseEvaluationResultValue(a, handles, refs));\n return result;\n }\n if ("o" in value) {\n const result = {};\n refs.set(value.id, result);\n for (const { k, v } of value.o) {\n if (k === "__proto__")\n continue;\n result[k] = parseEvaluationResultValue(v, handles, refs);\n }\n return result;\n }\n if ("h" in value)\n return handles[value.h];\n if ("ta" in value)\n return base64ToTypedArray(value.ta.b, typedArrayConstructors[value.ta.k]);\n if ("ab" in value)\n return base64ToTypedArray(value.ab.b, Uint8Array).buffer;\n }\n return value;\n}\nfunction serializeAsCallArgument(value, handleSerializer) {\n return serialize(value, handleSerializer, { visited: /* @__PURE__ */ new Map(), lastId: 0 });\n}\nfunction serialize(value, handleSerializer, visitorInfo) {\n if (value && typeof value === "object") {\n if (typeof globalThis.Window === "function" && value instanceof globalThis.Window)\n return "ref: ";\n if (typeof globalThis.Document === "function" && value instanceof globalThis.Document)\n return "ref: ";\n if (typeof globalThis.Node === "function" && value instanceof globalThis.Node)\n return "ref: ";\n }\n return innerSerialize(value, handleSerializer, visitorInfo);\n}\nfunction innerSerialize(value, handleSerializer, visitorInfo) {\n var _a;\n const result = handleSerializer(value);\n if ("fallThrough" in result)\n value = result.fallThrough;\n else\n return result;\n if (typeof value === "symbol")\n return { v: "undefined" };\n if (Object.is(value, void 0))\n return { v: "undefined" };\n if (Object.is(value, null))\n return { v: "null" };\n if (Object.is(value, NaN))\n return { v: "NaN" };\n if (Object.is(value, Infinity))\n return { v: "Infinity" };\n if (Object.is(value, -Infinity))\n return { v: "-Infinity" };\n if (Object.is(value, -0))\n return { v: "-0" };\n if (typeof value === "boolean")\n return value;\n if (typeof value === "number")\n return value;\n if (typeof value === "string")\n return value;\n if (typeof value === "bigint")\n return { bi: value.toString() };\n if (isError(value)) {\n let stack;\n if ((_a = value.stack) == null ? void 0 : _a.startsWith(value.name + ": " + value.message)) {\n stack = value.stack;\n } else {\n stack = `${value.name}: ${value.message}\n${value.stack}`;\n }\n return { e: { n: value.name, m: value.message, s: stack } };\n }\n if (isDate(value))\n return { d: value.toJSON() };\n if (isURL(value))\n return { u: value.toJSON() };\n if (isRegExp(value))\n return { r: { p: value.source, f: value.flags } };\n for (const [k, ctor] of Object.entries(typedArrayConstructors)) {\n if (isTypedArray(value, ctor))\n return { ta: { b: typedArrayToBase64(value), k } };\n }\n if (isArrayBuffer(value))\n return { ab: { b: typedArrayToBase64(new Uint8Array(value)) } };\n const id = visitorInfo.visited.get(value);\n if (id)\n return { ref: id };\n if (Array.isArray(value)) {\n const a = [];\n const id2 = ++visitorInfo.lastId;\n visitorInfo.visited.set(value, id2);\n for (let i = 0; i < value.length; ++i)\n a.push(serialize(value[i], handleSerializer, visitorInfo));\n return { a, id: id2 };\n }\n if (typeof value === "object") {\n const o = [];\n const id2 = ++visitorInfo.lastId;\n visitorInfo.visited.set(value, id2);\n for (const name of Object.keys(value)) {\n let item;\n try {\n item = value[name];\n } catch (e) {\n continue;\n }\n if (name === "toJSON" && typeof item === "function")\n o.push({ k: name, v: { o: [], id: 0 } });\n else\n o.push({ k: name, v: serialize(item, handleSerializer, visitorInfo) });\n }\n let jsonWrapper;\n try {\n if (o.length === 0 && value.toJSON && typeof value.toJSON === "function")\n jsonWrapper = { value: value.toJSON() };\n } catch (e) {\n }\n if (jsonWrapper)\n return innerSerialize(jsonWrapper.value, handleSerializer, visitorInfo);\n return { o, id: id2 };\n }\n}\n\n// packages/injected/src/utilityScript.ts\nvar UtilityScript = class {\n constructor(global, isUnderTest) {\n var _a, _b, _c, _d, _e, _f, _g, _h;\n this.global = global;\n this.isUnderTest = isUnderTest;\n if (global.__pwClock) {\n this.builtins = global.__pwClock.builtins;\n } else {\n this.builtins = {\n setTimeout: (_a = global.setTimeout) == null ? void 0 : _a.bind(global),\n clearTimeout: (_b = global.clearTimeout) == null ? void 0 : _b.bind(global),\n setInterval: (_c = global.setInterval) == null ? void 0 : _c.bind(global),\n clearInterval: (_d = global.clearInterval) == null ? void 0 : _d.bind(global),\n requestAnimationFrame: (_e = global.requestAnimationFrame) == null ? void 0 : _e.bind(global),\n cancelAnimationFrame: (_f = global.cancelAnimationFrame) == null ? void 0 : _f.bind(global),\n requestIdleCallback: (_g = global.requestIdleCallback) == null ? void 0 : _g.bind(global),\n cancelIdleCallback: (_h = global.cancelIdleCallback) == null ? void 0 : _h.bind(global),\n performance: global.performance,\n Intl: global.Intl,\n Date: global.Date,\n AbortSignal: global.AbortSignal\n };\n }\n if (this.isUnderTest)\n global.builtins = this.builtins;\n }\n evaluate(isFunction, returnByValue, expression, argCount, ...argsAndHandles) {\n const args = argsAndHandles.slice(0, argCount);\n const handles = argsAndHandles.slice(argCount);\n const parameters = [];\n for (let i = 0; i < args.length; i++)\n parameters[i] = parseEvaluationResultValue(args[i], handles);\n let result = this.global.eval(expression);\n if (isFunction === true) {\n result = result(...parameters);\n } else if (isFunction === false) {\n result = result;\n } else {\n if (typeof result === "function")\n result = result(...parameters);\n }\n return returnByValue ? this._promiseAwareJsonValueNoThrow(result) : result;\n }\n jsonValue(returnByValue, value) {\n if (value === void 0)\n return void 0;\n return serializeAsCallArgument(value, (value2) => ({ fallThrough: value2 }));\n }\n _promiseAwareJsonValueNoThrow(value) {\n const safeJson = (value2) => {\n try {\n return this.jsonValue(true, value2);\n } catch (e) {\n return void 0;\n }\n };\n if (value && typeof value === "object" && typeof value.then === "function") {\n return (async () => {\n const promiseValue = await value;\n return safeJson(promiseValue);\n })();\n }\n return safeJson(value);\n }\n};\n'; + } +}); + +// packages/playwright-core/src/server/javascript.ts +async function evaluate(context2, returnByValue, pageFunction, ...args) { + return evaluateExpression(context2, String(pageFunction), { returnByValue, isFunction: typeof pageFunction === "function" }, ...args); +} +async function evaluateExpression(context2, expression2, options, ...args) { + expression2 = normalizeEvaluationExpression(expression2, options.isFunction); + const handles = []; + const toDispose = []; + const pushHandle = (handle) => { + handles.push(handle); + return handles.length - 1; + }; + args = args.map((arg) => serializeAsCallArgument(arg, (handle) => { + if (handle instanceof JSHandle) { + if (!handle._objectId) + return { fallThrough: handle._value }; + if (handle._disposed) + throw new JavaScriptErrorInEvaluate("JSHandle is disposed!"); + const adopted = context2.adoptIfNeeded(handle); + if (adopted === null) + return { h: pushHandle(Promise.resolve(handle)) }; + toDispose.push(adopted); + return { h: pushHandle(adopted) }; + } + return { fallThrough: handle }; + })); + const utilityScriptObjects = []; + for (const handle of await Promise.all(handles)) { + if (handle._context !== context2) + throw new JavaScriptErrorInEvaluate("JSHandles can be evaluated only in the context they were created!"); + utilityScriptObjects.push(handle); + } + const utilityScriptValues = [options.isFunction, options.returnByValue, expression2, args.length, ...args]; + const script = `(utilityScript, ...args) => utilityScript.evaluate(...args)`; + try { + return await context2._evaluateWithArguments(script, options.returnByValue || false, utilityScriptValues, utilityScriptObjects); + } finally { + toDispose.map((handlePromise) => handlePromise.then((handle) => handle.dispose())); + } +} +function parseUnserializableValue(unserializableValue) { + if (unserializableValue === "NaN") + return NaN; + if (unserializableValue === "Infinity") + return Infinity; + if (unserializableValue === "-Infinity") + return -Infinity; + if (unserializableValue === "-0") + return -0; +} +function normalizeEvaluationExpression(expression2, isFunction2) { + expression2 = expression2.trim(); + if (isFunction2) { + try { + new Function("(" + expression2 + ")"); + } catch (e1) { + if (expression2.startsWith("async ")) + expression2 = "async function " + expression2.substring("async ".length); + else + expression2 = "function " + expression2; + try { + new Function("(" + expression2 + ")"); + } catch (e2) { + throw new Error("Passed function is not well-serializable!"); + } + } + } + if (/^(async)?\s*function(\s|\()/.test(expression2)) + expression2 = "(" + expression2 + ")"; + return expression2; +} +function isJavaScriptErrorInEvaluate(error) { + return error instanceof JavaScriptErrorInEvaluate; +} +function sparseArrayToString(entries) { + const arrayEntries = []; + for (const { name, value: value2 } of entries) { + const index = +name; + if (isNaN(index) || index < 0) + continue; + arrayEntries.push({ index, value: value2 }); + } + arrayEntries.sort((a, b) => a.index - b.index); + let lastIndex = -1; + const tokens = []; + for (const { index, value: value2 } of arrayEntries) { + const emptyItems = index - lastIndex - 1; + if (emptyItems === 1) + tokens.push(`empty`); + else if (emptyItems > 1) + tokens.push(`empty x ${emptyItems}`); + tokens.push(String(value2)); + lastIndex = index; + } + return "[" + tokens.join(", ") + "]"; +} +var ExecutionContext, JSHandle, JavaScriptErrorInEvaluate, snapshottedFunctionBuiltins, snapshottedObjectBuiltins, snapshottedTimerBuiltins, saveGlobalsSnapshotSource, mainWorldGlobalsSnapshotSource; +var init_javascript = __esm({ + "packages/playwright-core/src/server/javascript.ts"() { + "use strict"; + init_utilityScriptSerializers(); + init_manualPromise(); + init_debug(); + init_instrumentation(); + init_utilityScriptSource(); + ExecutionContext = class extends SdkObject { + constructor(parent, delegate, worldNameForTest, options) { + super(parent, "execution-context"); + this._contextDestroyedScope = new LongStandingScope(); + this.worldNameForTest = worldNameForTest; + this._noUtilityWorld = options?.noUtilityWorld; + this.delegate = delegate; + } + contextDestroyed(reason) { + this._contextDestroyedScope.close(new Error(reason)); + } + async raceAgainstContextDestroyed(promise) { + return this._contextDestroyedScope.race(promise); + } + rawEvaluateJSON(expression2) { + return this.raceAgainstContextDestroyed(this.delegate.rawEvaluateJSON(expression2)); + } + rawEvaluateHandle(expression2) { + return this.raceAgainstContextDestroyed(this.delegate.rawEvaluateHandle(this, expression2)); + } + async _evaluateWithArguments(expression2, returnByValue, values, handles) { + const utilityScript = await this._utilityScript(); + return this.raceAgainstContextDestroyed(this.delegate.evaluateWithArguments(expression2, returnByValue, utilityScript, values, handles)); + } + getProperties(object) { + return this.raceAgainstContextDestroyed(this.delegate.getProperties(object)); + } + _releaseHandle(handle) { + return this.delegate.releaseHandle(handle); + } + adoptIfNeeded(handle) { + return null; + } + _utilityScript() { + if (!this._utilityScriptPromise) { + const globalsSnapshot = this._noUtilityWorld ? mainWorldGlobalsSnapshotSource : ""; + const source11 = ` + (() => { + ${globalsSnapshot} + const module = {}; + ${source3} + return new (module.exports.UtilityScript())(globalThis, ${isUnderTest()}); + })();`; + this._utilityScriptPromise = this.raceAgainstContextDestroyed(this.delegate.rawEvaluateHandle(this, source11)).then((handle) => { + handle._setPreview("UtilityScript"); + return handle; + }); + } + return this._utilityScriptPromise; + } + }; + JSHandle = class extends SdkObject { + constructor(context2, type3, preview, objectId, value2) { + super(context2, "handle"); + this.__jshandle = true; + this._disposed = false; + this._context = context2; + this._objectId = objectId; + this._value = value2; + this._objectType = type3; + this._preview = this._objectId ? preview || `JSHandle@${this._objectType}` : String(value2); + if (this._objectId && globalThis.leakedJSHandles) + globalThis.leakedJSHandles.set(this, new Error("Leaked JSHandle")); + } + async evaluateExpression(progress2, expression2, options, arg) { + return await progress2.race(this.internalEvaluateExpression(expression2, options, arg)); + } + async evaluateExpressionHandle(progress2, expression2, options, arg) { + return await progress2.race(this._evaluateExpressionHandle(expression2, options, arg)); + } + async getProperty(progress2, propertyName) { + return await progress2.race(this._getProperty(propertyName)); + } + async getProperties(progress2) { + return await progress2.race(this.internalGetProperties()); + } + async jsonValue(progress2) { + return await progress2.race(this._jsonValue()); + } + async evaluate(pageFunction, arg) { + return evaluate(this._context, true, pageFunction, this, arg); + } + async evaluateHandle(pageFunction, arg) { + return evaluate(this._context, false, pageFunction, this, arg); + } + async internalEvaluateExpression(expression2, options, arg) { + return await evaluateExpression(this._context, expression2, { ...options, returnByValue: true }, this, arg); + } + async _evaluateExpressionHandle(expression2, options, arg) { + return await evaluateExpression(this._context, expression2, { ...options, returnByValue: false }, this, arg); + } + async _getProperty(propertyName) { + const objectHandle = await this.evaluateHandle((object, propertyName2) => { + const result3 = { __proto__: null }; + result3[propertyName2] = object[propertyName2]; + return result3; + }, propertyName); + const properties = await objectHandle.internalGetProperties(); + const result2 = properties.get(propertyName); + objectHandle.dispose(); + return result2; + } + async internalGetProperties() { + if (!this._objectId) + return /* @__PURE__ */ new Map(); + return this._context.getProperties(this); + } + rawValue() { + return this._value; + } + async _jsonValue() { + if (!this._objectId) + return this._value; + const script = `(utilityScript, ...args) => utilityScript.jsonValue(...args)`; + return this._context._evaluateWithArguments(script, true, [true], [this]); + } + asElement() { + return null; + } + dispose() { + if (this._disposed) + return; + this._disposed = true; + if (this._objectId) { + this._context._releaseHandle(this).catch((e) => { + }); + if (globalThis.leakedJSHandles) + globalThis.leakedJSHandles.delete(this); + } + } + toString() { + return this._preview; + } + _setPreviewCallback(callback) { + this._previewCallback = callback; + } + preview() { + return this._preview; + } + worldNameForTest() { + return this._context.worldNameForTest; + } + _setPreview(preview) { + this._preview = preview; + if (this._previewCallback) + this._previewCallback(preview); + } + }; + JavaScriptErrorInEvaluate = class extends Error { + }; + snapshottedFunctionBuiltins = [ + // DOM + "Node", + "Element", + "NodeFilter", + "HTMLElement", + "Document", + "ShadowRoot", + "MutationObserver", + "Event", + "CustomEvent", + "EventTarget", + // JS standard + "Map", + "Set", + "WeakMap", + "WeakSet", + "Promise", + "Symbol", + "Error", + "TypeError", + "RegExp", + "Array", + "Object" + ]; + snapshottedObjectBuiltins = ["JSON", "Math"]; + snapshottedTimerBuiltins = ["setTimeout"]; + saveGlobalsSnapshotSource = `window.__pwSnapshotGlobals = { +${[...snapshottedFunctionBuiltins, ...snapshottedObjectBuiltins, ...snapshottedTimerBuiltins].map((n) => ` ${n}: window.${n}`).join(",\n")} +};`; + mainWorldGlobalsSnapshotSource = ` + const __snap = globalThis.__pwSnapshotGlobals || {}; +${snapshottedFunctionBuiltins.map((n) => ` const ${n} = (typeof globalThis.${n} === 'function' ? globalThis.${n} : __snap.${n});`).join("\n")} +${snapshottedObjectBuiltins.map((n) => ` const ${n} = (typeof globalThis.${n} === 'object' && globalThis.${n} ? globalThis.${n} : __snap.${n});`).join("\n")} +`; + } +}); + +// packages/playwright-core/src/server/fileUploadUtils.ts +async function filesExceedUploadLimit(files) { + const sizes = await Promise.all(files.map(async (file) => (await import_fs13.default.promises.stat(file)).size)); + return sizes.reduce((total, size) => total + size, 0) >= fileUploadSizeLimit; +} +async function prepareFilesForUpload(frame, params2) { + const { payloads, streams, directoryStream } = params2; + let { localPaths, localDirectory } = params2; + if (localPaths && !frame.attribution.playwright.options.isClientCollocatedWithServer) + throw new Error("localPaths are not allowed when the client is not local"); + if ([payloads, localPaths, localDirectory, streams, directoryStream].filter(Boolean).length !== 1) + throw new Error("Exactly one of payloads, localPaths and streams must be provided"); + if (streams) + localPaths = streams.map((c) => c.path()); + if (directoryStream) + localDirectory = directoryStream.path(); + if (localPaths) { + for (const p of localPaths) + assert(import_path12.default.isAbsolute(p) && import_path12.default.resolve(p) === p, "Paths provided to localPaths must be absolute and fully resolved."); + } + let fileBuffers = payloads; + if (!frame._page.browserContext._browser._isBrowserCollocatedWithServer) { + if (localPaths) { + if (await filesExceedUploadLimit(localPaths)) + throw new Error("Cannot transfer files larger than 50Mb to a browser not co-located with the server"); + fileBuffers = await Promise.all(localPaths.map(async (item) => { + return { + name: import_path12.default.basename(item), + buffer: await import_fs13.default.promises.readFile(item), + lastModifiedMs: (await import_fs13.default.promises.stat(item)).mtimeMs + }; + })); + localPaths = void 0; + } + } + const filePayloads = fileBuffers?.map((payload) => ({ + name: payload.name, + mimeType: payload.mimeType || mime4.getType(payload.name) || "application/octet-stream", + buffer: payload.buffer.toString("base64"), + lastModifiedMs: payload.lastModifiedMs + })); + return { localPaths, localDirectory, filePayloads }; +} +var import_fs13, import_path12, mime4, fileUploadSizeLimit; +var init_fileUploadUtils = __esm({ + "packages/playwright-core/src/server/fileUploadUtils.ts"() { + "use strict"; + import_fs13 = __toESM(require("fs")); + import_path12 = __toESM(require("path")); + init_assert(); + mime4 = require("./utilsBundle").mime; + fileUploadSizeLimit = 50 * 1024 * 1024; + } +}); + +// packages/playwright-core/src/generated/injectedScriptSource.ts +var source4; +var init_injectedScriptSource = __esm({ + "packages/playwright-core/src/generated/injectedScriptSource.ts"() { + "use strict"; + source4 = '\nvar __commonJS = obj => {\n let required = false;\n let result;\n return function __require() {\n if (!required) {\n required = true;\n let fn;\n for (const name in obj) { fn = obj[name]; break; }\n const module = { exports: {} };\n fn(module.exports, module);\n result = module.exports;\n }\n return result;\n }\n};\nvar __export = (target, all) => {for (var name in all) target[name] = all[name];};\nvar __toESM = mod => ({ ...mod, \'default\': mod });\nvar __toCommonJS = mod => ({ ...mod, __esModule: true });\n\n\n// packages/injected/src/injectedScript.ts\nvar injectedScript_exports = {};\n__export(injectedScript_exports, {\n InjectedScript: () => InjectedScript\n});\nmodule.exports = __toCommonJS(injectedScript_exports);\n\n// packages/isomorphic/ariaSnapshot.ts\nfunction ariaNodesEqual(a, b) {\n if (a.role !== b.role || a.name !== b.name)\n return false;\n if (!ariaPropsEqual(a, b) || hasPointerCursor(a) !== hasPointerCursor(b))\n return false;\n const aKeys = Object.keys(a.props);\n const bKeys = Object.keys(b.props);\n return aKeys.length === bKeys.length && aKeys.every((k) => a.props[k] === b.props[k]);\n}\nfunction hasPointerCursor(ariaNode) {\n return ariaNode.box.cursor === "pointer";\n}\nfunction ariaPropsEqual(a, b) {\n return a.active === b.active && a.checked === b.checked && a.disabled === b.disabled && a.expanded === b.expanded && a.invalid === b.invalid && a.selected === b.selected && a.level === b.level && a.pressed === b.pressed;\n}\nfunction parseAriaSnapshot(yaml, text, options = {}) {\n var _a;\n const lineCounter = new yaml.LineCounter();\n const parseOptions = {\n keepSourceTokens: true,\n lineCounter,\n ...options\n };\n const yamlDoc = yaml.parseDocument(text, parseOptions);\n const errors = [];\n const convertRange = (range) => {\n return [lineCounter.linePos(range[0]), lineCounter.linePos(range[1])];\n };\n const addError = (error) => {\n errors.push({\n message: error.message,\n range: [lineCounter.linePos(error.pos[0]), lineCounter.linePos(error.pos[1])]\n });\n };\n const convertSeq = (container, seq) => {\n for (const item of seq.items) {\n const itemIsString = item instanceof yaml.Scalar && typeof item.value === "string";\n if (itemIsString) {\n const childNode = KeyParser.parse(item, parseOptions, errors);\n if (childNode) {\n container.children = container.children || [];\n container.children.push(childNode);\n }\n continue;\n }\n const itemIsMap = item instanceof yaml.YAMLMap;\n if (itemIsMap) {\n convertMap(container, item);\n continue;\n }\n errors.push({\n message: "Sequence items should be strings or maps",\n range: convertRange(item.range || seq.range)\n });\n }\n };\n const convertMap = (container, map) => {\n var _a2;\n for (const entry of map.items) {\n container.children = container.children || [];\n const keyIsString = entry.key instanceof yaml.Scalar && typeof entry.key.value === "string";\n if (!keyIsString) {\n errors.push({\n message: "Only string keys are supported",\n range: convertRange(entry.key.range || map.range)\n });\n continue;\n }\n const key = entry.key;\n const value = entry.value;\n if (key.value === "text") {\n const valueIsString = value instanceof yaml.Scalar && typeof value.value === "string";\n if (!valueIsString) {\n errors.push({\n message: "Text value should be a string",\n range: convertRange(entry.value.range || map.range)\n });\n continue;\n }\n container.children.push({\n kind: "text",\n text: textValue(value.value)\n });\n continue;\n }\n if (key.value === "/children") {\n const valueIsString = value instanceof yaml.Scalar && typeof value.value === "string";\n if (!valueIsString || value.value !== "contain" && value.value !== "equal" && value.value !== "deep-equal") {\n errors.push({\n message: \'Strict value should be "contain", "equal" or "deep-equal"\',\n range: convertRange(entry.value.range || map.range)\n });\n continue;\n }\n container.containerMode = value.value;\n continue;\n }\n if (key.value.startsWith("/")) {\n const valueIsString = value instanceof yaml.Scalar && typeof value.value === "string";\n if (!valueIsString) {\n errors.push({\n message: "Property value should be a string",\n range: convertRange(entry.value.range || map.range)\n });\n continue;\n }\n container.props = (_a2 = container.props) != null ? _a2 : {};\n container.props[key.value.slice(1)] = textValue(value.value);\n continue;\n }\n const childNode = KeyParser.parse(key, parseOptions, errors);\n if (!childNode)\n continue;\n const valueIsScalar = value instanceof yaml.Scalar;\n if (valueIsScalar) {\n const type = typeof value.value;\n if (type !== "string" && type !== "number" && type !== "boolean") {\n errors.push({\n message: "Node value should be a string or a sequence",\n range: convertRange(entry.value.range || map.range)\n });\n continue;\n }\n container.children.push({\n ...childNode,\n children: [{\n kind: "text",\n text: textValue(String(value.value))\n }]\n });\n continue;\n }\n const valueIsSequence = value instanceof yaml.YAMLSeq;\n if (valueIsSequence) {\n container.children.push(childNode);\n convertSeq(childNode, value);\n continue;\n }\n errors.push({\n message: "Map values should be strings or sequences",\n range: convertRange(entry.value.range || map.range)\n });\n }\n };\n const fragment = { kind: "role", role: "fragment" };\n yamlDoc.errors.forEach(addError);\n if (errors.length)\n return { errors, fragment };\n if (!(yamlDoc.contents instanceof yaml.YAMLSeq)) {\n errors.push({\n message: \'Aria snapshot must be a YAML sequence, elements starting with " -"\',\n range: yamlDoc.contents ? convertRange(yamlDoc.contents.range) : [{ line: 0, col: 0 }, { line: 0, col: 0 }]\n });\n }\n if (errors.length)\n return { errors, fragment };\n convertSeq(fragment, yamlDoc.contents);\n if (errors.length)\n return { errors, fragment: emptyFragment };\n if (((_a = fragment.children) == null ? void 0 : _a.length) === 1 && (!fragment.containerMode || fragment.containerMode === "contain"))\n return { fragment: fragment.children[0], errors: [] };\n return { fragment, errors: [] };\n}\nvar emptyFragment = { kind: "role", role: "fragment" };\nfunction normalizeWhitespace(text) {\n return text.replace(/[\\u200b\\u00ad]/g, "").replace(/[\\r\\n\\s\\t]+/g, " ").trim();\n}\nfunction textValue(value) {\n return {\n raw: value,\n normalized: normalizeWhitespace(value)\n };\n}\nvar KeyParser = class _KeyParser {\n static parse(text, options, errors) {\n try {\n return new _KeyParser(text.value)._parse();\n } catch (e) {\n if (e instanceof ParserError) {\n const message = options.prettyErrors === false ? e.message : e.message + ":\\n\\n" + text.value + "\\n" + " ".repeat(e.pos) + "^\\n";\n errors.push({\n message,\n range: [options.lineCounter.linePos(text.range[0]), options.lineCounter.linePos(text.range[0] + e.pos)]\n });\n return null;\n }\n throw e;\n }\n }\n constructor(input) {\n this._input = input;\n this._pos = 0;\n this._length = input.length;\n }\n _peek() {\n return this._input[this._pos] || "";\n }\n _next() {\n if (this._pos < this._length)\n return this._input[this._pos++];\n return null;\n }\n _eof() {\n return this._pos >= this._length;\n }\n _isWhitespace() {\n return !this._eof() && /\\s/.test(this._peek());\n }\n _skipWhitespace() {\n while (this._isWhitespace())\n this._pos++;\n }\n _readIdentifier(type) {\n if (this._eof())\n this._throwError(`Unexpected end of input when expecting ${type}`);\n const start = this._pos;\n while (!this._eof() && /[a-zA-Z]/.test(this._peek()))\n this._pos++;\n return this._input.slice(start, this._pos);\n }\n _readString() {\n let result = "";\n let escaped = false;\n while (!this._eof()) {\n const ch = this._next();\n if (escaped) {\n result += ch;\n escaped = false;\n } else if (ch === "\\\\") {\n escaped = true;\n } else if (ch === \'"\') {\n return result;\n } else {\n result += ch;\n }\n }\n this._throwError("Unterminated string");\n }\n _throwError(message, offset = 0) {\n throw new ParserError(message, offset || this._pos);\n }\n _readRegex() {\n let result = "";\n let escaped = false;\n let insideClass = false;\n while (!this._eof()) {\n const ch = this._next();\n if (escaped) {\n result += ch;\n escaped = false;\n } else if (ch === "\\\\") {\n escaped = true;\n result += ch;\n } else if (ch === "/" && !insideClass) {\n return { pattern: result };\n } else if (ch === "[") {\n insideClass = true;\n result += ch;\n } else if (ch === "]" && insideClass) {\n result += ch;\n insideClass = false;\n } else {\n result += ch;\n }\n }\n this._throwError("Unterminated regex");\n }\n _readStringOrRegex() {\n const ch = this._peek();\n if (ch === \'"\') {\n this._next();\n return normalizeWhitespace(this._readString());\n }\n if (ch === "/") {\n this._next();\n return this._readRegex();\n }\n return null;\n }\n _readAttributes(result) {\n let errorPos = this._pos;\n while (true) {\n this._skipWhitespace();\n if (this._peek() === "[") {\n this._next();\n this._skipWhitespace();\n errorPos = this._pos;\n const flagName = this._readIdentifier("attribute");\n this._skipWhitespace();\n let flagValue = "";\n if (this._peek() === "=") {\n this._next();\n this._skipWhitespace();\n errorPos = this._pos;\n while (this._peek() !== "]" && !this._isWhitespace() && !this._eof())\n flagValue += this._next();\n }\n this._skipWhitespace();\n if (this._peek() !== "]")\n this._throwError("Expected ]");\n this._next();\n this._applyAttribute(result, flagName, flagValue || "true", errorPos);\n } else {\n break;\n }\n }\n }\n _parse() {\n this._skipWhitespace();\n const role = this._readIdentifier("role");\n this._skipWhitespace();\n const name = this._readStringOrRegex() || "";\n const result = { kind: "role", role, name };\n this._readAttributes(result);\n this._skipWhitespace();\n if (!this._eof())\n this._throwError("Unexpected input");\n return result;\n }\n _applyAttribute(node, key, value, errorPos) {\n if (key === "checked") {\n this._assert(value === "true" || value === "false" || value === "mixed", \'Value of "checked" attribute must be a boolean or "mixed"\', errorPos);\n node.checked = value === "true" ? true : value === "false" ? false : "mixed";\n return;\n }\n if (key === "disabled") {\n this._assert(value === "true" || value === "false", \'Value of "disabled" attribute must be a boolean\', errorPos);\n node.disabled = value === "true";\n return;\n }\n if (key === "expanded") {\n this._assert(value === "true" || value === "false", \'Value of "expanded" attribute must be a boolean\', errorPos);\n node.expanded = value === "true";\n return;\n }\n if (key === "active") {\n this._assert(value === "true" || value === "false", \'Value of "active" attribute must be a boolean\', errorPos);\n node.active = value === "true";\n return;\n }\n if (key === "invalid") {\n this._assert(value === "true" || value === "false" || value === "grammar" || value === "spelling", \'Value of "invalid" attribute must be a boolean, "grammar" or "spelling"\', errorPos);\n node.invalid = value === "true" ? true : value === "false" ? false : value;\n return;\n }\n if (key === "level") {\n this._assert(!isNaN(Number(value)), \'Value of "level" attribute must be a number\', errorPos);\n node.level = Number(value);\n return;\n }\n if (key === "pressed") {\n this._assert(value === "true" || value === "false" || value === "mixed", \'Value of "pressed" attribute must be a boolean or "mixed"\', errorPos);\n node.pressed = value === "true" ? true : value === "false" ? false : "mixed";\n return;\n }\n if (key === "selected") {\n this._assert(value === "true" || value === "false", \'Value of "selected" attribute must be a boolean\', errorPos);\n node.selected = value === "true";\n return;\n }\n this._assert(false, `Unsupported attribute [${key}]`, errorPos);\n }\n _assert(value, message, valuePos) {\n if (!value)\n this._throwError(message || "Assertion error", valuePos);\n }\n};\nvar ParserError = class extends Error {\n constructor(message, pos) {\n super(message);\n this.pos = pos;\n }\n};\nfunction findNewNode(from, to) {\n var _a, _b;\n function fillMap(root, map, position) {\n let size = 1;\n let childPosition = position + size;\n for (const child of root.children || []) {\n if (typeof child === "string") {\n size++;\n childPosition++;\n } else {\n size += fillMap(child, map, childPosition);\n childPosition += size;\n }\n }\n if (!["none", "presentation", "fragment", "iframe", "generic"].includes(root.role) && root.name) {\n let byRole = map.get(root.role);\n if (!byRole) {\n byRole = /* @__PURE__ */ new Map();\n map.set(root.role, byRole);\n }\n const existing = byRole.get(root.name);\n const sizeAndPosition = size * 100 - position;\n if (!existing || existing.sizeAndPosition < sizeAndPosition)\n byRole.set(root.name, { node: root, sizeAndPosition });\n }\n return size;\n }\n const fromMap = /* @__PURE__ */ new Map();\n if (from)\n fillMap(from, fromMap, 0);\n const toMap = /* @__PURE__ */ new Map();\n fillMap(to, toMap, 0);\n const result = [];\n for (const [role, byRole] of toMap) {\n for (const [name, byName] of byRole) {\n const inFrom = (_a = fromMap.get(role)) == null ? void 0 : _a.get(name);\n if (!inFrom)\n result.push(byName);\n }\n }\n result.sort((a, b) => b.sizeAndPosition - a.sizeAndPosition);\n return (_b = result[0]) == null ? void 0 : _b.node;\n}\n\n// packages/isomorphic/cssTokenizer.ts\nvar between = function(num, first, last) {\n return num >= first && num <= last;\n};\nfunction digit(code) {\n return between(code, 48, 57);\n}\nfunction hexdigit(code) {\n return digit(code) || between(code, 65, 70) || between(code, 97, 102);\n}\nfunction uppercaseletter(code) {\n return between(code, 65, 90);\n}\nfunction lowercaseletter(code) {\n return between(code, 97, 122);\n}\nfunction letter(code) {\n return uppercaseletter(code) || lowercaseletter(code);\n}\nfunction nonascii(code) {\n return code >= 128;\n}\nfunction namestartchar(code) {\n return letter(code) || nonascii(code) || code === 95;\n}\nfunction namechar(code) {\n return namestartchar(code) || digit(code) || code === 45;\n}\nfunction nonprintable(code) {\n return between(code, 0, 8) || code === 11 || between(code, 14, 31) || code === 127;\n}\nfunction newline(code) {\n return code === 10;\n}\nfunction whitespace(code) {\n return newline(code) || code === 9 || code === 32;\n}\nvar maximumallowedcodepoint = 1114111;\nvar InvalidCharacterError = class extends Error {\n constructor(message) {\n super(message);\n this.name = "InvalidCharacterError";\n }\n};\nfunction preprocess(str) {\n const codepoints = [];\n for (let i = 0; i < str.length; i++) {\n let code = str.charCodeAt(i);\n if (code === 13 && str.charCodeAt(i + 1) === 10) {\n code = 10;\n i++;\n }\n if (code === 13 || code === 12)\n code = 10;\n if (code === 0)\n code = 65533;\n if (between(code, 55296, 56319) && between(str.charCodeAt(i + 1), 56320, 57343)) {\n const lead = code - 55296;\n const trail = str.charCodeAt(i + 1) - 56320;\n code = Math.pow(2, 16) + lead * Math.pow(2, 10) + trail;\n i++;\n }\n codepoints.push(code);\n }\n return codepoints;\n}\nfunction stringFromCode(code) {\n if (code <= 65535)\n return String.fromCharCode(code);\n code -= Math.pow(2, 16);\n const lead = Math.floor(code / Math.pow(2, 10)) + 55296;\n const trail = code % Math.pow(2, 10) + 56320;\n return String.fromCharCode(lead) + String.fromCharCode(trail);\n}\nfunction tokenize(str1) {\n const str = preprocess(str1);\n let i = -1;\n const tokens = [];\n let code;\n let line = 0;\n let column = 0;\n let lastLineLength = 0;\n const incrLineno = function() {\n line += 1;\n lastLineLength = column;\n column = 0;\n };\n const locStart = { line, column };\n const codepoint = function(i2) {\n if (i2 >= str.length)\n return -1;\n return str[i2];\n };\n const next = function(num) {\n if (num === void 0)\n num = 1;\n if (num > 3)\n throw "Spec Error: no more than three codepoints of lookahead.";\n return codepoint(i + num);\n };\n const consume = function(num) {\n if (num === void 0)\n num = 1;\n i += num;\n code = codepoint(i);\n if (newline(code))\n incrLineno();\n else\n column += num;\n return true;\n };\n const reconsume = function() {\n i -= 1;\n if (newline(code)) {\n line -= 1;\n column = lastLineLength;\n } else {\n column -= 1;\n }\n locStart.line = line;\n locStart.column = column;\n return true;\n };\n const eof = function(codepoint2) {\n if (codepoint2 === void 0)\n codepoint2 = code;\n return codepoint2 === -1;\n };\n const donothing = function() {\n };\n const parseerror = function() {\n };\n const consumeAToken = function() {\n consumeComments();\n consume();\n if (whitespace(code)) {\n while (whitespace(next()))\n consume();\n return new WhitespaceToken();\n } else if (code === 34) {\n return consumeAStringToken();\n } else if (code === 35) {\n if (namechar(next()) || areAValidEscape(next(1), next(2))) {\n const token = new HashToken("");\n if (wouldStartAnIdentifier(next(1), next(2), next(3)))\n token.type = "id";\n token.value = consumeAName();\n return token;\n } else {\n return new DelimToken(code);\n }\n } else if (code === 36) {\n if (next() === 61) {\n consume();\n return new SuffixMatchToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 39) {\n return consumeAStringToken();\n } else if (code === 40) {\n return new OpenParenToken();\n } else if (code === 41) {\n return new CloseParenToken();\n } else if (code === 42) {\n if (next() === 61) {\n consume();\n return new SubstringMatchToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 43) {\n if (startsWithANumber()) {\n reconsume();\n return consumeANumericToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 44) {\n return new CommaToken();\n } else if (code === 45) {\n if (startsWithANumber()) {\n reconsume();\n return consumeANumericToken();\n } else if (next(1) === 45 && next(2) === 62) {\n consume(2);\n return new CDCToken();\n } else if (startsWithAnIdentifier()) {\n reconsume();\n return consumeAnIdentlikeToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 46) {\n if (startsWithANumber()) {\n reconsume();\n return consumeANumericToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 58) {\n return new ColonToken();\n } else if (code === 59) {\n return new SemicolonToken();\n } else if (code === 60) {\n if (next(1) === 33 && next(2) === 45 && next(3) === 45) {\n consume(3);\n return new CDOToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 64) {\n if (wouldStartAnIdentifier(next(1), next(2), next(3)))\n return new AtKeywordToken(consumeAName());\n else\n return new DelimToken(code);\n } else if (code === 91) {\n return new OpenSquareToken();\n } else if (code === 92) {\n if (startsWithAValidEscape()) {\n reconsume();\n return consumeAnIdentlikeToken();\n } else {\n parseerror();\n return new DelimToken(code);\n }\n } else if (code === 93) {\n return new CloseSquareToken();\n } else if (code === 94) {\n if (next() === 61) {\n consume();\n return new PrefixMatchToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 123) {\n return new OpenCurlyToken();\n } else if (code === 124) {\n if (next() === 61) {\n consume();\n return new DashMatchToken();\n } else if (next() === 124) {\n consume();\n return new ColumnToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 125) {\n return new CloseCurlyToken();\n } else if (code === 126) {\n if (next() === 61) {\n consume();\n return new IncludeMatchToken();\n } else {\n return new DelimToken(code);\n }\n } else if (digit(code)) {\n reconsume();\n return consumeANumericToken();\n } else if (namestartchar(code)) {\n reconsume();\n return consumeAnIdentlikeToken();\n } else if (eof()) {\n return new EOFToken();\n } else {\n return new DelimToken(code);\n }\n };\n const consumeComments = function() {\n while (next(1) === 47 && next(2) === 42) {\n consume(2);\n while (true) {\n consume();\n if (code === 42 && next() === 47) {\n consume();\n break;\n } else if (eof()) {\n parseerror();\n return;\n }\n }\n }\n };\n const consumeANumericToken = function() {\n const num = consumeANumber();\n if (wouldStartAnIdentifier(next(1), next(2), next(3))) {\n const token = new DimensionToken();\n token.value = num.value;\n token.repr = num.repr;\n token.type = num.type;\n token.unit = consumeAName();\n return token;\n } else if (next() === 37) {\n consume();\n const token = new PercentageToken();\n token.value = num.value;\n token.repr = num.repr;\n return token;\n } else {\n const token = new NumberToken();\n token.value = num.value;\n token.repr = num.repr;\n token.type = num.type;\n return token;\n }\n };\n const consumeAnIdentlikeToken = function() {\n const str2 = consumeAName();\n if (str2.toLowerCase() === "url" && next() === 40) {\n consume();\n while (whitespace(next(1)) && whitespace(next(2)))\n consume();\n if (next() === 34 || next() === 39)\n return new FunctionToken(str2);\n else if (whitespace(next()) && (next(2) === 34 || next(2) === 39))\n return new FunctionToken(str2);\n else\n return consumeAURLToken();\n } else if (next() === 40) {\n consume();\n return new FunctionToken(str2);\n } else {\n return new IdentToken(str2);\n }\n };\n const consumeAStringToken = function(endingCodePoint) {\n if (endingCodePoint === void 0)\n endingCodePoint = code;\n let string = "";\n while (consume()) {\n if (code === endingCodePoint || eof()) {\n return new StringToken(string);\n } else if (newline(code)) {\n parseerror();\n reconsume();\n return new BadStringToken();\n } else if (code === 92) {\n if (eof(next()))\n donothing();\n else if (newline(next()))\n consume();\n else\n string += stringFromCode(consumeEscape());\n } else {\n string += stringFromCode(code);\n }\n }\n throw new Error("Internal error");\n };\n const consumeAURLToken = function() {\n const token = new URLToken("");\n while (whitespace(next()))\n consume();\n if (eof(next()))\n return token;\n while (consume()) {\n if (code === 41 || eof()) {\n return token;\n } else if (whitespace(code)) {\n while (whitespace(next()))\n consume();\n if (next() === 41 || eof(next())) {\n consume();\n return token;\n } else {\n consumeTheRemnantsOfABadURL();\n return new BadURLToken();\n }\n } else if (code === 34 || code === 39 || code === 40 || nonprintable(code)) {\n parseerror();\n consumeTheRemnantsOfABadURL();\n return new BadURLToken();\n } else if (code === 92) {\n if (startsWithAValidEscape()) {\n token.value += stringFromCode(consumeEscape());\n } else {\n parseerror();\n consumeTheRemnantsOfABadURL();\n return new BadURLToken();\n }\n } else {\n token.value += stringFromCode(code);\n }\n }\n throw new Error("Internal error");\n };\n const consumeEscape = function() {\n consume();\n if (hexdigit(code)) {\n const digits = [code];\n for (let total = 0; total < 5; total++) {\n if (hexdigit(next())) {\n consume();\n digits.push(code);\n } else {\n break;\n }\n }\n if (whitespace(next()))\n consume();\n let value = parseInt(digits.map(function(x) {\n return String.fromCharCode(x);\n }).join(""), 16);\n if (value > maximumallowedcodepoint)\n value = 65533;\n return value;\n } else if (eof()) {\n return 65533;\n } else {\n return code;\n }\n };\n const areAValidEscape = function(c1, c2) {\n if (c1 !== 92)\n return false;\n if (newline(c2))\n return false;\n return true;\n };\n const startsWithAValidEscape = function() {\n return areAValidEscape(code, next());\n };\n const wouldStartAnIdentifier = function(c1, c2, c3) {\n if (c1 === 45)\n return namestartchar(c2) || c2 === 45 || areAValidEscape(c2, c3);\n else if (namestartchar(c1))\n return true;\n else if (c1 === 92)\n return areAValidEscape(c1, c2);\n else\n return false;\n };\n const startsWithAnIdentifier = function() {\n return wouldStartAnIdentifier(code, next(1), next(2));\n };\n const wouldStartANumber = function(c1, c2, c3) {\n if (c1 === 43 || c1 === 45) {\n if (digit(c2))\n return true;\n if (c2 === 46 && digit(c3))\n return true;\n return false;\n } else if (c1 === 46) {\n if (digit(c2))\n return true;\n return false;\n } else if (digit(c1)) {\n return true;\n } else {\n return false;\n }\n };\n const startsWithANumber = function() {\n return wouldStartANumber(code, next(1), next(2));\n };\n const consumeAName = function() {\n let result = "";\n while (consume()) {\n if (namechar(code)) {\n result += stringFromCode(code);\n } else if (startsWithAValidEscape()) {\n result += stringFromCode(consumeEscape());\n } else {\n reconsume();\n return result;\n }\n }\n throw new Error("Internal parse error");\n };\n const consumeANumber = function() {\n let repr = "";\n let type = "integer";\n if (next() === 43 || next() === 45) {\n consume();\n repr += stringFromCode(code);\n }\n while (digit(next())) {\n consume();\n repr += stringFromCode(code);\n }\n if (next(1) === 46 && digit(next(2))) {\n consume();\n repr += stringFromCode(code);\n consume();\n repr += stringFromCode(code);\n type = "number";\n while (digit(next())) {\n consume();\n repr += stringFromCode(code);\n }\n }\n const c1 = next(1);\n const c2 = next(2);\n const c3 = next(3);\n if ((c1 === 69 || c1 === 101) && digit(c2)) {\n consume();\n repr += stringFromCode(code);\n consume();\n repr += stringFromCode(code);\n type = "number";\n while (digit(next())) {\n consume();\n repr += stringFromCode(code);\n }\n } else if ((c1 === 69 || c1 === 101) && (c2 === 43 || c2 === 45) && digit(c3)) {\n consume();\n repr += stringFromCode(code);\n consume();\n repr += stringFromCode(code);\n consume();\n repr += stringFromCode(code);\n type = "number";\n while (digit(next())) {\n consume();\n repr += stringFromCode(code);\n }\n }\n const value = convertAStringToANumber(repr);\n return { type, value, repr };\n };\n const convertAStringToANumber = function(string) {\n return +string;\n };\n const consumeTheRemnantsOfABadURL = function() {\n while (consume()) {\n if (code === 41 || eof()) {\n return;\n } else if (startsWithAValidEscape()) {\n consumeEscape();\n donothing();\n } else {\n donothing();\n }\n }\n };\n let iterationCount = 0;\n while (!eof(next())) {\n tokens.push(consumeAToken());\n iterationCount++;\n if (iterationCount > str.length * 2)\n throw new Error("I\'m infinite-looping!");\n }\n return tokens;\n}\nvar CSSParserToken = class {\n constructor() {\n this.tokenType = "";\n }\n toJSON() {\n return { token: this.tokenType };\n }\n toString() {\n return this.tokenType;\n }\n toSource() {\n return "" + this;\n }\n};\nvar BadStringToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = "BADSTRING";\n }\n};\nvar BadURLToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = "BADURL";\n }\n};\nvar WhitespaceToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = "WHITESPACE";\n }\n toString() {\n return "WS";\n }\n toSource() {\n return " ";\n }\n};\nvar CDOToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = "CDO";\n }\n toSource() {\n return "";\n }\n};\nvar ColonToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = ":";\n }\n};\nvar SemicolonToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = ";";\n }\n};\nvar CommaToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = ",";\n }\n};\nvar GroupingToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.value = "";\n this.mirror = "";\n }\n};\nvar OpenCurlyToken = class extends GroupingToken {\n constructor() {\n super();\n this.tokenType = "{";\n this.value = "{";\n this.mirror = "}";\n }\n};\nvar CloseCurlyToken = class extends GroupingToken {\n constructor() {\n super();\n this.tokenType = "}";\n this.value = "}";\n this.mirror = "{";\n }\n};\nvar OpenSquareToken = class extends GroupingToken {\n constructor() {\n super();\n this.tokenType = "[";\n this.value = "[";\n this.mirror = "]";\n }\n};\nvar CloseSquareToken = class extends GroupingToken {\n constructor() {\n super();\n this.tokenType = "]";\n this.value = "]";\n this.mirror = "[";\n }\n};\nvar OpenParenToken = class extends GroupingToken {\n constructor() {\n super();\n this.tokenType = "(";\n this.value = "(";\n this.mirror = ")";\n }\n};\nvar CloseParenToken = class extends GroupingToken {\n constructor() {\n super();\n this.tokenType = ")";\n this.value = ")";\n this.mirror = "(";\n }\n};\nvar IncludeMatchToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = "~=";\n }\n};\nvar DashMatchToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = "|=";\n }\n};\nvar PrefixMatchToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = "^=";\n }\n};\nvar SuffixMatchToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = "$=";\n }\n};\nvar SubstringMatchToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = "*=";\n }\n};\nvar ColumnToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = "||";\n }\n};\nvar EOFToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = "EOF";\n }\n toSource() {\n return "";\n }\n};\nvar DelimToken = class extends CSSParserToken {\n constructor(code) {\n super();\n this.tokenType = "DELIM";\n this.value = "";\n this.value = stringFromCode(code);\n }\n toString() {\n return "DELIM(" + this.value + ")";\n }\n toJSON() {\n const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);\n json.value = this.value;\n return json;\n }\n toSource() {\n if (this.value === "\\\\")\n return "\\\\\\n";\n else\n return this.value;\n }\n};\nvar StringValuedToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.value = "";\n }\n ASCIIMatch(str) {\n return this.value.toLowerCase() === str.toLowerCase();\n }\n toJSON() {\n const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);\n json.value = this.value;\n return json;\n }\n};\nvar IdentToken = class extends StringValuedToken {\n constructor(val) {\n super();\n this.tokenType = "IDENT";\n this.value = val;\n }\n toString() {\n return "IDENT(" + this.value + ")";\n }\n toSource() {\n return escapeIdent(this.value);\n }\n};\nvar FunctionToken = class extends StringValuedToken {\n constructor(val) {\n super();\n this.tokenType = "FUNCTION";\n this.value = val;\n this.mirror = ")";\n }\n toString() {\n return "FUNCTION(" + this.value + ")";\n }\n toSource() {\n return escapeIdent(this.value) + "(";\n }\n};\nvar AtKeywordToken = class extends StringValuedToken {\n constructor(val) {\n super();\n this.tokenType = "AT-KEYWORD";\n this.value = val;\n }\n toString() {\n return "AT(" + this.value + ")";\n }\n toSource() {\n return "@" + escapeIdent(this.value);\n }\n};\nvar HashToken = class extends StringValuedToken {\n constructor(val) {\n super();\n this.tokenType = "HASH";\n this.value = val;\n this.type = "unrestricted";\n }\n toString() {\n return "HASH(" + this.value + ")";\n }\n toJSON() {\n const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);\n json.value = this.value;\n json.type = this.type;\n return json;\n }\n toSource() {\n if (this.type === "id")\n return "#" + escapeIdent(this.value);\n else\n return "#" + escapeHash(this.value);\n }\n};\nvar StringToken = class extends StringValuedToken {\n constructor(val) {\n super();\n this.tokenType = "STRING";\n this.value = val;\n }\n toString() {\n return \'"\' + escapeString(this.value) + \'"\';\n }\n};\nvar URLToken = class extends StringValuedToken {\n constructor(val) {\n super();\n this.tokenType = "URL";\n this.value = val;\n }\n toString() {\n return "URL(" + this.value + ")";\n }\n toSource() {\n return \'url("\' + escapeString(this.value) + \'")\';\n }\n};\nvar NumberToken = class extends CSSParserToken {\n constructor() {\n super();\n this.tokenType = "NUMBER";\n this.type = "integer";\n this.repr = "";\n }\n toString() {\n if (this.type === "integer")\n return "INT(" + this.value + ")";\n return "NUMBER(" + this.value + ")";\n }\n toJSON() {\n const json = super.toJSON();\n json.value = this.value;\n json.type = this.type;\n json.repr = this.repr;\n return json;\n }\n toSource() {\n return this.repr;\n }\n};\nvar PercentageToken = class extends CSSParserToken {\n constructor() {\n super();\n this.tokenType = "PERCENTAGE";\n this.repr = "";\n }\n toString() {\n return "PERCENTAGE(" + this.value + ")";\n }\n toJSON() {\n const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);\n json.value = this.value;\n json.repr = this.repr;\n return json;\n }\n toSource() {\n return this.repr + "%";\n }\n};\nvar DimensionToken = class extends CSSParserToken {\n constructor() {\n super();\n this.tokenType = "DIMENSION";\n this.type = "integer";\n this.repr = "";\n this.unit = "";\n }\n toString() {\n return "DIM(" + this.value + "," + this.unit + ")";\n }\n toJSON() {\n const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);\n json.value = this.value;\n json.type = this.type;\n json.repr = this.repr;\n json.unit = this.unit;\n return json;\n }\n toSource() {\n const source = this.repr;\n let unit = escapeIdent(this.unit);\n if (unit[0].toLowerCase() === "e" && (unit[1] === "-" || between(unit.charCodeAt(1), 48, 57))) {\n unit = "\\\\65 " + unit.slice(1, unit.length);\n }\n return source + unit;\n }\n};\nfunction escapeIdent(string) {\n string = "" + string;\n let result = "";\n const firstcode = string.charCodeAt(0);\n for (let i = 0; i < string.length; i++) {\n const code = string.charCodeAt(i);\n if (code === 0)\n throw new InvalidCharacterError("Invalid character: the input contains U+0000.");\n if (between(code, 1, 31) || code === 127 || i === 0 && between(code, 48, 57) || i === 1 && between(code, 48, 57) && firstcode === 45)\n result += "\\\\" + code.toString(16) + " ";\n else if (code >= 128 || code === 45 || code === 95 || between(code, 48, 57) || between(code, 65, 90) || between(code, 97, 122))\n result += string[i];\n else\n result += "\\\\" + string[i];\n }\n return result;\n}\nfunction escapeHash(string) {\n string = "" + string;\n let result = "";\n for (let i = 0; i < string.length; i++) {\n const code = string.charCodeAt(i);\n if (code === 0)\n throw new InvalidCharacterError("Invalid character: the input contains U+0000.");\n if (code >= 128 || code === 45 || code === 95 || between(code, 48, 57) || between(code, 65, 90) || between(code, 97, 122))\n result += string[i];\n else\n result += "\\\\" + code.toString(16) + " ";\n }\n return result;\n}\nfunction escapeString(string) {\n string = "" + string;\n let result = "";\n for (let i = 0; i < string.length; i++) {\n const code = string.charCodeAt(i);\n if (code === 0)\n throw new InvalidCharacterError("Invalid character: the input contains U+0000.");\n if (between(code, 1, 31) || code === 127)\n result += "\\\\" + code.toString(16) + " ";\n else if (code === 34 || code === 92)\n result += "\\\\" + string[i];\n else\n result += string[i];\n }\n return result;\n}\n\n// packages/isomorphic/cssParser.ts\nvar InvalidSelectorError = class extends Error {\n};\nfunction parseCSS(selector, customNames) {\n let tokens;\n try {\n tokens = tokenize(selector);\n if (!(tokens[tokens.length - 1] instanceof EOFToken))\n tokens.push(new EOFToken());\n } catch (e) {\n const newMessage = e.message + ` while parsing css selector "${selector}". Did you mean to CSS.escape it?`;\n const index = (e.stack || "").indexOf(e.message);\n if (index !== -1)\n e.stack = e.stack.substring(0, index) + newMessage + e.stack.substring(index + e.message.length);\n e.message = newMessage;\n throw e;\n }\n const unsupportedToken = tokens.find((token) => {\n return token instanceof AtKeywordToken || token instanceof BadStringToken || token instanceof BadURLToken || token instanceof ColumnToken || token instanceof CDOToken || token instanceof CDCToken || token instanceof SemicolonToken || // TODO: Consider using these for something, e.g. to escape complex strings.\n // For example :xpath{ (//div/bar[@attr="foo"])[2]/baz }\n // Or this way :xpath( {complex-xpath-goes-here("hello")} )\n token instanceof OpenCurlyToken || token instanceof CloseCurlyToken || // TODO: Consider treating these as strings?\n token instanceof URLToken || token instanceof PercentageToken;\n });\n if (unsupportedToken)\n throw new InvalidSelectorError(`Unsupported token "${unsupportedToken.toSource()}" while parsing css selector "${selector}". Did you mean to CSS.escape it?`);\n let pos = 0;\n const names = /* @__PURE__ */ new Set();\n function unexpected() {\n return new InvalidSelectorError(`Unexpected token "${tokens[pos].toSource()}" while parsing css selector "${selector}". Did you mean to CSS.escape it?`);\n }\n function skipWhitespace() {\n while (tokens[pos] instanceof WhitespaceToken)\n pos++;\n }\n function isIdent(p = pos) {\n return tokens[p] instanceof IdentToken;\n }\n function isString(p = pos) {\n return tokens[p] instanceof StringToken;\n }\n function isNumber(p = pos) {\n return tokens[p] instanceof NumberToken;\n }\n function isComma(p = pos) {\n return tokens[p] instanceof CommaToken;\n }\n function isOpenParen(p = pos) {\n return tokens[p] instanceof OpenParenToken;\n }\n function isCloseParen(p = pos) {\n return tokens[p] instanceof CloseParenToken;\n }\n function isFunction(p = pos) {\n return tokens[p] instanceof FunctionToken;\n }\n function isStar(p = pos) {\n return tokens[p] instanceof DelimToken && tokens[p].value === "*";\n }\n function isEOF(p = pos) {\n return tokens[p] instanceof EOFToken;\n }\n function isClauseCombinator(p = pos) {\n return tokens[p] instanceof DelimToken && [">", "+", "~"].includes(tokens[p].value);\n }\n function isSelectorClauseEnd(p = pos) {\n return isComma(p) || isCloseParen(p) || isEOF(p) || isClauseCombinator(p) || tokens[p] instanceof WhitespaceToken;\n }\n function consumeFunctionArguments() {\n const result2 = [consumeArgument()];\n while (true) {\n skipWhitespace();\n if (!isComma())\n break;\n pos++;\n result2.push(consumeArgument());\n }\n return result2;\n }\n function consumeArgument() {\n skipWhitespace();\n if (isNumber())\n return tokens[pos++].value;\n if (isString())\n return tokens[pos++].value;\n return consumeComplexSelector();\n }\n function consumeComplexSelector() {\n const result2 = { simples: [] };\n skipWhitespace();\n if (isClauseCombinator()) {\n result2.simples.push({ selector: { functions: [{ name: "scope", args: [] }] }, combinator: "" });\n } else {\n result2.simples.push({ selector: consumeSimpleSelector(), combinator: "" });\n }\n while (true) {\n skipWhitespace();\n if (isClauseCombinator()) {\n result2.simples[result2.simples.length - 1].combinator = tokens[pos++].value;\n skipWhitespace();\n } else if (isSelectorClauseEnd()) {\n break;\n }\n result2.simples.push({ combinator: "", selector: consumeSimpleSelector() });\n }\n return result2;\n }\n function consumeSimpleSelector() {\n let rawCSSString = "";\n const functions = [];\n while (!isSelectorClauseEnd()) {\n if (isIdent() || isStar()) {\n rawCSSString += tokens[pos++].toSource();\n } else if (tokens[pos] instanceof HashToken) {\n rawCSSString += tokens[pos++].toSource();\n } else if (tokens[pos] instanceof DelimToken && tokens[pos].value === ".") {\n pos++;\n if (isIdent())\n rawCSSString += "." + tokens[pos++].toSource();\n else\n throw unexpected();\n } else if (tokens[pos] instanceof ColonToken) {\n pos++;\n if (isIdent()) {\n if (!customNames.has(tokens[pos].value.toLowerCase())) {\n rawCSSString += ":" + tokens[pos++].toSource();\n } else {\n const name = tokens[pos++].value.toLowerCase();\n functions.push({ name, args: [] });\n names.add(name);\n }\n } else if (isFunction()) {\n const name = tokens[pos++].value.toLowerCase();\n if (!customNames.has(name)) {\n rawCSSString += `:${name}(${consumeBuiltinFunctionArguments()})`;\n } else {\n functions.push({ name, args: consumeFunctionArguments() });\n names.add(name);\n }\n skipWhitespace();\n if (!isCloseParen())\n throw unexpected();\n pos++;\n } else {\n throw unexpected();\n }\n } else if (tokens[pos] instanceof OpenSquareToken) {\n rawCSSString += "[";\n pos++;\n while (!(tokens[pos] instanceof CloseSquareToken) && !isEOF())\n rawCSSString += tokens[pos++].toSource();\n if (!(tokens[pos] instanceof CloseSquareToken))\n throw unexpected();\n rawCSSString += "]";\n pos++;\n } else {\n throw unexpected();\n }\n }\n if (!rawCSSString && !functions.length)\n throw unexpected();\n return { css: rawCSSString || void 0, functions };\n }\n function consumeBuiltinFunctionArguments() {\n let s = "";\n let balance = 1;\n while (!isEOF()) {\n if (isOpenParen() || isFunction())\n balance++;\n if (isCloseParen())\n balance--;\n if (!balance)\n break;\n s += tokens[pos++].toSource();\n }\n return s;\n }\n const result = consumeFunctionArguments();\n if (!isEOF())\n throw unexpected();\n if (result.some((arg) => typeof arg !== "object" || !("simples" in arg)))\n throw new InvalidSelectorError(`Error while parsing css selector "${selector}". Did you mean to CSS.escape it?`);\n return { selector: result, names: Array.from(names) };\n}\n\n// packages/isomorphic/selectorParser.ts\nvar kNestedSelectorNames = /* @__PURE__ */ new Set(["internal:has", "internal:has-not", "internal:and", "internal:or", "internal:chain", "left-of", "right-of", "above", "below", "near"]);\nvar kNestedSelectorNamesWithDistance = /* @__PURE__ */ new Set(["left-of", "right-of", "above", "below", "near"]);\nvar customCSSNames = /* @__PURE__ */ new Set(["not", "is", "where", "has", "scope", "light", "visible", "text", "text-matches", "text-is", "has-text", "above", "below", "right-of", "left-of", "near", "nth-match"]);\nfunction parseSelector(selector) {\n const parsedStrings = parseSelectorString(selector);\n const parts = [];\n for (const part of parsedStrings.parts) {\n if (part.name === "css" || part.name === "css:light") {\n if (part.name === "css:light")\n part.body = ":light(" + part.body + ")";\n const parsedCSS = parseCSS(part.body, customCSSNames);\n parts.push({\n name: "css",\n body: parsedCSS.selector,\n source: part.body\n });\n continue;\n }\n if (kNestedSelectorNames.has(part.name)) {\n let innerSelector;\n let distance;\n try {\n const unescaped = JSON.parse("[" + part.body + "]");\n if (!Array.isArray(unescaped) || unescaped.length < 1 || unescaped.length > 2 || typeof unescaped[0] !== "string")\n throw new InvalidSelectorError(`Malformed selector: ${part.name}=` + part.body);\n innerSelector = unescaped[0];\n if (unescaped.length === 2) {\n if (typeof unescaped[1] !== "number" || !kNestedSelectorNamesWithDistance.has(part.name))\n throw new InvalidSelectorError(`Malformed selector: ${part.name}=` + part.body);\n distance = unescaped[1];\n }\n } catch (e) {\n throw new InvalidSelectorError(`Malformed selector: ${part.name}=` + part.body);\n }\n const nested = { name: part.name, source: part.body, body: { parsed: parseSelector(innerSelector), distance } };\n const lastFrame = [...nested.body.parsed.parts].reverse().find((part2) => part2.name === "internal:control" && part2.body === "enter-frame");\n const lastFrameIndex = lastFrame ? nested.body.parsed.parts.indexOf(lastFrame) : -1;\n if (lastFrameIndex !== -1 && selectorPartsEqual(nested.body.parsed.parts.slice(0, lastFrameIndex + 1), parts.slice(0, lastFrameIndex + 1)))\n nested.body.parsed.parts.splice(0, lastFrameIndex + 1);\n parts.push(nested);\n continue;\n }\n parts.push({ ...part, source: part.body });\n }\n if (kNestedSelectorNames.has(parts[0].name))\n throw new InvalidSelectorError(`"${parts[0].name}" selector cannot be first`);\n return {\n capture: parsedStrings.capture,\n parts\n };\n}\nfunction selectorPartsEqual(list1, list2) {\n return stringifySelector({ parts: list1 }) === stringifySelector({ parts: list2 });\n}\nfunction stringifySelector(selector, forceEngineName) {\n if (typeof selector === "string")\n return selector;\n return selector.parts.map((p, i) => {\n let includeEngine = true;\n if (!forceEngineName && i !== selector.capture) {\n if (p.name === "css")\n includeEngine = false;\n else if (p.name === "xpath" && p.source.startsWith("//") || p.source.startsWith(".."))\n includeEngine = false;\n }\n const prefix = includeEngine ? p.name + "=" : "";\n return `${i === selector.capture ? "*" : ""}${prefix}${p.source}`;\n }).join(" >> ");\n}\nfunction visitAllSelectorParts(selector, visitor) {\n const visit = (selector2, nested) => {\n for (const part of selector2.parts) {\n visitor(part, nested);\n if (kNestedSelectorNames.has(part.name))\n visit(part.body.parsed, true);\n }\n };\n visit(selector, false);\n}\nfunction parseSelectorString(selector) {\n let index = 0;\n let quote;\n let start = 0;\n const result = { parts: [] };\n const append = () => {\n const part = selector.substring(start, index).trim();\n const eqIndex = part.indexOf("=");\n let name;\n let body;\n if (eqIndex !== -1 && part.substring(0, eqIndex).trim().match(/^[a-zA-Z_0-9-+:*]+$/)) {\n name = part.substring(0, eqIndex).trim();\n body = part.substring(eqIndex + 1);\n } else if (part.length > 1 && part[0] === \'"\' && part[part.length - 1] === \'"\') {\n name = "text";\n body = part;\n } else if (part.length > 1 && part[0] === "\'" && part[part.length - 1] === "\'") {\n name = "text";\n body = part;\n } else if (/^\\(*\\/\\//.test(part) || part.startsWith("..")) {\n name = "xpath";\n body = part;\n } else {\n name = "css";\n body = part;\n }\n let capture = false;\n if (name[0] === "*") {\n capture = true;\n name = name.substring(1);\n }\n result.parts.push({ name, body });\n if (capture) {\n if (result.capture !== void 0)\n throw new InvalidSelectorError(`Only one of the selectors can capture using * modifier`);\n result.capture = result.parts.length - 1;\n }\n };\n if (!selector.includes(">>")) {\n index = selector.length;\n append();\n return result;\n }\n const shouldIgnoreTextSelectorQuote = () => {\n const prefix = selector.substring(start, index);\n const match = prefix.match(/^\\s*text\\s*=(.*)$/);\n return !!match && !!match[1];\n };\n while (index < selector.length) {\n const c = selector[index];\n if (c === "\\\\" && index + 1 < selector.length) {\n index += 2;\n } else if (c === quote) {\n quote = void 0;\n index++;\n } else if (!quote && (c === \'"\' || c === "\'" || c === "`") && !shouldIgnoreTextSelectorQuote()) {\n quote = c;\n index++;\n } else if (!quote && c === ">" && selector[index + 1] === ">") {\n append();\n index += 2;\n start = index;\n } else {\n index++;\n }\n }\n append();\n return result;\n}\nfunction parseAttributeSelector(selector, allowUnquotedStrings) {\n let wp = 0;\n let EOL = selector.length === 0;\n const next = () => selector[wp] || "";\n const eat1 = () => {\n const result2 = next();\n ++wp;\n EOL = wp >= selector.length;\n return result2;\n };\n const syntaxError = (stage) => {\n if (EOL)\n throw new InvalidSelectorError(`Unexpected end of selector while parsing selector \\`${selector}\\``);\n throw new InvalidSelectorError(`Error while parsing selector \\`${selector}\\` - unexpected symbol "${next()}" at position ${wp}` + (stage ? " during " + stage : ""));\n };\n function skipSpaces() {\n while (!EOL && /\\s/.test(next()))\n eat1();\n }\n function isCSSNameChar(char) {\n return char >= "\\x80" || char >= "0" && char <= "9" || char >= "A" && char <= "Z" || char >= "a" && char <= "z" || char >= "0" && char <= "9" || char === "_" || char === "-";\n }\n function readIdentifier() {\n let result2 = "";\n skipSpaces();\n while (!EOL && isCSSNameChar(next()))\n result2 += eat1();\n return result2;\n }\n function readQuotedString(quote) {\n let result2 = eat1();\n if (result2 !== quote)\n syntaxError("parsing quoted string");\n while (!EOL && next() !== quote) {\n if (next() === "\\\\")\n eat1();\n result2 += eat1();\n }\n if (next() !== quote)\n syntaxError("parsing quoted string");\n result2 += eat1();\n return result2;\n }\n function readRegularExpression() {\n if (eat1() !== "/")\n syntaxError("parsing regular expression");\n let source = "";\n let inClass = false;\n while (!EOL) {\n if (next() === "\\\\") {\n source += eat1();\n if (EOL)\n syntaxError("parsing regular expression");\n } else if (inClass && next() === "]") {\n inClass = false;\n } else if (!inClass && next() === "[") {\n inClass = true;\n } else if (!inClass && next() === "/") {\n break;\n }\n source += eat1();\n }\n if (eat1() !== "/")\n syntaxError("parsing regular expression");\n let flags = "";\n while (!EOL && next().match(/[dgimsuy]/))\n flags += eat1();\n try {\n return new RegExp(source, flags);\n } catch (e) {\n throw new InvalidSelectorError(`Error while parsing selector \\`${selector}\\`: ${e.message}`);\n }\n }\n function readAttributeToken() {\n let token = "";\n skipSpaces();\n if (next() === `\'` || next() === `"`)\n token = readQuotedString(next()).slice(1, -1);\n else\n token = readIdentifier();\n if (!token)\n syntaxError("parsing property path");\n return token;\n }\n function readOperator() {\n skipSpaces();\n let op = "";\n if (!EOL)\n op += eat1();\n if (!EOL && op !== "=")\n op += eat1();\n if (!["=", "*=", "^=", "$=", "|=", "~="].includes(op))\n syntaxError("parsing operator");\n return op;\n }\n function readAttribute() {\n eat1();\n const jsonPath = [];\n jsonPath.push(readAttributeToken());\n skipSpaces();\n while (next() === ".") {\n eat1();\n jsonPath.push(readAttributeToken());\n skipSpaces();\n }\n if (next() === "]") {\n eat1();\n return { name: jsonPath.join("."), jsonPath, op: "", value: null, caseSensitive: false };\n }\n const operator = readOperator();\n let value = void 0;\n let caseSensitive = true;\n skipSpaces();\n if (next() === "/") {\n if (operator !== "=")\n throw new InvalidSelectorError(`Error while parsing selector \\`${selector}\\` - cannot use ${operator} in attribute with regular expression`);\n value = readRegularExpression();\n } else if (next() === `\'` || next() === `"`) {\n value = readQuotedString(next()).slice(1, -1);\n skipSpaces();\n if (next() === "i" || next() === "I") {\n caseSensitive = false;\n eat1();\n } else if (next() === "s" || next() === "S") {\n caseSensitive = true;\n eat1();\n }\n } else {\n value = "";\n while (!EOL && (isCSSNameChar(next()) || next() === "+" || next() === "."))\n value += eat1();\n if (value === "true") {\n value = true;\n } else if (value === "false") {\n value = false;\n } else {\n if (!allowUnquotedStrings) {\n value = +value;\n if (Number.isNaN(value))\n syntaxError("parsing attribute value");\n }\n }\n }\n skipSpaces();\n if (next() !== "]")\n syntaxError("parsing attribute value");\n eat1();\n if (operator !== "=" && typeof value !== "string")\n throw new InvalidSelectorError(`Error while parsing selector \\`${selector}\\` - cannot use ${operator} in attribute with non-string matching value - ${value}`);\n return { name: jsonPath.join("."), jsonPath, op: operator, value, caseSensitive };\n }\n const result = {\n name: "",\n attributes: []\n };\n result.name = readIdentifier();\n skipSpaces();\n while (next() === "[") {\n result.attributes.push(readAttribute());\n skipSpaces();\n }\n if (!EOL)\n syntaxError(void 0);\n if (!result.name && !result.attributes.length)\n throw new InvalidSelectorError(`Error while parsing selector \\`${selector}\\` - selector cannot be empty`);\n return result;\n}\n\n// packages/isomorphic/stringUtils.ts\nfunction escapeWithQuotes(text, char = "\'") {\n const stringified = JSON.stringify(text);\n const escapedText = stringified.substring(1, stringified.length - 1).replace(/\\\\"/g, \'"\');\n if (char === "\'")\n return char + escapedText.replace(/[\']/g, "\\\\\'") + char;\n if (char === \'"\')\n return char + escapedText.replace(/["]/g, \'\\\\"\') + char;\n if (char === "`")\n return char + escapedText.replace(/[`]/g, "\\\\`") + char;\n throw new Error("Invalid escape char");\n}\nfunction toTitleCase(name) {\n return name.charAt(0).toUpperCase() + name.substring(1);\n}\nfunction toSnakeCase(name) {\n return name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([A-Z])([A-Z][a-z])/g, "$1_$2").toLowerCase();\n}\nfunction quoteCSSAttributeValue(text) {\n return `"${text.replace(/["\\\\]/g, (char) => "\\\\" + char)}"`;\n}\nvar normalizedWhitespaceCache;\nfunction cacheNormalizedWhitespaces() {\n normalizedWhitespaceCache = /* @__PURE__ */ new Map();\n}\nfunction normalizeWhiteSpace(text) {\n let result = normalizedWhitespaceCache == null ? void 0 : normalizedWhitespaceCache.get(text);\n if (result === void 0) {\n result = text.replace(/[\\u200b\\u00ad]/g, "").trim().replace(/\\s+/g, " ");\n normalizedWhitespaceCache == null ? void 0 : normalizedWhitespaceCache.set(text, result);\n }\n return result;\n}\nfunction normalizeEscapedRegexQuotes(source) {\n return source.replace(/(^|[^\\\\])(\\\\\\\\)*\\\\([\'"`])/g, "$1$2$3");\n}\nfunction escapeRegexForSelector(re) {\n if (re.unicode || re.unicodeSets)\n return String(re);\n return String(re).replace(/(^|[^\\\\])(\\\\\\\\)*(["\'`])/g, "$1$2\\\\$3").replace(/>>/g, "\\\\>\\\\>");\n}\nfunction escapeForTextSelector(text, exact) {\n if (typeof text !== "string")\n return escapeRegexForSelector(text);\n return `${JSON.stringify(text)}${exact ? "s" : "i"}`;\n}\nfunction escapeForAttributeSelector(value, exact) {\n if (typeof value !== "string")\n return escapeRegexForSelector(value);\n return `"${value.replace(/\\\\/g, "\\\\\\\\").replace(/["]/g, \'\\\\"\')}"${exact ? "s" : "i"}`;\n}\nfunction trimString(input, cap, suffix = "") {\n if (input.length <= cap)\n return input;\n const chars = [...input];\n if (chars.length > cap)\n return chars.slice(0, cap - suffix.length).join("") + suffix;\n return chars.join("");\n}\nfunction trimStringWithEllipsis(input, cap) {\n return trimString(input, cap, "\\u2026");\n}\nfunction escapeRegExp(s) {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\\\$&");\n}\nfunction longestCommonSubstring(s1, s2) {\n const n = s1.length;\n const m = s2.length;\n let maxLen = 0;\n let endingIndex = 0;\n const dp = Array(n + 1).fill(null).map(() => Array(m + 1).fill(0));\n for (let i = 1; i <= n; i++) {\n for (let j = 1; j <= m; j++) {\n if (s1[i - 1] === s2[j - 1]) {\n dp[i][j] = dp[i - 1][j - 1] + 1;\n if (dp[i][j] > maxLen) {\n maxLen = dp[i][j];\n endingIndex = i;\n }\n }\n }\n }\n return s1.slice(endingIndex - maxLen, endingIndex);\n}\nvar ansiRegex = new RegExp("([\\\\u001B\\\\u009B][[\\\\]()#?]*(?:(?:(?:[a-zA-Z\\\\d]*(?:;[-a-zA-Z\\\\d\\\\/#&.:=?%@~_]*)*)?\\\\u0007)|(?:(?:\\\\d{0,4}(?:;\\\\d{0,4})*)?[\\\\dA-PR-TZcf-ntqry=><~])))", "g");\n\n// packages/isomorphic/locatorGenerators.ts\nfunction asLocator(lang, selector, isFrameLocator = false) {\n return asLocators(lang, selector, isFrameLocator, 1)[0];\n}\nfunction asLocators(lang, selector, isFrameLocator = false, maxOutputSize = 20, preferredQuote) {\n try {\n return innerAsLocators(new generators[lang](preferredQuote), parseSelector(selector), isFrameLocator, maxOutputSize);\n } catch (e) {\n return [selector];\n }\n}\nfunction innerAsLocators(factory, parsed, isFrameLocator = false, maxOutputSize = 20) {\n const parts = [...parsed.parts];\n const tokens = [];\n let nextBase = isFrameLocator ? "frame-locator" : "page";\n for (let index = 0; index < parts.length; index++) {\n const part = parts[index];\n const base = nextBase;\n nextBase = "locator";\n if (part.name === "internal:describe")\n continue;\n if (part.name === "nth") {\n if (part.body === "0")\n tokens.push([factory.generateLocator(base, "first", ""), factory.generateLocator(base, "nth", "0")]);\n else if (part.body === "-1")\n tokens.push([factory.generateLocator(base, "last", ""), factory.generateLocator(base, "nth", "-1")]);\n else\n tokens.push([factory.generateLocator(base, "nth", part.body)]);\n continue;\n }\n if (part.name === "visible") {\n tokens.push([factory.generateLocator(base, "visible", part.body), factory.generateLocator(base, "default", `visible=${part.body}`)]);\n continue;\n }\n if (part.name === "internal:text") {\n const { exact, text } = detectExact(part.body);\n tokens.push([factory.generateLocator(base, "text", text, { exact })]);\n continue;\n }\n if (part.name === "internal:has-text") {\n const { exact, text } = detectExact(part.body);\n if (!exact) {\n tokens.push([factory.generateLocator(base, "has-text", text, { exact })]);\n continue;\n }\n }\n if (part.name === "internal:has-not-text") {\n const { exact, text } = detectExact(part.body);\n if (!exact) {\n tokens.push([factory.generateLocator(base, "has-not-text", text, { exact })]);\n continue;\n }\n }\n if (part.name === "internal:has") {\n const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize);\n tokens.push(inners.map((inner) => factory.generateLocator(base, "has", inner)));\n continue;\n }\n if (part.name === "internal:has-not") {\n const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize);\n tokens.push(inners.map((inner) => factory.generateLocator(base, "hasNot", inner)));\n continue;\n }\n if (part.name === "internal:and") {\n const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize);\n tokens.push(inners.map((inner) => factory.generateLocator(base, "and", inner)));\n continue;\n }\n if (part.name === "internal:or") {\n const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize);\n tokens.push(inners.map((inner) => factory.generateLocator(base, "or", inner)));\n continue;\n }\n if (part.name === "internal:chain") {\n const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize);\n tokens.push(inners.map((inner) => factory.generateLocator(base, "chain", inner)));\n continue;\n }\n if (part.name === "internal:label") {\n const { exact, text } = detectExact(part.body);\n tokens.push([factory.generateLocator(base, "label", text, { exact })]);\n continue;\n }\n if (part.name === "internal:role") {\n const attrSelector = parseAttributeSelector(part.body, true);\n const options = { attrs: [] };\n for (const attr of attrSelector.attributes) {\n if (attr.name === "name") {\n if (options.exact !== void 0 && options.exact !== attr.caseSensitive)\n throw new Error(`Conflicting exactness in internal:role selector: ${stringifySelector({ parts: [part] })}`);\n options.exact = attr.caseSensitive;\n options.name = attr.value;\n } else if (attr.name === "description") {\n if (options.exact !== void 0 && options.exact !== attr.caseSensitive)\n throw new Error(`Conflicting exactness in internal:role selector: ${stringifySelector({ parts: [part] })}`);\n options.exact = attr.caseSensitive;\n options.description = attr.value;\n } else {\n if (attr.name === "level" && typeof attr.value === "string")\n attr.value = +attr.value;\n options.attrs.push({ name: attr.name === "include-hidden" ? "includeHidden" : attr.name, value: attr.value });\n }\n }\n tokens.push([factory.generateLocator(base, "role", attrSelector.name, options)]);\n continue;\n }\n if (part.name === "internal:testid") {\n const attrSelector = parseAttributeSelector(part.body, true);\n const { value } = attrSelector.attributes[0];\n tokens.push([factory.generateLocator(base, "test-id", value)]);\n continue;\n }\n if (part.name === "internal:attr") {\n const attrSelector = parseAttributeSelector(part.body, true);\n const { name, value, caseSensitive } = attrSelector.attributes[0];\n const text = value;\n const exact = !!caseSensitive;\n if (name === "placeholder") {\n tokens.push([factory.generateLocator(base, "placeholder", text, { exact })]);\n continue;\n }\n if (name === "alt") {\n tokens.push([factory.generateLocator(base, "alt", text, { exact })]);\n continue;\n }\n if (name === "title") {\n tokens.push([factory.generateLocator(base, "title", text, { exact })]);\n continue;\n }\n }\n if (part.name === "internal:control" && part.body === "enter-frame") {\n const lastTokens = tokens[tokens.length - 1];\n const lastPart = parts[index - 1];\n const transformed = lastTokens.map((token) => factory.chainLocators([token, factory.generateLocator(base, "frame", "")]));\n if (["xpath", "css"].includes(lastPart.name)) {\n transformed.push(\n factory.generateLocator(base, "frame-locator", stringifySelector({ parts: [lastPart] })),\n factory.generateLocator(base, "frame-locator", stringifySelector({ parts: [lastPart] }, true))\n );\n }\n lastTokens.splice(0, lastTokens.length, ...transformed);\n nextBase = "frame-locator";\n continue;\n }\n const nextPart = parts[index + 1];\n const selectorPart = stringifySelector({ parts: [part] });\n const locatorPart = factory.generateLocator(base, "default", selectorPart);\n if (nextPart && ["internal:has-text", "internal:has-not-text"].includes(nextPart.name)) {\n const { exact, text } = detectExact(nextPart.body);\n if (!exact) {\n const nextLocatorPart = factory.generateLocator("locator", nextPart.name === "internal:has-text" ? "has-text" : "has-not-text", text, { exact });\n const options = {};\n if (nextPart.name === "internal:has-text")\n options.hasText = text;\n else\n options.hasNotText = text;\n const combinedPart = factory.generateLocator(base, "default", selectorPart, options);\n tokens.push([factory.chainLocators([locatorPart, nextLocatorPart]), combinedPart]);\n index++;\n continue;\n }\n }\n let locatorPartWithEngine;\n if (["xpath", "css"].includes(part.name)) {\n const selectorPart2 = stringifySelector(\n { parts: [part] },\n /* forceEngineName */\n true\n );\n locatorPartWithEngine = factory.generateLocator(base, "default", selectorPart2);\n }\n tokens.push([locatorPart, locatorPartWithEngine].filter(Boolean));\n }\n return combineTokens(factory, tokens, maxOutputSize);\n}\nfunction combineTokens(factory, tokens, maxOutputSize) {\n const currentTokens = tokens.map(() => "");\n const result = [];\n const visit = (index) => {\n if (index === tokens.length) {\n result.push(factory.chainLocators(currentTokens));\n return result.length < maxOutputSize;\n }\n for (const taken of tokens[index]) {\n currentTokens[index] = taken;\n if (!visit(index + 1))\n return false;\n }\n return true;\n };\n visit(0);\n return result;\n}\nfunction detectExact(text) {\n let exact = false;\n const match = text.match(/^\\/(.*)\\/([igm]*)$/);\n if (match)\n return { text: new RegExp(match[1], match[2]) };\n if (text.endsWith(\'"\')) {\n text = JSON.parse(text);\n exact = true;\n } else if (text.endsWith(\'"s\')) {\n text = JSON.parse(text.substring(0, text.length - 1));\n exact = true;\n } else if (text.endsWith(\'"i\')) {\n text = JSON.parse(text.substring(0, text.length - 1));\n exact = false;\n }\n return { exact, text };\n}\nvar JavaScriptLocatorFactory = class {\n constructor(preferredQuote) {\n this.preferredQuote = preferredQuote;\n }\n generateLocator(base, kind, body, options = {}) {\n switch (kind) {\n case "default":\n if (options.hasText !== void 0)\n return `locator(${this.quote(body)}, { hasText: ${this.toHasText(options.hasText)} })`;\n if (options.hasNotText !== void 0)\n return `locator(${this.quote(body)}, { hasNotText: ${this.toHasText(options.hasNotText)} })`;\n return `locator(${this.quote(body)})`;\n case "frame-locator":\n return `frameLocator(${this.quote(body)})`;\n case "frame":\n return `contentFrame()`;\n case "nth":\n return `nth(${body})`;\n case "first":\n return `first()`;\n case "last":\n return `last()`;\n case "visible":\n return `filter({ visible: ${body === "true" ? "true" : "false"} })`;\n case "role":\n const attrs = [];\n if (isRegExp(options.name))\n attrs.push(`name: ${this.regexToSourceString(options.name)}`);\n else if (typeof options.name === "string")\n attrs.push(`name: ${this.quote(options.name)}`);\n if (isRegExp(options.description))\n attrs.push(`description: ${this.regexToSourceString(options.description)}`);\n else if (typeof options.description === "string")\n attrs.push(`description: ${this.quote(options.description)}`);\n if (options.exact && (typeof options.name === "string" || typeof options.description === "string"))\n attrs.push(`exact: true`);\n for (const { name, value } of options.attrs)\n attrs.push(`${name}: ${typeof value === "string" ? this.quote(value) : value}`);\n const attrString = attrs.length ? `, { ${attrs.join(", ")} }` : "";\n return `getByRole(${this.quote(body)}${attrString})`;\n case "has-text":\n return `filter({ hasText: ${this.toHasText(body)} })`;\n case "has-not-text":\n return `filter({ hasNotText: ${this.toHasText(body)} })`;\n case "has":\n return `filter({ has: ${body} })`;\n case "hasNot":\n return `filter({ hasNot: ${body} })`;\n case "and":\n return `and(${body})`;\n case "or":\n return `or(${body})`;\n case "chain":\n return `locator(${body})`;\n case "test-id":\n return `getByTestId(${this.toTestIdValue(body)})`;\n case "text":\n return this.toCallWithExact("getByText", body, !!options.exact);\n case "alt":\n return this.toCallWithExact("getByAltText", body, !!options.exact);\n case "placeholder":\n return this.toCallWithExact("getByPlaceholder", body, !!options.exact);\n case "label":\n return this.toCallWithExact("getByLabel", body, !!options.exact);\n case "title":\n return this.toCallWithExact("getByTitle", body, !!options.exact);\n default:\n throw new Error("Unknown selector kind " + kind);\n }\n }\n chainLocators(locators) {\n return locators.join(".");\n }\n regexToSourceString(re) {\n return normalizeEscapedRegexQuotes(String(re));\n }\n toCallWithExact(method, body, exact) {\n if (isRegExp(body))\n return `${method}(${this.regexToSourceString(body)})`;\n return exact ? `${method}(${this.quote(body)}, { exact: true })` : `${method}(${this.quote(body)})`;\n }\n toHasText(body) {\n if (isRegExp(body))\n return this.regexToSourceString(body);\n return this.quote(body);\n }\n toTestIdValue(value) {\n if (isRegExp(value))\n return this.regexToSourceString(value);\n return this.quote(value);\n }\n quote(text) {\n var _a;\n return escapeWithQuotes(text, (_a = this.preferredQuote) != null ? _a : "\'");\n }\n};\nvar PythonLocatorFactory = class {\n generateLocator(base, kind, body, options = {}) {\n switch (kind) {\n case "default":\n if (options.hasText !== void 0)\n return `locator(${this.quote(body)}, has_text=${this.toHasText(options.hasText)})`;\n if (options.hasNotText !== void 0)\n return `locator(${this.quote(body)}, has_not_text=${this.toHasText(options.hasNotText)})`;\n return `locator(${this.quote(body)})`;\n case "frame-locator":\n return `frame_locator(${this.quote(body)})`;\n case "frame":\n return `content_frame`;\n case "nth":\n return `nth(${body})`;\n case "first":\n return `first`;\n case "last":\n return `last`;\n case "visible":\n return `filter(visible=${body === "true" ? "True" : "False"})`;\n case "role":\n const attrs = [];\n if (isRegExp(options.name))\n attrs.push(`name=${this.regexToString(options.name)}`);\n else if (typeof options.name === "string")\n attrs.push(`name=${this.quote(options.name)}`);\n if (isRegExp(options.description))\n attrs.push(`description=${this.regexToString(options.description)}`);\n else if (typeof options.description === "string")\n attrs.push(`description=${this.quote(options.description)}`);\n if (options.exact && (typeof options.name === "string" || typeof options.description === "string"))\n attrs.push(`exact=True`);\n for (const { name, value } of options.attrs) {\n let valueString = typeof value === "string" ? this.quote(value) : value;\n if (typeof value === "boolean")\n valueString = value ? "True" : "False";\n attrs.push(`${toSnakeCase(name)}=${valueString}`);\n }\n const attrString = attrs.length ? `, ${attrs.join(", ")}` : "";\n return `get_by_role(${this.quote(body)}${attrString})`;\n case "has-text":\n return `filter(has_text=${this.toHasText(body)})`;\n case "has-not-text":\n return `filter(has_not_text=${this.toHasText(body)})`;\n case "has":\n return `filter(has=${body})`;\n case "hasNot":\n return `filter(has_not=${body})`;\n case "and":\n return `and_(${body})`;\n case "or":\n return `or_(${body})`;\n case "chain":\n return `locator(${body})`;\n case "test-id":\n return `get_by_test_id(${this.toTestIdValue(body)})`;\n case "text":\n return this.toCallWithExact("get_by_text", body, !!options.exact);\n case "alt":\n return this.toCallWithExact("get_by_alt_text", body, !!options.exact);\n case "placeholder":\n return this.toCallWithExact("get_by_placeholder", body, !!options.exact);\n case "label":\n return this.toCallWithExact("get_by_label", body, !!options.exact);\n case "title":\n return this.toCallWithExact("get_by_title", body, !!options.exact);\n default:\n throw new Error("Unknown selector kind " + kind);\n }\n }\n chainLocators(locators) {\n return locators.join(".");\n }\n regexToString(body) {\n const suffix = body.flags.includes("i") ? ", re.IGNORECASE" : "";\n return `re.compile(r"${normalizeEscapedRegexQuotes(body.source).replace(/\\\\\\//, "/").replace(/"/g, \'\\\\"\')}"${suffix})`;\n }\n toCallWithExact(method, body, exact) {\n if (isRegExp(body))\n return `${method}(${this.regexToString(body)})`;\n if (exact)\n return `${method}(${this.quote(body)}, exact=True)`;\n return `${method}(${this.quote(body)})`;\n }\n toHasText(body) {\n if (isRegExp(body))\n return this.regexToString(body);\n return `${this.quote(body)}`;\n }\n toTestIdValue(value) {\n if (isRegExp(value))\n return this.regexToString(value);\n return this.quote(value);\n }\n quote(text) {\n return escapeWithQuotes(text, \'"\');\n }\n};\nvar JavaLocatorFactory = class {\n generateLocator(base, kind, body, options = {}) {\n let clazz;\n switch (base) {\n case "page":\n clazz = "Page";\n break;\n case "frame-locator":\n clazz = "FrameLocator";\n break;\n case "locator":\n clazz = "Locator";\n break;\n }\n switch (kind) {\n case "default":\n if (options.hasText !== void 0)\n return `locator(${this.quote(body)}, new ${clazz}.LocatorOptions().setHasText(${this.toHasText(options.hasText)}))`;\n if (options.hasNotText !== void 0)\n return `locator(${this.quote(body)}, new ${clazz}.LocatorOptions().setHasNotText(${this.toHasText(options.hasNotText)}))`;\n return `locator(${this.quote(body)})`;\n case "frame-locator":\n return `frameLocator(${this.quote(body)})`;\n case "frame":\n return `contentFrame()`;\n case "nth":\n return `nth(${body})`;\n case "first":\n return `first()`;\n case "last":\n return `last()`;\n case "visible":\n return `filter(new ${clazz}.FilterOptions().setVisible(${body === "true" ? "true" : "false"}))`;\n case "role":\n const attrs = [];\n if (isRegExp(options.name))\n attrs.push(`.setName(${this.regexToString(options.name)})`);\n else if (typeof options.name === "string")\n attrs.push(`.setName(${this.quote(options.name)})`);\n if (isRegExp(options.description))\n attrs.push(`.setDescription(${this.regexToString(options.description)})`);\n else if (typeof options.description === "string")\n attrs.push(`.setDescription(${this.quote(options.description)})`);\n if (options.exact && (typeof options.name === "string" || typeof options.description === "string"))\n attrs.push(`.setExact(true)`);\n for (const { name, value } of options.attrs)\n attrs.push(`.set${toTitleCase(name)}(${typeof value === "string" ? this.quote(value) : value})`);\n const attrString = attrs.length ? `, new ${clazz}.GetByRoleOptions()${attrs.join("")}` : "";\n return `getByRole(AriaRole.${toSnakeCase(body).toUpperCase()}${attrString})`;\n case "has-text":\n return `filter(new ${clazz}.FilterOptions().setHasText(${this.toHasText(body)}))`;\n case "has-not-text":\n return `filter(new ${clazz}.FilterOptions().setHasNotText(${this.toHasText(body)}))`;\n case "has":\n return `filter(new ${clazz}.FilterOptions().setHas(${body}))`;\n case "hasNot":\n return `filter(new ${clazz}.FilterOptions().setHasNot(${body}))`;\n case "and":\n return `and(${body})`;\n case "or":\n return `or(${body})`;\n case "chain":\n return `locator(${body})`;\n case "test-id":\n return `getByTestId(${this.toTestIdValue(body)})`;\n case "text":\n return this.toCallWithExact(clazz, "getByText", body, !!options.exact);\n case "alt":\n return this.toCallWithExact(clazz, "getByAltText", body, !!options.exact);\n case "placeholder":\n return this.toCallWithExact(clazz, "getByPlaceholder", body, !!options.exact);\n case "label":\n return this.toCallWithExact(clazz, "getByLabel", body, !!options.exact);\n case "title":\n return this.toCallWithExact(clazz, "getByTitle", body, !!options.exact);\n default:\n throw new Error("Unknown selector kind " + kind);\n }\n }\n chainLocators(locators) {\n return locators.join(".");\n }\n regexToString(body) {\n const suffix = body.flags.includes("i") ? ", Pattern.CASE_INSENSITIVE" : "";\n return `Pattern.compile(${this.quote(normalizeEscapedRegexQuotes(body.source))}${suffix})`;\n }\n toCallWithExact(clazz, method, body, exact) {\n if (isRegExp(body))\n return `${method}(${this.regexToString(body)})`;\n if (exact)\n return `${method}(${this.quote(body)}, new ${clazz}.${toTitleCase(method)}Options().setExact(true))`;\n return `${method}(${this.quote(body)})`;\n }\n toHasText(body) {\n if (isRegExp(body))\n return this.regexToString(body);\n return this.quote(body);\n }\n toTestIdValue(value) {\n if (isRegExp(value))\n return this.regexToString(value);\n return this.quote(value);\n }\n quote(text) {\n return escapeWithQuotes(text, \'"\');\n }\n};\nvar CSharpLocatorFactory = class {\n generateLocator(base, kind, body, options = {}) {\n switch (kind) {\n case "default":\n if (options.hasText !== void 0)\n return `Locator(${this.quote(body)}, new() { ${this.toHasText(options.hasText)} })`;\n if (options.hasNotText !== void 0)\n return `Locator(${this.quote(body)}, new() { ${this.toHasNotText(options.hasNotText)} })`;\n return `Locator(${this.quote(body)})`;\n case "frame-locator":\n return `FrameLocator(${this.quote(body)})`;\n case "frame":\n return `ContentFrame`;\n case "nth":\n return `Nth(${body})`;\n case "first":\n return `First`;\n case "last":\n return `Last`;\n case "visible":\n return `Filter(new() { Visible = ${body === "true" ? "true" : "false"} })`;\n case "role":\n const attrs = [];\n if (isRegExp(options.name))\n attrs.push(`NameRegex = ${this.regexToString(options.name)}`);\n else if (typeof options.name === "string")\n attrs.push(`Name = ${this.quote(options.name)}`);\n if (isRegExp(options.description))\n attrs.push(`DescriptionRegex = ${this.regexToString(options.description)}`);\n else if (typeof options.description === "string")\n attrs.push(`Description = ${this.quote(options.description)}`);\n if (options.exact && (typeof options.name === "string" || typeof options.description === "string"))\n attrs.push(`Exact = true`);\n for (const { name, value } of options.attrs)\n attrs.push(`${toTitleCase(name)} = ${typeof value === "string" ? this.quote(value) : value}`);\n const attrString = attrs.length ? `, new() { ${attrs.join(", ")} }` : "";\n return `GetByRole(AriaRole.${toTitleCase(body)}${attrString})`;\n case "has-text":\n return `Filter(new() { ${this.toHasText(body)} })`;\n case "has-not-text":\n return `Filter(new() { ${this.toHasNotText(body)} })`;\n case "has":\n return `Filter(new() { Has = ${body} })`;\n case "hasNot":\n return `Filter(new() { HasNot = ${body} })`;\n case "and":\n return `And(${body})`;\n case "or":\n return `Or(${body})`;\n case "chain":\n return `Locator(${body})`;\n case "test-id":\n return `GetByTestId(${this.toTestIdValue(body)})`;\n case "text":\n return this.toCallWithExact("GetByText", body, !!options.exact);\n case "alt":\n return this.toCallWithExact("GetByAltText", body, !!options.exact);\n case "placeholder":\n return this.toCallWithExact("GetByPlaceholder", body, !!options.exact);\n case "label":\n return this.toCallWithExact("GetByLabel", body, !!options.exact);\n case "title":\n return this.toCallWithExact("GetByTitle", body, !!options.exact);\n default:\n throw new Error("Unknown selector kind " + kind);\n }\n }\n chainLocators(locators) {\n return locators.join(".");\n }\n regexToString(body) {\n const suffix = body.flags.includes("i") ? ", RegexOptions.IgnoreCase" : "";\n return `new Regex(${this.quote(normalizeEscapedRegexQuotes(body.source))}${suffix})`;\n }\n toCallWithExact(method, body, exact) {\n if (isRegExp(body))\n return `${method}(${this.regexToString(body)})`;\n if (exact)\n return `${method}(${this.quote(body)}, new() { Exact = true })`;\n return `${method}(${this.quote(body)})`;\n }\n toHasText(body) {\n if (isRegExp(body))\n return `HasTextRegex = ${this.regexToString(body)}`;\n return `HasText = ${this.quote(body)}`;\n }\n toTestIdValue(value) {\n if (isRegExp(value))\n return this.regexToString(value);\n return this.quote(value);\n }\n toHasNotText(body) {\n if (isRegExp(body))\n return `HasNotTextRegex = ${this.regexToString(body)}`;\n return `HasNotText = ${this.quote(body)}`;\n }\n quote(text) {\n return escapeWithQuotes(text, \'"\');\n }\n};\nvar JsonlLocatorFactory = class {\n generateLocator(base, kind, body, options = {}) {\n return JSON.stringify({\n kind,\n body,\n options\n });\n }\n chainLocators(locators) {\n const objects = locators.map((l) => JSON.parse(l));\n for (let i = 0; i < objects.length - 1; ++i)\n objects[i].next = objects[i + 1];\n return JSON.stringify(objects[0]);\n }\n};\nvar generators = {\n javascript: JavaScriptLocatorFactory,\n python: PythonLocatorFactory,\n java: JavaLocatorFactory,\n csharp: CSharpLocatorFactory,\n jsonl: JsonlLocatorFactory\n};\nfunction isRegExp(obj) {\n return obj instanceof RegExp;\n}\n\n// packages/isomorphic/locatorUtils.ts\nfunction getByAttributeTextSelector(attrName, text, options) {\n return `internal:attr=[${attrName}=${escapeForAttributeSelector(text, (options == null ? void 0 : options.exact) || false)}]`;\n}\nfunction splitTestIdAttributeNames(testIdAttributeName) {\n return testIdAttributeName.split(",");\n}\nfunction encodeTestIdAttributeName(testIdAttributeName) {\n return testIdAttributeName.includes(",") ? JSON.stringify(testIdAttributeName) : testIdAttributeName;\n}\nfunction getByTestIdSelector(testIdAttributeName, testId) {\n return `internal:testid=[${encodeTestIdAttributeName(testIdAttributeName)}=${escapeForAttributeSelector(testId, true)}]`;\n}\nfunction getByLabelSelector(text, options) {\n return "internal:label=" + escapeForTextSelector(text, !!(options == null ? void 0 : options.exact));\n}\nfunction getByAltTextSelector(text, options) {\n return getByAttributeTextSelector("alt", text, options);\n}\nfunction getByTitleSelector(text, options) {\n return getByAttributeTextSelector("title", text, options);\n}\nfunction getByPlaceholderSelector(text, options) {\n return getByAttributeTextSelector("placeholder", text, options);\n}\nfunction getByTextSelector(text, options) {\n return "internal:text=" + escapeForTextSelector(text, !!(options == null ? void 0 : options.exact));\n}\nfunction getByRoleSelector(role, options = {}) {\n const props = [];\n if (options.checked !== void 0)\n props.push(["checked", String(options.checked)]);\n if (options.disabled !== void 0)\n props.push(["disabled", String(options.disabled)]);\n if (options.selected !== void 0)\n props.push(["selected", String(options.selected)]);\n if (options.expanded !== void 0)\n props.push(["expanded", String(options.expanded)]);\n if (options.includeHidden !== void 0)\n props.push(["include-hidden", String(options.includeHidden)]);\n if (options.level !== void 0)\n props.push(["level", String(options.level)]);\n if (options.name !== void 0)\n props.push(["name", escapeForAttributeSelector(options.name, !!options.exact)]);\n if (options.description !== void 0)\n props.push(["description", escapeForAttributeSelector(options.description, !!options.exact)]);\n if (options.pressed !== void 0)\n props.push(["pressed", String(options.pressed)]);\n return `internal:role=${role}${props.map(([n, v]) => `[${n}=${v}]`).join("")}`;\n}\n\n// packages/isomorphic/yaml.ts\nfunction yamlEscapeKeyIfNeeded(str) {\n if (!yamlStringNeedsQuotes(str))\n return str;\n return `\'` + str.replace(/\'/g, `\'\'`) + `\'`;\n}\nfunction yamlEscapeValueIfNeeded(str) {\n if (!yamlStringNeedsQuotes(str))\n return str;\n return \'"\' + str.replace(/[\\\\"\\x00-\\x1f\\x7f-\\x9f]/g, (c) => {\n switch (c) {\n case "\\\\":\n return "\\\\\\\\";\n case \'"\':\n return \'\\\\"\';\n case "\\b":\n return "\\\\b";\n case "\\f":\n return "\\\\f";\n case "\\n":\n return "\\\\n";\n case "\\r":\n return "\\\\r";\n case " ":\n return "\\\\t";\n default:\n const code = c.charCodeAt(0);\n return "\\\\x" + code.toString(16).padStart(2, "0");\n }\n }) + \'"\';\n}\nfunction yamlStringNeedsQuotes(str) {\n if (str.length === 0)\n return true;\n if (/^\\s|\\s$/.test(str))\n return true;\n if (/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f-\\x9f]/.test(str))\n return true;\n if (/^-/.test(str))\n return true;\n if (/[\\n:](\\s|$)/.test(str))\n return true;\n if (/\\s#/.test(str))\n return true;\n if (/[\\n\\r]/.test(str))\n return true;\n if (/^[&*\\],?!>|@"\'#%]/.test(str))\n return true;\n if (/[{}`]/.test(str))\n return true;\n if (/^\\[/.test(str))\n return true;\n if (!isNaN(Number(str)) || ["y", "n", "yes", "no", "true", "false", "on", "off", "null"].includes(str.toLowerCase()))\n return true;\n return false;\n}\n\n// packages/injected/src/domUtils.ts\nvar globalOptions = {};\nfunction setGlobalOptions(options) {\n globalOptions = options;\n}\nfunction isInsideScope(scope, element) {\n while (element) {\n if (scope.contains(element))\n return true;\n element = enclosingShadowHost(element);\n }\n return false;\n}\nfunction parentElementOrShadowHost(element) {\n if (element.parentElement)\n return element.parentElement;\n if (!element.parentNode)\n return;\n if (element.parentNode.nodeType === 11 && element.parentNode.host)\n return element.parentNode.host;\n}\nfunction enclosingShadowRootOrDocument(element) {\n let node = element;\n while (node.parentNode)\n node = node.parentNode;\n if (node.nodeType === 11 || node.nodeType === 9)\n return node;\n}\nfunction enclosingShadowHost(element) {\n while (element.parentElement)\n element = element.parentElement;\n return parentElementOrShadowHost(element);\n}\nfunction closestCrossShadow(element, css, scope) {\n while (element) {\n const closest = element.closest(css);\n if (scope && closest !== scope && (closest == null ? void 0 : closest.contains(scope)))\n return;\n if (closest)\n return closest;\n element = enclosingShadowHost(element);\n }\n}\nfunction getElementComputedStyle(element, pseudo) {\n const cache = pseudo === "::before" ? cacheStyleBefore : pseudo === "::after" ? cacheStyleAfter : cacheStyle;\n if (cache && cache.has(element))\n return cache.get(element);\n const style = element.ownerDocument && element.ownerDocument.defaultView ? element.ownerDocument.defaultView.getComputedStyle(element, pseudo) : void 0;\n cache == null ? void 0 : cache.set(element, style);\n return style;\n}\nfunction isElementStyleVisibilityVisible(element, style) {\n style = style != null ? style : getElementComputedStyle(element);\n if (!style)\n return true;\n if (Element.prototype.checkVisibility && globalOptions.browserNameForWorkarounds !== "webkit") {\n if (!element.checkVisibility())\n return false;\n } else {\n const detailsOrSummary = element.closest("details,summary");\n if (detailsOrSummary !== element && (detailsOrSummary == null ? void 0 : detailsOrSummary.nodeName) === "DETAILS" && !detailsOrSummary.open)\n return false;\n }\n if (style.visibility !== "visible")\n return false;\n return true;\n}\nfunction computeBox(element) {\n const style = getElementComputedStyle(element);\n if (!style)\n return { visible: true, inline: false };\n const cursor = style.cursor;\n if (style.display === "contents") {\n for (let child = element.firstChild; child; child = child.nextSibling) {\n if (child.nodeType === 1 && isElementVisible(child))\n return { visible: true, inline: false, cursor };\n if (child.nodeType === 3 && isVisibleTextNode(child))\n return { visible: true, inline: true, cursor };\n }\n return { visible: false, inline: false, cursor };\n }\n if (!isElementStyleVisibilityVisible(element, style))\n return { cursor, visible: false, inline: false };\n const rect = element.getBoundingClientRect();\n return { cursor, visible: rect.width > 0 && rect.height > 0, inline: style.display === "inline" };\n}\nfunction isElementVisible(element) {\n return computeBox(element).visible;\n}\nfunction isVisibleTextNode(node) {\n const range = node.ownerDocument.createRange();\n range.selectNode(node);\n const rect = range.getBoundingClientRect();\n return rect.width > 0 && rect.height > 0;\n}\nfunction elementSafeTagName(element) {\n const tagName = element.tagName;\n if (typeof tagName === "string")\n return tagName.toUpperCase();\n if (element instanceof HTMLFormElement)\n return "FORM";\n return element.tagName.toUpperCase();\n}\nvar cacheStyle;\nvar cacheStyleBefore;\nvar cacheStyleAfter;\nvar cachesCounter = 0;\nfunction beginDOMCaches() {\n ++cachesCounter;\n cacheStyle != null ? cacheStyle : cacheStyle = /* @__PURE__ */ new Map();\n cacheStyleBefore != null ? cacheStyleBefore : cacheStyleBefore = /* @__PURE__ */ new Map();\n cacheStyleAfter != null ? cacheStyleAfter : cacheStyleAfter = /* @__PURE__ */ new Map();\n}\nfunction endDOMCaches() {\n if (!--cachesCounter) {\n cacheStyle = void 0;\n cacheStyleBefore = void 0;\n cacheStyleAfter = void 0;\n }\n}\n\n// packages/injected/src/roleUtils.ts\nfunction hasExplicitAccessibleName(e) {\n return e.hasAttribute("aria-label") || e.hasAttribute("aria-labelledby");\n}\nvar kAncestorPreventingLandmark = "article:not([role]), aside:not([role]), main:not([role]), nav:not([role]), section:not([role]), [role=article], [role=complementary], [role=main], [role=navigation], [role=region]";\nvar kGlobalAriaAttributes = [\n ["aria-atomic", void 0],\n ["aria-busy", void 0],\n ["aria-controls", void 0],\n ["aria-current", void 0],\n ["aria-describedby", void 0],\n ["aria-details", void 0],\n // Global use deprecated in ARIA 1.2\n // [\'aria-disabled\', undefined],\n ["aria-dropeffect", void 0],\n // Global use deprecated in ARIA 1.2\n // [\'aria-errormessage\', undefined],\n ["aria-flowto", void 0],\n ["aria-grabbed", void 0],\n // Global use deprecated in ARIA 1.2\n // [\'aria-haspopup\', undefined],\n ["aria-hidden", void 0],\n // Global use deprecated in ARIA 1.2\n // [\'aria-invalid\', undefined],\n ["aria-keyshortcuts", void 0],\n ["aria-label", ["caption", "code", "deletion", "emphasis", "generic", "insertion", "paragraph", "presentation", "strong", "subscript", "superscript"]],\n ["aria-labelledby", ["caption", "code", "deletion", "emphasis", "generic", "insertion", "paragraph", "presentation", "strong", "subscript", "superscript"]],\n ["aria-live", void 0],\n ["aria-owns", void 0],\n ["aria-relevant", void 0],\n ["aria-roledescription", ["generic"]]\n];\nfunction hasGlobalAriaAttribute(element, forRole) {\n return kGlobalAriaAttributes.some(([attr, prohibited]) => {\n return !(prohibited == null ? void 0 : prohibited.includes(forRole || "")) && element.hasAttribute(attr);\n });\n}\nfunction hasTabIndex(element) {\n return !Number.isNaN(Number(String(element.getAttribute("tabindex"))));\n}\nfunction isFocusable(element) {\n return !isNativelyDisabled(element) && (isNativelyFocusable(element) || hasTabIndex(element));\n}\nfunction isNativelyFocusable(element) {\n const tagName = elementSafeTagName(element);\n if (["BUTTON", "DETAILS", "SELECT", "TEXTAREA"].includes(tagName))\n return true;\n if (tagName === "A" || tagName === "AREA")\n return element.hasAttribute("href");\n if (tagName === "INPUT")\n return !element.hidden;\n return false;\n}\nvar kImplicitRoleByTagName = {\n "A": (e) => {\n return e.hasAttribute("href") ? "link" : null;\n },\n "AREA": (e) => {\n return e.hasAttribute("href") ? "link" : null;\n },\n "ARTICLE": () => "article",\n "ASIDE": () => "complementary",\n "BLOCKQUOTE": () => "blockquote",\n "BUTTON": () => "button",\n "CAPTION": () => "caption",\n "CODE": () => "code",\n "DATALIST": () => "listbox",\n "DD": () => "definition",\n "DEL": () => "deletion",\n "DETAILS": () => "group",\n "DFN": () => "term",\n "DIALOG": () => "dialog",\n "DT": () => "term",\n "EM": () => "emphasis",\n "FIELDSET": () => "group",\n "FIGURE": () => "figure",\n "FOOTER": (e) => closestCrossShadow(e, kAncestorPreventingLandmark) ? null : "contentinfo",\n "FORM": (e) => hasExplicitAccessibleName(e) ? "form" : null,\n "H1": () => "heading",\n "H2": () => "heading",\n "H3": () => "heading",\n "H4": () => "heading",\n "H5": () => "heading",\n "H6": () => "heading",\n "HEADER": (e) => closestCrossShadow(e, kAncestorPreventingLandmark) ? null : "banner",\n "HR": () => "separator",\n "HTML": () => "document",\n "IMG": (e) => e.getAttribute("alt") === "" && !e.getAttribute("title") && !hasGlobalAriaAttribute(e) && !hasTabIndex(e) ? "presentation" : "img",\n "INPUT": (e) => {\n const type = e.type.toLowerCase();\n if (type === "search")\n return e.hasAttribute("list") ? "combobox" : "searchbox";\n if (["email", "tel", "text", "url", ""].includes(type)) {\n const list = getIdRefs(e, e.getAttribute("list"))[0];\n return list && elementSafeTagName(list) === "DATALIST" ? "combobox" : "textbox";\n }\n if (type === "hidden")\n return null;\n if (type === "file")\n return "button";\n return inputTypeToRole[type] || "textbox";\n },\n "INS": () => "insertion",\n "LI": () => "listitem",\n "MAIN": () => "main",\n "MARK": () => "mark",\n "MATH": () => "math",\n "MENU": () => "list",\n "METER": () => "meter",\n "NAV": () => "navigation",\n "OL": () => "list",\n "OPTGROUP": () => "group",\n "OPTION": () => "option",\n "OUTPUT": () => "status",\n "P": () => "paragraph",\n "PROGRESS": () => "progressbar",\n "SEARCH": () => "search",\n "SECTION": (e) => hasExplicitAccessibleName(e) ? "region" : null,\n "SELECT": (e) => e.hasAttribute("multiple") || e.size > 1 ? "listbox" : "combobox",\n "STRONG": () => "strong",\n "SUB": () => "subscript",\n "SUP": () => "superscript",\n // For we default to Chrome behavior:\n // - Chrome reports \'img\'.\n // - Firefox reports \'diagram\' that is not in official ARIA spec yet.\n // - Safari reports \'no role\', but still computes accessible name.\n "SVG": () => "img",\n "TABLE": () => "table",\n "TBODY": () => "rowgroup",\n "TD": (e) => {\n const table = closestCrossShadow(e, "table");\n const role = table ? getExplicitAriaRole(table) : "";\n return role === "grid" || role === "treegrid" ? "gridcell" : "cell";\n },\n "TEXTAREA": () => "textbox",\n "TFOOT": () => "rowgroup",\n "TH": (e) => {\n const scope = e.getAttribute("scope");\n if (scope === "col" || scope === "colgroup")\n return "columnheader";\n if (scope === "row" || scope === "rowgroup")\n return "rowheader";\n const nextSibling = e.nextElementSibling;\n const prevSibling = e.previousElementSibling;\n const row = !!e.parentElement && elementSafeTagName(e.parentElement) === "TR" ? e.parentElement : void 0;\n if (!nextSibling && !prevSibling) {\n if (row) {\n const table = closestCrossShadow(row, "table");\n if (table && table.rows.length <= 1)\n return null;\n }\n return "columnheader";\n }\n if (isHeaderCell(nextSibling) && isHeaderCell(prevSibling))\n return "columnheader";\n if (isNonEmptyDataCell(nextSibling) || isNonEmptyDataCell(prevSibling))\n return "rowheader";\n return "columnheader";\n },\n "THEAD": () => "rowgroup",\n "TIME": () => "time",\n "TR": () => "row",\n "UL": () => "list"\n};\nfunction isHeaderCell(element) {\n return !!element && elementSafeTagName(element) === "TH";\n}\nfunction isNonEmptyDataCell(element) {\n var _a;\n if (!element || elementSafeTagName(element) !== "TD")\n return false;\n return !!(((_a = element.textContent) == null ? void 0 : _a.trim()) || element.children.length > 0);\n}\nvar kPresentationInheritanceParents = {\n "DD": ["DL", "DIV"],\n "DIV": ["DL"],\n "DT": ["DL", "DIV"],\n "LI": ["OL", "UL"],\n "TBODY": ["TABLE"],\n "TD": ["TR"],\n "TFOOT": ["TABLE"],\n "TH": ["TR"],\n "THEAD": ["TABLE"],\n "TR": ["THEAD", "TBODY", "TFOOT", "TABLE"]\n};\nfunction getImplicitAriaRole(element) {\n var _a;\n const implicitRole = ((_a = kImplicitRoleByTagName[elementSafeTagName(element)]) == null ? void 0 : _a.call(kImplicitRoleByTagName, element)) || "";\n if (!implicitRole)\n return null;\n let ancestor = element;\n while (ancestor) {\n const parent = parentElementOrShadowHost(ancestor);\n const parents = kPresentationInheritanceParents[elementSafeTagName(ancestor)];\n if (!parents || !parent || !parents.includes(elementSafeTagName(parent)))\n break;\n const parentExplicitRole = getExplicitAriaRole(parent);\n if ((parentExplicitRole === "none" || parentExplicitRole === "presentation") && !hasPresentationConflictResolution(parent, parentExplicitRole))\n return parentExplicitRole;\n ancestor = parent;\n }\n return implicitRole;\n}\nvar validRoles = [\n "alert",\n "alertdialog",\n "application",\n "article",\n "banner",\n "blockquote",\n "button",\n "caption",\n "cell",\n "checkbox",\n "code",\n "columnheader",\n "combobox",\n "complementary",\n "contentinfo",\n "definition",\n "deletion",\n "dialog",\n "directory",\n "document",\n "emphasis",\n "feed",\n "figure",\n "form",\n "generic",\n "grid",\n "gridcell",\n "group",\n "heading",\n "img",\n "insertion",\n "link",\n "list",\n "listbox",\n "listitem",\n "log",\n "main",\n "mark",\n "marquee",\n "math",\n "meter",\n "menu",\n "menubar",\n "menuitem",\n "menuitemcheckbox",\n "menuitemradio",\n "navigation",\n "none",\n "note",\n "option",\n "paragraph",\n "presentation",\n "progressbar",\n "radio",\n "radiogroup",\n "region",\n "row",\n "rowgroup",\n "rowheader",\n "scrollbar",\n "search",\n "searchbox",\n "separator",\n "slider",\n "spinbutton",\n "status",\n "strong",\n "subscript",\n "superscript",\n "switch",\n "tab",\n "table",\n "tablist",\n "tabpanel",\n "term",\n "textbox",\n "time",\n "timer",\n "toolbar",\n "tooltip",\n "tree",\n "treegrid",\n "treeitem"\n];\nfunction getExplicitAriaRole(element) {\n const roles = (element.getAttribute("role") || "").split(" ").map((role) => role.trim());\n return roles.find((role) => validRoles.includes(role)) || null;\n}\nfunction hasPresentationConflictResolution(element, role) {\n return hasGlobalAriaAttribute(element, role) || isFocusable(element);\n}\nfunction getAriaRole(element) {\n const explicitRole = getExplicitAriaRole(element);\n if (!explicitRole)\n return getImplicitAriaRole(element);\n if (explicitRole === "none" || explicitRole === "presentation") {\n const implicitRole = getImplicitAriaRole(element);\n if (hasPresentationConflictResolution(element, implicitRole))\n return implicitRole;\n }\n return explicitRole;\n}\nfunction getAriaBoolean(attr) {\n return attr === null ? void 0 : attr.toLowerCase() === "true";\n}\nfunction isElementIgnoredForAria(element) {\n return ["STYLE", "SCRIPT", "NOSCRIPT", "TEMPLATE"].includes(elementSafeTagName(element));\n}\nfunction isElementHiddenForAria(element) {\n if (isElementIgnoredForAria(element))\n return true;\n const style = getElementComputedStyle(element);\n const isSlot = element.nodeName === "SLOT";\n if ((style == null ? void 0 : style.display) === "contents" && !isSlot) {\n for (let child = element.firstChild; child; child = child.nextSibling) {\n if (child.nodeType === 1 && !isElementHiddenForAria(child))\n return false;\n if (child.nodeType === 3 && isVisibleTextNode(child))\n return false;\n }\n return true;\n }\n const isOptionInsideSelect = element.nodeName === "OPTION" && !!element.closest("select");\n if (!isOptionInsideSelect && !isSlot && !isElementStyleVisibilityVisible(element, style))\n return true;\n return belongsToDisplayNoneOrAriaHiddenOrNonSlotted(element);\n}\nfunction belongsToDisplayNoneOrAriaHiddenOrNonSlotted(element) {\n let hidden = cacheIsHidden == null ? void 0 : cacheIsHidden.get(element);\n if (hidden === void 0) {\n hidden = false;\n if (element.parentElement && element.parentElement.shadowRoot && !element.assignedSlot)\n hidden = true;\n if (!hidden) {\n const style = getElementComputedStyle(element);\n hidden = !style || style.display === "none" || getAriaBoolean(element.getAttribute("aria-hidden")) === true;\n }\n if (!hidden) {\n const parent = parentElementOrShadowHost(element);\n if (parent)\n hidden = belongsToDisplayNoneOrAriaHiddenOrNonSlotted(parent);\n }\n cacheIsHidden == null ? void 0 : cacheIsHidden.set(element, hidden);\n }\n return hidden;\n}\nfunction getIdRefs(element, ref) {\n if (!ref)\n return [];\n const root = enclosingShadowRootOrDocument(element);\n if (!root)\n return [];\n try {\n const ids = ref.split(" ").filter((id) => !!id);\n const result = [];\n for (const id of ids) {\n const firstElement = root.querySelector("#" + CSS.escape(id));\n if (firstElement && !result.includes(firstElement))\n result.push(firstElement);\n }\n return result;\n } catch (e) {\n return [];\n }\n}\nfunction trimFlatString(s) {\n return s.trim();\n}\nfunction asFlatString(s) {\n return s.split("\\xA0").map((chunk) => chunk.replace(/\\r\\n/g, "\\n").replace(/[\\u200b\\u00ad]/g, "").replace(/\\s\\s*/g, " ")).join("\\xA0").trim();\n}\nfunction queryInAriaOwned(element, selector) {\n const result = [...element.querySelectorAll(selector)];\n for (const owned of getIdRefs(element, element.getAttribute("aria-owns"))) {\n if (owned.matches(selector))\n result.push(owned);\n result.push(...owned.querySelectorAll(selector));\n }\n return result;\n}\nfunction getCSSContent(element, pseudo) {\n const cache = pseudo === "::before" ? cachePseudoContentBefore : pseudo === "::after" ? cachePseudoContentAfter : cachePseudoContent;\n if (cache == null ? void 0 : cache.has(element))\n return cache == null ? void 0 : cache.get(element);\n const style = getElementComputedStyle(element, pseudo);\n let content;\n if (style) {\n const contentValue = style.content;\n if (contentValue && contentValue !== "none" && contentValue !== "normal") {\n if (style.display !== "none" && style.visibility !== "hidden") {\n content = parseCSSContentPropertyAsString(element, contentValue, !!pseudo);\n }\n }\n }\n if (pseudo && content !== void 0) {\n const display = (style == null ? void 0 : style.display) || "inline";\n if (display !== "inline")\n content = " " + content + " ";\n }\n if (cache)\n cache.set(element, content);\n return content;\n}\nfunction parseCSSContentPropertyAsString(element, content, isPseudo) {\n if (!content || content === "none" || content === "normal") {\n return;\n }\n try {\n let tokens = tokenize(content).filter((token) => !(token instanceof WhitespaceToken));\n const delimIndex = tokens.findIndex((token) => token instanceof DelimToken && token.value === "/");\n if (delimIndex !== -1) {\n tokens = tokens.slice(delimIndex + 1);\n } else if (!isPseudo) {\n return;\n }\n const accumulated = [];\n let index = 0;\n while (index < tokens.length) {\n if (tokens[index] instanceof StringToken) {\n accumulated.push(tokens[index].value);\n index++;\n } else if (index + 2 < tokens.length && tokens[index] instanceof FunctionToken && tokens[index].value === "attr" && tokens[index + 1] instanceof IdentToken && tokens[index + 2] instanceof CloseParenToken) {\n const attrName = tokens[index + 1].value;\n accumulated.push(element.getAttribute(attrName) || "");\n index += 3;\n } else {\n return;\n }\n }\n return accumulated.join("");\n } catch {\n }\n}\nfunction getAriaLabelledByElements(element) {\n const ref = element.getAttribute("aria-labelledby");\n if (ref === null)\n return null;\n const refs = getIdRefs(element, ref);\n return refs.length ? refs : null;\n}\nfunction allowsNameFromContent(role, targetDescendant) {\n const alwaysAllowsNameFromContent = ["button", "cell", "checkbox", "columnheader", "gridcell", "heading", "link", "menuitem", "menuitemcheckbox", "menuitemradio", "option", "radio", "row", "rowheader", "switch", "tab", "tooltip", "treeitem"].includes(role);\n const descendantAllowsNameFromContent = targetDescendant && ["", "caption", "code", "contentinfo", "definition", "deletion", "emphasis", "insertion", "list", "listitem", "mark", "none", "paragraph", "presentation", "region", "row", "rowgroup", "section", "strong", "subscript", "superscript", "table", "term", "time"].includes(role);\n return alwaysAllowsNameFromContent || descendantAllowsNameFromContent;\n}\nfunction getElementAccessibleName(element, includeHidden) {\n const cache = includeHidden ? cacheAccessibleNameHidden : cacheAccessibleName;\n let accessibleName = cache == null ? void 0 : cache.get(element);\n if (accessibleName === void 0) {\n accessibleName = "";\n const elementProhibitsNaming = ["caption", "code", "definition", "deletion", "emphasis", "generic", "insertion", "mark", "paragraph", "presentation", "strong", "subscript", "suggestion", "superscript", "term", "time"].includes(getAriaRole(element) || "");\n if (!elementProhibitsNaming) {\n accessibleName = asFlatString(getTextAlternativeInternal(element, {\n includeHidden,\n visitedElements: /* @__PURE__ */ new Set(),\n embeddedInTargetElement: "self"\n }));\n }\n cache == null ? void 0 : cache.set(element, accessibleName);\n }\n return accessibleName;\n}\nfunction getElementAccessibleDescription(element, includeHidden) {\n const cache = includeHidden ? cacheAccessibleDescriptionHidden : cacheAccessibleDescription;\n let accessibleDescription = cache == null ? void 0 : cache.get(element);\n if (accessibleDescription === void 0) {\n accessibleDescription = "";\n if (element.hasAttribute("aria-describedby")) {\n const describedBy = getIdRefs(element, element.getAttribute("aria-describedby"));\n accessibleDescription = asFlatString(describedBy.map((ref) => getTextAlternativeInternal(ref, {\n includeHidden,\n visitedElements: /* @__PURE__ */ new Set(),\n embeddedInDescribedBy: { element: ref, hidden: isElementHiddenForAria(ref) }\n })).join(" "));\n } else if (element.hasAttribute("aria-description")) {\n accessibleDescription = asFlatString(element.getAttribute("aria-description") || "");\n } else {\n accessibleDescription = asFlatString(element.getAttribute("title") || "");\n }\n cache == null ? void 0 : cache.set(element, accessibleDescription);\n }\n return accessibleDescription;\n}\nvar kAriaInvalidRoles = [\n "application",\n "checkbox",\n "columnheader",\n "combobox",\n "gridcell",\n "listbox",\n "radiogroup",\n "rowheader",\n "searchbox",\n "slider",\n "spinbutton",\n "switch",\n "textbox",\n "tree"\n];\nfunction getAriaInvalid(element) {\n const ariaInvalid = element.getAttribute("aria-invalid");\n if (!ariaInvalid || ariaInvalid.trim() === "" || ariaInvalid.toLocaleLowerCase() === "false")\n return "false";\n if (ariaInvalid === "true" || ariaInvalid === "grammar" || ariaInvalid === "spelling")\n return ariaInvalid;\n return "true";\n}\nfunction getValidityInvalid(element) {\n if ("validity" in element) {\n const validity = element.validity;\n return (validity == null ? void 0 : validity.valid) === false;\n }\n return false;\n}\nfunction getElementAccessibleErrorMessage(element) {\n const cache = cacheAccessibleErrorMessage;\n let accessibleErrorMessage = cacheAccessibleErrorMessage == null ? void 0 : cacheAccessibleErrorMessage.get(element);\n if (accessibleErrorMessage === void 0) {\n accessibleErrorMessage = "";\n const isAriaInvalid = getAriaInvalid(element) !== "false";\n const isValidityInvalid = getValidityInvalid(element);\n if (isAriaInvalid || isValidityInvalid) {\n const errorMessageId = element.getAttribute("aria-errormessage");\n const errorMessages = getIdRefs(element, errorMessageId);\n const parts = errorMessages.map((errorMessage) => asFlatString(\n getTextAlternativeInternal(errorMessage, {\n visitedElements: /* @__PURE__ */ new Set(),\n embeddedInDescribedBy: { element: errorMessage, hidden: isElementHiddenForAria(errorMessage) }\n })\n ));\n accessibleErrorMessage = parts.join(" ").trim();\n }\n cache == null ? void 0 : cache.set(element, accessibleErrorMessage);\n }\n return accessibleErrorMessage;\n}\nfunction getTextAlternativeInternal(element, options) {\n var _a, _b, _c, _d;\n if (options.visitedElements.has(element))\n return "";\n const childOptions = {\n ...options,\n embeddedInTargetElement: options.embeddedInTargetElement === "self" ? "descendant" : options.embeddedInTargetElement\n };\n if (!options.includeHidden) {\n const isEmbeddedInHiddenReferenceTraversal = !!((_a = options.embeddedInLabelledBy) == null ? void 0 : _a.hidden) || !!((_b = options.embeddedInDescribedBy) == null ? void 0 : _b.hidden) || !!((_c = options.embeddedInNativeTextAlternative) == null ? void 0 : _c.hidden) || !!((_d = options.embeddedInLabel) == null ? void 0 : _d.hidden);\n if (isElementIgnoredForAria(element) || !isEmbeddedInHiddenReferenceTraversal && isElementHiddenForAria(element)) {\n options.visitedElements.add(element);\n return "";\n }\n }\n const labelledBy = getAriaLabelledByElements(element);\n if (!options.embeddedInLabelledBy) {\n const accessibleName = (labelledBy || []).map((ref) => getTextAlternativeInternal(ref, {\n ...options,\n embeddedInLabelledBy: { element: ref, hidden: isElementHiddenForAria(ref) },\n embeddedInDescribedBy: void 0,\n embeddedInTargetElement: void 0,\n embeddedInLabel: void 0,\n embeddedInNativeTextAlternative: void 0\n })).join(" ");\n if (accessibleName)\n return accessibleName;\n }\n const role = getAriaRole(element) || "";\n const tagName = elementSafeTagName(element);\n if (!!options.embeddedInLabel || !!options.embeddedInLabelledBy || options.embeddedInTargetElement === "descendant") {\n const isOwnLabel = [...element.labels || []].includes(element);\n const isOwnLabelledBy = (labelledBy || []).includes(element);\n if (!isOwnLabel && !isOwnLabelledBy) {\n if (role === "textbox") {\n options.visitedElements.add(element);\n if (tagName === "INPUT" || tagName === "TEXTAREA")\n return element.value;\n return element.textContent || "";\n }\n if (["combobox", "listbox"].includes(role)) {\n options.visitedElements.add(element);\n let selectedOptions;\n if (tagName === "SELECT") {\n selectedOptions = [...element.selectedOptions];\n if (!selectedOptions.length && element.options.length)\n selectedOptions.push(element.options[0]);\n } else {\n const listbox = role === "combobox" ? queryInAriaOwned(element, "*").find((e) => getAriaRole(e) === "listbox") : element;\n selectedOptions = listbox ? queryInAriaOwned(listbox, \'[aria-selected="true"]\').filter((e) => getAriaRole(e) === "option") : [];\n }\n if (!selectedOptions.length && tagName === "INPUT") {\n return element.value;\n }\n return selectedOptions.map((option) => getTextAlternativeInternal(option, childOptions)).join(" ");\n }\n if (["progressbar", "scrollbar", "slider", "spinbutton", "meter"].includes(role)) {\n options.visitedElements.add(element);\n if (element.hasAttribute("aria-valuetext"))\n return element.getAttribute("aria-valuetext") || "";\n if (element.hasAttribute("aria-valuenow"))\n return element.getAttribute("aria-valuenow") || "";\n return element.getAttribute("value") || "";\n }\n if (["menu"].includes(role)) {\n options.visitedElements.add(element);\n return "";\n }\n }\n }\n const ariaLabel = element.getAttribute("aria-label") || "";\n if (trimFlatString(ariaLabel)) {\n options.visitedElements.add(element);\n return ariaLabel;\n }\n if (!["presentation", "none"].includes(role)) {\n if (tagName === "INPUT" && ["button", "submit", "reset"].includes(element.type)) {\n options.visitedElements.add(element);\n const value = element.value || "";\n if (trimFlatString(value))\n return value;\n if (element.type === "submit")\n return "Submit";\n if (element.type === "reset")\n return "Reset";\n const title = element.getAttribute("title") || "";\n return title;\n }\n if (tagName === "INPUT" && element.type === "file") {\n options.visitedElements.add(element);\n const labels = element.labels || [];\n if (labels.length && !options.embeddedInLabelledBy)\n return getAccessibleNameFromAssociatedLabels(labels, options);\n return "Choose File";\n }\n if (tagName === "INPUT" && element.type === "image") {\n options.visitedElements.add(element);\n const labels = element.labels || [];\n if (labels.length && !options.embeddedInLabelledBy)\n return getAccessibleNameFromAssociatedLabels(labels, options);\n const alt = element.getAttribute("alt") || "";\n if (trimFlatString(alt))\n return alt;\n const title = element.getAttribute("title") || "";\n if (trimFlatString(title))\n return title;\n return "Submit";\n }\n if (!labelledBy && tagName === "BUTTON") {\n options.visitedElements.add(element);\n const labels = element.labels || [];\n if (labels.length)\n return getAccessibleNameFromAssociatedLabels(labels, options);\n }\n if (!labelledBy && tagName === "OUTPUT") {\n options.visitedElements.add(element);\n const labels = element.labels || [];\n if (labels.length)\n return getAccessibleNameFromAssociatedLabels(labels, options);\n return element.getAttribute("title") || "";\n }\n if (!labelledBy && (tagName === "TEXTAREA" || tagName === "SELECT" || tagName === "INPUT")) {\n options.visitedElements.add(element);\n const labels = element.labels || [];\n if (labels.length)\n return getAccessibleNameFromAssociatedLabels(labels, options);\n const usePlaceholder = tagName === "INPUT" && ["text", "password", "search", "tel", "email", "url"].includes(element.type) || tagName === "TEXTAREA";\n const placeholder = element.getAttribute("placeholder") || "";\n const title = element.getAttribute("title") || "";\n if (!usePlaceholder || title)\n return title;\n return placeholder;\n }\n if (!labelledBy && tagName === "FIELDSET") {\n options.visitedElements.add(element);\n for (let child = element.firstElementChild; child; child = child.nextElementSibling) {\n if (elementSafeTagName(child) === "LEGEND") {\n return getTextAlternativeInternal(child, {\n ...childOptions,\n embeddedInNativeTextAlternative: { element: child, hidden: isElementHiddenForAria(child) }\n });\n }\n }\n const title = element.getAttribute("title") || "";\n return title;\n }\n if (!labelledBy && tagName === "FIGURE") {\n options.visitedElements.add(element);\n for (let child = element.firstElementChild; child; child = child.nextElementSibling) {\n if (elementSafeTagName(child) === "FIGCAPTION") {\n return getTextAlternativeInternal(child, {\n ...childOptions,\n embeddedInNativeTextAlternative: { element: child, hidden: isElementHiddenForAria(child) }\n });\n }\n }\n const title = element.getAttribute("title") || "";\n return title;\n }\n if (tagName === "IMG") {\n options.visitedElements.add(element);\n const alt = element.getAttribute("alt") || "";\n if (trimFlatString(alt))\n return alt;\n const title = element.getAttribute("title") || "";\n return title;\n }\n if (tagName === "TABLE") {\n options.visitedElements.add(element);\n for (let child = element.firstElementChild; child; child = child.nextElementSibling) {\n if (elementSafeTagName(child) === "CAPTION") {\n return getTextAlternativeInternal(child, {\n ...childOptions,\n embeddedInNativeTextAlternative: { element: child, hidden: isElementHiddenForAria(child) }\n });\n }\n }\n const summary = element.getAttribute("summary") || "";\n if (summary)\n return summary;\n }\n if (tagName === "AREA") {\n options.visitedElements.add(element);\n const alt = element.getAttribute("alt") || "";\n if (trimFlatString(alt))\n return alt;\n const title = element.getAttribute("title") || "";\n return title;\n }\n if (tagName === "SVG" || element.ownerSVGElement) {\n options.visitedElements.add(element);\n for (let child = element.firstElementChild; child; child = child.nextElementSibling) {\n if (elementSafeTagName(child) === "TITLE" && child.ownerSVGElement) {\n return getTextAlternativeInternal(child, {\n ...childOptions,\n embeddedInLabelledBy: { element: child, hidden: isElementHiddenForAria(child) }\n });\n }\n }\n }\n if (element.ownerSVGElement && tagName === "A") {\n const title = element.getAttribute("xlink:title") || "";\n if (trimFlatString(title)) {\n options.visitedElements.add(element);\n return title;\n }\n }\n }\n const shouldNameFromContentForSummary = tagName === "SUMMARY" && !["presentation", "none"].includes(role);\n if (allowsNameFromContent(role, options.embeddedInTargetElement === "descendant") || shouldNameFromContentForSummary || !!options.embeddedInLabelledBy || !!options.embeddedInDescribedBy || !!options.embeddedInLabel || !!options.embeddedInNativeTextAlternative) {\n options.visitedElements.add(element);\n const accessibleName = innerAccumulatedElementText(element, childOptions);\n const maybeTrimmedAccessibleName = options.embeddedInTargetElement === "self" ? trimFlatString(accessibleName) : accessibleName;\n if (maybeTrimmedAccessibleName)\n return accessibleName;\n }\n if (!["presentation", "none"].includes(role) || tagName === "IFRAME") {\n options.visitedElements.add(element);\n const title = element.getAttribute("title") || "";\n if (trimFlatString(title))\n return title;\n }\n options.visitedElements.add(element);\n return "";\n}\nfunction innerAccumulatedElementText(element, options) {\n const tokens = [];\n const visit = (node, skipSlotted) => {\n var _a;\n if (skipSlotted && node.assignedSlot)\n return;\n if (node.nodeType === 1) {\n const display = ((_a = getElementComputedStyle(node)) == null ? void 0 : _a.display) || "inline";\n let token = getTextAlternativeInternal(node, options);\n if (display !== "inline" || node.nodeName === "BR")\n token = " " + token + " ";\n tokens.push(token);\n } else if (node.nodeType === 3) {\n tokens.push(node.textContent || "");\n }\n };\n tokens.push(getCSSContent(element, "::before") || "");\n const content = getCSSContent(element);\n if (content !== void 0) {\n tokens.push(content);\n } else {\n const assignedNodes = element.nodeName === "SLOT" ? element.assignedNodes() : [];\n if (assignedNodes.length) {\n for (const child of assignedNodes)\n visit(child, false);\n } else {\n for (let child = element.firstChild; child; child = child.nextSibling)\n visit(child, true);\n if (element.shadowRoot) {\n for (let child = element.shadowRoot.firstChild; child; child = child.nextSibling)\n visit(child, true);\n }\n for (const owned of getIdRefs(element, element.getAttribute("aria-owns")))\n visit(owned, true);\n }\n }\n tokens.push(getCSSContent(element, "::after") || "");\n return tokens.join("");\n}\nvar kAriaSelectedRoles = ["gridcell", "option", "row", "tab", "rowheader", "columnheader", "treeitem"];\nfunction getAriaSelected(element) {\n if (elementSafeTagName(element) === "OPTION")\n return element.selected;\n if (kAriaSelectedRoles.includes(getAriaRole(element) || ""))\n return getAriaBoolean(element.getAttribute("aria-selected")) === true;\n return false;\n}\nvar kAriaCheckedRoles = ["checkbox", "menuitemcheckbox", "option", "radio", "switch", "menuitemradio", "treeitem"];\nfunction getAriaChecked(element) {\n const result = getChecked(element, true);\n return result === "error" ? false : result;\n}\nfunction getCheckedAllowMixed(element) {\n return getChecked(element, true);\n}\nfunction getCheckedWithoutMixed(element) {\n const result = getChecked(element, false);\n return result;\n}\nfunction getChecked(element, allowMixed) {\n const tagName = elementSafeTagName(element);\n if (allowMixed && tagName === "INPUT" && element.indeterminate)\n return "mixed";\n if (tagName === "INPUT" && ["checkbox", "radio"].includes(element.type))\n return element.checked;\n if (kAriaCheckedRoles.includes(getAriaRole(element) || "")) {\n const checked = element.getAttribute("aria-checked");\n if (checked === "true")\n return true;\n if (allowMixed && checked === "mixed")\n return "mixed";\n return false;\n }\n return "error";\n}\nvar kAriaReadonlyRoles = ["checkbox", "combobox", "grid", "gridcell", "listbox", "radiogroup", "slider", "spinbutton", "textbox", "columnheader", "rowheader", "searchbox", "switch", "treegrid"];\nfunction getReadonly(element) {\n const tagName = elementSafeTagName(element);\n if (["INPUT", "TEXTAREA", "SELECT"].includes(tagName))\n return element.hasAttribute("readonly");\n if (kAriaReadonlyRoles.includes(getAriaRole(element) || ""))\n return element.getAttribute("aria-readonly") === "true";\n if (element.isContentEditable)\n return false;\n return "error";\n}\nvar kAriaPressedRoles = ["button"];\nfunction getAriaPressed(element) {\n if (kAriaPressedRoles.includes(getAriaRole(element) || "")) {\n const pressed = element.getAttribute("aria-pressed");\n if (pressed === "true")\n return true;\n if (pressed === "mixed")\n return "mixed";\n }\n return false;\n}\nvar kAriaExpandedRoles = ["application", "button", "checkbox", "combobox", "gridcell", "link", "listbox", "menuitem", "row", "rowheader", "tab", "treeitem", "columnheader", "menuitemcheckbox", "menuitemradio", "rowheader", "switch"];\nfunction getAriaExpanded(element) {\n if (elementSafeTagName(element) === "DETAILS")\n return element.open;\n if (kAriaExpandedRoles.includes(getAriaRole(element) || "")) {\n const expanded = element.getAttribute("aria-expanded");\n if (expanded === null)\n return void 0;\n if (expanded === "true")\n return true;\n return false;\n }\n return void 0;\n}\nvar kAriaLevelRoles = ["heading", "listitem", "row", "treeitem"];\nfunction getAriaLevel(element) {\n const native = { "H1": 1, "H2": 2, "H3": 3, "H4": 4, "H5": 5, "H6": 6 }[elementSafeTagName(element)];\n if (native)\n return native;\n if (kAriaLevelRoles.includes(getAriaRole(element) || "")) {\n const attr = element.getAttribute("aria-level");\n const value = attr === null ? Number.NaN : Number(attr);\n if (Number.isInteger(value) && value >= 1)\n return value;\n }\n return 0;\n}\nvar kAriaDisabledRoles = ["application", "button", "composite", "gridcell", "group", "input", "link", "menuitem", "scrollbar", "separator", "tab", "checkbox", "columnheader", "combobox", "grid", "listbox", "menu", "menubar", "menuitemcheckbox", "menuitemradio", "option", "radio", "radiogroup", "row", "rowheader", "searchbox", "select", "slider", "spinbutton", "switch", "tablist", "textbox", "toolbar", "tree", "treegrid", "treeitem"];\nfunction getAriaDisabled(element) {\n return isNativelyDisabled(element) || hasExplicitAriaDisabled(element);\n}\nfunction isNativelyDisabled(element) {\n const isNativeFormControl = ["BUTTON", "INPUT", "SELECT", "TEXTAREA", "OPTION", "OPTGROUP"].includes(elementSafeTagName(element));\n return isNativeFormControl && (element.hasAttribute("disabled") || belongsToDisabledOptGroup(element) || belongsToDisabledFieldSet(element));\n}\nfunction belongsToDisabledOptGroup(element) {\n return elementSafeTagName(element) === "OPTION" && !!element.closest("OPTGROUP[DISABLED]");\n}\nfunction belongsToDisabledFieldSet(element) {\n const fieldSetElement = element == null ? void 0 : element.closest("FIELDSET[DISABLED]");\n if (!fieldSetElement)\n return false;\n const legendElement = fieldSetElement.querySelector(":scope > LEGEND");\n return !legendElement || !legendElement.contains(element);\n}\nfunction hasExplicitAriaDisabled(element, isAncestor = false) {\n if (!element)\n return false;\n if (isAncestor || kAriaDisabledRoles.includes(getAriaRole(element) || "")) {\n const attribute = (element.getAttribute("aria-disabled") || "").toLowerCase();\n if (attribute === "true")\n return true;\n if (attribute === "false")\n return false;\n return hasExplicitAriaDisabled(parentElementOrShadowHost(element), true);\n }\n return false;\n}\nfunction getAccessibleNameFromAssociatedLabels(labels, options) {\n return [...labels].map((label) => getTextAlternativeInternal(label, {\n ...options,\n embeddedInLabel: { element: label, hidden: isElementHiddenForAria(label) },\n embeddedInNativeTextAlternative: void 0,\n embeddedInLabelledBy: void 0,\n embeddedInDescribedBy: void 0,\n embeddedInTargetElement: void 0\n })).filter((accessibleName) => !!accessibleName).join(" ");\n}\nfunction receivesPointerEvents(element) {\n const cache = cachePointerEvents;\n let e = element;\n let result;\n const parents = [];\n for (; e; e = parentElementOrShadowHost(e)) {\n const cached = cache.get(e);\n if (cached !== void 0) {\n result = cached;\n break;\n }\n parents.push(e);\n const style = getElementComputedStyle(e);\n if (!style) {\n result = true;\n break;\n }\n const value = style.pointerEvents;\n if (value) {\n result = value !== "none";\n break;\n }\n }\n if (result === void 0)\n result = true;\n for (const parent of parents)\n cache.set(parent, result);\n return result;\n}\nvar cacheAccessibleName;\nvar cacheAccessibleNameHidden;\nvar cacheAccessibleDescription;\nvar cacheAccessibleDescriptionHidden;\nvar cacheAccessibleErrorMessage;\nvar cacheIsHidden;\nvar cachePseudoContent;\nvar cachePseudoContentBefore;\nvar cachePseudoContentAfter;\nvar cachePointerEvents;\nvar cachesCounter2 = 0;\nfunction beginAriaCaches() {\n beginDOMCaches();\n ++cachesCounter2;\n cacheAccessibleName != null ? cacheAccessibleName : cacheAccessibleName = /* @__PURE__ */ new Map();\n cacheAccessibleNameHidden != null ? cacheAccessibleNameHidden : cacheAccessibleNameHidden = /* @__PURE__ */ new Map();\n cacheAccessibleDescription != null ? cacheAccessibleDescription : cacheAccessibleDescription = /* @__PURE__ */ new Map();\n cacheAccessibleDescriptionHidden != null ? cacheAccessibleDescriptionHidden : cacheAccessibleDescriptionHidden = /* @__PURE__ */ new Map();\n cacheAccessibleErrorMessage != null ? cacheAccessibleErrorMessage : cacheAccessibleErrorMessage = /* @__PURE__ */ new Map();\n cacheIsHidden != null ? cacheIsHidden : cacheIsHidden = /* @__PURE__ */ new Map();\n cachePseudoContent != null ? cachePseudoContent : cachePseudoContent = /* @__PURE__ */ new Map();\n cachePseudoContentBefore != null ? cachePseudoContentBefore : cachePseudoContentBefore = /* @__PURE__ */ new Map();\n cachePseudoContentAfter != null ? cachePseudoContentAfter : cachePseudoContentAfter = /* @__PURE__ */ new Map();\n cachePointerEvents != null ? cachePointerEvents : cachePointerEvents = /* @__PURE__ */ new Map();\n}\nfunction endAriaCaches() {\n if (!--cachesCounter2) {\n cacheAccessibleName = void 0;\n cacheAccessibleNameHidden = void 0;\n cacheAccessibleDescription = void 0;\n cacheAccessibleDescriptionHidden = void 0;\n cacheAccessibleErrorMessage = void 0;\n cacheIsHidden = void 0;\n cachePseudoContent = void 0;\n cachePseudoContentBefore = void 0;\n cachePseudoContentAfter = void 0;\n cachePointerEvents = void 0;\n }\n endDOMCaches();\n}\nvar inputTypeToRole = {\n "button": "button",\n "checkbox": "checkbox",\n "image": "button",\n "number": "spinbutton",\n "radio": "radio",\n "range": "slider",\n "reset": "button",\n "submit": "button"\n};\n\n// packages/injected/src/ariaSnapshot.ts\nvar lastRef = 0;\nfunction toInternalOptions(options) {\n const renderBoxes = options.boxes;\n if (options.mode === "ai") {\n return {\n visibility: "ariaOrVisible",\n refs: "interactable",\n refPrefix: options.refPrefix,\n includeGenericRole: true,\n renderActive: !options.doNotRenderActive,\n renderCursorPointer: true,\n renderBoxes\n };\n }\n if (options.mode === "autoexpect") {\n return { visibility: "ariaAndVisible", refs: "none", renderBoxes };\n }\n if (options.mode === "codegen") {\n return { visibility: "aria", refs: "none", renderStringsAsRegex: true, renderBoxes };\n }\n return { visibility: "aria", refs: "none", renderBoxes };\n}\nfunction generateAriaTree(rootElement, publicOptions) {\n const options = toInternalOptions(publicOptions);\n const visited = /* @__PURE__ */ new Set();\n const snapshot = {\n root: { role: "fragment", name: "", children: [], props: {}, box: computeBox(rootElement), receivesPointerEvents: true },\n elements: /* @__PURE__ */ new Map(),\n refs: /* @__PURE__ */ new Map(),\n iframeRefs: []\n };\n setAriaNodeElement(snapshot.root, rootElement);\n const visit = (ariaNode, node, parentElementVisible) => {\n if (visited.has(node))\n return;\n visited.add(node);\n if (node.nodeType === Node.TEXT_NODE && node.nodeValue) {\n if (!parentElementVisible)\n return;\n const text = node.nodeValue;\n if (ariaNode.role !== "textbox" && text)\n ariaNode.children.push(node.nodeValue || "");\n return;\n }\n if (node.nodeType !== Node.ELEMENT_NODE)\n return;\n const element = node;\n const isElementVisibleForAria = !isElementHiddenForAria(element);\n let visible = isElementVisibleForAria;\n if (options.visibility === "ariaOrVisible")\n visible = isElementVisibleForAria || isElementVisible(element);\n if (options.visibility === "ariaAndVisible")\n visible = isElementVisibleForAria && isElementVisible(element);\n if (options.visibility === "aria" && !visible)\n return;\n const ariaChildren = [];\n if (element.hasAttribute("aria-owns")) {\n const ids = element.getAttribute("aria-owns").split(/\\s+/);\n for (const id of ids) {\n const ownedElement = rootElement.ownerDocument.getElementById(id);\n if (ownedElement)\n ariaChildren.push(ownedElement);\n }\n }\n const childAriaNode = visible ? toAriaNode(element, options) : null;\n if (childAriaNode) {\n if (childAriaNode.ref) {\n snapshot.elements.set(childAriaNode.ref, element);\n snapshot.refs.set(element, childAriaNode.ref);\n if (childAriaNode.role === "iframe")\n snapshot.iframeRefs.push(childAriaNode.ref);\n }\n ariaNode.children.push(childAriaNode);\n }\n processElement(childAriaNode || ariaNode, element, ariaChildren, visible);\n };\n function processElement(ariaNode, element, ariaChildren, parentElementVisible) {\n var _a;\n const display = ((_a = getElementComputedStyle(element)) == null ? void 0 : _a.display) || "inline";\n const treatAsBlock = display !== "inline" || element.nodeName === "BR" ? " " : "";\n if (treatAsBlock)\n ariaNode.children.push(treatAsBlock);\n ariaNode.children.push(getCSSContent(element, "::before") || "");\n const assignedNodes = element.nodeName === "SLOT" ? element.assignedNodes() : [];\n if (assignedNodes.length) {\n for (const child of assignedNodes)\n visit(ariaNode, child, parentElementVisible);\n } else {\n for (let child = element.firstChild; child; child = child.nextSibling) {\n if (!child.assignedSlot)\n visit(ariaNode, child, parentElementVisible);\n }\n if (element.shadowRoot) {\n for (let child = element.shadowRoot.firstChild; child; child = child.nextSibling)\n visit(ariaNode, child, parentElementVisible);\n }\n }\n for (const child of ariaChildren)\n visit(ariaNode, child, parentElementVisible);\n ariaNode.children.push(getCSSContent(element, "::after") || "");\n if (treatAsBlock)\n ariaNode.children.push(treatAsBlock);\n if (ariaNode.children.length === 1 && ariaNode.name === ariaNode.children[0])\n ariaNode.children = [];\n if (ariaNode.role === "link" && element.hasAttribute("href")) {\n const href = element.getAttribute("href");\n ariaNode.props["url"] = href;\n }\n if (ariaNode.role === "textbox" && element.hasAttribute("placeholder") && element.getAttribute("placeholder") !== ariaNode.name) {\n const placeholder = element.getAttribute("placeholder");\n ariaNode.props["placeholder"] = placeholder;\n }\n }\n beginAriaCaches();\n try {\n visit(snapshot.root, rootElement, true);\n } finally {\n endAriaCaches();\n }\n normalizeStringChildren(snapshot.root);\n normalizeGenericRoles(snapshot.root);\n return snapshot;\n}\nfunction computeAriaRef(ariaNode, options) {\n var _a;\n if (options.refs === "none")\n return;\n if (options.refs === "interactable" && (!ariaNode.box.visible || !ariaNode.receivesPointerEvents))\n return;\n const element = ariaNodeElement(ariaNode);\n let ariaRef = element._ariaRef;\n if (!ariaRef || ariaRef.role !== ariaNode.role || ariaRef.name !== ariaNode.name) {\n ariaRef = { role: ariaNode.role, name: ariaNode.name, ref: ((_a = options.refPrefix) != null ? _a : "") + "e" + ++lastRef };\n element._ariaRef = ariaRef;\n }\n ariaNode.ref = ariaRef.ref;\n}\nfunction toAriaNode(element, options) {\n var _a;\n const active = element.ownerDocument.activeElement === element;\n if (element.nodeName === "IFRAME") {\n const ariaNode = {\n role: "iframe",\n name: "",\n children: [],\n props: {},\n box: computeBox(element),\n receivesPointerEvents: true,\n active\n };\n setAriaNodeElement(ariaNode, element);\n computeAriaRef(ariaNode, options);\n return ariaNode;\n }\n const defaultRole = options.includeGenericRole ? "generic" : null;\n const role = (_a = getAriaRole(element)) != null ? _a : defaultRole;\n if (!role || role === "presentation" || role === "none")\n return null;\n const name = normalizeWhiteSpace(getElementAccessibleName(element, false) || "");\n const receivesPointerEvents2 = receivesPointerEvents(element);\n const box = computeBox(element);\n if (role === "generic" && box.inline && element.childNodes.length === 1 && element.childNodes[0].nodeType === Node.TEXT_NODE)\n return null;\n const result = {\n role,\n name,\n children: [],\n props: {},\n box,\n receivesPointerEvents: receivesPointerEvents2,\n active\n };\n setAriaNodeElement(result, element);\n computeAriaRef(result, options);\n if (kAriaCheckedRoles.includes(role))\n result.checked = getAriaChecked(element);\n if (kAriaDisabledRoles.includes(role))\n result.disabled = getAriaDisabled(element);\n if (kAriaExpandedRoles.includes(role))\n result.expanded = getAriaExpanded(element);\n if (kAriaInvalidRoles.includes(role)) {\n const invalid = getAriaInvalid(element);\n result.invalid = invalid === "false" ? false : invalid === "true" ? true : invalid;\n }\n if (kAriaLevelRoles.includes(role))\n result.level = getAriaLevel(element);\n if (kAriaPressedRoles.includes(role))\n result.pressed = getAriaPressed(element);\n if (kAriaSelectedRoles.includes(role))\n result.selected = getAriaSelected(element);\n if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) {\n if (element.type !== "checkbox" && element.type !== "radio" && element.type !== "file")\n result.children = [element.value];\n }\n return result;\n}\nfunction normalizeGenericRoles(node) {\n const normalizeChildren = (node2) => {\n const result = [];\n for (const child of node2.children || []) {\n if (typeof child === "string") {\n result.push(child);\n continue;\n }\n const normalized = normalizeChildren(child);\n result.push(...normalized);\n }\n const removeSelf = node2.role === "generic" && !node2.name && result.length <= 1 && result.every((c) => typeof c !== "string" && !!c.ref);\n if (removeSelf)\n return result;\n node2.children = result;\n return [node2];\n };\n normalizeChildren(node);\n}\nfunction normalizeStringChildren(rootA11yNode) {\n const flushChildren = (buffer, normalizedChildren) => {\n if (!buffer.length)\n return;\n const text = normalizeWhiteSpace(buffer.join(""));\n if (text)\n normalizedChildren.push(text);\n buffer.length = 0;\n };\n const visit = (ariaNode) => {\n const normalizedChildren = [];\n const buffer = [];\n for (const child of ariaNode.children || []) {\n if (typeof child === "string") {\n buffer.push(child);\n } else {\n flushChildren(buffer, normalizedChildren);\n visit(child);\n normalizedChildren.push(child);\n }\n }\n flushChildren(buffer, normalizedChildren);\n ariaNode.children = normalizedChildren.length ? normalizedChildren : [];\n if (ariaNode.children.length === 1 && ariaNode.children[0] === ariaNode.name)\n ariaNode.children = [];\n };\n visit(rootA11yNode);\n}\nfunction matchesStringOrRegex(text, template) {\n if (!template)\n return true;\n if (!text)\n return false;\n if (typeof template === "string")\n return text === template;\n return !!text.match(new RegExp(template.pattern));\n}\nfunction matchesTextValue(text, template) {\n if (!(template == null ? void 0 : template.normalized))\n return true;\n if (!text)\n return false;\n if (text === template.normalized)\n return true;\n if (text === template.raw)\n return true;\n const regex = cachedRegex(template);\n if (regex)\n return !!text.match(regex);\n return false;\n}\nvar cachedRegexSymbol = Symbol("cachedRegex");\nfunction cachedRegex(template) {\n if (template[cachedRegexSymbol] !== void 0)\n return template[cachedRegexSymbol];\n const { raw } = template;\n const canBeRegex = raw.startsWith("/") && raw.endsWith("/") && raw.length > 1;\n let regex;\n try {\n regex = canBeRegex ? new RegExp(raw.slice(1, -1)) : null;\n } catch (e) {\n regex = null;\n }\n template[cachedRegexSymbol] = regex;\n return regex;\n}\nfunction matchesExpectAriaTemplate(rootElement, template) {\n const snapshot = generateAriaTree(rootElement, { mode: "default" });\n const matches = matchesNodeDeep(snapshot.root, template, false, false);\n return {\n matches,\n received: {\n raw: renderAriaTree(snapshot, { mode: "default" }).text,\n regex: renderAriaTree(snapshot, { mode: "codegen" }).text\n }\n };\n}\nfunction getAllElementsMatchingExpectAriaTemplate(rootElement, template) {\n const root = generateAriaTree(rootElement, { mode: "default" }).root;\n const matches = matchesNodeDeep(root, template, true, false);\n return matches.map((n) => ariaNodeElement(n));\n}\nfunction matchesNode(node, template, isDeepEqual) {\n var _a;\n if (typeof node === "string" && template.kind === "text")\n return matchesTextValue(node, template.text);\n if (node === null || typeof node !== "object" || template.kind !== "role")\n return false;\n if (template.role !== "fragment" && template.role !== node.role)\n return false;\n if (template.checked !== void 0 && template.checked !== node.checked)\n return false;\n if (template.disabled !== void 0 && template.disabled !== node.disabled)\n return false;\n if (template.expanded !== void 0 && template.expanded !== node.expanded)\n return false;\n if (template.invalid !== void 0 && template.invalid !== node.invalid)\n return false;\n if (template.level !== void 0 && template.level !== node.level)\n return false;\n if (template.pressed !== void 0 && template.pressed !== node.pressed)\n return false;\n if (template.selected !== void 0 && template.selected !== node.selected)\n return false;\n if (!matchesStringOrRegex(node.name, template.name))\n return false;\n if (!matchesTextValue(node.props.url, (_a = template.props) == null ? void 0 : _a.url))\n return false;\n if (template.containerMode === "contain")\n return containsList(node.children || [], template.children || []);\n if (template.containerMode === "equal")\n return listEqual(node.children || [], template.children || [], false);\n if (template.containerMode === "deep-equal" || isDeepEqual)\n return listEqual(node.children || [], template.children || [], true);\n return containsList(node.children || [], template.children || []);\n}\nfunction listEqual(children, template, isDeepEqual) {\n if (template.length !== children.length)\n return false;\n for (let i = 0; i < template.length; ++i) {\n if (!matchesNode(children[i], template[i], isDeepEqual))\n return false;\n }\n return true;\n}\nfunction containsList(children, template) {\n if (template.length > children.length)\n return false;\n const cc = children.slice();\n const tt = template.slice();\n for (const t of tt) {\n let c = cc.shift();\n while (c) {\n if (matchesNode(c, t, false))\n break;\n c = cc.shift();\n }\n if (!c)\n return false;\n }\n return true;\n}\nfunction matchesNodeDeep(root, template, collectAll, isDeepEqual) {\n const results = [];\n const visit = (node, parent) => {\n if (matchesNode(node, template, isDeepEqual)) {\n const result = typeof node === "string" ? parent : node;\n if (result)\n results.push(result);\n return !collectAll;\n }\n if (typeof node === "string")\n return false;\n for (const child of node.children || []) {\n if (visit(child, node))\n return true;\n }\n return false;\n };\n visit(root, null);\n return results;\n}\nfunction buildByRefMap(root, map = /* @__PURE__ */ new Map()) {\n if (root == null ? void 0 : root.ref)\n map.set(root.ref, root);\n for (const child of (root == null ? void 0 : root.children) || []) {\n if (typeof child !== "string")\n buildByRefMap(child, map);\n }\n return map;\n}\nfunction compareSnapshots(ariaSnapshot, previousSnapshot) {\n var _a;\n const previousByRef = buildByRefMap(previousSnapshot == null ? void 0 : previousSnapshot.root);\n const result = /* @__PURE__ */ new Map();\n const visit = (ariaNode, previousNode) => {\n let same = ariaNode.children.length === (previousNode == null ? void 0 : previousNode.children.length) && ariaNodesEqual(ariaNode, previousNode);\n let canBeSkipped = same;\n for (let childIndex = 0; childIndex < ariaNode.children.length; childIndex++) {\n const child = ariaNode.children[childIndex];\n const previousChild = previousNode == null ? void 0 : previousNode.children[childIndex];\n if (typeof child === "string") {\n same && (same = child === previousChild);\n canBeSkipped && (canBeSkipped = child === previousChild);\n } else {\n let previous = typeof previousChild !== "string" ? previousChild : void 0;\n if (child.ref)\n previous = previousByRef.get(child.ref);\n const sameChild = visit(child, previous);\n if (!previous || !sameChild && !child.ref || previous !== previousChild)\n canBeSkipped = false;\n same && (same = sameChild && previous === previousChild);\n }\n }\n result.set(ariaNode, same ? "same" : canBeSkipped ? "skip" : "changed");\n return same;\n };\n visit(ariaSnapshot.root, previousByRef.get((_a = previousSnapshot == null ? void 0 : previousSnapshot.root) == null ? void 0 : _a.ref));\n return result;\n}\nfunction filterSnapshotDiff(nodes, statusMap) {\n const result = [];\n const visit = (ariaNode) => {\n const status = statusMap.get(ariaNode);\n if (status === "same") {\n } else if (status === "skip") {\n for (const child of ariaNode.children) {\n if (typeof child !== "string")\n visit(child);\n }\n } else {\n result.push(ariaNode);\n }\n };\n for (const node of nodes) {\n if (typeof node === "string")\n result.push(node);\n else\n visit(node);\n }\n return result;\n}\nfunction indent(depth) {\n return " ".repeat(depth);\n}\nfunction renderAriaTree(ariaSnapshot, publicOptions, previousSnapshot) {\n const options = toInternalOptions(publicOptions);\n const lines = [];\n const iframeDepths = {};\n const includeText = options.renderStringsAsRegex ? textContributesInfo : () => true;\n const renderString = options.renderStringsAsRegex ? convertToBestGuessRegex : (str) => str;\n let nodesToRender = ariaSnapshot.root.role === "fragment" ? ariaSnapshot.root.children : [ariaSnapshot.root];\n const statusMap = compareSnapshots(ariaSnapshot, previousSnapshot);\n if (previousSnapshot)\n nodesToRender = filterSnapshotDiff(nodesToRender, statusMap);\n const visitText = (text, depth) => {\n if (publicOptions.depth && depth > publicOptions.depth)\n return;\n const escaped = yamlEscapeValueIfNeeded(renderString(text));\n if (escaped)\n lines.push(indent(depth) + "- text: " + escaped);\n };\n const createKey = (ariaNode, renderCursorPointer) => {\n let key = ariaNode.role;\n if (ariaNode.name && ariaNode.name.length <= 900) {\n const name = renderString(ariaNode.name);\n if (name) {\n const stringifiedName = name.startsWith("/") && name.endsWith("/") ? name : JSON.stringify(name);\n key += " " + stringifiedName;\n }\n }\n if (ariaNode.checked === "mixed")\n key += ` [checked=mixed]`;\n if (ariaNode.checked === true)\n key += ` [checked]`;\n if (ariaNode.disabled)\n key += ` [disabled]`;\n if (ariaNode.expanded)\n key += ` [expanded]`;\n if (ariaNode.active && options.renderActive)\n key += ` [active]`;\n if (ariaNode.invalid === "grammar" || ariaNode.invalid === "spelling")\n key += ` [invalid=${ariaNode.invalid}]`;\n if (ariaNode.invalid === true)\n key += ` [invalid]`;\n if (ariaNode.level)\n key += ` [level=${ariaNode.level}]`;\n if (ariaNode.pressed === "mixed")\n key += ` [pressed=mixed]`;\n if (ariaNode.pressed === true)\n key += ` [pressed]`;\n if (ariaNode.selected === true)\n key += ` [selected]`;\n if (ariaNode.ref) {\n key += ` [ref=${ariaNode.ref}]`;\n if (renderCursorPointer && hasPointerCursor(ariaNode))\n key += " [cursor=pointer]";\n }\n if (options.renderBoxes) {\n const element = ariaNodeElement(ariaNode);\n if (element) {\n const r = element.getBoundingClientRect();\n key += ` [box=${Math.round(r.x)},${Math.round(r.y)},${Math.round(r.width)},${Math.round(r.height)}]`;\n }\n }\n return key;\n };\n const getSingleInlinedTextChild = (ariaNode) => {\n return (ariaNode == null ? void 0 : ariaNode.children.length) === 1 && typeof ariaNode.children[0] === "string" && !Object.keys(ariaNode.props).length ? ariaNode.children[0] : void 0;\n };\n const visit = (ariaNode, depth, renderCursorPointer) => {\n if (publicOptions.depth && depth > publicOptions.depth)\n return;\n if (ariaNode.role === "iframe" && ariaNode.ref)\n iframeDepths[ariaNode.ref] = depth;\n if (statusMap.get(ariaNode) === "same" && ariaNode.ref) {\n lines.push(indent(depth) + `- ref=${ariaNode.ref} [unchanged]`);\n return;\n }\n const isDiffRoot = !!previousSnapshot && !depth;\n const escapedKey = indent(depth) + "- " + (isDiffRoot ? " " : "") + yamlEscapeKeyIfNeeded(createKey(ariaNode, renderCursorPointer));\n const singleInlinedTextChild = getSingleInlinedTextChild(ariaNode);\n const isAtDepthLimit = !!publicOptions.depth && depth === publicOptions.depth;\n const hasNoChildren = !singleInlinedTextChild && (!ariaNode.children.length || isAtDepthLimit);\n if (hasNoChildren && !Object.keys(ariaNode.props).length) {\n lines.push(escapedKey);\n } else if (singleInlinedTextChild !== void 0) {\n const shouldInclude = includeText(ariaNode, singleInlinedTextChild);\n if (shouldInclude)\n lines.push(escapedKey + ": " + yamlEscapeValueIfNeeded(renderString(singleInlinedTextChild)));\n else\n lines.push(escapedKey);\n } else {\n lines.push(escapedKey + ":");\n for (const [name, value] of Object.entries(ariaNode.props))\n lines.push(indent(depth + 1) + "- /" + name + ": " + yamlEscapeValueIfNeeded(value));\n const inCursorPointer = !!ariaNode.ref && renderCursorPointer && hasPointerCursor(ariaNode);\n for (const child of ariaNode.children) {\n if (typeof child === "string")\n visitText(includeText(ariaNode, child) ? child : "", depth + 1);\n else\n visit(child, depth + 1, renderCursorPointer && !inCursorPointer);\n }\n }\n };\n for (const nodeToRender of nodesToRender) {\n if (typeof nodeToRender === "string")\n visitText(nodeToRender, 0);\n else\n visit(nodeToRender, 0, !!options.renderCursorPointer);\n }\n return { text: lines.join("\\n"), iframeDepths };\n}\nfunction convertToBestGuessRegex(text) {\n const dynamicContent = [\n // 550e8400-e29b-41d4-a716-446655440000\n { regex: /\\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\\b/, replacement: "[0-9a-fA-F-]+" },\n // 2mb\n { regex: /\\b[\\d,.]+[bkmBKM]+\\b/, replacement: "[\\\\d,.]+[bkmBKM]+" },\n // 2ms, 20s\n { regex: /\\b\\d+[hmsp]+\\b/, replacement: "\\\\d+[hmsp]+" },\n { regex: /\\b[\\d,.]+[hmsp]+\\b/, replacement: "[\\\\d,.]+[hmsp]+" },\n // Do not replace single digits with regex by default.\n // 2+ digits: [Issue 22, 22.3, 2.33, 2,333]\n { regex: /\\b\\d+,\\d+\\b/, replacement: "\\\\d+,\\\\d+" },\n { regex: /\\b\\d+\\.\\d{2,}\\b/, replacement: "\\\\d+\\\\.\\\\d+" },\n { regex: /\\b\\d{2,}\\.\\d+\\b/, replacement: "\\\\d+\\\\.\\\\d+" },\n { regex: /\\b\\d{2,}\\b/, replacement: "\\\\d+" }\n ];\n let pattern = "";\n let lastIndex = 0;\n const combinedRegex = new RegExp(dynamicContent.map((r) => "(" + r.regex.source + ")").join("|"), "g");\n text.replace(combinedRegex, (match, ...args) => {\n const offset = args[args.length - 2];\n const groups = args.slice(0, -2);\n pattern += escapeRegExp(text.slice(lastIndex, offset));\n for (let i = 0; i < groups.length; i++) {\n if (groups[i]) {\n const { replacement } = dynamicContent[i];\n pattern += replacement;\n break;\n }\n }\n lastIndex = offset + match.length;\n return match;\n });\n if (!pattern)\n return text;\n pattern += escapeRegExp(text.slice(lastIndex));\n return String(new RegExp(pattern));\n}\nfunction textContributesInfo(node, text) {\n if (!text.length)\n return false;\n if (!node.name)\n return true;\n const substr = text.length <= 200 && node.name.length <= 200 ? longestCommonSubstring(text, node.name) : "";\n let filtered = text;\n while (substr && filtered.includes(substr))\n filtered = filtered.replace(substr, "");\n return filtered.trim().length / text.length > 0.1;\n}\nvar elementSymbol = Symbol("element");\nfunction ariaNodeElement(ariaNode) {\n return ariaNode[elementSymbol];\n}\nfunction setAriaNodeElement(ariaNode, element) {\n ariaNode[elementSymbol] = element;\n}\nfunction findNewElement(from, to) {\n const node = findNewNode(from, to);\n return node ? ariaNodeElement(node) : void 0;\n}\n\n// packages/injected/src/highlight.css?inline\nvar highlight_default = ":host{font-size:13px;font-family:system-ui,Ubuntu,Droid Sans,sans-serif;color:#333;color-scheme:light}svg{position:absolute;height:0}x-pw-tooltip{backdrop-filter:blur(5px);background-color:#fff;border-radius:6px;box-shadow:0 .5rem 1.2rem #0000004d;display:none;font-size:12.8px;font-weight:400;left:0;line-height:1.5;max-width:600px;position:absolute;top:0;padding:0;flex-direction:column;overflow:hidden}x-pw-tooltip-line{display:flex;max-width:600px;padding:6px;user-select:none;cursor:pointer}x-pw-tooltip-footer{display:flex;max-width:600px;padding:6px;user-select:none;color:#777}x-pw-dialog{background-color:#fff;pointer-events:auto;border-radius:6px;box-shadow:0 .5rem 1.2rem #0000004d;display:flex;flex-direction:column;position:absolute;z-index:10;font-size:13px}x-pw-dialog:not(.autosize){width:400px;height:150px}x-pw-dialog-body{display:flex;flex-direction:column;flex:auto}x-pw-dialog-body label{margin:5px 8px;display:flex;flex-direction:row;align-items:center}x-pw-highlight{position:absolute;top:0;left:0;width:0;height:0}x-pw-action-point{position:absolute;width:20px;height:20px;background:red;border-radius:10px;margin:-10px 0 0 -10px;z-index:2}x-pw-action-cursor{position:absolute;width:18px;height:22px;pointer-events:none;z-index:4;filter:drop-shadow(0 1px 2px rgba(0,0,0,.4))}x-pw-action-cursor svg{width:100%;height:100%;position:static}x-pw-title{position:absolute;backdrop-filter:blur(5px);background-color:#00000080;color:#fff;border-radius:6px;padding:6px;font-size:24px;line-height:1.4;white-space:nowrap;user-select:none;z-index:3}x-pw-user-overlays,x-pw-user-overlay{position:absolute;inset:0}@keyframes pw-fade-out{0%{opacity:1}to{opacity:0}}x-pw-separator{height:1px;margin:6px 9px;background:#949494e5}x-pw-tool-gripper{height:28px;width:24px;margin:2px 0;cursor:grab}x-pw-tool-gripper:active{cursor:grabbing}x-pw-tool-gripper>x-div{width:16px;height:16px;margin:6px 4px;clip-path:url(#icon-gripper);background-color:#555}x-pw-tools-list>label{display:flex;align-items:center;margin:0 10px;user-select:none}x-pw-tools-list{display:flex;width:100%;border-bottom:1px solid #dddddd}x-pw-tool-item{pointer-events:auto;height:28px;width:28px;border-radius:3px}x-pw-tool-item:not(.disabled){cursor:pointer}x-pw-tool-item:not(.disabled):hover{background-color:#dbdbdb}x-pw-tool-item.toggled{background-color:#8acae480}x-pw-tool-item.toggled:not(.disabled):hover{background-color:#8acae4c4}x-pw-tool-item>x-div{width:16px;height:16px;margin:6px;background-color:#3a3a3a}x-pw-tool-item.disabled>x-div{background-color:#61616180;cursor:default}x-pw-tool-item.record.toggled{background-color:transparent}x-pw-tool-item.record.toggled:not(.disabled):hover{background-color:#dbdbdb}x-pw-tool-item.record.toggled>x-div{background-color:#a1260d}x-pw-tool-item.record.disabled.toggled>x-div{opacity:.8}x-pw-tool-item.accept>x-div{background-color:#388a34}x-pw-tool-item.record>x-div{clip-path:url(#icon-circle-large-filled)}x-pw-tool-item.record.toggled>x-div{clip-path:url(#icon-stop-circle)}x-pw-tool-item.pick-locator>x-div{clip-path:url(#icon-inspect)}x-pw-tool-item.text>x-div{clip-path:url(#icon-whole-word)}x-pw-tool-item.visibility>x-div{clip-path:url(#icon-eye)}x-pw-tool-item.value>x-div{clip-path:url(#icon-symbol-constant)}x-pw-tool-item.snapshot>x-div{clip-path:url(#icon-gist)}x-pw-tool-item.accept>x-div{clip-path:url(#icon-check)}x-pw-tool-item.cancel>x-div{clip-path:url(#icon-close)}x-pw-tool-item.succeeded>x-div{clip-path:url(#icon-pass);background-color:#388a34!important}x-pw-overlay{position:absolute;top:0;max-width:min-content;z-index:2147483647;background:transparent;pointer-events:auto}x-pw-overlay x-pw-tools-list{background-color:#fffd;box-shadow:#0000001a 0 5px 5px;border-radius:3px;border-bottom:none}x-pw-overlay x-pw-tool-item{margin:2px}textarea.text-editor{font-family:system-ui,Ubuntu,Droid Sans,sans-serif;flex:auto;border:none;margin:6px 10px;color:#333;outline:1px solid transparent!important;resize:none;padding:0;font-size:13px}textarea.text-editor.does-not-match{outline:1px solid red!important}x-div{display:block}x-spacer{flex:auto}*{box-sizing:border-box}*[hidden]{display:none!important}x-locator-editor{flex:none;width:100%;height:60px;padding:4px;border-bottom:1px solid #dddddd;outline:1px solid transparent}x-locator-editor.does-not-match{outline:1px solid red}.CodeMirror{width:100%!important;height:100%!important}x-pw-action-list{flex:auto;display:flex;flex-direction:column;user-select:none}x-pw-action-item{padding:6px 10px;cursor:pointer;overflow:hidden}x-pw-action-item:hover{background-color:#f2f2f2}x-pw-action-item:last-child{border-bottom-left-radius:6px;border-bottom-right-radius:6px}\\n";\n\n// packages/injected/src/highlight.ts\nvar Highlight = class {\n constructor(injectedScript) {\n this._renderedEntries = [];\n this._userOverlays = /* @__PURE__ */ new Map();\n this._userOverlayHidden = false;\n this._language = "javascript";\n this._elementHighlightSelectors = /* @__PURE__ */ new Map();\n this._injectedScript = injectedScript;\n const document = injectedScript.document;\n this._isUnderTest = injectedScript.isUnderTest;\n this._glassPaneElement = document.createElement("x-pw-glass");\n this._glassPaneElement.setAttribute("popover", "manual");\n this._glassPaneElement.style.inset = "0";\n this._glassPaneElement.style.width = "100%";\n this._glassPaneElement.style.height = "100%";\n this._glassPaneElement.style.maxWidth = "none";\n this._glassPaneElement.style.maxHeight = "none";\n this._glassPaneElement.style.padding = "0";\n this._glassPaneElement.style.margin = "0";\n this._glassPaneElement.style.border = "none";\n this._glassPaneElement.style.overflow = "visible";\n this._glassPaneElement.style.pointerEvents = "none";\n this._glassPaneElement.style.display = "flex";\n this._glassPaneElement.style.backgroundColor = "transparent";\n this._actionPointElement = document.createElement("x-pw-action-point");\n this._actionPointElement.setAttribute("hidden", "true");\n this._actionCursorElement = document.createElement("x-pw-action-cursor");\n this._actionCursorElement.style.visibility = "hidden";\n this._actionCursorElement.appendChild(this._createCursorSvg(document));\n this._titleElement = document.createElement("x-pw-title");\n this._titleElement.setAttribute("hidden", "true");\n this._userOverlayContainer = document.createElement("x-pw-user-overlays");\n this._userOverlayContainer.setAttribute("hidden", "true");\n this._glassPaneShadow = this._glassPaneElement.attachShadow({ mode: this._isUnderTest ? "open" : "closed" });\n if (typeof this._glassPaneShadow.adoptedStyleSheets.push === "function") {\n const sheet = new this._injectedScript.window.CSSStyleSheet();\n sheet.replaceSync(highlight_default);\n this._glassPaneShadow.adoptedStyleSheets.push(sheet);\n } else {\n const styleElement = this._injectedScript.document.createElement("style");\n styleElement.textContent = highlight_default;\n this._glassPaneShadow.appendChild(styleElement);\n }\n this._glassPaneShadow.appendChild(this._actionPointElement);\n this._glassPaneShadow.appendChild(this._actionCursorElement);\n this._glassPaneShadow.appendChild(this._titleElement);\n this._glassPaneShadow.appendChild(this._userOverlayContainer);\n }\n install() {\n if (!this._injectedScript.document.documentElement)\n return;\n if (!this._injectedScript.document.documentElement.contains(this._glassPaneElement) || this._glassPaneElement.nextElementSibling)\n this._injectedScript.document.documentElement.appendChild(this._glassPaneElement);\n this._bringToFront();\n }\n _bringToFront() {\n this._glassPaneElement.hidePopover();\n this._glassPaneElement.showPopover();\n }\n setLanguage(language) {\n this._language = language;\n }\n addElementHighlight(selector, cssStyle) {\n const key = stringifySelector(selector);\n this._elementHighlightSelectors.set(key, { selector, cssStyle });\n this._ensureElementHighlightRaf();\n }\n removeElementHighlight(selector) {\n const key = stringifySelector(selector);\n if (!this._elementHighlightSelectors.delete(key))\n return;\n if (this._elementHighlightSelectors.size === 0) {\n if (this._rafRequest) {\n this._injectedScript.utils.builtins.cancelAnimationFrame(this._rafRequest);\n this._rafRequest = void 0;\n }\n this.clearHighlight();\n }\n }\n _ensureElementHighlightRaf() {\n if (this._rafRequest)\n return;\n const tick = () => {\n const entries = [];\n for (const { selector, cssStyle } of this._elementHighlightSelectors.values()) {\n const elements = this._injectedScript.querySelectorAll(selector, this._injectedScript.document.documentElement);\n const locator = asLocator(this._language, stringifySelector(selector));\n const color = elements.length > 1 ? "#f6b26b7f" : "#6fa8dc7f";\n for (let i = 0; i < elements.length; ++i) {\n const suffix = elements.length > 1 ? ` [${i + 1} of ${elements.length}]` : "";\n entries.push({ element: elements[i], color, tooltipText: locator + suffix, cssStyle });\n }\n }\n this.updateHighlight(entries);\n this._rafRequest = this._injectedScript.utils.builtins.requestAnimationFrame(tick);\n };\n this._rafRequest = this._injectedScript.utils.builtins.requestAnimationFrame(tick);\n }\n uninstall() {\n if (this._rafRequest) {\n this._injectedScript.utils.builtins.cancelAnimationFrame(this._rafRequest);\n this._rafRequest = void 0;\n }\n this._elementHighlightSelectors.clear();\n this._glassPaneElement.remove();\n }\n showActionPoint(x, y, fadeDuration) {\n this._actionPointElement.style.top = y + "px";\n this._actionPointElement.style.left = x + "px";\n this._actionPointElement.hidden = false;\n if (fadeDuration)\n this._actionPointElement.style.animation = `pw-fade-out ${fadeDuration}ms ease-out forwards`;\n else\n this._actionPointElement.style.animation = "";\n }\n hideActionPoint() {\n this._actionPointElement.hidden = true;\n }\n moveActionCursor(x, y, fadeDuration) {\n const moveDuration = fadeDuration ? Math.max(80, Math.min(fadeDuration * 0.6, 400)) : 0;\n this._actionCursorElement.style.transition = `top ${moveDuration}ms ease, left ${moveDuration}ms ease`;\n this._actionCursorElement.style.left = x + "px";\n this._actionCursorElement.style.top = y + "px";\n this._actionCursorElement.style.visibility = "visible";\n }\n hideActionCursor() {\n this._actionCursorElement.style.visibility = "hidden";\n }\n _createCursorSvg(document) {\n const svgNs = "http://www.w3.org/2000/svg";\n const svg = document.createElementNS(svgNs, "svg");\n svg.setAttribute("viewBox", "0 0 18 22");\n const path = document.createElementNS(svgNs, "path");\n path.setAttribute("d", "M1 1 L1 17 L5.5 13 L8 20.5 L11 19.5 L8.5 12 L15 12 Z");\n path.setAttribute("fill", "white");\n path.setAttribute("stroke", "black");\n path.setAttribute("stroke-width", "1.5");\n path.setAttribute("stroke-linejoin", "round");\n svg.appendChild(path);\n return svg;\n }\n showActionTitle(text, fadeDuration, position, fontSize) {\n this._titleElement.textContent = text;\n this._titleElement.hidden = false;\n if (fadeDuration) {\n const fadeTime = fadeDuration / 4;\n this._titleElement.style.animation = `pw-fade-out ${fadeTime}ms ease-out ${fadeDuration - fadeTime}ms forwards`;\n } else {\n this._titleElement.style.animation = "";\n }\n this._titleElement.style.top = "";\n this._titleElement.style.bottom = "";\n this._titleElement.style.left = "";\n this._titleElement.style.right = "";\n this._titleElement.style.transform = "";\n switch (position) {\n case "top-left":\n this._titleElement.style.top = "6px";\n this._titleElement.style.left = "6px";\n break;\n case "top":\n this._titleElement.style.top = "6px";\n this._titleElement.style.left = "50%";\n this._titleElement.style.transform = "translateX(-50%)";\n break;\n case "bottom-left":\n this._titleElement.style.bottom = "6px";\n this._titleElement.style.left = "6px";\n break;\n case "bottom":\n this._titleElement.style.bottom = "6px";\n this._titleElement.style.left = "50%";\n this._titleElement.style.transform = "translateX(-50%)";\n break;\n case "bottom-right":\n this._titleElement.style.bottom = "6px";\n this._titleElement.style.right = "6px";\n break;\n case "top-right":\n default:\n this._titleElement.style.top = "6px";\n this._titleElement.style.right = "6px";\n break;\n }\n if (fontSize)\n this._titleElement.style.fontSize = fontSize + "px";\n }\n hideActionTitle() {\n this._titleElement.hidden = true;\n }\n addUserOverlay(id, html) {\n const element = this._injectedScript.document.createElement("div");\n element.className = "x-pw-user-overlay";\n element.innerHTML = html;\n for (const script of element.querySelectorAll("script"))\n script.remove();\n for (const el of element.querySelectorAll("*")) {\n for (const attr of [...el.attributes]) {\n if (attr.name.startsWith("on"))\n el.removeAttribute(attr.name);\n }\n }\n this._userOverlays.set(id, element);\n this._userOverlayContainer.appendChild(element);\n this._userOverlayContainer.hidden = this._userOverlayHidden;\n return id;\n }\n getUserOverlay(id) {\n return this._userOverlays.get(id);\n }\n removeUserOverlay(id) {\n const element = this._userOverlays.get(id);\n if (element) {\n element.remove();\n this._userOverlays.delete(id);\n }\n if (this._userOverlays.size === 0)\n this._userOverlayContainer.hidden = true;\n }\n setUserOverlaysVisible(visible) {\n this._userOverlayHidden = !visible;\n this._userOverlayContainer.hidden = !visible || this._userOverlays.size === 0;\n }\n clearHighlight() {\n var _a, _b;\n for (const entry of this._renderedEntries) {\n (_a = entry.highlightElement) == null ? void 0 : _a.remove();\n (_b = entry.tooltipElement) == null ? void 0 : _b.remove();\n }\n this._renderedEntries = [];\n }\n maskElements(elements, color) {\n this.updateHighlight(elements.map((element) => ({ element, color })));\n }\n updateHighlight(entries) {\n if (this._highlightIsUpToDate(entries))\n return;\n this.clearHighlight();\n for (const entry of entries) {\n const highlightElement = this._createHighlightElement();\n this._glassPaneShadow.appendChild(highlightElement);\n let tooltipElement;\n if (entry.tooltipText) {\n tooltipElement = this._injectedScript.document.createElement("x-pw-tooltip");\n this._glassPaneShadow.appendChild(tooltipElement);\n tooltipElement.style.top = "0";\n tooltipElement.style.left = "0";\n tooltipElement.style.display = "flex";\n const lineElement = this._injectedScript.document.createElement("x-pw-tooltip-line");\n lineElement.textContent = entry.tooltipText;\n tooltipElement.appendChild(lineElement);\n }\n this._renderedEntries.push({ targetElement: entry.element, box: toDOMRect(entry.box), color: entry.color, borderColor: entry.borderColor, fadeDuration: entry.fadeDuration, cssStyle: entry.cssStyle, tooltipElement, highlightElement });\n }\n for (const entry of this._renderedEntries) {\n if (!entry.box && !entry.targetElement)\n continue;\n entry.box = entry.box || entry.targetElement.getBoundingClientRect();\n if (!entry.tooltipElement)\n continue;\n const { anchorLeft, anchorTop } = this.tooltipPosition(entry.box, entry.tooltipElement);\n entry.tooltipTop = anchorTop;\n entry.tooltipLeft = anchorLeft;\n }\n for (const entry of this._renderedEntries) {\n if (entry.tooltipElement) {\n entry.tooltipElement.style.top = entry.tooltipTop + "px";\n entry.tooltipElement.style.left = entry.tooltipLeft + "px";\n }\n const box = entry.box;\n entry.highlightElement.style.backgroundColor = entry.color;\n entry.highlightElement.style.left = box.x + "px";\n entry.highlightElement.style.top = box.y + "px";\n entry.highlightElement.style.width = box.width + "px";\n entry.highlightElement.style.height = box.height + "px";\n entry.highlightElement.style.display = "block";\n if (entry.borderColor)\n entry.highlightElement.style.border = "2px solid " + entry.borderColor;\n if (entry.fadeDuration)\n entry.highlightElement.style.animation = `pw-fade-out ${entry.fadeDuration}ms ease-out forwards`;\n if (entry.cssStyle)\n entry.highlightElement.style.cssText += ";" + entry.cssStyle;\n if (this._isUnderTest)\n console.error("Highlight box for test: " + JSON.stringify({ x: box.x, y: box.y, width: box.width, height: box.height }));\n }\n }\n firstBox() {\n var _a;\n return (_a = this._renderedEntries[0]) == null ? void 0 : _a.box;\n }\n firstTooltipBox() {\n const entry = this._renderedEntries[0];\n if (!entry || !entry.tooltipElement || entry.tooltipLeft === void 0 || entry.tooltipTop === void 0)\n return;\n return {\n x: entry.tooltipLeft,\n y: entry.tooltipTop,\n left: entry.tooltipLeft,\n top: entry.tooltipTop,\n width: entry.tooltipElement.offsetWidth,\n height: entry.tooltipElement.offsetHeight,\n bottom: entry.tooltipTop + entry.tooltipElement.offsetHeight,\n right: entry.tooltipLeft + entry.tooltipElement.offsetWidth,\n toJSON: () => {\n }\n };\n }\n // Note: there is a copy of this method in dialog.tsx. Please fix bugs in both places.\n tooltipPosition(box, tooltipElement) {\n const tooltipWidth = tooltipElement.offsetWidth;\n const tooltipHeight = tooltipElement.offsetHeight;\n const totalWidth = this._glassPaneElement.offsetWidth;\n const totalHeight = this._glassPaneElement.offsetHeight;\n let anchorLeft = Math.max(5, box.left);\n if (anchorLeft + tooltipWidth > totalWidth - 5)\n anchorLeft = totalWidth - tooltipWidth - 5;\n let anchorTop = Math.max(0, box.bottom) + 5;\n if (anchorTop + tooltipHeight > totalHeight - 5) {\n if (Math.max(0, box.top) > tooltipHeight + 5) {\n anchorTop = Math.max(0, box.top) - tooltipHeight - 5;\n } else {\n anchorTop = totalHeight - 5 - tooltipHeight;\n }\n }\n return { anchorLeft, anchorTop };\n }\n _highlightIsUpToDate(entries) {\n if (entries.length !== this._renderedEntries.length)\n return false;\n for (let i = 0; i < this._renderedEntries.length; ++i) {\n if (entries[i].element !== this._renderedEntries[i].targetElement)\n return false;\n if (entries[i].color !== this._renderedEntries[i].color)\n return false;\n if (entries[i].cssStyle !== this._renderedEntries[i].cssStyle)\n return false;\n const oldBox = this._renderedEntries[i].box;\n if (!oldBox)\n return false;\n const box = entries[i].box ? toDOMRect(entries[i].box) : entries[i].element.getBoundingClientRect();\n if (box.top !== oldBox.top || box.right !== oldBox.right || box.bottom !== oldBox.bottom || box.left !== oldBox.left)\n return false;\n }\n return true;\n }\n _createHighlightElement() {\n return this._injectedScript.document.createElement("x-pw-highlight");\n }\n appendChild(element) {\n this._glassPaneShadow.appendChild(element);\n }\n onGlassPaneClick(handler) {\n this._glassPaneElement.style.pointerEvents = "auto";\n this._glassPaneElement.style.backgroundColor = "rgba(0, 0, 0, 0.3)";\n this._glassPaneElement.addEventListener("click", handler);\n }\n offGlassPaneClick(handler) {\n this._glassPaneElement.style.pointerEvents = "none";\n this._glassPaneElement.style.backgroundColor = "transparent";\n this._glassPaneElement.removeEventListener("click", handler);\n }\n};\nfunction toDOMRect(box) {\n if (!box)\n return void 0;\n return new DOMRect(box.x, box.y, box.width, box.height);\n}\n\n// packages/injected/src/layoutSelectorUtils.ts\nfunction boxRightOf(box1, box2, maxDistance) {\n const distance = box1.left - box2.right;\n if (distance < 0 || maxDistance !== void 0 && distance > maxDistance)\n return;\n return distance + Math.max(box2.bottom - box1.bottom, 0) + Math.max(box1.top - box2.top, 0);\n}\nfunction boxLeftOf(box1, box2, maxDistance) {\n const distance = box2.left - box1.right;\n if (distance < 0 || maxDistance !== void 0 && distance > maxDistance)\n return;\n return distance + Math.max(box2.bottom - box1.bottom, 0) + Math.max(box1.top - box2.top, 0);\n}\nfunction boxAbove(box1, box2, maxDistance) {\n const distance = box2.top - box1.bottom;\n if (distance < 0 || maxDistance !== void 0 && distance > maxDistance)\n return;\n return distance + Math.max(box1.left - box2.left, 0) + Math.max(box2.right - box1.right, 0);\n}\nfunction boxBelow(box1, box2, maxDistance) {\n const distance = box1.top - box2.bottom;\n if (distance < 0 || maxDistance !== void 0 && distance > maxDistance)\n return;\n return distance + Math.max(box1.left - box2.left, 0) + Math.max(box2.right - box1.right, 0);\n}\nfunction boxNear(box1, box2, maxDistance) {\n const kThreshold = maxDistance === void 0 ? 50 : maxDistance;\n let score = 0;\n if (box1.left - box2.right >= 0)\n score += box1.left - box2.right;\n if (box2.left - box1.right >= 0)\n score += box2.left - box1.right;\n if (box2.top - box1.bottom >= 0)\n score += box2.top - box1.bottom;\n if (box1.top - box2.bottom >= 0)\n score += box1.top - box2.bottom;\n return score > kThreshold ? void 0 : score;\n}\nvar kLayoutSelectorNames = ["left-of", "right-of", "above", "below", "near"];\nfunction layoutSelectorScore(name, element, inner, maxDistance) {\n const box = element.getBoundingClientRect();\n const scorer = { "left-of": boxLeftOf, "right-of": boxRightOf, "above": boxAbove, "below": boxBelow, "near": boxNear }[name];\n let bestScore;\n for (const e of inner) {\n if (e === element)\n continue;\n const score = scorer(box, e.getBoundingClientRect(), maxDistance);\n if (score === void 0)\n continue;\n if (bestScore === void 0 || score < bestScore)\n bestScore = score;\n }\n return bestScore;\n}\n\n// packages/injected/src/selectorUtils.ts\nfunction matchesAttributePart(value, attr) {\n const objValue = typeof value === "string" && !attr.caseSensitive ? value.toUpperCase() : value;\n const attrValue = typeof attr.value === "string" && !attr.caseSensitive ? attr.value.toUpperCase() : attr.value;\n if (attr.op === "")\n return !!objValue;\n if (attr.op === "=") {\n if (attrValue instanceof RegExp)\n return typeof objValue === "string" && !!objValue.match(attrValue);\n return objValue === attrValue;\n }\n if (typeof objValue !== "string" || typeof attrValue !== "string")\n return false;\n if (attr.op === "*=")\n return objValue.includes(attrValue);\n if (attr.op === "^=")\n return objValue.startsWith(attrValue);\n if (attr.op === "$=")\n return objValue.endsWith(attrValue);\n if (attr.op === "|=")\n return objValue === attrValue || objValue.startsWith(attrValue + "-");\n if (attr.op === "~=")\n return objValue.split(" ").includes(attrValue);\n return false;\n}\nfunction shouldSkipForTextMatching(element) {\n const document = element.ownerDocument;\n return element.nodeName === "SCRIPT" || element.nodeName === "NOSCRIPT" || element.nodeName === "STYLE" || document.head && document.head.contains(element);\n}\nfunction elementText(cache, root) {\n let value = cache.get(root);\n if (value === void 0) {\n value = { full: "", normalized: "", immediate: [] };\n if (!shouldSkipForTextMatching(root)) {\n let currentImmediate = "";\n if (root instanceof HTMLInputElement && (root.type === "submit" || root.type === "button")) {\n value = { full: root.value, normalized: normalizeWhiteSpace(root.value), immediate: [root.value] };\n } else {\n for (let child = root.firstChild; child; child = child.nextSibling) {\n if (child.nodeType === Node.TEXT_NODE) {\n value.full += child.nodeValue || "";\n currentImmediate += child.nodeValue || "";\n } else if (child.nodeType === Node.COMMENT_NODE) {\n continue;\n } else {\n if (currentImmediate)\n value.immediate.push(currentImmediate);\n currentImmediate = "";\n if (child.nodeType === Node.ELEMENT_NODE)\n value.full += elementText(cache, child).full;\n }\n }\n if (currentImmediate)\n value.immediate.push(currentImmediate);\n if (root.shadowRoot)\n value.full += elementText(cache, root.shadowRoot).full;\n if (value.full)\n value.normalized = normalizeWhiteSpace(value.full);\n }\n }\n cache.set(root, value);\n }\n return value;\n}\nfunction elementMatchesText(cache, element, matcher) {\n if (shouldSkipForTextMatching(element))\n return "none";\n if (!matcher(elementText(cache, element)))\n return "none";\n for (let child = element.firstChild; child; child = child.nextSibling) {\n if (child.nodeType === Node.ELEMENT_NODE && matcher(elementText(cache, child)))\n return "selfAndChildren";\n }\n if (element.shadowRoot && matcher(elementText(cache, element.shadowRoot)))\n return "selfAndChildren";\n return "self";\n}\nfunction getElementLabels(textCache, element) {\n const labels = getAriaLabelledByElements(element);\n if (labels)\n return labels.map((label) => elementText(textCache, label));\n const ariaLabel = element.getAttribute("aria-label");\n if (ariaLabel !== null && !!ariaLabel.trim())\n return [{ full: ariaLabel, normalized: normalizeWhiteSpace(ariaLabel), immediate: [ariaLabel] }];\n const isNonHiddenInput = element.nodeName === "INPUT" && element.type !== "hidden";\n if (["BUTTON", "METER", "OUTPUT", "PROGRESS", "SELECT", "TEXTAREA"].includes(element.nodeName) || isNonHiddenInput) {\n const labels2 = element.labels;\n if (labels2)\n return [...labels2].map((label) => elementText(textCache, label));\n }\n return [];\n}\n\n// packages/injected/src/roleSelectorEngine.ts\nvar kSupportedAttributes = ["selected", "checked", "pressed", "expanded", "level", "disabled", "name", "description", "include-hidden"];\nkSupportedAttributes.sort();\nfunction validateSupportedRole(attr, roles, role) {\n if (!roles.includes(role))\n throw new Error(`"${attr}" attribute is only supported for roles: ${roles.slice().sort().map((role2) => `"${role2}"`).join(", ")}`);\n}\nfunction validateSupportedValues(attr, values) {\n if (attr.op !== "" && !values.includes(attr.value))\n throw new Error(`"${attr.name}" must be one of ${values.map((v) => JSON.stringify(v)).join(", ")}`);\n}\nfunction validateSupportedOp(attr, ops) {\n if (!ops.includes(attr.op))\n throw new Error(`"${attr.name}" does not support "${attr.op}" matcher`);\n}\nfunction validateAttributes(attrs, role) {\n const options = { role };\n for (const attr of attrs) {\n switch (attr.name) {\n case "checked": {\n validateSupportedRole(attr.name, kAriaCheckedRoles, role);\n validateSupportedValues(attr, [true, false, "mixed"]);\n validateSupportedOp(attr, ["", "="]);\n options.checked = attr.op === "" ? true : attr.value;\n break;\n }\n case "pressed": {\n validateSupportedRole(attr.name, kAriaPressedRoles, role);\n validateSupportedValues(attr, [true, false, "mixed"]);\n validateSupportedOp(attr, ["", "="]);\n options.pressed = attr.op === "" ? true : attr.value;\n break;\n }\n case "selected": {\n validateSupportedRole(attr.name, kAriaSelectedRoles, role);\n validateSupportedValues(attr, [true, false]);\n validateSupportedOp(attr, ["", "="]);\n options.selected = attr.op === "" ? true : attr.value;\n break;\n }\n case "expanded": {\n validateSupportedRole(attr.name, kAriaExpandedRoles, role);\n validateSupportedValues(attr, [true, false]);\n validateSupportedOp(attr, ["", "="]);\n options.expanded = attr.op === "" ? true : attr.value;\n break;\n }\n case "level": {\n validateSupportedRole(attr.name, kAriaLevelRoles, role);\n if (typeof attr.value === "string")\n attr.value = +attr.value;\n if (attr.op !== "=" || typeof attr.value !== "number" || Number.isNaN(attr.value))\n throw new Error(`"level" attribute must be compared to a number`);\n options.level = attr.value;\n break;\n }\n case "disabled": {\n validateSupportedValues(attr, [true, false]);\n validateSupportedOp(attr, ["", "="]);\n options.disabled = attr.op === "" ? true : attr.value;\n break;\n }\n case "name": {\n if (attr.op === "")\n throw new Error(`"name" attribute must have a value`);\n if (typeof attr.value !== "string" && !(attr.value instanceof RegExp))\n throw new Error(`"name" attribute must be a string or a regular expression`);\n options.name = attr.value;\n options.nameOp = attr.op;\n options.nameExact = attr.caseSensitive;\n break;\n }\n case "description": {\n if (attr.op === "")\n throw new Error(`"description" attribute must have a value`);\n if (typeof attr.value !== "string" && !(attr.value instanceof RegExp))\n throw new Error(`"description" attribute must be a string or a regular expression`);\n options.description = attr.value;\n options.descriptionOp = attr.op;\n options.descriptionExact = attr.caseSensitive;\n break;\n }\n case "include-hidden": {\n validateSupportedValues(attr, [true, false]);\n validateSupportedOp(attr, ["", "="]);\n options.includeHidden = attr.op === "" ? true : attr.value;\n break;\n }\n default: {\n throw new Error(`Unknown attribute "${attr.name}", must be one of ${kSupportedAttributes.map((a) => `"${a}"`).join(", ")}.`);\n }\n }\n }\n return options;\n}\nfunction queryRole(scope, options, internal) {\n const result = [];\n const match = (element) => {\n if (getAriaRole(element) !== options.role)\n return;\n if (options.selected !== void 0 && getAriaSelected(element) !== options.selected)\n return;\n if (options.checked !== void 0 && getAriaChecked(element) !== options.checked)\n return;\n if (options.pressed !== void 0 && getAriaPressed(element) !== options.pressed)\n return;\n if (options.expanded !== void 0 && getAriaExpanded(element) !== options.expanded)\n return;\n if (options.level !== void 0 && getAriaLevel(element) !== options.level)\n return;\n if (options.disabled !== void 0 && getAriaDisabled(element) !== options.disabled)\n return;\n if (!options.includeHidden) {\n const isHidden = isElementHiddenForAria(element);\n if (isHidden)\n return;\n }\n if (options.name !== void 0) {\n const accessibleName = normalizeWhiteSpace(getElementAccessibleName(element, !!options.includeHidden));\n if (typeof options.name === "string")\n options.name = normalizeWhiteSpace(options.name);\n if (internal && !options.nameExact && options.nameOp === "=")\n options.nameOp = "*=";\n if (!matchesAttributePart(accessibleName, { name: "", jsonPath: [], op: options.nameOp || "=", value: options.name, caseSensitive: !!options.nameExact }))\n return;\n }\n if (options.description !== void 0) {\n const accessibleDescription = normalizeWhiteSpace(getElementAccessibleDescription(element, !!options.includeHidden));\n if (typeof options.description === "string")\n options.description = normalizeWhiteSpace(options.description);\n if (internal && !options.descriptionExact && options.descriptionOp === "=")\n options.descriptionOp = "*=";\n if (!matchesAttributePart(accessibleDescription, { name: "", jsonPath: [], op: options.descriptionOp || "=", value: options.description, caseSensitive: !!options.descriptionExact }))\n return;\n }\n result.push(element);\n };\n const query = (root) => {\n const shadows = [];\n if (root.shadowRoot)\n shadows.push(root.shadowRoot);\n for (const element of root.querySelectorAll("*")) {\n match(element);\n if (element.shadowRoot)\n shadows.push(element.shadowRoot);\n }\n shadows.forEach(query);\n };\n query(scope);\n return result;\n}\nfunction createRoleEngine(internal) {\n return {\n queryAll: (scope, selector) => {\n const parsed = parseAttributeSelector(selector, true);\n const role = parsed.name.toLowerCase();\n if (!role)\n throw new Error(`Role must not be empty`);\n const options = validateAttributes(parsed.attributes, role);\n beginAriaCaches();\n try {\n return queryRole(scope, options, internal);\n } finally {\n endAriaCaches();\n }\n }\n };\n}\n\n// packages/injected/src/selectorEvaluator.ts\nvar SelectorEvaluatorImpl = class {\n constructor() {\n this._retainCacheCounter = 0;\n this._cacheText = /* @__PURE__ */ new Map();\n this._cacheQueryCSS = /* @__PURE__ */ new Map();\n this._cacheMatches = /* @__PURE__ */ new Map();\n this._cacheQuery = /* @__PURE__ */ new Map();\n this._cacheMatchesSimple = /* @__PURE__ */ new Map();\n this._cacheMatchesParents = /* @__PURE__ */ new Map();\n this._cacheCallMatches = /* @__PURE__ */ new Map();\n this._cacheCallQuery = /* @__PURE__ */ new Map();\n this._cacheQuerySimple = /* @__PURE__ */ new Map();\n this._engines = /* @__PURE__ */ new Map();\n this._engines.set("not", notEngine);\n this._engines.set("is", isEngine);\n this._engines.set("where", isEngine);\n this._engines.set("has", hasEngine);\n this._engines.set("scope", scopeEngine);\n this._engines.set("light", lightEngine);\n this._engines.set("visible", visibleEngine);\n this._engines.set("text", textEngine);\n this._engines.set("text-is", textIsEngine);\n this._engines.set("text-matches", textMatchesEngine);\n this._engines.set("has-text", hasTextEngine);\n this._engines.set("right-of", createLayoutEngine("right-of"));\n this._engines.set("left-of", createLayoutEngine("left-of"));\n this._engines.set("above", createLayoutEngine("above"));\n this._engines.set("below", createLayoutEngine("below"));\n this._engines.set("near", createLayoutEngine("near"));\n this._engines.set("nth-match", nthMatchEngine);\n const allNames = [...this._engines.keys()];\n allNames.sort();\n const parserNames = [...customCSSNames];\n parserNames.sort();\n if (allNames.join("|") !== parserNames.join("|"))\n throw new Error(`Please keep customCSSNames in sync with evaluator engines: ${allNames.join("|")} vs ${parserNames.join("|")}`);\n }\n begin() {\n ++this._retainCacheCounter;\n }\n end() {\n --this._retainCacheCounter;\n if (!this._retainCacheCounter) {\n this._cacheQueryCSS.clear();\n this._cacheMatches.clear();\n this._cacheQuery.clear();\n this._cacheMatchesSimple.clear();\n this._cacheMatchesParents.clear();\n this._cacheCallMatches.clear();\n this._cacheCallQuery.clear();\n this._cacheQuerySimple.clear();\n this._cacheText.clear();\n }\n }\n _cached(cache, main, rest, cb) {\n if (!cache.has(main))\n cache.set(main, []);\n const entries = cache.get(main);\n const entry = entries.find((e) => rest.every((value, index) => e.rest[index] === value));\n if (entry)\n return entry.result;\n const result = cb();\n entries.push({ rest, result });\n return result;\n }\n _checkSelector(s) {\n const wellFormed = typeof s === "object" && s && (Array.isArray(s) || "simples" in s && s.simples.length);\n if (!wellFormed)\n throw new Error(`Malformed selector "${s}"`);\n return s;\n }\n matches(element, s, context) {\n const selector = this._checkSelector(s);\n this.begin();\n try {\n return this._cached(this._cacheMatches, element, [selector, context.scope, context.pierceShadow, context.originalScope], () => {\n if (Array.isArray(selector))\n return this._matchesEngine(isEngine, element, selector, context);\n if (this._hasScopeClause(selector))\n context = this._expandContextForScopeMatching(context);\n if (!this._matchesSimple(element, selector.simples[selector.simples.length - 1].selector, context))\n return false;\n return this._matchesParents(element, selector, selector.simples.length - 2, context);\n });\n } finally {\n this.end();\n }\n }\n query(context, s) {\n const selector = this._checkSelector(s);\n this.begin();\n try {\n return this._cached(this._cacheQuery, selector, [context.scope, context.pierceShadow, context.originalScope], () => {\n if (Array.isArray(selector))\n return this._queryEngine(isEngine, context, selector);\n if (this._hasScopeClause(selector))\n context = this._expandContextForScopeMatching(context);\n const previousScoreMap = this._scoreMap;\n this._scoreMap = /* @__PURE__ */ new Map();\n let elements = this._querySimple(context, selector.simples[selector.simples.length - 1].selector);\n elements = elements.filter((element) => this._matchesParents(element, selector, selector.simples.length - 2, context));\n if (this._scoreMap.size) {\n elements.sort((a, b) => {\n const aScore = this._scoreMap.get(a);\n const bScore = this._scoreMap.get(b);\n if (aScore === bScore)\n return 0;\n if (aScore === void 0)\n return 1;\n if (bScore === void 0)\n return -1;\n return aScore - bScore;\n });\n }\n this._scoreMap = previousScoreMap;\n return elements;\n });\n } finally {\n this.end();\n }\n }\n _markScore(element, score) {\n if (this._scoreMap)\n this._scoreMap.set(element, score);\n }\n _hasScopeClause(selector) {\n return selector.simples.some((simple) => simple.selector.functions.some((f) => f.name === "scope"));\n }\n _expandContextForScopeMatching(context) {\n if (context.scope.nodeType !== 1)\n return context;\n const scope = parentElementOrShadowHost(context.scope);\n if (!scope)\n return context;\n return { ...context, scope, originalScope: context.originalScope || context.scope };\n }\n _matchesSimple(element, simple, context) {\n return this._cached(this._cacheMatchesSimple, element, [simple, context.scope, context.pierceShadow, context.originalScope], () => {\n if (element === context.scope)\n return false;\n if (simple.css && !this._matchesCSS(element, simple.css))\n return false;\n for (const func of simple.functions) {\n if (!this._matchesEngine(this._getEngine(func.name), element, func.args, context))\n return false;\n }\n return true;\n });\n }\n _querySimple(context, simple) {\n if (!simple.functions.length)\n return this._queryCSS(context, simple.css || "*");\n return this._cached(this._cacheQuerySimple, simple, [context.scope, context.pierceShadow, context.originalScope], () => {\n let css = simple.css;\n const funcs = simple.functions;\n if (css === "*" && funcs.length)\n css = void 0;\n let elements;\n let firstIndex = -1;\n if (css !== void 0) {\n elements = this._queryCSS(context, css);\n } else {\n firstIndex = funcs.findIndex((func) => this._getEngine(func.name).query !== void 0);\n if (firstIndex === -1)\n firstIndex = 0;\n elements = this._queryEngine(this._getEngine(funcs[firstIndex].name), context, funcs[firstIndex].args);\n }\n for (let i = 0; i < funcs.length; i++) {\n if (i === firstIndex)\n continue;\n const engine = this._getEngine(funcs[i].name);\n if (engine.matches !== void 0)\n elements = elements.filter((e) => this._matchesEngine(engine, e, funcs[i].args, context));\n }\n for (let i = 0; i < funcs.length; i++) {\n if (i === firstIndex)\n continue;\n const engine = this._getEngine(funcs[i].name);\n if (engine.matches === void 0)\n elements = elements.filter((e) => this._matchesEngine(engine, e, funcs[i].args, context));\n }\n return elements;\n });\n }\n _matchesParents(element, complex, index, context) {\n if (index < 0)\n return true;\n return this._cached(this._cacheMatchesParents, element, [complex, index, context.scope, context.pierceShadow, context.originalScope], () => {\n const { selector: simple, combinator } = complex.simples[index];\n if (combinator === ">") {\n const parent = parentElementOrShadowHostInContext(element, context);\n if (!parent || !this._matchesSimple(parent, simple, context))\n return false;\n return this._matchesParents(parent, complex, index - 1, context);\n }\n if (combinator === "+") {\n const previousSibling = previousSiblingInContext(element, context);\n if (!previousSibling || !this._matchesSimple(previousSibling, simple, context))\n return false;\n return this._matchesParents(previousSibling, complex, index - 1, context);\n }\n if (combinator === "") {\n let parent = parentElementOrShadowHostInContext(element, context);\n while (parent) {\n if (this._matchesSimple(parent, simple, context)) {\n if (this._matchesParents(parent, complex, index - 1, context))\n return true;\n if (complex.simples[index - 1].combinator === "")\n break;\n }\n parent = parentElementOrShadowHostInContext(parent, context);\n }\n return false;\n }\n if (combinator === "~") {\n let previousSibling = previousSiblingInContext(element, context);\n while (previousSibling) {\n if (this._matchesSimple(previousSibling, simple, context)) {\n if (this._matchesParents(previousSibling, complex, index - 1, context))\n return true;\n if (complex.simples[index - 1].combinator === "~")\n break;\n }\n previousSibling = previousSiblingInContext(previousSibling, context);\n }\n return false;\n }\n if (combinator === ">=") {\n let parent = element;\n while (parent) {\n if (this._matchesSimple(parent, simple, context)) {\n if (this._matchesParents(parent, complex, index - 1, context))\n return true;\n if (complex.simples[index - 1].combinator === "")\n break;\n }\n parent = parentElementOrShadowHostInContext(parent, context);\n }\n return false;\n }\n throw new Error(`Unsupported combinator "${combinator}"`);\n });\n }\n _matchesEngine(engine, element, args, context) {\n if (engine.matches)\n return this._callMatches(engine, element, args, context);\n if (engine.query)\n return this._callQuery(engine, args, context).includes(element);\n throw new Error(`Selector engine should implement "matches" or "query"`);\n }\n _queryEngine(engine, context, args) {\n if (engine.query)\n return this._callQuery(engine, args, context);\n if (engine.matches)\n return this._queryCSS(context, "*").filter((element) => this._callMatches(engine, element, args, context));\n throw new Error(`Selector engine should implement "matches" or "query"`);\n }\n _callMatches(engine, element, args, context) {\n return this._cached(this._cacheCallMatches, element, [engine, context.scope, context.pierceShadow, context.originalScope, ...args], () => {\n return engine.matches(element, args, context, this);\n });\n }\n _callQuery(engine, args, context) {\n return this._cached(this._cacheCallQuery, engine, [context.scope, context.pierceShadow, context.originalScope, ...args], () => {\n return engine.query(context, args, this);\n });\n }\n _matchesCSS(element, css) {\n return element.matches(css);\n }\n _queryCSS(context, css) {\n return this._cached(this._cacheQueryCSS, css, [context.scope, context.pierceShadow, context.originalScope], () => {\n let result = [];\n function query(root) {\n result = result.concat([...root.querySelectorAll(css)]);\n if (!context.pierceShadow)\n return;\n if (root.shadowRoot)\n query(root.shadowRoot);\n for (const element of root.querySelectorAll("*")) {\n if (element.shadowRoot)\n query(element.shadowRoot);\n }\n }\n query(context.scope);\n return result;\n });\n }\n _getEngine(name) {\n const engine = this._engines.get(name);\n if (!engine)\n throw new Error(`Unknown selector engine "${name}"`);\n return engine;\n }\n};\nvar isEngine = {\n matches(element, args, context, evaluator) {\n if (args.length === 0)\n throw new Error(`"is" engine expects non-empty selector list`);\n return args.some((selector) => evaluator.matches(element, selector, context));\n },\n query(context, args, evaluator) {\n if (args.length === 0)\n throw new Error(`"is" engine expects non-empty selector list`);\n let elements = [];\n for (const arg of args)\n elements = elements.concat(evaluator.query(context, arg));\n return args.length === 1 ? elements : sortInDOMOrder(elements);\n }\n};\nvar hasEngine = {\n matches(element, args, context, evaluator) {\n if (args.length === 0)\n throw new Error(`"has" engine expects non-empty selector list`);\n return evaluator.query({ ...context, scope: element }, args).length > 0;\n }\n // TODO: we can implement efficient "query" by matching "args" and returning\n // all parents/descendants, just have to be careful with the ":scope" matching.\n};\nvar scopeEngine = {\n matches(element, args, context, evaluator) {\n if (args.length !== 0)\n throw new Error(`"scope" engine expects no arguments`);\n const actualScope = context.originalScope || context.scope;\n if (actualScope.nodeType === 9)\n return element === actualScope.documentElement;\n return element === actualScope;\n },\n query(context, args, evaluator) {\n if (args.length !== 0)\n throw new Error(`"scope" engine expects no arguments`);\n const actualScope = context.originalScope || context.scope;\n if (actualScope.nodeType === 9) {\n const root = actualScope.documentElement;\n return root ? [root] : [];\n }\n if (actualScope.nodeType === 1)\n return [actualScope];\n return [];\n }\n};\nvar notEngine = {\n matches(element, args, context, evaluator) {\n if (args.length === 0)\n throw new Error(`"not" engine expects non-empty selector list`);\n return !evaluator.matches(element, args, context);\n }\n};\nvar lightEngine = {\n query(context, args, evaluator) {\n return evaluator.query({ ...context, pierceShadow: false }, args);\n },\n matches(element, args, context, evaluator) {\n return evaluator.matches(element, args, { ...context, pierceShadow: false });\n }\n};\nvar visibleEngine = {\n matches(element, args, context, evaluator) {\n if (args.length)\n throw new Error(`"visible" engine expects no arguments`);\n return isElementVisible(element);\n }\n};\nvar textEngine = {\n matches(element, args, context, evaluator) {\n if (args.length !== 1 || typeof args[0] !== "string")\n throw new Error(`"text" engine expects a single string`);\n const text = normalizeWhiteSpace(args[0]).toLowerCase();\n const matcher = (elementText2) => elementText2.normalized.toLowerCase().includes(text);\n return elementMatchesText(evaluator._cacheText, element, matcher) === "self";\n }\n};\nvar textIsEngine = {\n matches(element, args, context, evaluator) {\n if (args.length !== 1 || typeof args[0] !== "string")\n throw new Error(`"text-is" engine expects a single string`);\n const text = normalizeWhiteSpace(args[0]);\n const matcher = (elementText2) => {\n if (!text && !elementText2.immediate.length)\n return true;\n return elementText2.immediate.some((s) => normalizeWhiteSpace(s) === text);\n };\n return elementMatchesText(evaluator._cacheText, element, matcher) !== "none";\n }\n};\nvar textMatchesEngine = {\n matches(element, args, context, evaluator) {\n if (args.length === 0 || typeof args[0] !== "string" || args.length > 2 || args.length === 2 && typeof args[1] !== "string")\n throw new Error(`"text-matches" engine expects a regexp body and optional regexp flags`);\n const re = new RegExp(args[0], args.length === 2 ? args[1] : void 0);\n const matcher = (elementText2) => re.test(elementText2.full);\n return elementMatchesText(evaluator._cacheText, element, matcher) === "self";\n }\n};\nvar hasTextEngine = {\n matches(element, args, context, evaluator) {\n if (args.length !== 1 || typeof args[0] !== "string")\n throw new Error(`"has-text" engine expects a single string`);\n if (shouldSkipForTextMatching(element))\n return false;\n const text = normalizeWhiteSpace(args[0]).toLowerCase();\n const matcher = (elementText2) => elementText2.normalized.toLowerCase().includes(text);\n return matcher(elementText(evaluator._cacheText, element));\n }\n};\nfunction createLayoutEngine(name) {\n return {\n matches(element, args, context, evaluator) {\n const maxDistance = args.length && typeof args[args.length - 1] === "number" ? args[args.length - 1] : void 0;\n const queryArgs = maxDistance === void 0 ? args : args.slice(0, args.length - 1);\n if (args.length < 1 + (maxDistance === void 0 ? 0 : 1))\n throw new Error(`"${name}" engine expects a selector list and optional maximum distance in pixels`);\n const inner = evaluator.query(context, queryArgs);\n const score = layoutSelectorScore(name, element, inner, maxDistance);\n if (score === void 0)\n return false;\n evaluator._markScore(element, score);\n return true;\n }\n };\n}\nvar nthMatchEngine = {\n query(context, args, evaluator) {\n let index = args[args.length - 1];\n if (args.length < 2)\n throw new Error(`"nth-match" engine expects non-empty selector list and an index argument`);\n if (typeof index !== "number" || index < 1)\n throw new Error(`"nth-match" engine expects a one-based index as the last argument`);\n const elements = isEngine.query(context, args.slice(0, args.length - 1), evaluator);\n index--;\n return index < elements.length ? [elements[index]] : [];\n }\n};\nfunction parentElementOrShadowHostInContext(element, context) {\n if (element === context.scope)\n return;\n if (!context.pierceShadow)\n return element.parentElement || void 0;\n return parentElementOrShadowHost(element);\n}\nfunction previousSiblingInContext(element, context) {\n if (element === context.scope)\n return;\n return element.previousElementSibling || void 0;\n}\nfunction sortInDOMOrder(elements) {\n const elementToEntry = /* @__PURE__ */ new Map();\n const roots = [];\n const result = [];\n function append(element) {\n let entry = elementToEntry.get(element);\n if (entry)\n return entry;\n const parent = parentElementOrShadowHost(element);\n if (parent) {\n const parentEntry = append(parent);\n parentEntry.children.push(element);\n } else {\n roots.push(element);\n }\n entry = { children: [], taken: false };\n elementToEntry.set(element, entry);\n return entry;\n }\n for (const e of elements)\n append(e).taken = true;\n function visit(element) {\n const entry = elementToEntry.get(element);\n if (entry.taken)\n result.push(element);\n if (entry.children.length > 1) {\n const set = new Set(entry.children);\n entry.children = [];\n let child = element.firstElementChild;\n while (child && entry.children.length < set.size) {\n if (set.has(child))\n entry.children.push(child);\n child = child.nextElementSibling;\n }\n child = element.shadowRoot ? element.shadowRoot.firstElementChild : null;\n while (child && entry.children.length < set.size) {\n if (set.has(child))\n entry.children.push(child);\n child = child.nextElementSibling;\n }\n }\n entry.children.forEach(visit);\n }\n roots.forEach(visit);\n return result;\n}\n\n// packages/injected/src/selectorGenerator.ts\nvar kTextScoreRange = 10;\nvar kExactPenalty = kTextScoreRange / 2;\nvar kTestIdScore = 1;\nvar kOtherTestIdScore = 2;\nvar kIframeByAttributeScore = 10;\nvar kBeginPenalizedScore = 50;\nvar kRoleWithNameScore = 100;\nvar kPlaceholderScore = 120;\nvar kLabelScore = 140;\nvar kAltTextScore = 160;\nvar kTextScore = 180;\nvar kTitleScore = 200;\nvar kTextScoreRegex = 250;\nvar kPlaceholderScoreExact = kPlaceholderScore + kExactPenalty;\nvar kLabelScoreExact = kLabelScore + kExactPenalty;\nvar kRoleWithNameScoreExact = kRoleWithNameScore + kExactPenalty;\nvar kAltTextScoreExact = kAltTextScore + kExactPenalty;\nvar kTextScoreExact = kTextScore + kExactPenalty;\nvar kTitleScoreExact = kTitleScore + kExactPenalty;\nvar kEndPenalizedScore = 300;\nvar kCSSIdScore = 500;\nvar kRoleWithoutNameScore = 510;\nvar kCSSInputTypeNameScore = 520;\nvar kCSSTagNameScore = 530;\nvar kNthScore = 1e4;\nvar kCSSFallbackScore = 1e7;\nvar kScoreThresholdForTextExpect = 1e3;\nfunction generateSelector(injectedScript, targetElement, options) {\n var _a;\n injectedScript._evaluator.begin();\n const cache = { allowText: /* @__PURE__ */ new Map(), disallowText: /* @__PURE__ */ new Map() };\n beginAriaCaches();\n beginDOMCaches();\n try {\n let selectors = [];\n if (options.forTextExpect) {\n let targetTokens = cssFallback(injectedScript, targetElement.ownerDocument.documentElement, options);\n for (let element = targetElement; element; element = parentElementOrShadowHost(element)) {\n const tokens = generateSelectorFor(cache, injectedScript, element, { ...options, noText: true });\n if (!tokens)\n continue;\n const score = combineScores(tokens);\n if (score <= kScoreThresholdForTextExpect) {\n targetTokens = tokens;\n break;\n }\n }\n selectors = [joinTokens(targetTokens)];\n } else {\n if (!targetElement.matches("input,textarea,select") && !targetElement.isContentEditable) {\n const interactiveParent = closestCrossShadow(targetElement, "button,select,input,[role=button],[role=checkbox],[role=radio],a,[role=link]", options.root);\n if (interactiveParent && isElementVisible(interactiveParent))\n targetElement = interactiveParent;\n }\n if (options.multiple) {\n const withText = generateSelectorFor(cache, injectedScript, targetElement, options);\n const withoutText = generateSelectorFor(cache, injectedScript, targetElement, { ...options, noText: true });\n let tokens = [withText, withoutText];\n cache.allowText.clear();\n cache.disallowText.clear();\n if (withText && hasCSSIdToken(withText))\n tokens.push(generateSelectorFor(cache, injectedScript, targetElement, { ...options, noCSSId: true }));\n if (withoutText && hasCSSIdToken(withoutText))\n tokens.push(generateSelectorFor(cache, injectedScript, targetElement, { ...options, noText: true, noCSSId: true }));\n tokens = tokens.filter(Boolean);\n if (!tokens.length) {\n const css = cssFallback(injectedScript, targetElement, options);\n tokens.push(css);\n if (hasCSSIdToken(css))\n tokens.push(cssFallback(injectedScript, targetElement, { ...options, noCSSId: true }));\n }\n selectors = [...new Set(tokens.map((t) => joinTokens(t)))];\n } else {\n const targetTokens = generateSelectorFor(cache, injectedScript, targetElement, options) || cssFallback(injectedScript, targetElement, options);\n selectors = [joinTokens(targetTokens)];\n }\n }\n const selector = selectors[0];\n const parsedSelector = injectedScript.parseSelector(selector);\n return {\n selector,\n selectors,\n elements: injectedScript.querySelectorAll(parsedSelector, (_a = options.root) != null ? _a : targetElement.ownerDocument)\n };\n } finally {\n endDOMCaches();\n endAriaCaches();\n injectedScript._evaluator.end();\n }\n}\nfunction generateSelectorFor(cache, injectedScript, targetElement, options) {\n var _a;\n if (options.root && !isInsideScope(options.root, targetElement))\n throw new Error(`Target element must belong to the root\'s subtree`);\n if (targetElement === options.root)\n return [{ engine: "css", selector: ":scope", score: 1 }];\n if (targetElement.ownerDocument.documentElement === targetElement)\n return [{ engine: "css", selector: "html", score: 1 }];\n let result = null;\n const updateResult = (candidate) => {\n if (!result || combineScores(candidate) < combineScores(result))\n result = candidate;\n };\n const candidates = [];\n if (!options.noText) {\n for (const candidate of buildTextCandidates(injectedScript, targetElement, !options.isRecursive))\n candidates.push({ candidate, isTextCandidate: true });\n }\n for (const token of buildNoTextCandidates(injectedScript, targetElement, options)) {\n if (options.omitInternalEngines && token.engine.startsWith("internal:"))\n continue;\n candidates.push({ candidate: [token], isTextCandidate: false });\n }\n candidates.sort((a, b) => combineScores(a.candidate) - combineScores(b.candidate));\n for (const { candidate, isTextCandidate } of candidates) {\n const elements = injectedScript.querySelectorAll(injectedScript.parseSelector(joinTokens(candidate)), (_a = options.root) != null ? _a : targetElement.ownerDocument);\n if (!elements.includes(targetElement)) {\n continue;\n }\n if (elements.length === 1) {\n updateResult(candidate);\n break;\n }\n const index = elements.indexOf(targetElement);\n if (index > 5) {\n continue;\n }\n updateResult([...candidate, { engine: "nth", selector: String(index), score: kNthScore }]);\n if (options.isRecursive) {\n continue;\n }\n for (let parent = parentElementOrShadowHost(targetElement); parent && parent !== options.root; parent = parentElementOrShadowHost(parent)) {\n const filtered = elements.filter((e) => isInsideScope(parent, e) && e !== parent);\n const newIndex = filtered.indexOf(targetElement);\n if (filtered.length > 5 || newIndex === -1 || newIndex === index && filtered.length > 1) {\n continue;\n }\n const inParent = filtered.length === 1 ? candidate : [...candidate, { engine: "nth", selector: String(newIndex), score: kNthScore }];\n const idealSelectorForParent = { engine: "", selector: "", score: 1 };\n if (result && combineScores([idealSelectorForParent, ...inParent]) >= combineScores(result)) {\n continue;\n }\n const noText = !!options.noText || isTextCandidate;\n const cacheMap = noText ? cache.disallowText : cache.allowText;\n let parentTokens = cacheMap.get(parent);\n if (parentTokens === void 0) {\n parentTokens = generateSelectorFor(cache, injectedScript, parent, { ...options, isRecursive: true, noText }) || cssFallback(injectedScript, parent, options);\n cacheMap.set(parent, parentTokens);\n }\n if (!parentTokens)\n continue;\n updateResult([...parentTokens, ...inParent]);\n }\n }\n return result;\n}\nfunction buildNoTextCandidates(injectedScript, element, options) {\n const candidates = [];\n const testIdAttributeNames = splitTestIdAttributeNames(options.testIdAttributeName);\n {\n for (const attr of ["data-testid", "data-test-id", "data-test"]) {\n if (!testIdAttributeNames.includes(attr) && element.getAttribute(attr))\n candidates.push({ engine: "css", selector: `[${attr}=${quoteCSSAttributeValue(element.getAttribute(attr))}]`, score: kOtherTestIdScore });\n }\n if (!options.noCSSId) {\n const idAttr = element.getAttribute("id");\n if (idAttr && !isGuidLike(idAttr))\n candidates.push({ engine: "css", selector: makeSelectorForId(idAttr), score: kCSSIdScore });\n }\n candidates.push({ engine: "css", selector: escapeNodeName(element), score: kCSSTagNameScore });\n }\n if (element.nodeName === "IFRAME") {\n for (const attribute of ["name", "title"]) {\n if (element.getAttribute(attribute))\n candidates.push({ engine: "css", selector: `${escapeNodeName(element)}[${attribute}=${quoteCSSAttributeValue(element.getAttribute(attribute))}]`, score: kIframeByAttributeScore });\n }\n for (const testIdAttr of testIdAttributeNames) {\n if (element.getAttribute(testIdAttr))\n candidates.push({ engine: "css", selector: `[${testIdAttr}=${quoteCSSAttributeValue(element.getAttribute(testIdAttr))}]`, score: kTestIdScore });\n }\n penalizeScoreForLength([candidates]);\n return candidates;\n }\n for (const testIdAttr of testIdAttributeNames) {\n if (element.getAttribute(testIdAttr))\n candidates.push({ engine: "internal:testid", selector: `[${testIdAttr}=${escapeForAttributeSelector(element.getAttribute(testIdAttr), true)}]`, score: kTestIdScore });\n }\n if (element.nodeName === "INPUT" || element.nodeName === "TEXTAREA") {\n const input = element;\n if (input.placeholder) {\n candidates.push({ engine: "internal:attr", selector: `[placeholder=${escapeForAttributeSelector(input.placeholder, true)}]`, score: kPlaceholderScoreExact });\n for (const alternative of suitableTextAlternatives(input.placeholder))\n candidates.push({ engine: "internal:attr", selector: `[placeholder=${escapeForAttributeSelector(alternative.text, false)}]`, score: kPlaceholderScore - alternative.scoreBonus });\n }\n }\n const labels = getElementLabels(injectedScript._evaluator._cacheText, element);\n for (const label of labels) {\n const labelText = label.normalized;\n candidates.push({ engine: "internal:label", selector: escapeForTextSelector(labelText, true), score: kLabelScoreExact });\n for (const alternative of suitableTextAlternatives(labelText))\n candidates.push({ engine: "internal:label", selector: escapeForTextSelector(alternative.text, false), score: kLabelScore - alternative.scoreBonus });\n }\n const ariaRole = getAriaRole(element);\n if (ariaRole && !["none", "presentation"].includes(ariaRole))\n candidates.push({ engine: "internal:role", selector: ariaRole, score: kRoleWithoutNameScore });\n if (element.getAttribute("name") && ["BUTTON", "FORM", "FIELDSET", "FRAME", "IFRAME", "INPUT", "KEYGEN", "OBJECT", "OUTPUT", "SELECT", "TEXTAREA", "MAP", "META", "PARAM"].includes(element.nodeName))\n candidates.push({ engine: "css", selector: `${escapeNodeName(element)}[name=${quoteCSSAttributeValue(element.getAttribute("name"))}]`, score: kCSSInputTypeNameScore });\n if (["INPUT", "TEXTAREA"].includes(element.nodeName) && element.getAttribute("type") !== "hidden") {\n if (element.getAttribute("type"))\n candidates.push({ engine: "css", selector: `${escapeNodeName(element)}[type=${quoteCSSAttributeValue(element.getAttribute("type"))}]`, score: kCSSInputTypeNameScore });\n }\n if (["INPUT", "TEXTAREA", "SELECT"].includes(element.nodeName) && element.getAttribute("type") !== "hidden")\n candidates.push({ engine: "css", selector: escapeNodeName(element), score: kCSSInputTypeNameScore + 1 });\n penalizeScoreForLength([candidates]);\n return candidates;\n}\nfunction buildTextCandidates(injectedScript, element, isTargetNode) {\n if (element.nodeName === "SELECT")\n return [];\n const candidates = [];\n const title = element.getAttribute("title");\n if (title) {\n candidates.push([{ engine: "internal:attr", selector: `[title=${escapeForAttributeSelector(title, true)}]`, score: kTitleScoreExact }]);\n for (const alternative of suitableTextAlternatives(title))\n candidates.push([{ engine: "internal:attr", selector: `[title=${escapeForAttributeSelector(alternative.text, false)}]`, score: kTitleScore - alternative.scoreBonus }]);\n }\n const alt = element.getAttribute("alt");\n if (alt && ["APPLET", "AREA", "IMG", "INPUT"].includes(element.nodeName)) {\n candidates.push([{ engine: "internal:attr", selector: `[alt=${escapeForAttributeSelector(alt, true)}]`, score: kAltTextScoreExact }]);\n for (const alternative of suitableTextAlternatives(alt))\n candidates.push([{ engine: "internal:attr", selector: `[alt=${escapeForAttributeSelector(alternative.text, false)}]`, score: kAltTextScore - alternative.scoreBonus }]);\n }\n const text = elementText(injectedScript._evaluator._cacheText, element).normalized;\n const textAlternatives = text ? suitableTextAlternatives(text) : [];\n if (text) {\n if (isTargetNode) {\n if (text.length <= 80)\n candidates.push([{ engine: "internal:text", selector: escapeForTextSelector(text, true), score: kTextScoreExact }]);\n for (const alternative of textAlternatives)\n candidates.push([{ engine: "internal:text", selector: escapeForTextSelector(alternative.text, false), score: kTextScore - alternative.scoreBonus }]);\n }\n const cssToken = { engine: "css", selector: escapeNodeName(element), score: kCSSTagNameScore };\n for (const alternative of textAlternatives)\n candidates.push([cssToken, { engine: "internal:has-text", selector: escapeForTextSelector(alternative.text, false), score: kTextScore - alternative.scoreBonus }]);\n if (isTargetNode && text.length <= 80) {\n const re = new RegExp("^" + escapeRegExp(text) + "$");\n candidates.push([cssToken, { engine: "internal:has-text", selector: escapeForTextSelector(re, false), score: kTextScoreRegex }]);\n }\n }\n const ariaRole = getAriaRole(element);\n if (ariaRole && !["none", "presentation"].includes(ariaRole)) {\n const ariaName = getElementAccessibleName(element, false);\n if (ariaName && !ariaName.match(/^\\p{Co}+$/u)) {\n const roleToken = { engine: "internal:role", selector: `${ariaRole}[name=${escapeForAttributeSelector(ariaName, true)}]`, score: kRoleWithNameScoreExact };\n candidates.push([roleToken]);\n for (const alternative of suitableTextAlternatives(ariaName))\n candidates.push([{ engine: "internal:role", selector: `${ariaRole}[name=${escapeForAttributeSelector(alternative.text, false)}]`, score: kRoleWithNameScore - alternative.scoreBonus }]);\n const ariaDescription = getElementAccessibleDescription(element, false);\n if (ariaDescription) {\n candidates.push([{ engine: "internal:role", selector: `${ariaRole}[name=${escapeForAttributeSelector(ariaName, true)}][description=${escapeForAttributeSelector(ariaDescription, true)}]`, score: kRoleWithNameScoreExact + 1 }]);\n for (const alternative of suitableTextAlternatives(ariaName))\n candidates.push([{ engine: "internal:role", selector: `${ariaRole}[name=${escapeForAttributeSelector(alternative.text, false)}][description=${escapeForAttributeSelector(ariaDescription, false)}]`, score: kRoleWithNameScore - alternative.scoreBonus + 1 }]);\n }\n } else {\n const roleToken = { engine: "internal:role", selector: `${ariaRole}`, score: kRoleWithoutNameScore };\n const ariaDescription = getElementAccessibleDescription(element, false);\n if (ariaDescription)\n candidates.push([{ engine: "internal:role", selector: `${ariaRole}[description=${escapeForAttributeSelector(ariaDescription, true)}]`, score: kRoleWithoutNameScore + 1 }]);\n for (const alternative of textAlternatives)\n candidates.push([roleToken, { engine: "internal:has-text", selector: escapeForTextSelector(alternative.text, false), score: kTextScore - alternative.scoreBonus }]);\n if (isTargetNode && text.length <= 80) {\n const re = new RegExp("^" + escapeRegExp(text) + "$");\n candidates.push([roleToken, { engine: "internal:has-text", selector: escapeForTextSelector(re, false), score: kTextScoreRegex }]);\n }\n }\n }\n penalizeScoreForLength(candidates);\n return candidates;\n}\nfunction makeSelectorForId(id) {\n return /^[a-zA-Z][a-zA-Z0-9\\-\\_]+$/.test(id) ? "#" + id : `[id=${quoteCSSAttributeValue(id)}]`;\n}\nfunction hasCSSIdToken(tokens) {\n return tokens.some((token) => token.engine === "css" && (token.selector.startsWith("#") || token.selector.startsWith(\'[id="\')));\n}\nfunction cssFallback(injectedScript, targetElement, options) {\n var _a;\n const root = (_a = options.root) != null ? _a : targetElement.ownerDocument;\n const tokens = [];\n function uniqueCSSSelector(prefix) {\n const path = tokens.slice();\n if (prefix)\n path.unshift(prefix);\n const selector = path.join(" > ");\n const parsedSelector = injectedScript.parseSelector(selector);\n const node = injectedScript.querySelector(parsedSelector, root, false);\n return node === targetElement ? selector : void 0;\n }\n function makeStrict(selector) {\n const token = { engine: "css", selector, score: kCSSFallbackScore };\n const parsedSelector = injectedScript.parseSelector(selector);\n const elements = injectedScript.querySelectorAll(parsedSelector, root);\n if (elements.length === 1)\n return [token];\n const nth = { engine: "nth", selector: String(elements.indexOf(targetElement)), score: kNthScore };\n return [token, nth];\n }\n for (let element = targetElement; element && element !== root; element = parentElementOrShadowHost(element)) {\n let bestTokenForLevel = "";\n if (element.id && !options.noCSSId) {\n const token = makeSelectorForId(element.id);\n const selector = uniqueCSSSelector(token);\n if (selector)\n return makeStrict(selector);\n bestTokenForLevel = token;\n }\n const parent = element.parentNode;\n const classes = [...element.classList].map(escapeClassName);\n for (let i = 0; i < classes.length; ++i) {\n const token = "." + classes.slice(0, i + 1).join(".");\n const selector = uniqueCSSSelector(token);\n if (selector)\n return makeStrict(selector);\n if (!bestTokenForLevel && parent) {\n const sameClassSiblings = parent.querySelectorAll(token);\n if (sameClassSiblings.length === 1)\n bestTokenForLevel = token;\n }\n }\n if (parent) {\n const siblings = [...parent.children];\n const nodeName = element.nodeName;\n const sameTagSiblings = siblings.filter((sibling) => sibling.nodeName === nodeName);\n const token = sameTagSiblings.indexOf(element) === 0 ? escapeNodeName(element) : `${escapeNodeName(element)}:nth-child(${1 + siblings.indexOf(element)})`;\n const selector = uniqueCSSSelector(token);\n if (selector)\n return makeStrict(selector);\n if (!bestTokenForLevel)\n bestTokenForLevel = token;\n } else if (!bestTokenForLevel) {\n bestTokenForLevel = escapeNodeName(element);\n }\n tokens.unshift(bestTokenForLevel);\n }\n return makeStrict(uniqueCSSSelector());\n}\nfunction penalizeScoreForLength(groups) {\n for (const group of groups) {\n for (const token of group) {\n if (token.score > kBeginPenalizedScore && token.score < kEndPenalizedScore)\n token.score += Math.min(kTextScoreRange, token.selector.length / 10 | 0);\n }\n }\n}\nfunction joinTokens(tokens) {\n const parts = [];\n let lastEngine = "";\n for (const { engine, selector } of tokens) {\n if (parts.length && (lastEngine !== "css" || engine !== "css" || selector.startsWith(":nth-match(")))\n parts.push(">>");\n lastEngine = engine;\n if (engine === "css")\n parts.push(selector);\n else\n parts.push(`${engine}=${selector}`);\n }\n return parts.join(" ");\n}\nfunction combineScores(tokens) {\n let score = 0;\n for (let i = 0; i < tokens.length; i++)\n score += tokens[i].score * (tokens.length - i);\n return score;\n}\nfunction isGuidLike(id) {\n let lastCharacterType;\n let transitionCount = 0;\n for (let i = 0; i < id.length; ++i) {\n const c = id[i];\n let characterType;\n if (c === "-" || c === "_")\n continue;\n if (c >= "a" && c <= "z")\n characterType = "lower";\n else if (c >= "A" && c <= "Z")\n characterType = "upper";\n else if (c >= "0" && c <= "9")\n characterType = "digit";\n else\n characterType = "other";\n if (characterType === "lower" && lastCharacterType === "upper") {\n lastCharacterType = characterType;\n continue;\n }\n if (lastCharacterType && lastCharacterType !== characterType)\n ++transitionCount;\n lastCharacterType = characterType;\n }\n return transitionCount >= id.length / 4;\n}\nfunction trimWordBoundary(text, maxLength) {\n if (text.length <= maxLength)\n return text;\n text = text.substring(0, maxLength);\n const match = text.match(/^(.*)\\b(.+?)$/);\n if (!match)\n return "";\n return match[1].trimEnd();\n}\nfunction suitableTextAlternatives(text) {\n let result = [];\n {\n const match = text.match(/^([\\d.,]+)[^.,\\w]/);\n const leadingNumberLength = match ? match[1].length : 0;\n if (leadingNumberLength) {\n const alt = trimWordBoundary(text.substring(leadingNumberLength).trimStart(), 80);\n result.push({ text: alt, scoreBonus: alt.length <= 30 ? 2 : 1 });\n }\n }\n {\n const match = text.match(/[^.,\\w]([\\d.,]+)$/);\n const trailingNumberLength = match ? match[1].length : 0;\n if (trailingNumberLength) {\n const alt = trimWordBoundary(text.substring(0, text.length - trailingNumberLength).trimEnd(), 80);\n result.push({ text: alt, scoreBonus: alt.length <= 30 ? 2 : 1 });\n }\n }\n if (text.length <= 30) {\n result.push({ text, scoreBonus: 0 });\n } else {\n result.push({ text: trimWordBoundary(text, 80), scoreBonus: 0 });\n result.push({ text: trimWordBoundary(text, 30), scoreBonus: 1 });\n }\n result = result.filter((r) => r.text);\n if (!result.length)\n result.push({ text: text.substring(0, 80), scoreBonus: 0 });\n return result;\n}\nfunction escapeNodeName(node) {\n return node.nodeName.toLocaleLowerCase().replace(/[:\\.]/g, (char) => "\\\\" + char);\n}\nfunction escapeClassName(className) {\n let result = "";\n for (let i = 0; i < className.length; i++)\n result += cssEscapeCharacter(className, i);\n return result;\n}\nfunction cssEscapeCharacter(s, i) {\n const c = s.charCodeAt(i);\n if (c === 0)\n return "\\uFFFD";\n if (c >= 1 && c <= 31 || c >= 48 && c <= 57 && (i === 0 || i === 1 && s.charCodeAt(0) === 45))\n return "\\\\" + c.toString(16) + " ";\n if (i === 0 && c === 45 && s.length === 1)\n return "\\\\" + s.charAt(i);\n if (c >= 128 || c === 45 || c === 95 || c >= 48 && c <= 57 || c >= 65 && c <= 90 || c >= 97 && c <= 122)\n return s.charAt(i);\n return "\\\\" + s.charAt(i);\n}\n\n// packages/injected/src/xpathSelectorEngine.ts\nvar XPathEngine = {\n queryAll(root, selector) {\n if (selector.startsWith("/") && root.nodeType !== Node.DOCUMENT_NODE)\n selector = "." + selector;\n const result = [];\n const document = root.ownerDocument || root;\n if (!document)\n return result;\n const it = document.evaluate(selector, root, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE);\n for (let node = it.iterateNext(); node; node = it.iterateNext()) {\n if (node.nodeType === Node.ELEMENT_NODE)\n result.push(node);\n }\n return result;\n }\n};\n\n// packages/injected/src/consoleApi.ts\nvar selectorSymbol = Symbol("selector");\nselectorSymbol;\nvar _Locator = class _Locator {\n constructor(injectedScript, selector, options) {\n if (options == null ? void 0 : options.hasText)\n selector += ` >> internal:has-text=${escapeForTextSelector(options.hasText, false)}`;\n if (options == null ? void 0 : options.hasNotText)\n selector += ` >> internal:has-not-text=${escapeForTextSelector(options.hasNotText, false)}`;\n if (options == null ? void 0 : options.has)\n selector += ` >> internal:has=` + JSON.stringify(options.has[selectorSymbol]);\n if (options == null ? void 0 : options.hasNot)\n selector += ` >> internal:has-not=` + JSON.stringify(options.hasNot[selectorSymbol]);\n if ((options == null ? void 0 : options.visible) !== void 0)\n selector += ` >> visible=${options.visible ? "true" : "false"}`;\n this[selectorSymbol] = selector;\n if (selector) {\n const parsed = injectedScript.parseSelector(selector);\n this.element = injectedScript.querySelector(parsed, injectedScript.document, false);\n this.elements = injectedScript.querySelectorAll(parsed, injectedScript.document);\n }\n const selectorBase = selector;\n const self = this;\n self.locator = (selector2, options2) => {\n return new _Locator(injectedScript, selectorBase ? selectorBase + " >> " + selector2 : selector2, options2);\n };\n self.getByTestId = (testId) => self.locator(getByTestIdSelector(injectedScript.testIdAttributeNameForStrictErrorAndConsoleCodegen(), testId));\n self.getByAltText = (text, options2) => self.locator(getByAltTextSelector(text, options2));\n self.getByLabel = (text, options2) => self.locator(getByLabelSelector(text, options2));\n self.getByPlaceholder = (text, options2) => self.locator(getByPlaceholderSelector(text, options2));\n self.getByText = (text, options2) => self.locator(getByTextSelector(text, options2));\n self.getByTitle = (text, options2) => self.locator(getByTitleSelector(text, options2));\n self.getByRole = (role, options2 = {}) => self.locator(getByRoleSelector(role, options2));\n self.filter = (options2) => new _Locator(injectedScript, selector, options2);\n self.first = () => self.locator("nth=0");\n self.last = () => self.locator("nth=-1");\n self.nth = (index) => self.locator(`nth=${index}`);\n self.and = (locator) => new _Locator(injectedScript, selectorBase + ` >> internal:and=` + JSON.stringify(locator[selectorSymbol]));\n self.or = (locator) => new _Locator(injectedScript, selectorBase + ` >> internal:or=` + JSON.stringify(locator[selectorSymbol]));\n }\n};\nvar Locator = _Locator;\nvar ConsoleAPI = class {\n constructor(injectedScript) {\n this._injectedScript = injectedScript;\n }\n install() {\n if (this._injectedScript.window.playwright)\n return;\n this._injectedScript.window.playwright = {\n $: (selector, strict) => this._querySelector(selector, !!strict),\n $$: (selector) => this._querySelectorAll(selector),\n inspect: (selector) => this._inspect(selector),\n selector: (element) => this._selector(element),\n generateLocator: (element, language) => this._generateLocator(element, language),\n ariaSnapshot: (element, options) => {\n return this._injectedScript.ariaSnapshot(element || this._injectedScript.document.body, options || { mode: "default" });\n },\n resume: () => this._resume(),\n ...new Locator(this._injectedScript, "")\n };\n delete this._injectedScript.window.playwright.filter;\n delete this._injectedScript.window.playwright.first;\n delete this._injectedScript.window.playwright.last;\n delete this._injectedScript.window.playwright.nth;\n delete this._injectedScript.window.playwright.and;\n delete this._injectedScript.window.playwright.or;\n }\n _querySelector(selector, strict) {\n if (typeof selector !== "string")\n throw new Error(`Usage: playwright.query(\'Playwright >> selector\').`);\n const parsed = this._injectedScript.parseSelector(selector);\n return this._injectedScript.querySelector(parsed, this._injectedScript.document, strict);\n }\n _querySelectorAll(selector) {\n if (typeof selector !== "string")\n throw new Error(`Usage: playwright.$$(\'Playwright >> selector\').`);\n const parsed = this._injectedScript.parseSelector(selector);\n return this._injectedScript.querySelectorAll(parsed, this._injectedScript.document);\n }\n _inspect(selector) {\n if (typeof selector !== "string")\n throw new Error(`Usage: playwright.inspect(\'Playwright >> selector\').`);\n this._injectedScript.window.inspect(this._querySelector(selector, false));\n }\n _selector(element) {\n if (!(element instanceof Element))\n throw new Error(`Usage: playwright.selector(element).`);\n return this._injectedScript.generateSelectorSimple(element);\n }\n _generateLocator(element, language) {\n if (!(element instanceof Element))\n throw new Error(`Usage: playwright.locator(element).`);\n const selector = this._injectedScript.generateSelectorSimple(element);\n return asLocator(language || "javascript", selector);\n }\n _resume() {\n if (!this._injectedScript.window.__pw_resume)\n return false;\n this._injectedScript.window.__pw_resume().catch(() => {\n });\n }\n};\n\n// packages/isomorphic/utilityScriptSerializers.ts\nfunction isRegExp2(obj) {\n try {\n return obj instanceof RegExp || Object.prototype.toString.call(obj) === "[object RegExp]";\n } catch (error) {\n return false;\n }\n}\nfunction isDate(obj) {\n try {\n return obj instanceof Date || Object.prototype.toString.call(obj) === "[object Date]";\n } catch (error) {\n return false;\n }\n}\nfunction isURL(obj) {\n try {\n return obj instanceof URL || Object.prototype.toString.call(obj) === "[object URL]";\n } catch (error) {\n return false;\n }\n}\nfunction isError(obj) {\n var _a;\n try {\n return obj instanceof Error || obj && ((_a = Object.getPrototypeOf(obj)) == null ? void 0 : _a.name) === "Error";\n } catch (error) {\n return false;\n }\n}\nfunction isTypedArray(obj, constructor) {\n try {\n return obj instanceof constructor || Object.prototype.toString.call(obj) === `[object ${constructor.name}]`;\n } catch (error) {\n return false;\n }\n}\nfunction isArrayBuffer(obj) {\n try {\n return obj instanceof ArrayBuffer || Object.prototype.toString.call(obj) === "[object ArrayBuffer]";\n } catch (error) {\n return false;\n }\n}\nvar typedArrayConstructors = {\n i8: Int8Array,\n ui8: Uint8Array,\n ui8c: Uint8ClampedArray,\n i16: Int16Array,\n ui16: Uint16Array,\n i32: Int32Array,\n ui32: Uint32Array,\n // TODO: add Float16Array once it\'s in baseline\n f32: Float32Array,\n f64: Float64Array,\n bi64: BigInt64Array,\n bui64: BigUint64Array\n};\nfunction typedArrayToBase64(array) {\n if ("toBase64" in array)\n return array.toBase64();\n const binary = Array.from(new Uint8Array(array.buffer, array.byteOffset, array.byteLength)).map((b) => String.fromCharCode(b)).join("");\n return btoa(binary);\n}\nfunction base64ToTypedArray(base64, TypedArrayConstructor) {\n const binary = atob(base64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++)\n bytes[i] = binary.charCodeAt(i);\n return new TypedArrayConstructor(bytes.buffer);\n}\nfunction parseEvaluationResultValue(value, handles = [], refs = /* @__PURE__ */ new Map()) {\n if (Object.is(value, void 0))\n return void 0;\n if (typeof value === "object" && value) {\n if ("ref" in value)\n return refs.get(value.ref);\n if ("v" in value) {\n if (value.v === "undefined")\n return void 0;\n if (value.v === "null")\n return null;\n if (value.v === "NaN")\n return NaN;\n if (value.v === "Infinity")\n return Infinity;\n if (value.v === "-Infinity")\n return -Infinity;\n if (value.v === "-0")\n return -0;\n return void 0;\n }\n if ("d" in value) {\n return new Date(value.d);\n }\n if ("u" in value)\n return new URL(value.u);\n if ("bi" in value)\n return BigInt(value.bi);\n if ("e" in value) {\n const error = new Error(value.e.m);\n error.name = value.e.n;\n error.stack = value.e.s;\n return error;\n }\n if ("r" in value)\n return new RegExp(value.r.p, value.r.f);\n if ("a" in value) {\n const result = [];\n refs.set(value.id, result);\n for (const a of value.a)\n result.push(parseEvaluationResultValue(a, handles, refs));\n return result;\n }\n if ("o" in value) {\n const result = {};\n refs.set(value.id, result);\n for (const { k, v } of value.o) {\n if (k === "__proto__")\n continue;\n result[k] = parseEvaluationResultValue(v, handles, refs);\n }\n return result;\n }\n if ("h" in value)\n return handles[value.h];\n if ("ta" in value)\n return base64ToTypedArray(value.ta.b, typedArrayConstructors[value.ta.k]);\n if ("ab" in value)\n return base64ToTypedArray(value.ab.b, Uint8Array).buffer;\n }\n return value;\n}\nfunction serializeAsCallArgument(value, handleSerializer) {\n return serialize(value, handleSerializer, { visited: /* @__PURE__ */ new Map(), lastId: 0 });\n}\nfunction serialize(value, handleSerializer, visitorInfo) {\n if (value && typeof value === "object") {\n if (typeof globalThis.Window === "function" && value instanceof globalThis.Window)\n return "ref: ";\n if (typeof globalThis.Document === "function" && value instanceof globalThis.Document)\n return "ref: ";\n if (typeof globalThis.Node === "function" && value instanceof globalThis.Node)\n return "ref: ";\n }\n return innerSerialize(value, handleSerializer, visitorInfo);\n}\nfunction innerSerialize(value, handleSerializer, visitorInfo) {\n var _a;\n const result = handleSerializer(value);\n if ("fallThrough" in result)\n value = result.fallThrough;\n else\n return result;\n if (typeof value === "symbol")\n return { v: "undefined" };\n if (Object.is(value, void 0))\n return { v: "undefined" };\n if (Object.is(value, null))\n return { v: "null" };\n if (Object.is(value, NaN))\n return { v: "NaN" };\n if (Object.is(value, Infinity))\n return { v: "Infinity" };\n if (Object.is(value, -Infinity))\n return { v: "-Infinity" };\n if (Object.is(value, -0))\n return { v: "-0" };\n if (typeof value === "boolean")\n return value;\n if (typeof value === "number")\n return value;\n if (typeof value === "string")\n return value;\n if (typeof value === "bigint")\n return { bi: value.toString() };\n if (isError(value)) {\n let stack;\n if ((_a = value.stack) == null ? void 0 : _a.startsWith(value.name + ": " + value.message)) {\n stack = value.stack;\n } else {\n stack = `${value.name}: ${value.message}\n${value.stack}`;\n }\n return { e: { n: value.name, m: value.message, s: stack } };\n }\n if (isDate(value))\n return { d: value.toJSON() };\n if (isURL(value))\n return { u: value.toJSON() };\n if (isRegExp2(value))\n return { r: { p: value.source, f: value.flags } };\n for (const [k, ctor] of Object.entries(typedArrayConstructors)) {\n if (isTypedArray(value, ctor))\n return { ta: { b: typedArrayToBase64(value), k } };\n }\n if (isArrayBuffer(value))\n return { ab: { b: typedArrayToBase64(new Uint8Array(value)) } };\n const id = visitorInfo.visited.get(value);\n if (id)\n return { ref: id };\n if (Array.isArray(value)) {\n const a = [];\n const id2 = ++visitorInfo.lastId;\n visitorInfo.visited.set(value, id2);\n for (let i = 0; i < value.length; ++i)\n a.push(serialize(value[i], handleSerializer, visitorInfo));\n return { a, id: id2 };\n }\n if (typeof value === "object") {\n const o = [];\n const id2 = ++visitorInfo.lastId;\n visitorInfo.visited.set(value, id2);\n for (const name of Object.keys(value)) {\n let item;\n try {\n item = value[name];\n } catch (e) {\n continue;\n }\n if (name === "toJSON" && typeof item === "function")\n o.push({ k: name, v: { o: [], id: 0 } });\n else\n o.push({ k: name, v: serialize(item, handleSerializer, visitorInfo) });\n }\n let jsonWrapper;\n try {\n if (o.length === 0 && value.toJSON && typeof value.toJSON === "function")\n jsonWrapper = { value: value.toJSON() };\n } catch (e) {\n }\n if (jsonWrapper)\n return innerSerialize(jsonWrapper.value, handleSerializer, visitorInfo);\n return { o, id: id2 };\n }\n}\n\n// packages/injected/src/utilityScript.ts\nvar UtilityScript = class {\n constructor(global, isUnderTest) {\n var _a, _b, _c, _d, _e, _f, _g, _h;\n this.global = global;\n this.isUnderTest = isUnderTest;\n if (global.__pwClock) {\n this.builtins = global.__pwClock.builtins;\n } else {\n this.builtins = {\n setTimeout: (_a = global.setTimeout) == null ? void 0 : _a.bind(global),\n clearTimeout: (_b = global.clearTimeout) == null ? void 0 : _b.bind(global),\n setInterval: (_c = global.setInterval) == null ? void 0 : _c.bind(global),\n clearInterval: (_d = global.clearInterval) == null ? void 0 : _d.bind(global),\n requestAnimationFrame: (_e = global.requestAnimationFrame) == null ? void 0 : _e.bind(global),\n cancelAnimationFrame: (_f = global.cancelAnimationFrame) == null ? void 0 : _f.bind(global),\n requestIdleCallback: (_g = global.requestIdleCallback) == null ? void 0 : _g.bind(global),\n cancelIdleCallback: (_h = global.cancelIdleCallback) == null ? void 0 : _h.bind(global),\n performance: global.performance,\n Intl: global.Intl,\n Date: global.Date,\n AbortSignal: global.AbortSignal\n };\n }\n if (this.isUnderTest)\n global.builtins = this.builtins;\n }\n evaluate(isFunction, returnByValue, expression, argCount, ...argsAndHandles) {\n const args = argsAndHandles.slice(0, argCount);\n const handles = argsAndHandles.slice(argCount);\n const parameters = [];\n for (let i = 0; i < args.length; i++)\n parameters[i] = parseEvaluationResultValue(args[i], handles);\n let result = this.global.eval(expression);\n if (isFunction === true) {\n result = result(...parameters);\n } else if (isFunction === false) {\n result = result;\n } else {\n if (typeof result === "function")\n result = result(...parameters);\n }\n return returnByValue ? this._promiseAwareJsonValueNoThrow(result) : result;\n }\n jsonValue(returnByValue, value) {\n if (value === void 0)\n return void 0;\n return serializeAsCallArgument(value, (value2) => ({ fallThrough: value2 }));\n }\n _promiseAwareJsonValueNoThrow(value) {\n const safeJson = (value2) => {\n try {\n return this.jsonValue(true, value2);\n } catch (e) {\n return void 0;\n }\n };\n if (value && typeof value === "object" && typeof value.then === "function") {\n return (async () => {\n const promiseValue = await value;\n return safeJson(promiseValue);\n })();\n }\n return safeJson(value);\n }\n};\n\n// packages/injected/src/injectedScript.ts\nvar InjectedScript = class {\n constructor(window, options) {\n this._testIdAttributeNameForStrictErrorAndConsoleCodegen = "data-testid";\n this._lastAriaSnapshotForTrack = /* @__PURE__ */ new Map();\n // Recorder must use any external dependencies through InjectedScript.\n // Otherwise it will end up with a copy of all modules it uses, and any\n // module-level globals will be duplicated, which leads to subtle bugs.\n this.utils = {\n asLocator,\n cacheNormalizedWhitespaces,\n elementText,\n getAriaRole,\n getElementAccessibleDescription,\n getElementAccessibleName,\n isElementVisible,\n isInsideScope,\n normalizeWhiteSpace,\n parseAriaSnapshot,\n generateAriaTree,\n findNewElement,\n // Builtins protect injected code from clock emulation.\n builtins: null\n };\n this.window = window;\n this.document = window.document;\n this.isUnderTest = options.isUnderTest;\n this.utils.builtins = new UtilityScript(window, options.isUnderTest).builtins;\n this._sdkLanguage = options.sdkLanguage;\n this._testIdAttributeNameForStrictErrorAndConsoleCodegen = options.testIdAttributeName;\n this._evaluator = new SelectorEvaluatorImpl();\n this.consoleApi = new ConsoleAPI(this);\n this.onGlobalListenersRemoved = /* @__PURE__ */ new Set();\n this._autoClosingTags = /* @__PURE__ */ new Set(["AREA", "BASE", "BR", "COL", "COMMAND", "EMBED", "HR", "IMG", "INPUT", "KEYGEN", "LINK", "MENUITEM", "META", "PARAM", "SOURCE", "TRACK", "WBR"]);\n this._booleanAttributes = /* @__PURE__ */ new Set(["checked", "selected", "disabled", "readonly", "multiple"]);\n this._eventTypes = /* @__PURE__ */ new Map([\n ["auxclick", "mouse"],\n ["click", "mouse"],\n ["dblclick", "mouse"],\n ["mousedown", "mouse"],\n ["mouseeenter", "mouse"],\n ["mouseleave", "mouse"],\n ["mousemove", "mouse"],\n ["mouseout", "mouse"],\n ["mouseover", "mouse"],\n ["mouseup", "mouse"],\n ["mouseleave", "mouse"],\n ["mousewheel", "mouse"],\n ["keydown", "keyboard"],\n ["keyup", "keyboard"],\n ["keypress", "keyboard"],\n ["textInput", "keyboard"],\n ["touchstart", "touch"],\n ["touchmove", "touch"],\n ["touchend", "touch"],\n ["touchcancel", "touch"],\n ["pointerover", "pointer"],\n ["pointerout", "pointer"],\n ["pointerenter", "pointer"],\n ["pointerleave", "pointer"],\n ["pointerdown", "pointer"],\n ["pointerup", "pointer"],\n ["pointermove", "pointer"],\n ["pointercancel", "pointer"],\n ["gotpointercapture", "pointer"],\n ["lostpointercapture", "pointer"],\n ["focus", "focus"],\n ["blur", "focus"],\n ["drag", "drag"],\n ["dragstart", "drag"],\n ["dragend", "drag"],\n ["dragover", "drag"],\n ["dragenter", "drag"],\n ["dragleave", "drag"],\n ["dragexit", "drag"],\n ["drop", "drag"],\n ["wheel", "wheel"],\n ["deviceorientation", "deviceorientation"],\n ["deviceorientationabsolute", "deviceorientation"],\n ["devicemotion", "devicemotion"]\n ]);\n this._hoverHitTargetInterceptorEvents = /* @__PURE__ */ new Set(["mousemove"]);\n this._tapHitTargetInterceptorEvents = /* @__PURE__ */ new Set(["pointerdown", "pointerup", "touchstart", "touchend", "touchcancel"]);\n this._mouseHitTargetInterceptorEvents = /* @__PURE__ */ new Set(["mousedown", "mouseup", "pointerdown", "pointerup", "click", "auxclick", "dblclick", "contextmenu"]);\n this._allHitTargetInterceptorEvents = /* @__PURE__ */ new Set([...this._hoverHitTargetInterceptorEvents, ...this._tapHitTargetInterceptorEvents, ...this._mouseHitTargetInterceptorEvents]);\n this._engines = /* @__PURE__ */ new Map();\n this._engines.set("xpath", XPathEngine);\n this._engines.set("xpath:light", XPathEngine);\n this._engines.set("role", createRoleEngine(false));\n this._engines.set("text", this._createTextEngine(true, false));\n this._engines.set("text:light", this._createTextEngine(false, false));\n this._engines.set("id", this._createAttributeEngine("id", true));\n this._engines.set("id:light", this._createAttributeEngine("id", false));\n this._engines.set("data-testid", this._createAttributeEngine("data-testid", true));\n this._engines.set("data-testid:light", this._createAttributeEngine("data-testid", false));\n this._engines.set("data-test-id", this._createAttributeEngine("data-test-id", true));\n this._engines.set("data-test-id:light", this._createAttributeEngine("data-test-id", false));\n this._engines.set("data-test", this._createAttributeEngine("data-test", true));\n this._engines.set("data-test:light", this._createAttributeEngine("data-test", false));\n this._engines.set("css", this._createCSSEngine());\n this._engines.set("nth", { queryAll: () => [] });\n this._engines.set("visible", this._createVisibleEngine());\n this._engines.set("internal:control", this._createControlEngine());\n this._engines.set("internal:has", this._createHasEngine());\n this._engines.set("internal:has-not", this._createHasNotEngine());\n this._engines.set("internal:and", { queryAll: () => [] });\n this._engines.set("internal:or", { queryAll: () => [] });\n this._engines.set("internal:chain", this._createInternalChainEngine());\n this._engines.set("internal:label", this._createInternalLabelEngine());\n this._engines.set("internal:text", this._createTextEngine(true, true));\n this._engines.set("internal:has-text", this._createInternalHasTextEngine());\n this._engines.set("internal:has-not-text", this._createInternalHasNotTextEngine());\n this._engines.set("internal:attr", this._createNamedAttributeEngine());\n this._engines.set("internal:testid", this._createTestIdEngine());\n this._engines.set("internal:role", createRoleEngine(true));\n this._engines.set("internal:describe", this._createDescribeEngine());\n this._engines.set("aria-ref", this._createAriaRefEngine());\n for (const { name, source } of options.customEngines)\n this._engines.set(name, this.eval(source));\n this._stableRafCount = options.stableRafCount;\n this._browserName = options.browserName;\n this._shouldPrependErrorPrefix = !!options.shouldPrependErrorPrefix;\n this._isUtilityWorld = !!options.isUtilityWorld;\n setGlobalOptions({ browserNameForWorkarounds: options.browserName });\n this._setupGlobalListenersRemovalDetection();\n this._setupHitTargetInterceptors();\n if (this.isUnderTest)\n this.window.__injectedScript = this;\n }\n eval(expression) {\n return this.window.eval(expression);\n }\n testIdAttributeNameForStrictErrorAndConsoleCodegen() {\n return this._testIdAttributeNameForStrictErrorAndConsoleCodegen;\n }\n parseSelector(selector) {\n const result = parseSelector(selector);\n visitAllSelectorParts(result, (part) => {\n if (!this._engines.has(part.name))\n throw this.createStacklessError(`Unknown engine "${part.name}" while parsing selector ${selector}`);\n });\n return result;\n }\n generateSelector(targetElement, options) {\n return generateSelector(this, targetElement, options);\n }\n generateSelectorSimple(targetElement, options) {\n return generateSelector(this, targetElement, { ...options, testIdAttributeName: this._testIdAttributeNameForStrictErrorAndConsoleCodegen }).selector;\n }\n querySelector(selector, root, strict) {\n const result = this.querySelectorAll(selector, root);\n if (strict && result.length > 1)\n throw this.strictModeViolationError(selector, result);\n this.checkDeprecatedSelectorUsage(selector, result);\n return result[0];\n }\n _queryNth(elements, part) {\n const list = [...elements];\n let nth = +part.body;\n if (nth === -1)\n nth = list.length - 1;\n return new Set(list.slice(nth, nth + 1));\n }\n _queryLayoutSelector(elements, part, originalRoot) {\n const name = part.name;\n const body = part.body;\n const result = [];\n const inner = this.querySelectorAll(body.parsed, originalRoot);\n for (const element of elements) {\n const score = layoutSelectorScore(name, element, inner, body.distance);\n if (score !== void 0)\n result.push({ element, score });\n }\n result.sort((a, b) => a.score - b.score);\n return new Set(result.map((r) => r.element));\n }\n ariaSnapshot(node, options) {\n return this.incrementalAriaSnapshot(node, options).full;\n }\n incrementalAriaSnapshot(node, options) {\n if (node.nodeType !== Node.ELEMENT_NODE)\n throw this.createStacklessError("Can only capture aria snapshot of Element nodes.");\n const ariaSnapshot = generateAriaTree(node, options);\n const rendered = renderAriaTree(ariaSnapshot, options);\n let incremental;\n if (options.track) {\n const previousSnapshot = this._lastAriaSnapshotForTrack.get(options.track);\n if (previousSnapshot)\n incremental = renderAriaTree(ariaSnapshot, options, previousSnapshot).text;\n this._lastAriaSnapshotForTrack.set(options.track, ariaSnapshot);\n }\n this._lastAriaSnapshotForQuery = ariaSnapshot;\n return { full: rendered.text, incremental, iframeRefs: ariaSnapshot.iframeRefs, iframeDepths: rendered.iframeDepths };\n }\n ariaSnapshotForRecorder() {\n const tree = generateAriaTree(this.document.body, { mode: "ai" });\n const { text: ariaSnapshot } = renderAriaTree(tree, { mode: "ai" });\n return { ariaSnapshot, refs: tree.refs };\n }\n getAllElementsMatchingExpectAriaTemplate(document, template) {\n return getAllElementsMatchingExpectAriaTemplate(document.documentElement, template);\n }\n querySelectorAll(selector, root) {\n if (selector.capture !== void 0) {\n if (selector.parts.some((part) => part.name === "nth"))\n throw this.createStacklessError(`Can\'t query n-th element in a request with the capture.`);\n const withHas = { parts: selector.parts.slice(0, selector.capture + 1) };\n if (selector.capture < selector.parts.length - 1) {\n const parsed = { parts: selector.parts.slice(selector.capture + 1) };\n const has = { name: "internal:has", body: { parsed }, source: stringifySelector(parsed) };\n withHas.parts.push(has);\n }\n return this.querySelectorAll(withHas, root);\n }\n if (!root["querySelectorAll"])\n throw this.createStacklessError("Node is not queryable.");\n if (selector.capture !== void 0) {\n throw this.createStacklessError("Internal error: there should not be a capture in the selector.");\n }\n if (root.nodeType === 11 && selector.parts.length === 1 && selector.parts[0].name === "css" && selector.parts[0].source === ":scope")\n return [root];\n this._evaluator.begin();\n try {\n let roots = /* @__PURE__ */ new Set([root]);\n for (const part of selector.parts) {\n if (part.name === "nth") {\n roots = this._queryNth(roots, part);\n } else if (part.name === "internal:and") {\n const andElements = this.querySelectorAll(part.body.parsed, root);\n roots = new Set(andElements.filter((e) => roots.has(e)));\n } else if (part.name === "internal:or") {\n const orElements = this.querySelectorAll(part.body.parsed, root);\n roots = new Set(sortInDOMOrder(/* @__PURE__ */ new Set([...roots, ...orElements])));\n } else if (kLayoutSelectorNames.includes(part.name)) {\n roots = this._queryLayoutSelector(roots, part, root);\n } else {\n const next = /* @__PURE__ */ new Set();\n for (const root2 of roots) {\n const all = this._queryEngineAll(part, root2);\n for (const one of all)\n next.add(one);\n }\n roots = next;\n }\n }\n return [...roots];\n } finally {\n this._evaluator.end();\n }\n }\n _queryEngineAll(part, root) {\n const result = this._engines.get(part.name).queryAll(root, part.body);\n for (const element of result) {\n if (!("nodeName" in element))\n throw this.createStacklessError(`Expected a Node but got ${Object.prototype.toString.call(element)}`);\n }\n return result;\n }\n _createAttributeEngine(attribute, shadow) {\n const toCSS = (selector) => {\n const css = `[${attribute}=${JSON.stringify(selector)}]`;\n return [{ simples: [{ selector: { css, functions: [] }, combinator: "" }] }];\n };\n return {\n queryAll: (root, selector) => {\n return this._evaluator.query({ scope: root, pierceShadow: shadow }, toCSS(selector));\n }\n };\n }\n _createCSSEngine() {\n return {\n queryAll: (root, body) => {\n return this._evaluator.query({ scope: root, pierceShadow: true }, body);\n }\n };\n }\n _createTextEngine(shadow, internal) {\n const queryAll = (root, selector) => {\n const { matcher, kind } = createTextMatcher(selector, internal);\n const result = [];\n let lastDidNotMatchSelf = null;\n const appendElement = (element) => {\n if (kind === "lax" && lastDidNotMatchSelf && lastDidNotMatchSelf.contains(element))\n return false;\n const matches = elementMatchesText(this._evaluator._cacheText, element, matcher);\n if (matches === "none")\n lastDidNotMatchSelf = element;\n if (matches === "self" || matches === "selfAndChildren" && kind === "strict" && !internal)\n result.push(element);\n };\n if (root.nodeType === Node.ELEMENT_NODE)\n appendElement(root);\n const elements = this._evaluator._queryCSS({ scope: root, pierceShadow: shadow }, "*");\n for (const element of elements)\n appendElement(element);\n return result;\n };\n return { queryAll };\n }\n _createInternalHasTextEngine() {\n return {\n queryAll: (root, selector) => {\n if (root.nodeType !== 1)\n return [];\n const element = root;\n const text = elementText(this._evaluator._cacheText, element);\n const { matcher } = createTextMatcher(selector, true);\n return matcher(text) ? [element] : [];\n }\n };\n }\n _createInternalHasNotTextEngine() {\n return {\n queryAll: (root, selector) => {\n if (root.nodeType !== 1)\n return [];\n const element = root;\n const text = elementText(this._evaluator._cacheText, element);\n const { matcher } = createTextMatcher(selector, true);\n return matcher(text) ? [] : [element];\n }\n };\n }\n _createInternalLabelEngine() {\n return {\n queryAll: (root, selector) => {\n const { matcher } = createTextMatcher(selector, true);\n const allElements = this._evaluator._queryCSS({ scope: root, pierceShadow: true }, "*");\n return allElements.filter((element) => {\n return getElementLabels(this._evaluator._cacheText, element).some((label) => matcher(label));\n });\n }\n };\n }\n _createNamedAttributeEngine() {\n const queryAll = (root, selector) => {\n const parsed = parseAttributeSelector(selector, true);\n if (parsed.name || parsed.attributes.length !== 1)\n throw new Error("Malformed attribute selector: " + selector);\n const { name } = parsed.attributes[0];\n const matcher = createAttributeMatcher(parsed.attributes[0]);\n const elements = this._evaluator._queryCSS({ scope: root, pierceShadow: true }, `[${name}]`);\n return elements.filter((e) => matcher(e.getAttribute(name)));\n };\n return { queryAll };\n }\n _createTestIdEngine() {\n const queryAll = (root, selector) => {\n const parsed = parseAttributeSelector(selector, true);\n if (parsed.name || parsed.attributes.length !== 1)\n throw new Error("Malformed test id selector: " + selector);\n const names = splitTestIdAttributeNames(parsed.attributes[0].name);\n const matcher = createAttributeMatcher(parsed.attributes[0]);\n const cssQuery = names.map((n) => `[${n}]`).join(",");\n const elements = this._evaluator._queryCSS({ scope: root, pierceShadow: true }, cssQuery);\n return elements.filter((e) => names.some((n) => {\n const actual = e.getAttribute(n);\n return actual !== null && matcher(actual);\n }));\n };\n return { queryAll };\n }\n _createDescribeEngine() {\n const queryAll = (root) => {\n if (root.nodeType !== 1)\n return [];\n return [root];\n };\n return { queryAll };\n }\n _createControlEngine() {\n return {\n queryAll(root, body) {\n if (body === "enter-frame")\n return [];\n if (body === "return-empty")\n return [];\n if (body === "component") {\n if (root.nodeType !== 1)\n return [];\n return [root.childElementCount === 1 ? root.firstElementChild : root];\n }\n throw new Error(`Internal error, unknown internal:control selector ${body}`);\n }\n };\n }\n _createHasEngine() {\n const queryAll = (root, body) => {\n if (root.nodeType !== 1)\n return [];\n const has = !!this.querySelector(body.parsed, root, false);\n return has ? [root] : [];\n };\n return { queryAll };\n }\n _createHasNotEngine() {\n const queryAll = (root, body) => {\n if (root.nodeType !== 1)\n return [];\n const has = !!this.querySelector(body.parsed, root, false);\n return has ? [] : [root];\n };\n return { queryAll };\n }\n _createVisibleEngine() {\n const queryAll = (root, body) => {\n if (root.nodeType !== 1)\n return [];\n const visible = body === "true";\n return isElementVisible(root) === visible ? [root] : [];\n };\n return { queryAll };\n }\n _createInternalChainEngine() {\n const queryAll = (root, body) => {\n return this.querySelectorAll(body.parsed, root);\n };\n return { queryAll };\n }\n extend(source, params) {\n const constrFunction = this.window.eval(`\n (() => {\n const module = {};\n ${source}\n return module.exports.default();\n })()`);\n return new constrFunction(this, params);\n }\n async viewportRatio(element) {\n return await new Promise((resolve) => {\n const observer = new IntersectionObserver((entries) => {\n resolve(entries[0].intersectionRatio);\n observer.disconnect();\n });\n observer.observe(element);\n this.utils.builtins.requestAnimationFrame(() => {\n });\n });\n }\n getElementBorderWidth(node) {\n if (node.nodeType !== Node.ELEMENT_NODE || !node.ownerDocument || !node.ownerDocument.defaultView)\n return { left: 0, top: 0 };\n const style = node.ownerDocument.defaultView.getComputedStyle(node);\n return { left: parseInt(style.borderLeftWidth || "", 10), top: parseInt(style.borderTopWidth || "", 10) };\n }\n describeIFrameStyle(iframe) {\n if (!iframe.ownerDocument || !iframe.ownerDocument.defaultView)\n return "error:notconnected";\n const defaultView = iframe.ownerDocument.defaultView;\n for (let e = iframe; e; e = parentElementOrShadowHost(e)) {\n if (defaultView.getComputedStyle(e).transform !== "none")\n return "transformed";\n }\n const iframeStyle = defaultView.getComputedStyle(iframe);\n return {\n left: parseInt(iframeStyle.borderLeftWidth || "", 10) + parseInt(iframeStyle.paddingLeft || "", 10),\n top: parseInt(iframeStyle.borderTopWidth || "", 10) + parseInt(iframeStyle.paddingTop || "", 10)\n };\n }\n retarget(node, behavior) {\n let element = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;\n if (!element)\n return null;\n if (behavior === "none")\n return element;\n if (!element.matches("input, textarea, select") && !element.isContentEditable) {\n if (behavior === "button-link")\n element = element.closest("button, [role=button], a, [role=link]") || element;\n else\n element = element.closest("button, [role=button], [role=checkbox], [role=radio]") || element;\n }\n if (behavior === "follow-label") {\n if (!element.matches("a, input, textarea, button, select, [role=link], [role=button], [role=checkbox], [role=radio]") && !element.isContentEditable) {\n const enclosingLabel = element.closest("label");\n if (enclosingLabel && enclosingLabel.control)\n element = enclosingLabel.control;\n }\n }\n return element;\n }\n async checkElementStates(node, states) {\n if (states.includes("stable")) {\n const stableResult = await this._checkElementIsStable(node);\n if (stableResult === false)\n return { missingState: "stable" };\n if (stableResult === "error:notconnected")\n return "error:notconnected";\n }\n for (const state of states) {\n if (state !== "stable") {\n const result = this.elementState(node, state);\n if (result.received === "error:notconnected")\n return "error:notconnected";\n if (!result.matches)\n return { missingState: state };\n }\n }\n }\n async _checkElementIsStable(node) {\n const continuePolling = Symbol("continuePolling");\n let lastRect;\n let stableRafCounter = 0;\n let lastTime = 0;\n const check = () => {\n const element = this.retarget(node, "no-follow-label");\n if (!element)\n return "error:notconnected";\n const time = this.utils.builtins.performance.now();\n if (this._stableRafCount > 1 && time - lastTime < 15)\n return continuePolling;\n lastTime = time;\n const clientRect = element.getBoundingClientRect();\n const rect = { x: clientRect.top, y: clientRect.left, width: clientRect.width, height: clientRect.height };\n if (lastRect) {\n const samePosition = rect.x === lastRect.x && rect.y === lastRect.y && rect.width === lastRect.width && rect.height === lastRect.height;\n if (!samePosition)\n return false;\n if (++stableRafCounter >= this._stableRafCount)\n return true;\n }\n lastRect = rect;\n return continuePolling;\n };\n let fulfill;\n let reject;\n const result = new Promise((f, r) => {\n fulfill = f;\n reject = r;\n });\n const raf = () => {\n try {\n const success = check();\n if (success !== continuePolling)\n fulfill(success);\n else\n this.utils.builtins.requestAnimationFrame(raf);\n } catch (e) {\n reject(e);\n }\n };\n this.utils.builtins.requestAnimationFrame(raf);\n return result;\n }\n _createAriaRefEngine() {\n const queryAll = (root, selector) => {\n var _a, _b;\n const result = (_b = (_a = this._lastAriaSnapshotForQuery) == null ? void 0 : _a.elements) == null ? void 0 : _b.get(selector);\n return result && result.isConnected ? [result] : [];\n };\n return { queryAll };\n }\n elementState(node, state) {\n const element = this.retarget(node, ["visible", "hidden"].includes(state) ? "none" : "follow-label");\n if (!element || !element.isConnected) {\n if (state === "hidden")\n return { matches: true, received: "hidden" };\n return { matches: false, received: "error:notconnected" };\n }\n if (state === "visible" || state === "hidden") {\n const visible = isElementVisible(element);\n return {\n matches: state === "visible" ? visible : !visible,\n received: visible ? "visible" : "hidden"\n };\n }\n if (state === "disabled" || state === "enabled") {\n const disabled = getAriaDisabled(element);\n return {\n matches: state === "disabled" ? disabled : !disabled,\n received: disabled ? "disabled" : "enabled"\n };\n }\n if (state === "editable") {\n const disabled = getAriaDisabled(element);\n const readonly = getReadonly(element);\n if (readonly === "error")\n throw this.createStacklessError("Element is not an , , or [contenteditable] and does not have a role allowing [aria-readonly]");\n return {\n matches: !disabled && !readonly,\n received: disabled ? "disabled" : readonly ? "readOnly" : "editable"\n };\n }\n if (state === "checked" || state === "unchecked") {\n const need = state === "checked";\n const checked = getCheckedWithoutMixed(element);\n if (checked === "error")\n throw this.createStacklessError("Not a checkbox or radio button");\n const isRadio = element.nodeName === "INPUT" && element.type === "radio";\n return {\n matches: need === checked,\n received: checked ? "checked" : "unchecked",\n isRadio\n };\n }\n if (state === "indeterminate") {\n const checked = getCheckedAllowMixed(element);\n if (checked === "error")\n throw this.createStacklessError("Not a checkbox or radio button");\n return {\n matches: checked === "mixed",\n received: checked === true ? "checked" : checked === false ? "unchecked" : "mixed"\n };\n }\n throw this.createStacklessError(`Unexpected element state "${state}"`);\n }\n selectOptions(node, optionsToSelect) {\n const element = this.retarget(node, "follow-label");\n if (!element)\n return "error:notconnected";\n if (element.nodeName.toLowerCase() !== "select")\n throw this.createStacklessError("Element is not a element");\n const select = element;\n const options = [...select.options];\n const selectedOptions = [];\n let remainingOptionsToSelect = optionsToSelect.slice();\n for (let index = 0; index < options.length; index++) {\n const option = options[index];\n const normalizedOptionLabel = normalizeWhiteSpace(option.label);\n const filter = (optionToSelect) => {\n if (optionToSelect instanceof Node)\n return option === optionToSelect;\n const matchesLabel = (label) => label === option.label || normalizeWhiteSpace(label) === normalizedOptionLabel;\n let matches = true;\n if (optionToSelect.valueOrLabel !== void 0)\n matches = matches && (optionToSelect.valueOrLabel === option.value || matchesLabel(optionToSelect.valueOrLabel));\n if (optionToSelect.value !== void 0)\n matches = matches && optionToSelect.value === option.value;\n if (optionToSelect.label !== void 0)\n matches = matches && matchesLabel(optionToSelect.label);\n if (optionToSelect.index !== void 0)\n matches = matches && optionToSelect.index === index;\n return matches;\n };\n if (!remainingOptionsToSelect.some(filter))\n continue;\n if (!this.elementState(option, "enabled").matches)\n return "error:optionnotenabled";\n selectedOptions.push(option);\n if (select.multiple) {\n remainingOptionsToSelect = remainingOptionsToSelect.filter((o) => !filter(o));\n } else {\n remainingOptionsToSelect = [];\n break;\n }\n }\n if (remainingOptionsToSelect.length)\n return "error:optionsnotfound";\n select.value = void 0;\n selectedOptions.forEach((option) => option.selected = true);\n select.dispatchEvent(new Event("input", { bubbles: true, composed: true }));\n select.dispatchEvent(new Event("change", { bubbles: true }));\n return selectedOptions.map((option) => option.value);\n }\n fill(node, value) {\n const element = this.retarget(node, "follow-label");\n if (!element)\n return "error:notconnected";\n if (element.nodeName.toLowerCase() === "input") {\n const input = element;\n const type = input.type.toLowerCase();\n const kInputTypesToSetValue = /* @__PURE__ */ new Set(["color", "date", "time", "datetime-local", "month", "range", "week"]);\n const kInputTypesToTypeInto = /* @__PURE__ */ new Set(["", "email", "number", "password", "search", "tel", "text", "url"]);\n if (!kInputTypesToTypeInto.has(type) && !kInputTypesToSetValue.has(type))\n throw this.createStacklessError(`Input of type "${type}" cannot be filled`);\n if (type === "number") {\n value = value.trim();\n if (isNaN(Number(value)))\n throw this.createStacklessError("Cannot type text into input[type=number]");\n }\n if (type === "color")\n value = value.toLowerCase();\n if (kInputTypesToSetValue.has(type)) {\n value = value.trim();\n input.focus();\n input.value = value;\n if (input.value !== value)\n throw this.createStacklessError("Malformed value");\n element.dispatchEvent(new Event("input", { bubbles: true, composed: true }));\n element.dispatchEvent(new Event("change", { bubbles: true }));\n return "done";\n }\n } else if (element.nodeName.toLowerCase() === "textarea") {\n } else if (!element.isContentEditable) {\n throw this.createStacklessError("Element is not an , or [contenteditable] element");\n }\n this.selectText(element);\n return "needsinput";\n }\n selectText(node) {\n const element = this.retarget(node, "follow-label");\n if (!element)\n return "error:notconnected";\n if (element.nodeName.toLowerCase() === "input") {\n const input = element;\n input.select();\n input.focus();\n return "done";\n }\n if (element.nodeName.toLowerCase() === "textarea") {\n const textarea = element;\n textarea.selectionStart = 0;\n textarea.selectionEnd = textarea.value.length;\n textarea.focus();\n return "done";\n }\n element.focus();\n const range = element.ownerDocument.createRange();\n range.selectNodeContents(element);\n const selection = element.ownerDocument.defaultView.getSelection();\n if (selection) {\n selection.removeAllRanges();\n selection.addRange(range);\n }\n return "done";\n }\n _activelyFocused(node) {\n const activeElement = node.getRootNode().activeElement;\n const isFocused = activeElement === node && !!node.ownerDocument && node.ownerDocument.hasFocus();\n return { activeElement, isFocused };\n }\n focusNode(node, resetSelectionIfNotFocused) {\n if (!node.isConnected)\n return "error:notconnected";\n if (node.nodeType !== Node.ELEMENT_NODE)\n throw this.createStacklessError("Node is not an element");\n const { activeElement, isFocused: wasFocused } = this._activelyFocused(node);\n if (node.isContentEditable && !wasFocused && activeElement && activeElement.blur) {\n activeElement.blur();\n }\n node.focus();\n node.focus();\n if (resetSelectionIfNotFocused && !wasFocused && node.nodeName.toLowerCase() === "input") {\n try {\n const input = node;\n input.setSelectionRange(0, 0);\n } catch (e) {\n }\n }\n return "done";\n }\n blurNode(node) {\n if (!node.isConnected)\n return "error:notconnected";\n if (node.nodeType !== Node.ELEMENT_NODE)\n throw this.createStacklessError("Node is not an element");\n node.blur();\n return "done";\n }\n setInputFiles(node, payloads) {\n if (node.nodeType !== Node.ELEMENT_NODE)\n return "Node is not of type HTMLElement";\n const element = node;\n if (element.nodeName !== "INPUT")\n return "Not an element";\n const input = element;\n const type = (input.getAttribute("type") || "").toLowerCase();\n if (type !== "file")\n return "Not an input[type=file] element";\n const files = payloads.map((file) => {\n const bytes = Uint8Array.from(atob(file.buffer), (c) => c.charCodeAt(0));\n return new File([bytes], file.name, { type: file.mimeType, lastModified: file.lastModifiedMs });\n });\n const dt = new DataTransfer();\n for (const file of files)\n dt.items.add(file);\n input.files = dt.files;\n input.dispatchEvent(new Event("input", { bubbles: true, composed: true }));\n input.dispatchEvent(new Event("change", { bubbles: true }));\n }\n expectHitTarget(hitPoint, targetElement) {\n var _a;\n const roots = [];\n let parentElement = targetElement;\n while (parentElement) {\n const root = enclosingShadowRootOrDocument(parentElement);\n if (!root)\n break;\n roots.push(root);\n if (root.nodeType === 9)\n break;\n parentElement = root.host;\n }\n let hitElement;\n for (let index = roots.length - 1; index >= 0; index--) {\n const root = roots[index];\n const elements = root.elementsFromPoint(hitPoint.x, hitPoint.y);\n const singleElement = root.elementFromPoint(hitPoint.x, hitPoint.y);\n if (singleElement && elements[0] && parentElementOrShadowHost(singleElement) === elements[0]) {\n const style = this.window.getComputedStyle(singleElement);\n if ((style == null ? void 0 : style.display) === "contents") {\n elements.unshift(singleElement);\n }\n }\n if (elements[0] && elements[0].shadowRoot === root && elements[1] === singleElement) {\n elements.shift();\n }\n const innerElement = elements[0];\n if (!innerElement)\n break;\n hitElement = innerElement;\n if (index && innerElement !== roots[index - 1].host)\n break;\n }\n const hitParents = [];\n while (hitElement && hitElement !== targetElement) {\n hitParents.push(hitElement);\n hitElement = (_a = hitElement.assignedSlot) != null ? _a : parentElementOrShadowHost(hitElement);\n }\n if (hitElement === targetElement)\n return "done";\n const hitTargetDescription = this.previewNode(hitParents[0] || this.document.documentElement);\n let rootHitTargetDescription;\n let element = targetElement;\n while (element) {\n const index = hitParents.indexOf(element);\n if (index !== -1) {\n if (index > 1)\n rootHitTargetDescription = this.previewNode(hitParents[index - 1]);\n break;\n }\n element = parentElementOrShadowHost(element);\n }\n if (rootHitTargetDescription)\n return { hitTargetDescription: `${hitTargetDescription} from ${rootHitTargetDescription} subtree` };\n return { hitTargetDescription };\n }\n // Life of a pointer action, for example click.\n //\n // 0. Retry items 1 and 2 while action fails due to navigation or element being detached.\n // 1. Resolve selector to an element.\n // 2. Retry the following steps until the element is detached or frame navigates away.\n // 2a. Wait for the element to be stable (not moving), visible and enabled.\n // 2b. Scroll element into view. Scrolling alternates between:\n // - Built-in protocol scrolling.\n // - Anchoring to the top/left, bottom/right and center/center.\n // This is to scroll elements from under sticky headers/footers.\n // 2c. Click point is calculated, either based on explicitly specified position,\n // or some visible point of the element based on protocol content quads.\n // 2d. Click point relative to page viewport is converted relative to the target iframe\n // for the next hit-point check.\n // 2e. (injected) Hit target at the click point must be a descendant of the target element.\n // This prevents mis-clicking in edge cases like overlaying the target.\n // 2f. (injected) Events specific for click (or some other action type) are intercepted on\n // the Window with capture:true. See 2i for details.\n // Note: this step is skipped for drag&drop (see inline comments for the reason).\n // 2g. Necessary keyboard modifiers are pressed.\n // 2h. Click event is issued (mousemove + mousedown + mouseup).\n // 2i. (injected) For each event, we check that hit target at the event point\n // is a descendant of the target element.\n // This guarantees no race between issuing the event and handling it in the page,\n // for example due to layout shift.\n // When hit target check fails, we block all future events in the page.\n // 2j. Keyboard modifiers are restored.\n // 2k. (injected) Event interceptor is removed.\n // 2l. All navigations triggered between 2g-2k are awaited to be either committed or canceled.\n // 2m. If failed, wait for increasing amount of time before the next retry.\n setupHitTargetInterceptor(node, action, hitPoint, blockAllEvents) {\n const element = this.retarget(node, "button-link");\n if (!element || !element.isConnected)\n return "error:notconnected";\n if (hitPoint) {\n const preliminaryResult = this.expectHitTarget(hitPoint, element);\n if (preliminaryResult !== "done")\n return preliminaryResult.hitTargetDescription;\n }\n if (action === "drag")\n return { stop: () => "done" };\n const events = {\n "hover": this._hoverHitTargetInterceptorEvents,\n "tap": this._tapHitTargetInterceptorEvents,\n "mouse": this._mouseHitTargetInterceptorEvents\n }[action];\n let result;\n const listener = (event) => {\n if (!events.has(event.type))\n return;\n if (!event.isTrusted && !event.__pwTrustedSynthetic)\n return;\n const point = !!this.window.TouchEvent && event instanceof this.window.TouchEvent ? event.touches[0] : event;\n if (result === void 0 && point)\n result = this.expectHitTarget({ x: point.clientX, y: point.clientY }, element);\n if (blockAllEvents || result !== "done" && result !== void 0) {\n event.preventDefault();\n event.stopPropagation();\n event.stopImmediatePropagation();\n }\n };\n const stop = () => {\n if (this._hitTargetInterceptor === listener)\n this._hitTargetInterceptor = void 0;\n return result || "done";\n };\n this._hitTargetInterceptor = listener;\n return { stop };\n }\n dispatchEvent(node, type, eventInitObj) {\n var _a, _b, _c, _d, _e;\n let event;\n const eventInit = { bubbles: true, cancelable: true, composed: true, ...eventInitObj };\n switch (this._eventTypes.get(type)) {\n case "mouse":\n event = new MouseEvent(type, eventInit);\n break;\n case "keyboard":\n event = new KeyboardEvent(type, eventInit);\n break;\n case "touch": {\n if (this._browserName === "webkit") {\n const createTouch = (t) => {\n var _a2, _b2, _c2;\n if (t instanceof Touch)\n return t;\n let pageX = t.pageX;\n if (pageX === void 0 && t.clientX !== void 0)\n pageX = t.clientX + (((_a2 = this.document.scrollingElement) == null ? void 0 : _a2.scrollLeft) || 0);\n let pageY = t.pageY;\n if (pageY === void 0 && t.clientY !== void 0)\n pageY = t.clientY + (((_b2 = this.document.scrollingElement) == null ? void 0 : _b2.scrollTop) || 0);\n return this.document.createTouch(this.window, (_c2 = t.target) != null ? _c2 : node, t.identifier, pageX, pageY, t.screenX, t.screenY, t.radiusX, t.radiusY, t.rotationAngle, t.force);\n };\n const createTouchList = (touches) => {\n if (touches instanceof TouchList || !touches)\n return touches;\n return this.document.createTouchList(...touches.map(createTouch));\n };\n (_a = eventInit.target) != null ? _a : eventInit.target = node;\n eventInit.touches = createTouchList(eventInit.touches);\n eventInit.targetTouches = createTouchList(eventInit.targetTouches);\n eventInit.changedTouches = createTouchList(eventInit.changedTouches);\n event = new TouchEvent(type, eventInit);\n } else {\n (_b = eventInit.target) != null ? _b : eventInit.target = node;\n eventInit.touches = (_c = eventInit.touches) == null ? void 0 : _c.map((t) => {\n var _a2;\n return t instanceof Touch ? t : new Touch({ ...t, target: (_a2 = t.target) != null ? _a2 : node });\n });\n eventInit.targetTouches = (_d = eventInit.targetTouches) == null ? void 0 : _d.map((t) => {\n var _a2;\n return t instanceof Touch ? t : new Touch({ ...t, target: (_a2 = t.target) != null ? _a2 : node });\n });\n eventInit.changedTouches = (_e = eventInit.changedTouches) == null ? void 0 : _e.map((t) => {\n var _a2;\n return t instanceof Touch ? t : new Touch({ ...t, target: (_a2 = t.target) != null ? _a2 : node });\n });\n event = new TouchEvent(type, eventInit);\n }\n break;\n }\n case "pointer":\n event = new PointerEvent(type, eventInit);\n break;\n case "focus":\n event = new FocusEvent(type, eventInit);\n break;\n case "drag":\n event = new DragEvent(type, eventInit);\n break;\n case "wheel":\n event = new WheelEvent(type, eventInit);\n break;\n case "deviceorientation":\n try {\n event = new DeviceOrientationEvent(type, eventInit);\n } catch {\n const { bubbles, cancelable, alpha, beta, gamma, absolute } = eventInit;\n event = this.document.createEvent("DeviceOrientationEvent");\n event.initDeviceOrientationEvent(type, bubbles, cancelable, alpha, beta, gamma, absolute);\n }\n break;\n case "devicemotion":\n try {\n event = new DeviceMotionEvent(type, eventInit);\n } catch {\n const { bubbles, cancelable, acceleration, accelerationIncludingGravity, rotationRate, interval } = eventInit;\n event = this.document.createEvent("DeviceMotionEvent");\n event.initDeviceMotionEvent(type, bubbles, cancelable, acceleration, accelerationIncludingGravity, rotationRate, interval);\n }\n break;\n default:\n event = new Event(type, eventInit);\n break;\n }\n node.dispatchEvent(event);\n }\n previewNode(node) {\n if (node.nodeType === Node.TEXT_NODE)\n return oneLine(`#text=${node.nodeValue || ""}`);\n if (node.nodeType !== Node.ELEMENT_NODE)\n return oneLine(`<${node.nodeName.toLowerCase()} />`);\n const element = node;\n const attrs = [];\n for (let i = 0; i < element.attributes.length; i++) {\n const { name, value } = element.attributes[i];\n if (name === "style")\n continue;\n if (!value && this._booleanAttributes.has(name))\n attrs.push(` ${name}`);\n else\n attrs.push(` ${name}="${value}"`);\n }\n attrs.sort((a, b) => a.length - b.length);\n const attrText = trimStringWithEllipsis(attrs.join(""), 500);\n if (this._autoClosingTags.has(element.nodeName))\n return oneLine(`<${element.nodeName.toLowerCase()}${attrText}/>`);\n const children = element.childNodes;\n let onlyText = false;\n if (children.length <= 5) {\n onlyText = true;\n for (let i = 0; i < children.length; i++)\n onlyText = onlyText && children[i].nodeType === Node.TEXT_NODE;\n }\n const text = onlyText ? element.textContent || "" : children.length ? "\\u2026" : "";\n return oneLine(`<${element.nodeName.toLowerCase()}${attrText}>${trimStringWithEllipsis(text, 50)}${element.nodeName.toLowerCase()}>`);\n }\n _generateSelectors(elements) {\n this._evaluator.begin();\n beginAriaCaches();\n beginDOMCaches();\n try {\n const maxElements = this._isUtilityWorld && this._browserName === "firefox" ? 2 : 10;\n const infos = elements.slice(0, maxElements).map((m) => ({\n preview: this.previewNode(m),\n selector: this.generateSelectorSimple(m)\n }));\n return infos.map((info, i) => `${i + 1}) ${info.preview} aka ${asLocator(this._sdkLanguage, info.selector)}`);\n } finally {\n endDOMCaches();\n endAriaCaches();\n this._evaluator.end();\n }\n }\n strictModeViolationError(selector, matches) {\n const lines = this._generateSelectors(matches).map((line) => `\n ` + line);\n if (lines.length < matches.length)\n lines.push("\\n ...");\n return this.createStacklessError(`strict mode violation: ${asLocator(this._sdkLanguage, stringifySelector(selector))} resolved to ${matches.length} elements:${lines.join("")}\n`);\n }\n checkDeprecatedSelectorUsage(selector, matches) {\n const kDeprecatedSelectors = /* @__PURE__ */ new Set([\n "_react",\n "_vue",\n "xpath:light",\n "text:light",\n "id:light",\n "data-testid:light",\n "data-test-id:light",\n "data-test:light"\n ]);\n if (!matches.length)\n return;\n const deperecated = selector.parts.find((part) => kDeprecatedSelectors.has(part.name));\n if (!deperecated)\n return;\n const lines = this._generateSelectors(matches).map((line) => `\n ` + line);\n if (lines.length < matches.length)\n lines.push("\\n ...");\n throw this.createStacklessError(`"${deperecated.name}" selector is not supported: ${asLocator(this._sdkLanguage, stringifySelector(selector))} resolved to ${matches.length} element${matches.length === 1 ? "" : "s"}:${lines.join("")}\n`);\n }\n createStacklessError(message) {\n const error = this._shouldPrependErrorPrefix ? new Error("Error: " + message) : new Error(message);\n if (this._browserName === "firefox") {\n error.stack = "";\n return error;\n }\n delete error.stack;\n return error;\n }\n createHighlight() {\n return new Highlight(this);\n }\n maskSelectors(selectors, color) {\n const highlight = this._createHighlight();\n const elements = [];\n for (const selector of selectors)\n elements.push(this.querySelectorAll(selector, this.document.documentElement));\n highlight.maskElements(elements.flat(), color);\n }\n _createHighlight() {\n if (this._highlight)\n this.hideHighlight();\n this._highlight = new Highlight(this);\n this._highlight.install();\n return this._highlight;\n }\n _ensureHighlight() {\n if (!this._highlight) {\n this._highlight = new Highlight(this);\n this._highlight.install();\n }\n return this._highlight;\n }\n addHighlight(selector, style) {\n const highlight = this._ensureHighlight();\n highlight.addElementHighlight(selector, style);\n }\n removeHighlight(selector) {\n const highlight = this._ensureHighlight();\n highlight.removeElementHighlight(selector);\n }\n setScreencastAnnotation(annotation) {\n var _a;\n const highlight = this._ensureHighlight();\n if (!annotation) {\n highlight.updateHighlight([]);\n highlight.hideActionPoint();\n highlight.hideActionTitle();\n highlight.hideActionCursor();\n return;\n }\n const fadeDuration = (_a = annotation.duration) != null ? _a : 500;\n if (annotation.box) {\n highlight.updateHighlight([{\n box: annotation.box,\n color: "rgba(0, 128, 255, 0.15)",\n borderColor: "rgba(0, 128, 255, 0.6)",\n fadeDuration\n }]);\n }\n if (annotation.point) {\n if (annotation.cursor !== "none")\n highlight.moveActionCursor(annotation.point.x, annotation.point.y, fadeDuration);\n highlight.showActionPoint(annotation.point.x, annotation.point.y, fadeDuration);\n }\n if (annotation.actionTitle)\n highlight.showActionTitle(annotation.actionTitle, fadeDuration, annotation.position, annotation.fontSize);\n }\n addUserOverlay(id, html) {\n const highlight = this._ensureHighlight();\n highlight.addUserOverlay(id, html);\n }\n getUserOverlay(id) {\n const highlight = this._ensureHighlight();\n return highlight.getUserOverlay(id);\n }\n removeUserOverlay(id) {\n const highlight = this._ensureHighlight();\n highlight.removeUserOverlay(id);\n }\n setUserOverlaysVisible(visible) {\n const highlight = this._ensureHighlight();\n highlight.setUserOverlaysVisible(visible);\n }\n hideHighlight() {\n if (this._highlight) {\n this._highlight.uninstall();\n delete this._highlight;\n }\n }\n markTargetElements(markedElements) {\n const resetEvent = new CustomEvent("__playwright_reset_targets__", {\n bubbles: true,\n cancelable: true,\n composed: true\n });\n this.document.dispatchEvent(resetEvent);\n const markEvent = new CustomEvent("__playwright_mark_target__", {\n bubbles: true,\n cancelable: true,\n composed: true\n });\n for (const element of markedElements)\n element.dispatchEvent(markEvent);\n }\n _setupGlobalListenersRemovalDetection() {\n const customEventName = "__playwright_global_listeners_check__";\n let seenEvent = false;\n const handleCustomEvent = () => seenEvent = true;\n this.window.addEventListener(customEventName, handleCustomEvent);\n new MutationObserver((entries) => {\n const newDocumentElement = entries.some((entry) => Array.from(entry.addedNodes).includes(this.document.documentElement));\n if (!newDocumentElement)\n return;\n seenEvent = false;\n this.window.dispatchEvent(new CustomEvent(customEventName));\n if (seenEvent)\n return;\n this.window.addEventListener(customEventName, handleCustomEvent);\n for (const callback of this.onGlobalListenersRemoved)\n callback();\n }).observe(this.document, { childList: true });\n }\n _setupHitTargetInterceptors() {\n const listener = (event) => {\n var _a;\n return (_a = this._hitTargetInterceptor) == null ? void 0 : _a.call(this, event);\n };\n const addHitTargetInterceptorListeners = () => {\n for (const event of this._allHitTargetInterceptorEvents)\n this.window.addEventListener(event, listener, { capture: true, passive: false });\n };\n addHitTargetInterceptorListeners();\n this.onGlobalListenersRemoved.add(addHitTargetInterceptorListeners);\n }\n async expect(element, options, elements) {\n const core = await this._expectCore(element, options, elements);\n const ariaSnapshot = core.matches !== options.isNot ? void 0 : this._ariaSnapshotForExpect(element, options);\n if (core.received === void 0 && ariaSnapshot === void 0)\n return { matches: core.matches, missingReceived: core.missingReceived };\n return { matches: core.matches, received: { value: core.received, ariaSnapshot }, missingReceived: core.missingReceived };\n }\n _ariaSnapshotForExpect(element, options) {\n const expression = options.expression;\n if (expression === "to.have.count" || expression.endsWith(".array"))\n return void 0;\n if (expression === "to.match.aria")\n return void 0;\n if (element && isElementVisible(element)) {\n const isContainment = expression === "to.have.text";\n return this._renderAriaSnapshot(element, { mode: "default", depth: isContainment ? void 0 : 1 });\n }\n if (!this.document.body)\n return void 0;\n return this._renderAriaSnapshot(this.document.body, { mode: "default" });\n }\n _renderAriaSnapshot(element, options) {\n return renderAriaTree(generateAriaTree(element, options), options).text;\n }\n async _expectCore(element, options, elements) {\n var _a, _b;\n const isArray = options.expression === "to.have.count" || options.expression.endsWith(".array");\n if (isArray)\n return this.expectArray(elements, options);\n if (!element) {\n if (!options.isNot && options.expression === "to.be.hidden")\n return { matches: true };\n if (options.isNot && options.expression === "to.be.visible")\n return { matches: false };\n if (!options.isNot && options.expression === "to.be.detached")\n return { matches: true };\n if (options.isNot && options.expression === "to.be.attached")\n return { matches: false };\n if (options.isNot && options.expression === "to.be.in.viewport")\n return { matches: false };\n if (options.expression === "to.have.title" && ((_a = options == null ? void 0 : options.expectedText) == null ? void 0 : _a[0])) {\n const matcher = new ExpectedTextMatcher(options.expectedText[0]);\n const received = this.document.title;\n return { received, matches: matcher.matches(received) };\n }\n if (options.expression === "to.have.url" && ((_b = options == null ? void 0 : options.expectedText) == null ? void 0 : _b[0])) {\n const matcher = new ExpectedTextMatcher(options.expectedText[0]);\n const received = this.document.location.href;\n return { received, matches: matcher.matches(received) };\n }\n if (options.expression === "to.match.aria" && !options.selector) {\n if (!this.document.body)\n return { matches: options.isNot, missingReceived: true };\n const result = matchesExpectAriaTemplate(this.document.body, options.expectedValue);\n return {\n received: result.received,\n matches: !!result.matches.length\n };\n }\n return { matches: options.isNot, missingReceived: true };\n }\n return await this.expectSingleElement(element, options);\n }\n async expectSingleElement(element, options) {\n var _a, _b;\n const expression = options.expression;\n {\n let result;\n if (expression === "to.have.attribute") {\n const hasAttribute = element.hasAttribute(options.expressionArg);\n result = {\n matches: hasAttribute,\n received: hasAttribute ? "attribute present" : "attribute not present"\n };\n } else if (expression === "to.be.checked") {\n const { checked, indeterminate } = options.expectedValue;\n if (indeterminate) {\n if (checked !== void 0)\n throw this.createStacklessError("Can\'t assert indeterminate and checked at the same time");\n result = this.elementState(element, "indeterminate");\n } else {\n result = this.elementState(element, checked === false ? "unchecked" : "checked");\n }\n } else if (expression === "to.be.disabled") {\n result = this.elementState(element, "disabled");\n } else if (expression === "to.be.editable") {\n result = this.elementState(element, "editable");\n } else if (expression === "to.be.readonly") {\n result = this.elementState(element, "editable");\n result.matches = !result.matches;\n } else if (expression === "to.be.empty") {\n if (element.nodeName === "INPUT" || element.nodeName === "TEXTAREA") {\n const value = element.value;\n result = { matches: !value, received: value ? "notEmpty" : "empty" };\n } else {\n const text = (_a = element.textContent) == null ? void 0 : _a.trim();\n result = { matches: !text, received: text ? "notEmpty" : "empty" };\n }\n } else if (expression === "to.be.enabled") {\n result = this.elementState(element, "enabled");\n } else if (expression === "to.be.focused") {\n const focused = this._activelyFocused(element).isFocused;\n result = {\n matches: focused,\n received: focused ? "focused" : "inactive"\n };\n } else if (expression === "to.be.hidden") {\n result = this.elementState(element, "hidden");\n } else if (expression === "to.be.visible") {\n result = this.elementState(element, "visible");\n } else if (expression === "to.be.attached") {\n result = {\n matches: true,\n received: "attached"\n };\n } else if (expression === "to.be.detached") {\n result = {\n matches: false,\n received: "attached"\n };\n }\n if (result) {\n if (result.received === "error:notconnected")\n throw this.createStacklessError("Element is not connected");\n return result;\n }\n }\n {\n if (expression === "to.have.property") {\n let target = element;\n const properties = options.expressionArg.split(".");\n for (let i = 0; i < properties.length - 1; i++) {\n if (typeof target !== "object" || !(properties[i] in target))\n return { received: void 0, matches: false };\n target = target[properties[i]];\n }\n const received = target[properties[properties.length - 1]];\n const matches = deepEquals(received, options.expectedValue);\n return { received, matches };\n }\n }\n {\n if (expression === "to.be.in.viewport") {\n const ratio = await this.viewportRatio(element);\n return { received: `viewport ratio ${ratio}`, matches: ratio > 0 && ratio > ((_b = options.expectedNumber) != null ? _b : 0) - 1e-9 };\n }\n }\n {\n if (expression === "to.have.values") {\n element = this.retarget(element, "follow-label");\n if (element.nodeName !== "SELECT" || !element.multiple)\n throw this.createStacklessError("Not a select element with a multiple attribute");\n const received = [...element.selectedOptions].map((o) => o.value);\n if (received.length !== options.expectedText.length)\n return { received, matches: false };\n return { received, matches: received.map((r, i) => new ExpectedTextMatcher(options.expectedText[i]).matches(r)).every(Boolean) };\n }\n }\n {\n if (expression === "to.match.aria") {\n const result = matchesExpectAriaTemplate(element, options.expectedValue);\n return {\n received: result.received,\n matches: !!result.matches.length\n };\n }\n }\n {\n let received;\n if (expression === "to.have.attribute.value") {\n const value = element.getAttribute(options.expressionArg);\n if (value === null)\n return { received: null, matches: false };\n received = value;\n } else if (["to.have.class", "to.contain.class"].includes(expression)) {\n if (!options.expectedText)\n throw this.createStacklessError("Expected text is not provided for " + expression);\n return {\n received: element.classList.toString(),\n matches: new ExpectedTextMatcher(options.expectedText[0]).matchesClassList(\n this,\n element.classList,\n /* partial */\n expression === "to.contain.class"\n )\n };\n } else if (expression === "to.have.css") {\n received = this.window.getComputedStyle(element, options.pseudo ? `::${options.pseudo}` : void 0).getPropertyValue(options.expressionArg);\n } else if (expression === "to.have.id") {\n received = element.id;\n } else if (expression === "to.have.text") {\n received = options.useInnerText ? element.innerText : elementText(/* @__PURE__ */ new Map(), element).full;\n } else if (expression === "to.have.accessible.name") {\n received = getElementAccessibleName(\n element,\n false\n /* includeHidden */\n );\n } else if (expression === "to.have.accessible.description") {\n received = getElementAccessibleDescription(\n element,\n false\n /* includeHidden */\n );\n } else if (expression === "to.have.accessible.error.message") {\n received = getElementAccessibleErrorMessage(element);\n } else if (expression === "to.have.role") {\n received = getAriaRole(element) || "";\n } else if (expression === "to.have.value") {\n element = this.retarget(element, "follow-label");\n if (element.nodeName !== "INPUT" && element.nodeName !== "TEXTAREA" && element.nodeName !== "SELECT")\n throw this.createStacklessError("Not an input element");\n received = element.value;\n }\n if (received !== void 0 && options.expectedText) {\n const matcher = new ExpectedTextMatcher(options.expectedText[0]);\n return { received, matches: matcher.matches(received) };\n }\n }\n throw this.createStacklessError("Unknown expect matcher: " + expression);\n }\n expectArray(elements, options) {\n const expression = options.expression;\n if (expression === "to.have.count") {\n const received2 = elements.length;\n const matches2 = received2 === options.expectedNumber;\n return { received: received2, matches: matches2 };\n }\n if (!options.expectedText)\n throw this.createStacklessError("Expected text is not provided for " + expression);\n if (["to.have.class.array", "to.contain.class.array"].includes(expression)) {\n const receivedClassLists = elements.map((e) => e.classList);\n const received2 = receivedClassLists.map(String);\n if (receivedClassLists.length !== options.expectedText.length)\n return { received: received2, matches: false };\n const matches2 = this._matchSequentially(\n options.expectedText,\n receivedClassLists,\n (matcher, r) => matcher.matchesClassList(\n this,\n r,\n /* partial */\n expression === "to.contain.class.array"\n )\n );\n return {\n received: received2,\n matches: matches2\n };\n }\n if (!["to.contain.text.array", "to.have.text.array"].includes(expression))\n throw this.createStacklessError("Unknown expect matcher: " + expression);\n const received = elements.map((e) => options.useInnerText ? e.innerText : elementText(/* @__PURE__ */ new Map(), e).full);\n const lengthShouldMatch = expression !== "to.contain.text.array";\n const matchesLength = received.length === options.expectedText.length || !lengthShouldMatch;\n if (!matchesLength)\n return { received, matches: false };\n const matches = this._matchSequentially(options.expectedText, received, (matcher, r) => matcher.matches(r));\n return { received, matches };\n }\n _matchSequentially(expectedText, received, matchFn) {\n const matchers = expectedText.map((e) => new ExpectedTextMatcher(e));\n let mIndex = 0;\n let rIndex = 0;\n while (mIndex < matchers.length && rIndex < received.length) {\n if (matchFn(matchers[mIndex], received[rIndex]))\n ++mIndex;\n ++rIndex;\n }\n return mIndex === matchers.length;\n }\n};\nfunction oneLine(s) {\n return s.replace(/\\n/g, "\\u21B5").replace(/\\t/g, "\\u21C6");\n}\nfunction createAttributeMatcher(part) {\n const { value, caseSensitive } = part;\n if (value instanceof RegExp)\n return (s) => !!s.match(value);\n if (caseSensitive)\n return (s) => s === value;\n const lowerCaseValue = value.toLowerCase();\n return (s) => s.toLowerCase().includes(lowerCaseValue);\n}\nfunction cssUnquote(s) {\n s = s.substring(1, s.length - 1);\n if (!s.includes("\\\\"))\n return s;\n const r = [];\n let i = 0;\n while (i < s.length) {\n if (s[i] === "\\\\" && i + 1 < s.length)\n i++;\n r.push(s[i++]);\n }\n return r.join("");\n}\nfunction createTextMatcher(selector, internal) {\n if (selector[0] === "/" && selector.lastIndexOf("/") > 0) {\n const lastSlash = selector.lastIndexOf("/");\n const re = new RegExp(selector.substring(1, lastSlash), selector.substring(lastSlash + 1));\n return { matcher: (elementText2) => re.test(elementText2.full), kind: "regex" };\n }\n const unquote = internal ? JSON.parse.bind(JSON) : cssUnquote;\n let strict = false;\n if (selector.length > 1 && selector[0] === \'"\' && selector[selector.length - 1] === \'"\') {\n selector = unquote(selector);\n strict = true;\n } else if (internal && selector.length > 1 && selector[0] === \'"\' && selector[selector.length - 2] === \'"\' && selector[selector.length - 1] === "i") {\n selector = unquote(selector.substring(0, selector.length - 1));\n strict = false;\n } else if (internal && selector.length > 1 && selector[0] === \'"\' && selector[selector.length - 2] === \'"\' && selector[selector.length - 1] === "s") {\n selector = unquote(selector.substring(0, selector.length - 1));\n strict = true;\n } else if (selector.length > 1 && selector[0] === "\'" && selector[selector.length - 1] === "\'") {\n selector = unquote(selector);\n strict = true;\n }\n selector = normalizeWhiteSpace(selector);\n if (strict) {\n if (internal)\n return { kind: "strict", matcher: (elementText2) => elementText2.normalized === selector };\n const strictTextNodeMatcher = (elementText2) => {\n if (!selector && !elementText2.immediate.length)\n return true;\n return elementText2.immediate.some((s) => normalizeWhiteSpace(s) === selector);\n };\n return { matcher: strictTextNodeMatcher, kind: "strict" };\n }\n selector = selector.toLowerCase();\n return { kind: "lax", matcher: (elementText2) => elementText2.normalized.toLowerCase().includes(selector) };\n}\nvar ExpectedTextMatcher = class {\n constructor(expected) {\n this._normalizeWhiteSpace = expected.normalizeWhiteSpace;\n this._ignoreCase = expected.ignoreCase;\n this._string = expected.matchSubstring ? void 0 : this.normalize(expected.string);\n this._substring = expected.matchSubstring ? this.normalize(expected.string) : void 0;\n if (expected.regexSource) {\n const flags = new Set((expected.regexFlags || "").split(""));\n if (expected.ignoreCase === false)\n flags.delete("i");\n if (expected.ignoreCase === true)\n flags.add("i");\n this._regex = new RegExp(expected.regexSource, [...flags].join(""));\n }\n }\n matches(text) {\n if (!this._regex)\n text = this.normalize(text);\n if (this._string !== void 0)\n return text === this._string;\n if (this._substring !== void 0)\n return text.includes(this._substring);\n if (this._regex)\n return !!this._regex.test(text);\n return false;\n }\n matchesClassList(injectedScript, classList, partial) {\n if (partial) {\n if (this._regex)\n throw injectedScript.createStacklessError("Partial matching does not support regular expressions. Please provide a string value.");\n return this._string.split(/\\s+/g).filter(Boolean).every((className) => classList.contains(className));\n }\n return this.matches(classList.toString());\n }\n normalize(s) {\n if (!s)\n return s;\n if (this._normalizeWhiteSpace)\n s = normalizeWhiteSpace(s);\n if (this._ignoreCase)\n s = s.toLocaleLowerCase();\n return s;\n }\n};\nfunction deepEquals(a, b) {\n if (a === b)\n return true;\n if (a && b && typeof a === "object" && typeof b === "object") {\n if (a.constructor !== b.constructor)\n return false;\n if (Array.isArray(a)) {\n if (a.length !== b.length)\n return false;\n for (let i = 0; i < a.length; ++i) {\n if (!deepEquals(a[i], b[i]))\n return false;\n }\n return true;\n }\n if (a instanceof RegExp)\n return a.source === b.source && a.flags === b.flags;\n if (a.valueOf !== Object.prototype.valueOf)\n return a.valueOf() === b.valueOf();\n if (a.toString !== Object.prototype.toString)\n return a.toString() === b.toString();\n const keys = Object.keys(a);\n if (keys.length !== Object.keys(b).length)\n return false;\n for (let i = 0; i < keys.length; ++i) {\n if (!b.hasOwnProperty(keys[i]))\n return false;\n }\n for (const key of keys) {\n if (!deepEquals(a[key], b[key]))\n return false;\n }\n return true;\n }\n if (typeof a === "number" && typeof b === "number")\n return isNaN(a) && isNaN(b);\n return false;\n}\n'; + } +}); + +// packages/playwright-core/src/server/dom.ts +function isNonRecoverableDOMError(error) { + return error instanceof NonRecoverableDOMError; +} +function throwRetargetableDOMError(result2) { + if (result2 === "error:notconnected") + throwElementIsNotAttached(); + return result2; +} +function throwElementIsNotAttached() { + throw new Error("Element is not attached to the DOM"); +} +function assertDone(result2) { +} +function roundPoint(point) { + return { + x: (point.x * 100 | 0) / 100, + y: (point.y * 100 | 0) / 100 + }; +} +function quadToRect(quad) { + let minX = Infinity; + let minY = Infinity; + let maxX = -Infinity; + let maxY = -Infinity; + for (const point of quad) { + minX = Math.min(minX, point.x); + minY = Math.min(minY, point.y); + maxX = Math.max(maxX, point.x); + maxY = Math.max(maxY, point.y); + } + return { x: minX, y: minY, width: maxX - minX, height: maxY - minY }; +} +function quadMiddlePoint(quad) { + const result2 = { x: 0, y: 0 }; + for (const point of quad) { + result2.x += point.x / 4; + result2.y += point.y / 4; + } + return result2; +} +function triangleArea(p1, p2, p3) { + return Math.abs(p1.x * (p2.y - p3.y) + p2.x * (p3.y - p1.y) + p3.x * (p1.y - p2.y)) / 2; +} +function isPointInsideQuad(point, quad) { + const area1 = triangleArea(point, quad[0], quad[1]) + triangleArea(point, quad[1], quad[2]) + triangleArea(point, quad[2], quad[3]) + triangleArea(point, quad[3], quad[0]); + const area2 = triangleArea(quad[0], quad[1], quad[2]) + triangleArea(quad[1], quad[2], quad[3]); + if (Math.abs(area1 - area2) > 0.1) + return false; + return point.x < Math.max(quad[0].x, quad[1].x, quad[2].x, quad[3].x) && point.y < Math.max(quad[0].y, quad[1].y, quad[2].y, quad[3].y); +} +function findIntegerPointInsideQuad(quad) { + const point = quadMiddlePoint(quad); + point.x = Math.floor(point.x); + point.y = Math.floor(point.y); + if (isPointInsideQuad(point, quad)) + return point; + point.x += 1; + if (isPointInsideQuad(point, quad)) + return point; + point.y += 1; + if (isPointInsideQuad(point, quad)) + return point; + point.x -= 1; + if (isPointInsideQuad(point, quad)) + return point; +} +var import_fs14, import_path13, mime5, NonRecoverableDOMError, FrameExecutionContext, ElementHandle, kUnableToAdoptErrorMessage; +var init_dom = __esm({ + "packages/playwright-core/src/server/dom.ts"() { + "use strict"; + import_fs14 = __toESM(require("fs")); + import_path13 = __toESM(require("path")); + init_debug(); + init_javascript(); + init_fileUploadUtils(); + init_injectedScriptSource(); + mime5 = require("./utilsBundle").mime; + NonRecoverableDOMError = class extends Error { + }; + FrameExecutionContext = class extends ExecutionContext { + constructor(delegate, frame, world) { + super(frame, delegate, world || "content-script", { noUtilityWorld: frame._page.delegate.noUtilityWorld?.() }); + this.frame = frame; + this.world = world; + } + adoptIfNeeded(handle) { + if (handle instanceof ElementHandle && handle._context !== this) + return this.frame._page.delegate.adoptElementHandle(handle, this); + return null; + } + async evaluate(pageFunction, arg) { + return evaluate(this, true, pageFunction, arg); + } + async evaluateHandle(pageFunction, arg) { + return evaluate(this, false, pageFunction, arg); + } + async evaluateExpression(expression2, options, arg) { + return evaluateExpression(this, expression2, { ...options, returnByValue: true }, arg); + } + async evaluateExpressionHandle(expression2, options, arg) { + return evaluateExpression(this, expression2, { ...options, returnByValue: false }, arg); + } + injectedScript() { + if (!this._injectedScriptPromise) { + const customEngines = []; + const selectorsRegistry = this.frame._page.browserContext.selectors(); + for (const [name, { source: source12 }] of selectorsRegistry._engines) + customEngines.push({ name, source: `(${source12})` }); + const sdkLanguage = this.frame._page.browserContext._browser.sdkLanguage(); + const options = { + isUnderTest: isUnderTest(), + sdkLanguage, + testIdAttributeName: selectorsRegistry.testIdAttributeName(), + stableRafCount: this.frame._page.delegate.rafCountForStablePosition(), + browserName: this.frame._page.browserContext._browser.options.name, + shouldPrependErrorPrefix: this.delegate.shouldPrependErrorPrefix(), + isUtilityWorld: this.world === "utility", + customEngines + }; + const globalsSnapshot = this.frame._page.delegate.noUtilityWorld?.() ? mainWorldGlobalsSnapshotSource : ""; + const source11 = ` + (() => { + ${globalsSnapshot} + const module = {}; + ${source4} + return new (module.exports.InjectedScript())(globalThis, ${JSON.stringify(options)}); + })(); + `; + this._injectedScriptPromise = this.rawEvaluateHandle(source11).then((handle) => { + handle._setPreview("InjectedScript"); + return handle; + }); + } + return this._injectedScriptPromise; + } + }; + ElementHandle = class extends JSHandle { + constructor(context2, objectId) { + super(context2, "node", void 0, objectId); + this.__elementhandle = true; + this._page = context2.frame._page; + this._frame = context2.frame; + this._initializePreview().catch((e) => { + }); + } + async _initializePreview() { + const utility = await this._context.injectedScript(); + this._setPreview(await utility.evaluate((injected, e) => "JSHandle@" + injected.previewNode(e), this)); + } + asElement() { + return this; + } + async evaluateInUtility(pageFunction, arg) { + try { + const utility = await this._frame.utilityContext(); + return await utility.evaluate(pageFunction, [await utility.injectedScript(), this, arg]); + } catch (e) { + if (this._frame.isNonRetriableError(e)) + throw e; + return "error:notconnected"; + } + } + async _evaluateHandleInUtility(pageFunction, arg) { + try { + const utility = await this._frame.utilityContext(); + return await utility.evaluateHandle(pageFunction, [await utility.injectedScript(), this, arg]); + } catch (e) { + if (this._frame.isNonRetriableError(e)) + throw e; + return "error:notconnected"; + } + } + async ownerFrame(progress2) { + return await progress2.race(this._ownerFrame()); + } + async _ownerFrame() { + const frameId = await this._page.delegate.getOwnerFrame(this); + if (!frameId) + return null; + const frame = this._page.frameManager.frame(frameId); + if (frame) + return frame; + for (const page of this._page.browserContext.pages()) { + const frame2 = page.frameManager.frame(frameId); + if (frame2) + return frame2; + } + return null; + } + async _isIframeElement() { + return this.evaluateInUtility(([injected, node]) => node && (node.nodeName === "IFRAME" || node.nodeName === "FRAME"), {}); + } + async contentFrame(progress2) { + return progress2.race(this._contentFrame()); + } + async _contentFrame() { + const isFrameElement = throwRetargetableDOMError(await this._isIframeElement()); + if (!isFrameElement) + return null; + return this._page.delegate.getContentFrame(this); + } + async getAttribute(progress2, name) { + return this._frame.getAttribute(progress2, ":scope", name, {}, this); + } + async inputValue(progress2) { + return this._frame.inputValue(progress2, ":scope", {}, this); + } + async textContent(progress2) { + return this._frame.textContent(progress2, ":scope", {}, this); + } + async innerText(progress2) { + return this._frame.innerText(progress2, ":scope", {}, this); + } + async innerHTML(progress2) { + return this._frame.innerHTML(progress2, ":scope", {}, this); + } + async dispatchEvent(progress2, type3, eventInit = {}) { + return this._frame.dispatchEvent(progress2, ":scope", type3, eventInit, {}, this); + } + async _scrollRectIntoViewIfNeeded(progress2, rect) { + return await progress2.race(this._page.delegate.scrollRectIntoViewIfNeeded(this, rect)); + } + async _waitAndScrollIntoViewIfNeeded(progress2, waitForVisible) { + const result2 = await this._retryAction(progress2, "scroll into view", async (progress3) => { + progress3.log(` waiting for element to be stable`); + const waitResult = await progress3.race(this.evaluateInUtility(async ([injected, node, { waitForVisible: waitForVisible2 }]) => { + return await injected.checkElementStates(node, waitForVisible2 ? ["visible", "stable"] : ["stable"]); + }, { waitForVisible })); + if (waitResult) + return waitResult; + return await this._scrollRectIntoViewIfNeeded(progress3); + }, {}); + assertDone(throwRetargetableDOMError(result2)); + } + async scrollIntoViewIfNeeded(progress2) { + await this._waitAndScrollIntoViewIfNeeded( + progress2, + false + /* waitForVisible */ + ); + } + async _clickablePoint(progress2) { + const intersectQuadWithViewport = (quad2) => { + return quad2.map((point) => ({ + x: Math.min(Math.max(point.x, 0), metrics.width), + y: Math.min(Math.max(point.y, 0), metrics.height) + })); + }; + const computeQuadArea = (quad2) => { + let area = 0; + for (let i = 0; i < quad2.length; ++i) { + const p1 = quad2[i]; + const p2 = quad2[(i + 1) % quad2.length]; + area += (p1.x * p2.y - p2.x * p1.y) / 2; + } + return Math.abs(area); + }; + const [quads, metrics] = await progress2.race(Promise.all([ + this._page.delegate.getContentQuads(this), + this._page.mainFrame().utilityContext().then((utility) => utility.evaluate(() => ({ width: innerWidth, height: innerHeight }))) + ])); + if (quads === "error:notconnected") + return quads; + if (!quads || !quads.length) + return "error:notvisible"; + const filtered = quads.map((quad2) => intersectQuadWithViewport(quad2)).filter((quad2) => computeQuadArea(quad2) > 0.99); + if (!filtered.length) + return "error:notinviewport"; + const quad = filtered[0]; + const box = quadToRect(quad); + if (this._page.browserContext._browser.options.name === "firefox") { + for (const q of filtered) { + const integerPoint = findIntegerPointInsideQuad(q); + if (integerPoint) + return { point: integerPoint, box }; + } + } + return { point: quadMiddlePoint(quad), box }; + } + async _offsetPoint(progress2, offset) { + const [box, border] = await progress2.race(Promise.all([ + this.boundingBox(progress2), + this.evaluateInUtility(([injected, node]) => injected.getElementBorderWidth(node), {}).catch((e) => { + }) + ])); + if (!box || !border) + return "error:notvisible"; + if (border === "error:notconnected") + return border; + return { + point: { + x: box.x + border.left + offset.x, + y: box.y + border.top + offset.y + }, + box + }; + } + async _retryAction(progress2, actionName, action, options) { + let retry2 = 0; + const waitTime = [0, 20, 100, 100, 500]; + const noAutoWaiting = options.__testHookNoAutoWaiting ?? options.noAutoWaiting; + while (true) { + if (retry2) { + progress2.log(`retrying ${actionName} action${options.trial ? " (trial run)" : ""}`); + const timeout = waitTime[Math.min(retry2 - 1, waitTime.length - 1)]; + if (timeout) { + progress2.log(` waiting ${timeout}ms`); + const result3 = await progress2.race(this.evaluateInUtility(([injected, node, timeout2]) => new Promise((f) => setTimeout(f, timeout2)), timeout)); + if (result3 === "error:notconnected") + return result3; + } + } else { + progress2.log(`attempting ${actionName} action${options.trial ? " (trial run)" : ""}`); + } + if (!options.skipActionPreChecks && !options.force && !noAutoWaiting) + await this._frame._page.performActionPreChecks(progress2); + const result2 = await action(progress2, retry2); + ++retry2; + if (result2 === "error:notvisible") { + if (options.force || noAutoWaiting) + throw new NonRecoverableDOMError("Element is not visible"); + progress2.log(" element is not visible"); + continue; + } + if (result2 === "error:notinviewport") { + if (options.force || noAutoWaiting) + throw new NonRecoverableDOMError("Element is outside of the viewport"); + progress2.log(" element is outside of the viewport"); + continue; + } + if (result2 === "error:optionsnotfound") { + if (noAutoWaiting) + throw new NonRecoverableDOMError("Did not find some options"); + progress2.log(" did not find some options"); + continue; + } + if (result2 === "error:optionnotenabled") { + if (noAutoWaiting) + throw new NonRecoverableDOMError("Option being selected is not enabled"); + progress2.log(" option being selected is not enabled"); + continue; + } + if (typeof result2 === "object" && "hitTargetDescription" in result2) { + if (noAutoWaiting) + throw new NonRecoverableDOMError(`${result2.hitTargetDescription} intercepts pointer events`); + progress2.log(` ${result2.hitTargetDescription} intercepts pointer events`); + continue; + } + if (typeof result2 === "object" && "missingState" in result2) { + if (noAutoWaiting) + throw new NonRecoverableDOMError(`Element is not ${result2.missingState}`); + progress2.log(` element is not ${result2.missingState}`); + continue; + } + return result2; + } + } + async _retryPointerAction(progress2, actionName, waitForEnabled, action, options) { + const skipActionPreChecks = actionName === "move and up"; + return await this._retryAction(progress2, actionName, async (progress3, retry2) => { + const scrollOptions = [ + void 0, + { block: "end", inline: "end" }, + { block: "center", inline: "center" }, + { block: "start", inline: "start" } + ]; + const forceScrollOptions = scrollOptions[retry2 % scrollOptions.length]; + return await this._performPointerAction(progress3, actionName, waitForEnabled, action, forceScrollOptions, options); + }, { ...options, skipActionPreChecks }); + } + async _performPointerAction(progress2, actionName, waitForEnabled, action, forceScrollOptions, options) { + const { force = false, position } = options; + const doScrollIntoView = async (progress3) => { + if (forceScrollOptions) { + return await progress3.race(this.evaluateInUtility(([injected, node, options2]) => { + if (node.nodeType === 1) + node.scrollIntoView(options2); + return "done"; + }, forceScrollOptions)); + } + return await this._scrollRectIntoViewIfNeeded(progress3, position ? { x: position.x, y: position.y, width: 0, height: 0 } : void 0); + }; + if (this._frame.parentFrame()) { + await doScrollIntoView(progress2).catch(() => { + }); + } + if (options.__testHookBeforeStable) + await progress2.race(options.__testHookBeforeStable()); + if (!force) { + const elementStates = waitForEnabled ? ["visible", "enabled", "stable"] : ["visible", "stable"]; + progress2.log(` waiting for element to be ${waitForEnabled ? "visible, enabled and stable" : "visible and stable"}`); + const result2 = await progress2.race(this.evaluateInUtility(async ([injected, node, { elementStates: elementStates2 }]) => { + return await injected.checkElementStates(node, elementStates2); + }, { elementStates })); + if (result2) + return result2; + progress2.log(` element is ${waitForEnabled ? "visible, enabled and stable" : "visible and stable"}`); + } + if (options.__testHookAfterStable) + await progress2.race(options.__testHookAfterStable()); + progress2.log(" scrolling into view if needed"); + const scrolled = await doScrollIntoView(progress2); + if (scrolled !== "done") + return scrolled; + progress2.log(" done scrolling"); + const maybeResult = position ? await this._offsetPoint(progress2, position) : await this._clickablePoint(progress2); + if (typeof maybeResult === "string") + return maybeResult; + const point = roundPoint(maybeResult.point); + await progress2.race(this.instrumentation.onBeforeInputAction(this, progress2.metadata, point, maybeResult.box)); + let hitTargetInterceptionHandle; + if (force) { + progress2.log(` forcing action`); + } else { + if (options.__testHookBeforeHitTarget) + await progress2.race(options.__testHookBeforeHitTarget()); + const frameCheckResult = await this._checkFrameIsHitTarget(progress2, point); + if (frameCheckResult === "error:notconnected" || "hitTargetDescription" in frameCheckResult) + return frameCheckResult; + const hitPoint = frameCheckResult.framePoint; + const actionType = actionName === "move and up" ? "drag" : actionName === "hover" || actionName === "tap" ? actionName : "mouse"; + const handle = await progress2.race(this._evaluateHandleInUtility(([injected, node, { actionType: actionType2, hitPoint: hitPoint2, trial }]) => injected.setupHitTargetInterceptor(node, actionType2, hitPoint2, trial), { actionType, hitPoint, trial: !!options.trial })); + if (handle === "error:notconnected") + return handle; + if (!handle._objectId) { + const error = handle.rawValue(); + if (error === "error:notconnected") + return error; + return { hitTargetDescription: error }; + } + hitTargetInterceptionHandle = handle; + } + const actionResult = await this._page.frameManager.waitForSignalsCreatedBy(progress2, options.waitAfter === true, async (progress3) => { + if (options.__testHookBeforePointerAction) + await progress3.race(options.__testHookBeforePointerAction()); + let restoreModifiers; + if (options && options.modifiers) + restoreModifiers = await this._page.keyboard.ensureModifiers(progress3, options.modifiers); + progress3.log(` performing ${actionName} action`); + await action(progress3, point); + if (restoreModifiers) + await this._page.keyboard.ensureModifiers(progress3, restoreModifiers); + if (hitTargetInterceptionHandle) { + const stopHitTargetInterception = this._frame.raceAgainstEvaluationStallingEvents(() => { + return hitTargetInterceptionHandle.evaluate((h) => h.stop()); + }).catch((e) => "done").finally(() => { + hitTargetInterceptionHandle?.dispose(); + }); + if (options.waitAfter !== false) { + const hitTargetResult = await progress3.race(stopHitTargetInterception); + if (hitTargetResult !== "done") + return hitTargetResult; + } + } + progress3.log(` ${options.trial ? "trial " : ""}${actionName} action done`); + progress3.log(" waiting for scheduled navigations to finish"); + if (options.__testHookAfterPointerAction) + await progress3.race(options.__testHookAfterPointerAction()); + return "done"; + }).finally(() => { + const stopPromise = hitTargetInterceptionHandle?.evaluate((h) => h.stop()).catch(() => { + }); + stopPromise?.then(() => hitTargetInterceptionHandle?.dispose()); + }); + if (actionResult !== "done") + return actionResult; + progress2.log(" navigations have finished"); + return "done"; + } + async _markAsTargetElement(progress2) { + await progress2.race(this.evaluateInUtility(([injected, node]) => { + if (node.nodeType === 1) + injected.markTargetElements(/* @__PURE__ */ new Set([node])); + }, {})); + } + async hover(progress2, options) { + await this._markAsTargetElement(progress2); + const result2 = await this._hover(progress2, options); + return assertDone(throwRetargetableDOMError(result2)); + } + _hover(progress2, options) { + return this._retryPointerAction(progress2, "hover", false, (progress3, point) => this._page.mouse.move(progress3, point.x, point.y), { ...options, waitAfter: "disabled" }); + } + async click(progress2, options) { + await this._markAsTargetElement(progress2); + const result2 = await this._click(progress2, { ...options, waitAfter: !options.noWaitAfter }); + return assertDone(throwRetargetableDOMError(result2)); + } + _click(progress2, options) { + return this._retryPointerAction(progress2, "click", true, (progress3, point) => this._page.mouse.click(progress3, point.x, point.y, options), options); + } + async dblclick(progress2, options) { + await this._markAsTargetElement(progress2); + const result2 = await this._dblclick(progress2, options); + return assertDone(throwRetargetableDOMError(result2)); + } + _dblclick(progress2, options) { + return this._retryPointerAction(progress2, "dblclick", true, (progress3, point) => this._page.mouse.click(progress3, point.x, point.y, { ...options, clickCount: 2 }), { ...options, waitAfter: "disabled" }); + } + async tap(progress2, options) { + await this._markAsTargetElement(progress2); + const result2 = await this._tap(progress2, options); + return assertDone(throwRetargetableDOMError(result2)); + } + _tap(progress2, options) { + return this._retryPointerAction(progress2, "tap", true, (progress3, point) => this._page.touchscreen.tap(progress3, point.x, point.y), { ...options, waitAfter: "disabled" }); + } + async selectOption(progress2, elements, values, options) { + await this._markAsTargetElement(progress2); + const result2 = await this._selectOption(progress2, elements, values, options); + return throwRetargetableDOMError(result2); + } + async _selectOption(progress2, elements, values, options) { + let resultingOptions = []; + const result2 = await this._retryAction(progress2, "select option", async (progress3) => { + await progress3.race(this.instrumentation.onBeforeInputAction(this, progress3.metadata)); + if (!options.force) + progress3.log(` waiting for element to be visible and enabled`); + const optionsToSelect = [...elements, ...values]; + const result3 = await progress3.race(this.evaluateInUtility(async ([injected, node, { optionsToSelect: optionsToSelect2, force }]) => { + if (!force) { + const checkResult = await injected.checkElementStates(node, ["visible", "enabled"]); + if (checkResult) + return checkResult; + } + return injected.selectOptions(node, optionsToSelect2); + }, { optionsToSelect, force: options.force })); + if (Array.isArray(result3)) { + progress3.log(" selected specified option(s)"); + resultingOptions = result3; + return "done"; + } + return result3; + }, options); + if (result2 === "error:notconnected") + return result2; + return resultingOptions; + } + async fill(progress2, value2, options) { + await this._markAsTargetElement(progress2); + const result2 = await this._fill(progress2, value2, options); + assertDone(throwRetargetableDOMError(result2)); + } + async _fill(progress2, value2, options) { + progress2.log(` fill("${value2}")`); + return await this._retryAction(progress2, "fill", async (progress3) => { + await progress3.race(this.instrumentation.onBeforeInputAction(this, progress3.metadata)); + if (!options.force) + progress3.log(" waiting for element to be visible, enabled and editable"); + const result2 = await progress3.race(this.evaluateInUtility(async ([injected, node, { value: value3, force }]) => { + if (!force) { + const checkResult = await injected.checkElementStates(node, ["visible", "enabled", "editable"]); + if (checkResult) + return checkResult; + } + return injected.fill(node, value3); + }, { value: value2, force: options.force })); + if (result2 === "needsinput") { + if (value2) + await this._page.keyboard.insertText(progress3, value2); + else + await this._page.keyboard.press(progress3, "Delete"); + return "done"; + } else { + return result2; + } + }, options); + } + async selectText(progress2, options) { + const result2 = await this._retryAction(progress2, "selectText", async (progress3) => { + if (!options.force) + progress3.log(" waiting for element to be visible"); + return await progress3.race(this.evaluateInUtility(async ([injected, node, { force }]) => { + if (!force) { + const checkResult = await injected.checkElementStates(node, ["visible"]); + if (checkResult) + return checkResult; + } + return injected.selectText(node); + }, { force: options.force })); + }, options); + assertDone(throwRetargetableDOMError(result2)); + } + async setInputFiles(progress2, params2) { + const inputFileItems = await progress2.race(prepareFilesForUpload(this._frame, params2)); + await this._markAsTargetElement(progress2); + const result2 = await this._setInputFiles(progress2, inputFileItems); + return assertDone(throwRetargetableDOMError(result2)); + } + async _drop(progress2, inputFileItems, data, options) { + const { filePayloads, localPaths } = inputFileItems; + let payloads; + if (localPaths && !filePayloads) { + payloads = await Promise.all(localPaths.map(async (p) => ({ + name: import_path13.default.basename(p), + mimeType: mime5.getType(p) || "application/octet-stream", + buffer: (await import_fs14.default.promises.readFile(p)).toString("base64"), + lastModifiedMs: (await import_fs14.default.promises.stat(p)).mtimeMs + }))); + } else { + payloads = (filePayloads ?? []).map((p) => ({ + name: p.name, + mimeType: p.mimeType || "application/octet-stream", + buffer: p.buffer, + lastModifiedMs: p.lastModifiedMs + })); + } + return this._retryPointerAction(progress2, "drop", false, async (progress3, point) => { + const mainContext = await progress3.race(this._frame.mainContext()); + const handle = this._context === mainContext ? this : await progress3.race(this._page.delegate.adoptElementHandle(this, mainContext)); + const disposeHandle = handle !== this; + try { + const result2 = await progress3.race(handle.evaluate((node, { payloads: payloads2, data: data2, point: point2 }) => { + if (!node.isConnected || node.nodeType !== 1) + return "error:notconnected"; + const element2 = node; + const dt = new DataTransfer(); + for (const p of payloads2) { + const bytes = Uint8Array.from(atob(p.buffer), (c) => c.charCodeAt(0)); + const file = new File([bytes], p.name, { type: p.mimeType, lastModified: p.lastModifiedMs }); + dt.items.add(file); + } + for (const entry of data2) + dt.setData(entry.mimeType, entry.value); + const makeEvent = (type3) => new DragEvent(type3, { + bubbles: true, + cancelable: true, + composed: true, + clientX: point2.x, + clientY: point2.y, + dataTransfer: dt + }); + element2.dispatchEvent(makeEvent("dragenter")); + const over = makeEvent("dragover"); + element2.dispatchEvent(over); + if (!over.defaultPrevented) { + element2.dispatchEvent(makeEvent("dragleave")); + return "not-accepted"; + } + element2.dispatchEvent(makeEvent("drop")); + return "accepted"; + }, { payloads, data, point })); + if (result2 === "not-accepted") + throw new NonRecoverableDOMError("Drop target did not accept the drop \u2014 its dragover handler did not call preventDefault()"); + } finally { + if (disposeHandle) + handle.dispose(); + } + }, { ...options, waitAfter: "disabled" }); + } + async _setInputFiles(progress2, items) { + const { filePayloads, localPaths, localDirectory } = items; + const multiple = filePayloads && filePayloads.length > 1 || localPaths && localPaths.length > 1; + const result2 = await progress2.race(this._evaluateHandleInUtility(([injected, node, { multiple: multiple2, directoryUpload }]) => { + const element2 = injected.retarget(node, "follow-label"); + if (!element2) + return; + if (element2.tagName !== "INPUT") + throw injected.createStacklessError("Node is not an HTMLInputElement"); + const inputElement = element2; + if (multiple2 && !inputElement.multiple && !inputElement.webkitdirectory) + throw injected.createStacklessError("Non-multiple file input can only accept single file"); + if (directoryUpload && !inputElement.webkitdirectory) + throw injected.createStacklessError("File input does not support directories, pass individual files instead"); + if (!directoryUpload && inputElement.webkitdirectory) + throw injected.createStacklessError("[webkitdirectory] input requires passing a path to a directory"); + return inputElement; + }, { multiple, directoryUpload: !!localDirectory })); + if (result2 === "error:notconnected" || !result2.asElement()) + return "error:notconnected"; + const retargeted = result2.asElement(); + await progress2.race(this.instrumentation.onBeforeInputAction(this, progress2.metadata)); + if (localPaths || localDirectory) { + const localPathsOrDirectory = localDirectory ? [localDirectory] : localPaths; + await progress2.race(Promise.all(localPathsOrDirectory.map((localPath) => import_fs14.default.promises.access(localPath, import_fs14.default.constants.F_OK)))); + const waitForInputEvent = localDirectory ? this.evaluate((node) => new Promise((fulfill) => { + node.addEventListener("input", fulfill, { once: true }); + })).catch(() => { + }) : Promise.resolve(); + await this._page.delegate.setInputFilePaths(progress2, retargeted, localPathsOrDirectory); + await progress2.race(waitForInputEvent); + } else { + await progress2.race(retargeted.evaluateInUtility(([injected, node, files]) => injected.setInputFiles(node, files), filePayloads)); + } + return "done"; + } + async focus(progress2) { + await this._markAsTargetElement(progress2); + const result2 = await this._focus(progress2); + return assertDone(throwRetargetableDOMError(result2)); + } + async _focus(progress2, resetSelectionIfNotFocused) { + return await progress2.race(this.evaluateInUtility(([injected, node, resetSelectionIfNotFocused2]) => injected.focusNode(node, resetSelectionIfNotFocused2), resetSelectionIfNotFocused)); + } + async _blur(progress2) { + return await progress2.race(this.evaluateInUtility(([injected, node]) => injected.blurNode(node), {})); + } + async type(progress2, text2, options) { + await this._markAsTargetElement(progress2); + const result2 = await this._type(progress2, text2, options); + return assertDone(throwRetargetableDOMError(result2)); + } + async _type(progress2, text2, options) { + progress2.log(`elementHandle.type("${text2}")`); + await progress2.race(this.instrumentation.onBeforeInputAction(this, progress2.metadata)); + const result2 = await this._focus( + progress2, + true + /* resetSelectionIfNotFocused */ + ); + if (result2 !== "done") + return result2; + await this._page.keyboard.type(progress2, text2, options); + return "done"; + } + async press(progress2, key, options) { + await this._markAsTargetElement(progress2); + const result2 = await this._press(progress2, key, options); + return assertDone(throwRetargetableDOMError(result2)); + } + async _press(progress2, key, options) { + progress2.log(`elementHandle.press("${key}")`); + await progress2.race(this.instrumentation.onBeforeInputAction(this, progress2.metadata)); + return this._page.frameManager.waitForSignalsCreatedBy(progress2, !options.noWaitAfter, async (progress3) => { + const result2 = await this._focus( + progress3, + true + /* resetSelectionIfNotFocused */ + ); + if (result2 !== "done") + return result2; + await this._page.keyboard.press(progress3, key, options); + return "done"; + }); + } + async check(progress2, options) { + const result2 = await this._setChecked(progress2, true, options); + return assertDone(throwRetargetableDOMError(result2)); + } + async uncheck(progress2, options) { + const result2 = await this._setChecked(progress2, false, options); + return assertDone(throwRetargetableDOMError(result2)); + } + async _setChecked(progress2, state, options) { + const isChecked = async (progress3) => { + const result3 = await progress3.race(this.evaluateInUtility(([injected, node]) => injected.elementState(node, "checked"), {})); + if (result3 === "error:notconnected" || result3.received === "error:notconnected") + throwElementIsNotAttached(); + return { matches: result3.matches, isRadio: result3.isRadio }; + }; + await this._markAsTargetElement(progress2); + const checkedState = await isChecked(progress2); + if (checkedState.matches === state) + return "done"; + if (!state && checkedState.isRadio) + throw new NonRecoverableDOMError("Cannot uncheck radio button. Radio buttons can only be unchecked by selecting another radio button in the same group."); + const result2 = await this._click(progress2, { ...options, waitAfter: "disabled" }); + if (result2 !== "done") + return result2; + if (options.trial) + return "done"; + const finalState = await isChecked(progress2); + if (finalState.matches !== state) + throw new NonRecoverableDOMError("Clicking the checkbox did not change its state"); + return "done"; + } + async boundingBox(progress2) { + return await progress2.race(this._page.delegate.getBoundingBox(this)); + } + async screenshot(progress2, options) { + return await this._page.screenshotter.screenshotElement(progress2, this, options); + } + async querySelector(progress2, selector, options) { + return progress2.race(this._querySelector(selector, options)); + } + async _querySelector(selector, options) { + return this._frame.selectors.query(selector, options, this); + } + async querySelectorAll(progress2, selector) { + return progress2.race(this._querySelectorAll(selector)); + } + async _querySelectorAll(selector) { + return this._frame.selectors.queryAll(selector, this); + } + async evalOnSelector(progress2, selector, strict, expression2, isFunction2, arg) { + return this._frame.evalOnSelector(progress2, selector, strict, expression2, isFunction2, arg, this); + } + async evalOnSelectorAll(progress2, selector, expression2, isFunction2, arg) { + return this._frame.evalOnSelectorAll(progress2, selector, expression2, isFunction2, arg, this); + } + async isVisible(progress2) { + return this._frame.isVisible(progress2, ":scope", {}, this); + } + async isHidden(progress2) { + return this._frame.isHidden(progress2, ":scope", {}, this); + } + async isEnabled(progress2) { + return this._frame.isEnabled(progress2, ":scope", {}, this); + } + async isDisabled(progress2) { + return this._frame.isDisabled(progress2, ":scope", {}, this); + } + async isEditable(progress2) { + return this._frame.isEditable(progress2, ":scope", {}, this); + } + async isChecked(progress2) { + return this._frame.isChecked(progress2, ":scope", {}, this); + } + async waitForElementState(progress2, state) { + const actionName = `wait for ${state}`; + const result2 = await this._retryAction(progress2, actionName, async (progress3) => { + return await progress3.race(this.evaluateInUtility(async ([injected, node, state2]) => { + return await injected.checkElementStates(node, [state2]) || "done"; + }, state)); + }, {}); + assertDone(throwRetargetableDOMError(result2)); + } + async waitForSelector(progress2, selector, options) { + return await this._frame.waitForSelector(progress2, selector, true, options, this); + } + async _adoptTo(context2) { + if (this._context !== context2) { + const adopted = await this._page.delegate.adoptElementHandle(this, context2); + this.dispose(); + return adopted; + } + return this; + } + async _checkFrameIsHitTarget(progress2, point) { + let frame = this._frame; + const data = []; + while (frame.parentFrame()) { + const frameElement = await frame.frameElement(progress2); + const box = await frameElement.boundingBox(progress2); + const style = await progress2.race(frameElement.evaluateInUtility(([injected, iframe]) => injected.describeIFrameStyle(iframe), {}).catch((e) => "error:notconnected")); + if (!box || style === "error:notconnected") + return "error:notconnected"; + if (style === "transformed") { + return { framePoint: void 0 }; + } + const pointInFrame = { x: point.x - box.x - style.left, y: point.y - box.y - style.top }; + data.push({ frame, frameElement, pointInFrame }); + frame = frame.parentFrame(); + } + data.push({ frame, frameElement: null, pointInFrame: point }); + for (let i = data.length - 1; i > 0; i--) { + const element2 = data[i - 1].frameElement; + const point2 = data[i].pointInFrame; + const hitTargetResult = await progress2.race(element2.evaluateInUtility(([injected, element3, hitPoint]) => { + return injected.expectHitTarget(hitPoint, element3); + }, point2)); + if (hitTargetResult !== "done") + return hitTargetResult; + } + return { framePoint: data[0].pointInFrame }; + } + }; + kUnableToAdoptErrorMessage = "Unable to adopt element handle from a different document"; + } +}); + +// packages/playwright-core/src/server/frameSelectors.ts +async function adoptIfNeeded(handle, context2) { + if (handle._context === context2) + return handle; + const adopted = await handle._page.delegate.adoptElementHandle(handle, context2); + handle.dispose(); + return adopted; +} +var FrameSelectors; +var init_frameSelectors = __esm({ + "packages/playwright-core/src/server/frameSelectors.ts"() { + "use strict"; + init_selectorParser(); + init_locatorGenerators(); + FrameSelectors = class { + constructor(frame) { + this.frame = frame; + } + _parseSelector(selector, options) { + const strict = typeof options?.strict === "boolean" ? options.strict : !!this.frame._page.browserContext._options.strictSelectors; + return this.frame._page.browserContext.selectors().parseSelector(selector, strict); + } + async query(selector, options, scope) { + const resolved = await this.resolveInjectedForSelector(selector, options, scope); + if (!resolved) + return null; + const handle = await resolved.injected.evaluateHandle((injected, { info, scope: scope2 }) => { + return injected.querySelector(info.parsed, scope2 || document, info.strict); + }, { info: resolved.info, scope: resolved.scope }); + const elementHandle = handle.asElement(); + if (!elementHandle) { + handle.dispose(); + return null; + } + return adoptIfNeeded(elementHandle, await resolved.frame.mainContext()); + } + async queryArrayInMainWorld(selector, scope) { + const resolved = await this.resolveInjectedForSelector(selector, { mainWorld: true }, scope); + if (!resolved) + throw new Error(`Failed to find frame for selector "${selector}"`); + return await resolved.injected.evaluateHandle((injected, { info, scope: scope2 }) => { + const elements = injected.querySelectorAll(info.parsed, scope2 || document); + injected.checkDeprecatedSelectorUsage(info.parsed, elements); + return elements; + }, { info: resolved.info, scope: resolved.scope }); + } + async queryCount(selector, options) { + const resolved = await this.resolveInjectedForSelector(selector); + if (!resolved) + throw new Error(`Failed to find frame for selector "${selector}"`); + await options.__testHookBeforeQuery?.(); + return await resolved.injected.evaluate((injected, { info }) => { + const elements = injected.querySelectorAll(info.parsed, document); + injected.checkDeprecatedSelectorUsage(info.parsed, elements); + return elements.length; + }, { info: resolved.info }); + } + async queryAll(selector, scope) { + const resolved = await this.resolveInjectedForSelector(selector, {}, scope); + if (!resolved) + return []; + const arrayHandle = await resolved.injected.evaluateHandle((injected, { info, scope: scope2 }) => { + const elements = injected.querySelectorAll(info.parsed, scope2 || document); + injected.checkDeprecatedSelectorUsage(info.parsed, elements); + return elements; + }, { info: resolved.info, scope: resolved.scope }); + const properties = await arrayHandle.internalGetProperties(); + arrayHandle.dispose(); + const targetContext = await resolved.frame.mainContext(); + const result2 = []; + for (const property of properties.values()) { + const elementHandle = property.asElement(); + if (elementHandle) + result2.push(adoptIfNeeded(elementHandle, targetContext)); + else + property.dispose(); + } + return Promise.all(result2); + } + _jumpToAriaRefFrameIfNeeded(selector, info, frame) { + if (info.parsed.parts[0].name !== "aria-ref") + return frame; + const body = info.parsed.parts[0].body; + const match = body.match(/^f(\d+)e\d+$/); + if (!match) + return frame; + const frameSeq = +match[1]; + const jumptToFrame = this.frame._page.frameManager.frames().find((frame2) => frame2.seq === frameSeq); + if (!jumptToFrame) + throw new InvalidSelectorError(`Invalid frame in aria-ref selector "${selector}"`); + return jumptToFrame; + } + async resolveFrameForSelector(selector, options = {}, scope) { + let frame = this.frame; + const frameChunks = splitSelectorByFrame(selector); + for (const chunk of frameChunks) { + visitAllSelectorParts(chunk, (part, nested) => { + if (nested && part.name === "internal:control" && part.body === "enter-frame") { + const locator2 = asLocator(this.frame._page.browserContext._browser.sdkLanguage(), selector); + throw new InvalidSelectorError(`Frame locators are not allowed inside composite locators, while querying "${locator2}"`); + } + }); + } + for (let i = 0; i < frameChunks.length - 1; ++i) { + const info = this._parseSelector(frameChunks[i], options); + frame = this._jumpToAriaRefFrameIfNeeded(selector, info, frame); + const context2 = await frame.context(info.world); + const injectedScript = await context2.injectedScript(); + const handle = await injectedScript.evaluateHandle((injected, { info: info2, scope: scope2, selectorString }) => { + const element3 = injected.querySelector(info2.parsed, scope2 || document, info2.strict); + if (element3 && element3.nodeName !== "IFRAME" && element3.nodeName !== "FRAME") + throw injected.createStacklessError(`Selector "${selectorString}" resolved to ${injected.previewNode(element3)}, was expected`); + return element3; + }, { info, scope: i === 0 ? scope : void 0, selectorString: stringifySelector(info.parsed) }); + const element2 = handle.asElement(); + if (!element2) + return null; + const maybeFrame = await frame._page.delegate.getContentFrame(element2); + element2.dispose(); + if (!maybeFrame) + return null; + frame = maybeFrame; + } + if (frame !== this.frame) + scope = void 0; + const lastChunk = frame.selectors._parseSelector(frameChunks[frameChunks.length - 1], options); + frame = this._jumpToAriaRefFrameIfNeeded(selector, lastChunk, frame); + return { frame, info: lastChunk, scope }; + } + async resolveInjectedForSelector(selector, options, scope) { + const resolved = await this.resolveFrameForSelector(selector, options, scope); + if (!resolved) + return; + const context2 = await resolved.frame.context(options?.mainWorld ? "main" : resolved.info.world); + const injected = await context2.injectedScript(); + return { injected, info: resolved.info, frame: resolved.frame, scope: resolved.scope }; + } + }; + } +}); + +// packages/playwright-core/src/server/helper.ts +var MAX_LOG_LENGTH, Helper, helper; +var init_helper = __esm({ + "packages/playwright-core/src/server/helper.ts"() { + "use strict"; + init_debugLogger(); + init_eventsHelper(); + MAX_LOG_LENGTH = process.env.MAX_LOG_LENGTH ? +process.env.MAX_LOG_LENGTH : Infinity; + Helper = class { + static completeUserURL(urlString) { + if (urlString.startsWith("localhost") || urlString.startsWith("127.0.0.1")) + urlString = "http://" + urlString; + return urlString; + } + static enclosingIntRect(rect) { + const x = Math.floor(rect.x + 1e-3); + const y = Math.floor(rect.y + 1e-3); + const x2 = Math.ceil(rect.x + rect.width - 1e-3); + const y2 = Math.ceil(rect.y + rect.height - 1e-3); + return { x, y, width: x2 - x, height: y2 - y }; + } + static enclosingIntSize(size) { + return { width: Math.floor(size.width + 1e-3), height: Math.floor(size.height + 1e-3) }; + } + static getViewportSizeFromWindowFeatures(features) { + const widthString = features.find((f) => f.startsWith("width=")); + const heightString = features.find((f) => f.startsWith("height=")); + const width = widthString ? parseInt(widthString.substring(6), 10) : NaN; + const height = heightString ? parseInt(heightString.substring(7), 10) : NaN; + if (!Number.isNaN(width) && !Number.isNaN(height)) + return { width, height }; + return null; + } + static waitForEvent(progress2, emitter, event, predicate) { + const listeners = []; + const dispose = () => eventsHelper.removeEventListeners(listeners); + const promise = progress2.race(new Promise((resolve, reject) => { + listeners.push(eventsHelper.addEventListener(emitter, event, (eventArg) => { + try { + if (predicate && !predicate(eventArg)) + return; + resolve(eventArg); + } catch (e) { + reject(e); + } + })); + })).finally(() => dispose()); + return { promise, dispose }; + } + static secondsToRoundishMillis(value2) { + return (value2 * 1e6 | 0) / 1e3; + } + static millisToRoundishMillis(value2) { + return (value2 * 1e3 | 0) / 1e3; + } + static debugProtocolLogger(protocolLogger) { + return (direction, message) => { + if (protocolLogger) + protocolLogger(direction, message); + if (debugLogger.isEnabled("protocol")) { + let text2 = JSON.stringify(message); + if (text2.length > MAX_LOG_LENGTH) + text2 = text2.substring(0, MAX_LOG_LENGTH / 2) + " <<<<<( LOG TRUNCATED )>>>>> " + text2.substring(text2.length - MAX_LOG_LENGTH / 2); + debugLogger.log("protocol", (direction === "send" ? "SEND \u25BA " : "\u25C0 RECV ") + text2); + } + }; + } + static formatBrowserLogs(logs, disconnectReason) { + if (!disconnectReason && !logs.length) + return ""; + return "\n" + (disconnectReason ? disconnectReason + "\n" : "") + logs.join("\n"); + } + }; + helper = Helper; + } +}); + +// packages/playwright-core/src/server/disposable.ts +async function disposeAll2(disposables) { + const copy = [...disposables]; + disposables.length = 0; + await Promise.all(copy.map((d) => d.dispose())); +} +var DisposableObject; +var init_disposable2 = __esm({ + "packages/playwright-core/src/server/disposable.ts"() { + "use strict"; + init_instrumentation(); + DisposableObject = class extends SdkObject { + constructor(parent) { + super(parent, "disposable"); + this.parent = parent; + } + }; + } +}); + +// packages/playwright-core/src/server/console.ts +var ConsoleMessage; +var init_console = __esm({ + "packages/playwright-core/src/server/console.ts"() { + "use strict"; + ConsoleMessage = class { + constructor(page, worker, type3, text2, args, location2, timestamp) { + this._page = page; + this._worker = worker; + this._type = type3; + this._text = text2; + this._args = args; + this._location = location2 || { url: "", lineNumber: 0, columnNumber: 0 }; + this._timestamp = timestamp; + } + page() { + return this._page; + } + worker() { + return this._worker; + } + type() { + return this._type; + } + text() { + if (this._text === void 0) + this._text = this._args.map((arg) => arg.preview()).join(" "); + return this._text; + } + args() { + return this._args; + } + location() { + return this._location; + } + timestamp() { + return this._timestamp; + } + }; + } +}); + +// packages/playwright-core/src/server/fileChooser.ts +var FileChooser; +var init_fileChooser = __esm({ + "packages/playwright-core/src/server/fileChooser.ts"() { + "use strict"; + FileChooser = class { + constructor(elementHandle, isMultiple) { + this._elementHandle = elementHandle; + this._isMultiple = isMultiple; + } + element() { + return this._elementHandle; + } + isMultiple() { + return this._isMultiple; + } + }; + } +}); + +// packages/playwright-core/src/server/usKeyboardLayout.ts +var keypadLocation, USKeyboardLayout; +var init_usKeyboardLayout = __esm({ + "packages/playwright-core/src/server/usKeyboardLayout.ts"() { + "use strict"; + keypadLocation = 3; + USKeyboardLayout = { + // Functions row + "Escape": { "keyCode": 27, "key": "Escape" }, + "F1": { "keyCode": 112, "key": "F1" }, + "F2": { "keyCode": 113, "key": "F2" }, + "F3": { "keyCode": 114, "key": "F3" }, + "F4": { "keyCode": 115, "key": "F4" }, + "F5": { "keyCode": 116, "key": "F5" }, + "F6": { "keyCode": 117, "key": "F6" }, + "F7": { "keyCode": 118, "key": "F7" }, + "F8": { "keyCode": 119, "key": "F8" }, + "F9": { "keyCode": 120, "key": "F9" }, + "F10": { "keyCode": 121, "key": "F10" }, + "F11": { "keyCode": 122, "key": "F11" }, + "F12": { "keyCode": 123, "key": "F12" }, + // Numbers row + "Backquote": { "keyCode": 192, "shiftKey": "~", "key": "`" }, + "Digit1": { "keyCode": 49, "shiftKey": "!", "key": "1" }, + "Digit2": { "keyCode": 50, "shiftKey": "@", "key": "2" }, + "Digit3": { "keyCode": 51, "shiftKey": "#", "key": "3" }, + "Digit4": { "keyCode": 52, "shiftKey": "$", "key": "4" }, + "Digit5": { "keyCode": 53, "shiftKey": "%", "key": "5" }, + "Digit6": { "keyCode": 54, "shiftKey": "^", "key": "6" }, + "Digit7": { "keyCode": 55, "shiftKey": "&", "key": "7" }, + "Digit8": { "keyCode": 56, "shiftKey": "*", "key": "8" }, + "Digit9": { "keyCode": 57, "shiftKey": "(", "key": "9" }, + "Digit0": { "keyCode": 48, "shiftKey": ")", "key": "0" }, + "Minus": { "keyCode": 189, "shiftKey": "_", "key": "-" }, + "Equal": { "keyCode": 187, "shiftKey": "+", "key": "=" }, + "Backslash": { "keyCode": 220, "shiftKey": "|", "key": "\\" }, + "Backspace": { "keyCode": 8, "key": "Backspace" }, + // First row + "Tab": { "keyCode": 9, "key": "Tab" }, + "KeyQ": { "keyCode": 81, "shiftKey": "Q", "key": "q" }, + "KeyW": { "keyCode": 87, "shiftKey": "W", "key": "w" }, + "KeyE": { "keyCode": 69, "shiftKey": "E", "key": "e" }, + "KeyR": { "keyCode": 82, "shiftKey": "R", "key": "r" }, + "KeyT": { "keyCode": 84, "shiftKey": "T", "key": "t" }, + "KeyY": { "keyCode": 89, "shiftKey": "Y", "key": "y" }, + "KeyU": { "keyCode": 85, "shiftKey": "U", "key": "u" }, + "KeyI": { "keyCode": 73, "shiftKey": "I", "key": "i" }, + "KeyO": { "keyCode": 79, "shiftKey": "O", "key": "o" }, + "KeyP": { "keyCode": 80, "shiftKey": "P", "key": "p" }, + "BracketLeft": { "keyCode": 219, "shiftKey": "{", "key": "[" }, + "BracketRight": { "keyCode": 221, "shiftKey": "}", "key": "]" }, + // Second row + "CapsLock": { "keyCode": 20, "key": "CapsLock" }, + "KeyA": { "keyCode": 65, "shiftKey": "A", "key": "a" }, + "KeyS": { "keyCode": 83, "shiftKey": "S", "key": "s" }, + "KeyD": { "keyCode": 68, "shiftKey": "D", "key": "d" }, + "KeyF": { "keyCode": 70, "shiftKey": "F", "key": "f" }, + "KeyG": { "keyCode": 71, "shiftKey": "G", "key": "g" }, + "KeyH": { "keyCode": 72, "shiftKey": "H", "key": "h" }, + "KeyJ": { "keyCode": 74, "shiftKey": "J", "key": "j" }, + "KeyK": { "keyCode": 75, "shiftKey": "K", "key": "k" }, + "KeyL": { "keyCode": 76, "shiftKey": "L", "key": "l" }, + "Semicolon": { "keyCode": 186, "shiftKey": ":", "key": ";" }, + "Quote": { "keyCode": 222, "shiftKey": '"', "key": "'" }, + "Enter": { "keyCode": 13, "key": "Enter", "text": "\r" }, + // Third row + "ShiftLeft": { "keyCode": 160, "keyCodeWithoutLocation": 16, "key": "Shift", "location": 1 }, + "KeyZ": { "keyCode": 90, "shiftKey": "Z", "key": "z" }, + "KeyX": { "keyCode": 88, "shiftKey": "X", "key": "x" }, + "KeyC": { "keyCode": 67, "shiftKey": "C", "key": "c" }, + "KeyV": { "keyCode": 86, "shiftKey": "V", "key": "v" }, + "KeyB": { "keyCode": 66, "shiftKey": "B", "key": "b" }, + "KeyN": { "keyCode": 78, "shiftKey": "N", "key": "n" }, + "KeyM": { "keyCode": 77, "shiftKey": "M", "key": "m" }, + "Comma": { "keyCode": 188, "shiftKey": "<", "key": "," }, + "Period": { "keyCode": 190, "shiftKey": ">", "key": "." }, + "Slash": { "keyCode": 191, "shiftKey": "?", "key": "/" }, + "ShiftRight": { "keyCode": 161, "keyCodeWithoutLocation": 16, "key": "Shift", "location": 2 }, + // Last row + "ControlLeft": { "keyCode": 162, "keyCodeWithoutLocation": 17, "key": "Control", "location": 1 }, + "MetaLeft": { "keyCode": 91, "key": "Meta", "location": 1 }, + "AltLeft": { "keyCode": 164, "keyCodeWithoutLocation": 18, "key": "Alt", "location": 1 }, + "Space": { "keyCode": 32, "key": " " }, + "AltRight": { "keyCode": 165, "keyCodeWithoutLocation": 18, "key": "Alt", "location": 2 }, + "AltGraph": { "keyCode": 225, "key": "AltGraph" }, + "MetaRight": { "keyCode": 92, "key": "Meta", "location": 2 }, + "ContextMenu": { "keyCode": 93, "key": "ContextMenu" }, + "ControlRight": { "keyCode": 163, "keyCodeWithoutLocation": 17, "key": "Control", "location": 2 }, + // Center block + "PrintScreen": { "keyCode": 44, "key": "PrintScreen" }, + "ScrollLock": { "keyCode": 145, "key": "ScrollLock" }, + "Pause": { "keyCode": 19, "key": "Pause" }, + "PageUp": { "keyCode": 33, "key": "PageUp" }, + "PageDown": { "keyCode": 34, "key": "PageDown" }, + "Insert": { "keyCode": 45, "key": "Insert" }, + "Delete": { "keyCode": 46, "key": "Delete" }, + "Home": { "keyCode": 36, "key": "Home" }, + "End": { "keyCode": 35, "key": "End" }, + "ArrowLeft": { "keyCode": 37, "key": "ArrowLeft" }, + "ArrowUp": { "keyCode": 38, "key": "ArrowUp" }, + "ArrowRight": { "keyCode": 39, "key": "ArrowRight" }, + "ArrowDown": { "keyCode": 40, "key": "ArrowDown" }, + // Media keys + "AudioVolumeMute": { "keyCode": 173, "key": "AudioVolumeMute" }, + "AudioVolumeDown": { "keyCode": 174, "key": "AudioVolumeDown" }, + "AudioVolumeUp": { "keyCode": 175, "key": "AudioVolumeUp" }, + "MediaTrackNext": { "keyCode": 176, "key": "MediaTrackNext" }, + "MediaTrackPrevious": { "keyCode": 177, "key": "MediaTrackPrevious" }, + "MediaPlayPause": { "keyCode": 179, "key": "MediaPlayPause" }, + // Numpad + "NumLock": { "keyCode": 144, "key": "NumLock" }, + "NumpadDivide": { "keyCode": 111, "key": "/", "location": 3 }, + "NumpadMultiply": { "keyCode": 106, "key": "*", "location": 3 }, + "NumpadSubtract": { "keyCode": 109, "key": "-", "location": 3 }, + "Numpad7": { "keyCode": 36, "shiftKeyCode": 103, "key": "Home", "shiftKey": "7", "location": 3 }, + "Numpad8": { "keyCode": 38, "shiftKeyCode": 104, "key": "ArrowUp", "shiftKey": "8", "location": 3 }, + "Numpad9": { "keyCode": 33, "shiftKeyCode": 105, "key": "PageUp", "shiftKey": "9", "location": 3 }, + "Numpad4": { "keyCode": 37, "shiftKeyCode": 100, "key": "ArrowLeft", "shiftKey": "4", "location": 3 }, + "Numpad5": { "keyCode": 12, "shiftKeyCode": 101, "key": "Clear", "shiftKey": "5", "location": 3 }, + "Numpad6": { "keyCode": 39, "shiftKeyCode": 102, "key": "ArrowRight", "shiftKey": "6", "location": 3 }, + "NumpadAdd": { "keyCode": 107, "key": "+", "location": 3 }, + "Numpad1": { "keyCode": 35, "shiftKeyCode": 97, "key": "End", "shiftKey": "1", "location": 3 }, + "Numpad2": { "keyCode": 40, "shiftKeyCode": 98, "key": "ArrowDown", "shiftKey": "2", "location": 3 }, + "Numpad3": { "keyCode": 34, "shiftKeyCode": 99, "key": "PageDown", "shiftKey": "3", "location": 3 }, + "Numpad0": { "keyCode": 45, "shiftKeyCode": 96, "key": "Insert", "shiftKey": "0", "location": 3 }, + "NumpadDecimal": { "keyCode": 46, "shiftKeyCode": 110, "key": "\0", "shiftKey": ".", "location": 3 }, + "NumpadEnter": { "keyCode": 13, "key": "Enter", "text": "\r", "location": 3 } + }; + } +}); + +// packages/playwright-core/src/server/input.ts +function resolveSmartModifierString(key) { + if (key === "ControlOrMeta") + return process.platform === "darwin" ? "Meta" : "Control"; + return key; +} +function resolveSmartModifier(m) { + return resolveSmartModifierString(m); +} +function buildLayoutClosure(layout) { + const result2 = /* @__PURE__ */ new Map(); + for (const code in layout) { + const definition = layout[code]; + const description = { + key: definition.key || "", + keyCode: definition.keyCode || 0, + keyCodeWithoutLocation: definition.keyCodeWithoutLocation || definition.keyCode || 0, + code, + text: definition.text || "", + location: definition.location || 0 + }; + if (definition.key.length === 1) + description.text = description.key; + let shiftedDescription; + if (definition.shiftKey) { + assert(definition.shiftKey.length === 1); + shiftedDescription = { ...description }; + shiftedDescription.key = definition.shiftKey; + shiftedDescription.text = definition.shiftKey; + if (definition.shiftKeyCode) + shiftedDescription.keyCode = definition.shiftKeyCode; + } + result2.set(code, { ...description, shifted: shiftedDescription }); + if (aliases.has(code)) { + for (const alias of aliases.get(code)) + result2.set(alias, description); + } + if (definition.location) + continue; + if (description.key.length === 1) + result2.set(description.key, description); + if (shiftedDescription) + result2.set(shiftedDescription.key, { ...shiftedDescription, shifted: void 0 }); + } + return result2; +} +var keypadLocation2, kModifiers, Keyboard, Mouse, aliases, usKeyboardLayout, Touchscreen; +var init_input = __esm({ + "packages/playwright-core/src/server/input.ts"() { + "use strict"; + init_assert(); + init_usKeyboardLayout(); + init_dom(); + keypadLocation2 = keypadLocation; + kModifiers = ["Alt", "Control", "Meta", "Shift"]; + Keyboard = class { + constructor(raw, page) { + this._pressedModifiers = /* @__PURE__ */ new Set(); + this._pressedKeys = /* @__PURE__ */ new Set(); + this._raw = raw; + this._page = page; + } + async apiDown(progress2, key) { + await progress2.race(this._page.instrumentation.onBeforeInputAction(this._page, progress2.metadata)); + await this.down(progress2, key); + } + async down(progress2, key) { + const description = this._keyDescriptionForString(key); + const autoRepeat = this._pressedKeys.has(description.code); + this._pressedKeys.add(description.code); + if (kModifiers.includes(description.key)) + this._pressedModifiers.add(description.key); + await this._raw.keydown(progress2, this._pressedModifiers, key, description, autoRepeat); + } + _keyDescriptionForString(str) { + const keyString = resolveSmartModifierString(str); + let description = usKeyboardLayout.get(keyString); + if (!description) + throw new NonRecoverableDOMError(`Unknown key: "${keyString}"`); + const shift = this._pressedModifiers.has("Shift"); + description = shift && description.shifted ? description.shifted : description; + if (this._pressedModifiers.size > 1 || !this._pressedModifiers.has("Shift") && this._pressedModifiers.size === 1) + return { ...description, text: "" }; + return description; + } + async apiUp(progress2, key) { + await progress2.race(this._page.instrumentation.onBeforeInputAction(this._page, progress2.metadata)); + await this.up(progress2, key); + } + async up(progress2, key) { + const description = this._keyDescriptionForString(key); + if (kModifiers.includes(description.key)) + this._pressedModifiers.delete(description.key); + this._pressedKeys.delete(description.code); + await this._raw.keyup(progress2, this._pressedModifiers, key, description); + } + async apiInsertText(progress2, text2) { + await progress2.race(this._page.instrumentation.onBeforeInputAction(this._page, progress2.metadata)); + await this.insertText(progress2, text2); + } + async insertText(progress2, text2) { + await this._raw.sendText(progress2, text2); + } + async apiType(progress2, text2, options) { + await progress2.race(this._page.instrumentation.onBeforeInputAction(this._page, progress2.metadata)); + await this.type(progress2, text2, options); + } + async type(progress2, text2, options) { + const delay = options && options.delay || void 0; + for (const char of text2) { + if (usKeyboardLayout.has(char)) { + await this.press(progress2, char, { delay }); + } else { + if (delay) + await progress2.wait(delay); + await this.insertText(progress2, char); + } + } + } + async apiPress(progress2, key, options = {}) { + await progress2.race(this._page.instrumentation.onBeforeInputAction(this._page, progress2.metadata)); + await this.press(progress2, key, options); + } + async press(progress2, key, options = {}) { + function split(keyString) { + const keys = []; + let building = ""; + for (const char of keyString) { + if (char === "+" && building) { + keys.push(building); + building = ""; + } else { + building += char; + } + } + keys.push(building); + return keys; + } + const tokens = split(key); + key = tokens[tokens.length - 1]; + for (let i = 0; i < tokens.length - 1; ++i) + await this.down(progress2, tokens[i]); + await this.down(progress2, key); + if (options.delay) + await progress2.wait(options.delay); + await this.up(progress2, key); + for (let i = tokens.length - 2; i >= 0; --i) + await this.up(progress2, tokens[i]); + } + async ensureModifiers(progress2, mm) { + const modifiers = mm.map(resolveSmartModifier); + for (const modifier of modifiers) { + if (!kModifiers.includes(modifier)) + throw new Error("Unknown modifier " + modifier); + } + const restore = Array.from(this._pressedModifiers); + for (const key of kModifiers) { + const needDown = modifiers.includes(key); + const isDown = this._pressedModifiers.has(key); + if (needDown && !isDown) + await this.down(progress2, key); + else if (!needDown && isDown) + await this.up(progress2, key); + } + return restore; + } + _modifiers() { + return this._pressedModifiers; + } + }; + Mouse = class { + constructor(raw, page) { + this._x = 0; + this._y = 0; + this._lastButton = "none"; + this._buttons = /* @__PURE__ */ new Set(); + this._raw = raw; + this._page = page; + this._keyboard = this._page.keyboard; + } + _currentPoint() { + return { x: this._x, y: this._y }; + } + async apiMove(progress2, x, y, options = {}) { + await progress2.race(this._page.instrumentation.onBeforeInputAction(this._page, progress2.metadata, { x, y })); + await this.move(progress2, x, y, options); + } + async move(progress2, x, y, options = {}) { + const { steps = 1 } = options; + const fromX = this._x; + const fromY = this._y; + this._x = x; + this._y = y; + for (let i = 1; i <= steps; i++) { + const middleX = fromX + (x - fromX) * (i / steps); + const middleY = fromY + (y - fromY) * (i / steps); + await this._raw.move(progress2, middleX, middleY, this._lastButton, this._buttons, this._keyboard._modifiers(), !!options.forClick); + } + } + async apiDown(progress2, options = {}) { + await progress2.race(this._page.instrumentation.onBeforeInputAction(this._page, progress2.metadata, this._currentPoint())); + await this.down(progress2, options); + } + async down(progress2, options = {}) { + const { button = "left", clickCount = 1 } = options; + this._lastButton = button; + this._buttons.add(button); + await this._raw.down(progress2, this._x, this._y, this._lastButton, this._buttons, this._keyboard._modifiers(), clickCount); + } + async apiUp(progress2, options = {}) { + await progress2.race(this._page.instrumentation.onBeforeInputAction(this._page, progress2.metadata, this._currentPoint())); + await this.up(progress2, options); + } + async up(progress2, options = {}) { + const { button = "left", clickCount = 1 } = options; + this._lastButton = "none"; + this._buttons.delete(button); + await this._raw.up(progress2, this._x, this._y, button, this._buttons, this._keyboard._modifiers(), clickCount); + } + async apiClick(progress2, x, y, options = {}) { + await progress2.race(this._page.instrumentation.onBeforeInputAction(this._page, progress2.metadata, { x, y })); + await this.click(progress2, x, y, options); + } + async click(progress2, x, y, options = {}) { + const { delay = null, clickCount = 1, steps } = options; + if (delay) { + await this.move(progress2, x, y, { forClick: true, steps }); + for (let cc = 1; cc <= clickCount; ++cc) { + await this.down(progress2, { ...options, clickCount: cc }); + await progress2.wait(delay); + await this.up(progress2, { ...options, clickCount: cc }); + if (cc < clickCount) + await progress2.wait(delay); + } + } else { + progress2.setAllowConcurrentOrNestedRaces(true); + const promises = []; + const movePromise = this.move(progress2, x, y, { forClick: true, steps }); + if (steps !== void 0 && steps > 1) + await movePromise; + else + promises.push(movePromise); + for (let cc = 1; cc <= clickCount; ++cc) { + promises.push(this.down(progress2, { ...options, clickCount: cc })); + promises.push(this.up(progress2, { ...options, clickCount: cc })); + } + await Promise.all(promises); + progress2.setAllowConcurrentOrNestedRaces(false); + } + } + async apiWheel(progress2, deltaX, deltaY) { + await progress2.race(this._page.instrumentation.onBeforeInputAction(this._page, progress2.metadata)); + await this._raw.wheel(progress2, this._x, this._y, this._buttons, this._keyboard._modifiers(), deltaX, deltaY); + } + }; + aliases = /* @__PURE__ */ new Map([ + ["ShiftLeft", ["Shift"]], + ["ControlLeft", ["Control"]], + ["AltLeft", ["Alt"]], + ["MetaLeft", ["Meta"]], + ["Enter", ["\n", "\r"]] + ]); + usKeyboardLayout = buildLayoutClosure(USKeyboardLayout); + Touchscreen = class { + constructor(raw, page) { + this._raw = raw; + this._page = page; + } + async apiTap(progress2, x, y) { + if (!this._page.browserContext._options.hasTouch) + throw new Error("hasTouch must be enabled on the browser context before using the touchscreen."); + await progress2.race(this._page.instrumentation.onBeforeInputAction(this._page, progress2.metadata, { x, y })); + await this.tap(progress2, x, y); + } + async tap(progress2, x, y) { + await this._raw.tap(progress2, x, y, this._page.keyboard._modifiers()); + } + }; + } +}); + +// packages/playwright-core/src/server/screenshotter.ts +function inPagePrepareForScreenshots(screenshotStyle, hideCaret, disableAnimations, syncAnimations) { + if (syncAnimations) { + const style = document.createElement("style"); + style.textContent = "body {}"; + document.head.appendChild(style); + document.documentElement.getBoundingClientRect(); + style.remove(); + } + if (!screenshotStyle && !hideCaret && !disableAnimations) + return; + const collectRoots = (root, roots2 = []) => { + roots2.push(root); + const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT); + do { + const node = walker.currentNode; + const shadowRoot = node instanceof Element ? node.shadowRoot : null; + if (shadowRoot) + collectRoots(shadowRoot, roots2); + } while (walker.nextNode()); + return roots2; + }; + const roots = collectRoots(document); + const cleanupCallbacks = []; + if (screenshotStyle) { + for (const root of roots) { + const styleTag = document.createElement("style"); + styleTag.textContent = screenshotStyle; + if (root === document) + document.documentElement.append(styleTag); + else + root.append(styleTag); + cleanupCallbacks.push(() => { + styleTag.remove(); + }); + } + } + if (hideCaret) { + const elements = /* @__PURE__ */ new Map(); + for (const root of roots) { + root.querySelectorAll("input,textarea,[contenteditable]").forEach((element2) => { + elements.set(element2, { + value: element2.style.getPropertyValue("caret-color"), + priority: element2.style.getPropertyPriority("caret-color") + }); + element2.style.setProperty("caret-color", "transparent", "important"); + }); + } + cleanupCallbacks.push(() => { + for (const [element2, value2] of elements) + element2.style.setProperty("caret-color", value2.value, value2.priority); + }); + } + if (disableAnimations) { + const infiniteAnimationsToResume = /* @__PURE__ */ new Set(); + const handleAnimations = (root) => { + for (const animation of root.getAnimations()) { + if (!animation.effect || animation.playbackRate === 0 || infiniteAnimationsToResume.has(animation)) + continue; + const endTime = animation.effect.getComputedTiming().endTime; + if (Number.isFinite(endTime)) { + try { + animation.finish(); + } catch (e) { + } + } else { + try { + animation.cancel(); + infiniteAnimationsToResume.add(animation); + } catch (e) { + } + } + } + }; + for (const root of roots) { + const handleRootAnimations = handleAnimations.bind(null, root); + handleRootAnimations(); + root.addEventListener("transitionrun", handleRootAnimations); + root.addEventListener("animationstart", handleRootAnimations); + cleanupCallbacks.push(() => { + root.removeEventListener("transitionrun", handleRootAnimations); + root.removeEventListener("animationstart", handleRootAnimations); + }); + } + cleanupCallbacks.push(() => { + for (const animation of infiniteAnimationsToResume) { + try { + animation.play(); + } catch (e) { + } + } + }); + } + window.__pwCleanupScreenshot = () => { + for (const cleanupCallback of cleanupCallbacks) + cleanupCallback(); + delete window.__pwCleanupScreenshot; + }; +} +function trimClipToSize(clip, size) { + const p1 = { + x: Math.max(0, Math.min(clip.x, size.width)), + y: Math.max(0, Math.min(clip.y, size.height)) + }; + const p2 = { + x: Math.max(0, Math.min(clip.x + clip.width, size.width)), + y: Math.max(0, Math.min(clip.y + clip.height, size.height)) + }; + const result2 = { x: p1.x, y: p1.y, width: p2.x - p1.x, height: p2.y - p1.y }; + assert(result2.width && result2.height, "Clipped area is either empty or outside the resulting image"); + return result2; +} +function validateScreenshotOptions(options) { + let format2 = null; + if (options.type) { + assert(options.type === "png" || options.type === "jpeg", "Unknown options.type value: " + options.type); + format2 = options.type; + } + if (!format2) + format2 = "png"; + if (options.quality !== void 0) { + assert(format2 === "jpeg", "options.quality is unsupported for the " + format2 + " screenshots"); + assert(typeof options.quality === "number", "Expected options.quality to be a number but found " + typeof options.quality); + assert(Number.isInteger(options.quality), "Expected options.quality to be an integer"); + assert(options.quality >= 0 && options.quality <= 100, "Expected options.quality to be between 0 and 100 (inclusive), got " + options.quality); + } + if (options.clip) { + assert(typeof options.clip.x === "number", "Expected options.clip.x to be a number but found " + typeof options.clip.x); + assert(typeof options.clip.y === "number", "Expected options.clip.y to be a number but found " + typeof options.clip.y); + assert(typeof options.clip.width === "number", "Expected options.clip.width to be a number but found " + typeof options.clip.width); + assert(typeof options.clip.height === "number", "Expected options.clip.height to be a number but found " + typeof options.clip.height); + assert(options.clip.width !== 0, "Expected options.clip.width not to be 0."); + assert(options.clip.height !== 0, "Expected options.clip.height not to be 0."); + } + return format2; +} +var Screenshotter, TaskQueue; +var init_screenshotter = __esm({ + "packages/playwright-core/src/server/screenshotter.ts"() { + "use strict"; + init_multimap(); + init_assert(); + init_helper(); + Screenshotter = class { + constructor(page) { + this._queue = new TaskQueue(); + this._page = page; + this._queue = new TaskQueue(); + } + async _originalViewportSize(progress2) { + let viewportSize = this._page.emulatedSize()?.viewport; + if (!viewportSize) + viewportSize = await this._page.mainFrame().waitForFunctionValueInUtility(progress2, () => ({ width: window.innerWidth, height: window.innerHeight })); + return viewportSize; + } + async _fullPageSize(progress2) { + const fullPageSize = await this._page.mainFrame().waitForFunctionValueInUtility(progress2, () => { + if (!document.body || !document.documentElement) + return null; + return { + width: Math.max( + document.body.scrollWidth, + document.documentElement.scrollWidth, + document.body.offsetWidth, + document.documentElement.offsetWidth, + document.body.clientWidth, + document.documentElement.clientWidth + ), + height: Math.max( + document.body.scrollHeight, + document.documentElement.scrollHeight, + document.body.offsetHeight, + document.documentElement.offsetHeight, + document.body.clientHeight, + document.documentElement.clientHeight + ) + }; + }); + return fullPageSize; + } + async screenshotPage(progress2, options) { + const format2 = validateScreenshotOptions(options); + return this._queue._postTask(async () => { + progress2.log("taking page screenshot"); + const viewportSize = await this._originalViewportSize(progress2); + await this._preparePageForScreenshot(progress2, this._page.mainFrame(), options.style, options.caret !== "initial", options.animations === "disabled"); + try { + if (options.fullPage) { + const fullPageSize = await this._fullPageSize(progress2); + let documentRect = { x: 0, y: 0, width: fullPageSize.width, height: fullPageSize.height }; + const fitsViewport = fullPageSize.width <= viewportSize.width && fullPageSize.height <= viewportSize.height; + if (options.clip) + documentRect = trimClipToSize(options.clip, documentRect); + return await this._screenshot(progress2, format2, documentRect, void 0, fitsViewport, options); + } + const viewportRect = options.clip ? trimClipToSize(options.clip, viewportSize) : { x: 0, y: 0, ...viewportSize }; + return await this._screenshot(progress2, format2, void 0, viewportRect, true, options); + } finally { + await progress2.race(this._restorePageAfterScreenshot()).catch(() => { + }); + } + }); + } + async screenshotElement(progress2, handle, options) { + const format2 = validateScreenshotOptions(options); + return this._queue._postTask(async () => { + progress2.log("taking element screenshot"); + const viewportSize = await this._originalViewportSize(progress2); + await this._preparePageForScreenshot(progress2, handle._frame, options.style, options.caret !== "initial", options.animations === "disabled"); + try { + await handle._waitAndScrollIntoViewIfNeeded( + progress2, + true + /* waitForVisible */ + ); + const boundingBox = await handle.boundingBox(progress2); + assert(boundingBox, "Node is either not visible or not an HTMLElement"); + assert(boundingBox.width !== 0, "Node has 0 width."); + assert(boundingBox.height !== 0, "Node has 0 height."); + const fitsViewport = boundingBox.width <= viewportSize.width && boundingBox.height <= viewportSize.height; + const scrollOffset = await this._page.mainFrame().waitForFunctionValueInUtility(progress2, () => ({ x: window.scrollX, y: window.scrollY })); + const documentRect = { ...boundingBox }; + documentRect.x += scrollOffset.x; + documentRect.y += scrollOffset.y; + return await this._screenshot(progress2, format2, helper.enclosingIntRect(documentRect), void 0, fitsViewport, options); + } finally { + await progress2.race(this._restorePageAfterScreenshot()).catch(() => { + }); + } + }); + } + async _preparePageForScreenshot(progress2, frame, screenshotStyle, hideCaret, disableAnimations) { + if (disableAnimations) + progress2.log(" disabled all CSS animations"); + const syncAnimations = this._page.delegate.shouldToggleStyleSheetToSyncAnimations(); + await progress2.race(this._page.safeNonStallingEvaluateInAllFrames("(" + inPagePrepareForScreenshots.toString() + `)(${JSON.stringify(screenshotStyle)}, ${hideCaret}, ${disableAnimations}, ${syncAnimations})`, "utility")); + try { + if (!process.env.PW_TEST_SCREENSHOT_NO_FONTS_READY) { + progress2.log("waiting for fonts to load..."); + await progress2.race(frame.nonStallingEvaluateInExistingContext("document.fonts.ready", "utility").catch(() => { + })); + progress2.log("fonts loaded"); + } + } catch (error) { + await progress2.race(this._restorePageAfterScreenshot()); + throw error; + } + } + async _restorePageAfterScreenshot() { + await this._page.safeNonStallingEvaluateInAllFrames("window.__pwCleanupScreenshot && window.__pwCleanupScreenshot()", "utility"); + } + async _maskElements(progress2, options) { + if (!options.mask || !options.mask.length) + return () => Promise.resolve(); + const framesToParsedSelectors = new MultiMap(); + await progress2.race(Promise.all((options.mask || []).map(async ({ frame, selector }) => { + const pair = await frame.selectors.resolveFrameForSelector(selector); + if (pair) + framesToParsedSelectors.set(pair.frame, pair.info.parsed); + }))); + const frames = [...framesToParsedSelectors.keys()]; + const cleanup = async () => { + await Promise.all(frames.map((frame) => frame.hideHighlight())); + }; + try { + const promises = frames.map((frame) => frame.maskSelectors(framesToParsedSelectors.get(frame), options.maskColor || "#F0F")); + await progress2.race(Promise.all(promises)); + return cleanup; + } catch (error) { + cleanup().catch(() => { + }); + throw error; + } + } + async _screenshot(progress2, format2, documentRect, viewportRect, fitsViewport, options) { + if (options.__testHookBeforeScreenshot) + await progress2.race(options.__testHookBeforeScreenshot()); + const shouldSetDefaultBackground = options.omitBackground && format2 === "png"; + if (shouldSetDefaultBackground) + await progress2.race(this._page.delegate.setBackgroundColor({ r: 0, g: 0, b: 0, a: 0 })); + const cleanupHighlight = await this._maskElements(progress2, options); + try { + const quality = format2 === "jpeg" ? options.quality ?? 80 : void 0; + const buffer = await this._page.delegate.takeScreenshot(progress2, format2, documentRect, viewportRect, quality, fitsViewport, options.scale || "device"); + await progress2.race(cleanupHighlight()); + if (shouldSetDefaultBackground) + await progress2.race(this._page.delegate.setBackgroundColor()); + if (options.__testHookAfterScreenshot) + await progress2.race(options.__testHookAfterScreenshot()); + return buffer; + } catch (error) { + cleanupHighlight().catch(() => { + }); + if (shouldSetDefaultBackground) + this._page.delegate.setBackgroundColor().catch(() => { + }); + throw error; + } + } + }; + TaskQueue = class { + constructor() { + this._chain = Promise.resolve(); + } + _postTask(task) { + const result2 = this._chain.then(task); + this._chain = result2.catch(() => { + }); + return result2; + } + }; + } +}); + +// packages/playwright-core/src/generated/bindingsControllerSource.ts +var source5; +var init_bindingsControllerSource = __esm({ + "packages/playwright-core/src/generated/bindingsControllerSource.ts"() { + "use strict"; + source5 = '\nvar __commonJS = obj => {\n let required = false;\n let result;\n return function __require() {\n if (!required) {\n required = true;\n let fn;\n for (const name in obj) { fn = obj[name]; break; }\n const module = { exports: {} };\n fn(module.exports, module);\n result = module.exports;\n }\n return result;\n }\n};\nvar __export = (target, all) => {for (var name in all) target[name] = all[name];};\nvar __toESM = mod => ({ ...mod, \'default\': mod });\nvar __toCommonJS = mod => ({ ...mod, __esModule: true });\n\n\n// packages/injected/src/bindingsController.ts\nvar bindingsController_exports = {};\n__export(bindingsController_exports, {\n BindingsController: () => BindingsController\n});\nmodule.exports = __toCommonJS(bindingsController_exports);\n\n// packages/isomorphic/utilityScriptSerializers.ts\nfunction isRegExp(obj) {\n try {\n return obj instanceof RegExp || Object.prototype.toString.call(obj) === "[object RegExp]";\n } catch (error) {\n return false;\n }\n}\nfunction isDate(obj) {\n try {\n return obj instanceof Date || Object.prototype.toString.call(obj) === "[object Date]";\n } catch (error) {\n return false;\n }\n}\nfunction isURL(obj) {\n try {\n return obj instanceof URL || Object.prototype.toString.call(obj) === "[object URL]";\n } catch (error) {\n return false;\n }\n}\nfunction isError(obj) {\n var _a;\n try {\n return obj instanceof Error || obj && ((_a = Object.getPrototypeOf(obj)) == null ? void 0 : _a.name) === "Error";\n } catch (error) {\n return false;\n }\n}\nfunction isTypedArray(obj, constructor) {\n try {\n return obj instanceof constructor || Object.prototype.toString.call(obj) === `[object ${constructor.name}]`;\n } catch (error) {\n return false;\n }\n}\nfunction isArrayBuffer(obj) {\n try {\n return obj instanceof ArrayBuffer || Object.prototype.toString.call(obj) === "[object ArrayBuffer]";\n } catch (error) {\n return false;\n }\n}\nvar typedArrayConstructors = {\n i8: Int8Array,\n ui8: Uint8Array,\n ui8c: Uint8ClampedArray,\n i16: Int16Array,\n ui16: Uint16Array,\n i32: Int32Array,\n ui32: Uint32Array,\n // TODO: add Float16Array once it\'s in baseline\n f32: Float32Array,\n f64: Float64Array,\n bi64: BigInt64Array,\n bui64: BigUint64Array\n};\nfunction typedArrayToBase64(array) {\n if ("toBase64" in array)\n return array.toBase64();\n const binary = Array.from(new Uint8Array(array.buffer, array.byteOffset, array.byteLength)).map((b) => String.fromCharCode(b)).join("");\n return btoa(binary);\n}\nfunction serializeAsCallArgument(value, handleSerializer) {\n return serialize(value, handleSerializer, { visited: /* @__PURE__ */ new Map(), lastId: 0 });\n}\nfunction serialize(value, handleSerializer, visitorInfo) {\n if (value && typeof value === "object") {\n if (typeof globalThis.Window === "function" && value instanceof globalThis.Window)\n return "ref: ";\n if (typeof globalThis.Document === "function" && value instanceof globalThis.Document)\n return "ref: ";\n if (typeof globalThis.Node === "function" && value instanceof globalThis.Node)\n return "ref: ";\n }\n return innerSerialize(value, handleSerializer, visitorInfo);\n}\nfunction innerSerialize(value, handleSerializer, visitorInfo) {\n var _a;\n const result = handleSerializer(value);\n if ("fallThrough" in result)\n value = result.fallThrough;\n else\n return result;\n if (typeof value === "symbol")\n return { v: "undefined" };\n if (Object.is(value, void 0))\n return { v: "undefined" };\n if (Object.is(value, null))\n return { v: "null" };\n if (Object.is(value, NaN))\n return { v: "NaN" };\n if (Object.is(value, Infinity))\n return { v: "Infinity" };\n if (Object.is(value, -Infinity))\n return { v: "-Infinity" };\n if (Object.is(value, -0))\n return { v: "-0" };\n if (typeof value === "boolean")\n return value;\n if (typeof value === "number")\n return value;\n if (typeof value === "string")\n return value;\n if (typeof value === "bigint")\n return { bi: value.toString() };\n if (isError(value)) {\n let stack;\n if ((_a = value.stack) == null ? void 0 : _a.startsWith(value.name + ": " + value.message)) {\n stack = value.stack;\n } else {\n stack = `${value.name}: ${value.message}\n${value.stack}`;\n }\n return { e: { n: value.name, m: value.message, s: stack } };\n }\n if (isDate(value))\n return { d: value.toJSON() };\n if (isURL(value))\n return { u: value.toJSON() };\n if (isRegExp(value))\n return { r: { p: value.source, f: value.flags } };\n for (const [k, ctor] of Object.entries(typedArrayConstructors)) {\n if (isTypedArray(value, ctor))\n return { ta: { b: typedArrayToBase64(value), k } };\n }\n if (isArrayBuffer(value))\n return { ab: { b: typedArrayToBase64(new Uint8Array(value)) } };\n const id = visitorInfo.visited.get(value);\n if (id)\n return { ref: id };\n if (Array.isArray(value)) {\n const a = [];\n const id2 = ++visitorInfo.lastId;\n visitorInfo.visited.set(value, id2);\n for (let i = 0; i < value.length; ++i)\n a.push(serialize(value[i], handleSerializer, visitorInfo));\n return { a, id: id2 };\n }\n if (typeof value === "object") {\n const o = [];\n const id2 = ++visitorInfo.lastId;\n visitorInfo.visited.set(value, id2);\n for (const name of Object.keys(value)) {\n let item;\n try {\n item = value[name];\n } catch (e) {\n continue;\n }\n if (name === "toJSON" && typeof item === "function")\n o.push({ k: name, v: { o: [], id: 0 } });\n else\n o.push({ k: name, v: serialize(item, handleSerializer, visitorInfo) });\n }\n let jsonWrapper;\n try {\n if (o.length === 0 && value.toJSON && typeof value.toJSON === "function")\n jsonWrapper = { value: value.toJSON() };\n } catch (e) {\n }\n if (jsonWrapper)\n return innerSerialize(jsonWrapper.value, handleSerializer, visitorInfo);\n return { o, id: id2 };\n }\n}\n\n// packages/injected/src/bindingsController.ts\nvar BindingsController = class {\n constructor(global, globalBindingName) {\n this._bindings = /* @__PURE__ */ new Map();\n this._global = global;\n this._globalBindingName = globalBindingName;\n }\n addBinding(bindingName) {\n const data = {\n callbacks: /* @__PURE__ */ new Map(),\n lastSeq: 0,\n removed: false\n };\n this._bindings.set(bindingName, data);\n this._global[bindingName] = (...args) => {\n if (data.removed)\n throw new Error(`binding "${bindingName}" has been removed`);\n const seq = ++data.lastSeq;\n const promise = new Promise((resolve, reject) => data.callbacks.set(seq, { resolve, reject }));\n const serializedArgs = [];\n for (let i = 0; i < args.length; i++) {\n serializedArgs[i] = serializeAsCallArgument(args[i], (v) => {\n return { fallThrough: v };\n });\n }\n const payload = { name: bindingName, seq, serializedArgs };\n this._global[this._globalBindingName](JSON.stringify(payload));\n return promise;\n };\n }\n removeBinding(bindingName) {\n const data = this._bindings.get(bindingName);\n if (data)\n data.removed = true;\n this._bindings.delete(bindingName);\n delete this._global[bindingName];\n }\n deliverBindingResult(arg) {\n const callbacks = this._bindings.get(arg.name).callbacks;\n if ("error" in arg)\n callbacks.get(arg.seq).reject(arg.error);\n else\n callbacks.get(arg.seq).resolve(arg.result);\n callbacks.delete(arg.seq);\n }\n};\n'; + } +}); + +// packages/playwright-core/src/server/overlay.ts +var Overlay; +var init_overlay = __esm({ + "packages/playwright-core/src/server/overlay.ts"() { + "use strict"; + init_crypto(); + init_debugLogger(); + init_stringUtils(); + init_page(); + Overlay = class { + constructor(page) { + this._overlays = /* @__PURE__ */ new Map(); + this._page = page; + this._page.on(Page.Events.InternalFrameNavigatedToNewDocument, (frame) => { + if (frame.parentFrame()) + return; + for (const [id, html] of this._overlays) + this._doAdd(id, html).catch((e) => debugLogger.log("error", e)); + }); + } + dispose() { + } + async show(html, duration) { + const id = createGuid(); + this._overlays.set(id, html); + await this._doAdd(id, html).catch((e) => debugLogger.log("error", e)); + if (duration) { + await new Promise((f) => setTimeout(f, duration)); + await this.remove(id); + } + return id; + } + async _doAdd(id, html) { + const utility = await this._page.mainFrame().utilityContext(); + await utility.evaluate(({ injected, html: html2, id: id2 }) => { + return injected.addUserOverlay(id2, html2); + }, { injected: await utility.injectedScript(), html, id }); + } + async remove(id) { + this._overlays.delete(id); + const utility = await this._page.mainFrame().utilityContext(); + await utility.evaluate(({ injected, id: id2 }) => { + injected.removeUserOverlay(id2); + }, { injected: await utility.injectedScript(), id }).catch((e) => debugLogger.log("error", e)); + } + async chapter(options) { + const fadeDuration = 300; + const descriptionHtml = options.description ? `${escapeHTML(options.description)}` : ""; + const styleSheet = ` + @keyframes pw-chapter-fade-in { + from { opacity: 0; } + to { opacity: 1; } + } + @keyframes pw-chapter-fade-out { + from { opacity: 1; } + to { opacity: 0; } + } + #background { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + backdrop-filter: blur(2px); + animation: pw-chapter-fade-in ${fadeDuration}ms ease-out forwards; + } + #background.fade-out { + animation: pw-chapter-fade-out ${fadeDuration}ms ease-in forwards; + } + #content { + background: rgba(0, 0, 0, 0.7); + border: 1px solid rgba(255, 255, 255, 0.15); + border-radius: 16px; + padding: 40px 56px; + max-width: 560px; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4); + } + #title { + color: white; + font-family: system-ui, -apple-system, sans-serif; + font-size: 28px; + font-weight: 600; + line-height: 1.3; + text-align: center; + letter-spacing: -0.01em; + } + #description { + color: rgba(255, 255, 255, 0.7); + font-family: system-ui, -apple-system, sans-serif; + font-size: 15px; + line-height: 1.5; + margin-top: 12px; + text-align: center; + } + `; + const duration = options.duration ?? 2e3; + const html = `${escapeHTML(options.title)}${descriptionHtml}`; + const id = await this.show(html); + await new Promise((f) => setTimeout(f, duration)); + const utility = await this._page.mainFrame().utilityContext(); + await utility.evaluate(({ injected, id: id2, fadeDuration: fadeDuration2 }) => { + const overlay = injected.getUserOverlay(id2); + const bg = overlay?.querySelector("#background"); + if (bg) + bg.classList.add("fade-out"); + return new Promise((f) => injected.utils.builtins.setTimeout(f, fadeDuration2)); + }, { injected: await utility.injectedScript(), id, fadeDuration }).catch((e) => debugLogger.log("error", e)); + await this.remove(id); + } + async setVisible(visible) { + if (!this._overlays.size) + return; + const utility = await this._page.mainFrame().utilityContext(); + await utility.evaluate(({ injected, visible: visible2 }) => { + injected.setUserOverlaysVisible(visible2); + }, { injected: await utility.injectedScript(), visible }).catch((e) => debugLogger.log("error", e)); + } + }; + } +}); + +// packages/playwright-core/src/server/screencast.ts +var Screencast; +var init_screencast = __esm({ + "packages/playwright-core/src/server/screencast.ts"() { + "use strict"; + init_manualPromise(); + init_protocolFormatter(); + init_debugLogger(); + init_progress(); + init_dom(); + Screencast = class { + constructor(page) { + this._clients = /* @__PURE__ */ new Map(); + this.page = page; + this.page.instrumentation.addListener(this, this.page.browserContext); + } + async handlePageOrContextClose() { + const clients = [...this._clients.keys()]; + this._clients.clear(); + for (const client of clients) { + if (client.gracefulClose) + await client.gracefulClose(); + } + } + dispose() { + for (const client of this._clients.keys()) + client.dispose(); + this._clients.clear(); + this.page.instrumentation.removeListener(this); + } + showActions(options) { + this._actions = options; + } + hideActions() { + this._actions = void 0; + } + addClient(client) { + const isFirst = this._clients.size === 0; + this._clients.set(client, new ManualPromise()); + if (isFirst) { + this._startScreencast(client.size, client.quality); + } else if (this._lastFrame) { + const frame = this._lastFrame; + setTimeout(() => { + if (this._clients.has(client)) + void client.onFrame(frame); + }, 0); + } + return { size: this._size }; + } + removeClient(client) { + const disconnected = this._clients.get(client); + if (!disconnected) + return; + this._clients.delete(client); + disconnected.resolve(); + if (!this._clients.size) + this._stopScreencast(); + } + _startScreencast(size, quality) { + this._size = size; + if (!this._size) { + const viewport = this.page.browserContext._options.viewport || { width: 800, height: 600 }; + const scale = Math.min(1, 800 / Math.max(viewport.width, viewport.height)); + this._size = { + width: Math.floor(viewport.width * scale), + height: Math.floor(viewport.height * scale) + }; + } + this._size = { + width: this._size.width & ~1, + height: this._size.height & ~1 + }; + this.page.delegate.startScreencast({ + width: this._size.width, + height: this._size.height, + quality: quality ?? 90 + }); + } + _stopScreencast() { + this._lastFrame = void 0; + this.page.delegate.stopScreencast(); + } + onScreencastFrame(frame, ack) { + this._lastFrame = frame; + const asyncResults = []; + for (const [client, disconnected] of this._clients) { + const result2 = client.onFrame(frame); + if (!result2) + continue; + asyncResults.push(Promise.race([result2.catch(() => { + }), disconnected])); + } + if (ack) { + if (!asyncResults.length) + ack(); + else + Promise.race(asyncResults).then(ack); + } + } + async onBeforeInputAction(sdkObject, metadata, point, box) { + if (!this._actions) + return; + const page = sdkObject.attribution.page; + if (page !== this.page) + return; + if (!box && sdkObject instanceof ElementHandle) + box = await sdkObject.boundingBox(nullProgress) || void 0; + const actionTitle2 = renderTitleForCall(metadata); + const utility = await page.mainFrame().utilityContext(); + await utility.evaluate(async (options) => { + const { injected, duration } = options; + injected.setScreencastAnnotation(options); + await new Promise((f) => injected.utils.builtins.setTimeout(f, duration)); + injected.setScreencastAnnotation(null); + }, { + injected: await utility.injectedScript(), + duration: this._actions?.duration ?? 500, + point, + box, + actionTitle: actionTitle2, + position: this._actions?.position, + fontSize: this._actions?.fontSize, + cursor: this._actions?.cursor ?? "pointer" + }).catch((e) => debugLogger.log("error", e)); + } + }; + } +}); + +// packages/playwright-core/src/server/page.ts +async function ariaSnapshotForFrame(progress2, frame, options = {}) { + const snapshot3 = await frame.retryWithProgressAndTimeouts(progress2, [1e3, 2e3, 4e3, 8e3], async (progress3, continuePolling) => { + try { + const context2 = await progress3.race(frame.utilityContext()); + const injectedScript = await progress3.race(context2.injectedScript()); + const snapshotOrRetry = await progress3.race(injectedScript.evaluate((injected, options2) => { + if (options2.info) { + const element2 = injected.querySelector(options2.info.parsed, injected.document, options2.info.strict); + if (!element2) + return false; + return injected.incrementalAriaSnapshot(element2, options2); + } + const node = injected.document.body; + if (!node) + return true; + return injected.incrementalAriaSnapshot(node, options2); + }, { + mode: options.mode ?? "default", + refPrefix: frame.seq ? "f" + frame.seq : "", + track: options.track, + doNotRenderActive: options.doNotRenderActive, + info: options.info, + depth: options.depth, + boxes: options.boxes + })); + if (snapshotOrRetry === true) + return continuePolling; + if (snapshotOrRetry === false) + throw new NonRecoverableDOMError(`Selector "${stringifySelector(options.info.parsed)}" does not match any element`); + return snapshotOrRetry; + } catch (e) { + if (frame.isNonRetriableError(e)) + throw e; + return continuePolling; + } + }); + const renderedIframeRefs = snapshot3.iframeRefs.filter((ref) => ref in snapshot3.iframeDepths); + progress2.setAllowConcurrentOrNestedRaces(true); + const childSnapshotPromises = renderedIframeRefs.map((ref) => { + const iframeDepth = snapshot3.iframeDepths[ref]; + const childDepth = options.depth ? options.depth - iframeDepth - 1 : void 0; + return ariaSnapshotFrameRef(progress2, frame, ref, { ...options, depth: childDepth }); + }); + const childSnapshots = await Promise.all(childSnapshotPromises); + progress2.setAllowConcurrentOrNestedRaces(false); + const full = []; + let incremental; + if (snapshot3.incremental !== void 0) { + incremental = snapshot3.incremental.split("\n"); + for (let i = 0; i < renderedIframeRefs.length; i++) { + const childSnapshot = childSnapshots[i]; + if (childSnapshot.incremental) + incremental.push(...childSnapshot.incremental); + else if (childSnapshot.full.length) + incremental.push("- iframe [ref=" + renderedIframeRefs[i] + "]:", ...childSnapshot.full.map((l) => " " + l)); + } + } + for (const line of snapshot3.full.split("\n")) { + const match = line.match(/^(\s*)- iframe (?:\[active\] )?\[ref=([^\]]*)\]/); + if (!match) { + full.push(line); + continue; + } + const leadingSpace = match[1]; + const ref = match[2]; + const childSnapshot = childSnapshots[renderedIframeRefs.indexOf(ref)] ?? { full: [] }; + full.push(childSnapshot.full.length ? line + ":" : line); + full.push(...childSnapshot.full.map((l) => leadingSpace + " " + l)); + } + return { full, incremental }; +} +async function ariaSnapshotFrameRef(progress2, parentFrame, frameRef, options) { + const frameSelector = `aria-ref=${frameRef} >> internal:control=enter-frame`; + const frameBodySelector = `${frameSelector} >> body`; + const child = await progress2.race(parentFrame.selectors.resolveFrameForSelector(frameBodySelector, { strict: true })); + if (!child) + return { full: [] }; + try { + return await ariaSnapshotForFrame(progress2, child.frame, { ...options, info: void 0 }); + } catch { + return { full: [] }; + } +} +function ensureArrayLimit(array, limit) { + if (array.length > limit) + return array.splice(0, limit / 10); + return []; +} +var PageEvent, navigationMarkSymbol, Page, WorkerEvent, Worker, PageBinding, InitScript; +var init_page = __esm({ + "packages/playwright-core/src/server/page.ts"() { + "use strict"; + init_selectorParser(); + init_manualPromise(); + init_utilityScriptSerializers(); + init_comparators(); + init_debugLogger(); + init_manualPromise(); + init_assert(); + init_stringUtils(); + init_locatorGenerators(); + init_browserContext(); + init_disposable2(); + init_console(); + init_errors(); + init_fileChooser(); + init_frames(); + init_helper(); + init_input(); + init_instrumentation(); + init_javascript(); + init_screenshotter(); + init_callLog(); + init_bindingsControllerSource(); + init_overlay(); + init_dom(); + init_screencast(); + init_javascript(); + PageEvent = { + Close: "close", + Crash: "crash", + Download: "download", + EmulatedSizeChanged: "emulatedsizechanged", + FileChooser: "filechooser", + FrameAttached: "frameattached", + FrameDetached: "framedetached", + InternalFrameNavigatedToNewDocument: "internalframenavigatedtonewdocument", + LocatorHandlerTriggered: "locatorhandlertriggered", + WebSocket: "websocket", + Worker: "worker" + }; + navigationMarkSymbol = Symbol("navigationMark"); + Page = class _Page extends SdkObject { + constructor(delegate, browserContext) { + super(browserContext, "page"); + this._closedState = "open"; + this.closedPromise = new ManualPromise(); + this._initializedPromise = new ManualPromise(); + this._consoleMessages = []; + this._pageErrors = []; + this._crashed = false; + this.openScope = new LongStandingScope(); + this._emulatedMedia = {}; + this._fileChooserInterceptedBy = /* @__PURE__ */ new Set(); + this._pageBindings = /* @__PURE__ */ new Map(); + this.initScripts = []; + this._workers = /* @__PURE__ */ new Map(); + this.requestInterceptors = []; + this._locatorHandlers = /* @__PURE__ */ new Map(); + this._lastLocatorHandlerUid = 0; + this._locatorHandlerRunningCounter = 0; + this._networkRequests = []; + this.attribution.page = this; + this.delegate = delegate; + this.browserContext = browserContext; + this.keyboard = new Keyboard(delegate.rawKeyboard, this); + this.mouse = new Mouse(delegate.rawMouse, this); + this.touchscreen = new Touchscreen(delegate.rawTouchscreen, this); + this.screenshotter = new Screenshotter(this); + this.frameManager = new FrameManager(this); + this.overlay = new Overlay(this); + this.screencast = new Screencast(this); + if (delegate.pdf) + this.pdf = delegate.pdf.bind(delegate); + this.coverage = delegate.coverage ? delegate.coverage() : null; + this.isStorageStatePage = browserContext.isCreatingStorageStatePage(); + } + static { + this.Events = PageEvent; + } + async reportAsNew(opener, error) { + if (this.delegate.noUtilityWorld?.()) { + await this._addInitScript(saveGlobalsSnapshotSource); + await this.safeNonStallingEvaluateInAllFrames(saveGlobalsSnapshotSource, "main"); + } + if (opener) { + const openerPageOrError = await opener.waitForInitializedOrError(); + if (openerPageOrError instanceof _Page && !openerPageOrError.isClosed()) + this._opener = openerPageOrError; + } + this._markInitialized(error); + } + _markInitialized(error = void 0) { + if (error) { + if (this.browserContext.isClosingOrClosed()) + return; + this.frameManager.createDummyMainFrameIfNeeded(); + } + this._initialized = error || this; + this.emitOnContext(BrowserContext.Events.Page, this); + for (const pageError of this._pageErrors) + this.emitOnContext(BrowserContext.Events.PageError, pageError, this); + for (const message of this._consoleMessages) + this.emitOnContext(BrowserContext.Events.Console, message); + if (this.isClosed()) + this.emit(_Page.Events.Close); + else + this.instrumentation.onPageOpen(this); + this._initializedPromise.resolve(this._initialized); + } + initializedOrUndefined() { + return this._initialized ? this : void 0; + } + waitForInitializedOrError() { + return this._initializedPromise; + } + emitOnContext(event, ...args) { + if (this.isStorageStatePage) + return; + this.browserContext.emit(event, ...args); + } + async resetForReuse(progress2) { + await this.mainFrame().gotoImpl(progress2, "about:blank", {}); + this._emulatedSize = void 0; + this._emulatedMedia = {}; + this._extraHTTPHeaders = void 0; + await progress2.race(Promise.all([ + this.delegate.updateEmulatedViewportSize(), + this.delegate.updateEmulateMedia(), + this.delegate.updateExtraHTTPHeaders() + ])); + await this.delegate.resetForReuse(progress2); + } + _didClose() { + this.frameManager.dispose(); + this.screencast.dispose(); + this.overlay.dispose(); + assert(this._closedState !== "closed", "Page closed twice"); + this._closedState = "closed"; + this.emit(_Page.Events.Close); + this.browserContext.emit(BrowserContext.Events.PageClosed, this); + this.closedPromise.resolve(); + this.instrumentation.onPageClose(this); + this.openScope.close(new TargetClosedError(this.closeReason())); + } + _didCrash() { + this.frameManager.dispose(); + this.screencast.dispose(); + this.overlay.dispose(); + this.emit(_Page.Events.Crash); + this._crashed = true; + this.instrumentation.onPageClose(this); + this.openScope.close(new Error("Page crashed")); + } + async _onFileChooserOpened(handle) { + let multiple; + try { + multiple = await handle.evaluate((element2) => !!element2.multiple); + } catch (e) { + return; + } + if (!this.listenerCount(_Page.Events.FileChooser)) { + handle.dispose(); + return; + } + const fileChooser = new FileChooser(handle, multiple); + this.emit(_Page.Events.FileChooser, fileChooser); + } + opener() { + return this._opener; + } + mainFrame() { + return this.frameManager.mainFrame(); + } + frames() { + return this.frameManager.frames(); + } + async exposeBinding(progress2, name, playwrightBinding) { + if (this._pageBindings.has(name)) + throw new Error(`Function "${name}" has been already registered`); + if (this.browserContext._pageBindings.has(name)) + throw new Error(`Function "${name}" has been already registered in the browser context`); + await progress2.race(this.browserContext.exposePlaywrightBindingIfNeeded()); + const binding = new PageBinding(this, name, playwrightBinding); + this._pageBindings.set(name, binding); + try { + await progress2.race(this.delegate.addInitScript(binding.initScript)); + await progress2.race(this.safeNonStallingEvaluateInAllFrames(binding.initScript.source, "main")); + return binding; + } catch (error) { + this._pageBindings.delete(name); + throw error; + } + } + async removeExposedBinding(binding) { + if (this._pageBindings.get(binding.name) !== binding) + return; + this._pageBindings.delete(binding.name); + await this.delegate.removeInitScripts([binding.initScript]); + const cleanup = `{ ${binding.cleanupScript} };`; + await this.safeNonStallingEvaluateInAllFrames(cleanup, "main"); + } + async setExtraHTTPHeaders(progress2, headers) { + const oldHeaders = this._extraHTTPHeaders; + try { + this._extraHTTPHeaders = headers; + await progress2.race(this.delegate.updateExtraHTTPHeaders()); + } catch (error) { + this._extraHTTPHeaders = oldHeaders; + this.delegate.updateExtraHTTPHeaders().catch(() => { + }); + throw error; + } + } + extraHTTPHeaders() { + return this._extraHTTPHeaders; + } + addNetworkRequest(request2) { + this._networkRequests.push(request2); + ensureArrayLimit(this._networkRequests, 100); + } + networkRequests() { + return this._networkRequests; + } + async onBindingCalled(payload, context2) { + if (this._closedState === "closed") + return; + await PageBinding.dispatch(this, payload, context2); + } + addConsoleMessage(worker, type3, args, location2, text2, timestamp) { + const message = new ConsoleMessage(this, worker, type3, text2, args, location2, timestamp); + const intercepted = this.frameManager.interceptConsoleMessage(message); + if (intercepted) { + args.forEach((arg) => arg.dispose()); + return; + } + this._consoleMessages.push(message); + ensureArrayLimit(this._consoleMessages, 200); + if (this._initialized) + this.emitOnContext(BrowserContext.Events.Console, message); + } + clearConsoleMessages() { + this._consoleMessages.length = 0; + } + consoleMessages(filter) { + if (filter === "all") + return this._consoleMessages; + const marked = this._consoleMessages.findLastIndex((m) => m[navigationMarkSymbol]); + return marked === -1 ? this._consoleMessages : this._consoleMessages.slice(marked + 1); + } + addPageError(error, location2) { + const pageError = { error, location: location2 }; + this._pageErrors.push(pageError); + ensureArrayLimit(this._pageErrors, 200); + if (this._initialized) + this.emitOnContext(BrowserContext.Events.PageError, pageError, this); + } + clearPageErrors() { + this._pageErrors.length = 0; + } + pageErrors(filter) { + if (filter === "all") + return this._pageErrors.map((e) => e.error); + const marked = this._pageErrors.findLastIndex((e) => e[navigationMarkSymbol]); + return (marked === -1 ? this._pageErrors : this._pageErrors.slice(marked + 1)).map((e) => e.error); + } + async reload(progress2, options) { + return this.mainFrame().raceNavigationAction(progress2, async () => { + const [response2] = await Promise.all([ + // Reload must be a new document, and should not be confused with a stray pushState. + this.mainFrame().waitForNavigation(progress2, true, options), + progress2.race(this.delegate.reload()) + ]); + return response2; + }); + } + async goBack(progress2, options) { + return this.mainFrame().raceNavigationAction(progress2, async () => { + let error; + const waitPromise = this.mainFrame().waitForNavigation(progress2, false, options).catch((e) => { + error = e; + return null; + }); + const result2 = await progress2.race(this.delegate.goBack()); + if (!result2) { + waitPromise.catch(() => { + }); + return null; + } + const response2 = await waitPromise; + if (error) + throw error; + return response2; + }); + } + async goForward(progress2, options) { + return this.mainFrame().raceNavigationAction(progress2, async () => { + let error; + const waitPromise = this.mainFrame().waitForNavigation(progress2, false, options).catch((e) => { + error = e; + return null; + }); + const result2 = await progress2.race(this.delegate.goForward()); + if (!result2) { + waitPromise.catch(() => { + }); + return null; + } + const response2 = await waitPromise; + if (error) + throw error; + return response2; + }); + } + requestGC(progress2) { + return progress2.race(this.delegate.requestGC()); + } + registerLocatorHandler(selector, noWaitAfter) { + const uid = ++this._lastLocatorHandlerUid; + this._locatorHandlers.set(uid, { selector, noWaitAfter }); + return uid; + } + resolveLocatorHandler(uid, remove) { + const handler = this._locatorHandlers.get(uid); + if (remove) + this._locatorHandlers.delete(uid); + if (handler) { + handler.resolved?.resolve(); + handler.resolved = void 0; + } + } + unregisterLocatorHandler(uid) { + this._locatorHandlers.delete(uid); + } + async performActionPreChecks(progress2) { + await this._performWaitForNavigationCheck(progress2); + await this._performLocatorHandlersCheckpoint(progress2); + await this._performWaitForNavigationCheck(progress2); + } + async _performWaitForNavigationCheck(progress2) { + if (process.env.PLAYWRIGHT_SKIP_NAVIGATION_CHECK) + return; + const mainFrame = this.frameManager.mainFrame(); + if (!mainFrame || !mainFrame.pendingDocument()) + return; + const url2 = mainFrame.pendingDocument()?.request?.url(); + const toUrl = url2 ? `" ${trimStringWithEllipsis(url2, 200)}"` : ""; + progress2.log(` waiting for${toUrl} navigation to finish...`); + await helper.waitForEvent(progress2, mainFrame, Frame.Events.InternalNavigation, (e) => { + if (!e.isPublic) + return false; + if (!e.error) + progress2.log(` navigated to "${trimStringWithEllipsis(mainFrame.url(), 200)}"`); + return true; + }).promise; + } + async _performLocatorHandlersCheckpoint(progress2) { + if (this._locatorHandlerRunningCounter) + return; + for (const [uid, handler] of this._locatorHandlers) { + if (!handler.resolved) { + if (await this.mainFrame().isVisibleInternal(progress2, handler.selector, { strict: true })) { + handler.resolved = new ManualPromise(); + this.emit(_Page.Events.LocatorHandlerTriggered, uid); + } + } + if (handler.resolved) { + ++this._locatorHandlerRunningCounter; + progress2.log(` found ${asLocator(this.browserContext._browser.sdkLanguage(), handler.selector)}, intercepting action to run the handler`); + const promise = handler.resolved.then(async () => { + if (!handler.noWaitAfter) { + progress2.log(` locator handler has finished, waiting for ${asLocator(this.browserContext._browser.sdkLanguage(), handler.selector)} to be hidden`); + await this.mainFrame().waitForSelector(progress2, handler.selector, false, { state: "hidden" }); + } else { + progress2.log(` locator handler has finished`); + } + }); + try { + progress2.setAllowConcurrentOrNestedRaces(true); + await progress2.race(this.openScope.race(promise)); + } finally { + progress2.setAllowConcurrentOrNestedRaces(false); + --this._locatorHandlerRunningCounter; + } + progress2.log(` interception handler has finished, continuing`); + } + } + } + async emulateMedia(progress2, options) { + const oldEmulatedMedia = { ...this._emulatedMedia }; + if (options.media !== void 0) + this._emulatedMedia.media = options.media; + if (options.colorScheme !== void 0) + this._emulatedMedia.colorScheme = options.colorScheme; + if (options.reducedMotion !== void 0) + this._emulatedMedia.reducedMotion = options.reducedMotion; + if (options.forcedColors !== void 0) + this._emulatedMedia.forcedColors = options.forcedColors; + if (options.contrast !== void 0) + this._emulatedMedia.contrast = options.contrast; + try { + await progress2.race(this.delegate.updateEmulateMedia()); + } catch (error) { + this._emulatedMedia = oldEmulatedMedia; + this.delegate.updateEmulateMedia().catch(() => { + }); + throw error; + } + } + emulatedMedia() { + const contextOptions = this.browserContext._options; + return { + media: this._emulatedMedia.media || "no-override", + colorScheme: this._emulatedMedia.colorScheme !== void 0 ? this._emulatedMedia.colorScheme : contextOptions.colorScheme ?? "light", + reducedMotion: this._emulatedMedia.reducedMotion !== void 0 ? this._emulatedMedia.reducedMotion : contextOptions.reducedMotion ?? "no-preference", + forcedColors: this._emulatedMedia.forcedColors !== void 0 ? this._emulatedMedia.forcedColors : contextOptions.forcedColors ?? "none", + contrast: this._emulatedMedia.contrast !== void 0 ? this._emulatedMedia.contrast : contextOptions.contrast ?? "no-preference" + }; + } + async setViewportSize(progress2, viewportSize) { + const oldEmulatedSize = this._emulatedSize; + try { + this._setEmulatedSize({ viewport: { ...viewportSize }, screen: { ...viewportSize } }); + await progress2.race(this.delegate.updateEmulatedViewportSize()); + } catch (error) { + this._emulatedSize = oldEmulatedSize; + this.delegate.updateEmulatedViewportSize().catch(() => { + }); + throw error; + } + } + setEmulatedSizeFromWindowOpen(emulatedSize) { + this._setEmulatedSize(emulatedSize); + } + _setEmulatedSize(emulatedSize) { + this._emulatedSize = emulatedSize; + this.emit(_Page.Events.EmulatedSizeChanged); + } + emulatedSize() { + if (this._emulatedSize) + return this._emulatedSize; + const contextOptions = this.browserContext._options; + return contextOptions.viewport ? { viewport: contextOptions.viewport, screen: contextOptions.screen || contextOptions.viewport } : void 0; + } + async bringToFront(progress2) { + await progress2.race(this.delegate.bringToFront()); + } + async addInitScript(progress2, source11) { + return await progress2.race(this._addInitScript(source11)); + } + async _addInitScript(source11) { + const initScript = new InitScript(this, source11); + this.initScripts.push(initScript); + try { + await this.delegate.addInitScript(initScript); + } catch (error) { + initScript.dispose().catch(() => { + }); + throw error; + } + return initScript; + } + async removeInitScript(initScript) { + this.initScripts = this.initScripts.filter((script) => initScript !== script); + await this.delegate.removeInitScripts([initScript]); + } + needsRequestInterception() { + return this.requestInterceptors.length > 0 || this.browserContext.requestInterceptors.length > 0; + } + async addRequestInterceptor(progress2, handler, prepend) { + if (prepend) + this.requestInterceptors.unshift(handler); + else + this.requestInterceptors.push(handler); + await progress2.race(this.delegate.updateRequestInterception()); + } + async removeRequestInterceptor(handler) { + const index = this.requestInterceptors.indexOf(handler); + if (index === -1) + return; + this.requestInterceptors.splice(index, 1); + await this.browserContext.notifyRoutesInFlightAboutRemovedHandler(handler); + await this.delegate.updateRequestInterception(); + } + async expectScreenshot(progress2, options) { + const locator2 = options.locator; + const rafrafScreenshot = locator2 ? async (progress3, timeout) => { + return await locator2.frame.rafrafTimeoutScreenshotElementWithProgress(progress3, locator2.selector, timeout, options || {}); + } : async (progress3, timeout) => { + await this.performActionPreChecks(progress3); + await this.mainFrame().rafrafTimeout(progress3, timeout); + return await this.screenshotter.screenshotPage(progress3, options || {}); + }; + const comparator = getComparator("image/png"); + let intermediateResult; + const areEqualScreenshots = (actual, expected, previous) => { + const comparatorResult = actual && expected ? comparator(actual, expected, options) : void 0; + if (comparatorResult !== void 0 && !!comparatorResult === !!options.isNot) + return true; + if (comparatorResult) + intermediateResult = { errorMessage: comparatorResult.errorMessage, diff: comparatorResult.diff, actual, previous }; + return false; + }; + try { + if (!options.expected && options.isNot) + throw new Error('"not" matcher requires expected result'); + const format2 = validateScreenshotOptions(options || {}); + if (format2 !== "png") + throw new Error("Only PNG screenshots are supported"); + let actual; + let previous; + const pollIntervals = [0, 100, 250, 500]; + if (options.expected) + progress2.log(` verifying given screenshot expectation`); + else + progress2.log(` generating new stable screenshot expectation`); + let isFirstIteration = true; + while (true) { + if (this.isClosed()) + throw new Error("The page has closed"); + const screenshotTimeout = pollIntervals.shift() ?? 1e3; + if (screenshotTimeout) + progress2.log(`waiting ${screenshotTimeout}ms before taking screenshot`); + previous = actual; + actual = await rafrafScreenshot(progress2, screenshotTimeout).catch((e) => { + if (this.mainFrame().isNonRetriableError(e)) + throw e; + progress2.log(`failed to take screenshot - ` + e.message); + return void 0; + }); + if (!actual) + continue; + const expectation = options.expected && isFirstIteration ? options.expected : previous; + if (areEqualScreenshots(actual, expectation, previous)) + break; + if (intermediateResult) + progress2.log(intermediateResult.errorMessage); + isFirstIteration = false; + } + if (!isFirstIteration) + progress2.log(`captured a stable screenshot`); + if (!options.expected) + return { actual }; + if (isFirstIteration) { + progress2.log(`screenshot matched expectation`); + return {}; + } + if (areEqualScreenshots(actual, options.expected, void 0)) { + progress2.log(`screenshot matched expectation`); + return {}; + } + throw new Error(intermediateResult.errorMessage); + } catch (e) { + if (isJavaScriptErrorInEvaluate(e) || isInvalidSelectorError(e)) + throw e; + let errorMessage = e.message; + if (e instanceof TimeoutError && intermediateResult?.previous) + errorMessage = `Failed to take two consecutive stable screenshots.`; + const details = { + log: compressCallLog(e.message ? [...progress2.metadata.log, e.message] : progress2.metadata.log), + actual: intermediateResult?.actual, + previous: intermediateResult?.previous, + diff: intermediateResult?.diff, + timedOut: e instanceof TimeoutError, + customErrorMessage: errorMessage + }; + const error = new Error("Expect failed"); + error.details = details; + throw error; + } + } + async screenshot(progress2, options) { + return await this.screenshotter.screenshotPage(progress2, options); + } + async close(progress2, options = {}) { + await progress2.race(this._close(options)); + } + async _close(options = {}) { + if (this._closedState === "closed") + return; + if (options.reason) + this._closeReason = options.reason; + await this.screencast.handlePageOrContextClose(); + if (this._closedState !== "closing") { + this._closedState = "closing"; + await this.delegate.closePage(false).catch((e) => debugLogger.log("error", e)); + } + await this.closedPromise; + } + async runBeforeUnload(progress2) { + await progress2.race(this._runBeforeUnload()); + } + async _runBeforeUnload() { + await this.delegate.closePage(true).catch((e) => debugLogger.log("error", e)); + } + isClosed() { + return this._closedState === "closed"; + } + hasCrashed() { + return this._crashed; + } + isClosedOrClosingOrCrashed() { + return this._closedState !== "open" || this._crashed; + } + addWorker(workerId, worker) { + this._workers.set(workerId, worker); + this.emit(_Page.Events.Worker, worker); + } + removeWorker(workerId) { + const worker = this._workers.get(workerId); + if (!worker) + return; + worker.didClose(); + this._workers.delete(workerId); + } + clearWorkers() { + for (const [workerId, worker] of this._workers) { + worker.didClose(); + this._workers.delete(workerId); + } + } + async setFileChooserInterceptedBy(progress2, enabled, by) { + await progress2.race(this._setFileChooserInterceptedBy(enabled, by)); + } + async _setFileChooserInterceptedBy(enabled, by) { + const wasIntercepted = this.fileChooserIntercepted(); + if (enabled) + this._fileChooserInterceptedBy.add(by); + else + this._fileChooserInterceptedBy.delete(by); + if (wasIntercepted !== this.fileChooserIntercepted()) + await this.delegate.updateFileChooserInterception(); + } + fileChooserIntercepted() { + return this._fileChooserInterceptedBy.size > 0; + } + frameNavigatedToNewDocument(frame) { + this.emit(_Page.Events.InternalFrameNavigatedToNewDocument, frame); + this.browserContext.emit(BrowserContext.Events.InternalFrameNavigatedToNewDocument, frame); + const origin = frame.origin(); + if (origin) + this.browserContext.addVisitedOrigin(origin); + if (frame === this.mainFrame()) { + if (this._consoleMessages.length > 0) + this._consoleMessages[this._consoleMessages.length - 1][navigationMarkSymbol] = true; + if (this._pageErrors.length > 0) + this._pageErrors[this._pageErrors.length - 1][navigationMarkSymbol] = true; + } + } + allInitScripts() { + const bindings = [...this.browserContext._pageBindings.values(), ...this._pageBindings.values()].map((binding) => binding.initScript); + if (this.browserContext.bindingsInitScript) + bindings.unshift(this.browserContext.bindingsInitScript); + return [...bindings, ...this.browserContext.initScripts, ...this.initScripts]; + } + getBinding(name) { + return this._pageBindings.get(name) || this.browserContext._pageBindings.get(name); + } + async safeNonStallingEvaluateInAllFrames(expression2, world, options = {}) { + await Promise.all(this.frames().map(async (frame) => { + try { + await frame.nonStallingEvaluateInExistingContext(expression2, world); + } catch (e) { + if (options.throwOnJSErrors && isJavaScriptErrorInEvaluate(e)) + throw e; + } + })); + } + async hideHighlight() { + await Promise.all(this.frames().map((frame) => frame.hideHighlight().catch(() => { + }))); + } + async setDockTile(image) { + await this.delegate.setDockTile(image); + } + async webStorageItems(progress2, kind) { + const storage = `${kind}Storage`; + return await this.mainFrame().evaluateExpression(progress2, `(() => { + const result = []; + for (let i = 0; i < ${storage}.length; i++) { + const name = ${storage}.key(i); + if (name !== null) + result.push({ name, value: ${storage}.getItem(name) ?? '' }); + } + return result; + })()`, { world: "utility" }); + } + async webStorageGetItem(progress2, kind, name) { + const value2 = await this.mainFrame().evaluateExpression(progress2, `${kind}Storage.getItem(${JSON.stringify(name)})`, { world: "utility" }); + return value2 === null ? void 0 : value2; + } + async webStorageSetItem(progress2, kind, name, value2) { + await this.mainFrame().evaluateExpression(progress2, `${kind}Storage.setItem(${JSON.stringify(name)}, ${JSON.stringify(value2)})`, { world: "utility" }); + } + async webStorageRemoveItem(progress2, kind, name) { + await this.mainFrame().evaluateExpression(progress2, `${kind}Storage.removeItem(${JSON.stringify(name)})`, { world: "utility" }); + } + async webStorageClear(progress2, kind) { + await this.mainFrame().evaluateExpression(progress2, `${kind}Storage.clear()`, { world: "utility" }); + } + }; + WorkerEvent = { + Console: "console", + Close: "close" + }; + Worker = class _Worker extends SdkObject { + constructor(parent, url2, onDisconnect) { + super(parent, "worker"); + this._executionContextPromise = new ManualPromise(); + this._workerScriptLoaded = false; + this.existingExecutionContext = null; + this.openScope = new LongStandingScope(); + this.attribution.worker = this; + this.url = url2; + this._onDisconnect = onDisconnect; + } + static { + this.Events = WorkerEvent; + } + createExecutionContext(delegate) { + this.existingExecutionContext = new ExecutionContext(this, delegate, "worker"); + if (this._workerScriptLoaded) + this._executionContextPromise.resolve(this.existingExecutionContext); + return this.existingExecutionContext; + } + destroyExecutionContext(errorMessage) { + if (this.existingExecutionContext) + this.existingExecutionContext.contextDestroyed(errorMessage); + this.existingExecutionContext = null; + this._workerScriptLoaded = false; + this._executionContextPromise = new ManualPromise(); + } + workerScriptLoaded() { + this._workerScriptLoaded = true; + if (this.existingExecutionContext) + this._executionContextPromise.resolve(this.existingExecutionContext); + } + didClose() { + if (this.existingExecutionContext) + this.existingExecutionContext.contextDestroyed("Worker was closed"); + this.emit(_Worker.Events.Close, this); + this.openScope.close(new Error("Worker closed")); + } + async evaluateExpression(progress2, expression2, isFunction2, arg) { + return progress2.race(evaluateExpression(await this._executionContextPromise, expression2, { returnByValue: true, isFunction: isFunction2 }, arg)); + } + async evaluateExpressionHandle(progress2, expression2, isFunction2, arg) { + return progress2.race(evaluateExpression(await this._executionContextPromise, expression2, { returnByValue: false, isFunction: isFunction2 }, arg)); + } + async disconnect(progress2, options = {}) { + if (!this._onDisconnect) + throw new Error("Cannot disconnect from this worker"); + this._closeReason = options.reason; + await progress2.race(this._onDisconnect()); + } + }; + PageBinding = class _PageBinding extends DisposableObject { + static { + this.kController = "__playwright__binding__controller__"; + } + static { + this.kBindingName = "__playwright__binding__"; + } + static createInitScript(browserContext) { + return new InitScript(browserContext, ` + (() => { + const module = {}; + ${source5} + const property = '${_PageBinding.kController}'; + if (!globalThis[property]) + globalThis[property] = new (module.exports.BindingsController())(globalThis, '${_PageBinding.kBindingName}'); + })(); + `); + } + constructor(parent, name, playwrightFunction) { + super(parent); + this.name = name; + this.playwrightFunction = playwrightFunction; + this.initScript = new InitScript(parent, `globalThis['${_PageBinding.kController}'].addBinding(${JSON.stringify(name)})`); + this.cleanupScript = `globalThis['${_PageBinding.kController}'].removeBinding(${JSON.stringify(name)})`; + } + static async dispatch(page, payload, context2) { + const { name, seq, serializedArgs } = JSON.parse(payload); + try { + assert(context2.world); + const binding = page.getBinding(name); + if (!binding) + throw new Error(`Function "${name}" is not exposed`); + if (!Array.isArray(serializedArgs)) + throw new Error(`serializedArgs is not an array. This can happen when Array.prototype.toJSON is defined incorrectly`); + const args = serializedArgs.map((a) => parseEvaluationResultValue(a)); + const result2 = await binding.playwrightFunction({ frame: context2.frame, page, context: page.browserContext }, ...args); + context2.evaluateExpressionHandle(`arg => globalThis['${_PageBinding.kController}'].deliverBindingResult(arg)`, { isFunction: true }, { name, seq, result: result2 }).catch((e) => debugLogger.log("error", e)); + } catch (error) { + context2.evaluateExpressionHandle(`arg => globalThis['${_PageBinding.kController}'].deliverBindingResult(arg)`, { isFunction: true }, { name, seq, error }).catch((e) => debugLogger.log("error", e)); + } + } + async dispose() { + await this.parent.removeExposedBinding(this); + } + }; + InitScript = class extends DisposableObject { + constructor(owner, source11) { + super(owner); + this.source = `(() => { + ${source11} + })();`; + } + async dispose() { + await this.parent.removeInitScript(this); + } + }; + } +}); + +// packages/playwright-core/src/server/types.ts +var kLifecycleEvents; +var init_types = __esm({ + "packages/playwright-core/src/server/types.ts"() { + "use strict"; + kLifecycleEvents = /* @__PURE__ */ new Set(["load", "domcontentloaded", "networkidle", "commit"]); + } +}); + +// packages/playwright-core/src/server/frames.ts +function verifyLifecycle(name, waitUntil) { + if (waitUntil === "networkidle0") + waitUntil = "networkidle"; + if (!kLifecycleEvents.has(waitUntil)) + throw new Error(`${name}: expected one of (load|domcontentloaded|networkidle|commit)`); + return waitUntil; +} +function renderUnexpectedValue(expression2, received) { + if (expression2 === "to.match.aria") + return received ? received.raw : received; + return received; +} +var yaml, NavigationAbortedError, ExpectError, kDummyFrameId, FrameManager, FrameEvent, Frame, SignalBarrier; +var init_frames = __esm({ + "packages/playwright-core/src/server/frames.ts"() { + "use strict"; + init_ariaSnapshot(); + init_selectorParser(); + init_manualPromise(); + init_eventsHelper(); + init_manualPromise(); + init_locatorGenerators(); + init_assert(); + init_urlMatch(); + init_task(); + init_crypto(); + init_browserContext(); + init_dom(); + init_errors(); + init_fileUploadUtils(); + init_frameSelectors(); + init_helper(); + init_instrumentation(); + init_javascript(); + init_network2(); + init_page(); + init_progress(); + init_types(); + init_protocolError(); + yaml = require("./utilsBundle").yaml; + NavigationAbortedError = class extends Error { + constructor(documentId, message) { + super(message); + this.documentId = documentId; + } + }; + ExpectError = class extends Error { + constructor(details) { + super("Expect failed"); + this.name = "ExpectError"; + this.details = details; + } + }; + kDummyFrameId = ""; + FrameManager = class { + constructor(page) { + this._frames = /* @__PURE__ */ new Map(); + this._consoleMessageTags = /* @__PURE__ */ new Map(); + this._signalBarriers = /* @__PURE__ */ new Set(); + this._webSockets = /* @__PURE__ */ new Map(); + this._nextFrameSeq = 0; + this._page = page; + this._mainFrame = void 0; + } + _allocateFrameSeq() { + return this._nextFrameSeq++; + } + createDummyMainFrameIfNeeded() { + if (!this._mainFrame) + this.frameAttached(kDummyFrameId, null); + } + dispose() { + for (const frame of this._frames.values()) { + frame._stopNetworkIdleTimer(); + frame.invalidateNonStallingEvaluations("Target crashed"); + } + } + mainFrame() { + return this._mainFrame; + } + frames() { + const frames = []; + collect(this._mainFrame); + return frames; + function collect(frame) { + frames.push(frame); + for (const subframe of frame._getChildFrames()) + collect(subframe); + } + } + frame(frameId) { + return this._frames.get(frameId) || null; + } + frameAttached(frameId, parentFrameId) { + const parentFrame = parentFrameId ? this._frames.get(parentFrameId) : null; + if (!parentFrame) { + if (this._mainFrame) { + this._frames.delete(this._mainFrame._id); + this._mainFrame._id = frameId; + } else { + assert(!this._frames.has(frameId)); + this._mainFrame = new Frame(this._page, frameId, parentFrame); + } + this._frames.set(frameId, this._mainFrame); + return this._mainFrame; + } else { + assert(!this._frames.has(frameId)); + const frame = new Frame(this._page, frameId, parentFrame); + this._frames.set(frameId, frame); + this._page.emit(Page.Events.FrameAttached, frame); + this._page.browserContext.emit(BrowserContext.Events.FrameAttached, frame); + return frame; + } + } + async waitForSignalsCreatedBy(progress2, waitAfter, action) { + if (!waitAfter) + return action(progress2); + const barrier = new SignalBarrier(progress2); + this._signalBarriers.add(barrier); + try { + const result2 = await action(progress2); + await progress2.race(this._page.delegate.inputActionEpilogue()); + await barrier.waitFor(progress2); + await new Promise(makeWaitForNextTask()); + return result2; + } finally { + this._signalBarriers.delete(barrier); + } + } + frameWillPotentiallyRequestNavigation() { + for (const barrier of this._signalBarriers) + barrier.retain(); + } + frameDidPotentiallyRequestNavigation() { + for (const barrier of this._signalBarriers) + barrier.release(); + } + frameRequestedNavigation(frameId, documentId) { + const frame = this._frames.get(frameId); + if (!frame) + return; + for (const barrier of this._signalBarriers) + barrier.addFrameNavigation(frame); + if (frame.pendingDocument() && frame.pendingDocument().documentId === documentId) { + return; + } + const request2 = documentId ? Array.from(frame._inflightRequests).find((request3) => request3._documentId === documentId) : void 0; + frame._setPendingDocument({ documentId, request: request2 }); + } + frameCommittedNewDocumentNavigation(frameId, url2, name, documentId, initial) { + const frame = this._frames.get(frameId); + this.removeChildFramesRecursively(frame); + this._clearWebSockets(frame); + frame._url = url2; + frame._name = name; + let keepPending; + const pendingDocument = frame.pendingDocument(); + if (pendingDocument) { + if (pendingDocument.documentId === void 0) { + pendingDocument.documentId = documentId; + } + if (pendingDocument.documentId === documentId) { + frame._currentDocument = pendingDocument; + } else { + keepPending = pendingDocument; + frame._currentDocument = { documentId, request: void 0 }; + } + frame._setPendingDocument(void 0); + } else { + frame._currentDocument = { documentId, request: void 0 }; + } + frame._onClearLifecycle(); + const navigationEvent = { url: url2, name, newDocument: frame._currentDocument, isPublic: true }; + this._fireInternalFrameNavigation(frame, navigationEvent); + if (!initial) { + frame.apiLog(` navigated to "${url2}"`); + this._page.frameNavigatedToNewDocument(frame); + } + frame._setPendingDocument(keepPending); + } + frameCommittedSameDocumentNavigation(frameId, url2) { + const frame = this._frames.get(frameId); + if (!frame) + return; + const pending = frame.pendingDocument(); + if (pending && pending.documentId === void 0 && pending.request === void 0) { + frame._setPendingDocument(void 0); + } + frame._url = url2; + const navigationEvent = { url: url2, name: frame._name, isPublic: true }; + this._fireInternalFrameNavigation(frame, navigationEvent); + frame.apiLog(` navigated to "${url2}"`); + } + frameAbortedNavigation(frameId, errorText, documentId) { + const frame = this._frames.get(frameId); + if (!frame || !frame.pendingDocument()) + return; + if (documentId !== void 0 && frame.pendingDocument().documentId !== documentId) + return; + const navigationEvent = { + url: frame._url, + name: frame._name, + newDocument: frame.pendingDocument(), + error: new NavigationAbortedError(documentId, errorText), + isPublic: !(documentId && frame._redirectedNavigations.has(documentId)) + }; + frame._setPendingDocument(void 0); + this._fireInternalFrameNavigation(frame, navigationEvent); + } + frameDetached(frameId) { + const frame = this._frames.get(frameId); + if (frame) { + this._removeFramesRecursively(frame); + this._page.mainFrame()._recalculateNetworkIdle(); + } + } + frameLifecycleEvent(frameId, event) { + const frame = this._frames.get(frameId); + if (frame) + frame.onLifecycleEvent(event); + } + requestStarted(request2, route2) { + const frame = request2.frame(); + this._inflightRequestStarted(request2); + if (request2._documentId) + frame._setPendingDocument({ documentId: request2._documentId, request: request2 }); + if (request2._isFavicon) { + route2?.abort("aborted").catch(() => { + }); + return; + } + this._page.addNetworkRequest(request2); + this._page.emitOnContext(BrowserContext.Events.Request, request2); + if (route2) + new Route(request2, route2).handle([...this._page.requestInterceptors, ...this._page.browserContext.requestInterceptors]); + } + requestReceivedResponse(response2) { + if (response2.request()._isFavicon) + return; + this._page.emitOnContext(BrowserContext.Events.Response, response2); + } + reportRequestFinished(request2, response2) { + this._inflightRequestFinished(request2); + if (request2._isFavicon) + return; + this._page.emitOnContext(BrowserContext.Events.RequestFinished, { request: request2, response: response2 }); + } + requestFailed(request2, canceled) { + const frame = request2.frame(); + this._inflightRequestFinished(request2); + if (frame.pendingDocument() && frame.pendingDocument().request === request2) { + let errorText = request2.failure().errorText; + if (canceled) + errorText += "; maybe frame was detached?"; + this.frameAbortedNavigation(frame._id, errorText, frame.pendingDocument().documentId); + } + if (request2._isFavicon) + return; + this._page.emitOnContext(BrowserContext.Events.RequestFailed, request2); + } + removeChildFramesRecursively(frame) { + for (const child of frame._getChildFrames()) + this._removeFramesRecursively(child); + } + _removeFramesRecursively(frame) { + this.removeChildFramesRecursively(frame); + frame._onDetached(); + this._frames.delete(frame._id); + if (!this._page.isClosed()) + this._page.emit(Page.Events.FrameDetached, frame); + } + _inflightRequestFinished(request2) { + const frame = request2.frame(); + if (request2._isFavicon) + return; + if (!frame._inflightRequests.has(request2)) + return; + frame._inflightRequests.delete(request2); + if (frame._inflightRequests.size === 0) + frame._startNetworkIdleTimer(); + } + _inflightRequestStarted(request2) { + const frame = request2.frame(); + if (request2._isFavicon) + return; + frame._inflightRequests.add(request2); + if (frame._inflightRequests.size === 1) + frame._stopNetworkIdleTimer(); + } + interceptConsoleMessage(message) { + if (message.type() !== "debug") + return false; + const tag = message.text(); + const handler = this._consoleMessageTags.get(tag); + if (!handler) + return false; + this._consoleMessageTags.delete(tag); + handler(); + return true; + } + _clearWebSockets(frame) { + if (frame.parentFrame()) + return; + this._webSockets.clear(); + } + onWebSocketCreated(requestId, url2) { + const ws4 = new WebSocket(this._page, url2); + this._webSockets.set(requestId, ws4); + } + onWebSocketRequest(requestId, headers, wallTimeMs) { + const ws4 = this._webSockets.get(requestId); + if (!ws4) + return; + ws4.setWallTimeMs(wallTimeMs); + if (ws4.markAsNotified()) { + this._page.emit(Page.Events.WebSocket, ws4); + this._page.browserContext.emit(BrowserContext.Events.WebSocket, ws4, this._page); + } + ws4.requestSent(headers); + } + onWebSocketResponse(requestId, status, statusText2, headers) { + const ws4 = this._webSockets.get(requestId); + if (!ws4) + return; + ws4.responseReceived(status, statusText2, headers); + if (status >= 400) + ws4.error(`${statusText2}: ${status}`); + } + onWebSocketFrameSent(requestId, opcode, data, wallTimeMs) { + const ws4 = this._webSockets.get(requestId); + if (ws4) + ws4.frameSent(opcode, data, wallTimeMs); + } + webSocketFrameReceived(requestId, opcode, data, wallTimeMs) { + const ws4 = this._webSockets.get(requestId); + if (ws4) + ws4.frameReceived(opcode, data, wallTimeMs); + } + webSocketClosed(requestId) { + const ws4 = this._webSockets.get(requestId); + if (ws4) { + if (ws4.markAsNotified()) { + this._page.emit(Page.Events.WebSocket, ws4); + this._page.browserContext.emit(BrowserContext.Events.WebSocket, ws4, this._page); + } + ws4.closed(); + } + this._webSockets.delete(requestId); + } + webSocketError(requestId, errorMessage) { + const ws4 = this._webSockets.get(requestId); + if (ws4) { + if (ws4.markAsNotified()) { + this._page.emit(Page.Events.WebSocket, ws4); + this._page.browserContext.emit(BrowserContext.Events.WebSocket, ws4, this._page); + } + ws4.error(errorMessage); + } + } + _fireInternalFrameNavigation(frame, event) { + frame.emit(Frame.Events.InternalNavigation, event); + } + }; + FrameEvent = { + InternalNavigation: "internalnavigation", + AddLifecycle: "addlifecycle", + RemoveLifecycle: "removelifecycle" + }; + Frame = class _Frame extends SdkObject { + constructor(page, id, parentFrame) { + super(page, "frame"); + this._firedLifecycleEvents = /* @__PURE__ */ new Set(); + this._firedNetworkIdleSelf = false; + this._url = ""; + this._contextData = /* @__PURE__ */ new Map(); + this._childFrames = /* @__PURE__ */ new Set(); + this._name = ""; + this._inflightRequests = /* @__PURE__ */ new Set(); + this._detachedScope = new LongStandingScope(); + this._raceAgainstEvaluationStallingEventsPromises = /* @__PURE__ */ new Set(); + this._redirectedNavigations = /* @__PURE__ */ new Map(); + this.attribution.frame = this; + this.seq = page.frameManager._allocateFrameSeq(); + this._id = id; + this._page = page; + this._parentFrame = parentFrame; + this._currentDocument = { documentId: void 0, request: void 0 }; + this.selectors = new FrameSelectors(this); + this._contextData.set("main", { contextPromise: new ManualPromise(), context: null }); + this._contextData.set("utility", { contextPromise: new ManualPromise(), context: null }); + this._setContext("main", null); + this._setContext("utility", null); + if (this._parentFrame) + this._parentFrame._childFrames.add(this); + this._firedLifecycleEvents.add("commit"); + if (id !== kDummyFrameId) + this._startNetworkIdleTimer(); + } + static { + this.Events = FrameEvent; + } + _isDetached() { + return this._detachedScope.isClosed(); + } + onLifecycleEvent(event) { + if (this._firedLifecycleEvents.has(event)) + return; + this._firedLifecycleEvents.add(event); + this.emit(_Frame.Events.AddLifecycle, event); + if (this === this._page.mainFrame() && this._url !== "about:blank") + this.apiLog(` "${event}" event fired`); + this._page.mainFrame()._recalculateNetworkIdle(); + } + _onClearLifecycle() { + for (const event of this._firedLifecycleEvents) + this.emit(_Frame.Events.RemoveLifecycle, event); + this._firedLifecycleEvents.clear(); + this._inflightRequests = new Set(Array.from(this._inflightRequests).filter((request2) => request2 === this._currentDocument.request)); + this._stopNetworkIdleTimer(); + if (this._inflightRequests.size === 0) + this._startNetworkIdleTimer(); + this._page.mainFrame()._recalculateNetworkIdle(this); + this.onLifecycleEvent("commit"); + } + _setPendingDocument(documentInfo) { + this._pendingDocument = documentInfo; + if (documentInfo) + this.invalidateNonStallingEvaluations("Navigation interrupted the evaluation"); + } + pendingDocument() { + return this._pendingDocument; + } + invalidateNonStallingEvaluations(message) { + if (!this._raceAgainstEvaluationStallingEventsPromises.size) + return; + const error = new Error(message); + for (const promise of this._raceAgainstEvaluationStallingEventsPromises) + promise.reject(error); + } + async raceAgainstEvaluationStallingEvents(cb) { + if (this._pendingDocument) + throw new Error("Frame is currently attempting a navigation"); + if (this._page.browserContext.dialogManager.hasOpenDialogsForPage(this._page)) + throw new Error("Open JavaScript dialog prevents evaluation"); + const promise = new ManualPromise(); + this._raceAgainstEvaluationStallingEventsPromises.add(promise); + try { + return await Promise.race([ + cb(), + promise + ]); + } finally { + this._raceAgainstEvaluationStallingEventsPromises.delete(promise); + } + } + nonStallingRawEvaluateInExistingMainContext(expression2) { + return this.raceAgainstEvaluationStallingEvents(() => { + const context2 = this._existingMainContext(); + if (!context2) + throw new Error("Frame does not yet have a main execution context"); + return context2.rawEvaluateJSON(expression2); + }); + } + nonStallingEvaluateInExistingContext(expression2, world) { + return this.raceAgainstEvaluationStallingEvents(() => { + const context2 = this._contextData.get(world)?.context; + if (!context2) + throw new Error("Frame does not yet have the execution context"); + return context2.evaluateExpression(expression2, { isFunction: false }); + }); + } + _recalculateNetworkIdle(frameThatAllowsRemovingNetworkIdle) { + let isNetworkIdle = this._firedNetworkIdleSelf; + for (const child of this._childFrames) { + child._recalculateNetworkIdle(frameThatAllowsRemovingNetworkIdle); + if (!child._firedLifecycleEvents.has("networkidle")) + isNetworkIdle = false; + } + if (isNetworkIdle && !this._firedLifecycleEvents.has("networkidle")) { + this._firedLifecycleEvents.add("networkidle"); + this.emit(_Frame.Events.AddLifecycle, "networkidle"); + if (this === this._page.mainFrame() && this._url !== "about:blank") + this.apiLog(` "networkidle" event fired`); + } + if (frameThatAllowsRemovingNetworkIdle !== this && this._firedLifecycleEvents.has("networkidle") && !isNetworkIdle) { + this._firedLifecycleEvents.delete("networkidle"); + this.emit(_Frame.Events.RemoveLifecycle, "networkidle"); + } + } + async raceNavigationAction(progress2, action) { + progress2.setAllowConcurrentOrNestedRaces(true); + const result2 = await progress2.race(LongStandingScope.raceMultiple([ + this._detachedScope, + this._page.openScope + ], action().catch((e) => { + if (e instanceof NavigationAbortedError && e.documentId) { + const data = this._redirectedNavigations.get(e.documentId); + if (data) { + progress2.log(`waiting for redirected navigation to "${data.url}"`); + return progress2.race(data.gotoPromise); + } + } + throw e; + }))); + progress2.setAllowConcurrentOrNestedRaces(false); + return result2; + } + redirectNavigation(url2, documentId, referer) { + const controller = new ProgressController(); + const data = { + url: url2, + gotoPromise: controller.run((progress2) => this.gotoImpl(progress2, url2, { referer }), 0) + }; + this._redirectedNavigations.set(documentId, data); + data.gotoPromise.finally(() => this._redirectedNavigations.delete(documentId)); + } + async goto(progress2, url2, options = {}) { + const constructedNavigationURL = constructURLBasedOnBaseURL(this._page.browserContext._options.baseURL, url2); + return this.raceNavigationAction(progress2, async () => this.gotoImpl(progress2, constructedNavigationURL, options)); + } + async gotoImpl(progress2, url2, options) { + const waitUntil = verifyLifecycle("waitUntil", options.waitUntil === void 0 ? "load" : options.waitUntil); + progress2.log(`navigating to "${url2}", waiting until "${waitUntil}"`); + const headers = this._page.extraHTTPHeaders() || []; + const refererHeader = headers.find((h) => h.name.toLowerCase() === "referer"); + let referer = refererHeader ? refererHeader.value : void 0; + if (options.referer !== void 0) { + if (referer !== void 0 && referer !== options.referer) + throw new Error('"referer" is already specified as extra HTTP header'); + referer = options.referer; + } + url2 = helper.completeUserURL(url2); + const navigationEvents = []; + const collectNavigations = (arg) => navigationEvents.push(arg); + this.on(_Frame.Events.InternalNavigation, collectNavigations); + let navigateResult; + try { + navigateResult = await progress2.race(this._page.delegate.navigateFrame(this, url2, referer)); + } finally { + this.off(_Frame.Events.InternalNavigation, collectNavigations); + } + let event; + if (navigateResult.newDocumentId) { + const predicate = (event2) => { + return event2.newDocument && (event2.newDocument.documentId === navigateResult.newDocumentId || !event2.error); + }; + const events = navigationEvents.filter(predicate); + if (events.length) + event = events[0]; + else + event = await helper.waitForEvent(progress2, this, _Frame.Events.InternalNavigation, predicate).promise; + if (event.newDocument.documentId !== navigateResult.newDocumentId) { + throw new NavigationAbortedError(navigateResult.newDocumentId, `Navigation to "${url2}" is interrupted by another navigation to "${event.url}"`); + } + if (event.error) + throw event.error; + } else { + const predicate = (e) => !e.newDocument; + const events = navigationEvents.filter(predicate); + if (events.length) + event = events[0]; + else + event = await helper.waitForEvent(progress2, this, _Frame.Events.InternalNavigation, predicate).promise; + } + if (!this._firedLifecycleEvents.has(waitUntil)) + await helper.waitForEvent(progress2, this, _Frame.Events.AddLifecycle, (e) => e === waitUntil).promise; + const request2 = event.newDocument ? event.newDocument.request : void 0; + const response2 = request2 ? await request2._finalRequest().response(progress2) : null; + return response2; + } + async waitForNavigation(progress2, requiresNewDocument, options) { + const waitUntil = verifyLifecycle("waitUntil", options.waitUntil === void 0 ? "load" : options.waitUntil); + progress2.log(`waiting for navigation until "${waitUntil}"`); + const navigationEvent = await helper.waitForEvent(progress2, this, _Frame.Events.InternalNavigation, (event) => { + if (event.error) + return true; + if (requiresNewDocument && !event.newDocument) + return false; + progress2.log(` navigated to "${this._url}"`); + return true; + }).promise; + if (navigationEvent.error) + throw navigationEvent.error; + if (!this._firedLifecycleEvents.has(waitUntil)) + await helper.waitForEvent(progress2, this, _Frame.Events.AddLifecycle, (e) => e === waitUntil).promise; + const request2 = navigationEvent.newDocument ? navigationEvent.newDocument.request : void 0; + return request2 ? request2._finalRequest().response(progress2) : null; + } + async waitForLoadState(progress2, state) { + const waitUntil = verifyLifecycle("state", state); + if (!this._firedLifecycleEvents.has(waitUntil)) + await helper.waitForEvent(progress2, this, _Frame.Events.AddLifecycle, (e) => e === waitUntil).promise; + } + async frameElement(progress2) { + return await progress2.race(this._page.delegate.getFrameElement(this)); + } + context(world) { + if (this._page.delegate.noUtilityWorld?.()) + world = "main"; + return this._contextData.get(world).contextPromise.then((contextOrDestroyedReason) => { + if (contextOrDestroyedReason instanceof ExecutionContext) + return contextOrDestroyedReason; + throw new Error(contextOrDestroyedReason.destroyedReason); + }); + } + mainContext() { + return this.context("main"); + } + _existingMainContext() { + return this._contextData.get("main")?.context || null; + } + utilityContext() { + return this.context("utility"); + } + async evaluateExpression(progress2, expression2, options = {}, arg) { + return await progress2.race(this._evaluateExpression(expression2, options, arg)); + } + async _evaluateExpression(expression2, options = {}, arg) { + const context2 = await this.context(options.world ?? "main"); + const value2 = await context2.evaluateExpression(expression2, options, arg); + return value2; + } + async evaluateExpressionHandle(progress2, expression2, options = {}, arg) { + return await progress2.race(this._evaluateExpressionHandle(expression2, options, arg)); + } + async _evaluateExpressionHandle(expression2, options = {}, arg) { + const context2 = await this.context(options.world ?? "main"); + const value2 = await context2.evaluateExpressionHandle(expression2, options, arg); + return value2; + } + async querySelector(progress2, selector, options) { + this.apiLog(` finding element using the selector "${selector}"`); + return progress2.race(this.selectors.query(selector, options)); + } + async waitForSelector(progress2, selector, performActionPreChecksAndLog, options, scope) { + if (options.visibility) + throw new Error("options.visibility is not supported, did you mean options.state?"); + if (options.waitFor && options.waitFor !== "visible") + throw new Error("options.waitFor is not supported, did you mean options.state?"); + const { state = "visible" } = options; + if (!["attached", "detached", "visible", "hidden"].includes(state)) + throw new Error(`state: expected one of (attached|detached|visible|hidden)`); + if (performActionPreChecksAndLog) + progress2.log(`waiting for ${this._asLocator(selector)}${state === "attached" ? "" : " to be " + state}`); + const promise = this.retryWithProgressAndBackoff(progress2, async (progress3, continuePolling) => { + if (performActionPreChecksAndLog) + await this._page.performActionPreChecks(progress3); + const resolved = await progress3.race(this.selectors.resolveInjectedForSelector(selector, options, scope)); + if (!resolved) { + if (state === "hidden" || state === "detached") + return null; + return continuePolling; + } + const result2 = await progress3.race(resolved.injected.evaluateHandle((injected, { info, root }) => { + if (root && !root.isConnected) + throw injected.createStacklessError("Element is not attached to the DOM"); + const elements = injected.querySelectorAll(info.parsed, root || document); + const element3 = elements[0]; + const visible2 = element3 ? injected.utils.isElementVisible(element3) : false; + let log3 = ""; + if (elements.length > 1) { + if (info.strict) + throw injected.strictModeViolationError(info.parsed, elements); + log3 = ` locator resolved to ${elements.length} elements. Proceeding with the first one: ${injected.previewNode(elements[0])}`; + } else if (element3) { + log3 = ` locator resolved to ${visible2 ? "visible" : "hidden"} ${injected.previewNode(element3)}`; + } + injected.checkDeprecatedSelectorUsage(info.parsed, elements); + return { log: log3, element: element3, visible: visible2, attached: !!element3 }; + }, { info: resolved.info, root: resolved.frame === this ? scope : void 0 })); + const { log: log2, visible, attached } = await progress3.race(result2.evaluate((r) => ({ log: r.log, visible: r.visible, attached: r.attached }))); + if (log2) + progress3.log(log2); + const success = { attached, detached: !attached, visible, hidden: !visible }[state]; + if (!success) { + result2.dispose(); + return continuePolling; + } + if (options.omitReturnValue) { + result2.dispose(); + return null; + } + const element2 = state === "attached" || state === "visible" ? await progress3.race(result2.evaluateHandle((r) => r.element)) : null; + result2.dispose(); + if (!element2) + return null; + if (options.__testHookBeforeAdoptNode) + await progress3.race(options.__testHookBeforeAdoptNode()); + try { + const mainContext = await progress3.race(resolved.frame.mainContext()); + return await progress3.race(element2._adoptTo(mainContext)); + } catch (e) { + return continuePolling; + } + }); + return scope ? scope._context.raceAgainstContextDestroyed(promise) : promise; + } + async dispatchEvent(progress2, selector, type3, eventInit = {}, options, scope) { + await this._callOnElementOnceMatches(progress2, selector, (injectedScript, element2, data) => { + injectedScript.dispatchEvent(element2, data.type, data.eventInit); + }, { type: type3, eventInit }, { mainWorld: true, ...options }, scope); + } + async evalOnSelector(progress2, selector, strict, expression2, isFunction2, arg, scope) { + return progress2.race(this._evalOnSelector(selector, strict, expression2, isFunction2, arg, scope)); + } + async _evalOnSelector(selector, strict, expression2, isFunction2, arg, scope) { + const handle = await this.selectors.query(selector, { strict }, scope); + if (!handle) + throw new Error(`Failed to find element matching selector "${selector}"`); + const result2 = await handle.internalEvaluateExpression(expression2, { isFunction: isFunction2 }, arg); + handle.dispose(); + return result2; + } + async evalOnSelectorAll(progress2, selector, expression2, isFunction2, arg, scope) { + return progress2.race(this._evalOnSelectorAll(selector, expression2, isFunction2, arg, scope)); + } + async _evalOnSelectorAll(selector, expression2, isFunction2, arg, scope) { + const arrayHandle = await this.selectors.queryArrayInMainWorld(selector, scope); + const result2 = await arrayHandle.internalEvaluateExpression(expression2, { isFunction: isFunction2 }, arg); + arrayHandle.dispose(); + return result2; + } + async maskSelectors(selectors, color) { + const context2 = await this.utilityContext(); + const injectedScript = await context2.injectedScript(); + await injectedScript.evaluate((injected, { parsed, color: color2 }) => { + injected.maskSelectors(parsed, color2); + }, { parsed: selectors, color }); + } + async querySelectorAll(progress2, selector) { + return progress2.race(this.selectors.queryAll(selector)); + } + async queryCount(progress2, selector, options) { + try { + return await progress2.race(this.selectors.queryCount(selector, options)); + } catch (e) { + if (this.isNonRetriableError(e)) + throw e; + return 0; + } + } + async content(progress2) { + return progress2.race(this._content()); + } + async _content() { + try { + const context2 = await this.utilityContext(); + return await context2.evaluate(() => { + let retVal = ""; + if (document.doctype) + retVal = new XMLSerializer().serializeToString(document.doctype); + if (document.documentElement) + retVal += document.documentElement.outerHTML; + return retVal; + }); + } catch (e) { + if (this.isNonRetriableError(e)) + throw e; + throw new Error(`Unable to retrieve content because the page is navigating and changing the content.`); + } + } + async setContent(progress2, html, options) { + const tag = `--playwright--set--content--${createGuid()}--`; + await this.raceNavigationAction(progress2, async () => { + const waitUntil = options.waitUntil === void 0 ? "load" : options.waitUntil; + progress2.log(`setting frame content, waiting until "${waitUntil}"`); + const context2 = await progress2.race(this.utilityContext()); + const tagPromise = new ManualPromise(); + this._page.frameManager._consoleMessageTags.set(tag, () => { + this._onClearLifecycle(); + tagPromise.resolve(); + }); + progress2.setAllowConcurrentOrNestedRaces(true); + const lifecyclePromise = progress2.race(tagPromise).then(() => this.waitForLoadState(progress2, waitUntil)); + const contentPromise = progress2.race(context2.evaluate(({ html: html2, tag: tag2 }) => { + document.open(); + console.debug(tag2); + document.write(html2); + document.close(); + }, { html, tag })); + await Promise.all([contentPromise, lifecyclePromise]); + progress2.setAllowConcurrentOrNestedRaces(false); + return null; + }).finally(() => { + this._page.frameManager._consoleMessageTags.delete(tag); + }); + } + name() { + return this._name || ""; + } + url() { + return this._url; + } + origin() { + if (!this._url.startsWith("http")) + return; + return parseURL2(this._url)?.origin; + } + parentFrame() { + return this._parentFrame; + } + _getChildFrames() { + return Array.from(this._childFrames); + } + async addScriptTag(progress2, params2) { + return await progress2.race(this._addScriptTag(params2)); + } + async _addScriptTag(params2) { + const { + url: url2 = null, + content = null, + type: type3 = "" + } = params2; + if (!url2 && !content) + throw new Error("Provide an object with a `url`, `path` or `content` property"); + const context2 = await this.mainContext(); + return this._raceWithCSPError(async () => { + if (url2 !== null) + return (await context2.evaluateHandle(addScriptUrl, { url: url2, type: type3 })).asElement(); + const result2 = (await context2.evaluateHandle(addScriptContent, { content, type: type3 })).asElement(); + if (this._page.delegate.cspErrorsAsynchronousForInlineScripts) + await context2.evaluate(() => true); + return result2; + }); + async function addScriptUrl(params3) { + const script = document.createElement("script"); + script.src = params3.url; + if (params3.type) + script.type = params3.type; + const promise = new Promise((res, rej) => { + script.onload = res; + script.onerror = (e) => rej(typeof e === "string" ? new Error(e) : new Error(`Failed to load script at ${script.src}`)); + }); + document.head.appendChild(script); + await promise; + return script; + } + function addScriptContent(params3) { + const script = document.createElement("script"); + script.type = params3.type || "text/javascript"; + script.text = params3.content; + let error = null; + script.onerror = (e) => error = e; + document.head.appendChild(script); + if (error) + throw error; + return script; + } + } + async addStyleTag(progress2, params2) { + return await progress2.race(this._addStyleTag(params2)); + } + async _addStyleTag(params2) { + const { + url: url2 = null, + content = null + } = params2; + if (!url2 && !content) + throw new Error("Provide an object with a `url`, `path` or `content` property"); + const context2 = await this.mainContext(); + return this._raceWithCSPError(async () => { + if (url2 !== null) + return (await context2.evaluateHandle(addStyleUrl, url2)).asElement(); + return (await context2.evaluateHandle(addStyleContent, content)).asElement(); + }); + async function addStyleUrl(url3) { + const link = document.createElement("link"); + link.rel = "stylesheet"; + link.href = url3; + const promise = new Promise((res, rej) => { + link.onload = res; + link.onerror = rej; + }); + document.head.appendChild(link); + await promise; + return link; + } + async function addStyleContent(content2) { + const style = document.createElement("style"); + style.type = "text/css"; + style.appendChild(document.createTextNode(content2)); + const promise = new Promise((res, rej) => { + style.onload = res; + style.onerror = rej; + }); + document.head.appendChild(style); + await promise; + return style; + } + } + async _raceWithCSPError(func) { + const listeners = []; + let result2; + let error; + let cspMessage; + const actionPromise = func().then((r) => result2 = r).catch((e) => error = e); + const errorPromise = new Promise((resolve) => { + listeners.push(eventsHelper.addEventListener(this._page.browserContext, BrowserContext.Events.Console, (message) => { + if (message.page() !== this._page || message.type() !== "error") + return; + if (message.text().includes("Content-Security-Policy") || message.text().includes("Content Security Policy")) { + cspMessage = message; + resolve(); + } + })); + }); + await Promise.race([actionPromise, errorPromise]); + eventsHelper.removeEventListeners(listeners); + if (cspMessage) + throw new Error(cspMessage.text()); + if (error) + throw error; + return result2; + } + async retryWithProgressAndBackoff(progress2, action) { + const backoffScale = [20, 50, 100, 100, 500]; + if (progress2.timeout) { + while (backoffScale.length && backoffScale[backoffScale.length - 1] > progress2.timeout / 5) + backoffScale.pop(); + } + return await this.retryWithProgressAndTimeouts(progress2, backoffScale, action); + } + async retryWithProgressAndTimeouts(progress2, timeouts, action) { + const continuePolling = Symbol("continuePolling"); + timeouts = [0, ...timeouts]; + let timeoutIndex = 0; + while (true) { + const timeout = timeouts[Math.min(timeoutIndex++, timeouts.length - 1)]; + if (timeout) { + const actionPromise = new Promise((f) => setTimeout(f, timeout)); + await progress2.race(LongStandingScope.raceMultiple([ + this._page.openScope, + this._detachedScope + ], actionPromise)); + } + try { + const result2 = await action(progress2, continuePolling); + if (result2 === continuePolling) + continue; + return result2; + } catch (e) { + if (this.isNonRetriableError(e)) + throw e; + continue; + } + } + } + isNonRetriableError(e) { + if (isAbortError(e)) + return true; + if (isJavaScriptErrorInEvaluate(e) || isSessionClosedError(e)) + return true; + if (isNonRecoverableDOMError(e) || isInvalidSelectorError(e)) + return true; + if (this._isDetached()) + return true; + return false; + } + async _retryWithProgressIfNotConnected(progress2, selector, options, action) { + progress2.log(`waiting for ${this._asLocator(selector)}`); + const noAutoWaiting = options.__testHookNoAutoWaiting ?? options.noAutoWaiting; + const performActionPreChecks = (options.performActionPreChecks ?? !options.force) && !noAutoWaiting; + return this.retryWithProgressAndBackoff(progress2, async (progress3, continuePolling) => { + if (performActionPreChecks) + await this._page.performActionPreChecks(progress3); + const resolved = await progress3.race(this.selectors.resolveInjectedForSelector(selector, { strict: options.strict })); + if (!resolved) { + if (noAutoWaiting) + throw new NonRecoverableDOMError("Element(s) not found"); + return continuePolling; + } + const result2 = await progress3.race(resolved.injected.evaluateHandle((injected, { info }) => { + const elements = injected.querySelectorAll(info.parsed, document); + injected.markTargetElements(new Set(elements)); + const element3 = elements[0]; + let log3 = ""; + if (elements.length > 1) { + if (info.strict) + throw injected.strictModeViolationError(info.parsed, elements); + log3 = ` locator resolved to ${elements.length} elements. Proceeding with the first one: ${injected.previewNode(elements[0])}`; + } else if (element3) { + log3 = ` locator resolved to ${injected.previewNode(element3)}`; + } + injected.checkDeprecatedSelectorUsage(info.parsed, elements); + return { log: log3, success: !!element3, element: element3 }; + }, { info: resolved.info })); + const { log: log2, success } = await progress3.race(result2.evaluate((r) => ({ log: r.log, success: r.success }))); + if (log2) + progress3.log(log2); + if (!success) { + if (noAutoWaiting) + throw new NonRecoverableDOMError("Element(s) not found"); + result2.dispose(); + return continuePolling; + } + const element2 = await progress3.race(result2.evaluateHandle((r) => r.element)); + result2.dispose(); + try { + const result3 = await action(progress3, element2); + if (result3 === "error:notconnected") { + if (noAutoWaiting) + throw new NonRecoverableDOMError("Element is not attached to the DOM"); + progress3.log("element was detached from the DOM, retrying"); + return continuePolling; + } + return result3; + } finally { + element2?.dispose(); + } + }); + } + async rafrafTimeoutScreenshotElementWithProgress(progress2, selector, timeout, options) { + return await this._retryWithProgressIfNotConnected(progress2, selector, { strict: true, performActionPreChecks: true }, async (progress3, handle) => { + await handle._frame.rafrafTimeout(progress3, timeout); + return await this._page.screenshotter.screenshotElement(progress3, handle, options); + }); + } + async click(progress2, selector, options) { + return assertDone(await this._retryWithProgressIfNotConnected(progress2, selector, options, (progress3, handle) => handle._click(progress3, { ...options, waitAfter: !options.noWaitAfter }))); + } + async dblclick(progress2, selector, options) { + return assertDone(await this._retryWithProgressIfNotConnected(progress2, selector, options, (progress3, handle) => handle._dblclick(progress3, options))); + } + async dragAndDrop(progress2, source11, target, options) { + assertDone(await this._retryWithProgressIfNotConnected(progress2, source11, options, async (progress3, handle) => { + return handle._retryPointerAction(progress3, "move and down", false, async (progress4, point) => { + await this._page.mouse.move(progress4, point.x, point.y); + await this._page.mouse.down(progress4); + }, { + ...options, + waitAfter: "disabled", + position: options.sourcePosition + }); + })); + assertDone(await this._retryWithProgressIfNotConnected(progress2, target, { ...options, performActionPreChecks: false }, async (progress3, handle) => { + return handle._retryPointerAction(progress3, "move and up", false, async (progress4, point) => { + await this._page.mouse.move(progress4, point.x, point.y, { steps: options.steps }); + await this._page.mouse.up(progress4); + }, { + ...options, + waitAfter: "disabled", + position: options.targetPosition + }); + })); + } + async tap(progress2, selector, options) { + if (!this._page.browserContext._options.hasTouch) + throw new Error("The page does not support tap. Use hasTouch context option to enable touch support."); + return assertDone(await this._retryWithProgressIfNotConnected(progress2, selector, options, (progress3, handle) => handle._tap(progress3, options))); + } + async fill(progress2, selector, value2, options) { + return assertDone(await this._retryWithProgressIfNotConnected(progress2, selector, options, (progress3, handle) => handle._fill(progress3, value2, options))); + } + async focus(progress2, selector, options) { + assertDone(await this._retryWithProgressIfNotConnected(progress2, selector, options, (progress3, handle) => handle._focus(progress3))); + } + async blur(progress2, selector, options) { + assertDone(await this._retryWithProgressIfNotConnected(progress2, selector, options, (progress3, handle) => handle._blur(progress3))); + } + async resolveSelector(progress2, selector, options = {}) { + const element2 = await progress2.race(this.selectors.query(selector, options)); + if (!element2) + throw new Error(`No element matching ${selector}`); + const generated = await progress2.race(element2.evaluateInUtility(async ([injected, node]) => { + return injected.generateSelectorSimple(node); + }, {})); + if (!generated) + throw new Error(`Unable to generate locator for ${selector}`); + let frame = element2._frame; + const result2 = [generated]; + while (frame?.parentFrame()) { + const frameElement = await frame.frameElement(progress2); + if (frameElement) { + const generated2 = await progress2.race(frameElement.evaluateInUtility(async ([injected, node]) => { + return injected.generateSelectorSimple(node); + }, {})); + frameElement.dispose(); + if (generated2 === "error:notconnected" || !generated2) + throw new Error(`Unable to generate locator for ${selector}`); + result2.push(generated2); + } + frame = frame.parentFrame(); + } + const resolvedSelector = result2.reverse().join(" >> internal:control=enter-frame >> "); + return { resolvedSelector }; + } + async textContent(progress2, selector, options, scope) { + return this._callOnElementOnceMatches(progress2, selector, (injected, element2) => element2.textContent, void 0, options, scope); + } + async innerText(progress2, selector, options, scope) { + return this._callOnElementOnceMatches(progress2, selector, (injectedScript, element2) => { + if (element2.namespaceURI !== "http://www.w3.org/1999/xhtml") + throw injectedScript.createStacklessError("Node is not an HTMLElement"); + return element2.innerText; + }, void 0, options, scope); + } + async innerHTML(progress2, selector, options, scope) { + return this._callOnElementOnceMatches(progress2, selector, (injected, element2) => element2.innerHTML, void 0, options, scope); + } + async getAttribute(progress2, selector, name, options, scope) { + return this._callOnElementOnceMatches(progress2, selector, (injected, element2, data) => element2.getAttribute(data.name), { name }, options, scope); + } + async inputValue(progress2, selector, options, scope) { + return this._callOnElementOnceMatches(progress2, selector, (injectedScript, node) => { + const element2 = injectedScript.retarget(node, "follow-label"); + if (!element2 || element2.nodeName !== "INPUT" && element2.nodeName !== "TEXTAREA" && element2.nodeName !== "SELECT") + throw injectedScript.createStacklessError("Node is not an , or element"); + return element2.value; + }, void 0, options, scope); + } + async addHighlight(progress2, selector, style) { + const resolved = await progress2.race(this.selectors.resolveInjectedForSelector(selector)); + if (!resolved) + return; + return await progress2.race(resolved.injected.evaluate((injected, { info, style: style2 }) => { + return injected.addHighlight(info.parsed, style2); + }, { info: resolved.info, style })); + } + async removeHighlight(progress2, selector) { + const resolved = await progress2.race(this.selectors.resolveInjectedForSelector(selector)); + if (!resolved) + return; + return await progress2.race(resolved.injected.evaluate((injected, { info }) => { + return injected.removeHighlight(info.parsed); + }, { info: resolved.info })); + } + async hideHighlight() { + return this.raceAgainstEvaluationStallingEvents(async () => { + const context2 = await this.utilityContext(); + const injectedScript = await context2.injectedScript(); + return await injectedScript.evaluate((injected) => { + return injected.hideHighlight(); + }); + }); + } + async _elementState(progress2, selector, state, options, scope) { + const result2 = await this._callOnElementOnceMatches(progress2, selector, (injected, element2, data) => { + return injected.elementState(element2, data.state); + }, { state }, options, scope); + if (result2.received === "error:notconnected") + throwElementIsNotAttached(); + return result2.matches; + } + async isVisible(progress2, selector, options = {}, scope) { + progress2.log(` checking visibility of ${this._asLocator(selector)}`); + return await this.isVisibleInternal(progress2, selector, options, scope); + } + async isVisibleInternal(progress2, selector, options = {}, scope) { + try { + const resolved = await progress2.race(this.selectors.resolveInjectedForSelector(selector, options, scope)); + if (!resolved) + return false; + return await progress2.race(resolved.injected.evaluate((injected, { info, root }) => { + const element2 = injected.querySelector(info.parsed, root || document, info.strict); + const state = element2 ? injected.elementState(element2, "visible") : { matches: false, received: "error:notconnected" }; + return state.matches; + }, { info: resolved.info, root: resolved.frame === this ? scope : void 0 })); + } catch (e) { + if (this.isNonRetriableError(e)) + throw e; + return false; + } + } + async isHidden(progress2, selector, options = {}, scope) { + return !await this.isVisible(progress2, selector, options, scope); + } + async isDisabled(progress2, selector, options, scope) { + return this._elementState(progress2, selector, "disabled", options, scope); + } + async isEnabled(progress2, selector, options, scope) { + return this._elementState(progress2, selector, "enabled", options, scope); + } + async isEditable(progress2, selector, options, scope) { + return this._elementState(progress2, selector, "editable", options, scope); + } + async isChecked(progress2, selector, options, scope) { + return this._elementState(progress2, selector, "checked", options, scope); + } + async hover(progress2, selector, options) { + return assertDone(await this._retryWithProgressIfNotConnected(progress2, selector, options, (progress3, handle) => handle._hover(progress3, options))); + } + async selectOption(progress2, selector, elements, values, options) { + return await this._retryWithProgressIfNotConnected(progress2, selector, options, (progress3, handle) => handle._selectOption(progress3, elements, values, options)); + } + async setInputFiles(progress2, selector, params2) { + const inputFileItems = await progress2.race(prepareFilesForUpload(this, params2)); + return assertDone(await this._retryWithProgressIfNotConnected(progress2, selector, params2, (progress3, handle) => handle._setInputFiles(progress3, inputFileItems))); + } + async drop(progress2, selector, params2, options) { + const hasFiles = !!(params2.payloads?.length || params2.localPaths?.length || params2.streams?.length); + const hasData = !!params2.data?.length; + if (!hasFiles && !hasData) + throw new Error('At least one of "files" or "data" must be provided.'); + const inputFileItems = hasFiles ? await progress2.race(prepareFilesForUpload(this, params2)) : { filePayloads: void 0, localPaths: void 0 }; + const data = params2.data ?? []; + assertDone(await this._retryWithProgressIfNotConnected(progress2, selector, options, (progress3, handle) => handle._drop(progress3, inputFileItems, data, options))); + } + async type(progress2, selector, text2, options) { + return assertDone(await this._retryWithProgressIfNotConnected(progress2, selector, options, (progress3, handle) => handle._type(progress3, text2, options))); + } + async press(progress2, selector, key, options) { + return assertDone(await this._retryWithProgressIfNotConnected(progress2, selector, options, (progress3, handle) => handle._press(progress3, key, options))); + } + async check(progress2, selector, options) { + return assertDone(await this._retryWithProgressIfNotConnected(progress2, selector, options, (progress3, handle) => handle._setChecked(progress3, true, options))); + } + async uncheck(progress2, selector, options) { + return assertDone(await this._retryWithProgressIfNotConnected(progress2, selector, options, (progress3, handle) => handle._setChecked(progress3, false, options))); + } + async waitForTimeout(progress2, timeout) { + let timer; + const promise = new Promise((f) => timer = setTimeout(f, timeout)); + try { + await progress2.race(LongStandingScope.raceMultiple([ + this._page.openScope, + this._detachedScope + ], promise)); + } finally { + clearTimeout(timer); + } + } + async expect(progress2, selector, options) { + if (options.expression === "to.match.aria" && options.expectedValue) { + try { + options = { ...options, expectedValue: parseAriaSnapshotUnsafe(yaml, options.expectedValue) }; + } catch (e) { + throw new ExpectError({ customErrorMessage: e.message }); + } + } + const lastIntermediateResult = { isSet: false }; + try { + if (selector) + progress2.log(`waiting for ${this._asLocator(selector)}`); + await this._page.performActionPreChecks(progress2); + try { + const resultOneShot = await this._expectInternal(progress2, selector, options, lastIntermediateResult, true); + if (resultOneShot.matches !== options.isNot) + return; + } catch (e) { + if (this.isNonRetriableError(e)) + throw e; + } + await this.retryWithProgressAndBackoff(progress2, async (progress3, continuePolling) => { + await this._page.performActionPreChecks(progress3); + const { matches, received } = await this._expectInternal(progress3, selector, options, lastIntermediateResult, false); + if (matches === options.isNot) { + return continuePolling; + } + return { matches, received }; + }); + } catch (e) { + const details = {}; + if (isInvalidSelectorError(e)) { + details.customErrorMessage = e.message; + } else if (isJavaScriptErrorInEvaluate(e)) { + details.customErrorMessage = e.message.startsWith("Error: ") ? e.message.substring("Error: ".length) : e.message; + } else if (lastIntermediateResult.isSet) { + details.received = lastIntermediateResult.received; + details.customErrorMessage = lastIntermediateResult.errorMessage; + } + if (e instanceof TimeoutError) + details.timedOut = true; + throw new ExpectError(details); + } + } + async _expectInternal(progress2, selector, options, lastIntermediateResult, noAbort) { + const progressLog = (text2) => progress2.log(text2); + if (noAbort) + progress2 = nullProgress; + const selectorInFrame = selector ? await progress2.race(this.selectors.resolveFrameForSelector(selector, { strict: true })) : void 0; + const { frame, info } = selectorInFrame || { frame: this, info: void 0 }; + const world = options.expression === "to.have.property" ? "main" : info?.world ?? "utility"; + const context2 = await progress2.race(frame.context(world)); + const injected = await progress2.race(context2.injectedScript()); + const { log: log2, matches, received, missingReceived } = await progress2.race(injected.evaluate(async (injected2, { info: info2, options: options2 }) => { + const elements = info2 ? injected2.querySelectorAll(info2.parsed, document) : []; + injected2.markTargetElements(new Set(elements)); + const isArray = options2.expression === "to.have.count" || options2.expression.endsWith(".array"); + let log3 = ""; + if (isArray) + log3 = ` locator resolved to ${elements.length} element${elements.length === 1 ? "" : "s"}`; + else if (elements.length > 1) + throw injected2.strictModeViolationError(info2.parsed, elements); + else if (elements.length) + log3 = ` locator resolved to ${injected2.previewNode(elements[0])}`; + if (info2) + injected2.checkDeprecatedSelectorUsage(info2.parsed, elements); + return { log: log3, ...await injected2.expect(elements[0], options2, elements) }; + }, { info, options })); + if (log2) + progressLog(log2); + if (matches === options.isNot) { + lastIntermediateResult.errorMessage = missingReceived ? "element(s) not found" : void 0; + lastIntermediateResult.received = received; + lastIntermediateResult.isSet = true; + if (!missingReceived && !Array.isArray(received?.value)) + progressLog(` unexpected value "${renderUnexpectedValue(options.expression, received?.value)}"`); + } + return { matches, received }; + } + async waitForFunctionExpression(progress2, expression2, isFunction2, arg, options, world = "main") { + if (typeof options.pollingInterval === "number") + assert(options.pollingInterval > 0, "Cannot poll with non-positive interval: " + options.pollingInterval); + expression2 = normalizeEvaluationExpression(expression2, isFunction2); + return this.retryWithProgressAndTimeouts(progress2, [100], async () => { + const context2 = world === "main" ? await progress2.race(this.mainContext()) : await progress2.race(this.utilityContext()); + const injectedScript = await progress2.race(context2.injectedScript()); + const handle = await progress2.race(injectedScript.evaluateHandle((injected, { expression: expression3, isFunction: isFunction3, polling, arg: arg2 }) => { + let evaledExpression; + const predicate = () => { + let result3 = evaledExpression ?? globalThis.eval(expression3); + if (isFunction3 === true) { + evaledExpression = result3; + result3 = result3(arg2); + } else if (isFunction3 === false) { + result3 = result3; + } else { + if (typeof result3 === "function") { + evaledExpression = result3; + result3 = result3(arg2); + } + } + return result3; + }; + let fulfill; + let reject; + let aborted = false; + const result2 = new Promise((f, r) => { + fulfill = f; + reject = r; + }); + const next = () => { + if (aborted) + return; + try { + const success = predicate(); + if (success) { + fulfill(success); + return; + } + if (typeof polling !== "number") + injected.utils.builtins.requestAnimationFrame(next); + else + injected.utils.builtins.setTimeout(next, polling); + } catch (e) { + reject(e); + } + }; + next(); + return { result: result2, abort: () => aborted = true }; + }, { expression: expression2, isFunction: isFunction2, polling: options.pollingInterval, arg })); + try { + return await progress2.race(handle.evaluateHandle((h) => h.result)); + } catch (error) { + await handle.evaluate((h) => h.abort()).catch(() => { + }); + throw error; + } finally { + handle.dispose(); + } + }); + } + async waitForFunctionValueInUtility(progress2, pageFunction) { + const expression2 = `() => { + const result = (${pageFunction})(); + if (!result) + return result; + return JSON.stringify(result); + }`; + const handle = await this.waitForFunctionExpression(progress2, expression2, true, void 0, {}, "utility"); + return JSON.parse(handle.rawValue()); + } + async title(progress2) { + return progress2.race(this._title()); + } + async _title() { + try { + return await this.raceAgainstEvaluationStallingEvents(async () => { + const context2 = await this.utilityContext(); + return await context2.evaluate(() => document.title); + }); + } catch { + const url2 = this.pendingDocument()?.request?.url(); + if (url2) + return `Loading ${url2}`; + return ""; + } + } + async rafrafTimeout(progress2, timeout) { + if (timeout === 0) + return; + const context2 = await progress2.race(this.utilityContext()); + progress2.setAllowConcurrentOrNestedRaces(true); + await Promise.all([ + // wait for double raf + progress2.race(context2.evaluate(() => new Promise((x) => { + requestAnimationFrame(() => { + requestAnimationFrame(x); + }); + }))), + progress2.wait(timeout) + ]); + progress2.setAllowConcurrentOrNestedRaces(false); + } + _onDetached() { + this._stopNetworkIdleTimer(); + this._detachedScope.close(new Error("Frame was detached")); + for (const data of this._contextData.values()) { + if (data.context) + data.context.contextDestroyed("Frame was detached"); + data.contextPromise.resolve({ destroyedReason: "Frame was detached" }); + } + if (this._parentFrame) + this._parentFrame._childFrames.delete(this); + this._parentFrame = null; + } + async _callOnElementOnceMatches(progress2, selector, body, taskData, options, scope) { + const callbackText = body.toString(); + progress2.log(`waiting for ${this._asLocator(selector)}`); + const promise = this.retryWithProgressAndBackoff(progress2, async (progress3, continuePolling) => { + const resolved = await progress3.race(this.selectors.resolveInjectedForSelector(selector, options, scope)); + if (!resolved) + return continuePolling; + const { log: log2, success, value: value2 } = await progress3.race(resolved.injected.evaluate((injected, { info, callbackText: callbackText2, taskData: taskData2, root }) => { + const callback = injected.eval(callbackText2); + const element2 = injected.querySelector(info.parsed, root || document, info.strict); + if (!element2) + return { success: false }; + const log3 = ` locator resolved to ${injected.previewNode(element2)}`; + injected.markTargetElements(/* @__PURE__ */ new Set([element2])); + return { log: log3, success: true, value: callback(injected, element2, taskData2) }; + }, { info: resolved.info, callbackText, taskData, root: resolved.frame === this ? scope : void 0 })); + if (log2) + progress3.log(log2); + if (!success) + return continuePolling; + return value2; + }); + return scope ? scope._context.raceAgainstContextDestroyed(promise) : promise; + } + _setContext(world, context2) { + const data = this._contextData.get(world); + data.context = context2; + if (context2) + data.contextPromise.resolve(context2); + else + data.contextPromise = new ManualPromise(); + } + contextCreated(world, context2) { + const data = this._contextData.get(world); + if (data.context) { + data.context.contextDestroyed("Execution context was destroyed, most likely because of a navigation"); + this._setContext(world, null); + } + this._setContext(world, context2); + } + contextDestroyed(context2) { + if (this._detachedScope.isClosed()) + return; + context2.contextDestroyed("Execution context was destroyed, most likely because of a navigation"); + for (const [world, data] of this._contextData) { + if (data.context === context2) + this._setContext(world, null); + } + } + _startNetworkIdleTimer() { + assert(!this._networkIdleTimer); + if (this._firedLifecycleEvents.has("networkidle") || this._detachedScope.isClosed()) + return; + this._networkIdleTimer = setTimeout(() => { + this._firedNetworkIdleSelf = true; + this._page.mainFrame()._recalculateNetworkIdle(); + }, 500); + } + _stopNetworkIdleTimer() { + if (this._networkIdleTimer) + clearTimeout(this._networkIdleTimer); + this._networkIdleTimer = void 0; + this._firedNetworkIdleSelf = false; + } + async extendInjectedScript(source11, arg) { + const context2 = await this.context("main"); + const injectedScriptHandle = await context2.injectedScript(); + await injectedScriptHandle.evaluate((injectedScript, { source: source12, arg: arg2 }) => { + injectedScript.extend(source12, arg2); + }, { source: source11, arg }); + } + async ariaSnapshot(progress2, options = {}) { + if (options.selector && options.track) + throw new Error("Cannot specify both selector and track options"); + if (options.selector && options.mode !== "ai") { + const snapshot4 = await this._retryWithProgressIfNotConnected(progress2, options.selector, { strict: true, performActionPreChecks: true }, async (progress3, handle) => { + return await progress3.race(handle.evaluateInUtility(([injected, element2, opts]) => injected.ariaSnapshot(element2, opts), { mode: "default", depth: options.depth, boxes: options.boxes })); + }); + return { snapshot: snapshot4 }; + } + let targetFrame; + let info; + if (options.selector) { + const resolved = await progress2.race(this.selectors.resolveInjectedForSelector(options.selector, { strict: true })); + if (!resolved) + throw new Error(`Selector "${options.selector}" did not resolve to any element`); + targetFrame = resolved.frame; + info = resolved.info; + } else { + targetFrame = this; + } + const result2 = await ariaSnapshotForFrame(progress2, targetFrame, { ...options, info }); + const snapshot3 = options.track && result2.incremental ? result2.incremental.join("\n") : result2.full.join("\n"); + return { snapshot: snapshot3 }; + } + _asLocator(selector) { + return asLocator(this._page.browserContext._browser.sdkLanguage(), selector); + } + }; + SignalBarrier = class { + constructor(progress2) { + this._protectCount = 0; + this._promise = new ManualPromise(); + this._progress = progress2; + this.retain(); + } + waitFor(progress2) { + this.release(); + return progress2.race(this._promise); + } + addFrameNavigation(frame) { + if (frame.parentFrame()) + return; + this.retain(); + const waiter = helper.waitForEvent(this._progress, frame, Frame.Events.InternalNavigation, (e) => { + if (!e.isPublic) + return false; + if (!e.error && this._progress) + this._progress.log(` navigated to "${frame._url}"`); + return true; + }); + LongStandingScope.raceMultiple([ + frame._page.openScope, + frame._detachedScope + ], waiter.promise).catch(() => { + }).finally(() => { + waiter.dispose(); + this.release(); + }); + } + retain() { + ++this._protectCount; + } + release() { + --this._protectCount; + if (!this._protectCount) + this._promise.resolve(); + } + }; + } +}); + +// packages/playwright-core/src/server/har/harTracer.ts +function createHarEntry(pageRef, method, url2, frameref, options, wallTime) { + const startedDateTime = wallTime && safeDateToISOString(wallTime) || (/* @__PURE__ */ new Date()).toISOString(); + const harEntry = { + pageref: pageRef, + startedDateTime, + time: -1, + request: { + method, + url: url2.toString(), + httpVersion: FALLBACK_HTTP_VERSION, + cookies: [], + headers: [], + queryString: [...url2.searchParams].map((e) => ({ name: e[0], value: e[1] })), + headersSize: -1, + bodySize: -1 + }, + response: { + status: -1, + statusText: "", + httpVersion: FALLBACK_HTTP_VERSION, + cookies: [], + headers: [], + content: { + size: -1, + mimeType: "x-unknown" + }, + headersSize: -1, + bodySize: -1, + redirectURL: "", + _transferSize: options.omitSizes ? void 0 : -1 + }, + cache: {}, + timings: { + send: -1, + wait: -1, + receive: -1 + }, + _frameref: options.includeTraceInfo ? frameref : void 0, + _monotonicTime: options.includeTraceInfo ? monotonicTime() : void 0 + }; + return harEntry; +} +function parseCookie(c) { + const cookie = { + name: "", + value: "" + }; + let first = true; + for (const pair of c.split(/; */)) { + const indexOfEquals = pair.indexOf("="); + const name = indexOfEquals !== -1 ? pair.substr(0, indexOfEquals).trim() : pair.trim(); + const value2 = indexOfEquals !== -1 ? pair.substr(indexOfEquals + 1, pair.length).trim() : ""; + if (first) { + first = false; + cookie.name = name; + cookie.value = value2; + continue; + } + if (name === "Domain") + cookie.domain = value2; + if (name === "Expires") + cookie.expires = safeDateToISOString(value2); + if (name === "HttpOnly") + cookie.httpOnly = true; + if (name === "Max-Age") + cookie.expires = safeDateToISOString(Date.now() + +value2 * 1e3); + if (name === "Path") + cookie.path = value2; + if (name === "SameSite") + cookie.sameSite = value2; + if (name === "Secure") + cookie.secure = true; + } + return cookie; +} +function safeDateToISOString(value2) { + try { + return new Date(value2).toISOString(); + } catch (e) { + } +} +var mime6, FALLBACK_HTTP_VERSION, HarTracer, startedDateSymbol; +var init_harTracer = __esm({ + "packages/playwright-core/src/server/har/harTracer.ts"() { + "use strict"; + init_base64(); + init_manualPromise(); + init_eventsHelper(); + init_assert(); + init_crypto(); + init_time(); + init_mimeType(); + init_urlMatch(); + init_userAgent(); + init_browserContext(); + init_fetch(); + init_frames(); + init_helper(); + init_network2(); + init_progress(); + mime6 = require("./utilsBundle").mime; + FALLBACK_HTTP_VERSION = "HTTP/1.1"; + HarTracer = class { + constructor(context2, page, delegate, options) { + this._barrierPromises = /* @__PURE__ */ new Set(); + this._pageEntries = []; + this._eventListeners = []; + this._started = false; + this._omitWebSocketFrames = true; + this._context = context2; + this._page = page; + this._delegate = delegate; + this._options = options; + if (options.slimMode) { + options.omitSecurityDetails = true; + options.omitCookies = true; + options.omitTiming = true; + options.omitServerIP = true; + options.omitSizes = true; + options.omitPages = true; + } + this._entrySymbol = Symbol("requestHarEntry"); + this._pageEntrySymbol = Symbol("pageHarEntry"); + this._baseURL = context2 instanceof APIRequestContext ? context2._defaultOptions().baseURL : context2._options.baseURL; + } + setOmitWebSocketFrames(omitWebSocketFrames) { + this._omitWebSocketFrames = omitWebSocketFrames; + } + start(options) { + if (this._started) + return; + this._options.omitScripts = options.omitScripts; + this._started = true; + const apiRequest = this._context instanceof APIRequestContext ? this._context : this._context.fetchRequest; + this._eventListeners = [ + eventsHelper.addEventListener(apiRequest, APIRequestContext.Events.Request, (event) => this._onAPIRequest(event)), + eventsHelper.addEventListener(apiRequest, APIRequestContext.Events.RequestFinished, (event) => this._onAPIRequestFinished(event)) + ]; + if (this._context instanceof BrowserContext) { + this._eventListeners.push( + eventsHelper.addEventListener(this._context, BrowserContext.Events.Page, (page) => this._createPageEntryIfNeeded(page)), + eventsHelper.addEventListener(this._context, BrowserContext.Events.Request, (request2) => this._onRequest(request2)), + eventsHelper.addEventListener(this._context, BrowserContext.Events.RequestFinished, ({ request: request2, response: response2 }) => this._onRequestFinished(request2, response2).catch(() => { + })), + eventsHelper.addEventListener(this._context, BrowserContext.Events.RequestFailed, (request2) => this._onRequestFailed(request2)), + eventsHelper.addEventListener(this._context, BrowserContext.Events.Response, (response2) => this._onResponse(response2)), + eventsHelper.addEventListener(this._context, BrowserContext.Events.RequestAborted, (request2) => this._onRequestAborted(request2)), + eventsHelper.addEventListener(this._context, BrowserContext.Events.RequestFulfilled, (request2) => this._onRequestFulfilled(request2)), + eventsHelper.addEventListener(this._context, BrowserContext.Events.RequestContinued, (request2) => this._onRequestContinued(request2)), + eventsHelper.addEventListener(this._context, BrowserContext.Events.WebSocket, (webSocket, page) => this._onWebSocket(page, webSocket)) + ); + for (const page of this._context.pages()) + this._createPageEntryIfNeeded(page); + } + } + _shouldIncludeEntryWithUrl(urlString) { + return !this._options.urlFilter || urlMatches(this._baseURL, urlString, this._options.urlFilter); + } + _entryForRequest(request2) { + return request2[this._entrySymbol]; + } + _createPageEntryIfNeeded(page) { + if (!page) + return; + if (this._options.omitPages) + return; + if (this._page && page !== this._page) + return; + let pageEntry = page[this._pageEntrySymbol]; + if (!pageEntry) { + const date = /* @__PURE__ */ new Date(); + pageEntry = { + startedDateTime: date.toISOString(), + id: page.guid, + title: "", + pageTimings: this._options.omitTiming ? {} : { + onContentLoad: -1, + onLoad: -1 + } + }; + pageEntry[startedDateSymbol] = date; + page.mainFrame().on(Frame.Events.AddLifecycle, (event) => { + if (event === "load") + this._onLoad(page, pageEntry); + if (event === "domcontentloaded") + this._onDOMContentLoaded(page, pageEntry); + }); + page[this._pageEntrySymbol] = pageEntry; + this._pageEntries.push(pageEntry); + } + return pageEntry; + } + _onDOMContentLoaded(page, pageEntry) { + const promise = page.mainFrame().evaluateExpression(nullProgress, String(() => { + return { + title: document.title, + domContentLoaded: performance.timing.domContentLoadedEventStart + }; + }), { isFunction: true, world: "utility" }).then((result2) => { + pageEntry.title = result2.title; + if (!this._options.omitTiming) + pageEntry.pageTimings.onContentLoad = result2.domContentLoaded; + }).catch(() => { + }); + this._addBarrier(page, promise); + } + _onLoad(page, pageEntry) { + const promise = page.mainFrame().evaluateExpression(nullProgress, String(() => { + return { + title: document.title, + loaded: performance.timing.loadEventStart + }; + }), { isFunction: true, world: "utility" }).then((result2) => { + pageEntry.title = result2.title; + if (!this._options.omitTiming) + pageEntry.pageTimings.onLoad = result2.loaded; + }).catch(() => { + }); + this._addBarrier(page, promise); + } + _addBarrier(target, promise) { + if (!target) + return null; + if (!this._options.waitForContentOnStop) + return; + const race = target.openScope.safeRace(promise); + this._barrierPromises.add(race); + race.then(() => this._barrierPromises.delete(race)); + } + _onAPIRequest(event) { + if (!this._shouldIncludeEntryWithUrl(event.url.toString())) + return; + const harEntry = createHarEntry(void 0, event.method, event.url, void 0, this._options); + harEntry._apiRequest = true; + if (!this._options.omitCookies) + harEntry.request.cookies = event.cookies; + harEntry.request.headers = Object.entries(event.headers).map(([name, value2]) => ({ name, value: value2 })); + harEntry.request.postData = this._postDataForBuffer(event.postData || null, event.headers["content-type"], this._options.content); + if (!this._options.omitSizes) + harEntry.request.bodySize = event.postData?.length || 0; + event[this._entrySymbol] = harEntry; + if (this._started) + this._delegate.onEntryStarted(harEntry); + } + _onAPIRequestFinished(event) { + const harEntry = this._entryForRequest(event.requestEvent); + if (!harEntry) + return; + harEntry.response.status = event.statusCode; + harEntry.response.statusText = event.statusMessage; + harEntry.response.httpVersion = event.httpVersion; + harEntry.response.redirectURL = event.headers.location || ""; + if (!this._options.omitServerIP) { + harEntry.serverIPAddress = event.serverIPAddress; + harEntry._serverPort = event.serverPort; + } + if (!this._options.omitTiming) { + harEntry.timings = event.timings; + this._computeHarEntryTotalTime(harEntry); + } + if (!this._options.omitSecurityDetails) + harEntry._securityDetails = event.securityDetails; + for (let i = 0; i < event.rawHeaders.length; i += 2) { + harEntry.response.headers.push({ + name: event.rawHeaders[i], + value: event.rawHeaders[i + 1] + }); + } + harEntry.response.cookies = this._options.omitCookies ? [] : event.cookies.map((c) => { + return { + ...c, + expires: c.expires === -1 ? void 0 : safeDateToISOString(c.expires) + }; + }); + const content = harEntry.response.content; + const contentType = event.headers["content-type"]; + if (contentType) + content.mimeType = contentType; + this._storeResponseContent(event.body, content, "other"); + if (!this._options.omitSizes) + harEntry.response.bodySize = event.body?.length ?? 0; + if (this._started) + this._delegate.onEntryFinished(harEntry); + } + _onRequest(request2) { + if (!this._shouldIncludeEntryWithUrl(request2.url())) + return; + const page = request2.frame()?._page; + if (this._page && page !== this._page) + return; + const url2 = parseURL2(request2.url()); + if (!url2) + return; + const pageEntry = this._createPageEntryIfNeeded(page); + const harEntry = createHarEntry(pageEntry?.id, request2.method(), url2, request2.frame()?.guid, this._options, request2.wallTimeMs()); + harEntry._resourceType = request2.resourceType(); + this._recordRequestHeadersAndCookies(harEntry, request2.headers()); + harEntry.request.postData = this._postDataForRequest(request2, this._options.content); + if (!this._options.omitSizes) + harEntry.request.bodySize = request2.bodySize(); + if (request2.redirectedFrom()) { + const fromEntry = this._entryForRequest(request2.redirectedFrom()); + if (fromEntry) + fromEntry.response.redirectURL = request2.url(); + } + request2[this._entrySymbol] = harEntry; + assert(this._started); + this._delegate.onEntryStarted(harEntry); + } + _recordRequestHeadersAndCookies(harEntry, headers) { + if (!this._options.omitCookies) { + harEntry.request.cookies = []; + for (const header of headers.filter((header2) => header2.name.toLowerCase() === "cookie")) + harEntry.request.cookies.push(...header.value.split(";").map(parseCookie)); + } + harEntry.request.headers = headers; + } + _recordRequestOverrides(harEntry, request2) { + if (!request2.overrides() || !this._options.recordRequestOverrides) + return; + harEntry.request.method = request2.method(); + harEntry.request.url = request2.url(); + harEntry.request.postData = this._postDataForRequest(request2, this._options.content); + this._recordRequestHeadersAndCookies(harEntry, request2.headers()); + } + async _onRequestFinished(request2, response2) { + if (!response2) + return; + const harEntry = this._entryForRequest(request2); + if (!harEntry) + return; + const page = request2.frame()?._page; + if (!this._options.omitServerIP) { + this._addBarrier(page || request2.serviceWorker(), response2.internalServerAddr().then((server) => { + if (server?.ipAddress) + harEntry.serverIPAddress = server.ipAddress; + if (server?.port) + harEntry._serverPort = server.port; + })); + } + if (!this._options.omitSecurityDetails) { + this._addBarrier(page || request2.serviceWorker(), response2.internalSecurityDetails().then((details) => { + if (details) + harEntry._securityDetails = details; + })); + } + const compressionCalculationBarrier = this._options.omitSizes ? void 0 : { + _encodedBodySize: -1, + _decodedBodySize: -1, + barrier: new ManualPromise(), + _check: function() { + if (this._encodedBodySize !== -1 && this._decodedBodySize !== -1) { + harEntry.response.content.compression = Math.max(0, this._decodedBodySize - this._encodedBodySize); + this.barrier.resolve(); + } + }, + setEncodedBodySize: function(encodedBodySize) { + this._encodedBodySize = encodedBodySize; + this._check(); + }, + setDecodedBodySize: function(decodedBodySize) { + this._decodedBodySize = decodedBodySize; + this._check(); + } + }; + if (compressionCalculationBarrier) + this._addBarrier(page || request2.serviceWorker(), compressionCalculationBarrier.barrier); + const promise = response2.internalBody().then((buffer) => { + if (this._options.omitScripts && request2.resourceType() === "script") { + compressionCalculationBarrier?.setDecodedBodySize(0); + return; + } + const content = harEntry.response.content; + compressionCalculationBarrier?.setDecodedBodySize(buffer.length); + this._storeResponseContent(buffer, content, request2.resourceType()); + }).catch(() => { + compressionCalculationBarrier?.setDecodedBodySize(0); + }).then(() => { + if (this._started) + this._delegate.onEntryFinished(harEntry); + }); + this._addBarrier(page || request2.serviceWorker(), promise); + this._addBarrier(page || request2.serviceWorker(), response2.internalHttpVersion().then((httpVersion) => { + harEntry.request.httpVersion = httpVersion; + harEntry.response.httpVersion = httpVersion; + })); + const timing = response2.timing(); + harEntry.timings.receive = response2.request()._responseEndTiming !== -1 ? helper.millisToRoundishMillis(response2.request()._responseEndTiming - timing.responseStart) : -1; + this._computeHarEntryTotalTime(harEntry); + if (!this._options.omitSizes) { + this._addBarrier(page || request2.serviceWorker(), response2.internalSizes().then((sizes) => { + harEntry.response.bodySize = sizes.responseBodySize; + harEntry.response.headersSize = sizes.responseHeadersSize; + harEntry.response._transferSize = sizes.transferSize; + harEntry.request.headersSize = sizes.requestHeadersSize; + compressionCalculationBarrier?.setEncodedBodySize(sizes.responseBodySize); + })); + } + } + async _onRequestFailed(request2) { + const harEntry = this._entryForRequest(request2); + if (!harEntry) + return; + if (request2._failureText !== null) + harEntry.response._failureText = request2._failureText; + this._recordRequestOverrides(harEntry, request2); + if (this._started) + this._delegate.onEntryFinished(harEntry); + } + _onRequestAborted(request2) { + const harEntry = this._entryForRequest(request2); + if (harEntry) + harEntry._wasAborted = true; + } + _onRequestFulfilled(request2) { + const harEntry = this._entryForRequest(request2); + if (harEntry) + harEntry._wasFulfilled = true; + } + _onRequestContinued(request2) { + const harEntry = this._entryForRequest(request2); + if (harEntry) + harEntry._wasContinued = true; + } + _onWebSocket(page, webSocket) { + if (this._page && page !== this._page) + return; + if (!this._shouldIncludeEntryWithUrl(webSocket.url())) + return; + const url2 = parseURL2(webSocket.url()); + if (!url2) + return; + const method = "GET"; + const pageEntry = this._createPageEntryIfNeeded(page); + const harEntry = createHarEntry(pageEntry?.id, method, url2, page.mainFrame().guid, this._options, webSocket.wallTimeMs()); + harEntry._resourceType = "websocket"; + let sha1 = void 0; + const recordMessage = (type3, opcode, data, wallTimeMs) => { + if (this._omitWebSocketFrames) + return; + const message = { type: type3, time: this._options.omitTiming ? -1 : wallTimeMs, opcode, data }; + if (this._options.content === "embed") { + harEntry._webSocketMessages ??= []; + harEntry._webSocketMessages.push(message); + } else if (this._options.content === "attach") { + if (!sha1) { + sha1 = createGuid() + ".jsonl"; + if (this._options.includeTraceInfo) + harEntry.response.content._sha1 = sha1; + else + harEntry.response.content._file = sha1; + } + if (this._started) + this._delegate.onContentBlobAppend(sha1, JSON.stringify(message) + "\n"); + } + }; + let oldestWallTimeMs = Infinity; + let newestWallTimeMs = -Infinity; + const updateTime = (wallTimeMs) => { + if (this._options.omitTiming) + return; + if (wallTimeMs >= oldestWallTimeMs && wallTimeMs <= newestWallTimeMs) + return; + if (wallTimeMs < oldestWallTimeMs) + oldestWallTimeMs = wallTimeMs; + if (wallTimeMs > newestWallTimeMs) + newestWallTimeMs = wallTimeMs; + if (oldestWallTimeMs === newestWallTimeMs) + return; + harEntry.time = newestWallTimeMs - oldestWallTimeMs; + }; + const eventListeners = [ + eventsHelper.addEventListener(webSocket, WebSocket.Events.Request, ({ headers }) => { + this._recordRequestHeadersAndCookies(harEntry, headers); + if (!this._options.omitSizes) + harEntry.request.headersSize = requestHeadersSize(headers, webSocket.url(), method); + }), + eventsHelper.addEventListener(webSocket, WebSocket.Events.Response, ({ status, statusText: statusText2, headers }) => { + harEntry.response.status = status; + harEntry.response.statusText = statusText2; + this._recordResponseHeaders(harEntry, headers); + if (!this._options.omitSizes) { + harEntry.response.headersSize = responseHeadersSize(headers, statusText2); + harEntry.response._transferSize = Math.max(0, harEntry.response._transferSize) + harEntry.response.headersSize; + } + }), + eventsHelper.addEventListener(webSocket, WebSocket.Events.FrameSent, ({ opcode, data, wallTimeMs }) => { + recordMessage("send", opcode, data, wallTimeMs); + updateTime(wallTimeMs); + }), + eventsHelper.addEventListener(webSocket, WebSocket.Events.FrameReceived, ({ opcode, data, wallTimeMs }) => { + recordMessage("receive", opcode, data, wallTimeMs); + updateTime(wallTimeMs); + if (!this._options.omitSizes) { + const length = opcode === 1 ? Buffer.byteLength(data, "utf8") : base64ByteLength(data); + let headerSize = 6; + if (length >= 2 ** 16) + headerSize += 8; + else if (length > 125) + headerSize += 2; + harEntry.response._transferSize = Math.max(0, harEntry.response._transferSize) + headerSize + length; + } + }), + eventsHelper.addEventListener(webSocket, WebSocket.Events.SocketError, (errorMessage) => { + harEntry.response._failureText = errorMessage; + }), + eventsHelper.addEventListener(webSocket, WebSocket.Events.Close, () => { + eventsHelper.removeEventListeners(eventListeners); + if (this._started) + this._delegate.onEntryFinished(harEntry); + }) + ]; + this._eventListeners.push(...eventListeners); + if (this._started) + this._delegate.onEntryStarted(harEntry); + } + _storeResponseContent(buffer, content, resourceType) { + if (!buffer) { + content.size = 0; + return; + } + if (!this._options.omitSizes) + content.size = buffer.length; + if (this._options.content === "embed") { + if (isTextualMimeType(content.mimeType) && resourceType !== "font") { + content.text = buffer.toString(); + } else { + content.text = buffer.toString("base64"); + content.encoding = "base64"; + } + } else if (this._options.content === "attach") { + const sha1 = calculateSha1(buffer) + "." + (mime6.getExtension(content.mimeType) || "dat"); + if (this._options.includeTraceInfo) + content._sha1 = sha1; + else + content._file = sha1; + if (this._started) + this._delegate.onContentBlob(sha1, buffer); + } + } + _onResponse(response2) { + const harEntry = this._entryForRequest(response2.request()); + if (!harEntry) + return; + const page = response2.frame()?._page; + const pageEntry = this._createPageEntryIfNeeded(page); + const request2 = response2.request(); + harEntry.response = { + status: response2.status(), + statusText: response2.statusText(), + httpVersion: FALLBACK_HTTP_VERSION, + // These are bad values that will be overwritten below. + cookies: [], + headers: [], + content: { + size: -1, + mimeType: "x-unknown" + }, + headersSize: -1, + bodySize: -1, + redirectURL: "", + _transferSize: this._options.omitSizes ? void 0 : -1 + }; + if (!this._options.omitTiming) { + const startDateTime = pageEntry ? pageEntry[startedDateSymbol].valueOf() : 0; + const timing = response2.timing(); + if (pageEntry && startDateTime > timing.startTime) + pageEntry.startedDateTime = new Date(timing.startTime).toISOString(); + if (request2.wallTimeMs() === void 0 && timing.startTime > 0) { + const startedDateTime = safeDateToISOString(timing.startTime); + if (startedDateTime) + harEntry.startedDateTime = startedDateTime; + } + const dns2 = timing.domainLookupEnd !== -1 ? helper.millisToRoundishMillis(timing.domainLookupEnd - timing.domainLookupStart) : -1; + const connect2 = timing.connectEnd !== -1 ? helper.millisToRoundishMillis(timing.connectEnd - timing.connectStart) : -1; + const ssl = timing.connectEnd !== -1 ? helper.millisToRoundishMillis(timing.connectEnd - timing.secureConnectionStart) : -1; + const wait2 = timing.responseStart !== -1 ? helper.millisToRoundishMillis(timing.responseStart - timing.requestStart) : -1; + const receive = -1; + harEntry.timings = { + dns: dns2, + connect: connect2, + ssl, + send: 0, + wait: wait2, + receive + }; + this._computeHarEntryTotalTime(harEntry); + } + this._recordRequestOverrides(harEntry, request2); + this._addBarrier(page || request2.serviceWorker(), request2.internalRawRequestHeaders().then((headers) => { + this._recordRequestHeadersAndCookies(harEntry, headers); + })); + this._recordResponseHeaders(harEntry, response2.headers()); + this._addBarrier(page || request2.serviceWorker(), response2.internalRawResponseHeaders().then((headers) => { + this._recordResponseHeaders(harEntry, headers); + })); + } + _recordResponseHeaders(harEntry, headers) { + if (!this._options.omitCookies) { + harEntry.response.cookies = headers.filter((header) => header.name.toLowerCase() === "set-cookie").map((header) => parseCookie(header.value)); + } + harEntry.response.headers = headers; + const contentType = headers.find((header) => header.name.toLowerCase() === "content-type"); + if (contentType) + harEntry.response.content.mimeType = contentType.value; + } + _computeHarEntryTotalTime(harEntry) { + harEntry.time = [ + harEntry.timings.dns, + harEntry.timings.connect, + harEntry.timings.ssl, + harEntry.timings.wait, + harEntry.timings.receive + ].reduce((pre, cur) => (cur || -1) > 0 ? cur + pre : pre, 0); + } + async flush() { + await Promise.all(this._barrierPromises); + } + stop() { + this._started = false; + eventsHelper.removeEventListeners(this._eventListeners); + this._barrierPromises.clear(); + const context2 = this._context instanceof BrowserContext ? this._context : void 0; + const log2 = { + version: "1.2", + creator: { + name: "Playwright", + version: getPlaywrightVersion() + }, + browser: { + name: context2?._browser.options.name || "", + version: context2?._browser.version() || "" + }, + pages: this._pageEntries.length ? this._pageEntries.slice() : void 0, + entries: [] + }; + if (!this._options.omitTiming) { + for (const pageEntry of log2.pages || []) { + const startDateTime = pageEntry[startedDateSymbol].valueOf(); + if (typeof pageEntry.pageTimings.onContentLoad === "number" && pageEntry.pageTimings.onContentLoad >= 0) + pageEntry.pageTimings.onContentLoad -= startDateTime; + else + pageEntry.pageTimings.onContentLoad = -1; + if (typeof pageEntry.pageTimings.onLoad === "number" && pageEntry.pageTimings.onLoad >= 0) + pageEntry.pageTimings.onLoad -= startDateTime; + else + pageEntry.pageTimings.onLoad = -1; + } + } + this._pageEntries = []; + return log2; + } + _postDataForRequest(request2, content) { + const postData = request2.postDataBuffer(); + if (!postData) + return; + const contentType = request2.headerValue("content-type"); + return this._postDataForBuffer(postData, contentType, content); + } + _postDataForBuffer(postData, contentType, content) { + if (!postData) + return; + contentType ??= "application/octet-stream"; + const result2 = { + mimeType: contentType, + text: "", + params: [] + }; + if (content === "embed" && contentType !== "application/octet-stream") + result2.text = postData.toString(); + if (content === "attach") { + const sha1 = calculateSha1(postData) + "." + (mime6.getExtension(contentType) || "dat"); + if (this._options.includeTraceInfo) + result2._sha1 = sha1; + else + result2._file = sha1; + this._delegate.onContentBlob(sha1, postData); + } + if (contentType === "application/x-www-form-urlencoded") { + const parsed = new URLSearchParams(postData.toString()); + for (const [name, value2] of parsed.entries()) + result2.params.push({ name, value: value2 }); + } + return result2; + } + }; + startedDateSymbol = Symbol("startedDate"); + } +}); + +// packages/playwright-core/src/server/har/harRecorder.ts +var import_path14, HarRecorder; +var init_harRecorder = __esm({ + "packages/playwright-core/src/server/har/harRecorder.ts"() { + "use strict"; + import_path14 = __toESM(require("path")); + init_serializedFS(); + init_artifact(); + init_harTracer(); + HarRecorder = class { + constructor(context2, fallbackDir, harId, page, options) { + this._fs = new SerializedFS(); + this._isFlushed = false; + this._entries = []; + this._writtenContentEntries = /* @__PURE__ */ new Set(); + this._context = context2; + const isServer = !!context2.attribution.playwright.options.isServer; + this._harFilePath = !isServer && options.harPath ? options.harPath : import_path14.default.join(fallbackDir, `${harId}.har`); + if (!isServer && options.resourcesDir) + this._resourcesDir = options.resourcesDir; + else if (!isServer && options.harPath) + this._resourcesDir = import_path14.default.dirname(options.harPath); + else + this._resourcesDir = import_path14.default.join(fallbackDir, `${harId}-resources`); + const urlFilterRe = options.urlRegexSource !== void 0 && options.urlRegexFlags !== void 0 ? new RegExp(options.urlRegexSource, options.urlRegexFlags) : void 0; + const content = options.content || "embed"; + this._tracer = new HarTracer(context2, page, this, { + content, + slimMode: options.mode === "minimal", + includeTraceInfo: false, + recordRequestOverrides: true, + waitForContentOnStop: true, + urlFilter: urlFilterRe ?? options.urlGlob + }); + this._tracer.setOmitWebSocketFrames(!!process.env.PLAYWRIGHT_HAR_NO_WEBSOCKET_FRAMES); + this._tracer.start({ omitScripts: false }); + } + onEntryStarted(entry) { + this._entries.push(entry); + } + onEntryFinished(entry) { + } + onContentBlob(sha1, buffer) { + if (this._writtenContentEntries.has(sha1)) + return; + if (!this._writtenContentEntries.size) + this._fs.mkdir(this._resourcesDir); + this._writtenContentEntries.add(sha1); + this._fs.writeFile( + import_path14.default.join(this._resourcesDir, sha1), + buffer, + true + /* skipIfExists */ + ); + } + onContentBlobAppend(sha1, text2) { + if (!this._writtenContentEntries.size) + this._fs.mkdir(this._resourcesDir); + this._writtenContentEntries.add(sha1); + this._fs.appendFile(import_path14.default.join(this._resourcesDir, sha1), text2); + } + async _flush() { + if (this._isFlushed) + return; + this._isFlushed = true; + await this._tracer.flush(); + const log2 = this._tracer.stop(); + log2.entries = this._entries; + this._fs.mkdir(import_path14.default.dirname(this._harFilePath)); + const file = this._harFilePath; + this._fs.writeFile(file, ""); + this._fs.appendFile(file, '{"log":{"version":' + JSON.stringify(log2.version)); + this._fs.appendFile(file, ',"creator":' + JSON.stringify(log2.creator)); + if (log2.browser) + this._fs.appendFile(file, ',"browser":' + JSON.stringify(log2.browser)); + if (log2.pages) { + this._fs.appendFile(file, ',"pages":['); + for (let i = 0; i < log2.pages.length; i++) { + if (i) + this._fs.appendFile(file, ","); + this._fs.appendFile(file, JSON.stringify(log2.pages[i])); + } + this._fs.appendFile(file, "]"); + } + this._fs.appendFile(file, ',"entries":['); + for (let i = 0; i < log2.entries.length; i++) { + if (i) + this._fs.appendFile(file, ","); + this._fs.appendFile(file, JSON.stringify(log2.entries[i])); + } + this._fs.appendFile(file, "]"); + if (log2.comment !== void 0) + this._fs.appendFile(file, ',"comment":' + JSON.stringify(log2.comment)); + this._fs.appendFile( + file, + "}}", + true + /* flush */ + ); + } + async flush() { + await this._flush(); + const error = await this._fs.syncAndGetError(); + if (error) + throw error; + } + async export(mode) { + await this._flush(); + const entries = [{ name: "har.har", value: this._harFilePath }]; + for (const sha1 of this._writtenContentEntries) + entries.push({ name: sha1, value: import_path14.default.join(this._resourcesDir, sha1) }); + const zipPath = this._harFilePath + ".zip"; + if (mode === "archive") + this._fs.zip(entries, zipPath); + const error = await this._fs.syncAndGetError(); + if (error) + throw error; + if (mode === "entries") + return { entries }; + const artifact = new Artifact(this._context, zipPath); + artifact.reportFinished(); + return { artifact }; + } + }; + } +}); + +// packages/playwright-core/src/server/trace/recorder/tracing.ts +function visitTraceEvent(object, sha1s) { + if (Array.isArray(object)) + return object.map((o) => visitTraceEvent(o, sha1s)); + if (object instanceof Dispatcher) + return `<${object._type}>`; + if (object instanceof Buffer) + return ``; + if (object instanceof Date) + return object; + if (typeof object === "object") { + const result2 = {}; + for (const key in object) { + if (key === "sha1" || key === "_sha1" || key.endsWith("Sha1")) { + const sha1 = object[key]; + if (sha1) + sha1s.add(sha1); + } + result2[key] = visitTraceEvent(object[key], sha1s); + } + return result2; + } + return object; +} +function createBeforeActionTraceEvent(metadata, parentId) { + if (metadata.internal || metadata.method.startsWith("tracing")) + return null; + const event = { + type: "before", + callId: metadata.id, + startTime: metadata.startTime, + title: metadata.title, + class: metadata.type, + method: metadata.method, + params: metadata.params, + stepId: metadata.stepId, + pageId: metadata.pageId + }; + if (parentId) + event.parentId = parentId; + return event; +} +function createInputActionTraceEvent(metadata, point) { + if (metadata.internal || metadata.method.startsWith("tracing")) + return null; + return { + type: "input", + callId: metadata.id, + point + }; +} +function createActionLogTraceEvent(metadata, message) { + if (metadata.internal || metadata.method.startsWith("tracing")) + return null; + return { + type: "log", + callId: metadata.id, + time: monotonicTime(), + message + }; +} +function createAfterActionTraceEvent(metadata) { + if (metadata.internal || metadata.method.startsWith("tracing")) + return null; + return { + type: "after", + callId: metadata.id, + endTime: metadata.endTime, + error: metadata.error?.error, + result: metadata.result + }; +} +var import_fs15, import_os5, import_path15, version, Tracing, throttledRate, unthrottleDuration, ScreencastTracingRecorder; +var init_tracing = __esm({ + "packages/playwright-core/src/server/trace/recorder/tracing.ts"() { + "use strict"; + import_fs15 = __toESM(require("fs")); + import_os5 = __toESM(require("os")); + import_path15 = __toESM(require("path")); + init_protocolMetainfo(); + init_assert(); + init_time(); + init_manualPromise(); + init_eventsHelper(); + init_crypto(); + init_fileUtils(); + init_serializedFS(); + init_userAgent(); + init_snapshotter(); + init_artifact(); + init_browserContext(); + init_dispatcher(); + init_errors(); + init_harRecorder(); + init_harTracer(); + init_instrumentation(); + init_progress(); + version = 8; + Tracing = class extends SdkObject { + constructor(context2, tracesDir) { + super(context2, "tracing"); + this._fs = new SerializedFS(); + this._screencastListeners = []; + this._pageTracingRecorders = /* @__PURE__ */ new Map(); + this._eventListeners = []; + this._isStopping = false; + this._allResources = /* @__PURE__ */ new Set(); + this._pendingHarEntries = /* @__PURE__ */ new Set(); + this._started = false; + this.harRecorders = /* @__PURE__ */ new Map(); + this._context = context2; + this._precreatedTracesDir = tracesDir; + this._harTracer = new HarTracer(context2, null, this, { + content: "attach", + includeTraceInfo: true, + recordRequestOverrides: false, + waitForContentOnStop: false + }); + const testIdAttributeName2 = "selectors" in context2 ? context2.selectors().testIdAttributeName() : void 0; + this._contextCreatedEvent = { + version, + type: "context-options", + origin: "library", + browserName: "", + playwrightVersion: getPlaywrightVersion(), + options: {}, + platform: process.platform, + wallTime: 0, + monotonicTime: 0, + sdkLanguage: this._sdkLanguage(), + testIdAttributeName: testIdAttributeName2, + contextId: context2.guid + }; + if (context2 instanceof BrowserContext) { + this._snapshotter = new Snapshotter(context2, this); + assert(tracesDir, "tracesDir must be specified for BrowserContext"); + this._contextCreatedEvent.browserName = context2._browser.options.name; + this._contextCreatedEvent.channel = context2._browser.options.channel; + this._contextCreatedEvent.options = context2._options; + } + } + _sdkLanguage() { + return this._context instanceof BrowserContext ? this._context._browser.sdkLanguage() : this._context.attribution.playwright.options.sdkLanguage; + } + async resetForReuse(progress2) { + await this.stopChunk(progress2, { mode: "discard" }).catch(() => { + }); + await progress2.race(this._stop()); + if (this._snapshotter) + await progress2.race(this._snapshotter.resetForReuse()); + } + start(progress2, options) { + if (this._isStopping) + throw new Error("Cannot start tracing while stopping"); + if (this._state) + throw new Error("Tracing has been already started"); + this._contextCreatedEvent.sdkLanguage = this._sdkLanguage(); + const traceName = options.name || createGuid(); + const tracesDir = this._createTracesDirIfNeeded(); + this._state = { + options, + traceName, + tracesDir, + traceFile: import_path15.default.join(tracesDir, traceName + ".trace"), + networkFile: import_path15.default.join(tracesDir, traceName + ".network"), + resourcesDir: import_path15.default.join(tracesDir, "resources"), + chunkOrdinal: 0, + traceSha1s: /* @__PURE__ */ new Set(), + networkSha1s: /* @__PURE__ */ new Set(), + recording: false, + callIds: /* @__PURE__ */ new Set(), + groupStack: [] + }; + this._fs.mkdir(this._state.resourcesDir); + this._fs.writeFile(this._state.networkFile, ""); + if (options.snapshots) + this._harTracer.start({ omitScripts: !options.live }); + this._started = true; + } + async startChunk(progress2, options = {}) { + if (this._state && this._state.recording) + await this.stopChunk(progress2, { mode: "discard" }); + if (!this._state) + throw new Error("Must start tracing before starting a new chunk"); + if (this._isStopping) + throw new Error("Cannot start a trace chunk while stopping"); + this._state.recording = true; + this._state.callIds.clear(); + const preserveNetworkResources = this._context instanceof BrowserContext; + if (options.name && options.name !== this._state.traceName) + this._changeTraceName(this._state, options.name, preserveNetworkResources); + else + this._allocateNewTraceFile(this._state); + if (!preserveNetworkResources) + this._fs.writeFile(this._state.networkFile, ""); + this._fs.mkdir(import_path15.default.dirname(this._state.traceFile)); + const event = { + ...this._contextCreatedEvent, + title: options.title, + wallTime: Date.now(), + monotonicTime: monotonicTime() + }; + this._appendTraceEvent(event); + this._context.instrumentation.addListener(this, this._context); + this._eventListeners.push( + eventsHelper.addEventListener(this._context, BrowserContext.Events.Console, this._onConsoleMessage.bind(this)), + eventsHelper.addEventListener(this._context, BrowserContext.Events.PageError, this._onPageError.bind(this)) + ); + if (this._state.options.screenshots) + this._startScreencast(); + this._harTracer.setOmitWebSocketFrames(!!process.env.PLAYWRIGHT_TRACING_NO_WEBSOCKET_FRAMES); + if (this._state.options.snapshots) + await this._snapshotter?.start(progress2); + return { traceName: this._state.traceName }; + } + _currentGroupId() { + return this._state?.groupStack.length ? this._state.groupStack[this._state.groupStack.length - 1] : void 0; + } + group(progress2, name, location2) { + if (!this._state) + return; + const metadata = progress2.metadata; + const stackFrames = []; + const { file, line, column } = location2 ?? metadata.location ?? {}; + if (file) { + stackFrames.push({ + file, + line: line ?? 0, + column: column ?? 0 + }); + } + const event = { + type: "before", + callId: metadata.id, + startTime: metadata.startTime, + title: name, + class: "Tracing", + method: "tracingGroup", + params: {}, + stepId: metadata.stepId, + stack: stackFrames + }; + if (this._currentGroupId()) + event.parentId = this._currentGroupId(); + this._state.groupStack.push(event.callId); + this._appendTraceEvent(event); + } + groupEnd(progress2) { + this._groupEnd(); + } + _groupEnd() { + if (!this._state) + return; + const callId = this._state.groupStack.pop(); + if (!callId) + return; + const event = { + type: "after", + callId, + endTime: monotonicTime() + }; + this._appendTraceEvent(event); + } + _startScreencast() { + if (!(this._context instanceof BrowserContext)) + return; + for (const page of this._context.pages()) + this._startScreencastInPage(page); + this._screencastListeners.push( + eventsHelper.addEventListener(this._context, BrowserContext.Events.Page, this._startScreencastInPage.bind(this)) + ); + } + _stopScreencast() { + eventsHelper.removeEventListeners(this._screencastListeners); + for (const recorder of this._pageTracingRecorders.values()) + recorder.dispose(); + this._pageTracingRecorders.clear(); + } + _allocateNewTraceFile(state) { + const suffix = state.chunkOrdinal ? `-chunk${state.chunkOrdinal}` : ``; + state.chunkOrdinal++; + state.traceFile = import_path15.default.join(state.tracesDir, `${state.traceName}${suffix}.trace`); + } + _changeTraceName(state, name, preserveNetworkResources) { + state.traceName = name; + state.chunkOrdinal = 0; + this._allocateNewTraceFile(state); + const newNetworkFile = import_path15.default.join(state.tracesDir, name + ".network"); + if (preserveNetworkResources) + this._fs.copyFile(state.networkFile, newNetworkFile); + state.networkFile = newNetworkFile; + } + async stop(progress2) { + await progress2.race(this._stop()); + } + async _stop() { + if (!this._state) + return; + if (this._isStopping) + throw new Error(`Tracing is already stopping`); + if (this._state.recording) + throw new Error(`Must stop trace file before stopping tracing`); + this._closeAllGroups(); + this._harTracer.stop(); + this.flushHarEntries(); + await this._fs.syncAndGetError().finally(() => { + this._state = void 0; + }); + } + async deleteTmpTracesDir() { + if (this._tracesTmpDir) + await removeFolders([this._tracesTmpDir]); + } + _createTracesDirIfNeeded() { + if (this._precreatedTracesDir) + return this._precreatedTracesDir; + this._tracesTmpDir = import_fs15.default.mkdtempSync(import_path15.default.join(import_os5.default.tmpdir(), "playwright-tracing-")); + return this._tracesTmpDir; + } + abort() { + this._snapshotter?.dispose(); + this._harTracer.stop(); + } + async flush() { + this.abort(); + for (const harRecorder of this.harRecorders.values()) + await harRecorder.flush(); + await this._fs.syncAndGetError(); + } + harStart(page, options) { + const harId = createGuid(); + const artifactsDir = this._context instanceof BrowserContext ? this._context._browser.options.artifactsDir : this._createTracesDirIfNeeded(); + this.harRecorders.set(harId, new HarRecorder(this._context, artifactsDir, harId, page, options)); + return harId; + } + async harExport(progress2, harId, mode) { + const recorder = this.harRecorders.get(harId || ""); + const result2 = await progress2.race(recorder.export(mode)); + this.harRecorders.delete(harId || ""); + return result2; + } + _closeAllGroups() { + while (this._currentGroupId()) + this._groupEnd(); + } + async stopChunk(progress2, params2) { + if (this._isStopping) + throw new Error(`Tracing is already stopping`); + this._isStopping = true; + if (!this._state || !this._state.recording) { + this._isStopping = false; + if (params2.mode !== "discard") + throw new Error(`Must start tracing before stopping`); + return {}; + } + this._closeAllGroups(); + this._context.instrumentation.removeListener(this); + eventsHelper.removeEventListeners(this._eventListeners); + if (this._state.options.screenshots) + this._stopScreencast(); + this._harTracer.setOmitWebSocketFrames(true); + if (this._state.options.snapshots) + this._snapshotter?.stop(); + this.flushHarEntries(); + const newNetworkFile = import_path15.default.join(this._state.tracesDir, this._state.traceName + `-pwnetcopy-${this._state.chunkOrdinal}.network`); + const entries = []; + entries.push({ name: "trace.trace", value: this._state.traceFile }); + entries.push({ name: "trace.network", value: newNetworkFile }); + for (const sha1 of /* @__PURE__ */ new Set([...this._state.traceSha1s, ...this._state.networkSha1s])) + entries.push({ name: import_path15.default.join("resources", sha1), value: import_path15.default.join(this._state.resourcesDir, sha1) }); + this._state.traceSha1s = /* @__PURE__ */ new Set(); + if (params2.mode === "discard") { + this._isStopping = false; + this._state.recording = false; + return {}; + } + this._fs.copyFile(this._state.networkFile, newNetworkFile); + const zipFileName = this._state.traceFile + ".zip"; + if (params2.mode === "archive") + this._fs.zip(entries, zipFileName); + let error; + try { + await progress2.race(this._fs.syncAndGetError()); + } catch (e) { + error = e; + } + this._isStopping = false; + if (this._state) + this._state.recording = false; + if (error) { + if (!isAbortError(error) && this._context instanceof BrowserContext && !this._context._browser.isConnected()) + return {}; + throw error; + } + if (params2.mode === "entries") + return { entries }; + const artifact = new Artifact(this._context, zipFileName); + artifact.reportFinished(); + return { artifact }; + } + async _captureSnapshot(snapshotName, sdkObject, metadata, resetTargets) { + if (!snapshotName || !sdkObject.attribution.page) + return; + await this._snapshotter?.captureSnapshot(sdkObject.attribution.page, metadata.id, snapshotName, resetTargets).catch(() => { + }); + } + _shouldCaptureSnapshot(sdkObject, metadata, phase) { + if (!sdkObject.attribution.page || !this._snapshotter?.started()) + return; + const metainfo = getMetainfo(metadata); + if (!metainfo?.snapshot) + return false; + switch (phase) { + case "before": + return !metainfo.input || !!metainfo.isAutoWaiting; + case "input": + return !!metainfo.input; + case "after": + return true; + } + } + onBeforeCall(sdkObject, metadata, parentId) { + const event = createBeforeActionTraceEvent(metadata, parentId ?? this._currentGroupId()); + if (!event) + return Promise.resolve(); + this._temporarilyDisableThrottling(sdkObject.attribution.page); + if (this._shouldCaptureSnapshot(sdkObject, metadata, "before")) + event.beforeSnapshot = `before@${metadata.id}`; + this._state?.callIds.add(metadata.id); + this._appendTraceEvent(event); + return this._captureSnapshot(event.beforeSnapshot, sdkObject, metadata, true); + } + onBeforeInputAction(sdkObject, metadata, point) { + if (!this._state?.callIds.has(metadata.id)) + return Promise.resolve(); + const event = createInputActionTraceEvent(metadata, point); + if (!event) + return Promise.resolve(); + this._temporarilyDisableThrottling(sdkObject.attribution.page); + if (this._shouldCaptureSnapshot(sdkObject, metadata, "input")) + event.inputSnapshot = `input@${metadata.id}`; + this._appendTraceEvent(event); + return this._captureSnapshot(event.inputSnapshot, sdkObject, metadata, false); + } + onCallLog(sdkObject, metadata, logName, message) { + if (!this._state?.callIds.has(metadata.id)) + return; + if (metadata.internal) + return; + if (logName !== "api") + return; + const event = createActionLogTraceEvent(metadata, message); + if (event) + this._appendTraceEvent(event); + } + onAfterCall(sdkObject, metadata) { + if (!this._state?.callIds.has(metadata.id)) + return Promise.resolve(); + this._state?.callIds.delete(metadata.id); + const event = createAfterActionTraceEvent(metadata); + if (!event) + return Promise.resolve(); + this._temporarilyDisableThrottling(sdkObject.attribution.page); + if (this._shouldCaptureSnapshot(sdkObject, metadata, "after")) + event.afterSnapshot = `after@${metadata.id}`; + this._appendTraceEvent(event); + return this._captureSnapshot(event.afterSnapshot, sdkObject, metadata, false); + } + onEntryStarted(entry) { + this._pendingHarEntries.add(entry); + } + onEntryFinished(entry) { + this._pendingHarEntries.delete(entry); + const event = { type: "resource-snapshot", snapshot: entry }; + const visited = visitTraceEvent(event, this._state.networkSha1s); + this._fs.appendFile( + this._state.networkFile, + JSON.stringify(visited) + "\n", + true + /* flush */ + ); + } + flushHarEntries() { + const harLines = []; + for (const entry of this._pendingHarEntries) { + const event = { type: "resource-snapshot", snapshot: entry }; + const visited = visitTraceEvent(event, this._state.networkSha1s); + harLines.push(JSON.stringify(visited)); + } + this._pendingHarEntries.clear(); + if (harLines.length) + this._fs.appendFile( + this._state.networkFile, + harLines.join("\n") + "\n", + true + /* flush */ + ); + } + onContentBlob(sha1, buffer) { + this._appendResource(sha1, buffer); + } + onContentBlobAppend(sha1, text2) { + if (!this._allResources.has(sha1)) + this._allResources.add(sha1); + this._fs.appendFile( + import_path15.default.join(this._state.resourcesDir, sha1), + text2, + this._state.options.live + /* flush */ + ); + } + onSnapshotterBlob(blob) { + this._appendResource(blob.sha1, blob.buffer); + } + onFrameSnapshot(snapshot3) { + this._appendTraceEvent({ type: "frame-snapshot", snapshot: snapshot3 }); + } + _onConsoleMessage(message) { + const event = { + type: "console", + messageType: message.type(), + text: message.text(), + args: message.args().map((a) => ({ preview: a.toString(), value: a.rawValue() })), + location: message.location(), + time: monotonicTime(), + pageId: message.page()?.guid + }; + this._appendTraceEvent(event); + } + onDialog(dialog) { + const event = { + type: "event", + time: monotonicTime(), + class: "BrowserContext", + method: "dialog", + params: { pageId: dialog.page().guid, type: dialog.type(), message: dialog.message(), defaultValue: dialog.defaultValue() } + }; + this._appendTraceEvent(event); + } + onDownload(page, download) { + const event = { + type: "event", + time: monotonicTime(), + class: "BrowserContext", + method: "download", + params: { pageId: page.guid, url: download.url, suggestedFilename: download.suggestedFilename() } + }; + this._appendTraceEvent(event); + } + onPageOpen(page) { + const event = { + type: "event", + time: monotonicTime(), + class: "BrowserContext", + method: "page", + params: { pageId: page.guid, openerPageId: page.opener()?.guid } + }; + this._appendTraceEvent(event); + } + onPageClose(page) { + const event = { + type: "event", + time: monotonicTime(), + class: "BrowserContext", + method: "pageClosed", + params: { pageId: page.guid } + }; + this._appendTraceEvent(event); + this._pageTracingRecorders.get(page)?.dispose(); + this._pageTracingRecorders.delete(page); + } + dispose(params2) { + if (this._started) + this.stopChunk(nullProgress, params2).then(() => this._stop()).catch(() => { + }); + this._started = false; + } + _onPageError(pageError, page) { + const event = { + type: "event", + time: monotonicTime(), + class: "BrowserContext", + method: "pageError", + params: { + error: serializeError(pageError.error), + location: { + url: pageError.location.url, + line: pageError.location.lineNumber, + column: pageError.location.columnNumber + } + }, + pageId: page.guid + }; + this._appendTraceEvent(event); + } + _temporarilyDisableThrottling(page) { + if (page) + this._pageTracingRecorders.get(page)?.temporarilyDisableThrottling(); + } + _startScreencastInPage(page) { + const prefix = page.guid; + const onFrame = (params2) => { + const suffix = Date.now(); + const sha1 = `${prefix}-${suffix}.jpeg`; + const event = { + type: "screencast-frame", + pageId: page.guid, + sha1, + width: params2.viewportWidth, + height: params2.viewportHeight, + timestamp: monotonicTime(), + frameSwapWallTime: params2.frameSwapWallTime + }; + this._appendResource(sha1, params2.buffer); + this._appendTraceEvent(event); + }; + this._pageTracingRecorders.set(page, new ScreencastTracingRecorder(page.screencast, onFrame)); + } + _appendTraceEvent(event) { + const visited = visitTraceEvent(event, this._state.traceSha1s); + const flush = this._state.options.live || event.type !== "event" && event.type !== "console" && event.type !== "log"; + this._fs.appendFile(this._state.traceFile, JSON.stringify(visited) + "\n", flush); + } + _appendResource(sha1, buffer) { + if (this._allResources.has(sha1)) + return; + this._allResources.add(sha1); + const resourcePath = import_path15.default.join(this._state.resourcesDir, sha1); + this._fs.writeFile( + resourcePath, + buffer, + true + /* skipIfExists */ + ); + } + }; + throttledRate = 200; + unthrottleDuration = 500; + ScreencastTracingRecorder = class { + constructor(screencast, onFrame) { + this._unthrottledUntil = 0; + this._screencast = screencast; + this._client = { + onFrame: (frame) => { + const time = monotonicTime(); + if (time < this._unthrottledUntil) { + onFrame(frame); + return; + } + if (this._pendingAck) + return; + onFrame(frame); + this._pendingAck = new ManualPromise(); + this._timer = setTimeout(() => this._clearPendingAck(), throttledRate); + return this._pendingAck; + }, + gracefulClose: () => this.dispose(), + dispose: () => this.dispose() + }; + this._screencast.addClient(this._client); + } + dispose() { + this._screencast.removeClient(this._client); + this._clearPendingAck(); + } + temporarilyDisableThrottling() { + this._unthrottledUntil = monotonicTime() + unthrottleDuration; + this._clearPendingAck(); + } + _clearPendingAck() { + this._pendingAck?.resolve(); + this._pendingAck = void 0; + if (this._timer) { + clearTimeout(this._timer); + this._timer = void 0; + } + } + }; + } +}); + +// packages/playwright-core/src/server/fetch.ts +function toHeadersArray(rawHeaders) { + const result2 = []; + for (let i = 0; i < rawHeaders.length; i += 2) + result2.push({ name: rawHeaders[i], value: rawHeaders[i + 1] }); + return result2; +} +function parseCookie2(header) { + const raw = parseRawCookie(header); + if (!raw) + return null; + const cookie = { + domain: "", + path: "", + expires: -1, + httpOnly: false, + secure: false, + // From https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite + // The cookie-sending behavior if SameSite is not specified is SameSite=Lax. + sameSite: "Lax", + ...raw + }; + return cookie; +} +function serializePostData(params2, headers) { + assert((params2.postData ? 1 : 0) + (params2.jsonData ? 1 : 0) + (params2.formData ? 1 : 0) + (params2.multipartData ? 1 : 0) <= 1, `Only one of 'data', 'form' or 'multipart' can be specified`); + if (params2.jsonData !== void 0) { + setHeader(headers, "content-type", "application/json", true); + return Buffer.from(params2.jsonData, "utf8"); + } else if (params2.formData) { + const searchParams = new URLSearchParams(); + for (const { name, value: value2 } of params2.formData) + searchParams.append(name, value2); + setHeader(headers, "content-type", "application/x-www-form-urlencoded", true); + return Buffer.from(searchParams.toString(), "utf8"); + } else if (params2.multipartData) { + const formData = new MultipartFormData(); + for (const field of params2.multipartData) { + if (field.file) + formData.addFileField(field.name, field.file); + else if (field.value) + formData.addField(field.name, field.value); + } + setHeader(headers, "content-type", formData.contentTypeHeader(), true); + return formData.finish(); + } else if (params2.postData !== void 0) { + setHeader(headers, "content-type", "application/octet-stream", true); + return params2.postData; + } + return void 0; +} +function setHeader(headers, name, value2, keepExisting = false) { + const existing = Object.entries(headers).find((pair) => pair[0].toLowerCase() === name.toLowerCase()); + if (!existing) + headers[name] = value2; + else if (!keepExisting) + headers[existing[0]] = value2; +} +function getHeader(headers, name) { + const existing = Object.entries(headers).find((pair) => pair[0].toLowerCase() === name.toLowerCase()); + return existing ? existing[1] : void 0; +} +function removeHeader(headers, name) { + const existing = Object.entries(headers).find((pair) => pair[0].toLowerCase() === name.toLowerCase()); + if (existing) + delete headers[existing[0]]; +} +function isNetworkConnectionError(e) { + const code = e?.code; + return code === "ECONNRESET" || code === "EPIPE" || code === "ECONNABORTED"; +} +function setBasicAuthorizationHeader(headers, credentials) { + const { username, password } = credentials; + const encoded = Buffer.from(`${username || ""}:${password || ""}`).toString("base64"); + setHeader(headers, "authorization", `Basic ${encoded}`); +} +var import_http3, import_https3, import_stream4, import_tls3, zlib, APIRequestContext, SafeEmptyStreamTransform, BrowserContextAPIRequestContext, GlobalAPIRequestContext, redirectStatus; +var init_fetch = __esm({ + "packages/playwright-core/src/server/fetch.ts"() { + "use strict"; + import_http3 = __toESM(require("http")); + import_https3 = __toESM(require("https")); + import_stream4 = require("stream"); + import_tls3 = require("tls"); + zlib = __toESM(require("zlib")); + init_crypto(); + init_happyEyeballs(); + init_assert(); + init_urlMatch(); + init_eventsHelper(); + init_time(); + init_network(); + init_userAgent(); + init_browserContext(); + init_cookieStore(); + init_formData(); + init_errors(); + init_instrumentation(); + init_progress(); + init_socksClientCertificatesInterceptor(); + init_tracing(); + APIRequestContext = class _APIRequestContext extends SdkObject { + constructor(parent) { + super(parent, "request-context"); + this.fetchResponses = /* @__PURE__ */ new Map(); + this.fetchLog = /* @__PURE__ */ new Map(); + this._disposed = false; + _APIRequestContext.allInstances.add(this); + } + static { + this.Events = { + Dispose: "dispose", + Request: "request", + RequestFinished: "requestfinished" + }; + } + static { + this.allInstances = /* @__PURE__ */ new Set(); + } + static findResponseBody(guid) { + for (const request2 of _APIRequestContext.allInstances) { + const body = request2.fetchResponses.get(guid); + if (body) + return body; + } + return void 0; + } + fetchResponseBody(progress2, fetchUid) { + return this.fetchResponses.get(fetchUid); + } + fetchLogForUid(progress2, fetchUid) { + return this.fetchLog.get(fetchUid) || []; + } + disposeResponse(progress2, fetchUid) { + this._disposeResponse(fetchUid); + } + _disposeImpl() { + this._disposed = true; + _APIRequestContext.allInstances.delete(this); + this.fetchResponses.clear(); + this.fetchLog.clear(); + this.emit(_APIRequestContext.Events.Dispose); + } + _disposeResponse(fetchUid) { + this.fetchResponses.delete(fetchUid); + this.fetchLog.delete(fetchUid); + } + _storeResponseBody(body) { + const uid = createGuid(); + this.fetchResponses.set(uid, body); + return uid; + } + async fetch(progress2, params2) { + const defaults = this._defaultOptions(); + const headers = { + "user-agent": defaults.userAgent, + "accept": "*/*", + "accept-encoding": "gzip,deflate,br" + }; + if (defaults.extraHTTPHeaders) { + for (const { name, value: value2 } of defaults.extraHTTPHeaders) + setHeader(headers, name, value2); + } + if (params2.headers) { + for (const { name, value: value2 } of params2.headers) + setHeader(headers, name, value2); + } + const requestUrl = new URL(constructURLBasedOnBaseURL(defaults.baseURL, params2.url)); + if (params2.encodedParams) { + requestUrl.search = params2.encodedParams; + } else if (params2.params) { + for (const { name, value: value2 } of params2.params) + requestUrl.searchParams.append(name, value2); + } + const credentials = this._getHttpCredentials(requestUrl); + if (credentials?.send === "always") + setBasicAuthorizationHeader(headers, credentials); + const method = params2.method?.toUpperCase() || "GET"; + const proxy = defaults.proxy; + let agent; + if (proxy?.server !== "per-context") + agent = createProxyAgent(proxy, requestUrl); + let maxRedirects = params2.maxRedirects ?? (defaults.maxRedirects ?? 20); + maxRedirects = maxRedirects === 0 ? -1 : maxRedirects; + const options = { + method, + headers, + agent, + maxRedirects, + ...getMatchingTLSOptionsForOrigin(this._defaultOptions().clientCertificates, requestUrl.origin), + __testHookLookup: params2.__testHookLookup + }; + if (params2.ignoreHTTPSErrors || defaults.ignoreHTTPSErrors) + options.rejectUnauthorized = false; + const postData = serializePostData(params2, headers); + if (postData) + setHeader(headers, "content-length", String(postData.byteLength)); + const { body, log: log2, response: response2 } = await this._sendRequestWithRetries(progress2, requestUrl, options, postData, params2.maxRetries); + const failOnStatusCode = params2.failOnStatusCode !== void 0 ? params2.failOnStatusCode : !!defaults.failOnStatusCode; + if (failOnStatusCode && (response2.status < 200 || response2.status >= 400)) { + let responseText = ""; + if (body.byteLength) { + let text2 = body.toString("utf8"); + if (text2.length > 1e3) + text2 = text2.substring(0, 997) + "..."; + responseText = ` +Response text: +${text2}`; + } + throw new Error(`${response2.status} ${response2.statusText}${responseText}`); + } + const fetchUid = this._storeResponseBody(body); + this.fetchLog.set(fetchUid, log2); + return { ...response2, fetchUid }; + } + _parseSetCookieHeader(responseUrl, setCookie) { + if (!setCookie) + return []; + const url2 = new URL(responseUrl); + const defaultPath = "/" + url2.pathname.substr(1).split("/").slice(0, -1).join("/"); + const cookies = []; + for (const header of setCookie) { + const cookie = parseCookie2(header); + if (!cookie) + continue; + if (!cookie.domain) + cookie.domain = url2.hostname; + else + assert(cookie.domain.startsWith(".") || !cookie.domain.includes(".")); + if (!domainMatches(url2.hostname, cookie.domain)) + continue; + if (!cookie.path || !cookie.path.startsWith("/")) + cookie.path = defaultPath; + cookies.push(cookie); + } + return cookies; + } + async _updateRequestCookieHeader(progress2, url2, headers) { + if (getHeader(headers, "cookie") !== void 0) + return; + const contextCookies = await this.cookies(progress2, url2); + const cookies = contextCookies.filter((c) => new Cookie(c).matches(url2)); + if (cookies.length) { + const valueArray = cookies.map((c) => `${c.name}=${c.value}`); + setHeader(headers, "cookie", valueArray.join("; ")); + } + } + async _sendRequestWithRetries(progress2, url2, options, postData, maxRetries) { + const log2 = []; + maxRetries ??= 0; + let backoff = 250; + for (let i = 0; i <= maxRetries; i++) { + try { + return await this._sendRequest(progress2, log2, url2, options, postData); + } catch (e) { + if (isAbortError(e)) + throw e; + e = rewriteOpenSSLErrorIfNeeded(e); + if (maxRetries === 0) + throw e; + if (i === maxRetries) + throw new Error(`Failed after ${i + 1} attempt(s): ${e}`); + if (e.code !== "ECONNRESET") + throw e; + const message = ` Received ECONNRESET, will retry after ${backoff}ms.`; + log2.push(message); + progress2.log(message); + await progress2.wait(backoff); + backoff *= 2; + } + } + throw new Error("Unreachable"); + } + async _sendRequest(progress2, log2, url2, options, postData) { + const fetchLog = (message) => { + log2.push(message); + progress2.log(message); + }; + await this._updateRequestCookieHeader(progress2, url2, options.headers); + const requestCookies = getHeader(options.headers, "cookie")?.split(";").map((p) => { + const indexOfEquals = p.indexOf("="); + const name = indexOfEquals !== -1 ? p.substring(0, indexOfEquals).trim() : p.trim(); + const value2 = indexOfEquals !== -1 ? p.substring(indexOfEquals + 1).trim() : ""; + return { name, value: value2 }; + }) || []; + const requestEvent = { + url: url2, + method: options.method, + headers: options.headers, + cookies: requestCookies, + postData + }; + this.emit(_APIRequestContext.Events.Request, requestEvent); + if (this._disposed) + throw new TargetClosedError(this._closeReason || "Request context disposed."); + let destroyRequest; + progress2.setAllowConcurrentOrNestedRaces(true); + const resultPromise = new Promise((fulfill, reject) => { + const requestConstructor = (url2.protocol === "https:" ? import_https3.default : import_http3.default).request; + const agent = options.agent || (url2.protocol === "https:" ? httpsHappyEyeballsAgent : httpHappyEyeballsAgent); + const requestOptions = { ...options, agent }; + const startAt = monotonicTime(); + let reusedSocketAt; + let dnsLookupAt; + let tcpConnectionAt; + let tlsHandshakeAt; + let requestFinishAt; + let serverIPAddress; + let serverPort; + let securityDetails; + const listeners = []; + const request2 = requestConstructor(url2, requestOptions, async (response2) => { + const responseAt = monotonicTime(); + const notifyRequestFinished = (body2) => { + const endAt = monotonicTime(); + const connectEnd = tlsHandshakeAt ?? tcpConnectionAt; + const timings = { + send: requestFinishAt - startAt, + wait: responseAt - requestFinishAt, + receive: endAt - responseAt, + dns: dnsLookupAt ? dnsLookupAt - startAt : -1, + connect: connectEnd ? connectEnd - startAt : -1, + // "If [ssl] is defined then the time is also included in the connect field " + ssl: tlsHandshakeAt ? tlsHandshakeAt - tcpConnectionAt : -1, + blocked: reusedSocketAt ? reusedSocketAt - startAt : -1 + }; + const requestFinishedEvent = { + requestEvent, + httpVersion: response2.httpVersion, + statusCode: response2.statusCode || 0, + statusMessage: response2.statusMessage || "", + headers: response2.headers, + rawHeaders: response2.rawHeaders, + cookies, + body: body2, + timings, + serverIPAddress, + serverPort, + securityDetails + }; + this.emit(_APIRequestContext.Events.RequestFinished, requestFinishedEvent); + }; + fetchLog(`\u2190 ${response2.statusCode} ${response2.statusMessage}`); + for (const [name, value2] of Object.entries(response2.headers)) + fetchLog(` ${name}: ${value2}`); + const cookies = this._parseSetCookieHeader(response2.url || url2.toString(), response2.headers["set-cookie"]); + if (cookies.length) { + try { + await this.addCookies(cookies); + } catch (e) { + await Promise.all(cookies.map((c) => this.addCookies([c]).catch(() => { + }))); + } + } + if (redirectStatus.includes(response2.statusCode) && options.maxRedirects >= 0) { + if (options.maxRedirects === 0) { + reject(new Error("Max redirect count exceeded")); + request2.destroy(); + return; + } + const headers = { ...options.headers }; + removeHeader(headers, `cookie`); + const status = response2.statusCode; + let method = options.method; + if ((status === 301 || status === 302) && method === "POST" || status === 303 && !["GET", "HEAD"].includes(method)) { + method = "GET"; + postData = void 0; + removeHeader(headers, `content-encoding`); + removeHeader(headers, `content-language`); + removeHeader(headers, `content-length`); + removeHeader(headers, `content-location`); + removeHeader(headers, `content-type`); + } + const redirectOptions = { + method, + headers, + agent: options.agent, + maxRedirects: options.maxRedirects - 1, + __testHookLookup: options.__testHookLookup + }; + if (options.rejectUnauthorized === false) + redirectOptions.rejectUnauthorized = false; + const locationHeaderValue = Buffer.from(response2.headers.location ?? "", "latin1").toString("utf8"); + if (locationHeaderValue) { + let locationURL; + try { + locationURL = new URL(locationHeaderValue, url2); + } catch (error) { + reject(new Error(`uri requested responds with an invalid redirect URL: ${locationHeaderValue}`)); + request2.destroy(); + return; + } + if (headers["host"]) + headers["host"] = locationURL.host; + if (locationURL.origin !== url2.origin) + removeHeader(headers, "authorization"); + Object.assign( + redirectOptions, + getMatchingTLSOptionsForOrigin(this._defaultOptions().clientCertificates, locationURL.origin) + ); + notifyRequestFinished(); + fulfill(this._sendRequest(progress2, log2, locationURL, redirectOptions, postData)); + request2.destroy(); + return; + } + } + if (response2.statusCode === 401 && !getHeader(options.headers, "authorization")) { + const auth = response2.headers["www-authenticate"]; + const credentials = this._getHttpCredentials(url2); + if (auth?.trim().startsWith("Basic") && credentials) { + setBasicAuthorizationHeader(options.headers, credentials); + notifyRequestFinished(); + fulfill(this._sendRequest(progress2, log2, url2, options, postData)); + request2.destroy(); + return; + } + } + const chunks = []; + const notifyBodyFinished = () => { + const body2 = Buffer.concat(chunks); + notifyRequestFinished(body2); + fulfill({ + body: body2, + log: log2, + response: { + url: response2.url || url2.toString(), + status: response2.statusCode || 0, + statusText: response2.statusMessage || "", + headers: toHeadersArray(response2.rawHeaders), + securityDetails, + serverAddr: serverIPAddress !== void 0 && serverPort !== void 0 ? { ipAddress: serverIPAddress, port: serverPort } : void 0 + } + }); + }; + let body = response2; + let transform2; + const encoding = response2.headers["content-encoding"]; + if (encoding === "gzip" || encoding === "x-gzip") { + transform2 = zlib.createGunzip({ + flush: zlib.constants.Z_SYNC_FLUSH, + finishFlush: zlib.constants.Z_SYNC_FLUSH + }); + } else if (encoding === "br") { + transform2 = zlib.createBrotliDecompress({ + flush: zlib.constants.BROTLI_OPERATION_FLUSH, + finishFlush: zlib.constants.BROTLI_OPERATION_FLUSH + }); + } else if (encoding === "deflate") { + transform2 = zlib.createInflate(); + } + if (transform2) { + const emptyStreamTransform = new SafeEmptyStreamTransform(notifyBodyFinished); + body = (0, import_stream4.pipeline)(response2, emptyStreamTransform, transform2, (e) => { + if (e) { + if (isNetworkConnectionError(e)) + reject(e); + else + reject(new Error(`failed to decompress '${encoding}' encoding: ${e.message}`)); + } + }); + body.on("error", (e) => { + if (isNetworkConnectionError(e)) + reject(e); + else + reject(new Error(`failed to decompress '${encoding}' encoding: ${e}`)); + }); + } else { + response2.on("aborted", () => reject(new Error("aborted"))); + body.on("error", reject); + } + body.on("data", (chunk) => chunks.push(chunk)); + body.on("end", notifyBodyFinished); + }); + request2.on("error", reject); + destroyRequest = () => request2.destroy(); + listeners.push( + eventsHelper.addEventListener(this, _APIRequestContext.Events.Dispose, () => { + reject(new TargetClosedError(this._closeReason || "Request context disposed.")); + request2.destroy(); + }) + ); + request2.on("close", () => eventsHelper.removeEventListeners(listeners)); + const captureSecurityDetails = (socket) => { + if (!(socket instanceof import_tls3.TLSSocket)) + return; + const peerCertificate = socket.getPeerCertificate(); + securityDetails = { + protocol: socket.getProtocol() ?? void 0, + subjectName: peerCertificate.subject.CN, + validFrom: new Date(peerCertificate.valid_from).getTime() / 1e3, + validTo: new Date(peerCertificate.valid_to).getTime() / 1e3, + issuer: peerCertificate.issuer.CN + }; + }; + request2.on("socket", (socket) => { + serverIPAddress = socket.remoteAddress; + serverPort = socket.remotePort; + if (request2.reusedSocket) { + reusedSocketAt = monotonicTime(); + captureSecurityDetails(socket); + return; + } + const happyEyeBallsTimings = timingForSocket(socket); + dnsLookupAt = happyEyeBallsTimings.dnsLookupAt; + tcpConnectionAt = happyEyeBallsTimings.tcpConnectionAt; + listeners.push( + eventsHelper.addEventListener(socket, "lookup", () => { + dnsLookupAt = monotonicTime(); + }), + eventsHelper.addEventListener(socket, "connect", () => { + tcpConnectionAt = monotonicTime(); + }), + eventsHelper.addEventListener(socket, "secureConnect", () => { + tlsHandshakeAt = monotonicTime(); + captureSecurityDetails(socket); + }) + ); + }); + request2.on("finish", () => { + requestFinishAt = monotonicTime(); + }); + fetchLog(`\u2192 ${options.method} ${url2.toString()}`); + if (options.headers) { + for (const [name, value2] of Object.entries(options.headers)) + fetchLog(` ${name}: ${value2}`); + } + if (postData) + request2.write(postData); + request2.end(); + }); + return progress2.race(resultPromise).catch((error) => { + destroyRequest?.(); + throw error; + }).finally(() => { + progress2.setAllowConcurrentOrNestedRaces(false); + }); + } + _getHttpCredentials(url2) { + if (!this._defaultOptions().httpCredentials?.origin || url2.origin.toLowerCase() === this._defaultOptions().httpCredentials?.origin?.toLowerCase()) + return this._defaultOptions().httpCredentials; + return void 0; + } + }; + SafeEmptyStreamTransform = class extends import_stream4.Transform { + constructor(onEmptyStreamCallback) { + super(); + this._receivedSomeData = false; + this._onEmptyStreamCallback = onEmptyStreamCallback; + } + _transform(chunk, encoding, callback) { + this._receivedSomeData = true; + callback(null, chunk); + } + _flush(callback) { + if (this._receivedSomeData) + callback(null); + else + this._onEmptyStreamCallback(); + } + }; + BrowserContextAPIRequestContext = class extends APIRequestContext { + constructor(context2) { + super(context2); + this._context = context2; + context2.once(BrowserContext.Events.Close, () => this._disposeImpl()); + } + tracing() { + return this._context.tracing; + } + async dispose(options) { + this._closeReason = options.reason; + this.fetchResponses.clear(); + } + _defaultOptions() { + return { + userAgent: this._context._options.userAgent || this._context._browser.userAgent(), + extraHTTPHeaders: this._context._options.extraHTTPHeaders, + failOnStatusCode: void 0, + httpCredentials: this._context._options.httpCredentials, + proxy: this._context._options.proxy || this._context._browser.options.proxy, + ignoreHTTPSErrors: this._context._options.ignoreHTTPSErrors, + baseURL: this._context._options.baseURL, + clientCertificates: this._context._options.clientCertificates + }; + } + async addCookies(cookies) { + await this._context.addCookies(cookies); + } + async cookies(progress2, url2) { + return await this._context.cookies(progress2, url2.toString()); + } + async storageState(progress2, indexedDB) { + return this._context.storageState(progress2, indexedDB); + } + }; + GlobalAPIRequestContext = class extends APIRequestContext { + constructor(playwright2, options) { + super(playwright2); + this._cookieStore = new CookieStore(); + this.attribution.context = this; + if (options.storageState) { + this._origins = options.storageState.origins?.map((origin) => ({ indexedDB: [], ...origin })); + this._cookieStore.addCookies(options.storageState.cookies || []); + } + verifyClientCertificates(options.clientCertificates); + this._options = { + baseURL: options.baseURL, + userAgent: options.userAgent || getUserAgent(), + extraHTTPHeaders: options.extraHTTPHeaders, + failOnStatusCode: !!options.failOnStatusCode, + ignoreHTTPSErrors: !!options.ignoreHTTPSErrors, + maxRedirects: options.maxRedirects, + httpCredentials: options.httpCredentials, + clientCertificates: options.clientCertificates, + proxy: options.proxy + }; + this._tracing = new Tracing(this, options.tracesDir); + } + tracing() { + return this._tracing; + } + async dispose(options) { + this._closeReason = options.reason; + await this._tracing.flush(); + await this._tracing.deleteTmpTracesDir(); + this._disposeImpl(); + } + _defaultOptions() { + return this._options; + } + async addCookies(cookies) { + this._cookieStore.addCookies(cookies); + } + async cookies(progress2, url2) { + return this._cookieStore.cookies(url2); + } + async storageState(progress2, indexedDB = false) { + return { + cookies: this._cookieStore.allCookies(), + origins: (this._origins || []).map((origin) => ({ ...origin, indexedDB: indexedDB ? origin.indexedDB : [] })) + }; + } + }; + redirectStatus = [301, 302, 303, 307, 308]; + } +}); + +// packages/playwright-core/src/server/utils.ts +async function fetchData(progress2, params2, onError) { + const promise = new ManualPromise(); + const { cancel } = httpRequest(params2, async (response2) => { + if (response2.statusCode !== 200) { + const error = onError ? await onError(params2, response2) : new Error(`fetch failed: server returned code ${response2.statusCode}. URL: ${params2.url}`); + promise.reject(error); + return; + } + let body = ""; + response2.on("data", (chunk) => body += chunk); + response2.on("error", (error) => promise.reject(error)); + response2.on("end", () => promise.resolve(body)); + }, (error) => promise.reject(error)); + if (!progress2) + return promise; + try { + return await progress2.race(promise); + } catch (error) { + cancel(error); + throw error; + } +} +var init_utils2 = __esm({ + "packages/playwright-core/src/server/utils.ts"() { + "use strict"; + init_manualPromise(); + init_network(); + } +}); + +// packages/playwright-core/src/server/registry/nativeDeps.ts +var deps; +var init_nativeDeps = __esm({ + "packages/playwright-core/src/server/registry/nativeDeps.ts"() { + "use strict"; + deps = { + "ubuntu20.04-x64": { + tools: [ + "xvfb", + "fonts-noto-color-emoji", + "ttf-unifont", + "libfontconfig", + "libfreetype6", + "xfonts-cyrillic", + "xfonts-scalable", + "fonts-liberation", + "fonts-ipafont-gothic", + "fonts-wqy-zenhei", + "fonts-tlwg-loma-otf", + "ttf-ubuntu-font-family" + ], + chromium: [ + "fonts-liberation", + "libasound2", + "libatk-bridge2.0-0", + "libatk1.0-0", + "libatspi2.0-0", + "libcairo2", + "libcups2", + "libdbus-1-3", + "libdrm2", + "libegl1", + "libgbm1", + "libglib2.0-0", + "libgtk-3-0", + "libnspr4", + "libnss3", + "libpango-1.0-0", + "libx11-6", + "libx11-xcb1", + "libxcb1", + "libxcomposite1", + "libxdamage1", + "libxext6", + "libxfixes3", + "libxrandr2", + "libxshmfence1" + ], + firefox: [ + "ffmpeg", + "libatk1.0-0", + "libcairo-gobject2", + "libcairo2", + "libdbus-1-3", + "libdbus-glib-1-2", + "libfontconfig1", + "libfreetype6", + "libgdk-pixbuf2.0-0", + "libglib2.0-0", + "libgtk-3-0", + "libpango-1.0-0", + "libpangocairo-1.0-0", + "libpangoft2-1.0-0", + "libx11-6", + "libx11-xcb1", + "libxcb-shm0", + "libxcb1", + "libxcomposite1", + "libxcursor1", + "libxdamage1", + "libxext6", + "libxfixes3", + "libxi6", + "libxrender1", + "libxt6", + "libxtst6" + ], + webkit: [ + "libenchant-2-2", + "libflite1", + "libx264-155", + "libatk-bridge2.0-0", + "libatk1.0-0", + "libcairo2", + "libegl1", + "libenchant1c2a", + "libepoxy0", + "libevdev2", + "libfontconfig1", + "libfreetype6", + "libgdk-pixbuf2.0-0", + "libgl1", + "libgles2", + "libglib2.0-0", + "libgtk-3-0", + "libgudev-1.0-0", + "libharfbuzz-icu0", + "libharfbuzz0b", + "libhyphen0", + "libicu66", + "libjpeg-turbo8", + "libnghttp2-14", + "libnotify4", + "libopengl0", + "libopenjp2-7", + "libopus0", + "libpango-1.0-0", + "libpng16-16", + "libsecret-1-0", + "libvpx6", + "libwayland-client0", + "libwayland-egl1", + "libwayland-server0", + "libwebp6", + "libwebpdemux2", + "libwoff1", + "libx11-6", + "libxcomposite1", + "libxdamage1", + "libxkbcommon0", + "libxml2", + "libxslt1.1", + "libatomic1", + "libevent-2.1-7" + ], + lib2package: { + "libflite.so.1": "libflite1", + "libflite_usenglish.so.1": "libflite1", + "libflite_cmu_grapheme_lang.so.1": "libflite1", + "libflite_cmu_grapheme_lex.so.1": "libflite1", + "libflite_cmu_indic_lang.so.1": "libflite1", + "libflite_cmu_indic_lex.so.1": "libflite1", + "libflite_cmulex.so.1": "libflite1", + "libflite_cmu_time_awb.so.1": "libflite1", + "libflite_cmu_us_awb.so.1": "libflite1", + "libflite_cmu_us_kal16.so.1": "libflite1", + "libflite_cmu_us_kal.so.1": "libflite1", + "libflite_cmu_us_rms.so.1": "libflite1", + "libflite_cmu_us_slt.so.1": "libflite1", + "libx264.so": "libx264-155", + "libasound.so.2": "libasound2", + "libatk-1.0.so.0": "libatk1.0-0", + "libatk-bridge-2.0.so.0": "libatk-bridge2.0-0", + "libatspi.so.0": "libatspi2.0-0", + "libcairo-gobject.so.2": "libcairo-gobject2", + "libcairo.so.2": "libcairo2", + "libcups.so.2": "libcups2", + "libdbus-1.so.3": "libdbus-1-3", + "libdbus-glib-1.so.2": "libdbus-glib-1-2", + "libdrm.so.2": "libdrm2", + "libEGL.so.1": "libegl1", + "libenchant.so.1": "libenchant1c2a", + "libevdev.so.2": "libevdev2", + "libepoxy.so.0": "libepoxy0", + "libfontconfig.so.1": "libfontconfig1", + "libfreetype.so.6": "libfreetype6", + "libgbm.so.1": "libgbm1", + "libgdk_pixbuf-2.0.so.0": "libgdk-pixbuf2.0-0", + "libgdk-3.so.0": "libgtk-3-0", + "libgdk-x11-2.0.so.0": "libgtk2.0-0", + "libgio-2.0.so.0": "libglib2.0-0", + "libGL.so.1": "libgl1", + "libGLESv2.so.2": "libgles2", + "libglib-2.0.so.0": "libglib2.0-0", + "libgmodule-2.0.so.0": "libglib2.0-0", + "libgobject-2.0.so.0": "libglib2.0-0", + "libgthread-2.0.so.0": "libglib2.0-0", + "libgtk-3.so.0": "libgtk-3-0", + "libgtk-x11-2.0.so.0": "libgtk2.0-0", + "libgudev-1.0.so.0": "libgudev-1.0-0", + "libharfbuzz-icu.so.0": "libharfbuzz-icu0", + "libharfbuzz.so.0": "libharfbuzz0b", + "libhyphen.so.0": "libhyphen0", + "libicui18n.so.66": "libicu66", + "libicuuc.so.66": "libicu66", + "libjpeg.so.8": "libjpeg-turbo8", + "libnotify.so.4": "libnotify4", + "libnspr4.so": "libnspr4", + "libnss3.so": "libnss3", + "libnssutil3.so": "libnss3", + "libOpenGL.so.0": "libopengl0", + "libopenjp2.so.7": "libopenjp2-7", + "libopus.so.0": "libopus0", + "libpango-1.0.so.0": "libpango-1.0-0", + "libpangocairo-1.0.so.0": "libpangocairo-1.0-0", + "libpangoft2-1.0.so.0": "libpangoft2-1.0-0", + "libpng16.so.16": "libpng16-16", + "libsecret-1.so.0": "libsecret-1-0", + "libsmime3.so": "libnss3", + "libvpx.so.6": "libvpx6", + "libwayland-client.so.0": "libwayland-client0", + "libwayland-egl.so.1": "libwayland-egl1", + "libwayland-server.so.0": "libwayland-server0", + "libwebp.so.6": "libwebp6", + "libwebpdemux.so.2": "libwebpdemux2", + "libwoff2dec.so.1.0.2": "libwoff1", + "libX11-xcb.so.1": "libx11-xcb1", + "libX11.so.6": "libx11-6", + "libxcb-dri3.so.0": "libxcb-dri3-0", + "libxcb-shm.so.0": "libxcb-shm0", + "libxcb.so.1": "libxcb1", + "libXcomposite.so.1": "libxcomposite1", + "libXcursor.so.1": "libxcursor1", + "libXdamage.so.1": "libxdamage1", + "libXext.so.6": "libxext6", + "libXfixes.so.3": "libxfixes3", + "libXi.so.6": "libxi6", + "libxkbcommon.so.0": "libxkbcommon0", + "libxml2.so.2": "libxml2", + "libXrandr.so.2": "libxrandr2", + "libXrender.so.1": "libxrender1", + "libxslt.so.1": "libxslt1.1", + "libXt.so.6": "libxt6", + "libXtst.so.6": "libxtst6", + "libxshmfence.so.1": "libxshmfence1", + "libatomic.so.1": "libatomic1", + "libenchant-2.so.2": "libenchant-2-2", + "libevent-2.1.so.7": "libevent-2.1-7" + } + }, + "ubuntu22.04-x64": { + tools: [ + "xvfb", + "fonts-noto-color-emoji", + "fonts-unifont", + "libfontconfig1", + "libfreetype6", + "xfonts-cyrillic", + "xfonts-scalable", + "fonts-liberation", + "fonts-ipafont-gothic", + "fonts-wqy-zenhei", + "fonts-tlwg-loma-otf", + "fonts-freefont-ttf" + ], + chromium: [ + "libasound2", + "libatk-bridge2.0-0", + "libatk1.0-0", + "libatspi2.0-0", + "libcairo2", + "libcups2", + "libdbus-1-3", + "libdrm2", + "libgbm1", + "libglib2.0-0", + "libnspr4", + "libnss3", + "libpango-1.0-0", + "libwayland-client0", + "libx11-6", + "libxcb1", + "libxcomposite1", + "libxdamage1", + "libxext6", + "libxfixes3", + "libxkbcommon0", + "libxrandr2" + ], + firefox: [ + "ffmpeg", + "libasound2", + "libatk1.0-0", + "libcairo-gobject2", + "libcairo2", + "libdbus-1-3", + "libdbus-glib-1-2", + "libfontconfig1", + "libfreetype6", + "libgdk-pixbuf-2.0-0", + "libglib2.0-0", + "libgtk-3-0", + "libpango-1.0-0", + "libpangocairo-1.0-0", + "libx11-6", + "libx11-xcb1", + "libxcb-shm0", + "libxcb1", + "libxcomposite1", + "libxcursor1", + "libxdamage1", + "libxext6", + "libxfixes3", + "libxi6", + "libxrandr2", + "libxrender1", + "libxtst6" + ], + webkit: [ + "libsoup-3.0-0", + "libenchant-2-2", + "gstreamer1.0-libav", + "gstreamer1.0-plugins-bad", + "gstreamer1.0-plugins-base", + "gstreamer1.0-plugins-good", + "libicu70", + "libatk-bridge2.0-0", + "libatk1.0-0", + "libcairo2", + "libdbus-1-3", + "libdrm2", + "libegl1", + "libepoxy0", + "libevdev2", + "libffi7", + "libfontconfig1", + "libfreetype6", + "libgbm1", + "libgdk-pixbuf-2.0-0", + "libgles2", + "libglib2.0-0", + "libglx0", + "libgstreamer-gl1.0-0", + "libgstreamer-plugins-base1.0-0", + "libgstreamer1.0-0", + "libgtk-4-1", + "libgudev-1.0-0", + "libharfbuzz-icu0", + "libharfbuzz0b", + "libhyphen0", + "libjpeg-turbo8", + "liblcms2-2", + "libmanette-0.2-0", + "libnotify4", + "libopengl0", + "libopenjp2-7", + "libopus0", + "libpango-1.0-0", + "libpng16-16", + "libproxy1v5", + "libsecret-1-0", + "libwayland-client0", + "libwayland-egl1", + "libwayland-server0", + "libwebpdemux2", + "libwoff1", + "libx11-6", + "libxcomposite1", + "libxdamage1", + "libxkbcommon0", + "libxml2", + "libxslt1.1", + "libx264-163", + "libatomic1", + "libevent-2.1-7", + "libavif13" + ], + lib2package: { + "libavif.so.13": "libavif13", + "libsoup-3.0.so.0": "libsoup-3.0-0", + "libasound.so.2": "libasound2", + "libatk-1.0.so.0": "libatk1.0-0", + "libatk-bridge-2.0.so.0": "libatk-bridge2.0-0", + "libatspi.so.0": "libatspi2.0-0", + "libcairo-gobject.so.2": "libcairo-gobject2", + "libcairo.so.2": "libcairo2", + "libcups.so.2": "libcups2", + "libdbus-1.so.3": "libdbus-1-3", + "libdbus-glib-1.so.2": "libdbus-glib-1-2", + "libdrm.so.2": "libdrm2", + "libEGL.so.1": "libegl1", + "libepoxy.so.0": "libepoxy0", + "libevdev.so.2": "libevdev2", + "libffi.so.7": "libffi7", + "libfontconfig.so.1": "libfontconfig1", + "libfreetype.so.6": "libfreetype6", + "libgbm.so.1": "libgbm1", + "libgdk_pixbuf-2.0.so.0": "libgdk-pixbuf-2.0-0", + "libgdk-3.so.0": "libgtk-3-0", + "libgio-2.0.so.0": "libglib2.0-0", + "libGLESv2.so.2": "libgles2", + "libglib-2.0.so.0": "libglib2.0-0", + "libGLX.so.0": "libglx0", + "libgmodule-2.0.so.0": "libglib2.0-0", + "libgobject-2.0.so.0": "libglib2.0-0", + "libgstallocators-1.0.so.0": "libgstreamer-plugins-base1.0-0", + "libgstapp-1.0.so.0": "libgstreamer-plugins-base1.0-0", + "libgstaudio-1.0.so.0": "libgstreamer-plugins-base1.0-0", + "libgstbase-1.0.so.0": "libgstreamer1.0-0", + "libgstfft-1.0.so.0": "libgstreamer-plugins-base1.0-0", + "libgstgl-1.0.so.0": "libgstreamer-gl1.0-0", + "libgstpbutils-1.0.so.0": "libgstreamer-plugins-base1.0-0", + "libgstreamer-1.0.so.0": "libgstreamer1.0-0", + "libgsttag-1.0.so.0": "libgstreamer-plugins-base1.0-0", + "libgstvideo-1.0.so.0": "libgstreamer-plugins-base1.0-0", + "libgtk-3.so.0": "libgtk-3-0", + "libgtk-4.so.1": "libgtk-4-1", + "libgudev-1.0.so.0": "libgudev-1.0-0", + "libharfbuzz-icu.so.0": "libharfbuzz-icu0", + "libharfbuzz.so.0": "libharfbuzz0b", + "libhyphen.so.0": "libhyphen0", + "libjpeg.so.8": "libjpeg-turbo8", + "liblcms2.so.2": "liblcms2-2", + "libmanette-0.2.so.0": "libmanette-0.2-0", + "libnotify.so.4": "libnotify4", + "libnspr4.so": "libnspr4", + "libnss3.so": "libnss3", + "libnssutil3.so": "libnss3", + "libOpenGL.so.0": "libopengl0", + "libopenjp2.so.7": "libopenjp2-7", + "libopus.so.0": "libopus0", + "libpango-1.0.so.0": "libpango-1.0-0", + "libpangocairo-1.0.so.0": "libpangocairo-1.0-0", + "libpng16.so.16": "libpng16-16", + "libproxy.so.1": "libproxy1v5", + "libsecret-1.so.0": "libsecret-1-0", + "libsmime3.so": "libnss3", + "libwayland-client.so.0": "libwayland-client0", + "libwayland-egl.so.1": "libwayland-egl1", + "libwayland-server.so.0": "libwayland-server0", + "libwebpdemux.so.2": "libwebpdemux2", + "libwoff2dec.so.1.0.2": "libwoff1", + "libX11-xcb.so.1": "libx11-xcb1", + "libX11.so.6": "libx11-6", + "libxcb-shm.so.0": "libxcb-shm0", + "libxcb.so.1": "libxcb1", + "libXcomposite.so.1": "libxcomposite1", + "libXcursor.so.1": "libxcursor1", + "libXdamage.so.1": "libxdamage1", + "libXext.so.6": "libxext6", + "libXfixes.so.3": "libxfixes3", + "libXi.so.6": "libxi6", + "libxkbcommon.so.0": "libxkbcommon0", + "libxml2.so.2": "libxml2", + "libXrandr.so.2": "libxrandr2", + "libXrender.so.1": "libxrender1", + "libxslt.so.1": "libxslt1.1", + "libXtst.so.6": "libxtst6", + "libicui18n.so.60": "libicu70", + "libicuuc.so.66": "libicu70", + "libicui18n.so.66": "libicu70", + "libwebp.so.6": "libwebp6", + "libenchant-2.so.2": "libenchant-2-2", + "libx264.so": "libx264-163", + "libvpx.so.7": "libvpx7", + "libatomic.so.1": "libatomic1", + "libevent-2.1.so.7": "libevent-2.1-7" + } + }, + "ubuntu24.04-x64": { + tools: [ + "xvfb", + "fonts-noto-color-emoji", + "fonts-unifont", + "libfontconfig1", + "libfreetype6", + "xfonts-cyrillic", + "xfonts-scalable", + "fonts-liberation", + "fonts-ipafont-gothic", + "fonts-wqy-zenhei", + "fonts-tlwg-loma-otf", + "fonts-freefont-ttf" + ], + chromium: [ + "libasound2t64", + "libatk-bridge2.0-0t64", + "libatk1.0-0t64", + "libatspi2.0-0t64", + "libcairo2", + "libcups2t64", + "libdbus-1-3", + "libdrm2", + "libgbm1", + "libglib2.0-0t64", + "libnspr4", + "libnss3", + "libpango-1.0-0", + "libx11-6", + "libxcb1", + "libxcomposite1", + "libxdamage1", + "libxext6", + "libxfixes3", + "libxkbcommon0", + "libxrandr2" + ], + firefox: [ + "libasound2t64", + "libatk1.0-0t64", + "libavcodec60", + "libcairo-gobject2", + "libcairo2", + "libdbus-1-3", + "libfontconfig1", + "libfreetype6", + "libgdk-pixbuf-2.0-0", + "libglib2.0-0t64", + "libgtk-3-0t64", + "libpango-1.0-0", + "libpangocairo-1.0-0", + "libx11-6", + "libx11-xcb1", + "libxcb-shm0", + "libxcb1", + "libxcomposite1", + "libxcursor1", + "libxdamage1", + "libxext6", + "libxfixes3", + "libxi6", + "libxrandr2", + "libxrender1" + ], + webkit: [ + "gstreamer1.0-libav", + "gstreamer1.0-plugins-bad", + "gstreamer1.0-plugins-base", + "gstreamer1.0-plugins-good", + "libicu74", + "libatomic1", + "libatk-bridge2.0-0t64", + "libatk1.0-0t64", + "libcairo-gobject2", + "libcairo2", + "libdbus-1-3", + "libdrm2", + "libenchant-2-2", + "libepoxy0", + "libevent-2.1-7t64", + "libflite1", + "libfontconfig1", + "libfreetype6", + "libgbm1", + "libgdk-pixbuf-2.0-0", + "libgles2", + "libglib2.0-0t64", + "libgstreamer-gl1.0-0", + "libgstreamer-plugins-bad1.0-0", + "libgstreamer-plugins-base1.0-0", + "libgstreamer1.0-0", + "libgtk-4-1", + "libharfbuzz-icu0", + "libharfbuzz0b", + "libhyphen0", + "libicu74", + "libjpeg-turbo8", + "liblcms2-2", + "libmanette-0.2-0", + "libopus0", + "libpango-1.0-0", + "libpangocairo-1.0-0", + "libpng16-16t64", + "libsecret-1-0", + "libvpx9", + "libwayland-client0", + "libwayland-egl1", + "libwayland-server0", + "libwebp7", + "libwebpdemux2", + "libwoff1", + "libx11-6", + "libxkbcommon0", + "libxml2", + "libxslt1.1", + "libx264-164", + "libavif16" + ], + lib2package: { + "libavif.so.16": "libavif16", + "libasound.so.2": "libasound2t64", + "libatk-1.0.so.0": "libatk1.0-0t64", + "libatk-bridge-2.0.so.0": "libatk-bridge2.0-0t64", + "libatomic.so.1": "libatomic1", + "libatspi.so.0": "libatspi2.0-0t64", + "libcairo-gobject.so.2": "libcairo-gobject2", + "libcairo.so.2": "libcairo2", + "libcups.so.2": "libcups2t64", + "libdbus-1.so.3": "libdbus-1-3", + "libdrm.so.2": "libdrm2", + "libenchant-2.so.2": "libenchant-2-2", + "libepoxy.so.0": "libepoxy0", + "libevent-2.1.so.7": "libevent-2.1-7t64", + "libflite_cmu_grapheme_lang.so.1": "libflite1", + "libflite_cmu_grapheme_lex.so.1": "libflite1", + "libflite_cmu_indic_lang.so.1": "libflite1", + "libflite_cmu_indic_lex.so.1": "libflite1", + "libflite_cmu_time_awb.so.1": "libflite1", + "libflite_cmu_us_awb.so.1": "libflite1", + "libflite_cmu_us_kal.so.1": "libflite1", + "libflite_cmu_us_kal16.so.1": "libflite1", + "libflite_cmu_us_rms.so.1": "libflite1", + "libflite_cmu_us_slt.so.1": "libflite1", + "libflite_cmulex.so.1": "libflite1", + "libflite_usenglish.so.1": "libflite1", + "libflite.so.1": "libflite1", + "libfontconfig.so.1": "libfontconfig1", + "libfreetype.so.6": "libfreetype6", + "libgbm.so.1": "libgbm1", + "libgdk_pixbuf-2.0.so.0": "libgdk-pixbuf-2.0-0", + "libgdk-3.so.0": "libgtk-3-0t64", + "libgio-2.0.so.0": "libglib2.0-0t64", + "libGLESv2.so.2": "libgles2", + "libglib-2.0.so.0": "libglib2.0-0t64", + "libgmodule-2.0.so.0": "libglib2.0-0t64", + "libgobject-2.0.so.0": "libglib2.0-0t64", + "libgstallocators-1.0.so.0": "libgstreamer-plugins-base1.0-0", + "libgstapp-1.0.so.0": "libgstreamer-plugins-base1.0-0", + "libgstaudio-1.0.so.0": "libgstreamer-plugins-base1.0-0", + "libgstbase-1.0.so.0": "libgstreamer1.0-0", + "libgstcodecparsers-1.0.so.0": "libgstreamer-plugins-bad1.0-0", + "libgstfft-1.0.so.0": "libgstreamer-plugins-base1.0-0", + "libgstgl-1.0.so.0": "libgstreamer-gl1.0-0", + "libgstpbutils-1.0.so.0": "libgstreamer-plugins-base1.0-0", + "libgstreamer-1.0.so.0": "libgstreamer1.0-0", + "libgsttag-1.0.so.0": "libgstreamer-plugins-base1.0-0", + "libgstvideo-1.0.so.0": "libgstreamer-plugins-base1.0-0", + "libgtk-3.so.0": "libgtk-3-0t64", + "libgtk-4.so.1": "libgtk-4-1", + "libharfbuzz-icu.so.0": "libharfbuzz-icu0", + "libharfbuzz.so.0": "libharfbuzz0b", + "libhyphen.so.0": "libhyphen0", + "libicudata.so.74": "libicu74", + "libicui18n.so.74": "libicu74", + "libicuuc.so.74": "libicu74", + "libjpeg.so.8": "libjpeg-turbo8", + "liblcms2.so.2": "liblcms2-2", + "libmanette-0.2.so.0": "libmanette-0.2-0", + "libnspr4.so": "libnspr4", + "libnss3.so": "libnss3", + "libnssutil3.so": "libnss3", + "libopus.so.0": "libopus0", + "libpango-1.0.so.0": "libpango-1.0-0", + "libpangocairo-1.0.so.0": "libpangocairo-1.0-0", + "libpng16.so.16": "libpng16-16t64", + "libsecret-1.so.0": "libsecret-1-0", + "libsmime3.so": "libnss3", + "libsoup-3.0.so.0": "libsoup-3.0-0", + "libvpx.so.9": "libvpx9", + "libwayland-client.so.0": "libwayland-client0", + "libwayland-egl.so.1": "libwayland-egl1", + "libwayland-server.so.0": "libwayland-server0", + "libwebp.so.7": "libwebp7", + "libwebpdemux.so.2": "libwebpdemux2", + "libwoff2dec.so.1.0.2": "libwoff1", + "libX11-xcb.so.1": "libx11-xcb1", + "libX11.so.6": "libx11-6", + "libxcb-shm.so.0": "libxcb-shm0", + "libxcb.so.1": "libxcb1", + "libXcomposite.so.1": "libxcomposite1", + "libXcursor.so.1": "libxcursor1", + "libXdamage.so.1": "libxdamage1", + "libXext.so.6": "libxext6", + "libXfixes.so.3": "libxfixes3", + "libXi.so.6": "libxi6", + "libxkbcommon.so.0": "libxkbcommon0", + "libxml2.so.2": "libxml2", + "libXrandr.so.2": "libxrandr2", + "libXrender.so.1": "libxrender1", + "libxslt.so.1": "libxslt1.1", + "libx264.so": "libx264-164" + } + }, + "ubuntu26.04-x64": { + tools: [ + "xvfb", + "fonts-noto-color-emoji", + "fonts-unifont", + "libfontconfig1", + "libfreetype6", + "xfonts-cyrillic", + "xfonts-scalable", + "fonts-liberation", + "fonts-ipafont-gothic", + "fonts-wqy-zenhei", + "fonts-tlwg-loma-otf", + "fonts-freefont-ttf" + ], + chromium: [ + "libasound2t64", + "libatk-bridge2.0-0t64", + "libatk1.0-0t64", + "libatspi2.0-0t64", + "libcairo2", + "libcups2t64", + "libdbus-1-3", + "libdrm2", + "libgbm1", + "libglib2.0-0t64", + "libnspr4", + "libnss3", + "libpango-1.0-0", + "libx11-6", + "libxcb1", + "libxcomposite1", + "libxdamage1", + "libxext6", + "libxfixes3", + "libxkbcommon0", + "libxrandr2" + ], + firefox: [ + "libasound2t64", + "libatk1.0-0t64", + "libavcodec62", + "libcairo-gobject2", + "libcairo2", + "libdbus-1-3", + "libfontconfig1", + "libfreetype6", + "libgdk-pixbuf-2.0-0", + "libglib2.0-0t64", + "libgtk-3-0t64", + "libpango-1.0-0", + "libpangocairo-1.0-0", + "libx11-6", + "libx11-xcb1", + "libxcb-shm0", + "libxcb1", + "libxcomposite1", + "libxcursor1", + "libxdamage1", + "libxext6", + "libxfixes3", + "libxi6", + "libxrandr2", + "libxrender1" + ], + webkit: [ + "gstreamer1.0-libav", + "gstreamer1.0-plugins-bad", + "gstreamer1.0-plugins-base", + "gstreamer1.0-plugins-good", + "libicu78", + "libatomic1", + "libatk-bridge2.0-0t64", + "libatk1.0-0t64", + "libcairo-gobject2", + "libcairo2", + "libdbus-1-3", + "libdrm2", + "libenchant-2-2", + "libepoxy0", + "libevent-2.1-7t64", + "libflite1", + "libfontconfig1", + "libfreetype6", + "libgbm1", + "libgdk-pixbuf-2.0-0", + "libgles2", + "libglib2.0-0t64", + "libgstreamer-gl1.0-0", + "libgstreamer-plugins-bad1.0-0", + "libgstreamer-plugins-base1.0-0", + "libgstreamer1.0-0", + "libgtk-4-1", + "libharfbuzz-icu0", + "libharfbuzz0b", + "libhyphen0", + "libjpeg-turbo8", + "liblcms2-2", + "libmanette-0.2-0", + "libopus0", + "libpango-1.0-0", + "libpangocairo-1.0-0", + "libpng16-16t64", + "libsecret-1-0", + "libvpx12", + "libwayland-client0", + "libwayland-egl1", + "libwayland-server0", + "libwebp7", + "libwebpdemux2", + "libwoff1", + "libx11-6", + "libxkbcommon0", + "libxml2-16", + "libxslt1.1", + "libx264-165", + "libavif16" + ], + lib2package: { + "libavif.so.16": "libavif16", + "libasound.so.2": "libasound2t64", + "libatk-1.0.so.0": "libatk1.0-0t64", + "libatk-bridge-2.0.so.0": "libatk-bridge2.0-0t64", + "libatomic.so.1": "libatomic1", + "libatspi.so.0": "libatspi2.0-0t64", + "libcairo-gobject.so.2": "libcairo-gobject2", + "libcairo.so.2": "libcairo2", + "libcups.so.2": "libcups2t64", + "libdbus-1.so.3": "libdbus-1-3", + "libdrm.so.2": "libdrm2", + "libenchant-2.so.2": "libenchant-2-2", + "libepoxy.so.0": "libepoxy0", + "libevent-2.1.so.7": "libevent-2.1-7t64", + "libflite_cmu_grapheme_lang.so.1": "libflite1", + "libflite_cmu_grapheme_lex.so.1": "libflite1", + "libflite_cmu_indic_lang.so.1": "libflite1", + "libflite_cmu_indic_lex.so.1": "libflite1", + "libflite_cmu_time_awb.so.1": "libflite1", + "libflite_cmu_us_awb.so.1": "libflite1", + "libflite_cmu_us_kal.so.1": "libflite1", + "libflite_cmu_us_kal16.so.1": "libflite1", + "libflite_cmu_us_rms.so.1": "libflite1", + "libflite_cmu_us_slt.so.1": "libflite1", + "libflite_cmulex.so.1": "libflite1", + "libflite_usenglish.so.1": "libflite1", + "libflite.so.1": "libflite1", + "libfontconfig.so.1": "libfontconfig1", + "libfreetype.so.6": "libfreetype6", + "libgbm.so.1": "libgbm1", + "libgdk_pixbuf-2.0.so.0": "libgdk-pixbuf-2.0-0", + "libgdk-3.so.0": "libgtk-3-0t64", + "libgio-2.0.so.0": "libglib2.0-0t64", + "libGLESv2.so.2": "libgles2", + "libglib-2.0.so.0": "libglib2.0-0t64", + "libgmodule-2.0.so.0": "libglib2.0-0t64", + "libgobject-2.0.so.0": "libglib2.0-0t64", + "libgstallocators-1.0.so.0": "libgstreamer-plugins-base1.0-0", + "libgstapp-1.0.so.0": "libgstreamer-plugins-base1.0-0", + "libgstaudio-1.0.so.0": "libgstreamer-plugins-base1.0-0", + "libgstbase-1.0.so.0": "libgstreamer1.0-0", + "libgstcodecparsers-1.0.so.0": "libgstreamer-plugins-bad1.0-0", + "libgstfft-1.0.so.0": "libgstreamer-plugins-base1.0-0", + "libgstgl-1.0.so.0": "libgstreamer-gl1.0-0", + "libgstpbutils-1.0.so.0": "libgstreamer-plugins-base1.0-0", + "libgstreamer-1.0.so.0": "libgstreamer1.0-0", + "libgsttag-1.0.so.0": "libgstreamer-plugins-base1.0-0", + "libgstvideo-1.0.so.0": "libgstreamer-plugins-base1.0-0", + "libgtk-3.so.0": "libgtk-3-0t64", + "libgtk-4.so.1": "libgtk-4-1", + "libharfbuzz-icu.so.0": "libharfbuzz-icu0", + "libharfbuzz.so.0": "libharfbuzz0b", + "libhyphen.so.0": "libhyphen0", + "libicudata.so.78": "libicu78", + "libicui18n.so.78": "libicu78", + "libicuuc.so.78": "libicu78", + "libjpeg.so.8": "libjpeg-turbo8", + "liblcms2.so.2": "liblcms2-2", + "libmanette-0.2.so.0": "libmanette-0.2-0", + "libnspr4.so": "libnspr4", + "libnss3.so": "libnss3", + "libnssutil3.so": "libnss3", + "libopus.so.0": "libopus0", + "libpango-1.0.so.0": "libpango-1.0-0", + "libpangocairo-1.0.so.0": "libpangocairo-1.0-0", + "libpng16.so.16": "libpng16-16t64", + "libsecret-1.so.0": "libsecret-1-0", + "libsmime3.so": "libnss3", + "libsoup-3.0.so.0": "libsoup-3.0-0", + "libvpx.so.12": "libvpx12", + "libwayland-client.so.0": "libwayland-client0", + "libwayland-egl.so.1": "libwayland-egl1", + "libwayland-server.so.0": "libwayland-server0", + "libwebp.so.7": "libwebp7", + "libwebpdemux.so.2": "libwebpdemux2", + "libwoff2dec.so.1.0.2": "libwoff1", + "libX11-xcb.so.1": "libx11-xcb1", + "libX11.so.6": "libx11-6", + "libxcb-shm.so.0": "libxcb-shm0", + "libxcb.so.1": "libxcb1", + "libXcomposite.so.1": "libxcomposite1", + "libXcursor.so.1": "libxcursor1", + "libXdamage.so.1": "libxdamage1", + "libXext.so.6": "libxext6", + "libXfixes.so.3": "libxfixes3", + "libXi.so.6": "libxi6", + "libxkbcommon.so.0": "libxkbcommon0", + "libxml2.so.16": "libxml2-16", + "libXrandr.so.2": "libxrandr2", + "libXrender.so.1": "libxrender1", + "libxslt.so.1": "libxslt1.1", + "libx264.so": "libx264-165" + } + }, + "debian11-x64": { + tools: [ + "xvfb", + "fonts-noto-color-emoji", + "fonts-unifont", + "libfontconfig1", + "libfreetype6", + "xfonts-cyrillic", + "xfonts-scalable", + "fonts-liberation", + "fonts-ipafont-gothic", + "fonts-wqy-zenhei", + "fonts-tlwg-loma-otf", + "fonts-freefont-ttf" + ], + chromium: [ + "libasound2", + "libatk-bridge2.0-0", + "libatk1.0-0", + "libatspi2.0-0", + "libcairo2", + "libcups2", + "libdbus-1-3", + "libdrm2", + "libgbm1", + "libglib2.0-0", + "libnspr4", + "libnss3", + "libpango-1.0-0", + "libwayland-client0", + "libx11-6", + "libxcb1", + "libxcomposite1", + "libxdamage1", + "libxext6", + "libxfixes3", + "libxkbcommon0", + "libxrandr2" + ], + firefox: [ + "libasound2", + "libatk1.0-0", + "libavcodec58", + "libcairo-gobject2", + "libcairo2", + "libdbus-1-3", + "libdbus-glib-1-2", + "libfontconfig1", + "libfreetype6", + "libgdk-pixbuf-2.0-0", + "libglib2.0-0", + "libgtk-3-0", + "libharfbuzz0b", + "libpango-1.0-0", + "libpangocairo-1.0-0", + "libx11-6", + "libx11-xcb1", + "libxcb-shm0", + "libxcb1", + "libxcomposite1", + "libxcursor1", + "libxdamage1", + "libxext6", + "libxfixes3", + "libxi6", + "libxrandr2", + "libxrender1", + "libxtst6" + ], + webkit: [ + "gstreamer1.0-libav", + "gstreamer1.0-plugins-bad", + "gstreamer1.0-plugins-base", + "gstreamer1.0-plugins-good", + "libatk-bridge2.0-0", + "libatk1.0-0", + "libcairo2", + "libdbus-1-3", + "libdrm2", + "libegl1", + "libenchant-2-2", + "libepoxy0", + "libevdev2", + "libfontconfig1", + "libfreetype6", + "libgbm1", + "libgdk-pixbuf-2.0-0", + "libgles2", + "libglib2.0-0", + "libglx0", + "libgstreamer-gl1.0-0", + "libgstreamer-plugins-base1.0-0", + "libgstreamer1.0-0", + "libgtk-3-0", + "libgudev-1.0-0", + "libharfbuzz-icu0", + "libharfbuzz0b", + "libhyphen0", + "libicu67", + "libjpeg62-turbo", + "liblcms2-2", + "libmanette-0.2-0", + "libnghttp2-14", + "libnotify4", + "libopengl0", + "libopenjp2-7", + "libopus0", + "libpango-1.0-0", + "libpng16-16", + "libproxy1v5", + "libsecret-1-0", + "libwayland-client0", + "libwayland-egl1", + "libwayland-server0", + "libwebp6", + "libwebpdemux2", + "libwoff1", + "libx11-6", + "libxcomposite1", + "libxdamage1", + "libxkbcommon0", + "libxml2", + "libxslt1.1", + "libatomic1", + "libevent-2.1-7" + ], + lib2package: { + "libasound.so.2": "libasound2", + "libatk-1.0.so.0": "libatk1.0-0", + "libatk-bridge-2.0.so.0": "libatk-bridge2.0-0", + "libatspi.so.0": "libatspi2.0-0", + "libcairo-gobject.so.2": "libcairo-gobject2", + "libcairo.so.2": "libcairo2", + "libcups.so.2": "libcups2", + "libdbus-1.so.3": "libdbus-1-3", + "libdbus-glib-1.so.2": "libdbus-glib-1-2", + "libdrm.so.2": "libdrm2", + "libEGL.so.1": "libegl1", + "libenchant-2.so.2": "libenchant-2-2", + "libepoxy.so.0": "libepoxy0", + "libevdev.so.2": "libevdev2", + "libfontconfig.so.1": "libfontconfig1", + "libfreetype.so.6": "libfreetype6", + "libgbm.so.1": "libgbm1", + "libgdk_pixbuf-2.0.so.0": "libgdk-pixbuf-2.0-0", + "libgdk-3.so.0": "libgtk-3-0", + "libgio-2.0.so.0": "libglib2.0-0", + "libGLESv2.so.2": "libgles2", + "libglib-2.0.so.0": "libglib2.0-0", + "libGLX.so.0": "libglx0", + "libgmodule-2.0.so.0": "libglib2.0-0", + "libgobject-2.0.so.0": "libglib2.0-0", + "libgstallocators-1.0.so.0": "libgstreamer-plugins-base1.0-0", + "libgstapp-1.0.so.0": "libgstreamer-plugins-base1.0-0", + "libgstaudio-1.0.so.0": "libgstreamer-plugins-base1.0-0", + "libgstbase-1.0.so.0": "libgstreamer1.0-0", + "libgstfft-1.0.so.0": "libgstreamer-plugins-base1.0-0", + "libgstgl-1.0.so.0": "libgstreamer-gl1.0-0", + "libgstpbutils-1.0.so.0": "libgstreamer-plugins-base1.0-0", + "libgstreamer-1.0.so.0": "libgstreamer1.0-0", + "libgsttag-1.0.so.0": "libgstreamer-plugins-base1.0-0", + "libgstvideo-1.0.so.0": "libgstreamer-plugins-base1.0-0", + "libgtk-3.so.0": "libgtk-3-0", + "libgudev-1.0.so.0": "libgudev-1.0-0", + "libharfbuzz-icu.so.0": "libharfbuzz-icu0", + "libharfbuzz.so.0": "libharfbuzz0b", + "libhyphen.so.0": "libhyphen0", + "libicui18n.so.67": "libicu67", + "libicuuc.so.67": "libicu67", + "libjpeg.so.62": "libjpeg62-turbo", + "liblcms2.so.2": "liblcms2-2", + "libmanette-0.2.so.0": "libmanette-0.2-0", + "libnotify.so.4": "libnotify4", + "libnspr4.so": "libnspr4", + "libnss3.so": "libnss3", + "libnssutil3.so": "libnss3", + "libOpenGL.so.0": "libopengl0", + "libopenjp2.so.7": "libopenjp2-7", + "libopus.so.0": "libopus0", + "libpango-1.0.so.0": "libpango-1.0-0", + "libpangocairo-1.0.so.0": "libpangocairo-1.0-0", + "libpng16.so.16": "libpng16-16", + "libproxy.so.1": "libproxy1v5", + "libsecret-1.so.0": "libsecret-1-0", + "libsmime3.so": "libnss3", + "libwayland-client.so.0": "libwayland-client0", + "libwayland-egl.so.1": "libwayland-egl1", + "libwayland-server.so.0": "libwayland-server0", + "libwebp.so.6": "libwebp6", + "libwebpdemux.so.2": "libwebpdemux2", + "libwoff2dec.so.1.0.2": "libwoff1", + "libX11-xcb.so.1": "libx11-xcb1", + "libX11.so.6": "libx11-6", + "libxcb-shm.so.0": "libxcb-shm0", + "libxcb.so.1": "libxcb1", + "libXcomposite.so.1": "libxcomposite1", + "libXcursor.so.1": "libxcursor1", + "libXdamage.so.1": "libxdamage1", + "libXext.so.6": "libxext6", + "libXfixes.so.3": "libxfixes3", + "libXi.so.6": "libxi6", + "libxkbcommon.so.0": "libxkbcommon0", + "libxml2.so.2": "libxml2", + "libXrandr.so.2": "libxrandr2", + "libXrender.so.1": "libxrender1", + "libxslt.so.1": "libxslt1.1", + "libXtst.so.6": "libxtst6", + "libatomic.so.1": "libatomic1", + "libevent-2.1.so.7": "libevent-2.1-7" + } + }, + "debian12-x64": { + tools: [ + "xvfb", + "fonts-noto-color-emoji", + "fonts-unifont", + "libfontconfig1", + "libfreetype6", + "xfonts-scalable", + "fonts-liberation", + "fonts-ipafont-gothic", + "fonts-wqy-zenhei", + "fonts-tlwg-loma-otf", + "fonts-freefont-ttf" + ], + chromium: [ + "libasound2", + "libatk-bridge2.0-0", + "libatk1.0-0", + "libatspi2.0-0", + "libcairo2", + "libcups2", + "libdbus-1-3", + "libdrm2", + "libgbm1", + "libglib2.0-0", + "libnspr4", + "libnss3", + "libpango-1.0-0", + "libx11-6", + "libxcb1", + "libxcomposite1", + "libxdamage1", + "libxext6", + "libxfixes3", + "libxkbcommon0", + "libxrandr2" + ], + firefox: [ + "libasound2", + "libatk1.0-0", + "libavcodec59", + "libcairo-gobject2", + "libcairo2", + "libdbus-1-3", + "libdbus-glib-1-2", + "libfontconfig1", + "libfreetype6", + "libgdk-pixbuf-2.0-0", + "libglib2.0-0", + "libgtk-3-0", + "libharfbuzz0b", + "libpango-1.0-0", + "libpangocairo-1.0-0", + "libx11-6", + "libx11-xcb1", + "libxcb-shm0", + "libxcb1", + "libxcomposite1", + "libxcursor1", + "libxdamage1", + "libxext6", + "libxfixes3", + "libxi6", + "libxrandr2", + "libxrender1", + "libxtst6" + ], + webkit: [ + "libsoup-3.0-0", + "gstreamer1.0-libav", + "gstreamer1.0-plugins-bad", + "gstreamer1.0-plugins-base", + "gstreamer1.0-plugins-good", + "libatk-bridge2.0-0", + "libatk1.0-0", + "libcairo2", + "libdbus-1-3", + "libdrm2", + "libegl1", + "libenchant-2-2", + "libepoxy0", + "libevdev2", + "libfontconfig1", + "libfreetype6", + "libgbm1", + "libgdk-pixbuf-2.0-0", + "libgles2", + "libglib2.0-0", + "libglx0", + "libgstreamer-gl1.0-0", + "libgstreamer-plugins-base1.0-0", + "libgstreamer1.0-0", + "libgtk-4-1", + "libgudev-1.0-0", + "libharfbuzz-icu0", + "libharfbuzz0b", + "libhyphen0", + "libicu72", + "libjpeg62-turbo", + "liblcms2-2", + "libmanette-0.2-0", + "libnotify4", + "libopengl0", + "libopenjp2-7", + "libopus0", + "libpango-1.0-0", + "libpng16-16", + "libproxy1v5", + "libsecret-1-0", + "libwayland-client0", + "libwayland-egl1", + "libwayland-server0", + "libwebp7", + "libwebpdemux2", + "libwoff1", + "libx11-6", + "libxcomposite1", + "libxdamage1", + "libxkbcommon0", + "libxml2", + "libxslt1.1", + "libatomic1", + "libevent-2.1-7", + "libavif15" + ], + lib2package: { + "libavif.so.15": "libavif15", + "libsoup-3.0.so.0": "libsoup-3.0-0", + "libasound.so.2": "libasound2", + "libatk-1.0.so.0": "libatk1.0-0", + "libatk-bridge-2.0.so.0": "libatk-bridge2.0-0", + "libatspi.so.0": "libatspi2.0-0", + "libcairo.so.2": "libcairo2", + "libcups.so.2": "libcups2", + "libdbus-1.so.3": "libdbus-1-3", + "libdrm.so.2": "libdrm2", + "libgbm.so.1": "libgbm1", + "libgio-2.0.so.0": "libglib2.0-0", + "libglib-2.0.so.0": "libglib2.0-0", + "libgobject-2.0.so.0": "libglib2.0-0", + "libnspr4.so": "libnspr4", + "libnss3.so": "libnss3", + "libnssutil3.so": "libnss3", + "libpango-1.0.so.0": "libpango-1.0-0", + "libsmime3.so": "libnss3", + "libX11.so.6": "libx11-6", + "libxcb.so.1": "libxcb1", + "libXcomposite.so.1": "libxcomposite1", + "libXdamage.so.1": "libxdamage1", + "libXext.so.6": "libxext6", + "libXfixes.so.3": "libxfixes3", + "libxkbcommon.so.0": "libxkbcommon0", + "libXrandr.so.2": "libxrandr2", + "libgtk-4.so.1": "libgtk-4-1" + } + }, + "debian13-x64": { + tools: [ + "xvfb", + "fonts-noto-color-emoji", + "fonts-unifont", + "libfontconfig1", + "libfreetype6", + "xfonts-scalable", + "fonts-liberation", + "fonts-ipafont-gothic", + "fonts-wqy-zenhei", + "fonts-tlwg-loma-otf", + "fonts-freefont-ttf" + ], + chromium: [ + "libasound2t64", + "libatk-bridge2.0-0t64", + "libatk1.0-0t64", + "libatspi2.0-0t64", + "libcairo2", + "libcups2t64", + "libdbus-1-3", + "libdrm2", + "libgbm1", + "libglib2.0-0t64", + "libnspr4", + "libnss3", + "libpango-1.0-0", + "libx11-6", + "libxcb1", + "libxcomposite1", + "libxdamage1", + "libxext6", + "libxfixes3", + "libxkbcommon0", + "libxrandr2" + ], + firefox: [ + "libasound2", + "libatk1.0-0t64", + "libavcodec61", + "libcairo-gobject2", + "libcairo2", + "libdbus-1-3", + "libdbus-glib-1-2", + "libfontconfig1", + "libfreetype6", + "libgdk-pixbuf-2.0-0", + "libglib2.0-0t64", + "libgtk-3-0t64", + "libharfbuzz0b", + "libpango-1.0-0", + "libpangocairo-1.0-0", + "libx11-6", + "libx11-xcb1", + "libxcb-shm0", + "libxcb1", + "libxcomposite1", + "libxcursor1", + "libxdamage1", + "libxext6", + "libxfixes3", + "libxi6", + "libxrandr2", + "libxrender1", + "libxtst6" + ], + webkit: [ + "libsoup-3.0-0", + "gstreamer1.0-libav", + "gstreamer1.0-plugins-bad", + "gstreamer1.0-plugins-base", + "gstreamer1.0-plugins-good", + "libatk-bridge2.0-0t64", + "libatk1.0-0t64", + "libcairo2", + "libdbus-1-3", + "libdrm2", + "libegl1", + "libenchant-2-2", + "libepoxy0", + "libevdev2", + "libfontconfig1", + "libfreetype6", + "libgbm1", + "libgdk-pixbuf-2.0-0", + "libgles2", + "libglib2.0-0t64", + "libglx0", + "libgstreamer-gl1.0-0", + "libgstreamer-plugins-base1.0-0", + "libgstreamer1.0-0", + "libgtk-4-1", + "libgudev-1.0-0", + "libharfbuzz-icu0", + "libharfbuzz0b", + "libhyphen0", + "libicu76", + "libjpeg62-turbo", + "liblcms2-2", + "libmanette-0.2-0", + "libnotify4", + "libopengl0", + "libopenjp2-7", + "libopus0", + "libpango-1.0-0", + "libpng16-16t64", + "libproxy1v5", + "libsecret-1-0", + "libwayland-client0", + "libwayland-egl1", + "libwayland-server0", + "libwebp7", + "libwebpdemux2", + "libwoff1", + "libx11-6", + "libxcomposite1", + "libxdamage1", + "libxkbcommon0", + "libxml2", + "libxslt1.1", + "libatomic1", + "libevent-2.1-7t64", + "libavif16" + ], + lib2package: { + "libicudata.so.74": "libicu76", + "libicui18n.so.74": "libicu76", + "libicuuc.so.74": "libicu76", + "libevent-2.1.so.7": "libevent-2.1-7t64", + "libpng16.so.16": "libpng16-16t64", + "libgdk-3.so.0": "libgtk-3-0t64", + "libgtk-3.so.0": "libgtk-3-0t64", + "libavif.so.16": "libavif16", + "libsoup-3.0.so.0": "libsoup-3.0-0", + "libasound.so.2": "libasound2t64", + "libatk-1.0.so.0": "libatk1.0-0t64", + "libatk-bridge-2.0.so.0": "libatk-bridge2.0-0t64", + "libatspi.so.0": "libatspi2.0-0t64", + "libcairo.so.2": "libcairo2", + "libcups.so.2": "libcups2t64", + "libdbus-1.so.3": "libdbus-1-3", + "libdrm.so.2": "libdrm2", + "libgbm.so.1": "libgbm1", + "libgio-2.0.so.0": "libglib2.0-0t64", + "libglib-2.0.so.0": "libglib2.0-0t64", + "libgobject-2.0.so.0": "libglib2.0-0t64", + "libnspr4.so": "libnspr4", + "libnss3.so": "libnss3", + "libnssutil3.so": "libnss3", + "libpango-1.0.so.0": "libpango-1.0-0", + "libsmime3.so": "libnss3", + "libX11.so.6": "libx11-6", + "libxcb.so.1": "libxcb1", + "libXcomposite.so.1": "libxcomposite1", + "libXdamage.so.1": "libxdamage1", + "libXext.so.6": "libxext6", + "libXfixes.so.3": "libxfixes3", + "libxkbcommon.so.0": "libxkbcommon0", + "libXrandr.so.2": "libxrandr2", + "libgtk-4.so.1": "libgtk-4-1" + } + } + }; + deps["ubuntu20.04-arm64"] = { + tools: [...deps["ubuntu20.04-x64"].tools], + chromium: [...deps["ubuntu20.04-x64"].chromium], + firefox: [ + ...deps["ubuntu20.04-x64"].firefox + ], + webkit: [ + ...deps["ubuntu20.04-x64"].webkit + ], + lib2package: { + ...deps["ubuntu20.04-x64"].lib2package + } + }; + deps["ubuntu22.04-arm64"] = { + tools: [...deps["ubuntu22.04-x64"].tools], + chromium: [...deps["ubuntu22.04-x64"].chromium], + firefox: [ + ...deps["ubuntu22.04-x64"].firefox + ], + webkit: [ + ...deps["ubuntu22.04-x64"].webkit + ], + lib2package: { + ...deps["ubuntu22.04-x64"].lib2package + } + }; + deps["ubuntu24.04-arm64"] = { + tools: [...deps["ubuntu24.04-x64"].tools], + chromium: [...deps["ubuntu24.04-x64"].chromium], + firefox: [ + ...deps["ubuntu24.04-x64"].firefox + ], + webkit: [ + ...deps["ubuntu24.04-x64"].webkit + ], + lib2package: { + ...deps["ubuntu24.04-x64"].lib2package + } + }; + deps["ubuntu26.04-arm64"] = { + tools: [...deps["ubuntu26.04-x64"].tools], + chromium: [...deps["ubuntu26.04-x64"].chromium], + firefox: [ + ...deps["ubuntu26.04-x64"].firefox + ], + webkit: [ + ...deps["ubuntu26.04-x64"].webkit + ], + lib2package: { + ...deps["ubuntu26.04-x64"].lib2package + } + }; + deps["debian11-arm64"] = { + tools: [...deps["debian11-x64"].tools], + chromium: [...deps["debian11-x64"].chromium], + firefox: [ + ...deps["debian11-x64"].firefox + ], + webkit: [ + ...deps["debian11-x64"].webkit + ], + lib2package: { + ...deps["debian11-x64"].lib2package + } + }; + deps["debian12-arm64"] = { + tools: [...deps["debian12-x64"].tools], + chromium: [...deps["debian12-x64"].chromium], + firefox: [ + ...deps["debian12-x64"].firefox + ], + webkit: [ + ...deps["debian12-x64"].webkit + ], + lib2package: { + ...deps["debian12-x64"].lib2package + } + }; + deps["debian13-arm64"] = { + tools: [...deps["debian13-x64"].tools], + chromium: [...deps["debian13-x64"].chromium], + firefox: [ + ...deps["debian13-x64"].firefox + ], + webkit: [ + ...deps["debian13-x64"].webkit + ], + lib2package: { + ...deps["debian13-x64"].lib2package + } + }; + } +}); + +// packages/playwright-core/src/server/registry/dependencies.ts +async function writeDockerVersion(dockerImageNameTemplate) { + await import_fs16.default.promises.mkdir(import_path16.default.dirname(dockerVersionFilePath), { recursive: true }); + await import_fs16.default.promises.writeFile(dockerVersionFilePath, JSON.stringify(dockerVersion(dockerImageNameTemplate), null, 2), "utf8"); + await import_fs16.default.promises.chmod(dockerVersionFilePath, 511); +} +function dockerVersion(dockerImageNameTemplate) { + return { + driverVersion: languageBindingVersion, + dockerImageName: dockerImageNameTemplate.replace("%version%", languageBindingVersion) + }; +} +function readDockerVersionSync() { + try { + const data = JSON.parse(import_fs16.default.readFileSync(dockerVersionFilePath, "utf8")); + return { + ...data, + dockerImageNameTemplate: data.dockerImageName.replace(data.driverVersion, "%version%") + }; + } catch (e) { + return null; + } +} +function isSupportedWindowsVersion() { + if (import_os6.default.platform() !== "win32" || import_os6.default.arch() !== "x64") + return false; + const [major2, minor] = import_os6.default.release().split(".").map((token) => parseInt(token, 10)); + return major2 > 6 || major2 === 6 && minor > 1; +} +async function installDependenciesWindows(targets, dryRun) { + if (targets.has("chromium")) { + const command = "powershell.exe"; + const args = ["-ExecutionPolicy", "Bypass", "-File", import_path16.default.join(binPath, "install_media_pack.ps1")]; + if (dryRun) { + console.log(`${command} ${quoteProcessArgs(args).join(" ")}`); + return; + } + const { code } = await spawnAsync(command, args, { cwd: binPath, stdio: "inherit" }); + if (code !== 0) + throw new Error("Failed to install windows dependencies!"); + } +} +async function installDependenciesLinux(targets, dryRun) { + const libraries = []; + const platform = hostPlatform; + if (!isOfficiallySupportedPlatform) + console.warn(`BEWARE: your OS is not officially supported by Playwright; installing dependencies for ${platform} as a fallback.`); + for (const target of targets) { + const info = deps[platform]; + if (!info) { + console.warn(`Cannot install dependencies for ${platform} with Playwright ${getPlaywrightVersion()}!`); + return; + } + libraries.push(...info[target]); + } + const uniqueLibraries = Array.from(new Set(libraries)); + if (dryRun) { + await reportMissingDependenciesLinux(uniqueLibraries); + return; + } + console.log(`Installing dependencies...`); + const commands2 = []; + commands2.push("apt-get update"); + commands2.push([ + "apt-get", + "install", + "-y", + "--no-install-recommends", + ...uniqueLibraries + ].join(" ")); + const { command, args, elevatedPermissions } = await transformCommandsForRoot(commands2); + if (elevatedPermissions) + console.log("Switching to root user to install dependencies..."); + const child = childProcess2.spawn(command, args, { stdio: "inherit" }); + await new Promise((resolve, reject) => { + child.on("exit", (code) => code === 0 ? resolve() : reject(new Error(`Installation process exited with code: ${code}`))); + child.on("error", reject); + }); +} +async function reportMissingDependenciesLinux(packages) { + const { code, stdout, stderr, error } = await spawnAsync("apt-get", ["install", "-s", "--no-install-recommends", ...packages], {}); + if (error) + throw new Error(`Failed to run 'apt-get install -s' to simulate dependency install: ${error.message}`); + if (code !== 0) + throw new Error(`'apt-get install -s' exited with code ${code}: +${stderr || stdout}`); + const missingPackages = []; + for (const line of stdout.split("\n")) { + const match = /^Inst (\S+) /.exec(line); + if (match) + missingPackages.push(match[1]); + } + if (!missingPackages.length) { + console.log("All system dependencies are installed."); + return; + } + console.log(`Missing system dependencies (${missingPackages.length}): +${missingPackages.sort().map((p) => ` ${p}`).join("\n")}`); + process.exitCode = 1; +} +async function validateDependenciesWindows(sdkLanguage, windowsExeAndDllDirectories) { + const directoryPaths = windowsExeAndDllDirectories; + const lddPaths = []; + for (const directoryPath of directoryPaths) + lddPaths.push(...await executablesOrSharedLibraries(directoryPath)); + const allMissingDeps = await Promise.all(lddPaths.map((lddPath) => missingFileDependenciesWindows(sdkLanguage, lddPath))); + const missingDeps = /* @__PURE__ */ new Set(); + for (const deps2 of allMissingDeps) { + for (const dep of deps2) + missingDeps.add(dep); + } + if (!missingDeps.size) + return; + let isCrtMissing = false; + let isMediaFoundationMissing = false; + for (const dep of missingDeps) { + if (dep.startsWith("api-ms-win-crt") || dep === "vcruntime140.dll" || dep === "vcruntime140_1.dll" || dep === "msvcp140.dll") + isCrtMissing = true; + else if (dep === "mf.dll" || dep === "mfplat.dll" || dep === "msmpeg2vdec.dll" || dep === "evr.dll" || dep === "avrt.dll") + isMediaFoundationMissing = true; + } + const details = []; + if (isCrtMissing) { + details.push( + `Some of the Universal C Runtime files cannot be found on the system. You can fix`, + `that by installing Microsoft Visual C++ Redistributable for Visual Studio from:`, + `https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads`, + `` + ); + } + if (isMediaFoundationMissing) { + details.push( + `Some of the Media Foundation files cannot be found on the system. If you are`, + `on Windows Server try fixing this by running the following command in PowerShell`, + `as Administrator:`, + ``, + ` Install-WindowsFeature Server-Media-Foundation`, + ``, + `For Windows N editions visit:`, + `https://support.microsoft.com/en-us/help/3145500/media-feature-pack-list-for-windows-n-editions`, + `` + ); + } + details.push( + `Full list of missing libraries:`, + ` ${[...missingDeps].join("\n ")}`, + `` + ); + const message = `Host system is missing dependencies! + +${details.join("\n")}`; + if (isSupportedWindowsVersion()) { + throw new Error(message); + } else { + console.warn(`WARNING: running on unsupported windows version!`); + console.warn(message); + } +} +async function validateDependenciesLinux(sdkLanguage, linuxLddDirectories, dlOpenLibraries) { + const directoryPaths = linuxLddDirectories; + const lddPaths = []; + for (const directoryPath of directoryPaths) + lddPaths.push(...await executablesOrSharedLibraries(directoryPath)); + const missingDepsPerFile = await Promise.all(lddPaths.map((lddPath) => missingFileDependencies(lddPath, directoryPaths))); + const missingDeps = /* @__PURE__ */ new Set(); + for (const deps2 of missingDepsPerFile) { + for (const dep of deps2) + missingDeps.add(dep); + } + for (const dep of await missingDLOPENLibraries(dlOpenLibraries)) + missingDeps.add(dep); + if (!missingDeps.size) + return; + const allMissingDeps = new Set(missingDeps); + const missingPackages = /* @__PURE__ */ new Set(); + const libraryToPackageNameMapping = deps[hostPlatform] ? { + ...deps[hostPlatform]?.lib2package || {}, + ...MANUAL_LIBRARY_TO_PACKAGE_NAME_UBUNTU + } : {}; + for (const missingDep of missingDeps) { + const packageName = libraryToPackageNameMapping[missingDep]; + if (packageName) { + missingPackages.add(packageName); + missingDeps.delete(missingDep); + } + } + const maybeSudo = process.getuid?.() && import_os6.default.platform() !== "win32" ? "sudo " : ""; + const dockerInfo = readDockerVersionSync(); + const errorLines = [ + `Host system is missing dependencies to run browsers.` + ]; + if (dockerInfo && !dockerInfo.driverVersion.startsWith(getPlaywrightVersion( + true + /* majorMinorOnly */ + ) + ".")) { + const pwVersion = getPlaywrightVersion(); + const requiredDockerImage = dockerInfo.dockerImageName.replace(dockerInfo.driverVersion, pwVersion); + errorLines.push(...[ + `This is most likely due to Docker image version not matching Playwright version:`, + `- Playwright : ${pwVersion}`, + `- Docker image: ${dockerInfo.driverVersion}`, + ``, + `Either:`, + `- (recommended) use Docker image "${requiredDockerImage}"`, + `- (alternative 1) run the following command inside Docker to install missing dependencies:`, + ``, + ` ${maybeSudo}${buildPlaywrightCLICommand(sdkLanguage, "install-deps")}`, + ``, + `- (alternative 2) use apt inside Docker:`, + ``, + ` ${maybeSudo}apt-get install ${[...missingPackages].join("\\\n ")}`, + ``, + `<3 Playwright Team` + ]); + } else if (missingPackages.size && !missingDeps.size) { + errorLines.push(...[ + `Please install them with the following command:`, + ``, + ` ${maybeSudo}${buildPlaywrightCLICommand(sdkLanguage, "install-deps")}`, + ``, + `Alternatively, use apt:`, + ` ${maybeSudo}apt-get install ${[...missingPackages].join("\\\n ")}`, + ``, + `<3 Playwright Team` + ]); + } else { + errorLines.push(...[ + `Missing libraries:`, + ...[...allMissingDeps].map((dep) => " " + dep) + ]); + } + throw new Error("\n" + wrapInASCIIBox(errorLines.join("\n"), 1)); +} +function isSharedLib(basename) { + switch (import_os6.default.platform()) { + case "linux": + return basename.endsWith(".so") || basename.includes(".so."); + case "win32": + return basename.endsWith(".dll"); + default: + return false; + } +} +async function executablesOrSharedLibraries(directoryPath) { + if (!import_fs16.default.existsSync(directoryPath)) + return []; + const allPaths = (await import_fs16.default.promises.readdir(directoryPath)).map((file) => import_path16.default.resolve(directoryPath, file)); + const allStats = await Promise.all(allPaths.map((aPath) => import_fs16.default.promises.stat(aPath))); + const filePaths = allPaths.filter((aPath, index) => allStats[index].isFile()); + const executablersOrLibraries = (await Promise.all(filePaths.map(async (filePath) => { + const basename = import_path16.default.basename(filePath).toLowerCase(); + if (isSharedLib(basename)) + return filePath; + if (await checkExecutable(filePath)) + return filePath; + return false; + }))).filter(Boolean); + return executablersOrLibraries; +} +async function missingFileDependenciesWindows(sdkLanguage, filePath) { + const executable = registry.findExecutable("winldd").executablePathOrDie(sdkLanguage); + const dirname = import_path16.default.dirname(filePath); + const { stdout, code } = await spawnAsync(executable, [filePath], { + cwd: dirname, + env: { + ...process.env, + LD_LIBRARY_PATH: process.env.LD_LIBRARY_PATH ? `${process.env.LD_LIBRARY_PATH}:${dirname}` : dirname + } + }); + if (code !== 0) + return []; + const missingDeps = stdout.split("\n").map((line) => line.trim()).filter((line) => line.endsWith("not found") && line.includes("=>")).map((line) => line.split("=>")[0].trim().toLowerCase()); + return missingDeps; +} +async function missingFileDependencies(filePath, extraLDPaths) { + const dirname = import_path16.default.dirname(filePath); + let LD_LIBRARY_PATH = extraLDPaths.join(":"); + if (process.env.LD_LIBRARY_PATH) + LD_LIBRARY_PATH = `${process.env.LD_LIBRARY_PATH}:${LD_LIBRARY_PATH}`; + const { stdout, code } = await spawnAsync("ldd", [filePath], { + cwd: dirname, + env: { + ...process.env, + LD_LIBRARY_PATH + } + }); + if (code !== 0) + return []; + const missingDeps = stdout.split("\n").map((line) => line.trim()).filter((line) => line.endsWith("not found") && line.includes("=>")).map((line) => line.split("=>")[0].trim()); + return missingDeps; +} +async function missingDLOPENLibraries(libraries) { + if (!libraries.length) + return []; + const { stdout, code, error } = await spawnAsync("/sbin/ldconfig", ["-p"], {}); + if (code !== 0 || error) + return []; + const isLibraryAvailable = (library) => stdout.toLowerCase().includes(library.toLowerCase()); + return libraries.filter((library) => !isLibraryAvailable(library)); +} +function quoteProcessArgs(args) { + return args.map((arg) => { + if (arg.includes(" ")) + return `"${arg}"`; + return arg; + }); +} +async function transformCommandsForRoot(commands2) { + const isRoot = process.getuid?.() === 0; + if (isRoot) + return { command: "sh", args: ["-c", `${commands2.join("&& ")}`], elevatedPermissions: false }; + const sudoExists = await spawnAsync("which", ["sudo"]); + if (sudoExists.code === 0) + return { command: "sudo", args: ["--", "sh", "-c", `${commands2.join("&& ")}`], elevatedPermissions: true }; + return { command: "su", args: ["root", "-c", `${commands2.join("&& ")}`], elevatedPermissions: true }; +} +var childProcess2, import_fs16, import_os6, import_path16, languageBindingVersion, dockerVersionFilePath, checkExecutable, MANUAL_LIBRARY_TO_PACKAGE_NAME_UBUNTU; +var init_dependencies = __esm({ + "packages/playwright-core/src/server/registry/dependencies.ts"() { + "use strict"; + childProcess2 = __toESM(require("child_process")); + import_fs16 = __toESM(require("fs")); + import_os6 = __toESM(require("os")); + import_path16 = __toESM(require("path")); + init_ascii(); + init_hostPlatform(); + init_spawnAsync(); + init_userAgent(); + init_nativeDeps(); + init_package(); + init_registry(); + languageBindingVersion = process.env.PW_CLI_DISPLAY_VERSION || packageJSON.version; + dockerVersionFilePath = "/ms-playwright/.docker-info"; + checkExecutable = (filePath) => { + if (process.platform === "win32") + return filePath.endsWith(".exe"); + return import_fs16.default.promises.access(filePath, import_fs16.default.constants.X_OK).then(() => true).catch(() => false); + }; + MANUAL_LIBRARY_TO_PACKAGE_NAME_UBUNTU = { + // libgstlibav.so (the only actual library provided by gstreamer1.0-libav) is not + // in the ldconfig cache, so we detect the actual library required for playing h.264 + // and if it's missing recommend installing missing gstreamer lib. + // gstreamer1.0-libav -> libavcodec57 -> libx264-152 + "libx264.so": "gstreamer1.0-libav" + }; + } +}); + +// packages/playwright-core/src/server/registry/browserFetcher.ts +async function downloadBrowserWithProgressBar(title, browserDirectory, executablePath, downloadURLs, downloadFileName, downloadSocketTimeout, force) { + if (await existsAsync(browserDirectoryToMarkerFilePath(browserDirectory))) { + debugLogger.log("install", `${title} is already downloaded.`); + if (force) + debugLogger.log("install", `force-downloading ${title}.`); + else + return; + } + const uniqueTempDir = await import_fs17.default.promises.mkdtemp(import_path17.default.join(import_os7.default.tmpdir(), "playwright-download-")); + const zipPath = import_path17.default.join(uniqueTempDir, downloadFileName); + try { + const retryCount = 5; + for (let attempt = 1; attempt <= retryCount; ++attempt) { + debugLogger.log("install", `downloading ${title} - attempt #${attempt}`); + const url2 = downloadURLs[(attempt - 1) % downloadURLs.length]; + logPolitely(`Downloading ${title}` + colors3.dim(` from ${url2}`)); + const { error } = await downloadBrowserWithProgressBarOutOfProcess(title, browserDirectory, url2, zipPath, executablePath, downloadSocketTimeout); + if (!error) { + debugLogger.log("install", `SUCCESS installing ${title}`); + break; + } + if (await existsAsync(zipPath)) + await import_fs17.default.promises.unlink(zipPath); + if (await existsAsync(browserDirectory)) + await removeFolders([browserDirectory]); + const errorMessage = error?.message || ""; + debugLogger.log("install", `attempt #${attempt} - ERROR: ${errorMessage}`); + if (attempt >= retryCount) + throw error; + } + } catch (e) { + debugLogger.log("install", `FAILED installation ${title} with error: ${e}`); + process.exitCode = 1; + throw e; + } finally { + await removeFolders([uniqueTempDir]); + } + logPolitely(`${title} downloaded to ${browserDirectory}`); +} +function downloadBrowserWithProgressBarOutOfProcess(title, browserDirectory, url2, zipPath, executablePath, socketTimeout) { + const cp = childProcess3.fork(libPath("entry", "oopBrowserDownload.js")); + const promise = new ManualPromise(); + const progress2 = getDownloadProgress(); + cp.on("message", (message) => { + if (message?.method === "log") + debugLogger.log("install", message.params.message); + if (message?.method === "progress") + progress2(message.params.done, message.params.total); + }); + cp.on("exit", (code) => { + if (code !== 0) { + promise.resolve({ error: new Error(`Download failure, code=${code}`) }); + return; + } + if (!import_fs17.default.existsSync(browserDirectoryToMarkerFilePath(browserDirectory))) + promise.resolve({ error: new Error(`Download failure, ${browserDirectoryToMarkerFilePath(browserDirectory)} does not exist`) }); + else + promise.resolve({ error: null }); + }); + cp.on("error", (error) => { + promise.resolve({ error }); + }); + debugLogger.log("install", `running download:`); + debugLogger.log("install", `-- from url: ${url2}`); + debugLogger.log("install", `-- to location: ${zipPath}`); + const downloadParams = { + title, + browserDirectory, + url: url2, + zipPath, + executablePath, + socketTimeout, + userAgent: getUserAgent() + }; + cp.send({ method: "download", params: downloadParams }); + return promise; +} +function logPolitely(toBeLogged) { + const logLevel = process.env.npm_config_loglevel; + const logLevelDisplay = ["silent", "error", "warn"].indexOf(logLevel || "") > -1; + if (!logLevelDisplay) + console.log(toBeLogged); +} +function getDownloadProgress() { + if (process.stdout.isTTY) + return getAnimatedDownloadProgress(); + return getBasicDownloadProgress(); +} +function getAnimatedDownloadProgress() { + let progressBar; + let lastDownloadedBytes = 0; + return (downloadedBytes, totalBytes) => { + if (!progressBar) { + progressBar = new ProgressBar( + `${toMegabytes( + totalBytes + )} [:bar] :percent :etas`, + { + complete: "=", + incomplete: " ", + width: 20, + total: totalBytes + } + ); + } + const delta = downloadedBytes - lastDownloadedBytes; + lastDownloadedBytes = downloadedBytes; + progressBar.tick(delta); + }; +} +function getBasicDownloadProgress() { + const totalRows = 10; + const stepWidth = 8; + let lastRow = -1; + return (downloadedBytes, totalBytes) => { + const percentage = downloadedBytes / totalBytes; + const row = Math.floor(totalRows * percentage); + if (row > lastRow) { + lastRow = row; + const percentageString = String(percentage * 100 | 0).padStart(3); + console.log(`|${"\u25A0".repeat(row * stepWidth)}${" ".repeat((totalRows - row) * stepWidth)}| ${percentageString}% of ${toMegabytes(totalBytes)}`); + } + }; +} +function toMegabytes(bytes) { + const mb = bytes / 1024 / 1024; + return `${Math.round(mb * 10) / 10} MiB`; +} +var childProcess3, import_fs17, import_os7, import_path17, ProgressBar, colors3; +var init_browserFetcher = __esm({ + "packages/playwright-core/src/server/registry/browserFetcher.ts"() { + "use strict"; + childProcess3 = __toESM(require("child_process")); + import_fs17 = __toESM(require("fs")); + import_os7 = __toESM(require("os")); + import_path17 = __toESM(require("path")); + init_manualPromise(); + init_debugLogger(); + init_fileUtils(); + init_userAgent(); + init_package(); + init_registry(); + ProgressBar = require("./utilsBundle").progress; + colors3 = require("./utilsBundle").colors; + } +}); + +// packages/playwright-core/src/server/registry/oopDownloadBrowserMain.ts +function log(message) { + process.send?.({ method: "log", params: { message } }); +} +function progress(done, total) { + process.send?.({ method: "progress", params: { done, total } }); +} +function browserDirectoryToMarkerFilePath2(browserDirectory) { + return import_path18.default.join(browserDirectory, "INSTALLATION_COMPLETE"); +} +function downloadFile(options) { + let downloadedBytes = 0; + let totalBytes = 0; + let chunked = false; + const reportProgress = !getAsBooleanFromENV("PLAYWRIGHT_DOWNLOAD_NO_PROGRESS"); + const promise = new ManualPromise(); + httpRequest({ + url: options.url, + headers: { + "User-Agent": options.userAgent + }, + socketTimeout: options.socketTimeout + }, (response2) => { + log(`-- response status code: ${response2.statusCode}`); + if (response2.statusCode !== 200) { + let content = ""; + const handleError = () => { + const error = new Error(`Download failed: server returned code ${response2.statusCode} body '${content}'. URL: ${options.url}`); + response2.resume(); + promise.reject(error); + }; + response2.on("data", (chunk) => content += chunk).on("end", handleError).on("error", handleError); + return; + } + chunked = response2.headers["transfer-encoding"] === "chunked"; + log(`-- is chunked: ${chunked}`); + totalBytes = parseInt(response2.headers["content-length"] || "0", 10); + log(`-- total bytes: ${totalBytes}`); + const file = import_fs18.default.createWriteStream(options.zipPath); + file.on("finish", () => { + if (!chunked && downloadedBytes !== totalBytes) { + log(`-- download failed, size mismatch: ${downloadedBytes} != ${totalBytes}`); + promise.reject(new Error(`Download failed: size mismatch, file size: ${downloadedBytes}, expected size: ${totalBytes} URL: ${options.url}`)); + } else { + log(`-- download complete, size: ${downloadedBytes}`); + promise.resolve(); + } + }); + file.on("error", (error) => promise.reject(error)); + response2.pipe(file); + response2.on("data", onData); + response2.on("error", (error) => { + file.close(); + if (error?.code === "ECONNRESET") { + log(`-- download failed, server closed connection`); + promise.reject(new Error(`Download failed: server closed connection. URL: ${options.url}`)); + } else { + log(`-- download failed, unexpected error`); + promise.reject(new Error(`Download failed: ${error?.message ?? error}. URL: ${options.url}`)); + } + }); + }, (error) => promise.reject(error)); + return promise; + function onData(chunk) { + downloadedBytes += chunk.length; + if (!chunked && reportProgress) + progress(downloadedBytes, totalBytes); + } +} +async function main(options) { + await downloadFile(options); + log(`SUCCESS downloading ${options.title}`); + log(`removing existing browser directory if any`); + await removeFolders([options.browserDirectory]); + log(`extracting archive`); + await extractZip(options.zipPath, { dir: options.browserDirectory }); + if (options.executablePath) { + log(`fixing permissions at ${options.executablePath}`); + await import_fs18.default.promises.chmod(options.executablePath, 493); + } + await import_fs18.default.promises.writeFile(browserDirectoryToMarkerFilePath2(options.browserDirectory), ""); +} +function runOopDownloadBrowserMain() { + process.on("message", async (message) => { + const { method, params: params2 } = message; + if (method === "download") { + try { + await main(params2); + process.exit(0); + } catch (e) { + console.error(e); + process.exit(1); + } + } + }); + process.on("disconnect", () => { + process.exit(0); + }); +} +var import_fs18, import_path18; +var init_oopDownloadBrowserMain = __esm({ + "packages/playwright-core/src/server/registry/oopDownloadBrowserMain.ts"() { + "use strict"; + import_fs18 = __toESM(require("fs")); + import_path18 = __toESM(require("path")); + init_manualPromise(); + init_env(); + init_network(); + init_fileUtils(); + init_extractZip(); + } +}); + +// packages/playwright-core/src/server/registry/index.ts +var registry_exports = {}; +__export(registry_exports, { + Registry: () => Registry, + browserDirectoryToMarkerFilePath: () => browserDirectoryToMarkerFilePath, + buildPlaywrightCLICommand: () => buildPlaywrightCLICommand, + defaultCacheDirectory: () => defaultCacheDirectory, + defaultRegistryDirectory: () => defaultRegistryDirectory, + findChromiumChannelBestEffort: () => findChromiumChannelBestEffort, + installBrowsersForNpmInstall: () => installBrowsersForNpmInstall, + registry: () => registry, + registryDirectory: () => registryDirectory, + runOopDownloadBrowserMain: () => runOopDownloadBrowserMain, + writeDockerVersion: () => writeDockerVersion +}); +function cftUrl(suffix) { + return ({ browserVersion }) => { + return { + path: `builds/cft/${browserVersion}/${suffix}`, + mirrors: [ + "https://cdn.playwright.dev" + ] + }; + }; +} +function isBrowserDirectory(browserDirectory) { + const baseName = import_path19.default.basename(browserDirectory); + for (const browserName of allDownloadableDirectoriesThatEverExisted) { + if (baseName.startsWith(browserName.replace(/-/g, "_") + "-")) + return true; + } + return false; +} +function readDescriptors(browsersJSON) { + return browsersJSON["browsers"].map((obj) => { + const name = obj.name; + const revisionOverride = (obj.revisionOverrides || {})[hostPlatform]; + const revision = revisionOverride || obj.revision; + const browserDirectoryPrefix = revisionOverride ? `${name}_${hostPlatform}_special` : `${name}`; + const descriptor = { + name, + revision, + hasRevisionOverride: !!revisionOverride, + // We only put browser version for the supported operating systems. + browserVersion: revisionOverride ? void 0 : obj.browserVersion, + title: obj["title"], + installByDefault: !!obj.installByDefault, + // Method `isBrowserDirectory` determines directory to be browser iff + // it starts with some browser name followed by '-'. Some browser names + // are prefixes of others, e.g. 'webkit' is a prefix of `webkit-technology-preview`. + // To avoid older registries erroneously removing 'webkit-technology-preview', we have to + // ensure that browser folders to never include dashes inside. + dir: import_path19.default.join(registryDirectory, browserDirectoryPrefix.replace(/-/g, "_") + "-" + revision) + }; + return descriptor; + }); +} +function browserDirectoryToMarkerFilePath(browserDirectory) { + return import_path19.default.join(browserDirectory, "INSTALLATION_COMPLETE"); +} +function buildPlaywrightCLICommand(sdkLanguage, parameters) { + switch (sdkLanguage) { + case "python": + return `playwright ${parameters}`; + case "java": + return `mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="${parameters}"`; + case "csharp": + return `pwsh bin/Debug/netX/playwright.ps1 ${parameters}`; + default: { + const packageManagerCommand = getPackageManagerExecCommand(); + return `${packageManagerCommand} playwright ${parameters}`; + } + } +} +async function installBrowsersForNpmInstall(browsers) { + if (getAsBooleanFromENV("PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD")) { + logPolitely("Skipping browsers download because `PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD` env variable is set"); + return false; + } + const executables = []; + if (process.platform === "win32") + executables.push(registry.findExecutable("winldd")); + for (const browserName of browsers) { + const executable = registry.findExecutable(browserName); + if (!executable || executable.installType === "none") + throw new Error(`Cannot install ${browserName}`); + executables.push(executable); + } + await registry.install(executables); +} +function findChromiumChannelBestEffort(sdkLanguage) { + let channel = null; + for (const name of ["chromium", "chrome", "msedge"]) { + try { + registry.findExecutable(name).executablePathOrDie(sdkLanguage); + channel = name === "chromium" ? void 0 : name; + break; + } catch (e) { + } + } + if (channel === null) { + const installCommand = buildPlaywrightCLICommand(sdkLanguage, `install chromium`); + const prettyMessage = [ + `No chromium-based browser found on the system.`, + `Please run the following command to download one:`, + ``, + ` ${installCommand}`, + ``, + `<3 Playwright Team` + ].join("\n"); + throw new Error("\n" + wrapInASCIIBox(prettyMessage, 1)); + } + return channel; +} +function lowercaseAllKeys(json) { + if (typeof json !== "object" || !json) + return json; + if (Array.isArray(json)) + return json.map(lowercaseAllKeys); + const result2 = {}; + for (const [key, value2] of Object.entries(json)) + result2[key.toLowerCase()] = lowercaseAllKeys(value2); + return result2; +} +var import_fs19, import_os8, import_path19, util2, PACKAGE_PATH, BIN_PATH, PLAYWRIGHT_CDN_MIRRORS, EXECUTABLE_PATHS, DOWNLOAD_PATHS, defaultCacheDirectory, defaultRegistryDirectory, registryDirectory, allDownloadableDirectoriesThatEverExisted, chromiumAliases, Registry, registry; +var init_registry = __esm({ + "packages/playwright-core/src/server/registry/index.ts"() { + "use strict"; + import_fs19 = __toESM(require("fs")); + import_os8 = __toESM(require("os")); + import_path19 = __toESM(require("path")); + util2 = __toESM(require("util")); + init_ascii(); + init_debugLogger(); + init_hostPlatform(); + init_network(); + init_spawnAsync(); + init_fileUtils(); + init_crypto(); + init_env(); + init_lockfile(); + init_utils2(); + init_userAgent(); + init_dependencies(); + init_dependencies(); + init_browserFetcher(); + init_package(); + init_dependencies(); + init_oopDownloadBrowserMain(); + PACKAGE_PATH = packageRoot; + BIN_PATH = binPath; + PLAYWRIGHT_CDN_MIRRORS = [ + "https://cdn.playwright.dev/dbazure/download/playwright", + // ESRP CDN + "https://playwright.download.prss.microsoft.com/dbazure/download/playwright", + // Directly hit ESRP CDN + "https://cdn.playwright.dev" + // Hit the Storage Bucket directly + ]; + if (process.env.PW_TEST_CDN_THAT_SHOULD_WORK) { + for (let i = 0; i < PLAYWRIGHT_CDN_MIRRORS.length; i++) { + const cdn = PLAYWRIGHT_CDN_MIRRORS[i]; + if (cdn !== process.env.PW_TEST_CDN_THAT_SHOULD_WORK) { + const parsedCDN = new URL(cdn); + parsedCDN.hostname = parsedCDN.hostname + ".does-not-resolve.playwright.dev"; + PLAYWRIGHT_CDN_MIRRORS[i] = parsedCDN.toString(); + } + } + } + EXECUTABLE_PATHS = { + "chromium": { + "": void 0, + "linux-x64": ["chrome-linux64", "chrome"], + "linux-arm64": ["chrome-linux", "chrome"], + // non-cft build + "mac-x64": ["chrome-mac-x64", "Google Chrome for Testing.app", "Contents", "MacOS", "Google Chrome for Testing"], + "mac-arm64": ["chrome-mac-arm64", "Google Chrome for Testing.app", "Contents", "MacOS", "Google Chrome for Testing"], + "win-x64": ["chrome-win64", "chrome.exe"] + }, + "chromium-headless-shell": { + "": void 0, + "linux-x64": ["chrome-headless-shell-linux64", "chrome-headless-shell"], + "linux-arm64": ["chrome-linux", "headless_shell"], + // non-cft build + "mac-x64": ["chrome-headless-shell-mac-x64", "chrome-headless-shell"], + "mac-arm64": ["chrome-headless-shell-mac-arm64", "chrome-headless-shell"], + "win-x64": ["chrome-headless-shell-win64", "chrome-headless-shell.exe"] + }, + "chromium-tip-of-tree": { + "": void 0, + "linux-x64": ["chrome-linux64", "chrome"], + "linux-arm64": ["chrome-linux", "chrome"], + // non-cft build + "mac-x64": ["chrome-mac-x64", "Google Chrome for Testing.app", "Contents", "MacOS", "Google Chrome for Testing"], + "mac-arm64": ["chrome-mac-arm64", "Google Chrome for Testing.app", "Contents", "MacOS", "Google Chrome for Testing"], + "win-x64": ["chrome-win64", "chrome.exe"] + }, + "chromium-tip-of-tree-headless-shell": { + "": void 0, + "linux-x64": ["chrome-headless-shell-linux64", "chrome-headless-shell"], + "linux-arm64": ["chrome-linux", "headless_shell"], + // non-cft build + "mac-x64": ["chrome-headless-shell-mac-x64", "chrome-headless-shell"], + "mac-arm64": ["chrome-headless-shell-mac-arm64", "chrome-headless-shell"], + "win-x64": ["chrome-headless-shell-win64", "chrome-headless-shell.exe"] + }, + "firefox": { + "": void 0, + "linux-x64": ["firefox", "firefox"], + "linux-arm64": ["firefox", "firefox"], + "mac-x64": ["firefox", "Nightly.app", "Contents", "MacOS", "firefox"], + "mac-arm64": ["firefox", "Nightly.app", "Contents", "MacOS", "firefox"], + "win-x64": ["firefox", "firefox.exe"] + }, + "webkit": { + "": void 0, + "linux-x64": ["pw_run.sh"], + "linux-arm64": ["pw_run.sh"], + "mac-x64": ["pw_run.sh"], + "mac-arm64": ["pw_run.sh"], + "win-x64": ["Playwright.exe"] + }, + "ffmpeg": { + "": void 0, + "linux-x64": ["ffmpeg-linux"], + "linux-arm64": ["ffmpeg-linux"], + "mac-x64": ["ffmpeg-mac"], + "mac-arm64": ["ffmpeg-mac"], + "win-x64": ["ffmpeg-win64.exe"] + }, + "winldd": { + "": void 0, + "linux-x64": void 0, + "linux-arm64": void 0, + "mac-x64": void 0, + "mac-arm64": void 0, + "win-x64": ["PrintDeps.exe"] + } + }; + DOWNLOAD_PATHS = { + "chromium": { + "": void 0, + "ubuntu18.04-x64": void 0, + "ubuntu20.04-x64": cftUrl("linux64/chrome-linux64.zip"), + "ubuntu22.04-x64": cftUrl("linux64/chrome-linux64.zip"), + "ubuntu24.04-x64": cftUrl("linux64/chrome-linux64.zip"), + "ubuntu26.04-x64": cftUrl("linux64/chrome-linux64.zip"), + "ubuntu18.04-arm64": void 0, + "ubuntu20.04-arm64": "builds/chromium/%s/chromium-linux-arm64.zip", + "ubuntu22.04-arm64": "builds/chromium/%s/chromium-linux-arm64.zip", + "ubuntu24.04-arm64": "builds/chromium/%s/chromium-linux-arm64.zip", + "ubuntu26.04-arm64": "builds/chromium/%s/chromium-linux-arm64.zip", + "debian11-x64": cftUrl("linux64/chrome-linux64.zip"), + "debian11-arm64": "builds/chromium/%s/chromium-linux-arm64.zip", + "debian12-x64": cftUrl("linux64/chrome-linux64.zip"), + "debian12-arm64": "builds/chromium/%s/chromium-linux-arm64.zip", + "debian13-x64": cftUrl("linux64/chrome-linux64.zip"), + "debian13-arm64": "builds/chromium/%s/chromium-linux-arm64.zip", + "mac10.13": cftUrl("mac-x64/chrome-mac-x64.zip"), + "mac10.14": cftUrl("mac-x64/chrome-mac-x64.zip"), + "mac10.15": cftUrl("mac-x64/chrome-mac-x64.zip"), + "mac11": cftUrl("mac-x64/chrome-mac-x64.zip"), + "mac11-arm64": cftUrl("mac-arm64/chrome-mac-arm64.zip"), + "mac12": cftUrl("mac-x64/chrome-mac-x64.zip"), + "mac12-arm64": cftUrl("mac-arm64/chrome-mac-arm64.zip"), + "mac13": cftUrl("mac-x64/chrome-mac-x64.zip"), + "mac13-arm64": cftUrl("mac-arm64/chrome-mac-arm64.zip"), + "mac14": cftUrl("mac-x64/chrome-mac-x64.zip"), + "mac14-arm64": cftUrl("mac-arm64/chrome-mac-arm64.zip"), + "mac15": cftUrl("mac-x64/chrome-mac-x64.zip"), + "mac15-arm64": cftUrl("mac-arm64/chrome-mac-arm64.zip"), + "mac26": cftUrl("mac-x64/chrome-mac-x64.zip"), + "mac26-arm64": cftUrl("mac-arm64/chrome-mac-arm64.zip"), + "win64": cftUrl("win64/chrome-win64.zip") + }, + "chromium-headless-shell": { + "": void 0, + "ubuntu18.04-x64": void 0, + "ubuntu20.04-x64": cftUrl("linux64/chrome-headless-shell-linux64.zip"), + "ubuntu22.04-x64": cftUrl("linux64/chrome-headless-shell-linux64.zip"), + "ubuntu24.04-x64": cftUrl("linux64/chrome-headless-shell-linux64.zip"), + "ubuntu26.04-x64": cftUrl("linux64/chrome-headless-shell-linux64.zip"), + "ubuntu18.04-arm64": void 0, + "ubuntu20.04-arm64": "builds/chromium/%s/chromium-headless-shell-linux-arm64.zip", + "ubuntu22.04-arm64": "builds/chromium/%s/chromium-headless-shell-linux-arm64.zip", + "ubuntu24.04-arm64": "builds/chromium/%s/chromium-headless-shell-linux-arm64.zip", + "ubuntu26.04-arm64": "builds/chromium/%s/chromium-headless-shell-linux-arm64.zip", + "debian11-x64": cftUrl("linux64/chrome-headless-shell-linux64.zip"), + "debian11-arm64": "builds/chromium/%s/chromium-headless-shell-linux-arm64.zip", + "debian12-x64": cftUrl("linux64/chrome-headless-shell-linux64.zip"), + "debian12-arm64": "builds/chromium/%s/chromium-headless-shell-linux-arm64.zip", + "debian13-x64": cftUrl("linux64/chrome-headless-shell-linux64.zip"), + "debian13-arm64": "builds/chromium/%s/chromium-headless-shell-linux-arm64.zip", + "mac10.13": void 0, + "mac10.14": void 0, + "mac10.15": void 0, + "mac11": cftUrl("mac-x64/chrome-headless-shell-mac-x64.zip"), + "mac11-arm64": cftUrl("mac-arm64/chrome-headless-shell-mac-arm64.zip"), + "mac12": cftUrl("mac-x64/chrome-headless-shell-mac-x64.zip"), + "mac12-arm64": cftUrl("mac-arm64/chrome-headless-shell-mac-arm64.zip"), + "mac13": cftUrl("mac-x64/chrome-headless-shell-mac-x64.zip"), + "mac13-arm64": cftUrl("mac-arm64/chrome-headless-shell-mac-arm64.zip"), + "mac14": cftUrl("mac-x64/chrome-headless-shell-mac-x64.zip"), + "mac14-arm64": cftUrl("mac-arm64/chrome-headless-shell-mac-arm64.zip"), + "mac15": cftUrl("mac-x64/chrome-headless-shell-mac-x64.zip"), + "mac15-arm64": cftUrl("mac-arm64/chrome-headless-shell-mac-arm64.zip"), + "mac26": cftUrl("mac-x64/chrome-headless-shell-mac-x64.zip"), + "mac26-arm64": cftUrl("mac-arm64/chrome-headless-shell-mac-arm64.zip"), + "win64": cftUrl("win64/chrome-headless-shell-win64.zip") + }, + "chromium-tip-of-tree": { + "": void 0, + "ubuntu18.04-x64": void 0, + "ubuntu20.04-x64": cftUrl("linux64/chrome-linux64.zip"), + "ubuntu22.04-x64": cftUrl("linux64/chrome-linux64.zip"), + "ubuntu24.04-x64": cftUrl("linux64/chrome-linux64.zip"), + "ubuntu26.04-x64": cftUrl("linux64/chrome-linux64.zip"), + "ubuntu18.04-arm64": void 0, + "ubuntu20.04-arm64": "builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip", + "ubuntu22.04-arm64": "builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip", + "ubuntu24.04-arm64": "builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip", + "ubuntu26.04-arm64": "builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip", + "debian11-x64": cftUrl("linux64/chrome-linux64.zip"), + "debian11-arm64": "builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip", + "debian12-x64": cftUrl("linux64/chrome-linux64.zip"), + "debian12-arm64": "builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip", + "debian13-x64": cftUrl("linux64/chrome-linux64.zip"), + "debian13-arm64": "builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip", + "mac10.13": cftUrl("mac-x64/chrome-mac-x64.zip"), + "mac10.14": cftUrl("mac-x64/chrome-mac-x64.zip"), + "mac10.15": cftUrl("mac-x64/chrome-mac-x64.zip"), + "mac11": cftUrl("mac-x64/chrome-mac-x64.zip"), + "mac11-arm64": cftUrl("mac-arm64/chrome-mac-arm64.zip"), + "mac12": cftUrl("mac-x64/chrome-mac-x64.zip"), + "mac12-arm64": cftUrl("mac-arm64/chrome-mac-arm64.zip"), + "mac13": cftUrl("mac-x64/chrome-mac-x64.zip"), + "mac13-arm64": cftUrl("mac-arm64/chrome-mac-arm64.zip"), + "mac14": cftUrl("mac-x64/chrome-mac-x64.zip"), + "mac14-arm64": cftUrl("mac-arm64/chrome-mac-arm64.zip"), + "mac15": cftUrl("mac-x64/chrome-mac-x64.zip"), + "mac15-arm64": cftUrl("mac-arm64/chrome-mac-arm64.zip"), + "mac26": cftUrl("mac-x64/chrome-mac-x64.zip"), + "mac26-arm64": cftUrl("mac-arm64/chrome-mac-arm64.zip"), + "win64": cftUrl("win64/chrome-win64.zip") + }, + "chromium-tip-of-tree-headless-shell": { + "": void 0, + "ubuntu18.04-x64": void 0, + "ubuntu20.04-x64": cftUrl("linux64/chrome-headless-shell-linux64.zip"), + "ubuntu22.04-x64": cftUrl("linux64/chrome-headless-shell-linux64.zip"), + "ubuntu24.04-x64": cftUrl("linux64/chrome-headless-shell-linux64.zip"), + "ubuntu26.04-x64": cftUrl("linux64/chrome-headless-shell-linux64.zip"), + "ubuntu18.04-arm64": void 0, + "ubuntu20.04-arm64": "builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip", + "ubuntu22.04-arm64": "builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip", + "ubuntu24.04-arm64": "builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip", + "ubuntu26.04-arm64": "builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip", + "debian11-x64": cftUrl("linux64/chrome-headless-shell-linux64.zip"), + "debian11-arm64": "builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip", + "debian12-x64": cftUrl("linux64/chrome-headless-shell-linux64.zip"), + "debian12-arm64": "builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip", + "debian13-x64": cftUrl("linux64/chrome-headless-shell-linux64.zip"), + "debian13-arm64": "builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip", + "mac10.13": void 0, + "mac10.14": void 0, + "mac10.15": void 0, + "mac11": cftUrl("mac-x64/chrome-headless-shell-mac-x64.zip"), + "mac11-arm64": cftUrl("mac-arm64/chrome-headless-shell-mac-arm64.zip"), + "mac12": cftUrl("mac-x64/chrome-headless-shell-mac-x64.zip"), + "mac12-arm64": cftUrl("mac-arm64/chrome-headless-shell-mac-arm64.zip"), + "mac13": cftUrl("mac-x64/chrome-headless-shell-mac-x64.zip"), + "mac13-arm64": cftUrl("mac-arm64/chrome-headless-shell-mac-arm64.zip"), + "mac14": cftUrl("mac-x64/chrome-headless-shell-mac-x64.zip"), + "mac14-arm64": cftUrl("mac-arm64/chrome-headless-shell-mac-arm64.zip"), + "mac15": cftUrl("mac-x64/chrome-headless-shell-mac-x64.zip"), + "mac15-arm64": cftUrl("mac-arm64/chrome-headless-shell-mac-arm64.zip"), + "mac26": cftUrl("mac-x64/chrome-headless-shell-mac-x64.zip"), + "mac26-arm64": cftUrl("mac-arm64/chrome-headless-shell-mac-arm64.zip"), + "win64": cftUrl("win64/chrome-headless-shell-win64.zip") + }, + "firefox": { + "": void 0, + "ubuntu18.04-x64": void 0, + "ubuntu20.04-x64": "builds/firefox/%s/firefox-ubuntu-20.04.zip", + "ubuntu22.04-x64": "builds/firefox/%s/firefox-ubuntu-22.04.zip", + "ubuntu24.04-x64": "builds/firefox/%s/firefox-ubuntu-24.04.zip", + "ubuntu26.04-x64": "builds/firefox/%s/firefox-ubuntu-24.04.zip", + "ubuntu18.04-arm64": void 0, + "ubuntu20.04-arm64": "builds/firefox/%s/firefox-ubuntu-20.04-arm64.zip", + "ubuntu22.04-arm64": "builds/firefox/%s/firefox-ubuntu-22.04-arm64.zip", + "ubuntu24.04-arm64": "builds/firefox/%s/firefox-ubuntu-24.04-arm64.zip", + "ubuntu26.04-arm64": "builds/firefox/%s/firefox-ubuntu-24.04-arm64.zip", + "debian11-x64": "builds/firefox/%s/firefox-debian-11.zip", + "debian11-arm64": "builds/firefox/%s/firefox-debian-11-arm64.zip", + "debian12-x64": "builds/firefox/%s/firefox-debian-12.zip", + "debian12-arm64": "builds/firefox/%s/firefox-debian-12-arm64.zip", + "debian13-x64": "builds/firefox/%s/firefox-debian-13.zip", + "debian13-arm64": "builds/firefox/%s/firefox-debian-13-arm64.zip", + "mac10.13": "builds/firefox/%s/firefox-mac.zip", + "mac10.14": "builds/firefox/%s/firefox-mac.zip", + "mac10.15": "builds/firefox/%s/firefox-mac.zip", + "mac11": "builds/firefox/%s/firefox-mac.zip", + "mac11-arm64": "builds/firefox/%s/firefox-mac-arm64.zip", + "mac12": "builds/firefox/%s/firefox-mac.zip", + "mac12-arm64": "builds/firefox/%s/firefox-mac-arm64.zip", + "mac13": "builds/firefox/%s/firefox-mac.zip", + "mac13-arm64": "builds/firefox/%s/firefox-mac-arm64.zip", + "mac14": "builds/firefox/%s/firefox-mac.zip", + "mac14-arm64": "builds/firefox/%s/firefox-mac-arm64.zip", + "mac15": "builds/firefox/%s/firefox-mac.zip", + "mac15-arm64": "builds/firefox/%s/firefox-mac-arm64.zip", + "mac26": "builds/firefox/%s/firefox-mac.zip", + "mac26-arm64": "builds/firefox/%s/firefox-mac-arm64.zip", + "win64": "builds/firefox/%s/firefox-win64.zip" + }, + "firefox-beta": { + "": void 0, + "ubuntu18.04-x64": void 0, + "ubuntu20.04-x64": "builds/firefox-beta/%s/firefox-beta-ubuntu-20.04.zip", + "ubuntu22.04-x64": "builds/firefox-beta/%s/firefox-beta-ubuntu-22.04.zip", + "ubuntu24.04-x64": "builds/firefox-beta/%s/firefox-beta-ubuntu-24.04.zip", + "ubuntu26.04-x64": "builds/firefox-beta/%s/firefox-beta-ubuntu-24.04.zip", + "ubuntu18.04-arm64": void 0, + "ubuntu20.04-arm64": void 0, + "ubuntu22.04-arm64": "builds/firefox-beta/%s/firefox-beta-ubuntu-22.04-arm64.zip", + "ubuntu24.04-arm64": "builds/firefox-beta/%s/firefox-beta-ubuntu-24.04-arm64.zip", + "ubuntu26.04-arm64": "builds/firefox-beta/%s/firefox-beta-ubuntu-24.04-arm64.zip", + "debian11-x64": "builds/firefox-beta/%s/firefox-beta-debian-11.zip", + "debian11-arm64": "builds/firefox-beta/%s/firefox-beta-debian-11-arm64.zip", + "debian12-x64": "builds/firefox-beta/%s/firefox-beta-debian-12.zip", + "debian12-arm64": "builds/firefox-beta/%s/firefox-beta-debian-12-arm64.zip", + "debian13-x64": "builds/firefox-beta/%s/firefox-beta-debian-12.zip", + "debian13-arm64": "builds/firefox-beta/%s/firefox-beta-debian-12-arm64.zip", + "mac10.13": "builds/firefox-beta/%s/firefox-beta-mac.zip", + "mac10.14": "builds/firefox-beta/%s/firefox-beta-mac.zip", + "mac10.15": "builds/firefox-beta/%s/firefox-beta-mac.zip", + "mac11": "builds/firefox-beta/%s/firefox-beta-mac.zip", + "mac11-arm64": "builds/firefox-beta/%s/firefox-beta-mac-arm64.zip", + "mac12": "builds/firefox-beta/%s/firefox-beta-mac.zip", + "mac12-arm64": "builds/firefox-beta/%s/firefox-beta-mac-arm64.zip", + "mac13": "builds/firefox-beta/%s/firefox-beta-mac.zip", + "mac13-arm64": "builds/firefox-beta/%s/firefox-beta-mac-arm64.zip", + "mac14": "builds/firefox-beta/%s/firefox-beta-mac.zip", + "mac14-arm64": "builds/firefox-beta/%s/firefox-beta-mac-arm64.zip", + "mac15": "builds/firefox-beta/%s/firefox-beta-mac.zip", + "mac15-arm64": "builds/firefox-beta/%s/firefox-beta-mac-arm64.zip", + "mac26": "builds/firefox-beta/%s/firefox-beta-mac.zip", + "mac26-arm64": "builds/firefox-beta/%s/firefox-beta-mac-arm64.zip", + "win64": "builds/firefox-beta/%s/firefox-beta-win64.zip" + }, + "webkit": { + "": void 0, + "ubuntu18.04-x64": void 0, + "ubuntu20.04-x64": "builds/webkit/%s/webkit-ubuntu-20.04.zip", + "ubuntu22.04-x64": "builds/webkit/%s/webkit-ubuntu-22.04.zip", + "ubuntu24.04-x64": "builds/webkit/%s/webkit-ubuntu-24.04.zip", + "ubuntu26.04-x64": "builds/webkit/%s/webkit-ubuntu-26.04.zip", + "ubuntu18.04-arm64": void 0, + "ubuntu20.04-arm64": "builds/webkit/%s/webkit-ubuntu-20.04-arm64.zip", + "ubuntu22.04-arm64": "builds/webkit/%s/webkit-ubuntu-22.04-arm64.zip", + "ubuntu24.04-arm64": "builds/webkit/%s/webkit-ubuntu-24.04-arm64.zip", + "ubuntu26.04-arm64": "builds/webkit/%s/webkit-ubuntu-26.04-arm64.zip", + "debian11-x64": "builds/webkit/%s/webkit-debian-11.zip", + "debian11-arm64": "builds/webkit/%s/webkit-debian-11-arm64.zip", + "debian12-x64": "builds/webkit/%s/webkit-debian-12.zip", + "debian12-arm64": "builds/webkit/%s/webkit-debian-12-arm64.zip", + "debian13-x64": "builds/webkit/%s/webkit-debian-13.zip", + "debian13-arm64": "builds/webkit/%s/webkit-debian-13-arm64.zip", + "mac10.13": void 0, + "mac10.14": void 0, + "mac10.15": void 0, + "mac11": void 0, + "mac11-arm64": void 0, + "mac12": void 0, + "mac12-arm64": void 0, + "mac13": void 0, + "mac13-arm64": void 0, + "mac14": "builds/webkit/%s/webkit-mac-14.zip", + "mac14-arm64": "builds/webkit/%s/webkit-mac-14-arm64.zip", + "mac15": "builds/webkit/%s/webkit-mac-15.zip", + "mac15-arm64": "builds/webkit/%s/webkit-mac-15-arm64.zip", + "mac26": "builds/webkit/%s/webkit-mac-15.zip", + "mac26-arm64": "builds/webkit/%s/webkit-mac-15-arm64.zip", + "win64": "builds/webkit/%s/webkit-win64.zip" + }, + "ffmpeg": { + "": void 0, + "ubuntu18.04-x64": void 0, + "ubuntu20.04-x64": "builds/ffmpeg/%s/ffmpeg-linux.zip", + "ubuntu22.04-x64": "builds/ffmpeg/%s/ffmpeg-linux.zip", + "ubuntu24.04-x64": "builds/ffmpeg/%s/ffmpeg-linux.zip", + "ubuntu26.04-x64": "builds/ffmpeg/%s/ffmpeg-linux.zip", + "ubuntu18.04-arm64": void 0, + "ubuntu20.04-arm64": "builds/ffmpeg/%s/ffmpeg-linux-arm64.zip", + "ubuntu22.04-arm64": "builds/ffmpeg/%s/ffmpeg-linux-arm64.zip", + "ubuntu24.04-arm64": "builds/ffmpeg/%s/ffmpeg-linux-arm64.zip", + "ubuntu26.04-arm64": "builds/ffmpeg/%s/ffmpeg-linux-arm64.zip", + "debian11-x64": "builds/ffmpeg/%s/ffmpeg-linux.zip", + "debian11-arm64": "builds/ffmpeg/%s/ffmpeg-linux-arm64.zip", + "debian12-x64": "builds/ffmpeg/%s/ffmpeg-linux.zip", + "debian12-arm64": "builds/ffmpeg/%s/ffmpeg-linux-arm64.zip", + "debian13-x64": "builds/ffmpeg/%s/ffmpeg-linux.zip", + "debian13-arm64": "builds/ffmpeg/%s/ffmpeg-linux-arm64.zip", + "mac10.13": "builds/ffmpeg/%s/ffmpeg-mac.zip", + "mac10.14": "builds/ffmpeg/%s/ffmpeg-mac.zip", + "mac10.15": "builds/ffmpeg/%s/ffmpeg-mac.zip", + "mac11": "builds/ffmpeg/%s/ffmpeg-mac.zip", + "mac11-arm64": "builds/ffmpeg/%s/ffmpeg-mac-arm64.zip", + "mac12": "builds/ffmpeg/%s/ffmpeg-mac.zip", + "mac12-arm64": "builds/ffmpeg/%s/ffmpeg-mac-arm64.zip", + "mac13": "builds/ffmpeg/%s/ffmpeg-mac.zip", + "mac13-arm64": "builds/ffmpeg/%s/ffmpeg-mac-arm64.zip", + "mac14": "builds/ffmpeg/%s/ffmpeg-mac.zip", + "mac14-arm64": "builds/ffmpeg/%s/ffmpeg-mac-arm64.zip", + "mac15": "builds/ffmpeg/%s/ffmpeg-mac.zip", + "mac15-arm64": "builds/ffmpeg/%s/ffmpeg-mac-arm64.zip", + "mac26": "builds/ffmpeg/%s/ffmpeg-mac.zip", + "mac26-arm64": "builds/ffmpeg/%s/ffmpeg-mac-arm64.zip", + "win64": "builds/ffmpeg/%s/ffmpeg-win64.zip" + }, + "winldd": { + "": void 0, + "ubuntu18.04-x64": void 0, + "ubuntu20.04-x64": void 0, + "ubuntu22.04-x64": void 0, + "ubuntu24.04-x64": void 0, + "ubuntu26.04-x64": void 0, + "ubuntu18.04-arm64": void 0, + "ubuntu20.04-arm64": void 0, + "ubuntu22.04-arm64": void 0, + "ubuntu24.04-arm64": void 0, + "ubuntu26.04-arm64": void 0, + "debian11-x64": void 0, + "debian11-arm64": void 0, + "debian12-x64": void 0, + "debian12-arm64": void 0, + "debian13-x64": void 0, + "debian13-arm64": void 0, + "mac10.13": void 0, + "mac10.14": void 0, + "mac10.15": void 0, + "mac11": void 0, + "mac11-arm64": void 0, + "mac12": void 0, + "mac12-arm64": void 0, + "mac13": void 0, + "mac13-arm64": void 0, + "mac14": void 0, + "mac14-arm64": void 0, + "mac15": void 0, + "mac15-arm64": void 0, + "mac26": void 0, + "mac26-arm64": void 0, + "win64": "builds/winldd/%s/winldd-win64.zip" + }, + "android": { + "": "builds/android/%s/android.zip", + "ubuntu18.04-x64": void 0, + "ubuntu20.04-x64": "builds/android/%s/android.zip", + "ubuntu22.04-x64": "builds/android/%s/android.zip", + "ubuntu24.04-x64": "builds/android/%s/android.zip", + "ubuntu26.04-x64": "builds/android/%s/android.zip", + "ubuntu18.04-arm64": void 0, + "ubuntu20.04-arm64": "builds/android/%s/android.zip", + "ubuntu22.04-arm64": "builds/android/%s/android.zip", + "ubuntu24.04-arm64": "builds/android/%s/android.zip", + "ubuntu26.04-arm64": "builds/android/%s/android.zip", + "debian11-x64": "builds/android/%s/android.zip", + "debian11-arm64": "builds/android/%s/android.zip", + "debian12-x64": "builds/android/%s/android.zip", + "debian12-arm64": "builds/android/%s/android.zip", + "debian13-x64": "builds/android/%s/android.zip", + "debian13-arm64": "builds/android/%s/android.zip", + "mac10.13": "builds/android/%s/android.zip", + "mac10.14": "builds/android/%s/android.zip", + "mac10.15": "builds/android/%s/android.zip", + "mac11": "builds/android/%s/android.zip", + "mac11-arm64": "builds/android/%s/android.zip", + "mac12": "builds/android/%s/android.zip", + "mac12-arm64": "builds/android/%s/android.zip", + "mac13": "builds/android/%s/android.zip", + "mac13-arm64": "builds/android/%s/android.zip", + "mac14": "builds/android/%s/android.zip", + "mac14-arm64": "builds/android/%s/android.zip", + "mac15": "builds/android/%s/android.zip", + "mac15-arm64": "builds/android/%s/android.zip", + "mac26": "builds/android/%s/android.zip", + "mac26-arm64": "builds/android/%s/android.zip", + "win64": "builds/android/%s/android.zip" + } + }; + defaultCacheDirectory = (() => { + if (process.platform === "linux") + return process.env.XDG_CACHE_HOME || import_path19.default.join(import_os8.default.homedir(), ".cache"); + if (process.platform === "darwin") + return import_path19.default.join(import_os8.default.homedir(), "Library", "Caches"); + if (process.platform === "win32") + return process.env.LOCALAPPDATA || import_path19.default.join(import_os8.default.homedir(), "AppData", "Local"); + throw new Error("Unsupported platform: " + process.platform); + })(); + defaultRegistryDirectory = import_path19.default.join(defaultCacheDirectory, "ms-playwright"); + registryDirectory = (() => { + let result2; + const envDefined = getFromENV("PLAYWRIGHT_BROWSERS_PATH"); + if (envDefined === "0") + result2 = import_path19.default.join(packageRoot, ".local-browsers"); + else if (envDefined) + result2 = envDefined; + else + result2 = defaultRegistryDirectory; + if (!import_path19.default.isAbsolute(result2)) { + result2 = import_path19.default.resolve(getFromENV("INIT_CWD") || process.cwd(), result2); + } + return result2; + })(); + allDownloadableDirectoriesThatEverExisted = ["android", "chromium", "firefox", "webkit", "ffmpeg", "firefox-beta", "chromium-tip-of-tree", "chromium-headless-shell", "chromium-tip-of-tree-headless-shell", "winldd"]; + chromiumAliases = ["chrome-for-testing"]; + Registry = class { + constructor(browsersJSON) { + const descriptors = readDescriptors(browsersJSON); + const findExecutablePath = (dir, name) => { + const tokens = EXECUTABLE_PATHS[name][shortPlatform]; + return tokens ? import_path19.default.join(dir, ...tokens) : void 0; + }; + const executablePathOrDie = (name, e, installByDefault, sdkLanguage) => { + if (!e) + throw new Error(`${name} is not supported on ${hostPlatform}`); + const installCommand = buildPlaywrightCLICommand(sdkLanguage, `install${installByDefault ? "" : " " + name}`); + if (!canAccessFile(e)) { + const currentDockerVersion = readDockerVersionSync(); + const preferredDockerVersion = currentDockerVersion ? dockerVersion(currentDockerVersion.dockerImageNameTemplate) : null; + const isOutdatedDockerImage = currentDockerVersion && preferredDockerVersion && currentDockerVersion.dockerImageName !== preferredDockerVersion.dockerImageName; + const isFfmpeg = name === "ffmpeg"; + let prettyMessage; + if (isOutdatedDockerImage) { + prettyMessage = [ + `Looks like Playwright was just updated to ${preferredDockerVersion.driverVersion}.`, + `Please update docker image as well.`, + `- current: ${currentDockerVersion.dockerImageName}`, + `- required: ${preferredDockerVersion.dockerImageName}`, + ``, + `<3 Playwright Team` + ].join("\n"); + } else if (isFfmpeg) { + prettyMessage = [ + `Video rendering requires ffmpeg binary.`, + `Downloading it will not affect any of the system-wide settings.`, + `Please run the following command:`, + ``, + ` ${buildPlaywrightCLICommand(sdkLanguage, "install ffmpeg")}`, + ``, + `<3 Playwright Team` + ].join("\n"); + } else { + prettyMessage = [ + `Looks like Playwright was just installed or updated.`, + `Please run the following command to download new browser${installByDefault ? "s" : ""}:`, + ``, + ` ${installCommand}`, + ``, + `<3 Playwright Team` + ].join("\n"); + } + throw new Error(`Executable doesn't exist at ${e} +${wrapInASCIIBox(prettyMessage, 1)}`); + } + return e; + }; + this._executables = []; + const chromium = descriptors.find((d) => d.name === "chromium"); + const chromiumExecutable = findExecutablePath(chromium.dir, "chromium"); + this._executables.push({ + name: "chromium", + browserName: "chromium", + directory: chromium.dir, + executablePath: () => chromiumExecutable, + executablePathOrDie: (sdkLanguage) => executablePathOrDie("chromium", chromiumExecutable, chromium.installByDefault, sdkLanguage), + installType: chromium.installByDefault ? "download-by-default" : "download-on-demand", + _validateHostRequirements: (sdkLanguage) => this._validateHostRequirements(sdkLanguage, chromium.dir, ["chrome-linux"], [], ["chrome-win"]), + downloadURLs: this._downloadURLs(chromium), + title: chromium.title, + revision: chromium.revision, + browserVersion: chromium.browserVersion, + _install: (force) => this._downloadExecutable(chromium, force, chromiumExecutable), + _dependencyGroup: "chromium", + _isHermeticInstallation: true + }); + const chromiumHeadlessShell = descriptors.find((d) => d.name === "chromium-headless-shell"); + const chromiumHeadlessShellExecutable = findExecutablePath(chromiumHeadlessShell.dir, "chromium-headless-shell"); + this._executables.push({ + name: "chromium-headless-shell", + browserName: "chromium", + directory: chromiumHeadlessShell.dir, + executablePath: () => chromiumHeadlessShellExecutable, + executablePathOrDie: (sdkLanguage) => executablePathOrDie("chromium", chromiumHeadlessShellExecutable, chromiumHeadlessShell.installByDefault, sdkLanguage), + installType: chromiumHeadlessShell.installByDefault ? "download-by-default" : "download-on-demand", + _validateHostRequirements: (sdkLanguage) => this._validateHostRequirements(sdkLanguage, chromiumHeadlessShell.dir, ["chrome-linux"], [], ["chrome-win"]), + downloadURLs: this._downloadURLs(chromiumHeadlessShell), + title: chromiumHeadlessShell.title, + revision: chromiumHeadlessShell.revision, + browserVersion: chromiumHeadlessShell.browserVersion, + _install: (force) => this._downloadExecutable(chromiumHeadlessShell, force, chromiumHeadlessShellExecutable), + _dependencyGroup: "chromium", + _isHermeticInstallation: true + }); + const chromiumTipOfTreeHeadlessShell = descriptors.find((d) => d.name === "chromium-tip-of-tree-headless-shell"); + const chromiumTipOfTreeHeadlessShellExecutable = findExecutablePath(chromiumTipOfTreeHeadlessShell.dir, "chromium-tip-of-tree-headless-shell"); + this._executables.push({ + name: "chromium-tip-of-tree-headless-shell", + browserName: "chromium", + directory: chromiumTipOfTreeHeadlessShell.dir, + executablePath: () => chromiumTipOfTreeHeadlessShellExecutable, + executablePathOrDie: (sdkLanguage) => executablePathOrDie("chromium", chromiumTipOfTreeHeadlessShellExecutable, chromiumTipOfTreeHeadlessShell.installByDefault, sdkLanguage), + installType: chromiumTipOfTreeHeadlessShell.installByDefault ? "download-by-default" : "download-on-demand", + _validateHostRequirements: (sdkLanguage) => this._validateHostRequirements(sdkLanguage, chromiumTipOfTreeHeadlessShell.dir, ["chrome-linux"], [], ["chrome-win"]), + downloadURLs: this._downloadURLs(chromiumTipOfTreeHeadlessShell), + title: chromiumTipOfTreeHeadlessShell.title, + revision: chromiumTipOfTreeHeadlessShell.revision, + browserVersion: chromiumTipOfTreeHeadlessShell.browserVersion, + _install: (force) => this._downloadExecutable(chromiumTipOfTreeHeadlessShell, force, chromiumTipOfTreeHeadlessShellExecutable), + _dependencyGroup: "chromium", + _isHermeticInstallation: true + }); + const chromiumTipOfTree = descriptors.find((d) => d.name === "chromium-tip-of-tree"); + const chromiumTipOfTreeExecutable = findExecutablePath(chromiumTipOfTree.dir, "chromium-tip-of-tree"); + this._executables.push({ + name: "chromium-tip-of-tree", + browserName: "chromium", + directory: chromiumTipOfTree.dir, + executablePath: () => chromiumTipOfTreeExecutable, + executablePathOrDie: (sdkLanguage) => executablePathOrDie("chromium-tip-of-tree", chromiumTipOfTreeExecutable, chromiumTipOfTree.installByDefault, sdkLanguage), + installType: chromiumTipOfTree.installByDefault ? "download-by-default" : "download-on-demand", + _validateHostRequirements: (sdkLanguage) => this._validateHostRequirements(sdkLanguage, chromiumTipOfTree.dir, ["chrome-linux"], [], ["chrome-win"]), + downloadURLs: this._downloadURLs(chromiumTipOfTree), + title: chromiumTipOfTree.title, + revision: chromiumTipOfTree.revision, + browserVersion: chromiumTipOfTree.browserVersion, + _install: (force) => this._downloadExecutable(chromiumTipOfTree, force, chromiumTipOfTreeExecutable), + _dependencyGroup: "chromium", + _isHermeticInstallation: true + }); + this._executables.push(this._createChromiumChannel("chrome", { + "linux": "/opt/google/chrome/chrome", + "darwin": "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", + "win32": `\\Google\\Chrome\\Application\\chrome.exe` + }, () => this._installChromiumChannel("chrome", { + "linux": "reinstall_chrome_stable_linux.sh", + "darwin": "reinstall_chrome_stable_mac.sh", + "win32": "reinstall_chrome_stable_win.ps1" + }))); + this._executables.push(this._createChromiumChannel("chrome-beta", { + "linux": "/opt/google/chrome-beta/chrome", + "darwin": "/Applications/Google Chrome Beta.app/Contents/MacOS/Google Chrome Beta", + "win32": `\\Google\\Chrome Beta\\Application\\chrome.exe` + }, () => this._installChromiumChannel("chrome-beta", { + "linux": "reinstall_chrome_beta_linux.sh", + "darwin": "reinstall_chrome_beta_mac.sh", + "win32": "reinstall_chrome_beta_win.ps1" + }))); + this._executables.push(this._createChromiumChannel("chrome-dev", { + "linux": "/opt/google/chrome-unstable/chrome", + "darwin": "/Applications/Google Chrome Dev.app/Contents/MacOS/Google Chrome Dev", + "win32": `\\Google\\Chrome Dev\\Application\\chrome.exe` + })); + this._executables.push(this._createChromiumChannel("chrome-canary", { + "linux": "/opt/google/chrome-canary/chrome", + "darwin": "/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary", + "win32": `\\Google\\Chrome SxS\\Application\\chrome.exe` + })); + this._executables.push(this._createChromiumChannel("msedge", { + "linux": "/opt/microsoft/msedge/msedge", + "darwin": "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge", + "win32": `\\Microsoft\\Edge\\Application\\msedge.exe` + }, () => this._installMSEdgeChannel("msedge", { + "linux": "reinstall_msedge_stable_linux.sh", + "darwin": "reinstall_msedge_stable_mac.sh", + "win32": "reinstall_msedge_stable_win.ps1" + }))); + this._executables.push(this._createChromiumChannel("msedge-beta", { + "linux": "/opt/microsoft/msedge-beta/msedge", + "darwin": "/Applications/Microsoft Edge Beta.app/Contents/MacOS/Microsoft Edge Beta", + "win32": `\\Microsoft\\Edge Beta\\Application\\msedge.exe` + }, () => this._installMSEdgeChannel("msedge-beta", { + "darwin": "reinstall_msedge_beta_mac.sh", + "linux": "reinstall_msedge_beta_linux.sh", + "win32": "reinstall_msedge_beta_win.ps1" + }))); + this._executables.push(this._createChromiumChannel("msedge-dev", { + "linux": "/opt/microsoft/msedge-dev/msedge", + "darwin": "/Applications/Microsoft Edge Dev.app/Contents/MacOS/Microsoft Edge Dev", + "win32": `\\Microsoft\\Edge Dev\\Application\\msedge.exe` + }, () => this._installMSEdgeChannel("msedge-dev", { + "darwin": "reinstall_msedge_dev_mac.sh", + "linux": "reinstall_msedge_dev_linux.sh", + "win32": "reinstall_msedge_dev_win.ps1" + }))); + this._executables.push(this._createChromiumChannel("msedge-canary", { + "linux": "", + "darwin": "/Applications/Microsoft Edge Canary.app/Contents/MacOS/Microsoft Edge Canary", + "win32": `\\Microsoft\\Edge SxS\\Application\\msedge.exe` + })); + this._executables.push(this._createBidiFirefoxChannel("moz-firefox", { + "linux": "/snap/bin/firefox", + "darwin": "/Applications/Firefox.app/Contents/MacOS/firefox", + "win32": "\\Mozilla Firefox\\firefox.exe" + })); + this._executables.push(this._createBidiFirefoxChannel("moz-firefox-beta", { + "linux": "/opt/firefox-beta/firefox", + "darwin": "/Applications/Firefox.app/Contents/MacOS/firefox", + "win32": "\\Mozilla Firefox\\firefox.exe" + })); + this._executables.push(this._createBidiFirefoxChannel("moz-firefox-nightly", { + "linux": "/opt/firefox-nightly/firefox", + "darwin": "/Applications/Firefox Nightly.app/Contents/MacOS/firefox", + "win32": "\\Mozilla Firefox\\firefox.exe" + })); + const firefox = descriptors.find((d) => d.name === "firefox"); + const firefoxExecutable = findExecutablePath(firefox.dir, "firefox"); + this._executables.push({ + name: "firefox", + browserName: "firefox", + directory: firefox.dir, + executablePath: () => firefoxExecutable, + executablePathOrDie: (sdkLanguage) => executablePathOrDie("firefox", firefoxExecutable, firefox.installByDefault, sdkLanguage), + installType: firefox.installByDefault ? "download-by-default" : "download-on-demand", + _validateHostRequirements: (sdkLanguage) => this._validateHostRequirements(sdkLanguage, firefox.dir, ["firefox"], [], ["firefox"]), + downloadURLs: this._downloadURLs(firefox), + title: firefox.title, + revision: firefox.revision, + browserVersion: firefox.browserVersion, + _install: (force) => this._downloadExecutable(firefox, force, firefoxExecutable), + _dependencyGroup: "firefox", + _isHermeticInstallation: true + }); + const firefoxBeta = descriptors.find((d) => d.name === "firefox-beta"); + const firefoxBetaExecutable = findExecutablePath(firefoxBeta.dir, "firefox"); + this._executables.push({ + name: "firefox-beta", + browserName: "firefox", + directory: firefoxBeta.dir, + executablePath: () => firefoxBetaExecutable, + executablePathOrDie: (sdkLanguage) => executablePathOrDie("firefox-beta", firefoxBetaExecutable, firefoxBeta.installByDefault, sdkLanguage), + installType: firefoxBeta.installByDefault ? "download-by-default" : "download-on-demand", + _validateHostRequirements: (sdkLanguage) => this._validateHostRequirements(sdkLanguage, firefoxBeta.dir, ["firefox"], [], ["firefox"]), + downloadURLs: this._downloadURLs(firefoxBeta), + title: firefoxBeta.title, + revision: firefoxBeta.revision, + browserVersion: firefoxBeta.browserVersion, + _install: (force) => this._downloadExecutable(firefoxBeta, force, firefoxBetaExecutable), + _dependencyGroup: "firefox", + _isHermeticInstallation: true + }); + const webkit = descriptors.find((d) => d.name === "webkit"); + const webkitExecutable = findExecutablePath(webkit.dir, "webkit"); + const webkitLinuxLddDirectories = [ + import_path19.default.join("minibrowser-gtk"), + import_path19.default.join("minibrowser-gtk", "bin"), + import_path19.default.join("minibrowser-gtk", "lib"), + import_path19.default.join("minibrowser-gtk", "sys", "lib"), + import_path19.default.join("minibrowser-wpe"), + import_path19.default.join("minibrowser-wpe", "bin"), + import_path19.default.join("minibrowser-wpe", "lib"), + import_path19.default.join("minibrowser-wpe", "sys", "lib") + ]; + this._executables.push({ + name: "webkit", + browserName: "webkit", + directory: webkit.dir, + executablePath: () => webkitExecutable, + executablePathOrDie: (sdkLanguage) => executablePathOrDie("webkit", webkitExecutable, webkit.installByDefault, sdkLanguage), + installType: webkit.installByDefault ? "download-by-default" : "download-on-demand", + _validateHostRequirements: (sdkLanguage) => this._validateHostRequirements(sdkLanguage, webkit.dir, webkitLinuxLddDirectories, ["libGLESv2.so.2", "libx264.so"], [""]), + downloadURLs: this._downloadURLs(webkit), + title: webkit.title, + revision: webkit.revision, + browserVersion: webkit.browserVersion, + _install: (force) => this._downloadExecutable(webkit, force, webkitExecutable), + _dependencyGroup: "webkit", + _isHermeticInstallation: true + }); + this._executables.push({ + name: "webkit-wsl", + browserName: "webkit", + directory: webkit.dir, + executablePath: () => webkitExecutable, + executablePathOrDie: (sdkLanguage) => executablePathOrDie("webkit", webkitExecutable, webkit.installByDefault, sdkLanguage), + installType: "download-on-demand", + title: "Webkit in WSL", + _validateHostRequirements: (sdkLanguage) => Promise.resolve(), + _isHermeticInstallation: true, + _install: async () => { + if (process.platform !== "win32") + throw new Error(`WebKit via WSL is only supported on Windows`); + const script = import_path19.default.join(BIN_PATH, "install_webkit_wsl.ps1"); + const { code } = await spawnAsync("powershell.exe", [ + "-ExecutionPolicy", + "Bypass", + "-File", + script + ], { + stdio: "inherit" + }); + if (code !== 0) + throw new Error(`Failed to install WebKit via WSL`); + } + }); + const ffmpeg = descriptors.find((d) => d.name === "ffmpeg"); + const ffmpegExecutable = findExecutablePath(ffmpeg.dir, "ffmpeg"); + this._executables.push({ + name: "ffmpeg", + browserName: void 0, + directory: ffmpeg.dir, + executablePath: () => ffmpegExecutable, + executablePathOrDie: (sdkLanguage) => executablePathOrDie("ffmpeg", ffmpegExecutable, ffmpeg.installByDefault, sdkLanguage), + installType: ffmpeg.installByDefault ? "download-by-default" : "download-on-demand", + _validateHostRequirements: () => Promise.resolve(), + downloadURLs: this._downloadURLs(ffmpeg), + title: ffmpeg.title, + revision: ffmpeg.revision, + _install: (force) => this._downloadExecutable(ffmpeg, force, ffmpegExecutable), + _dependencyGroup: "tools", + _isHermeticInstallation: true + }); + const winldd = descriptors.find((d) => d.name === "winldd"); + const winlddExecutable = findExecutablePath(winldd.dir, "winldd"); + this._executables.push({ + name: "winldd", + browserName: void 0, + directory: winldd.dir, + executablePath: () => winlddExecutable, + executablePathOrDie: (sdkLanguage) => executablePathOrDie("winldd", winlddExecutable, winldd.installByDefault, sdkLanguage), + installType: process.platform === "win32" ? "download-by-default" : "none", + _validateHostRequirements: () => Promise.resolve(), + downloadURLs: this._downloadURLs(winldd), + title: winldd.title, + revision: winldd.revision, + _install: (force) => this._downloadExecutable(winldd, force, winlddExecutable), + _dependencyGroup: "tools", + _isHermeticInstallation: true + }); + const android = descriptors.find((d) => d.name === "android"); + this._executables.push({ + name: "android", + browserName: void 0, + directory: android.dir, + executablePath: () => void 0, + executablePathOrDie: () => "", + installType: "download-on-demand", + _validateHostRequirements: () => Promise.resolve(), + downloadURLs: this._downloadURLs(android), + title: android.title, + revision: android.revision, + _install: (force) => this._downloadExecutable(android, force), + _dependencyGroup: "tools", + _isHermeticInstallation: true + }); + } + _createChromiumChannel(name, lookAt, install2) { + const executablePath = (sdkLanguage, shouldThrow) => { + const suffix = lookAt[process.platform]; + if (!suffix) { + if (shouldThrow) + throw new Error(`Chromium distribution '${name}' is not supported on ${process.platform}`); + return void 0; + } + const prefixes = process.platform === "win32" ? [ + process.env.LOCALAPPDATA, + process.env.PROGRAMFILES, + process.env["PROGRAMFILES(X86)"], + // In some cases there is no PROGRAMFILES/(86) env var set but HOMEDRIVE is set. + process.env.HOMEDRIVE + "\\Program Files", + process.env.HOMEDRIVE + "\\Program Files (x86)" + ].filter(Boolean) : [""]; + for (const prefix of prefixes) { + const executablePath2 = import_path19.default.join(prefix, suffix); + if (canAccessFile(executablePath2)) + return executablePath2; + } + if (!shouldThrow) + return void 0; + const location2 = prefixes.length ? ` at ${import_path19.default.join(prefixes[0], suffix)}` : ``; + const installation = install2 ? ` +Run "${buildPlaywrightCLICommand(sdkLanguage, "install " + name)}"` : ""; + throw new Error(`Chromium distribution '${name}' is not found${location2}${installation}`); + }; + return { + name, + browserName: "chromium", + directory: void 0, + executablePath: () => executablePath("", false), + executablePathOrDie: (sdkLanguage) => executablePath(sdkLanguage, true), + installType: install2 ? "install-script" : "none", + _validateHostRequirements: () => Promise.resolve(), + _isHermeticInstallation: false, + _install: install2 + }; + } + _createBidiFirefoxChannel(name, lookAt, install2) { + const executablePath = (sdkLanguage, shouldThrow) => { + const suffix = lookAt[process.platform]; + if (!suffix) { + if (shouldThrow) + throw new Error(`Firefox distribution '${name}' is not supported on ${process.platform}`); + return void 0; + } + const prefixes = process.platform === "win32" ? [ + process.env.LOCALAPPDATA, + process.env.PROGRAMFILES, + process.env["PROGRAMFILES(X86)"], + // In some cases there is no PROGRAMFILES/(86) env var set but HOMEDRIVE is set. + process.env.HOMEDRIVE + "\\Program Files", + process.env.HOMEDRIVE + "\\Program Files (x86)" + ].filter(Boolean) : [""]; + for (const prefix of prefixes) { + const executablePath2 = import_path19.default.join(prefix, suffix); + if (canAccessFile(executablePath2)) + return executablePath2; + } + if (shouldThrow) + throw new Error(`Cannot find Firefox installation for channel '${name}' at the standard system paths. ${`Tried paths: + ${prefixes.map((p) => import_path19.default.join(p, suffix)).join("\n ")}`}`); + return void 0; + }; + return { + name, + browserName: "firefox", + directory: void 0, + executablePath: () => executablePath("", false), + executablePathOrDie: (sdkLanguage) => executablePath(sdkLanguage, true), + installType: "none", + _validateHostRequirements: () => Promise.resolve(), + _isHermeticInstallation: true, + _install: install2 + }; + } + executables() { + return this._executables; + } + findExecutable(name) { + return this._executables.find((b) => b.name === name); + } + defaultExecutables() { + return this._executables.filter((e) => e.installType === "download-by-default"); + } + _dedupe(executables) { + return Array.from(new Set(executables)); + } + async _validateHostRequirements(sdkLanguage, browserDirectory, linuxLddDirectories, dlOpenLibraries, windowsExeAndDllDirectories) { + if (import_os8.default.platform() === "linux") + return await validateDependenciesLinux(sdkLanguage, linuxLddDirectories.map((d) => import_path19.default.join(browserDirectory, d)), dlOpenLibraries); + if (import_os8.default.platform() === "win32" && import_os8.default.arch() === "x64") + return await validateDependenciesWindows(sdkLanguage, windowsExeAndDllDirectories.map((d) => import_path19.default.join(browserDirectory, d))); + } + async installDeps(executablesToInstallDeps, dryRun) { + const executables = this._dedupe(executablesToInstallDeps); + const targets = /* @__PURE__ */ new Set(); + for (const executable of executables) { + if (executable._dependencyGroup) + targets.add(executable._dependencyGroup); + } + targets.add("tools"); + if (import_os8.default.platform() === "win32") + return await installDependenciesWindows(targets, dryRun); + if (import_os8.default.platform() === "linux") + return await installDependenciesLinux(targets, dryRun); + } + async install(executablesToInstall, options) { + const executables = this._dedupe(executablesToInstall); + await import_fs19.default.promises.mkdir(registryDirectory, { recursive: true }); + const lockfilePath = import_path19.default.join(registryDirectory, "__dirlock"); + const linksDir = import_path19.default.join(registryDirectory, ".links"); + let releaseLock; + try { + releaseLock = await lock(registryDirectory, { + retries: { + // Retry 20 times during 10 minutes with + // exponential back-off. + // See documentation at: https://www.npmjs.com/package/retry#retrytimeoutsoptions + retries: 20, + factor: 1.27579 + }, + onCompromised: (err) => { + throw new Error(`${err.message} Path: ${lockfilePath}`); + }, + lockfilePath + }); + await import_fs19.default.promises.mkdir(linksDir, { recursive: true }); + await import_fs19.default.promises.writeFile(import_path19.default.join(linksDir, calculateSha1(PACKAGE_PATH)), PACKAGE_PATH); + if (!getAsBooleanFromENV("PLAYWRIGHT_SKIP_BROWSER_GC")) + await this._validateInstallationCache(linksDir); + for (const executable of executables) { + if (!executable._install) + throw new Error(`ERROR: Playwright does not support installing ${executable.name}`); + if (!getAsBooleanFromENV("CI") && !executable._isHermeticInstallation && !options?.force && executable.executablePath()) { + const { embedderName } = getEmbedderName(); + const command = buildPlaywrightCLICommand(embedderName, "install --force " + executable.name); + process.stderr.write("\n" + wrapInASCIIBox([ + `ATTENTION: "${executable.name}" is already installed on the system!`, + ``, + `"${executable.name}" installation is not hermetic; installing newer version`, + `requires *removal* of a current installation first.`, + ``, + `To *uninstall* current version and re-install latest "${executable.name}":`, + ``, + `- Close all running instances of "${executable.name}", if any`, + `- Use "--force" to install browser:`, + ``, + ` ${command}`, + ``, + `<3 Playwright Team` + ].join("\n"), 1) + "\n\n"); + return; + } + await executable._install(!!options?.force); + } + } catch (e) { + if (e.code === "ELOCKED") { + const rmCommand = process.platform === "win32" ? "rm -R" : "rm -rf"; + throw new Error("\n" + wrapInASCIIBox([ + `An active lockfile is found at:`, + ``, + ` ${lockfilePath}`, + ``, + `Either:`, + `- wait a few minutes if other Playwright is installing browsers in parallel`, + `- remove lock manually with:`, + ``, + ` ${rmCommand} ${lockfilePath}`, + ``, + `<3 Playwright Team` + ].join("\n"), 1)); + } else { + throw e; + } + } finally { + if (releaseLock) + await releaseLock(); + } + } + async uninstall(all) { + const linksDir = import_path19.default.join(registryDirectory, ".links"); + if (all) { + const links = await import_fs19.default.promises.readdir(linksDir).catch(() => []); + for (const link of links) + await import_fs19.default.promises.unlink(import_path19.default.join(linksDir, link)); + } else { + await import_fs19.default.promises.unlink(import_path19.default.join(linksDir, calculateSha1(PACKAGE_PATH))).catch(() => { + }); + } + await this._validateInstallationCache(linksDir); + return { + numberOfBrowsersLeft: (await import_fs19.default.promises.readdir(registryDirectory).catch(() => [])).filter((browserDirectory) => isBrowserDirectory(browserDirectory)).length + }; + } + async validateHostRequirementsForExecutablesIfNeeded(executables, sdkLanguage) { + if (getAsBooleanFromENV("PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS")) { + process.stderr.write("Skipping host requirements validation logic because `PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS` env variable is set.\n"); + return; + } + for (const executable of executables) + await this._validateHostRequirementsForExecutableIfNeeded(executable, sdkLanguage); + } + async _validateHostRequirementsForExecutableIfNeeded(executable, sdkLanguage) { + const kMaximumReValidationPeriod = 30 * 24 * 60 * 60 * 1e3; + if (!executable.directory) + return; + const markerFile = import_path19.default.join(executable.directory, "DEPENDENCIES_VALIDATED"); + if (await import_fs19.default.promises.stat(markerFile).then((stat) => Date.now() - stat.mtime.getTime() < kMaximumReValidationPeriod).catch(() => false)) + return; + debugLogger.log("install", `validating host requirements for "${executable.name}"`); + try { + await executable._validateHostRequirements(sdkLanguage); + debugLogger.log("install", `validation passed for ${executable.name}`); + } catch (error) { + debugLogger.log("install", `validation failed for ${executable.name}`); + throw error; + } + await import_fs19.default.promises.writeFile(markerFile, "").catch(() => { + }); + } + _downloadURLs(descriptor) { + const paths = DOWNLOAD_PATHS[descriptor.name]; + const downloadPathTemplate = paths[hostPlatform] || paths[""]; + if (!downloadPathTemplate) + return []; + let downloadPath; + let mirrors; + if (typeof downloadPathTemplate === "function") { + const result2 = downloadPathTemplate(descriptor); + downloadPath = result2.path; + mirrors = result2.mirrors; + } else { + downloadPath = util2.format(downloadPathTemplate, descriptor.revision); + mirrors = PLAYWRIGHT_CDN_MIRRORS; + } + let downloadHostEnv; + if (descriptor.name.startsWith("chromium")) + downloadHostEnv = "PLAYWRIGHT_CHROMIUM_DOWNLOAD_HOST"; + else if (descriptor.name.startsWith("firefox")) + downloadHostEnv = "PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST"; + else if (descriptor.name.startsWith("webkit")) + downloadHostEnv = "PLAYWRIGHT_WEBKIT_DOWNLOAD_HOST"; + const customHostOverride = downloadHostEnv && getFromENV(downloadHostEnv) || getFromENV("PLAYWRIGHT_DOWNLOAD_HOST"); + if (customHostOverride) + mirrors = [customHostOverride]; + return mirrors.map((mirror) => `${mirror}/${downloadPath}`); + } + async _downloadExecutable(descriptor, force, executablePath) { + const downloadURLs = this._downloadURLs(descriptor); + if (!downloadURLs.length) + throw new Error(`ERROR: Playwright does not support ${descriptor.name} on ${hostPlatform}`); + if (!isOfficiallySupportedPlatform) + logPolitely(`BEWARE: your OS is not officially supported by Playwright; downloading fallback build for ${hostPlatform}.`); + if (descriptor.hasRevisionOverride) { + const message = `You are using a frozen ${descriptor.name} browser which does not receive updates anymore on ${hostPlatform}. Please update to the latest version of your operating system to test up-to-date browsers.`; + if (process.env.GITHUB_ACTIONS) + console.log(`::warning title=Playwright::${message}`); + else + logPolitely(message); + } + const title = this.calculateDownloadTitle(descriptor); + const downloadFileName = `playwright-download-${descriptor.name}-${hostPlatform}-${descriptor.revision}.zip`; + const downloadSocketTimeoutEnv = getFromENV("PLAYWRIGHT_DOWNLOAD_CONNECTION_TIMEOUT"); + const downloadSocketTimeout = +(downloadSocketTimeoutEnv || "0") || NET_DEFAULT_TIMEOUT; + await downloadBrowserWithProgressBar(title, descriptor.dir, executablePath, downloadURLs, downloadFileName, downloadSocketTimeout, force).catch((e) => { + throw new Error(`Failed to download ${title}, caused by +${e.stack}`); + }); + } + calculateDownloadTitle(descriptor) { + const title = descriptor.title ?? descriptor.name.split("-").map((word) => { + return word === "ffmpeg" ? "FFmpeg" : word.charAt(0).toUpperCase() + word.slice(1); + }).join(" "); + const version3 = descriptor.browserVersion ? " " + descriptor.browserVersion : ""; + return `${title}${version3} (playwright ${descriptor.name} v${descriptor.revision})`; + } + async _installMSEdgeChannel(channel, scripts) { + const scriptArgs = []; + if (process.platform !== "linux") { + const products = lowercaseAllKeys(JSON.parse(await fetchData(void 0, { url: "https://edgeupdates.microsoft.com/api/products" }))); + const productName = { + "msedge": "Stable", + "msedge-beta": "Beta", + "msedge-dev": "Dev" + }[channel]; + const product = products.find((product2) => product2.product === productName); + const searchConfig = { + darwin: { platform: "MacOS", arch: "universal", artifact: "pkg" }, + win32: { platform: "Windows", arch: "x64", artifact: "msi" } + }[process.platform]; + const release = searchConfig ? product.releases.find((release2) => release2.platform === searchConfig.platform && release2.architecture === searchConfig.arch && release2.artifacts.length > 0) : null; + const artifact = release ? release.artifacts.find((artifact2) => artifact2.artifactname === searchConfig.artifact) : null; + if (artifact) + scriptArgs.push( + artifact.location + /* url */ + ); + else + throw new Error(`Cannot install ${channel} on ${process.platform}`); + } + await this._installChromiumChannel(channel, scripts, scriptArgs); + } + async _installChromiumChannel(channel, scripts, scriptArgs = []) { + const scriptName = scripts[process.platform]; + if (!scriptName) + throw new Error(`Cannot install ${channel} on ${process.platform}`); + const cwd = BIN_PATH; + const isPowerShell = scriptName.endsWith(".ps1"); + if (isPowerShell) { + const args = [ + "-ExecutionPolicy", + "Bypass", + "-File", + import_path19.default.join(BIN_PATH, scriptName), + ...scriptArgs + ]; + const { code } = await spawnAsync("powershell.exe", args, { cwd, stdio: "inherit" }); + if (code !== 0) + throw new Error(`Failed to install ${channel}`); + } else { + const shellArgs = scriptArgs.map((a) => `'${a.replace(/'/g, `'\\''`)}'`).join(" "); + const { command, args, elevatedPermissions } = await transformCommandsForRoot([`bash "${import_path19.default.join(BIN_PATH, scriptName)}" ${shellArgs}`]); + if (elevatedPermissions) + console.log("Switching to root user to install dependencies..."); + const { code } = await spawnAsync(command, args, { cwd, stdio: "inherit" }); + if (code !== 0) + throw new Error(`Failed to install ${channel}`); + } + } + async listInstalledBrowsers() { + const linksDir = import_path19.default.join(registryDirectory, ".links"); + const { browsers } = await this._traverseBrowserInstallations(linksDir); + return browsers.filter((browser) => import_fs19.default.existsSync(browser.browserPath)); + } + async _validateInstallationCache(linksDir) { + const { browsers, brokenLinks } = await this._traverseBrowserInstallations(linksDir); + await this._deleteStaleBrowsers(browsers); + await this._deleteBrokenInstallations(brokenLinks); + } + async _traverseBrowserInstallations(linksDir) { + const browserList = []; + const brokenLinks = []; + for (const fileName of await import_fs19.default.promises.readdir(linksDir)) { + const linkPath = import_path19.default.join(linksDir, fileName); + let linkTarget = ""; + try { + linkTarget = (await import_fs19.default.promises.readFile(linkPath)).toString(); + const browsersJSON = require(import_path19.default.join(linkTarget, "browsers.json")); + const descriptors = readDescriptors(browsersJSON); + for (const browserName of allDownloadableDirectoriesThatEverExisted) { + const descriptor = descriptors.find((d) => d.name === browserName); + if (!descriptor) + continue; + const browserPath = descriptor.dir; + const browserVersion = parseInt(descriptor.revision, 10); + browserList.push({ + browserName, + browserVersion, + browserPath, + referenceDir: linkTarget + }); + } + } catch (e) { + brokenLinks.push(linkPath); + } + } + return { browsers: browserList, brokenLinks }; + } + async _deleteStaleBrowsers(browserList) { + const usedBrowserPaths = /* @__PURE__ */ new Set(); + for (const browser of browserList) { + const { browserName, browserVersion, browserPath } = browser; + const shouldHaveMarkerFile = browserName === "chromium" && (browserVersion >= 786218 || browserVersion < 3e5) || browserName === "firefox" && browserVersion >= 1128 || browserName === "webkit" && browserVersion >= 1307 || // All new applications have a marker file right away. + browserName !== "firefox" && browserName !== "chromium" && browserName !== "webkit"; + if (!shouldHaveMarkerFile || await existsAsync(browserDirectoryToMarkerFilePath(browserPath))) + usedBrowserPaths.add(browserPath); + } + let downloadedBrowsers = (await import_fs19.default.promises.readdir(registryDirectory)).map((file) => import_path19.default.join(registryDirectory, file)); + downloadedBrowsers = downloadedBrowsers.filter((file) => isBrowserDirectory(file)); + const directories = new Set(downloadedBrowsers); + for (const browserDirectory of usedBrowserPaths) + directories.delete(browserDirectory); + for (const directory of directories) + logPolitely("Removing unused browser at " + directory); + await removeFolders([...directories]); + } + async _deleteBrokenInstallations(brokenLinks) { + for (const linkPath of brokenLinks) + await import_fs19.default.promises.unlink(linkPath).catch((e) => { + }); + } + _defaultBrowsersToInstall(options) { + let executables = this.defaultExecutables(); + if (options.shell === "no") + executables = executables.filter((e) => e.name !== "chromium-headless-shell" && e.name !== "chromium-tip-of-tree-headless-shell"); + if (options.shell === "only") + executables = executables.filter((e) => e.name !== "chromium" && e.name !== "chromium-tip-of-tree"); + return executables; + } + suggestedBrowsersToInstall() { + const names = this.executables().filter((e) => e.installType !== "none").map((e) => e.name); + names.push(...chromiumAliases); + return names.sort().join(", "); + } + isChromiumAlias(name) { + return chromiumAliases.includes(name); + } + resolveBrowsers(aliases2, options) { + if (aliases2.length === 0) + return this._defaultBrowsersToInstall(options); + const faultyArguments = []; + const executables = []; + const handleArgument = (arg) => { + const executable = this.findExecutable(arg); + if (!executable || executable.installType === "none") + faultyArguments.push(arg); + else + executables.push(executable); + if (executable?.browserName) + executables.push(this.findExecutable("ffmpeg")); + }; + for (const alias of aliases2) { + if (alias === "chromium" || chromiumAliases.includes(alias)) { + if (options.shell !== "only") + handleArgument("chromium"); + if (options.shell !== "no") + handleArgument("chromium-headless-shell"); + } else if (alias === "chromium-tip-of-tree") { + if (options.shell !== "only") + handleArgument("chromium-tip-of-tree"); + if (options.shell !== "no") + handleArgument("chromium-tip-of-tree-headless-shell"); + } else { + handleArgument(alias); + } + } + if (process.platform === "win32") + executables.push(this.findExecutable("winldd")); + if (faultyArguments.length) + throw new Error(`Invalid installation targets: ${faultyArguments.map((name) => `'${name}'`).join(", ")}. Expecting one of: ${this.suggestedBrowsersToInstall()}`); + return [...new Set(executables)]; + } + }; + registry = new Registry(require(import_path19.default.join(packageRoot, "browsers.json"))); + } +}); + +// packages/playwright-core/src/server/launchApp.ts +async function launchApp(browserType, options) { + const args = [...options.persistentContextOptions?.args ?? []]; + let channel = options.persistentContextOptions?.channel; + if (browserType.name() === "chromium") { + args.push( + "--app=data:text/html,", + `--window-size=${options.windowSize.width},${options.windowSize.height}`, + ...options.windowPosition ? [`--window-position=${options.windowPosition.x},${options.windowPosition.y}`] : [], + "--test-type=" + ); + if (!channel && !options.persistentContextOptions?.executablePath) + channel = findChromiumChannelBestEffort(options.sdkLanguage); + } + const controller = new ProgressController(); + let context2; + try { + context2 = await controller.run((progress2) => browserType.launchPersistentContext(progress2, "", { + ignoreDefaultArgs: ["--enable-automation"], + ...options?.persistentContextOptions, + channel, + noDefaultViewport: options.persistentContextOptions?.noDefaultViewport ?? true, + acceptDownloads: options?.persistentContextOptions?.acceptDownloads ?? (isUnderTest() ? "accept" : "internal-browser-default"), + colorScheme: options?.persistentContextOptions?.colorScheme ?? "no-override", + args + }), 0); + } catch (error) { + if (channel) { + error = rewriteErrorMessage(error, [ + `Failed to launch "${channel}" channel.`, + "Using custom channels could lead to unexpected behavior due to Enterprise policies (chrome://policy).", + "Install the default browser instead:", + wrapInASCIIBox(`${buildPlaywrightCLICommand(options.sdkLanguage, "install")}`, 2) + ].join("\n")); + } + throw error; + } + const [page] = context2.pages(); + if (browserType.name() === "chromium" && process.platform === "darwin") { + context2.on("page", async (newPage) => { + if (newPage.mainFrame().url() === "chrome://new-tab-page/") { + await page.bringToFront(nullProgress); + await newPage.close(nullProgress); + } + }); + } + if (browserType.name() === "chromium") + await installAppIcon(page); + return { context: context2, page }; +} +async function installAppIcon(page) { + const icon = await import_fs20.default.promises.readFile(libPath("server", "chromium", "appIcon.png")); + const crPage = page.delegate; + await crPage._mainFrameSession._client.send("Browser.setDockTile", { + image: icon.toString("base64") + }); +} +async function syncLocalStorageWithSettings(page, appName) { + if (isUnderTest()) + return; + const settingsFile = import_path20.default.join(registryDirectory, ".settings", `${appName}.json`); + const controller = new ProgressController(); + await controller.run(async (progress2) => { + await page.exposeBinding(progress2, "_saveSerializedSettings", (_, settings2) => { + import_fs20.default.mkdirSync(import_path20.default.dirname(settingsFile), { recursive: true }); + import_fs20.default.writeFileSync(settingsFile, settings2); + }); + const settings = await progress2.race(import_fs20.default.promises.readFile(settingsFile, "utf-8").catch(() => "{}")); + await page.addInitScript( + progress2, + `(${String((settings2) => { + if (location && location.protocol === "data:") + return; + if (window.top !== window) + return; + Object.entries(settings2).map(([k, v]) => localStorage[k] = v); + window.saveSettings = () => { + window._saveSerializedSettings(JSON.stringify({ ...localStorage })); + }; + })})(${settings}); + ` + ); + }); +} +var import_fs20, import_path20; +var init_launchApp = __esm({ + "packages/playwright-core/src/server/launchApp.ts"() { + "use strict"; + import_fs20 = __toESM(require("fs")); + import_path20 = __toESM(require("path")); + init_debug(); + init_stackTrace(); + init_ascii(); + init_package(); + init_registry(); + init_registry(); + init_progress(); + } +}); + +// packages/playwright-core/src/server/recorder/throttledFile.ts +var import_fs21, ThrottledFile; +var init_throttledFile = __esm({ + "packages/playwright-core/src/server/recorder/throttledFile.ts"() { + "use strict"; + import_fs21 = __toESM(require("fs")); + ThrottledFile = class { + constructor(file) { + this._file = file; + } + setContent(text2) { + this._text = text2; + if (!this._timer) + this._timer = setTimeout(() => this.flush(), 250); + } + flush() { + if (this._timer) { + clearTimeout(this._timer); + this._timer = void 0; + } + if (this._text) + import_fs21.default.writeFileSync(this._file, this._text); + this._text = void 0; + } + }; + } +}); + +// packages/playwright-core/src/server/codegen/language.ts +function generateCode(actions, languageGenerator, options) { + const header = languageGenerator.generateHeader(options); + const footer = languageGenerator.generateFooter(options.saveStorage); + const actionTexts = actions.map((a) => generateActionText(languageGenerator, a, !!options.generateAutoExpect)).filter(Boolean); + const text2 = [header, ...actionTexts, footer].join("\n"); + return { header, footer, actionTexts, text: text2 }; +} +function generateActionText(generator, action, generateAutoExpect) { + let text2 = generator.generateAction(action); + if (!text2) + return; + if (generateAutoExpect && action.action.preconditionSelector) { + const expectAction = { + frame: action.frame, + startTime: action.startTime, + endTime: action.startTime, + action: { + name: "assertVisible", + selector: action.action.preconditionSelector, + signals: [] + } + }; + const expectText = generator.generateAction(expectAction); + if (expectText) + text2 = expectText + "\n\n" + text2; + } + return text2; +} +function sanitizeDeviceOptions(device, options) { + const cleanedOptions = {}; + for (const property in options) { + if (JSON.stringify(device[property]) !== JSON.stringify(options[property])) + cleanedOptions[property] = options[property]; + } + return cleanedOptions; +} +function toSignalMap(action) { + let popup; + let download; + let dialog; + for (const signal of action.signals) { + if (signal.name === "popup") + popup = signal; + else if (signal.name === "download") + download = signal; + else if (signal.name === "dialog") + dialog = signal; + } + return { + popup, + download, + dialog + }; +} +function toKeyboardModifiers(modifiers) { + const result2 = []; + if (modifiers & 1) + result2.push("Alt"); + if (modifiers & 2) + result2.push("ControlOrMeta"); + if (modifiers & 4) + result2.push("ControlOrMeta"); + if (modifiers & 8) + result2.push("Shift"); + return result2; +} +function toClickOptionsForSourceCode(action) { + const modifiers = toKeyboardModifiers(action.modifiers); + const options = {}; + if (action.button !== "left") + options.button = action.button; + if (modifiers.length) + options.modifiers = modifiers; + if (action.clickCount > 2) + options.clickCount = action.clickCount; + if (action.position) + options.position = action.position; + return options; +} +var init_language = __esm({ + "packages/playwright-core/src/server/codegen/language.ts"() { + "use strict"; + } +}); + +// packages/playwright-core/src/server/deviceDescriptorsSource.json +var deviceDescriptorsSource_default; +var init_deviceDescriptorsSource = __esm({ + "packages/playwright-core/src/server/deviceDescriptorsSource.json"() { + deviceDescriptorsSource_default = { + "Blackberry PlayBook": { + userAgent: "Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML like Gecko) Version/26.5 Safari/536.2+", + viewport: { + width: 600, + height: 1024 + }, + deviceScaleFactor: 1, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "Blackberry PlayBook landscape": { + userAgent: "Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML like Gecko) Version/26.5 Safari/536.2+", + viewport: { + width: 1024, + height: 600 + }, + deviceScaleFactor: 1, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "BlackBerry Z30": { + userAgent: "Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) Version/26.5 Mobile Safari/537.10+", + viewport: { + width: 360, + height: 640 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "BlackBerry Z30 landscape": { + userAgent: "Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) Version/26.5 Mobile Safari/537.10+", + viewport: { + width: 640, + height: 360 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "Galaxy Note 3": { + userAgent: "Mozilla/5.0 (Linux; U; Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/26.5 Mobile Safari/534.30", + viewport: { + width: 360, + height: 640 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "Galaxy Note 3 landscape": { + userAgent: "Mozilla/5.0 (Linux; U; Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/26.5 Mobile Safari/534.30", + viewport: { + width: 640, + height: 360 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "Galaxy Note II": { + userAgent: "Mozilla/5.0 (Linux; U; Android 4.1; en-us; GT-N7100 Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/26.5 Mobile Safari/534.30", + viewport: { + width: 360, + height: 640 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "Galaxy Note II landscape": { + userAgent: "Mozilla/5.0 (Linux; U; Android 4.1; en-us; GT-N7100 Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/26.5 Mobile Safari/534.30", + viewport: { + width: 640, + height: 360 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "Galaxy S III": { + userAgent: "Mozilla/5.0 (Linux; U; Android 4.0; en-us; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/26.5 Mobile Safari/534.30", + viewport: { + width: 360, + height: 640 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "Galaxy S III landscape": { + userAgent: "Mozilla/5.0 (Linux; U; Android 4.0; en-us; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/26.5 Mobile Safari/534.30", + viewport: { + width: 640, + height: 360 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "Galaxy S5": { + userAgent: "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + viewport: { + width: 360, + height: 640 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Galaxy S5 landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + viewport: { + width: 640, + height: 360 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Galaxy S8": { + userAgent: "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + viewport: { + width: 360, + height: 740 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Galaxy S8 landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + viewport: { + width: 740, + height: 360 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Galaxy S9+": { + userAgent: "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + viewport: { + width: 320, + height: 658 + }, + deviceScaleFactor: 4.5, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Galaxy S9+ landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + viewport: { + width: 658, + height: 320 + }, + deviceScaleFactor: 4.5, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Galaxy S24": { + userAgent: "Mozilla/5.0 (Linux; Android 14; SM-S921U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + viewport: { + width: 360, + height: 780 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Galaxy S24 landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 14; SM-S921U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + viewport: { + width: 780, + height: 360 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Galaxy A55": { + userAgent: "Mozilla/5.0 (Linux; Android 14; SM-A556B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + viewport: { + width: 480, + height: 1040 + }, + deviceScaleFactor: 2.25, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Galaxy A55 landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 14; SM-A556B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + viewport: { + width: 1040, + height: 480 + }, + deviceScaleFactor: 2.25, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Galaxy Tab S4": { + userAgent: "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36", + viewport: { + width: 712, + height: 1138 + }, + deviceScaleFactor: 2.25, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Galaxy Tab S4 landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36", + viewport: { + width: 1138, + height: 712 + }, + deviceScaleFactor: 2.25, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Galaxy Tab S9": { + userAgent: "Mozilla/5.0 (Linux; Android 14; SM-X710) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36", + viewport: { + width: 640, + height: 1024 + }, + deviceScaleFactor: 2.5, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Galaxy Tab S9 landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 14; SM-X710) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36", + viewport: { + width: 1024, + height: 640 + }, + deviceScaleFactor: 2.5, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Galaxy Z Fold 6": { + userAgent: "Mozilla/5.0 (Linux; Android 10; SM-F956U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 928, + height: 1080 + }, + viewport: { + width: 928, + height: 1004 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Galaxy Z Fold 6 landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 10; SM-F956U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 1080, + height: 928 + }, + viewport: { + width: 1028, + height: 876 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Galaxy Z Fold 6 Cover": { + userAgent: "Mozilla/5.0 (Linux; Android 10; SM-F956U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 484, + height: 1188 + }, + viewport: { + width: 484, + height: 1112 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Galaxy Z Fold 6 Cover landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 10; SM-F956U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 1188, + height: 484 + }, + viewport: { + width: 1136, + height: 432 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Galaxy Z Fold 7": { + userAgent: "Mozilla/5.0 (Linux; Android 10; SM-F966U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 984, + height: 1092 + }, + viewport: { + width: 984, + height: 1016 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Galaxy Z Fold 7 landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 10; SM-F966U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 1092, + height: 984 + }, + viewport: { + width: 1040, + height: 932 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Galaxy Z Fold 7 Cover": { + userAgent: "Mozilla/5.0 (Linux; Android 10; SM-F966U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 360, + height: 840 + }, + viewport: { + width: 360, + height: 764 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Galaxy Z Fold 7 Cover landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 10; SM-F966U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 840, + height: 360 + }, + viewport: { + width: 788, + height: 308 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Galaxy Z Flip 6": { + userAgent: "Mozilla/5.0 (Linux; Android 10; SM-F741U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 360, + height: 880 + }, + viewport: { + width: 360, + height: 804 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Galaxy Z Flip 6 landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 10; SM-F741U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 880, + height: 360 + }, + viewport: { + width: 828, + height: 308 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Galaxy Z Flip 6 Cover": { + userAgent: "Mozilla/5.0 (Linux; Android 10; SM-F741U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 360, + height: 374 + }, + viewport: { + width: 360, + height: 298 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Galaxy Z Flip 6 Cover landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 10; SM-F741U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 374, + height: 360 + }, + viewport: { + width: 322, + height: 308 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Galaxy Z Flip 7": { + userAgent: "Mozilla/5.0 (Linux; Android 10; SM-F761U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 360, + height: 840 + }, + viewport: { + width: 360, + height: 764 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Galaxy Z Flip 7 landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 10; SM-F761U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 840, + height: 360 + }, + viewport: { + width: 788, + height: 308 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Galaxy Z Flip 7 Cover": { + userAgent: "Mozilla/5.0 (Linux; Android 10; SM-F761U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 474, + height: 524 + }, + viewport: { + width: 474, + height: 448 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Galaxy Z Flip 7 Cover landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 10; SM-F761U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 524, + height: 474 + }, + viewport: { + width: 472, + height: 422 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "iPad (gen 5)": { + userAgent: "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + viewport: { + width: 768, + height: 1024 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPad (gen 5) landscape": { + userAgent: "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + viewport: { + width: 1024, + height: 768 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPad (gen 6)": { + userAgent: "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + viewport: { + width: 768, + height: 1024 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPad (gen 6) landscape": { + userAgent: "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + viewport: { + width: 1024, + height: 768 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPad (gen 7)": { + userAgent: "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + viewport: { + width: 810, + height: 1080 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPad (gen 7) landscape": { + userAgent: "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + viewport: { + width: 1080, + height: 810 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPad (gen 11)": { + userAgent: "Mozilla/5.0 (iPad; CPU OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/19E241 Safari/604.1", + viewport: { + width: 656, + height: 944 + }, + deviceScaleFactor: 2.5, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPad (gen 11) landscape": { + userAgent: "Mozilla/5.0 (iPad; CPU OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/19E241 Safari/604.1", + viewport: { + width: 944, + height: 656 + }, + deviceScaleFactor: 2.5, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPad Mini": { + userAgent: "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + viewport: { + width: 768, + height: 1024 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPad Mini landscape": { + userAgent: "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + viewport: { + width: 1024, + height: 768 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPad Pro 11": { + userAgent: "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + viewport: { + width: 834, + height: 1194 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPad Pro 11 landscape": { + userAgent: "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + viewport: { + width: 1194, + height: 834 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 6": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + viewport: { + width: 375, + height: 667 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 6 landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + viewport: { + width: 667, + height: 375 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 6 Plus": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + viewport: { + width: 414, + height: 736 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 6 Plus landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + viewport: { + width: 736, + height: 414 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 7": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + viewport: { + width: 375, + height: 667 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 7 landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + viewport: { + width: 667, + height: 375 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 7 Plus": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + viewport: { + width: 414, + height: 736 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 7 Plus landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + viewport: { + width: 736, + height: 414 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 8": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + viewport: { + width: 375, + height: 667 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 8 landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + viewport: { + width: 667, + height: 375 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 8 Plus": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + viewport: { + width: 414, + height: 736 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 8 Plus landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + viewport: { + width: 736, + height: 414 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone SE": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/26.5 Mobile/14E304 Safari/602.1", + viewport: { + width: 320, + height: 568 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone SE landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/26.5 Mobile/14E304 Safari/602.1", + viewport: { + width: 568, + height: 320 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone SE (3rd gen)": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/26.5 Mobile/19E241 Safari/602.1", + viewport: { + width: 375, + height: 667 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone SE (3rd gen) landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/26.5 Mobile/19E241 Safari/602.1", + viewport: { + width: 667, + height: 375 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone X": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + viewport: { + width: 375, + height: 812 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone X landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + viewport: { + width: 812, + height: 375 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone XR": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + viewport: { + width: 414, + height: 896 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone XR landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + viewport: { + width: 896, + height: 414 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 11": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 414, + height: 896 + }, + viewport: { + width: 414, + height: 715 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 11 landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 414, + height: 896 + }, + viewport: { + width: 800, + height: 364 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 11 Pro": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 375, + height: 812 + }, + viewport: { + width: 375, + height: 635 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 11 Pro landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 375, + height: 812 + }, + viewport: { + width: 724, + height: 325 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 11 Pro Max": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 414, + height: 896 + }, + viewport: { + width: 414, + height: 715 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 11 Pro Max landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 414, + height: 896 + }, + viewport: { + width: 808, + height: 364 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 12": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 390, + height: 844 + }, + viewport: { + width: 390, + height: 664 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 12 landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 390, + height: 844 + }, + viewport: { + width: 750, + height: 340 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 12 Pro": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 390, + height: 844 + }, + viewport: { + width: 390, + height: 664 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 12 Pro landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 390, + height: 844 + }, + viewport: { + width: 750, + height: 340 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 12 Pro Max": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 428, + height: 926 + }, + viewport: { + width: 428, + height: 746 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 12 Pro Max landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 428, + height: 926 + }, + viewport: { + width: 832, + height: 378 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 12 Mini": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 375, + height: 812 + }, + viewport: { + width: 375, + height: 629 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 12 Mini landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 375, + height: 812 + }, + viewport: { + width: 712, + height: 325 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 13": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 390, + height: 844 + }, + viewport: { + width: 390, + height: 664 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 13 landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 390, + height: 844 + }, + viewport: { + width: 750, + height: 342 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 13 Pro": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 390, + height: 844 + }, + viewport: { + width: 390, + height: 664 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 13 Pro landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 390, + height: 844 + }, + viewport: { + width: 750, + height: 342 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 13 Pro Max": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 428, + height: 926 + }, + viewport: { + width: 428, + height: 746 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 13 Pro Max landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 428, + height: 926 + }, + viewport: { + width: 832, + height: 380 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 13 Mini": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 375, + height: 812 + }, + viewport: { + width: 375, + height: 629 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 13 Mini landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 375, + height: 812 + }, + viewport: { + width: 712, + height: 327 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 14": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 390, + height: 844 + }, + viewport: { + width: 390, + height: 664 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 14 landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 390, + height: 844 + }, + viewport: { + width: 750, + height: 340 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 14 Plus": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 428, + height: 926 + }, + viewport: { + width: 428, + height: 746 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 14 Plus landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 428, + height: 926 + }, + viewport: { + width: 832, + height: 378 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 14 Pro": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 393, + height: 852 + }, + viewport: { + width: 393, + height: 660 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 14 Pro landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 393, + height: 852 + }, + viewport: { + width: 734, + height: 343 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 14 Pro Max": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 430, + height: 932 + }, + viewport: { + width: 430, + height: 740 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 14 Pro Max landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 430, + height: 932 + }, + viewport: { + width: 814, + height: 380 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 15": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 393, + height: 852 + }, + viewport: { + width: 393, + height: 659 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 15 landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 393, + height: 852 + }, + viewport: { + width: 734, + height: 343 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 15 Plus": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 430, + height: 932 + }, + viewport: { + width: 430, + height: 739 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 15 Plus landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 430, + height: 932 + }, + viewport: { + width: 814, + height: 380 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 15 Pro": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 393, + height: 852 + }, + viewport: { + width: 393, + height: 659 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 15 Pro landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 393, + height: 852 + }, + viewport: { + width: 734, + height: 343 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 15 Pro Max": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 430, + height: 932 + }, + viewport: { + width: 430, + height: 739 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 15 Pro Max landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 430, + height: 932 + }, + viewport: { + width: 814, + height: 380 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 16": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 393, + height: 852 + }, + viewport: { + width: 393, + height: 659 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 16 landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 393, + height: 852 + }, + viewport: { + width: 734, + height: 343 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 16 Plus": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 430, + height: 932 + }, + viewport: { + width: 430, + height: 739 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 16 Plus landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 430, + height: 932 + }, + viewport: { + width: 814, + height: 380 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 16 Pro": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 402, + height: 874 + }, + viewport: { + width: 402, + height: 681 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 16 Pro landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 402, + height: 874 + }, + viewport: { + width: 756, + height: 352 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 16 Pro Max": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 440, + height: 956 + }, + viewport: { + width: 440, + height: 763 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 16 Pro Max landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 440, + height: 956 + }, + viewport: { + width: 838, + height: 390 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 16e": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 390, + height: 844 + }, + viewport: { + width: 390, + height: 651 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 16e landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 390, + height: 844 + }, + viewport: { + width: 726, + height: 340 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 17": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 402, + height: 874 + }, + viewport: { + width: 402, + height: 681 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 17 landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 402, + height: 874 + }, + viewport: { + width: 756, + height: 352 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone Air": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 420, + height: 912 + }, + viewport: { + width: 420, + height: 719 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone Air landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 420, + height: 912 + }, + viewport: { + width: 794, + height: 370 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 17 Pro": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 402, + height: 874 + }, + viewport: { + width: 402, + height: 681 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 17 Pro landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 402, + height: 874 + }, + viewport: { + width: 756, + height: 352 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 17 Pro Max": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 440, + height: 956 + }, + viewport: { + width: 440, + height: 763 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 17 Pro Max landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 440, + height: 956 + }, + viewport: { + width: 838, + height: 390 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 17e": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 390, + height: 844 + }, + viewport: { + width: 390, + height: 651 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "iPhone 17e landscape": { + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + screen: { + width: 390, + height: 844 + }, + viewport: { + width: 726, + height: 340 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "Kindle Fire HDX": { + userAgent: "Mozilla/5.0 (Linux; U; en-us; KFAPWI Build/JDQ39) AppleWebKit/535.19 (KHTML, like Gecko) Silk/3.13 Safari/535.19 Silk-Accelerated=true", + viewport: { + width: 800, + height: 1280 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "Kindle Fire HDX landscape": { + userAgent: "Mozilla/5.0 (Linux; U; en-us; KFAPWI Build/JDQ39) AppleWebKit/535.19 (KHTML, like Gecko) Silk/3.13 Safari/535.19 Silk-Accelerated=true", + viewport: { + width: 1280, + height: 800 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "LG Optimus L70": { + userAgent: "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/149.0.7827.55 Mobile Safari/537.36", + viewport: { + width: 384, + height: 640 + }, + deviceScaleFactor: 1.25, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "LG Optimus L70 landscape": { + userAgent: "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/149.0.7827.55 Mobile Safari/537.36", + viewport: { + width: 640, + height: 384 + }, + deviceScaleFactor: 1.25, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Microsoft Lumia 550": { + userAgent: "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36 Edge/14.14263", + viewport: { + width: 360, + height: 640 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Microsoft Lumia 550 landscape": { + userAgent: "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36 Edge/14.14263", + viewport: { + width: 640, + height: 360 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Microsoft Lumia 950": { + userAgent: "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36 Edge/14.14263", + viewport: { + width: 360, + height: 640 + }, + deviceScaleFactor: 4, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Microsoft Lumia 950 landscape": { + userAgent: "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36 Edge/14.14263", + viewport: { + width: 640, + height: 360 + }, + deviceScaleFactor: 4, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Nexus 10": { + userAgent: "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36", + viewport: { + width: 800, + height: 1280 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Nexus 10 landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36", + viewport: { + width: 1280, + height: 800 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Nexus 4": { + userAgent: "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + viewport: { + width: 384, + height: 640 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Nexus 4 landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + viewport: { + width: 640, + height: 384 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Nexus 5": { + userAgent: "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + viewport: { + width: 360, + height: 640 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Nexus 5 landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + viewport: { + width: 640, + height: 360 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Nexus 5X": { + userAgent: "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + viewport: { + width: 412, + height: 732 + }, + deviceScaleFactor: 2.625, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Nexus 5X landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + viewport: { + width: 732, + height: 412 + }, + deviceScaleFactor: 2.625, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Nexus 6": { + userAgent: "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + viewport: { + width: 412, + height: 732 + }, + deviceScaleFactor: 3.5, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Nexus 6 landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + viewport: { + width: 732, + height: 412 + }, + deviceScaleFactor: 3.5, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Nexus 6P": { + userAgent: "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + viewport: { + width: 412, + height: 732 + }, + deviceScaleFactor: 3.5, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Nexus 6P landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + viewport: { + width: 732, + height: 412 + }, + deviceScaleFactor: 3.5, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Nexus 7": { + userAgent: "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36", + viewport: { + width: 600, + height: 960 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Nexus 7 landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36", + viewport: { + width: 960, + height: 600 + }, + deviceScaleFactor: 2, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Nokia Lumia 520": { + userAgent: "Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 520)", + viewport: { + width: 320, + height: 533 + }, + deviceScaleFactor: 1.5, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Nokia Lumia 520 landscape": { + userAgent: "Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 520)", + viewport: { + width: 533, + height: 320 + }, + deviceScaleFactor: 1.5, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Nokia N9": { + userAgent: "Mozilla/5.0 (MeeGo; NokiaN9) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13", + viewport: { + width: 480, + height: 854 + }, + deviceScaleFactor: 1, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "Nokia N9 landscape": { + userAgent: "Mozilla/5.0 (MeeGo; NokiaN9) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13", + viewport: { + width: 854, + height: 480 + }, + deviceScaleFactor: 1, + isMobile: true, + hasTouch: true, + defaultBrowserType: "webkit" + }, + "Pixel 2": { + userAgent: "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + viewport: { + width: 411, + height: 731 + }, + deviceScaleFactor: 2.625, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 2 landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + viewport: { + width: 731, + height: 411 + }, + deviceScaleFactor: 2.625, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 2 XL": { + userAgent: "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + viewport: { + width: 411, + height: 823 + }, + deviceScaleFactor: 3.5, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 2 XL landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + viewport: { + width: 823, + height: 411 + }, + deviceScaleFactor: 3.5, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 3": { + userAgent: "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + viewport: { + width: 393, + height: 786 + }, + deviceScaleFactor: 2.75, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 3 landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + viewport: { + width: 786, + height: 393 + }, + deviceScaleFactor: 2.75, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 4": { + userAgent: "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + viewport: { + width: 353, + height: 745 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 4 landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + viewport: { + width: 745, + height: 353 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 4a (5G)": { + userAgent: "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 412, + height: 892 + }, + viewport: { + width: 412, + height: 765 + }, + deviceScaleFactor: 2.63, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 4a (5G) landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + height: 892, + width: 412 + }, + viewport: { + width: 840, + height: 312 + }, + deviceScaleFactor: 2.63, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 5": { + userAgent: "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 393, + height: 851 + }, + viewport: { + width: 393, + height: 727 + }, + deviceScaleFactor: 2.75, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 5 landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 851, + height: 393 + }, + viewport: { + width: 802, + height: 293 + }, + deviceScaleFactor: 2.75, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 6": { + userAgent: "Mozilla/5.0 (Linux; Android 12; Pixel 6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 412, + height: 915 + }, + viewport: { + width: 412, + height: 839 + }, + deviceScaleFactor: 2.625, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 6 landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 12; Pixel 6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 915, + height: 412 + }, + viewport: { + width: 863, + height: 360 + }, + deviceScaleFactor: 2.625, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 6 Pro": { + userAgent: "Mozilla/5.0 (Linux; Android 12; Pixel 6 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 412, + height: 892 + }, + viewport: { + width: 412, + height: 816 + }, + deviceScaleFactor: 3.5, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 6 Pro landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 12; Pixel 6 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 892, + height: 412 + }, + viewport: { + width: 840, + height: 360 + }, + deviceScaleFactor: 3.5, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 6a": { + userAgent: "Mozilla/5.0 (Linux; Android 12; Pixel 6a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 412, + height: 915 + }, + viewport: { + width: 412, + height: 839 + }, + deviceScaleFactor: 2.625, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 6a landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 12; Pixel 6a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 915, + height: 412 + }, + viewport: { + width: 863, + height: 360 + }, + deviceScaleFactor: 2.625, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 7": { + userAgent: "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 412, + height: 915 + }, + viewport: { + width: 412, + height: 839 + }, + deviceScaleFactor: 2.625, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 7 landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 915, + height: 412 + }, + viewport: { + width: 863, + height: 360 + }, + deviceScaleFactor: 2.625, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 7 Pro": { + userAgent: "Mozilla/5.0 (Linux; Android 13; Pixel 7 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 412, + height: 892 + }, + viewport: { + width: 412, + height: 816 + }, + deviceScaleFactor: 3.5, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 7 Pro landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 13; Pixel 7 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 892, + height: 412 + }, + viewport: { + width: 840, + height: 360 + }, + deviceScaleFactor: 3.5, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 7a": { + userAgent: "Mozilla/5.0 (Linux; Android 13; Pixel 7a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 412, + height: 915 + }, + viewport: { + width: 412, + height: 839 + }, + deviceScaleFactor: 2.625, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 7a landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 13; Pixel 7a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 915, + height: 412 + }, + viewport: { + width: 863, + height: 360 + }, + deviceScaleFactor: 2.625, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 8": { + userAgent: "Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 412, + height: 915 + }, + viewport: { + width: 412, + height: 839 + }, + deviceScaleFactor: 2.625, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 8 landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 915, + height: 412 + }, + viewport: { + width: 863, + height: 360 + }, + deviceScaleFactor: 2.625, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 8 Pro": { + userAgent: "Mozilla/5.0 (Linux; Android 14; Pixel 8 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 448, + height: 997 + }, + viewport: { + width: 448, + height: 921 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 8 Pro landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 14; Pixel 8 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 997, + height: 448 + }, + viewport: { + width: 945, + height: 396 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 8a": { + userAgent: "Mozilla/5.0 (Linux; Android 14; Pixel 8a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 412, + height: 915 + }, + viewport: { + width: 412, + height: 839 + }, + deviceScaleFactor: 2.625, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 8a landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 14; Pixel 8a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 915, + height: 412 + }, + viewport: { + width: 863, + height: 360 + }, + deviceScaleFactor: 2.625, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 9": { + userAgent: "Mozilla/5.0 (Linux; Android 14; Pixel 9) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 360, + height: 808 + }, + viewport: { + width: 360, + height: 732 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 9 landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 14; Pixel 9) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 808, + height: 360 + }, + viewport: { + width: 756, + height: 308 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 9 Pro": { + userAgent: "Mozilla/5.0 (Linux; Android 14; Pixel 9 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 427, + height: 952 + }, + viewport: { + width: 427, + height: 876 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 9 Pro landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 14; Pixel 9 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 952, + height: 427 + }, + viewport: { + width: 900, + height: 375 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 9 Pro XL": { + userAgent: "Mozilla/5.0 (Linux; Android 14; Pixel 9 Pro XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 448, + height: 997 + }, + viewport: { + width: 448, + height: 921 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 9 Pro XL landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 14; Pixel 9 Pro XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 997, + height: 448 + }, + viewport: { + width: 945, + height: 396 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 10": { + userAgent: "Mozilla/5.0 (Linux; Android 16; Pixel 10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 360, + height: 808 + }, + viewport: { + width: 360, + height: 732 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 10 landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 16; Pixel 10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 808, + height: 360 + }, + viewport: { + width: 756, + height: 308 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 10 Pro": { + userAgent: "Mozilla/5.0 (Linux; Android 16; Pixel 10 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 427, + height: 952 + }, + viewport: { + width: 427, + height: 876 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 10 Pro landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 16; Pixel 10 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 952, + height: 427 + }, + viewport: { + width: 900, + height: 375 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 10 Pro XL": { + userAgent: "Mozilla/5.0 (Linux; Android 16; Pixel 10 Pro XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 448, + height: 997 + }, + viewport: { + width: 448, + height: 921 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Pixel 10 Pro XL landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 16; Pixel 10 Pro XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + screen: { + width: 997, + height: 448 + }, + viewport: { + width: 945, + height: 396 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Moto G4": { + userAgent: "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + viewport: { + width: 360, + height: 640 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Moto G4 landscape": { + userAgent: "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + viewport: { + width: 640, + height: 360 + }, + deviceScaleFactor: 3, + isMobile: true, + hasTouch: true, + defaultBrowserType: "chromium" + }, + "Desktop Chrome HiDPI": { + userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36", + screen: { + width: 1792, + height: 1120 + }, + viewport: { + width: 1280, + height: 720 + }, + deviceScaleFactor: 2, + isMobile: false, + hasTouch: false, + defaultBrowserType: "chromium" + }, + "Desktop Edge HiDPI": { + userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36 Edg/149.0.7827.55", + screen: { + width: 1792, + height: 1120 + }, + viewport: { + width: 1280, + height: 720 + }, + deviceScaleFactor: 2, + isMobile: false, + hasTouch: false, + defaultBrowserType: "chromium" + }, + "Desktop Firefox HiDPI": { + userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:151.0) Gecko/20100101 Firefox/151.0", + screen: { + width: 1792, + height: 1120 + }, + viewport: { + width: 1280, + height: 720 + }, + deviceScaleFactor: 2, + isMobile: false, + hasTouch: false, + defaultBrowserType: "firefox" + }, + "Desktop Safari": { + userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Safari/605.1.15", + screen: { + width: 1792, + height: 1120 + }, + viewport: { + width: 1280, + height: 720 + }, + deviceScaleFactor: 2, + isMobile: false, + hasTouch: false, + defaultBrowserType: "webkit" + }, + "Desktop Chrome": { + userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36", + screen: { + width: 1920, + height: 1080 + }, + viewport: { + width: 1280, + height: 720 + }, + deviceScaleFactor: 1, + isMobile: false, + hasTouch: false, + defaultBrowserType: "chromium" + }, + "Desktop Edge": { + userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36 Edg/149.0.7827.55", + screen: { + width: 1920, + height: 1080 + }, + viewport: { + width: 1280, + height: 720 + }, + deviceScaleFactor: 1, + isMobile: false, + hasTouch: false, + defaultBrowserType: "chromium" + }, + "Desktop Firefox": { + userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:151.0) Gecko/20100101 Firefox/151.0", + screen: { + width: 1920, + height: 1080 + }, + viewport: { + width: 1280, + height: 720 + }, + deviceScaleFactor: 1, + isMobile: false, + hasTouch: false, + defaultBrowserType: "firefox" + } + }; + } +}); + +// packages/playwright-core/src/server/deviceDescriptors.ts +var deviceDescriptors; +var init_deviceDescriptors = __esm({ + "packages/playwright-core/src/server/deviceDescriptors.ts"() { + "use strict"; + init_deviceDescriptorsSource(); + deviceDescriptors = deviceDescriptorsSource_default; + } +}); + +// packages/playwright-core/src/server/codegen/csharp.ts +function formatObject2(value2, indent = " ", name = "") { + if (typeof value2 === "string") { + if (["colorScheme", "modifiers", "button", "recordHarContent", "recordHarMode", "serviceWorkers"].includes(name)) + return `${getEnumName(name)}.${toPascal(value2)}`; + return quote(value2); + } + if (Array.isArray(value2)) + return `new[] { ${value2.map((o) => formatObject2(o, indent, name)).join(", ")} }`; + if (typeof value2 === "object") { + const keys = Object.keys(value2).filter((key) => value2[key] !== void 0).sort(); + if (!keys.length) + return `new()`; + const tokens = []; + for (const key of keys) { + const property = getPropertyName(key); + tokens.push(`${property} = ${formatObject2(value2[key], indent, key)},`); + } + return `new() +{ +${indent}${tokens.join(` +${indent}`)} +${indent}}`; + } + if (name === "latitude" || name === "longitude") + return String(value2) + "m"; + return String(value2); +} +function getEnumName(value2) { + switch (value2) { + case "modifiers": + return "KeyboardModifier"; + case "button": + return "MouseButton"; + case "recordHarMode": + return "HarMode"; + case "recordHarContent": + return "HarContentPolicy"; + case "serviceWorkers": + return "ServiceWorkerPolicy"; + default: + return toPascal(value2); + } +} +function getPropertyName(key) { + switch (key) { + case "storageState": + return "StorageStatePath"; + case "viewport": + return "ViewportSize"; + default: + return toPascal(key); + } +} +function toPascal(value2) { + return value2[0].toUpperCase() + value2.slice(1); +} +function formatContextOptions(contextOptions, deviceName) { + const options = { ...contextOptions }; + delete options.recordHar; + const device = deviceName && deviceDescriptors[deviceName]; + if (!device) { + if (!Object.entries(options).length) + return ""; + return formatObject2(options, " "); + } + if (!Object.entries(sanitizeDeviceOptions(device, options)).length) + return `playwright.Devices[${quote(deviceName)}]`; + delete options["defaultBrowserType"]; + return formatObject2(options, " "); +} +function quote(text2) { + return escapeWithQuotes(text2, '"'); +} +var CSharpLanguageGenerator, CSharpFormatter; +var init_csharp = __esm({ + "packages/playwright-core/src/server/codegen/csharp.ts"() { + "use strict"; + init_locatorGenerators(); + init_stringUtils(); + init_language(); + init_deviceDescriptors(); + CSharpLanguageGenerator = class { + constructor(mode) { + this.groupName = ".NET C#"; + this.highlighter = "csharp"; + if (mode === "library") { + this.name = "Library"; + this.id = "csharp"; + } else if (mode === "mstest") { + this.name = "MSTest"; + this.id = "csharp-mstest"; + } else if (mode === "nunit") { + this.name = "NUnit"; + this.id = "csharp-nunit"; + } else if (mode === "xunit") { + this.name = "xUnit"; + this.id = "csharp-xunit"; + } else { + throw new Error(`Unknown C# language mode: ${mode}`); + } + this._mode = mode; + } + generateAction(actionInContext) { + const action = this._generateActionInner(actionInContext); + if (action) + return action; + return ""; + } + _generateActionInner(actionInContext) { + const action = actionInContext.action; + if (this._mode !== "library" && (action.name === "openPage" || action.name === "closePage")) + return ""; + const pageAlias = this._formatPageAlias(actionInContext.frame.pageAlias); + const formatter = new CSharpFormatter(this._mode === "library" ? 0 : 8); + if (action.name === "openPage") { + formatter.add(`var ${pageAlias} = await context.NewPageAsync();`); + if (action.url && action.url !== "about:blank" && action.url !== "chrome://newtab/") + formatter.add(`await ${pageAlias}.GotoAsync(${quote(action.url)});`); + return formatter.format(); + } + const locators = actionInContext.frame.framePath.map((selector) => `.${this._asLocator(selector)}.ContentFrame`); + const subject = `${pageAlias}${locators.join("")}`; + const signals = toSignalMap(action); + if (signals.dialog) { + formatter.add(` void ${pageAlias}_Dialog${signals.dialog.dialogAlias}_EventHandler(object sender, IDialog dialog) + { + Console.WriteLine($"Dialog message: {dialog.Message}"); + dialog.DismissAsync(); + ${pageAlias}.Dialog -= ${pageAlias}_Dialog${signals.dialog.dialogAlias}_EventHandler; + } + ${pageAlias}.Dialog += ${pageAlias}_Dialog${signals.dialog.dialogAlias}_EventHandler;`); + } + const lines = []; + lines.push(this._generateActionCall(subject, actionInContext)); + if (signals.download) { + lines.unshift(`var download${signals.download.downloadAlias} = await ${pageAlias}.RunAndWaitForDownloadAsync(async () => +{`); + lines.push(`});`); + } + if (signals.popup) { + lines.unshift(`var ${this._formatPageAlias(signals.popup.popupAlias)} = await ${pageAlias}.RunAndWaitForPopupAsync(async () => +{`); + lines.push(`});`); + } + for (const line of lines) + formatter.add(line); + return formatter.format(); + } + _formatPageAlias(pageAlias) { + if (this._mode === "library") + return pageAlias; + if (pageAlias === "page") + return "Page"; + return pageAlias; + } + _generateActionCall(subject, actionInContext) { + const action = actionInContext.action; + switch (action.name) { + case "openPage": + throw Error("Not reached"); + case "closePage": + return `await ${subject}.CloseAsync();`; + case "click": { + let method = "Click"; + if (action.clickCount === 2) + method = "DblClick"; + const options = toClickOptionsForSourceCode(action); + if (!Object.entries(options).length) + return `await ${subject}.${this._asLocator(action.selector)}.${method}Async();`; + const optionsString = formatObject2(options, " "); + return `await ${subject}.${this._asLocator(action.selector)}.${method}Async(${optionsString});`; + } + case "hover": { + const optionsString = action.position ? formatObject2({ position: action.position }, " ") : ""; + return `await ${subject}.${this._asLocator(action.selector)}.HoverAsync(${optionsString});`; + } + case "check": + return `await ${subject}.${this._asLocator(action.selector)}.CheckAsync();`; + case "uncheck": + return `await ${subject}.${this._asLocator(action.selector)}.UncheckAsync();`; + case "fill": + return `await ${subject}.${this._asLocator(action.selector)}.FillAsync(${quote(action.text)});`; + case "setInputFiles": + return `await ${subject}.${this._asLocator(action.selector)}.SetInputFilesAsync(${formatObject2(action.files)});`; + case "press": { + const modifiers = toKeyboardModifiers(action.modifiers); + const shortcut = [...modifiers, action.key].join("+"); + return `await ${subject}.${this._asLocator(action.selector)}.PressAsync(${quote(shortcut)});`; + } + case "navigate": + return `await ${subject}.GotoAsync(${quote(action.url)});`; + case "select": + return `await ${subject}.${this._asLocator(action.selector)}.SelectOptionAsync(${formatObject2(action.options)});`; + case "assertText": + return `await Expect(${subject}.${this._asLocator(action.selector)}).${action.substring ? "ToContainTextAsync" : "ToHaveTextAsync"}(${quote(action.text)});`; + case "assertChecked": + return `await Expect(${subject}.${this._asLocator(action.selector)})${action.checked ? "" : ".Not"}.ToBeCheckedAsync();`; + case "assertVisible": + return `await Expect(${subject}.${this._asLocator(action.selector)}).ToBeVisibleAsync();`; + case "assertValue": { + const assertion = action.value ? `ToHaveValueAsync(${quote(action.value)})` : `ToBeEmptyAsync()`; + return `await Expect(${subject}.${this._asLocator(action.selector)}).${assertion};`; + } + case "assertSnapshot": + return `await Expect(${subject}.${this._asLocator(action.selector)}).ToMatchAriaSnapshotAsync(${quote(action.ariaSnapshot)});`; + } + } + _asLocator(selector) { + return asLocator("csharp", selector); + } + generateHeader(options) { + if (this._mode === "library") + return this.generateStandaloneHeader(options); + return this.generateTestRunnerHeader(options); + } + generateStandaloneHeader(options) { + const formatter = new CSharpFormatter(0); + formatter.add(` + using Microsoft.Playwright; + using System; + using System.Threading.Tasks; + + using var playwright = await Playwright.CreateAsync(); + await using var browser = await playwright.${toPascal(options.browserName)}.LaunchAsync(${formatObject2(options.launchOptions, " ")}); + var context = await browser.NewContextAsync(${formatContextOptions(options.contextOptions, options.deviceName)});`); + if (options.contextOptions.recordHar) { + const url2 = options.contextOptions.recordHar.urlFilter; + formatter.add(` await context.RouteFromHARAsync(${quote(options.contextOptions.recordHar.path)}${url2 ? `, ${formatObject2({ url: url2 }, " ")}` : ""});`); + } + formatter.newLine(); + return formatter.format(); + } + generateTestRunnerHeader(options) { + const formatter = new CSharpFormatter(0); + const playwrightNamespace = this._mode === "nunit" ? "NUnit" : this._mode === "xunit" ? "Xunit" : "MSTest"; + const classAttributes = this._mode === "nunit" ? `[Parallelizable(ParallelScope.Self)] + [TestFixture] + ` : this._mode === "mstest" ? `[TestClass] + ` : ""; + formatter.add(` + using Microsoft.Playwright.${playwrightNamespace}; + using Microsoft.Playwright;${this._mode === "xunit" ? ` + using Xunit;` : ""} + + ${classAttributes}public class Tests : PageTest + {`); + const formattedContextOptions = formatContextOptions(options.contextOptions, options.deviceName); + if (formattedContextOptions) { + formatter.add(`public override BrowserNewContextOptions ContextOptions() + { + return ${formattedContextOptions}; + }`); + formatter.newLine(); + } + const testAttribute = this._mode === "nunit" ? "Test" : this._mode === "xunit" ? "Fact" : "TestMethod"; + formatter.add(` [${testAttribute}] + public async Task MyTest() + {`); + if (options.contextOptions.recordHar) { + const url2 = options.contextOptions.recordHar.urlFilter; + formatter.add(` await Context.RouteFromHARAsync(${quote(options.contextOptions.recordHar.path)}${url2 ? `, ${formatObject2({ url: url2 }, " ")}` : ""});`); + } + return formatter.format(); + } + generateFooter(saveStorage) { + const offset = this._mode === "library" ? "" : " "; + let storageStateLine = saveStorage ? ` +${offset}await context.StorageStateAsync(new() +${offset}{ +${offset} Path = ${quote(saveStorage)} +${offset}}); +` : ""; + if (this._mode !== "library") + storageStateLine += ` } +} +`; + return storageStateLine; + } + }; + CSharpFormatter = class { + constructor(offset = 0) { + this._lines = []; + this._baseIndent = " ".repeat(4); + this._baseOffset = " ".repeat(offset); + } + prepend(text2) { + this._lines = text2.trim().split("\n").map((line) => line.trim()).concat(this._lines); + } + add(text2) { + this._lines.push(...text2.trim().split("\n").map((line) => line.trim())); + } + newLine() { + this._lines.push(""); + } + format() { + let spaces = ""; + let previousLine = ""; + return this._lines.map((line) => { + if (line === "") + return line; + if (line.startsWith("}") || line.startsWith("]") || line.includes("});") || line === ");") + spaces = spaces.substring(this._baseIndent.length); + const extraSpaces = /^(for|while|if).*\(.*\)$/.test(previousLine) ? this._baseIndent : ""; + previousLine = line; + line = spaces + extraSpaces + line; + if (line.endsWith("{") || line.endsWith("[") || line.endsWith("(")) + spaces += this._baseIndent; + if (line.endsWith("));")) + spaces = spaces.substring(this._baseIndent.length); + return this._baseOffset + line; + }).join("\n"); + } + }; + } +}); + +// packages/playwright-core/src/server/codegen/javascript.ts +function formatOptions(value2, hasArguments) { + const keys = Object.keys(value2).filter((key) => value2[key] !== void 0); + if (!keys.length) + return ""; + return (hasArguments ? ", " : "") + formatObject(value2); +} +function formatContextOptions2(options, deviceName, isTest) { + const device = deviceName && deviceDescriptors[deviceName]; + options = { ...options, recordHar: void 0 }; + if (!device) + return formatObjectOrVoid(options); + let serializedObject = formatObjectOrVoid(sanitizeDeviceOptions(device, options)); + if (!serializedObject) + serializedObject = "{\n}"; + const lines = serializedObject.split("\n"); + lines.splice(1, 0, `...devices[${quote2(deviceName)}],`); + return lines.join("\n"); +} +function quote2(text2) { + return escapeWithQuotes(text2, "'"); +} +function wrapWithStep(description, body) { + return description ? `await test.step(\`${description}\`, async () => { +${body} +});` : body; +} +function quoteMultiline(text2, indent = " ") { + const escape = (text3) => text3.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${"); + const lines = text2.split("\n"); + if (lines.length === 1) + return "`" + escape(text2) + "`"; + return "`\n" + lines.map((line) => indent + escape(line).replace(/\${/g, "\\${")).join("\n") + ` +${indent}\``; +} +function isMultilineString(text2) { + return text2.match(/`[\S\s]*`/)?.[0].includes("\n"); +} +var JavaScriptLanguageGenerator, JavaScriptFormatter; +var init_javascript2 = __esm({ + "packages/playwright-core/src/server/codegen/javascript.ts"() { + "use strict"; + init_locatorGenerators(); + init_stringUtils(); + init_language(); + init_deviceDescriptors(); + JavaScriptLanguageGenerator = class { + constructor(isTest) { + this.groupName = "Node.js"; + this.highlighter = "javascript"; + this.id = isTest ? "playwright-test" : "javascript"; + this.name = isTest ? "Test Runner" : "Library"; + this._isTest = isTest; + } + generateAction(actionInContext) { + const action = actionInContext.action; + if (this._isTest && (action.name === "openPage" || action.name === "closePage")) + return ""; + const pageAlias = actionInContext.frame.pageAlias; + const formatter = new JavaScriptFormatter(2); + if (action.name === "openPage") { + formatter.add(`const ${pageAlias} = await context.newPage();`); + if (action.url && action.url !== "about:blank" && action.url !== "chrome://newtab/") + formatter.add(`await ${pageAlias}.goto(${quote2(action.url)});`); + return formatter.format(); + } + const locators = actionInContext.frame.framePath.map((selector) => `.${this._asLocator(selector)}.contentFrame()`); + const subject = `${pageAlias}${locators.join("")}`; + const signals = toSignalMap(action); + if (signals.dialog) { + formatter.add(` ${pageAlias}.once('dialog', dialog => { + console.log(\`Dialog message: \${dialog.message()}\`); + dialog.dismiss().catch(() => {}); + });`); + } + if (signals.popup) + formatter.add(`const ${signals.popup.popupAlias}Promise = ${pageAlias}.waitForEvent('popup');`); + if (signals.download) + formatter.add(`const download${signals.download.downloadAlias}Promise = ${pageAlias}.waitForEvent('download');`); + formatter.add(wrapWithStep(actionInContext.description, this._generateActionCall(subject, actionInContext))); + if (signals.popup) + formatter.add(`const ${signals.popup.popupAlias} = await ${signals.popup.popupAlias}Promise;`); + if (signals.download) + formatter.add(`const download${signals.download.downloadAlias} = await download${signals.download.downloadAlias}Promise;`); + return formatter.format(); + } + _generateActionCall(subject, actionInContext) { + const action = actionInContext.action; + switch (action.name) { + case "openPage": + throw Error("Not reached"); + case "closePage": + return `await ${subject}.close();`; + case "click": { + let method = "click"; + if (action.clickCount === 2) + method = "dblclick"; + const options = toClickOptionsForSourceCode(action); + const optionsString = formatOptions(options, false); + return `await ${subject}.${this._asLocator(action.selector)}.${method}(${optionsString});`; + } + case "hover": + return `await ${subject}.${this._asLocator(action.selector)}.hover(${formatOptions({ position: action.position }, false)});`; + case "check": + return `await ${subject}.${this._asLocator(action.selector)}.check();`; + case "uncheck": + return `await ${subject}.${this._asLocator(action.selector)}.uncheck();`; + case "fill": + return `await ${subject}.${this._asLocator(action.selector)}.fill(${quote2(action.text)});`; + case "setInputFiles": + return `await ${subject}.${this._asLocator(action.selector)}.setInputFiles(${formatObject(action.files.length === 1 ? action.files[0] : action.files)});`; + case "press": { + const modifiers = toKeyboardModifiers(action.modifiers); + const shortcut = [...modifiers, action.key].join("+"); + return `await ${subject}.${this._asLocator(action.selector)}.press(${quote2(shortcut)});`; + } + case "navigate": + return `await ${subject}.goto(${quote2(action.url)});`; + case "select": + return `await ${subject}.${this._asLocator(action.selector)}.selectOption(${formatObject(action.options.length === 1 ? action.options[0] : action.options)});`; + case "assertText": + return `${this._isTest ? "" : "// "}await expect(${subject}.${this._asLocator(action.selector)}).${action.substring ? "toContainText" : "toHaveText"}(${quote2(action.text)});`; + case "assertChecked": + return `${this._isTest ? "" : "// "}await expect(${subject}.${this._asLocator(action.selector)})${action.checked ? "" : ".not"}.toBeChecked();`; + case "assertVisible": + return `${this._isTest ? "" : "// "}await expect(${subject}.${this._asLocator(action.selector)}).toBeVisible();`; + case "assertValue": { + const assertion = action.value ? `toHaveValue(${quote2(action.value)})` : `toBeEmpty()`; + return `${this._isTest ? "" : "// "}await expect(${subject}.${this._asLocator(action.selector)}).${assertion};`; + } + case "assertSnapshot": { + const commentIfNeeded = this._isTest ? "" : "// "; + return `${commentIfNeeded}await expect(${subject}.${this._asLocator(action.selector)}).toMatchAriaSnapshot(${quoteMultiline(action.ariaSnapshot, `${commentIfNeeded} `)});`; + } + } + } + _asLocator(selector) { + return asLocator("javascript", selector); + } + generateHeader(options) { + if (this._isTest) + return this.generateTestHeader(options); + return this.generateStandaloneHeader(options); + } + generateFooter(saveStorage) { + if (this._isTest) + return this.generateTestFooter(saveStorage); + return this.generateStandaloneFooter(saveStorage); + } + generateTestHeader(options) { + const formatter = new JavaScriptFormatter(); + const useText = formatContextOptions2(options.contextOptions, options.deviceName, this._isTest); + formatter.add(` + import { test, expect${options.deviceName ? ", devices" : ""} } from '@playwright/test'; +${useText ? "\ntest.use(" + useText + ");\n" : ""} + test('test', async ({ page }) => {`); + if (options.contextOptions.recordHar) { + const url2 = options.contextOptions.recordHar.urlFilter; + formatter.add(` await page.routeFromHAR(${quote2(options.contextOptions.recordHar.path)}${url2 ? `, ${formatOptions({ url: url2 }, false)}` : ""});`); + } + return formatter.format(); + } + generateTestFooter(saveStorage) { + return `});`; + } + generateStandaloneHeader(options) { + const formatter = new JavaScriptFormatter(); + formatter.add(` + const { ${options.browserName}${options.deviceName ? ", devices" : ""} } = require('playwright'); + + (async () => { + const browser = await ${options.browserName}.launch(${formatObjectOrVoid(options.launchOptions)}); + const context = await browser.newContext(${formatContextOptions2(options.contextOptions, options.deviceName, false)});`); + if (options.contextOptions.recordHar) + formatter.add(` await context.routeFromHAR(${quote2(options.contextOptions.recordHar.path)});`); + return formatter.format(); + } + generateStandaloneFooter(saveStorage) { + const storageStateLine = saveStorage ? ` + await context.storageState({ path: ${quote2(saveStorage)} });` : ""; + return ` + // ---------------------${storageStateLine} + await context.close(); + await browser.close(); +})();`; + } + }; + JavaScriptFormatter = class { + constructor(offset = 0) { + this._lines = []; + this._baseIndent = " ".repeat(2); + this._baseOffset = " ".repeat(offset); + } + prepend(text2) { + const trim = isMultilineString(text2) ? (line) => line : (line) => line.trim(); + this._lines = text2.trim().split("\n").map(trim).concat(this._lines); + } + add(text2) { + const trim = isMultilineString(text2) ? (line) => line : (line) => line.trim(); + this._lines.push(...text2.trim().split("\n").map(trim)); + } + newLine() { + this._lines.push(""); + } + format() { + let spaces = ""; + let previousLine = ""; + return this._lines.map((line) => { + if (line === "") + return line; + if (line.startsWith("}") || line.startsWith("]")) + spaces = spaces.substring(this._baseIndent.length); + const extraSpaces = /^(for|while|if|try).*\(.*\)$/.test(previousLine) ? this._baseIndent : ""; + previousLine = line; + const callCarryOver = line.startsWith(".set"); + line = spaces + extraSpaces + (callCarryOver ? this._baseIndent : "") + line; + if (line.endsWith("{") || line.endsWith("[")) + spaces += this._baseIndent; + return this._baseOffset + line; + }).join("\n"); + } + }; + } +}); + +// packages/playwright-core/src/server/codegen/java.ts +function formatPath(files) { + if (Array.isArray(files)) { + if (files.length === 0) + return "new Path[0]"; + return `new Path[] {${files.map((s) => "Paths.get(" + quote3(s) + ")").join(", ")}}`; + } + return `Paths.get(${quote3(files)})`; +} +function formatSelectOption(options) { + if (Array.isArray(options)) { + if (options.length === 0) + return "new String[0]"; + return `new String[] {${options.map((s) => quote3(s)).join(", ")}}`; + } + return quote3(options); +} +function formatLaunchOptions(options) { + const lines = []; + if (!Object.keys(options).filter((key) => options[key] !== void 0).length) + return ""; + lines.push("new BrowserType.LaunchOptions()"); + if (options.channel) + lines.push(` .setChannel(${quote3(options.channel)})`); + if (typeof options.headless === "boolean") + lines.push(` .setHeadless(false)`); + return lines.join("\n"); +} +function formatContextOptions3(contextOptions, deviceName) { + const lines = []; + if (!Object.keys(contextOptions).length && !deviceName) + return ""; + const device = deviceName ? deviceDescriptors[deviceName] : {}; + const options = { ...device, ...contextOptions }; + lines.push("new Browser.NewContextOptions()"); + if (options.acceptDownloads) + lines.push(` .setAcceptDownloads(true)`); + if (options.bypassCSP) + lines.push(` .setBypassCSP(true)`); + if (options.colorScheme) + lines.push(` .setColorScheme(ColorScheme.${options.colorScheme.toUpperCase()})`); + if (options.deviceScaleFactor) + lines.push(` .setDeviceScaleFactor(${options.deviceScaleFactor})`); + if (options.geolocation) + lines.push(` .setGeolocation(${options.geolocation.latitude}, ${options.geolocation.longitude})`); + if (options.hasTouch) + lines.push(` .setHasTouch(${options.hasTouch})`); + if (options.isMobile) + lines.push(` .setIsMobile(${options.isMobile})`); + if (options.locale) + lines.push(` .setLocale(${quote3(options.locale)})`); + if (options.proxy) + lines.push(` .setProxy(new Proxy(${quote3(options.proxy.server)}))`); + if (options.serviceWorkers) + lines.push(` .setServiceWorkers(ServiceWorkerPolicy.${options.serviceWorkers.toUpperCase()})`); + if (options.storageState) + lines.push(` .setStorageStatePath(Paths.get(${quote3(options.storageState)}))`); + if (options.timezoneId) + lines.push(` .setTimezoneId(${quote3(options.timezoneId)})`); + if (options.userAgent) + lines.push(` .setUserAgent(${quote3(options.userAgent)})`); + if (options.viewport) + lines.push(` .setViewportSize(${options.viewport.width}, ${options.viewport.height})`); + return lines.join("\n"); +} +function formatClickOptions(options) { + const lines = []; + if (options.button) + lines.push(` .setButton(MouseButton.${options.button.toUpperCase()})`); + if (options.modifiers) + lines.push(` .setModifiers(Arrays.asList(${options.modifiers.map((m) => `KeyboardModifier.${m.toUpperCase()}`).join(", ")}))`); + if (options.clickCount) + lines.push(` .setClickCount(${options.clickCount})`); + if (options.position) + lines.push(` .setPosition(${options.position.x}, ${options.position.y})`); + if (!lines.length) + return ""; + lines.unshift(`new Locator.ClickOptions()`); + return lines.join("\n"); +} +function quote3(text2) { + return escapeWithQuotes(text2, '"'); +} +var JavaLanguageGenerator; +var init_java = __esm({ + "packages/playwright-core/src/server/codegen/java.ts"() { + "use strict"; + init_locatorGenerators(); + init_stringUtils(); + init_language(); + init_deviceDescriptors(); + init_javascript2(); + JavaLanguageGenerator = class { + constructor(mode) { + this.groupName = "Java"; + this.highlighter = "java"; + if (mode === "library") { + this.name = "Library"; + this.id = "java"; + } else if (mode === "junit") { + this.name = "JUnit"; + this.id = "java-junit"; + } else { + throw new Error(`Unknown Java language mode: ${mode}`); + } + this._mode = mode; + } + generateAction(actionInContext) { + const action = actionInContext.action; + const pageAlias = actionInContext.frame.pageAlias; + const offset = this._mode === "junit" ? 4 : 6; + const formatter = new JavaScriptFormatter(offset); + if (this._mode !== "library" && (action.name === "openPage" || action.name === "closePage")) + return ""; + if (action.name === "openPage") { + formatter.add(`Page ${pageAlias} = context.newPage();`); + if (action.url && action.url !== "about:blank" && action.url !== "chrome://newtab/") + formatter.add(`${pageAlias}.navigate(${quote3(action.url)});`); + return formatter.format(); + } + const locators = actionInContext.frame.framePath.map((selector) => `.${this._asLocator(selector, false)}.contentFrame()`); + const subject = `${pageAlias}${locators.join("")}`; + const signals = toSignalMap(action); + if (signals.dialog) { + formatter.add(` ${pageAlias}.onceDialog(dialog -> { + System.out.println(String.format("Dialog message: %s", dialog.message())); + dialog.dismiss(); + });`); + } + let code = this._generateActionCall(subject, actionInContext, !!actionInContext.frame.framePath.length); + if (signals.popup) { + code = `Page ${signals.popup.popupAlias} = ${pageAlias}.waitForPopup(() -> { + ${code} + });`; + } + if (signals.download) { + code = `Download download${signals.download.downloadAlias} = ${pageAlias}.waitForDownload(() -> { + ${code} + });`; + } + formatter.add(code); + return formatter.format(); + } + _generateActionCall(subject, actionInContext, inFrameLocator) { + const action = actionInContext.action; + switch (action.name) { + case "openPage": + throw Error("Not reached"); + case "closePage": + return `${subject}.close();`; + case "click": { + let method = "click"; + if (action.clickCount === 2) + method = "dblclick"; + const options = toClickOptionsForSourceCode(action); + const optionsText = formatClickOptions(options); + return `${subject}.${this._asLocator(action.selector, inFrameLocator)}.${method}(${optionsText});`; + } + case "hover": { + const optionsText = action.position ? `new Locator.HoverOptions().setPosition(${action.position.x}, ${action.position.y})` : ""; + return `${subject}.${this._asLocator(action.selector, inFrameLocator)}.hover(${optionsText});`; + } + case "check": + return `${subject}.${this._asLocator(action.selector, inFrameLocator)}.check();`; + case "uncheck": + return `${subject}.${this._asLocator(action.selector, inFrameLocator)}.uncheck();`; + case "fill": + return `${subject}.${this._asLocator(action.selector, inFrameLocator)}.fill(${quote3(action.text)});`; + case "setInputFiles": + return `${subject}.${this._asLocator(action.selector, inFrameLocator)}.setInputFiles(${formatPath(action.files.length === 1 ? action.files[0] : action.files)});`; + case "press": { + const modifiers = toKeyboardModifiers(action.modifiers); + const shortcut = [...modifiers, action.key].join("+"); + return `${subject}.${this._asLocator(action.selector, inFrameLocator)}.press(${quote3(shortcut)});`; + } + case "navigate": + return `${subject}.navigate(${quote3(action.url)});`; + case "select": + return `${subject}.${this._asLocator(action.selector, inFrameLocator)}.selectOption(${formatSelectOption(action.options.length === 1 ? action.options[0] : action.options)});`; + case "assertText": + return `assertThat(${subject}.${this._asLocator(action.selector, inFrameLocator)}).${action.substring ? "containsText" : "hasText"}(${quote3(action.text)});`; + case "assertChecked": + return `assertThat(${subject}.${this._asLocator(action.selector, inFrameLocator)})${action.checked ? "" : ".not()"}.isChecked();`; + case "assertVisible": + return `assertThat(${subject}.${this._asLocator(action.selector, inFrameLocator)}).isVisible();`; + case "assertValue": { + const assertion = action.value ? `hasValue(${quote3(action.value)})` : `isEmpty()`; + return `assertThat(${subject}.${this._asLocator(action.selector, inFrameLocator)}).${assertion};`; + } + case "assertSnapshot": + return `assertThat(${subject}.${this._asLocator(action.selector, inFrameLocator)}).matchesAriaSnapshot(${quote3(action.ariaSnapshot)});`; + } + } + _asLocator(selector, inFrameLocator) { + return asLocator("java", selector, inFrameLocator); + } + generateHeader(options) { + const formatter = new JavaScriptFormatter(); + if (this._mode === "junit") { + formatter.add(` + import com.microsoft.playwright.junit.UsePlaywright; + import com.microsoft.playwright.Page; + import com.microsoft.playwright.options.*; + + ${options.contextOptions.recordHar ? `import java.nio.file.Paths; +` : ""}import org.junit.jupiter.api.*; + import static com.microsoft.playwright.assertions.PlaywrightAssertions.*; + + @UsePlaywright + public class TestExample { + @Test + void test(Page page) {`); + if (options.contextOptions.recordHar) { + const url2 = options.contextOptions.recordHar.urlFilter; + const recordHarOptions = typeof url2 === "string" ? `, new Page.RouteFromHAROptions() + .setUrl(${quote3(url2)})` : ""; + formatter.add(` page.routeFromHAR(Paths.get(${quote3(options.contextOptions.recordHar.path)})${recordHarOptions});`); + } + return formatter.format(); + } + formatter.add(` + import com.microsoft.playwright.*; + import com.microsoft.playwright.options.*; + import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat; + ${options.contextOptions.recordHar ? `import java.nio.file.Paths; +` : ""}import java.util.*; + + public class Example { + public static void main(String[] args) { + try (Playwright playwright = Playwright.create()) { + Browser browser = playwright.${options.browserName}().launch(${formatLaunchOptions(options.launchOptions)}); + BrowserContext context = browser.newContext(${formatContextOptions3(options.contextOptions, options.deviceName)});`); + if (options.contextOptions.recordHar) { + const url2 = options.contextOptions.recordHar.urlFilter; + const recordHarOptions = typeof url2 === "string" ? `, new BrowserContext.RouteFromHAROptions() + .setUrl(${quote3(url2)})` : ""; + formatter.add(` context.routeFromHAR(Paths.get(${quote3(options.contextOptions.recordHar.path)})${recordHarOptions});`); + } + return formatter.format(); + } + generateFooter(saveStorage) { + const storageStateLine = saveStorage ? ` + context.storageState(new BrowserContext.StorageStateOptions().setPath(${quote3(saveStorage)})); +` : ""; + if (this._mode === "junit") { + return `${storageStateLine} } +}`; + } + return `${storageStateLine} } + } +}`; + } + }; + } +}); + +// packages/playwright-core/src/server/codegen/jsonl.ts +var JsonlLanguageGenerator; +var init_jsonl = __esm({ + "packages/playwright-core/src/server/codegen/jsonl.ts"() { + "use strict"; + init_locatorGenerators(); + JsonlLanguageGenerator = class { + constructor() { + this.id = "jsonl"; + this.groupName = ""; + this.name = "JSONL"; + this.highlighter = "javascript"; + } + generateAction(actionInContext) { + const locator2 = actionInContext.action.selector ? JSON.parse(asLocator("jsonl", actionInContext.action.selector)) : void 0; + const entry = { + ...actionInContext.action, + ...actionInContext.frame, + locator: locator2, + ariaSnapshot: void 0 + }; + return JSON.stringify(entry); + } + generateHeader(options) { + return JSON.stringify(options); + } + generateFooter(saveStorage) { + return ""; + } + }; + } +}); + +// packages/playwright-core/src/server/codegen/python.ts +function formatValue(value2) { + if (value2 === false) + return "False"; + if (value2 === true) + return "True"; + if (value2 === void 0) + return "None"; + if (Array.isArray(value2)) + return `[${value2.map(formatValue).join(", ")}]`; + if (typeof value2 === "string") + return quote4(value2); + if (typeof value2 === "object") + return JSON.stringify(value2); + return String(value2); +} +function formatOptions2(value2, hasArguments, asDict) { + const keys = Object.keys(value2).filter((key) => value2[key] !== void 0).sort(); + if (!keys.length) + return ""; + return (hasArguments ? ", " : "") + keys.map((key) => { + if (asDict) + return `"${toSnakeCase(key)}": ${formatValue(value2[key])}`; + return `${toSnakeCase(key)}=${formatValue(value2[key])}`; + }).join(", "); +} +function formatContextOptions4(options, deviceName, asDict) { + options = { ...options, recordHar: void 0 }; + const device = deviceName && deviceDescriptors[deviceName]; + if (!device) + return formatOptions2(options, false, asDict); + return `**playwright.devices[${quote4(deviceName)}]` + formatOptions2(sanitizeDeviceOptions(device, options), true, asDict); +} +function quote4(text2) { + return escapeWithQuotes(text2, '"'); +} +var PythonLanguageGenerator, PythonFormatter; +var init_python = __esm({ + "packages/playwright-core/src/server/codegen/python.ts"() { + "use strict"; + init_locatorGenerators(); + init_stringUtils(); + init_language(); + init_deviceDescriptors(); + PythonLanguageGenerator = class { + constructor(isAsync, isPyTest) { + this.groupName = "Python"; + this.highlighter = "python"; + this.id = isPyTest ? "python-pytest" : isAsync ? "python-async" : "python"; + this.name = isPyTest ? "Pytest" : isAsync ? "Library Async" : "Library"; + this._isAsync = isAsync; + this._isPyTest = isPyTest; + this._awaitPrefix = isAsync ? "await " : ""; + this._asyncPrefix = isAsync ? "async " : ""; + } + generateAction(actionInContext) { + const action = actionInContext.action; + if (this._isPyTest && (action.name === "openPage" || action.name === "closePage")) + return ""; + const pageAlias = actionInContext.frame.pageAlias; + const formatter = new PythonFormatter(4); + if (action.name === "openPage") { + formatter.add(`${pageAlias} = ${this._awaitPrefix}context.new_page()`); + if (action.url && action.url !== "about:blank" && action.url !== "chrome://newtab/") + formatter.add(`${this._awaitPrefix}${pageAlias}.goto(${quote4(action.url)})`); + return formatter.format(); + } + const locators = actionInContext.frame.framePath.map((selector) => `.${this._asLocator(selector)}.content_frame`); + const subject = `${pageAlias}${locators.join("")}`; + const signals = toSignalMap(action); + if (signals.dialog) + formatter.add(` ${pageAlias}.once("dialog", lambda dialog: dialog.dismiss())`); + let code = `${this._awaitPrefix}${this._generateActionCall(subject, actionInContext)}`; + if (signals.popup) { + code = `${this._asyncPrefix}with ${pageAlias}.expect_popup() as ${signals.popup.popupAlias}_info { + ${code} + } + ${signals.popup.popupAlias} = ${this._awaitPrefix}${signals.popup.popupAlias}_info.value`; + } + if (signals.download) { + code = `${this._asyncPrefix}with ${pageAlias}.expect_download() as download${signals.download.downloadAlias}_info { + ${code} + } + download${signals.download.downloadAlias} = ${this._awaitPrefix}download${signals.download.downloadAlias}_info.value`; + } + formatter.add(code); + return formatter.format(); + } + _generateActionCall(subject, actionInContext) { + const action = actionInContext.action; + switch (action.name) { + case "openPage": + throw Error("Not reached"); + case "closePage": + return `${subject}.close()`; + case "click": { + let method = "click"; + if (action.clickCount === 2) + method = "dblclick"; + const options = toClickOptionsForSourceCode(action); + const optionsString = formatOptions2(options, false); + return `${subject}.${this._asLocator(action.selector)}.${method}(${optionsString})`; + } + case "hover": + return `${subject}.${this._asLocator(action.selector)}.hover(${formatOptions2({ position: action.position }, false)})`; + case "check": + return `${subject}.${this._asLocator(action.selector)}.check()`; + case "uncheck": + return `${subject}.${this._asLocator(action.selector)}.uncheck()`; + case "fill": + return `${subject}.${this._asLocator(action.selector)}.fill(${quote4(action.text)})`; + case "setInputFiles": + return `${subject}.${this._asLocator(action.selector)}.set_input_files(${formatValue(action.files.length === 1 ? action.files[0] : action.files)})`; + case "press": { + const modifiers = toKeyboardModifiers(action.modifiers); + const shortcut = [...modifiers, action.key].join("+"); + return `${subject}.${this._asLocator(action.selector)}.press(${quote4(shortcut)})`; + } + case "navigate": + return `${subject}.goto(${quote4(action.url)})`; + case "select": + return `${subject}.${this._asLocator(action.selector)}.select_option(${formatValue(action.options.length === 1 ? action.options[0] : action.options)})`; + case "assertText": + return `expect(${subject}.${this._asLocator(action.selector)}).${action.substring ? "to_contain_text" : "to_have_text"}(${quote4(action.text)})`; + case "assertChecked": + return `expect(${subject}.${this._asLocator(action.selector)}).${action.checked ? "to_be_checked()" : "not_to_be_checked()"}`; + case "assertVisible": + return `expect(${subject}.${this._asLocator(action.selector)}).to_be_visible()`; + case "assertValue": { + const assertion = action.value ? `to_have_value(${quote4(action.value)})` : `to_be_empty()`; + return `expect(${subject}.${this._asLocator(action.selector)}).${assertion};`; + } + case "assertSnapshot": + return `expect(${subject}.${this._asLocator(action.selector)}).to_match_aria_snapshot(${quote4(action.ariaSnapshot)})`; + } + } + _asLocator(selector) { + return asLocator("python", selector); + } + generateHeader(options) { + const formatter = new PythonFormatter(); + const recordHar = options.contextOptions.recordHar; + if (this._isPyTest) { + const contextOptions = formatContextOptions4( + options.contextOptions, + options.deviceName, + true + /* asDict */ + ); + const fixture = contextOptions ? ` + +@pytest.fixture(scope="session") +def browser_context_args(browser_context_args, playwright) { + return {${contextOptions}} +} +` : ""; + formatter.add(`${options.deviceName || contextOptions ? "import pytest\n" : ""}import re +from playwright.sync_api import Page, expect +${fixture} + +def test_example(page: Page) -> None {`); + if (recordHar) + formatter.add(` page.route_from_har(${quote4(recordHar.path)}${typeof recordHar.urlFilter === "string" ? `, url=${quote4(recordHar.urlFilter)}` : ""})`); + } else if (this._isAsync) { + formatter.add(` +import asyncio +import re +from playwright.async_api import Playwright, async_playwright, expect + + +async def run(playwright: Playwright) -> None { + browser = await playwright.${options.browserName}.launch(${formatOptions2(options.launchOptions, false)}) + context = await browser.new_context(${formatContextOptions4(options.contextOptions, options.deviceName)})`); + if (recordHar) + formatter.add(` await context.route_from_har(${quote4(recordHar.path)}${typeof recordHar.urlFilter === "string" ? `, url=${quote4(recordHar.urlFilter)}` : ""})`); + } else { + formatter.add(` +import re +from playwright.sync_api import Playwright, sync_playwright, expect + + +def run(playwright: Playwright) -> None { + browser = playwright.${options.browserName}.launch(${formatOptions2(options.launchOptions, false)}) + context = browser.new_context(${formatContextOptions4(options.contextOptions, options.deviceName)})`); + if (recordHar) + formatter.add(` context.route_from_har(${quote4(recordHar.path)}${typeof recordHar.urlFilter === "string" ? `, url=${quote4(recordHar.urlFilter)}` : ""})`); + } + return formatter.format(); + } + generateFooter(saveStorage) { + if (this._isPyTest) { + return ""; + } else if (this._isAsync) { + const storageStateLine = saveStorage ? ` + await context.storage_state(path=${quote4(saveStorage)})` : ""; + return ` + # ---------------------${storageStateLine} + await context.close() + await browser.close() + + +async def main() -> None: + async with async_playwright() as playwright: + await run(playwright) + + +asyncio.run(main()) +`; + } else { + const storageStateLine = saveStorage ? ` + context.storage_state(path=${quote4(saveStorage)})` : ""; + return ` + # ---------------------${storageStateLine} + context.close() + browser.close() + + +with sync_playwright() as playwright: + run(playwright) +`; + } + } + }; + PythonFormatter = class { + constructor(offset = 0) { + this._lines = []; + this._baseIndent = " ".repeat(4); + this._baseOffset = " ".repeat(offset); + } + prepend(text2) { + this._lines = text2.trim().split("\n").map((line) => line.trim()).concat(this._lines); + } + add(text2) { + this._lines.push(...text2.trim().split("\n").map((line) => line.trim())); + } + newLine() { + this._lines.push(""); + } + format() { + let spaces = ""; + const lines = []; + this._lines.forEach((line) => { + if (line === "") + return lines.push(line); + if (line === "}") { + spaces = spaces.substring(this._baseIndent.length); + return; + } + line = spaces + line; + if (line.endsWith("{")) { + spaces += this._baseIndent; + line = line.substring(0, line.length - 1).trimEnd() + ":"; + } + return lines.push(this._baseOffset + line); + }); + return lines.join("\n"); + } + }; + } +}); + +// packages/playwright-core/src/server/codegen/languages.ts +function languageSet() { + return /* @__PURE__ */ new Set([ + new JavaScriptLanguageGenerator( + /* isPlaywrightTest */ + true + ), + new JavaScriptLanguageGenerator( + /* isPlaywrightTest */ + false + ), + new PythonLanguageGenerator( + /* isAsync */ + false, + /* isPytest */ + true + ), + new PythonLanguageGenerator( + /* isAsync */ + false, + /* isPytest */ + false + ), + new PythonLanguageGenerator( + /* isAsync */ + true, + /* isPytest */ + false + ), + new CSharpLanguageGenerator("mstest"), + new CSharpLanguageGenerator("nunit"), + new CSharpLanguageGenerator("xunit"), + new CSharpLanguageGenerator("library"), + new JavaLanguageGenerator("junit"), + new JavaLanguageGenerator("library"), + new JsonlLanguageGenerator() + ]); +} +var init_languages = __esm({ + "packages/playwright-core/src/server/codegen/languages.ts"() { + "use strict"; + init_csharp(); + init_java(); + init_javascript2(); + init_jsonl(); + init_python(); + } +}); + +// packages/playwright-core/src/server/recorder/recorderUtils.ts +function buildFullSelector(framePath, selector) { + return [...framePath, selector].join(" >> internal:control=enter-frame >> "); +} +function metadataToCallLog(metadata, status) { + const title = renderTitleForCall(metadata); + if (metadata.error) + status = "error"; + const params2 = { + url: metadata.params?.url, + selector: metadata.params?.selector + }; + let duration = metadata.endTime ? metadata.endTime - metadata.startTime : void 0; + if (typeof duration === "number" && metadata.pauseStartTime && metadata.pauseEndTime) { + duration -= metadata.pauseEndTime - metadata.pauseStartTime; + duration = Math.max(duration, 0); + } + const callLog = { + id: metadata.id, + messages: metadata.log, + title: title ?? "", + status, + error: metadata.error?.error?.message, + params: params2, + duration + }; + return callLog; +} +function mainFrameForAction(pageAliases, actionInContext) { + const pageAlias = actionInContext.frame.pageAlias; + const page = [...pageAliases.entries()].find(([, alias]) => pageAlias === alias)?.[0]; + if (!page) + throw new Error(`Internal error: page ${pageAlias} not found in [${[...pageAliases.values()]}]`); + return page.mainFrame(); +} +function isSameAction(a, b) { + return a.action.name === b.action.name && a.frame.pageAlias === b.frame.pageAlias && a.frame.framePath.join("|") === b.frame.framePath.join("|"); +} +function isSameSelector(action, lastAction) { + return "selector" in action.action && "selector" in lastAction.action && action.action.selector === lastAction.action.selector; +} +function isShortlyAfter(action, lastAction) { + return action.startTime - lastAction.startTime < 500; +} +function shouldMergeAction(action, lastAction) { + if (!lastAction) + return false; + switch (action.action.name) { + case "fill": + return isSameAction(action, lastAction) && isSameSelector(action, lastAction); + case "navigate": + return isSameAction(action, lastAction); + case "click": + return isSameAction(action, lastAction) && isSameSelector(action, lastAction) && isShortlyAfter(action, lastAction) && action.action.clickCount > lastAction.action.clickCount; + } + return false; +} +function collapseActions(actions) { + const result2 = []; + for (const action of actions) { + const lastAction = result2[result2.length - 1]; + const shouldMerge = shouldMergeAction(action, lastAction); + if (!shouldMerge) { + result2.push(action); + continue; + } + const startTime = result2[result2.length - 1].startTime; + result2[result2.length - 1] = action; + result2[result2.length - 1].startTime = startTime; + } + return result2; +} +async function generateFrameSelector(progress2, frame) { + const selectorPromises = []; + progress2.setAllowConcurrentOrNestedRaces(true); + while (frame) { + const parent = frame.parentFrame(); + if (!parent) + break; + selectorPromises.push(generateFrameSelectorInParent(progress2, parent, frame)); + frame = parent; + } + const result2 = await Promise.all(selectorPromises); + progress2.setAllowConcurrentOrNestedRaces(false); + return result2.reverse(); +} +async function generateFrameSelectorInParent(prgoress, parent, frame) { + const result2 = await raceAgainstDeadline(async () => { + try { + const frameElement = await frame.frameElement(prgoress); + if (!frameElement || !parent) + return; + const utility = await parent.utilityContext(); + const injected = await utility.injectedScript(); + const selector = await injected.evaluate((injected2, element2) => { + return injected2.generateSelectorSimple(element2); + }, frameElement); + return selector; + } catch (e) { + } + }, monotonicTime() + 2e3); + if (!result2.timedOut && result2.result) + return result2.result; + if (frame.name()) + return `iframe[name=${quoteCSSAttributeValue(frame.name())}]`; + return `iframe[src=${quoteCSSAttributeValue(frame.url())}]`; +} +var init_recorderUtils = __esm({ + "packages/playwright-core/src/server/recorder/recorderUtils.ts"() { + "use strict"; + init_protocolFormatter(); + init_timeoutRunner(); + init_time(); + init_stringUtils(); + } +}); + +// packages/playwright-core/src/server/recorder/recorderSignalProcessor.ts +var RecorderSignalProcessor; +var init_recorderSignalProcessor = __esm({ + "packages/playwright-core/src/server/recorder/recorderSignalProcessor.ts"() { + "use strict"; + init_time(); + init_debug(); + init_recorderUtils(); + init_progress(); + RecorderSignalProcessor = class { + constructor(actionSink) { + this._lastAction = null; + this._delegate = actionSink; + } + addAction(actionInContext) { + this._lastAction = actionInContext; + this._delegate.addAction(actionInContext); + } + signal(pageAlias, frame, signal) { + const timestamp = monotonicTime(); + if (signal.name === "navigation" && frame._page.mainFrame() === frame) { + const lastAction = this._lastAction; + const signalThreshold = isUnderTest() ? 500 : 5e3; + let generateGoto = false; + if (!lastAction) + generateGoto = true; + else if (lastAction.action.name !== "click" && lastAction.action.name !== "press" && lastAction.action.name !== "fill") + generateGoto = true; + else if (timestamp - lastAction.startTime > signalThreshold) + generateGoto = true; + if (generateGoto) { + this.addAction({ + frame: { + pageGuid: frame._page.guid, + pageAlias, + framePath: [] + }, + action: { + name: "navigate", + url: frame.url(), + signals: [] + }, + startTime: timestamp, + endTime: timestamp + }); + } + return; + } + generateFrameSelector(nullProgress, frame).then((framePath) => { + const signalInContext = { + frame: { + pageGuid: frame._page.guid, + pageAlias, + framePath + }, + signal, + timestamp + }; + this._delegate.addSignal(signalInContext); + }); + } + }; + } +}); + +// packages/playwright-core/src/generated/pollingRecorderSource.ts +var source6; +var init_pollingRecorderSource = __esm({ + "packages/playwright-core/src/generated/pollingRecorderSource.ts"() { + "use strict"; + source6 = '\nvar __commonJS = obj => {\n let required = false;\n let result;\n return function __require() {\n if (!required) {\n required = true;\n let fn;\n for (const name in obj) { fn = obj[name]; break; }\n const module = { exports: {} };\n fn(module.exports, module);\n result = module.exports;\n }\n return result;\n }\n};\nvar __export = (target, all) => {for (var name in all) target[name] = all[name];};\nvar __toESM = mod => ({ ...mod, \'default\': mod });\nvar __toCommonJS = mod => ({ ...mod, __esModule: true });\n\n\n// packages/injected/src/recorder/pollingRecorder.ts\nvar pollingRecorder_exports = {};\n__export(pollingRecorder_exports, {\n PollingRecorder: () => PollingRecorder,\n default: () => pollingRecorder_default\n});\nmodule.exports = __toCommonJS(pollingRecorder_exports);\n\n// packages/injected/src/recorder/clipPaths.ts\nvar svgJson = { "tagName": "svg", "children": [{ "tagName": "defs", "children": [{ "tagName": "clipPath", "attrs": { "width": "16", "height": "16", "viewBox": "0 0 16 16", "fill": "currentColor", "id": "icon-gripper" }, "children": [{ "tagName": "path", "attrs": { "d": "M5 3h2v2H5zm0 4h2v2H5zm0 4h2v2H5zm4-8h2v2H9zm0 4h2v2H9zm0 4h2v2H9z" } }] }, { "tagName": "clipPath", "attrs": { "width": "16", "height": "16", "viewBox": "0 0 16 16", "fill": "currentColor", "id": "icon-circle-large-filled" }, "children": [{ "tagName": "path", "attrs": { "d": "M8 1a6.8 6.8 0 0 1 1.86.253 6.899 6.899 0 0 1 3.083 1.805 6.903 6.903 0 0 1 1.804 3.083C14.916 6.738 15 7.357 15 8s-.084 1.262-.253 1.86a6.9 6.9 0 0 1-.704 1.674 7.157 7.157 0 0 1-2.516 2.509 6.966 6.966 0 0 1-1.668.71A6.984 6.984 0 0 1 8 15a6.984 6.984 0 0 1-1.86-.246 7.098 7.098 0 0 1-1.674-.711 7.3 7.3 0 0 1-1.415-1.094 7.295 7.295 0 0 1-1.094-1.415 7.098 7.098 0 0 1-.71-1.675A6.985 6.985 0 0 1 1 8c0-.643.082-1.262.246-1.86a6.968 6.968 0 0 1 .711-1.667 7.156 7.156 0 0 1 2.509-2.516 6.895 6.895 0 0 1 1.675-.704A6.808 6.808 0 0 1 8 1z" } }] }, { "tagName": "clipPath", "attrs": { "width": "16", "height": "16", "viewBox": "0 0 16 16", "fill": "currentColor", "id": "icon-stop-circle" }, "children": [{ "tagName": "path", "attrs": { "d": "M6 6h4v4H6z" } }, { "tagName": "path", "attrs": { "fill-rule": "evenodd", "clip-rule": "evenodd", "d": "M8.6 1c1.6.1 3.1.9 4.2 2 1.3 1.4 2 3.1 2 5.1 0 1.6-.6 3.1-1.6 4.4-1 1.2-2.4 2.1-4 2.4-1.6.3-3.2.1-4.6-.7-1.4-.8-2.5-2-3.1-3.5C.9 9.2.8 7.5 1.3 6c.5-1.6 1.4-2.9 2.8-3.8C5.4 1.3 7 .9 8.6 1zm.5 12.9c1.3-.3 2.5-1 3.4-2.1.8-1.1 1.3-2.4 1.2-3.8 0-1.6-.6-3.2-1.7-4.3-1-1-2.2-1.6-3.6-1.7-1.3-.1-2.7.2-3.8 1-1.1.8-1.9 1.9-2.3 3.3-.4 1.3-.4 2.7.2 4 .6 1.3 1.5 2.3 2.7 3 1.2.7 2.6.9 3.9.6z" } }] }, { "tagName": "clipPath", "attrs": { "width": "16", "height": "16", "viewBox": "0 0 16 16", "fill": "currentColor", "id": "icon-inspect" }, "children": [{ "tagName": "path", "attrs": { "fill-rule": "evenodd", "clip-rule": "evenodd", "d": "M1 3l1-1h12l1 1v6h-1V3H2v8h5v1H2l-1-1V3zm14.707 9.707L9 6v9.414l2.707-2.707h4zM10 13V8.414l3.293 3.293h-2L10 13z" } }] }, { "tagName": "clipPath", "attrs": { "width": "16", "height": "16", "viewBox": "0 0 16 16", "fill": "currentColor", "id": "icon-whole-word" }, "children": [{ "tagName": "path", "attrs": { "fill-rule": "evenodd", "clip-rule": "evenodd", "d": "M0 11H1V13H15V11H16V14H15H1H0V11Z" } }, { "tagName": "path", "attrs": { "d": "M6.84048 11H5.95963V10.1406H5.93814C5.555 10.7995 4.99104 11.1289 4.24625 11.1289C3.69839 11.1289 3.26871 10.9839 2.95718 10.6938C2.64924 10.4038 2.49527 10.0189 2.49527 9.53906C2.49527 8.51139 3.10041 7.91341 4.3107 7.74512L5.95963 7.51416C5.95963 6.57959 5.58186 6.1123 4.82632 6.1123C4.16389 6.1123 3.56591 6.33789 3.03238 6.78906V5.88672C3.57307 5.54297 4.19612 5.37109 4.90152 5.37109C6.19416 5.37109 6.84048 6.05501 6.84048 7.42285V11ZM5.95963 8.21777L4.63297 8.40039C4.22476 8.45768 3.91682 8.55973 3.70914 8.70654C3.50145 8.84977 3.39761 9.10579 3.39761 9.47461C3.39761 9.74316 3.4925 9.96338 3.68228 10.1353C3.87564 10.3035 4.13166 10.3877 4.45035 10.3877C4.8872 10.3877 5.24706 10.2355 5.52994 9.93115C5.8164 9.62321 5.95963 9.2347 5.95963 8.76562V8.21777Z" } }, { "tagName": "path", "attrs": { "d": "M9.3475 10.2051H9.32601V11H8.44515V2.85742H9.32601V6.4668H9.3475C9.78076 5.73633 10.4146 5.37109 11.2489 5.37109C11.9543 5.37109 12.5057 5.61816 12.9032 6.1123C13.3042 6.60286 13.5047 7.26172 13.5047 8.08887C13.5047 9.00911 13.2809 9.74674 12.8333 10.3018C12.3857 10.8532 11.7734 11.1289 10.9964 11.1289C10.2695 11.1289 9.71989 10.821 9.3475 10.2051ZM9.32601 7.98682V8.75488C9.32601 9.20964 9.47282 9.59635 9.76644 9.91504C10.0636 10.2301 10.4396 10.3877 10.8944 10.3877C11.4279 10.3877 11.8451 10.1836 12.1458 9.77539C12.4502 9.36719 12.6024 8.79964 12.6024 8.07275C12.6024 7.46045 12.4609 6.98063 12.1781 6.6333C11.8952 6.28597 11.512 6.1123 11.0286 6.1123C10.5166 6.1123 10.1048 6.29134 9.7933 6.64941C9.48177 7.00391 9.32601 7.44971 9.32601 7.98682Z" } }] }, { "tagName": "clipPath", "attrs": { "width": "16", "height": "16", "viewBox": "0 0 16 16", "fill": "currentColor", "id": "icon-eye" }, "children": [{ "tagName": "path", "attrs": { "d": "M7.99993 6.00316C9.47266 6.00316 10.6666 7.19708 10.6666 8.66981C10.6666 10.1426 9.47266 11.3365 7.99993 11.3365C6.52715 11.3365 5.33324 10.1426 5.33324 8.66981C5.33324 7.19708 6.52715 6.00316 7.99993 6.00316ZM7.99993 7.00315C7.07946 7.00315 6.33324 7.74935 6.33324 8.66981C6.33324 9.59028 7.07946 10.3365 7.99993 10.3365C8.9204 10.3365 9.6666 9.59028 9.6666 8.66981C9.6666 7.74935 8.9204 7.00315 7.99993 7.00315ZM7.99993 3.66675C11.0756 3.66675 13.7307 5.76675 14.4673 8.70968C14.5344 8.97755 14.3716 9.24908 14.1037 9.31615C13.8358 9.38315 13.5643 9.22041 13.4973 8.95248C12.8713 6.45205 10.6141 4.66675 7.99993 4.66675C5.38454 4.66675 3.12664 6.45359 2.50182 8.95555C2.43491 9.22341 2.16348 9.38635 1.89557 9.31948C1.62766 9.25255 1.46471 8.98115 1.53162 8.71321C2.26701 5.76856 4.9229 3.66675 7.99993 3.66675Z" } }] }, { "tagName": "clipPath", "attrs": { "width": "16", "height": "16", "viewBox": "0 0 16 16", "fill": "currentColor", "id": "icon-symbol-constant" }, "children": [{ "tagName": "path", "attrs": { "fill-rule": "evenodd", "clip-rule": "evenodd", "d": "M4 6h8v1H4V6zm8 3H4v1h8V9z" } }, { "tagName": "path", "attrs": { "fill-rule": "evenodd", "clip-rule": "evenodd", "d": "M1 4l1-1h12l1 1v8l-1 1H2l-1-1V4zm1 0v8h12V4H2z" } }] }, { "tagName": "clipPath", "attrs": { "width": "16", "height": "16", "viewBox": "0 0 16 16", "fill": "currentColor", "id": "icon-check" }, "children": [{ "tagName": "path", "attrs": { "fill-rule": "evenodd", "clip-rule": "evenodd", "d": "M14.431 3.323l-8.47 10-.79-.036-3.35-4.77.818-.574 2.978 4.24 8.051-9.506.764.646z" } }] }, { "tagName": "clipPath", "attrs": { "width": "16", "height": "16", "viewBox": "0 0 16 16", "fill": "currentColor", "id": "icon-close" }, "children": [{ "tagName": "path", "attrs": { "fill-rule": "evenodd", "clip-rule": "evenodd", "d": "M8 8.707l3.646 3.647.708-.707L8.707 8l3.647-3.646-.707-.708L8 7.293 4.354 3.646l-.707.708L7.293 8l-3.646 3.646.707.708L8 8.707z" } }] }, { "tagName": "clipPath", "attrs": { "width": "16", "height": "16", "viewBox": "0 0 16 16", "fill": "currentColor", "id": "icon-pass" }, "children": [{ "tagName": "path", "attrs": { "d": "M6.27 10.87h.71l4.56-4.56-.71-.71-4.2 4.21-1.92-1.92L4 8.6l2.27 2.27z" } }, { "tagName": "path", "attrs": { "fill-rule": "evenodd", "clip-rule": "evenodd", "d": "M8.6 1c1.6.1 3.1.9 4.2 2 1.3 1.4 2 3.1 2 5.1 0 1.6-.6 3.1-1.6 4.4-1 1.2-2.4 2.1-4 2.4-1.6.3-3.2.1-4.6-.7-1.4-.8-2.5-2-3.1-3.5C.9 9.2.8 7.5 1.3 6c.5-1.6 1.4-2.9 2.8-3.8C5.4 1.3 7 .9 8.6 1zm.5 12.9c1.3-.3 2.5-1 3.4-2.1.8-1.1 1.3-2.4 1.2-3.8 0-1.6-.6-3.2-1.7-4.3-1-1-2.2-1.6-3.6-1.7-1.3-.1-2.7.2-3.8 1-1.1.8-1.9 1.9-2.3 3.3-.4 1.3-.4 2.7.2 4 .6 1.3 1.5 2.3 2.7 3 1.2.7 2.6.9 3.9.6z" } }] }, { "tagName": "clipPath", "attrs": { "width": "16", "height": "16", "viewBox": "0 0 16 16", "fill": "currentColor", "id": "icon-gist" }, "children": [{ "tagName": "path", "attrs": { "fill-rule": "evenodd", "clip-rule": "evenodd", "d": "M10.57 1.14l3.28 3.3.15.36v9.7l-.5.5h-11l-.5-.5v-13l.5-.5h7.72l.35.14zM10 5h3l-3-3v3zM3 2v12h10V6H9.5L9 5.5V2H3zm2.062 7.533l1.817-1.828L6.17 7 4 9.179v.707l2.171 2.174.707-.707-1.816-1.82zM8.8 7.714l.7-.709 2.189 2.175v.709L9.5 12.062l-.705-.709 1.831-1.82L8.8 7.714z" } }] }] }] };\nvar clipPaths_default = svgJson;\n\n// packages/injected/src/recorder/recorder.ts\nvar HighlightColors = {\n multiple: "#f6b26b7f",\n single: "#6fa8dc7f",\n assert: "#8acae480",\n action: "#dc6f6f7f"\n};\nvar NoneTool = class {\n};\nvar InspectTool = class {\n constructor(recorder, assertVisibility) {\n this._hoveredModel = null;\n this._hoveredElement = null;\n this._recorder = recorder;\n this._assertVisibility = assertVisibility;\n }\n cursor() {\n return "pointer";\n }\n uninstall() {\n this._hoveredModel = null;\n this._hoveredElement = null;\n }\n onClick(event) {\n var _a;\n consumeEvent(event);\n if (event.button !== 0)\n return;\n if ((_a = this._hoveredModel) == null ? void 0 : _a.selector)\n this._commit(this._hoveredModel.selector, this._hoveredModel);\n }\n onPointerDown(event) {\n consumeEvent(event);\n }\n onPointerUp(event) {\n consumeEvent(event);\n }\n onMouseDown(event) {\n consumeEvent(event);\n }\n onMouseUp(event) {\n consumeEvent(event);\n }\n onMouseMove(event) {\n var _a;\n consumeEvent(event);\n let target = this._recorder.deepEventTarget(event);\n if (!target.isConnected)\n target = null;\n if (this._hoveredElement === target)\n return;\n this._hoveredElement = target;\n let model = null;\n if (this._hoveredElement) {\n const generated = this._recorder.injectedScript.generateSelector(this._hoveredElement, { testIdAttributeName: this._recorder.state.testIdAttributeName, multiple: false });\n model = {\n selector: generated.selector,\n elements: generated.elements,\n tooltipText: this._recorder.injectedScript.utils.asLocator(this._recorder.state.language, generated.selector),\n color: this._assertVisibility ? HighlightColors.assert : HighlightColors.single\n };\n }\n if (((_a = this._hoveredModel) == null ? void 0 : _a.selector) === (model == null ? void 0 : model.selector))\n return;\n this._hoveredModel = model;\n this._recorder.updateHighlight(model, true);\n }\n onMouseEnter(event) {\n consumeEvent(event);\n }\n onMouseLeave(event) {\n consumeEvent(event);\n const window = this._recorder.injectedScript.window;\n if (window.top !== window && this._recorder.deepEventTarget(event).nodeType === Node.DOCUMENT_NODE)\n this._reset(true);\n }\n onKeyDown(event) {\n consumeEvent(event);\n if (event.key === "Escape") {\n if (this._assertVisibility)\n this._recorder.setMode("recording");\n }\n }\n onKeyUp(event) {\n consumeEvent(event);\n }\n onScroll(event) {\n this._reset(false);\n }\n _commit(selector, model) {\n var _a;\n if (this._assertVisibility) {\n void this._recorder.recordAction({\n name: "assertVisible",\n selector,\n signals: []\n });\n this._recorder.setMode("recording");\n (_a = this._recorder.overlay) == null ? void 0 : _a.flashToolSucceeded("assertingVisibility");\n } else {\n this._recorder.elementPicked(selector, model);\n }\n }\n _reset(userGesture) {\n this._hoveredElement = null;\n this._hoveredModel = null;\n this._recorder.updateHighlight(null, userGesture);\n }\n};\nvar RecordActionTool = class {\n constructor(recorder) {\n this._hoveredModel = null;\n this._hoveredElement = null;\n this._activeModel = null;\n this._expectProgrammaticKeyUp = false;\n this._observer = null;\n this._recorder = recorder;\n this._performingActions = /* @__PURE__ */ new Set();\n this._dialog = new Dialog(recorder);\n }\n cursor() {\n return "pointer";\n }\n _installObserverIfNeeded() {\n var _a;\n if (this._observer)\n return;\n if (!((_a = this._recorder.injectedScript.document) == null ? void 0 : _a.body))\n return;\n this._observer = new MutationObserver((mutations) => {\n if (!this._hoveredElement)\n return;\n for (const mutation of mutations) {\n for (const node of mutation.removedNodes) {\n if (node === this._hoveredElement || node.contains(this._hoveredElement))\n this._resetHoveredModel();\n }\n }\n });\n this._observer.observe(this._recorder.injectedScript.document.body, { childList: true, subtree: true });\n }\n uninstall() {\n var _a;\n (_a = this._observer) == null ? void 0 : _a.disconnect();\n this._observer = null;\n this._hoveredModel = null;\n this._hoveredElement = null;\n this._activeModel = null;\n this._expectProgrammaticKeyUp = false;\n this._dialog.close();\n }\n onClick(event) {\n if (this._dialog.isShowing()) {\n if (event.button === 2 && event.type === "auxclick") {\n consumeEvent(event);\n }\n return;\n }\n if (isRangeInput(this._hoveredElement))\n return;\n if (this._shouldIgnoreMouseEvent(event))\n return;\n if (this._actionInProgress(event))\n return;\n if (this._consumedDueToNoModel(event, this._hoveredModel))\n return;\n if (event.button === 2 && event.type === "auxclick") {\n this._showActionListDialog(this._hoveredModel, event);\n return;\n }\n const checkbox = asCheckbox(this._recorder.deepEventTarget(event));\n if (checkbox && event.detail === 1) {\n this._performAction({\n name: checkbox.checked ? "check" : "uncheck",\n selector: this._hoveredModel.selector,\n signals: []\n });\n return;\n }\n this._cancelPendingClickAction();\n if (event.detail === 1) {\n this._pendingClickAction = {\n action: {\n name: "click",\n selector: this._hoveredModel.selector,\n position: positionForEvent(event),\n signals: [],\n button: buttonForEvent(event),\n modifiers: modifiersForEvent(event),\n clickCount: event.detail\n },\n timeout: this._recorder.injectedScript.utils.builtins.setTimeout(() => this._commitPendingClickAction(), 200)\n };\n }\n }\n onDblClick(event) {\n if (this._dialog.isShowing())\n return;\n if (isRangeInput(this._hoveredElement))\n return;\n if (this._shouldIgnoreMouseEvent(event))\n return;\n if (this._actionInProgress(event))\n return;\n if (this._consumedDueToNoModel(event, this._hoveredModel))\n return;\n this._cancelPendingClickAction();\n this._performAction({\n name: "click",\n selector: this._hoveredModel.selector,\n position: positionForEvent(event),\n signals: [],\n button: buttonForEvent(event),\n modifiers: modifiersForEvent(event),\n clickCount: event.detail\n });\n }\n _commitPendingClickAction() {\n if (this._pendingClickAction)\n this._performAction(this._pendingClickAction.action);\n this._cancelPendingClickAction();\n }\n _cancelPendingClickAction() {\n if (this._pendingClickAction)\n this._recorder.injectedScript.utils.builtins.clearTimeout(this._pendingClickAction.timeout);\n this._pendingClickAction = void 0;\n }\n onContextMenu(event) {\n if (this._dialog.isShowing()) {\n consumeEvent(event);\n return;\n }\n if (this._shouldIgnoreMouseEvent(event))\n return;\n if (this._actionInProgress(event))\n return;\n if (this._consumedDueToNoModel(event, this._hoveredModel))\n return;\n this._showActionListDialog(this._hoveredModel, event);\n }\n onPointerDown(event) {\n if (this._dialog.isShowing())\n return;\n if (this._shouldIgnoreMouseEvent(event))\n return;\n this._consumeWhenAboutToPerform(event);\n }\n onPointerUp(event) {\n if (this._dialog.isShowing())\n return;\n if (this._shouldIgnoreMouseEvent(event))\n return;\n this._consumeWhenAboutToPerform(event);\n }\n onMouseDown(event) {\n if (this._dialog.isShowing())\n return;\n if (this._shouldIgnoreMouseEvent(event))\n return;\n this._consumeWhenAboutToPerform(event);\n this._activeModel = this._hoveredModel;\n }\n onMouseUp(event) {\n if (this._dialog.isShowing())\n return;\n if (this._shouldIgnoreMouseEvent(event))\n return;\n this._consumeWhenAboutToPerform(event);\n }\n onMouseMove(event) {\n if (this._dialog.isShowing())\n return;\n const target = this._recorder.deepEventTarget(event);\n if (this._hoveredElement === target)\n return;\n this._hoveredElement = target;\n this._updateModelForHoveredElement();\n }\n onMouseLeave(event) {\n if (this._dialog.isShowing())\n return;\n const window = this._recorder.injectedScript.window;\n if (window.top !== window && this._recorder.deepEventTarget(event).nodeType === Node.DOCUMENT_NODE) {\n this._hoveredElement = null;\n this._updateModelForHoveredElement();\n }\n }\n onFocus(event) {\n if (this._dialog.isShowing())\n return;\n this._onFocus(true);\n }\n onInput(event) {\n if (this._dialog.isShowing())\n return;\n const target = this._recorder.deepEventTarget(event);\n if (target.nodeName === "INPUT" && target.type.toLowerCase() === "file") {\n const selector = target === this._hoveredElement ? this._hoveredModel.selector : this._recorder.injectedScript.generateSelector(target, { testIdAttributeName: this._recorder.state.testIdAttributeName }).selector;\n this._recordAction({\n name: "setInputFiles",\n selector,\n signals: [],\n files: [...target.files || []].map((file) => file.name)\n });\n return;\n }\n if (isRangeInput(target)) {\n this._recordAction({\n name: "fill",\n // must use hoveredModel instead of activeModel for it to work in webkit\n selector: this._hoveredModel.selector,\n signals: [],\n text: target.value\n });\n return;\n }\n if (["INPUT", "TEXTAREA"].includes(target.nodeName) || target.isContentEditable) {\n if (target.nodeName === "INPUT" && ["checkbox", "radio"].includes(target.type.toLowerCase())) {\n return;\n }\n if (this._consumedDueWrongTarget(event))\n return;\n this._recordAction({\n name: "fill",\n selector: this._activeModel.selector,\n signals: [],\n text: target.isContentEditable ? target.innerText : target.value\n });\n }\n if (target.nodeName === "SELECT") {\n const selectElement = target;\n this._recordAction({\n name: "select",\n selector: this._activeModel.selector,\n options: [...selectElement.selectedOptions].map((option) => option.value),\n signals: []\n });\n }\n }\n onKeyDown(event) {\n if (this._dialog.isShowing())\n return;\n if (!this._shouldGenerateKeyPressFor(event))\n return;\n if (this._actionInProgress(event)) {\n this._expectProgrammaticKeyUp = true;\n return;\n }\n if (this._consumedDueWrongTarget(event))\n return;\n if (event.key === " ") {\n const checkbox = asCheckbox(this._recorder.deepEventTarget(event));\n if (checkbox && event.detail === 0) {\n this._performAction({\n name: checkbox.checked ? "uncheck" : "check",\n selector: this._activeModel.selector,\n signals: []\n });\n return;\n }\n }\n this._performAction({\n name: "press",\n selector: this._activeModel.selector,\n signals: [],\n key: event.key,\n modifiers: modifiersForEvent(event)\n });\n }\n onKeyUp(event) {\n if (this._dialog.isShowing())\n return;\n if (!this._shouldGenerateKeyPressFor(event))\n return;\n if (!this._expectProgrammaticKeyUp) {\n consumeEvent(event);\n return;\n }\n this._expectProgrammaticKeyUp = false;\n }\n onScroll(event) {\n if (this._dialog.isShowing())\n return;\n this._resetHoveredModel();\n }\n _showActionListDialog(model, event) {\n consumeEvent(event);\n const actionPosition = positionForEvent(event);\n const actions = [\n {\n title: "Click",\n cb: () => this._performAction({\n name: "click",\n selector: model.selector,\n position: actionPosition,\n signals: [],\n button: "left",\n modifiers: 0,\n clickCount: 0\n })\n },\n {\n title: "Right click",\n cb: () => this._performAction({\n name: "click",\n selector: model.selector,\n position: actionPosition,\n signals: [],\n button: "right",\n modifiers: 0,\n clickCount: 0\n })\n },\n {\n title: "Double click",\n cb: () => this._performAction({\n name: "click",\n selector: model.selector,\n position: actionPosition,\n signals: [],\n button: "left",\n modifiers: 0,\n clickCount: 2\n })\n },\n {\n title: "Hover",\n cb: () => this._performAction({\n name: "hover",\n selector: model.selector,\n position: actionPosition,\n signals: []\n })\n },\n {\n title: "Pick locator",\n cb: () => this._recorder.elementPicked(model.selector, model)\n }\n ];\n const listElement = this._recorder.document.createElement("x-pw-action-list");\n listElement.setAttribute("role", "list");\n listElement.setAttribute("aria-label", "Choose action");\n for (const action of actions) {\n const actionElement = this._recorder.document.createElement("x-pw-action-item");\n actionElement.setAttribute("role", "listitem");\n actionElement.textContent = action.title;\n actionElement.setAttribute("aria-label", action.title);\n actionElement.addEventListener("click", () => {\n this._dialog.close();\n action.cb();\n });\n listElement.appendChild(actionElement);\n }\n const dialogElement = this._dialog.show({\n label: "Choose action",\n body: listElement,\n autosize: true\n });\n const anchorBox = this._recorder.highlight.firstTooltipBox() || model.elements[0].getBoundingClientRect();\n const dialogPosition = this._recorder.highlight.tooltipPosition(anchorBox, dialogElement);\n this._dialog.moveTo(dialogPosition.anchorTop, dialogPosition.anchorLeft);\n }\n _resetHoveredModel() {\n this._hoveredModel = null;\n this._hoveredElement = null;\n this._updateHighlight(false);\n }\n _onFocus(userGesture) {\n const activeElement = deepActiveElement(this._recorder.document);\n if (userGesture && activeElement === this._recorder.document.body)\n return;\n const result = activeElement ? this._recorder.injectedScript.generateSelector(activeElement, { testIdAttributeName: this._recorder.state.testIdAttributeName }) : null;\n this._activeModel = result && result.selector ? { ...result, color: HighlightColors.action } : null;\n if (userGesture) {\n this._hoveredElement = activeElement;\n this._updateModelForHoveredElement();\n }\n }\n _shouldIgnoreMouseEvent(event) {\n return shouldIgnoreMouseEvent(this._recorder.deepEventTarget(event));\n }\n _actionInProgress(event) {\n const isKeyEvent = event instanceof KeyboardEvent;\n const isMouseOrPointerEvent = event instanceof MouseEvent || event instanceof PointerEvent;\n for (const action of this._performingActions) {\n if (isKeyEvent && action.name === "press" && event.key === action.key)\n return true;\n if (isMouseOrPointerEvent && (action.name === "click" || action.name === "hover" || action.name === "check" || action.name === "uncheck"))\n return true;\n }\n consumeEvent(event);\n return false;\n }\n _consumedDueToNoModel(event, model) {\n if (model)\n return false;\n consumeEvent(event);\n return true;\n }\n _consumedDueWrongTarget(event) {\n if (this._activeModel && this._activeModel.elements[0] === this._recorder.deepEventTarget(event))\n return false;\n consumeEvent(event);\n return true;\n }\n _consumeWhenAboutToPerform(event) {\n if (!this._performingActions.size)\n consumeEvent(event);\n }\n _reportPerformedActionForTests() {\n if (!this._recorder.injectedScript.isUnderTest)\n return;\n console.error("Action performed for test: " + JSON.stringify({\n // eslint-disable-line no-console\n hovered: this._hoveredModel ? this._hoveredModel.selector : null,\n active: this._activeModel ? this._activeModel.selector : null\n }));\n }\n _recordAction(action) {\n void this._recorder.recordAction(action).then(() => this._reportPerformedActionForTests());\n }\n _performAction(action) {\n this._recorder.updateHighlight(null, false);\n this._performingActions.add(action);\n void this._recorder.performAction(action).finally(() => {\n this._performingActions.delete(action);\n this._onFocus(false);\n }).then(() => this._reportPerformedActionForTests());\n }\n _shouldGenerateKeyPressFor(event) {\n if (typeof event.key !== "string")\n return false;\n if (event.key === "Enter" && (this._recorder.deepEventTarget(event).nodeName === "TEXTAREA" || this._recorder.deepEventTarget(event).isContentEditable))\n return false;\n if (["Backspace", "Delete", "AltGraph"].includes(event.key))\n return false;\n if (event.key === "@" && event.code === "KeyL")\n return false;\n if (navigator.platform.includes("Mac")) {\n if (event.key === "v" && event.metaKey)\n return false;\n } else {\n if (event.key === "v" && event.ctrlKey)\n return false;\n if (event.key === "Insert" && event.shiftKey)\n return false;\n }\n if (["Shift", "Control", "Meta", "Alt", "Process"].includes(event.key))\n return false;\n const hasModifier = event.ctrlKey || event.altKey || event.metaKey;\n if (event.key.length === 1 && !hasModifier)\n return !!asCheckbox(this._recorder.deepEventTarget(event));\n return true;\n }\n _updateModelForHoveredElement() {\n this._installObserverIfNeeded();\n if (this._performingActions.size)\n return;\n if (!this._hoveredElement || !this._hoveredElement.isConnected) {\n this._hoveredModel = null;\n this._hoveredElement = null;\n this._updateHighlight(true);\n return;\n }\n const { selector, elements } = this._recorder.injectedScript.generateSelector(this._hoveredElement, { testIdAttributeName: this._recorder.state.testIdAttributeName });\n if (this._hoveredModel && this._hoveredModel.selector === selector)\n return;\n this._hoveredModel = selector ? { selector, elements, color: HighlightColors.action } : null;\n this._updateHighlight(true);\n }\n _updateHighlight(userGesture) {\n this._recorder.updateHighlight(this._hoveredModel, userGesture);\n }\n};\nvar JsonRecordActionTool = class {\n constructor(recorder) {\n this._recorder = recorder;\n }\n install() {\n this._recorder.highlight.uninstall();\n }\n uninstall() {\n this._recorder.highlight.install();\n }\n onClick(event) {\n const element = this._recorder.deepEventTarget(event);\n if (isRangeInput(element))\n return;\n if (event.button === 2 && event.type === "auxclick")\n return;\n if (this._shouldIgnoreMouseEvent(event))\n return;\n const checkbox = asCheckbox(element);\n const { ariaSnapshot, selector, ref } = this._ariaSnapshot(element);\n if (checkbox && event.detail === 1) {\n void this._recorder.recordAction({\n name: checkbox.checked ? "check" : "uncheck",\n selector,\n ref,\n signals: [],\n ariaSnapshot\n });\n return;\n }\n void this._recorder.recordAction({\n name: "click",\n selector,\n ref,\n ariaSnapshot,\n position: positionForEvent(event),\n signals: [],\n button: buttonForEvent(event),\n modifiers: modifiersForEvent(event),\n clickCount: event.detail\n });\n }\n onContextMenu(event) {\n const element = this._recorder.deepEventTarget(event);\n const { ariaSnapshot, selector, ref } = this._ariaSnapshot(element);\n void this._recorder.recordAction({\n name: "click",\n selector,\n ref,\n ariaSnapshot,\n position: positionForEvent(event),\n signals: [],\n button: "right",\n modifiers: modifiersForEvent(event),\n clickCount: 1\n });\n }\n onInput(event) {\n const element = this._recorder.deepEventTarget(event);\n const { ariaSnapshot, selector, ref } = this._ariaSnapshot(element);\n if (isRangeInput(element)) {\n void this._recorder.recordAction({\n name: "fill",\n selector,\n ref,\n ariaSnapshot,\n signals: [],\n text: element.value\n });\n return;\n }\n if (["INPUT", "TEXTAREA"].includes(element.nodeName) || element.isContentEditable) {\n if (element.nodeName === "INPUT" && ["checkbox", "radio"].includes(element.type.toLowerCase())) {\n return;\n }\n void this._recorder.recordAction({\n name: "fill",\n ref,\n selector,\n ariaSnapshot,\n signals: [],\n text: element.isContentEditable ? element.innerText : element.value\n });\n return;\n }\n if (element.nodeName === "SELECT") {\n const selectElement = element;\n void this._recorder.recordAction({\n name: "select",\n selector,\n ref,\n ariaSnapshot,\n options: [...selectElement.selectedOptions].map((option) => option.value),\n signals: []\n });\n return;\n }\n }\n onKeyDown(event) {\n if (!this._shouldGenerateKeyPressFor(event))\n return;\n const element = this._recorder.deepEventTarget(event);\n const { ariaSnapshot, selector, ref } = this._ariaSnapshot(element);\n if (event.key === " ") {\n const checkbox = asCheckbox(element);\n if (checkbox && event.detail === 0) {\n void this._recorder.recordAction({\n name: checkbox.checked ? "uncheck" : "check",\n selector,\n ref,\n ariaSnapshot,\n signals: []\n });\n return;\n }\n }\n void this._recorder.recordAction({\n name: "press",\n selector,\n ref,\n ariaSnapshot,\n signals: [],\n key: event.key,\n modifiers: modifiersForEvent(event)\n });\n }\n _shouldIgnoreMouseEvent(event) {\n return shouldIgnoreMouseEvent(this._recorder.deepEventTarget(event));\n }\n _shouldGenerateKeyPressFor(event) {\n if (typeof event.key !== "string")\n return false;\n if (event.key === "Enter" && (this._recorder.deepEventTarget(event).nodeName === "TEXTAREA" || this._recorder.deepEventTarget(event).isContentEditable))\n return false;\n if (["Backspace", "Delete", "AltGraph"].includes(event.key))\n return false;\n if (event.key === "@" && event.code === "KeyL")\n return false;\n if (navigator.platform.includes("Mac")) {\n if (event.key === "v" && event.metaKey)\n return false;\n } else {\n if (event.key === "v" && event.ctrlKey)\n return false;\n if (event.key === "Insert" && event.shiftKey)\n return false;\n }\n if (["Shift", "Control", "Meta", "Alt", "Process"].includes(event.key))\n return false;\n const hasModifier = event.ctrlKey || event.altKey || event.metaKey;\n if (event.key.length === 1 && !hasModifier)\n return !this._isEditable(this._recorder.deepEventTarget(event));\n return true;\n }\n _isEditable(element) {\n if (element.nodeName === "TEXTAREA" || element.nodeName === "INPUT")\n return true;\n if (element.isContentEditable)\n return true;\n return false;\n }\n _ariaSnapshot(element) {\n const { ariaSnapshot, refs } = this._recorder.injectedScript.ariaSnapshotForRecorder();\n const ref = element ? refs.get(element) : void 0;\n const elementInfo = element ? this._recorder.injectedScript.generateSelector(element, { testIdAttributeName: this._recorder.state.testIdAttributeName }) : void 0;\n return { ariaSnapshot, selector: elementInfo == null ? void 0 : elementInfo.selector, ref };\n }\n};\nvar TextAssertionTool = class {\n constructor(recorder, kind) {\n this._hoverHighlight = null;\n this._action = null;\n this._recorder = recorder;\n this._textCache = /* @__PURE__ */ new Map();\n this._kind = kind;\n this._dialog = new Dialog(recorder);\n }\n cursor() {\n return "pointer";\n }\n uninstall() {\n this._dialog.close();\n this._hoverHighlight = null;\n }\n onClick(event) {\n consumeEvent(event);\n if (this._kind === "value") {\n this._commitAssertValue();\n } else {\n if (!this._dialog.isShowing())\n this._showDialog();\n }\n }\n onMouseDown(event) {\n const target = this._recorder.deepEventTarget(event);\n if (this._elementHasValue(target))\n event.preventDefault();\n }\n onPointerUp(event) {\n var _a;\n const target = (_a = this._hoverHighlight) == null ? void 0 : _a.elements[0];\n if (this._kind === "value" && target && (target.nodeName === "INPUT" || target.nodeName === "SELECT") && target.disabled) {\n this._commitAssertValue();\n }\n }\n onMouseMove(event) {\n var _a;\n if (this._dialog.isShowing())\n return;\n const target = this._recorder.deepEventTarget(event);\n if (((_a = this._hoverHighlight) == null ? void 0 : _a.elements[0]) === target)\n return;\n if (this._kind === "text" || this._kind === "snapshot") {\n this._hoverHighlight = this._recorder.injectedScript.utils.elementText(this._textCache, target).full ? { elements: [target], selector: "", color: HighlightColors.assert } : null;\n } else if (this._elementHasValue(target)) {\n const generated = this._recorder.injectedScript.generateSelector(target, { testIdAttributeName: this._recorder.state.testIdAttributeName });\n this._hoverHighlight = { selector: generated.selector, elements: generated.elements, color: HighlightColors.assert };\n } else {\n this._hoverHighlight = null;\n }\n this._recorder.updateHighlight(this._hoverHighlight, true);\n }\n onKeyDown(event) {\n if (event.key === "Escape")\n this._recorder.setMode("recording");\n consumeEvent(event);\n }\n onScroll(event) {\n this._recorder.updateHighlight(this._hoverHighlight, false);\n }\n _elementHasValue(element) {\n return element.nodeName === "TEXTAREA" || element.nodeName === "SELECT" || element.nodeName === "INPUT" && !["button", "image", "reset", "submit"].includes(element.type);\n }\n _generateAction() {\n var _a;\n this._textCache.clear();\n const target = (_a = this._hoverHighlight) == null ? void 0 : _a.elements[0];\n if (!target)\n return null;\n if (this._kind === "value") {\n if (!this._elementHasValue(target))\n return null;\n const { selector } = this._recorder.injectedScript.generateSelector(target, { testIdAttributeName: this._recorder.state.testIdAttributeName });\n if (target.nodeName === "INPUT" && ["checkbox", "radio"].includes(target.type.toLowerCase())) {\n return {\n name: "assertChecked",\n selector,\n signals: [],\n // Interestingly, inputElement.checked is reversed inside this event handler.\n checked: !target.checked\n };\n } else {\n return {\n name: "assertValue",\n selector,\n signals: [],\n value: target.value\n };\n }\n } else if (this._kind === "snapshot") {\n const generated = this._recorder.injectedScript.generateSelector(target, { testIdAttributeName: this._recorder.state.testIdAttributeName, forTextExpect: true });\n this._hoverHighlight = { selector: generated.selector, elements: generated.elements, color: HighlightColors.assert };\n this._recorder.updateHighlight(this._hoverHighlight, true);\n return {\n name: "assertSnapshot",\n selector: this._hoverHighlight.selector,\n signals: [],\n ariaSnapshot: this._recorder.injectedScript.ariaSnapshot(target, { mode: "codegen" })\n };\n } else {\n const generated = this._recorder.injectedScript.generateSelector(target, { testIdAttributeName: this._recorder.state.testIdAttributeName, forTextExpect: true });\n this._hoverHighlight = { selector: generated.selector, elements: generated.elements, color: HighlightColors.assert };\n this._recorder.updateHighlight(this._hoverHighlight, true);\n return {\n name: "assertText",\n selector: this._hoverHighlight.selector,\n signals: [],\n text: this._recorder.injectedScript.utils.elementText(this._textCache, target).normalized,\n substring: true\n };\n }\n }\n _renderValue(action) {\n if ((action == null ? void 0 : action.name) === "assertText")\n return this._recorder.injectedScript.utils.normalizeWhiteSpace(action.text);\n if ((action == null ? void 0 : action.name) === "assertChecked")\n return String(action.checked);\n if ((action == null ? void 0 : action.name) === "assertValue")\n return action.value;\n if ((action == null ? void 0 : action.name) === "assertSnapshot")\n return action.ariaSnapshot;\n return "";\n }\n _commit() {\n if (!this._action || !this._dialog.isShowing())\n return;\n this._dialog.close();\n void this._recorder.recordAction(this._action);\n this._recorder.setMode("recording");\n }\n _showDialog() {\n var _a, _b, _c, _d;\n if (!((_a = this._hoverHighlight) == null ? void 0 : _a.elements[0]))\n return;\n this._action = this._generateAction();\n if (((_b = this._action) == null ? void 0 : _b.name) === "assertText") {\n this._showTextDialog(this._action);\n } else if (((_c = this._action) == null ? void 0 : _c.name) === "assertSnapshot") {\n void this._recorder.recordAction(this._action);\n this._recorder.setMode("recording");\n (_d = this._recorder.overlay) == null ? void 0 : _d.flashToolSucceeded("assertingSnapshot");\n }\n }\n _showTextDialog(action) {\n const textElement = this._recorder.document.createElement("textarea");\n textElement.setAttribute("spellcheck", "false");\n textElement.value = this._renderValue(action);\n textElement.classList.add("text-editor");\n const updateAndValidate = () => {\n var _a;\n const newValue = this._recorder.injectedScript.utils.normalizeWhiteSpace(textElement.value);\n const target = (_a = this._hoverHighlight) == null ? void 0 : _a.elements[0];\n if (!target)\n return;\n action.text = newValue;\n const targetText = this._recorder.injectedScript.utils.elementText(this._textCache, target).normalized;\n const matches = newValue && targetText.includes(newValue);\n textElement.classList.toggle("does-not-match", !matches);\n };\n textElement.addEventListener("input", updateAndValidate);\n const label = "Assert that element contains text";\n const dialogElement = this._dialog.show({\n label,\n body: textElement,\n onCommit: () => this._commit()\n });\n const position = this._recorder.highlight.tooltipPosition(this._recorder.highlight.firstBox(), dialogElement);\n this._dialog.moveTo(position.anchorTop, position.anchorLeft);\n textElement.focus();\n }\n _commitAssertValue() {\n var _a;\n if (this._kind !== "value")\n return;\n const action = this._generateAction();\n if (!action)\n return;\n void this._recorder.recordAction(action);\n this._recorder.setMode("recording");\n (_a = this._recorder.overlay) == null ? void 0 : _a.flashToolSucceeded("assertingValue");\n }\n};\nvar Overlay = class {\n constructor(recorder) {\n this._listeners = [];\n this._offsetX = 0;\n this._measure = { width: 0, height: 0 };\n this._recorder = recorder;\n const document = this._recorder.document;\n this._overlayElement = document.createElement("x-pw-overlay");\n const toolsListElement = document.createElement("x-pw-tools-list");\n this._overlayElement.appendChild(toolsListElement);\n this._dragHandle = document.createElement("x-pw-tool-gripper");\n this._dragHandle.appendChild(document.createElement("x-div"));\n toolsListElement.appendChild(this._dragHandle);\n this._recordToggle = this._recorder.document.createElement("x-pw-tool-item");\n this._recordToggle.title = "Record";\n this._recordToggle.classList.add("record");\n this._recordToggle.appendChild(this._recorder.document.createElement("x-div"));\n toolsListElement.appendChild(this._recordToggle);\n this._pickLocatorToggle = this._recorder.document.createElement("x-pw-tool-item");\n this._pickLocatorToggle.title = "Pick locator";\n this._pickLocatorToggle.classList.add("pick-locator");\n this._pickLocatorToggle.appendChild(this._recorder.document.createElement("x-div"));\n toolsListElement.appendChild(this._pickLocatorToggle);\n this._assertVisibilityToggle = this._recorder.document.createElement("x-pw-tool-item");\n this._assertVisibilityToggle.title = "Assert visibility";\n this._assertVisibilityToggle.classList.add("visibility");\n this._assertVisibilityToggle.appendChild(this._recorder.document.createElement("x-div"));\n toolsListElement.appendChild(this._assertVisibilityToggle);\n this._assertTextToggle = this._recorder.document.createElement("x-pw-tool-item");\n this._assertTextToggle.title = "Assert text";\n this._assertTextToggle.classList.add("text");\n this._assertTextToggle.appendChild(this._recorder.document.createElement("x-div"));\n toolsListElement.appendChild(this._assertTextToggle);\n this._assertValuesToggle = this._recorder.document.createElement("x-pw-tool-item");\n this._assertValuesToggle.title = "Assert value";\n this._assertValuesToggle.classList.add("value");\n this._assertValuesToggle.appendChild(this._recorder.document.createElement("x-div"));\n toolsListElement.appendChild(this._assertValuesToggle);\n this._assertSnapshotToggle = this._recorder.document.createElement("x-pw-tool-item");\n this._assertSnapshotToggle.title = "Assert snapshot";\n this._assertSnapshotToggle.classList.add("snapshot");\n this._assertSnapshotToggle.appendChild(this._recorder.document.createElement("x-div"));\n toolsListElement.appendChild(this._assertSnapshotToggle);\n this._updateVisualPosition();\n this._refreshListeners();\n }\n _refreshListeners() {\n removeEventListeners(this._listeners);\n this._listeners = [\n addEventListener(this._dragHandle, "mousedown", (event) => {\n this._dragState = { offsetX: this._offsetX, dragStart: { x: event.clientX, y: 0 } };\n }),\n addEventListener(this._recordToggle, "click", () => {\n if (this._recordToggle.classList.contains("disabled"))\n return;\n this._recorder.setMode(this._recorder.state.mode === "none" || this._recorder.state.mode === "standby" || this._recorder.state.mode === "inspecting" ? "recording" : "standby");\n }),\n addEventListener(this._pickLocatorToggle, "click", () => {\n if (this._pickLocatorToggle.classList.contains("disabled"))\n return;\n const newMode = {\n "inspecting": "standby",\n "none": "inspecting",\n "standby": "inspecting",\n "recording": "recording-inspecting",\n "recording-inspecting": "recording",\n "assertingText": "recording-inspecting",\n "assertingVisibility": "recording-inspecting",\n "assertingValue": "recording-inspecting",\n "assertingSnapshot": "recording-inspecting"\n };\n this._recorder.setMode(newMode[this._recorder.state.mode]);\n }),\n addEventListener(this._assertVisibilityToggle, "click", () => {\n if (!this._assertVisibilityToggle.classList.contains("disabled"))\n this._recorder.setMode(this._recorder.state.mode === "assertingVisibility" ? "recording" : "assertingVisibility");\n }),\n addEventListener(this._assertTextToggle, "click", () => {\n if (!this._assertTextToggle.classList.contains("disabled"))\n this._recorder.setMode(this._recorder.state.mode === "assertingText" ? "recording" : "assertingText");\n }),\n addEventListener(this._assertValuesToggle, "click", () => {\n if (!this._assertValuesToggle.classList.contains("disabled"))\n this._recorder.setMode(this._recorder.state.mode === "assertingValue" ? "recording" : "assertingValue");\n }),\n addEventListener(this._assertSnapshotToggle, "click", () => {\n if (!this._assertSnapshotToggle.classList.contains("disabled"))\n this._recorder.setMode(this._recorder.state.mode === "assertingSnapshot" ? "recording" : "assertingSnapshot");\n })\n ];\n }\n install() {\n this._recorder.highlight.appendChild(this._overlayElement);\n this._refreshListeners();\n this._updateVisualPosition();\n }\n contains(element) {\n return this._recorder.injectedScript.utils.isInsideScope(this._overlayElement, element);\n }\n setUIState(state) {\n const isRecording = state.mode === "recording" || state.mode === "assertingText" || state.mode === "assertingVisibility" || state.mode === "assertingValue" || state.mode === "assertingSnapshot" || state.mode === "recording-inspecting";\n this._recordToggle.classList.toggle("toggled", isRecording);\n this._recordToggle.title = isRecording ? "Stop Recording" : "Start Recording";\n this._pickLocatorToggle.classList.toggle("toggled", state.mode === "inspecting" || state.mode === "recording-inspecting");\n this._assertVisibilityToggle.classList.toggle("toggled", state.mode === "assertingVisibility");\n this._assertVisibilityToggle.classList.toggle("disabled", state.mode === "none" || state.mode === "standby" || state.mode === "inspecting");\n this._assertTextToggle.classList.toggle("toggled", state.mode === "assertingText");\n this._assertTextToggle.classList.toggle("disabled", state.mode === "none" || state.mode === "standby" || state.mode === "inspecting");\n this._assertValuesToggle.classList.toggle("toggled", state.mode === "assertingValue");\n this._assertValuesToggle.classList.toggle("disabled", state.mode === "none" || state.mode === "standby" || state.mode === "inspecting");\n this._assertSnapshotToggle.classList.toggle("toggled", state.mode === "assertingSnapshot");\n this._assertSnapshotToggle.classList.toggle("disabled", state.mode === "none" || state.mode === "standby" || state.mode === "inspecting");\n if (this._offsetX !== state.overlay.offsetX) {\n this._offsetX = state.overlay.offsetX;\n this._updateVisualPosition();\n }\n if (state.mode === "none")\n this._hideOverlay();\n else\n this._showOverlay();\n }\n flashToolSucceeded(tool) {\n let element;\n if (tool === "assertingVisibility")\n element = this._assertVisibilityToggle;\n else if (tool === "assertingSnapshot")\n element = this._assertSnapshotToggle;\n else\n element = this._assertValuesToggle;\n element.classList.add("succeeded");\n this._recorder.injectedScript.utils.builtins.setTimeout(() => element.classList.remove("succeeded"), 2e3);\n }\n _hideOverlay() {\n this._overlayElement.setAttribute("hidden", "true");\n }\n _showOverlay() {\n if (!this._overlayElement.hasAttribute("hidden"))\n return;\n this._overlayElement.removeAttribute("hidden");\n this._updateVisualPosition();\n }\n _updateVisualPosition() {\n this._measure = this._overlayElement.getBoundingClientRect();\n this._overlayElement.style.left = (this._recorder.injectedScript.window.innerWidth - this._measure.width) / 2 + this._offsetX + "px";\n }\n onMouseMove(event) {\n if (!event.buttons) {\n this._dragState = void 0;\n return false;\n }\n if (this._dragState) {\n this._offsetX = this._dragState.offsetX + event.clientX - this._dragState.dragStart.x;\n const halfGapSize = (this._recorder.injectedScript.window.innerWidth - this._measure.width) / 2 - 10;\n this._offsetX = Math.max(-halfGapSize, Math.min(halfGapSize, this._offsetX));\n this._updateVisualPosition();\n this._recorder.setOverlayState({ offsetX: this._offsetX });\n consumeEvent(event);\n return true;\n }\n return false;\n }\n onMouseUp(event) {\n if (this._dragState) {\n consumeEvent(event);\n return true;\n }\n return false;\n }\n onClick(event) {\n if (this._dragState) {\n this._dragState = void 0;\n consumeEvent(event);\n return true;\n }\n return false;\n }\n onDblClick(event) {\n return false;\n }\n};\nvar Recorder = class {\n constructor(injectedScript, options) {\n this._listeners = [];\n this._lastHighlightedSelector = void 0;\n this._lastHighlightedAriaTemplateJSON = "undefined";\n this.state = {\n mode: "none",\n testIdAttributeName: "data-testid",\n language: "javascript",\n overlay: { offsetX: 0 }\n };\n this._delegate = {};\n var _a, _b;\n this.document = injectedScript.document;\n this.injectedScript = injectedScript;\n this.highlight = injectedScript.createHighlight();\n this._tools = {\n "none": new NoneTool(),\n "standby": new NoneTool(),\n "inspecting": new InspectTool(this, false),\n "recording": (options == null ? void 0 : options.recorderMode) === "api" ? new JsonRecordActionTool(this) : new RecordActionTool(this),\n "recording-inspecting": new InspectTool(this, false),\n "assertingText": new TextAssertionTool(this, "text"),\n "assertingVisibility": new InspectTool(this, true),\n "assertingValue": new TextAssertionTool(this, "value"),\n "assertingSnapshot": new TextAssertionTool(this, "snapshot")\n };\n this._currentTool = this._tools.none;\n (_b = (_a = this._currentTool).install) == null ? void 0 : _b.call(_a);\n if (injectedScript.window.top === injectedScript.window && !(options == null ? void 0 : options.hideToolbar)) {\n this.overlay = new Overlay(this);\n this.overlay.setUIState(this.state);\n }\n this._stylesheet = new injectedScript.window.CSSStyleSheet();\n this._stylesheet.replaceSync(`\n body[data-pw-cursor=pointer] *, body[data-pw-cursor=pointer] *::after { cursor: pointer !important; }\n body[data-pw-cursor=text] *, body[data-pw-cursor=text] *::after { cursor: text !important; }\n `);\n this.installListeners();\n injectedScript.utils.cacheNormalizedWhitespaces();\n if (injectedScript.isUnderTest)\n console.error("Recorder script ready for test");\n injectedScript.consoleApi.install();\n }\n installListeners() {\n var _a, _b, _c;\n removeEventListeners(this._listeners);\n this._listeners = [\n addEventListener(this.document, "click", (event) => this._onClick(event), true),\n addEventListener(this.document, "auxclick", (event) => this._onClick(event), true),\n addEventListener(this.document, "dblclick", (event) => this._onDblClick(event), true),\n addEventListener(this.document, "contextmenu", (event) => this._onContextMenu(event), true),\n addEventListener(this.document, "dragstart", (event) => this._onDragStart(event), true),\n addEventListener(this.document, "input", (event) => this._onInput(event), true),\n addEventListener(this.document, "keydown", (event) => this._onKeyDown(event), true),\n addEventListener(this.document, "keyup", (event) => this._onKeyUp(event), true),\n addEventListener(this.document, "pointerdown", (event) => this._onPointerDown(event), true),\n addEventListener(this.document, "pointerup", (event) => this._onPointerUp(event), true),\n addEventListener(this.document, "mousedown", (event) => this._onMouseDown(event), true),\n addEventListener(this.document, "mouseup", (event) => this._onMouseUp(event), true),\n addEventListener(this.document, "mousemove", (event) => this._onMouseMove(event), true),\n addEventListener(this.document, "mouseleave", (event) => this._onMouseLeave(event), true),\n addEventListener(this.document, "mouseenter", (event) => this._onMouseEnter(event), true),\n addEventListener(this.document, "focus", (event) => this._onFocus(event), true),\n addEventListener(this.document, "scroll", (event) => this._onScroll(event), true)\n ];\n this.highlight.install();\n let recreationInterval;\n const recreate = () => {\n this.highlight.install();\n recreationInterval = this.injectedScript.utils.builtins.setTimeout(recreate, 500);\n };\n recreationInterval = this.injectedScript.utils.builtins.setTimeout(recreate, 500);\n this._listeners.push(() => this.injectedScript.utils.builtins.clearTimeout(recreationInterval));\n this.highlight.appendChild(createSvgElement(this.document, clipPaths_default));\n (_a = this.overlay) == null ? void 0 : _a.install();\n (_c = (_b = this._currentTool) == null ? void 0 : _b.install) == null ? void 0 : _c.call(_b);\n this.document.adoptedStyleSheets.push(this._stylesheet);\n }\n _switchCurrentTool() {\n var _a, _b, _c, _d, _e, _f;\n const newTool = this._tools[this.state.mode];\n if (newTool === this._currentTool)\n return;\n (_b = (_a = this._currentTool).uninstall) == null ? void 0 : _b.call(_a);\n this.clearHighlight();\n this._currentTool = newTool;\n (_d = (_c = this._currentTool).install) == null ? void 0 : _d.call(_c);\n const cursor = (_e = newTool.cursor) == null ? void 0 : _e.call(newTool);\n if (cursor)\n (_f = this.injectedScript.document.body) == null ? void 0 : _f.setAttribute("data-pw-cursor", cursor);\n }\n setUIState(state, delegate) {\n var _a;\n this._delegate = delegate;\n if (state.actionPoint && this.state.actionPoint && state.actionPoint.x === this.state.actionPoint.x && state.actionPoint.y === this.state.actionPoint.y) {\n } else if (!state.actionPoint && !this.state.actionPoint) {\n } else {\n if (state.actionPoint)\n this.highlight.showActionPoint(state.actionPoint.x, state.actionPoint.y);\n else\n this.highlight.hideActionPoint();\n }\n this.state = state;\n this.highlight.setLanguage(state.language);\n this._switchCurrentTool();\n (_a = this.overlay) == null ? void 0 : _a.setUIState(state);\n let highlight = "noop";\n if (state.actionSelector !== this._lastHighlightedSelector) {\n const entries = state.actionSelector ? entriesForSelectorHighlight(this.injectedScript, state.language, state.actionSelector, this.document) : null;\n highlight = (entries == null ? void 0 : entries.length) ? entries : "clear";\n this._lastHighlightedSelector = (entries == null ? void 0 : entries.length) ? state.actionSelector : void 0;\n }\n const ariaTemplateJSON = JSON.stringify(state.ariaTemplate);\n if (this._lastHighlightedAriaTemplateJSON !== ariaTemplateJSON) {\n const elements = state.ariaTemplate ? this.injectedScript.getAllElementsMatchingExpectAriaTemplate(this.document, state.ariaTemplate) : [];\n if (elements.length) {\n const color = elements.length > 1 ? HighlightColors.multiple : HighlightColors.single;\n highlight = elements.map((element) => ({ element, color }));\n this._lastHighlightedAriaTemplateJSON = ariaTemplateJSON;\n } else {\n if (!this._lastHighlightedSelector)\n highlight = "clear";\n this._lastHighlightedAriaTemplateJSON = "undefined";\n }\n }\n if (highlight === "clear")\n this.highlight.clearHighlight();\n else if (highlight !== "noop")\n this.highlight.updateHighlight(highlight);\n }\n clearHighlight() {\n this.updateHighlight(null, false);\n }\n _onClick(event) {\n var _a, _b, _c;\n if (!event.isTrusted)\n return;\n if ((_a = this.overlay) == null ? void 0 : _a.onClick(event))\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_c = (_b = this._currentTool).onClick) == null ? void 0 : _c.call(_b, event);\n }\n _onDblClick(event) {\n var _a, _b, _c;\n if (!event.isTrusted)\n return;\n if ((_a = this.overlay) == null ? void 0 : _a.onDblClick(event))\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_c = (_b = this._currentTool).onDblClick) == null ? void 0 : _c.call(_b, event);\n }\n _onContextMenu(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n (_b = (_a = this._currentTool).onContextMenu) == null ? void 0 : _b.call(_a, event);\n }\n _onDragStart(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onDragStart) == null ? void 0 : _b.call(_a, event);\n }\n _onPointerDown(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onPointerDown) == null ? void 0 : _b.call(_a, event);\n }\n _onPointerUp(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onPointerUp) == null ? void 0 : _b.call(_a, event);\n }\n _onMouseDown(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onMouseDown) == null ? void 0 : _b.call(_a, event);\n }\n _onMouseUp(event) {\n var _a, _b, _c;\n if (!event.isTrusted)\n return;\n if ((_a = this.overlay) == null ? void 0 : _a.onMouseUp(event))\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_c = (_b = this._currentTool).onMouseUp) == null ? void 0 : _c.call(_b, event);\n }\n _onMouseMove(event) {\n var _a, _b, _c;\n if (!event.isTrusted)\n return;\n if ((_a = this.overlay) == null ? void 0 : _a.onMouseMove(event))\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_c = (_b = this._currentTool).onMouseMove) == null ? void 0 : _c.call(_b, event);\n }\n _onMouseEnter(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onMouseEnter) == null ? void 0 : _b.call(_a, event);\n }\n _onMouseLeave(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onMouseLeave) == null ? void 0 : _b.call(_a, event);\n }\n _onFocus(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onFocus) == null ? void 0 : _b.call(_a, event);\n }\n _onScroll(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n this._lastHighlightedSelector = void 0;\n this._lastHighlightedAriaTemplateJSON = "undefined";\n this.highlight.hideActionPoint();\n (_b = (_a = this._currentTool).onScroll) == null ? void 0 : _b.call(_a, event);\n }\n _onInput(event) {\n var _a, _b;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onInput) == null ? void 0 : _b.call(_a, event);\n }\n _onKeyDown(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onKeyDown) == null ? void 0 : _b.call(_a, event);\n }\n _onKeyUp(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onKeyUp) == null ? void 0 : _b.call(_a, event);\n }\n updateHighlight(model, userGesture) {\n this._lastHighlightedSelector = void 0;\n this._lastHighlightedAriaTemplateJSON = "undefined";\n this._updateHighlight(model, userGesture);\n }\n _updateHighlight(model, userGesture) {\n var _a, _b;\n let tooltipText = model == null ? void 0 : model.tooltipText;\n if (tooltipText === void 0 && (model == null ? void 0 : model.selector))\n tooltipText = this.injectedScript.utils.asLocator(this.state.language, model.selector);\n if (model)\n this.highlight.updateHighlight(model.elements.map((element) => ({ element, color: model.color, tooltipText })));\n else\n this.highlight.clearHighlight();\n if (userGesture)\n (_b = (_a = this._delegate).highlightUpdated) == null ? void 0 : _b.call(_a);\n }\n _ignoreOverlayEvent(event) {\n return event.composedPath().some((e) => {\n const nodeName = e.nodeName || "";\n return nodeName.toLowerCase() === "x-pw-glass";\n });\n }\n deepEventTarget(event) {\n var _a;\n for (const element of event.composedPath()) {\n if (!((_a = this.overlay) == null ? void 0 : _a.contains(element)))\n return element;\n }\n return event.composedPath()[0];\n }\n setMode(mode) {\n var _a, _b;\n void ((_b = (_a = this._delegate).setMode) == null ? void 0 : _b.call(_a, mode));\n }\n _captureAutoExpectSnapshot() {\n const documentElement = this.injectedScript.document.documentElement;\n return documentElement ? this.injectedScript.utils.generateAriaTree(documentElement, { mode: "autoexpect" }) : void 0;\n }\n async performAction(action) {\n var _a, _b, _c;\n const previousSnapshot = this._lastActionAutoexpectSnapshot;\n this._lastActionAutoexpectSnapshot = this._captureAutoExpectSnapshot();\n if (!isAssertAction(action) && this._lastActionAutoexpectSnapshot) {\n const element = this.injectedScript.utils.findNewElement(previousSnapshot == null ? void 0 : previousSnapshot.root, (_a = this._lastActionAutoexpectSnapshot) == null ? void 0 : _a.root);\n action.preconditionSelector = element ? this.injectedScript.generateSelector(element, { testIdAttributeName: this.state.testIdAttributeName }).selector : void 0;\n if (action.preconditionSelector === action.selector)\n action.preconditionSelector = void 0;\n }\n await ((_c = (_b = this._delegate).performAction) == null ? void 0 : _c.call(_b, action).catch(() => {\n }));\n }\n async recordAction(action) {\n var _a, _b;\n this._lastActionAutoexpectSnapshot = this._captureAutoExpectSnapshot();\n await ((_b = (_a = this._delegate).recordAction) == null ? void 0 : _b.call(_a, action));\n }\n setOverlayState(state) {\n var _a, _b;\n void ((_b = (_a = this._delegate).setOverlayState) == null ? void 0 : _b.call(_a, state));\n }\n elementPicked(selector, model) {\n var _a, _b;\n const ariaSnapshot = this.injectedScript.ariaSnapshot(model.elements[0], { mode: "default" });\n void ((_b = (_a = this._delegate).elementPicked) == null ? void 0 : _b.call(_a, { selector, ariaSnapshot }));\n }\n};\nvar Dialog = class {\n constructor(recorder) {\n this._dialogElement = null;\n this._recorder = recorder;\n }\n isShowing() {\n return !!this._dialogElement;\n }\n show(options) {\n const acceptButton = this._recorder.document.createElement("x-pw-tool-item");\n acceptButton.title = "Accept";\n acceptButton.classList.add("accept");\n acceptButton.appendChild(this._recorder.document.createElement("x-div"));\n acceptButton.addEventListener("click", () => {\n var _a;\n return (_a = options.onCommit) == null ? void 0 : _a.call(options);\n });\n const cancelButton = this._recorder.document.createElement("x-pw-tool-item");\n cancelButton.title = "Close";\n cancelButton.classList.add("cancel");\n cancelButton.appendChild(this._recorder.document.createElement("x-div"));\n cancelButton.addEventListener("click", () => {\n var _a;\n this.close();\n (_a = options.onCancel) == null ? void 0 : _a.call(options);\n });\n this._dialogElement = this._recorder.document.createElement("x-pw-dialog");\n if (options.autosize)\n this._dialogElement.classList.add("autosize");\n this._keyboardListener = (event) => {\n var _a;\n if (event.key === "Escape") {\n this.close();\n (_a = options.onCancel) == null ? void 0 : _a.call(options);\n return;\n }\n if (options.onCommit && event.key === "Enter" && (event.ctrlKey || event.metaKey)) {\n if (this._dialogElement)\n options.onCommit();\n return;\n }\n };\n this._onGlassPaneClickHandler = (event) => {\n var _a;\n this.close();\n (_a = options.onCancel) == null ? void 0 : _a.call(options);\n };\n this._dialogElement.addEventListener("click", (event) => event.stopPropagation());\n const toolbarElement = this._recorder.document.createElement("x-pw-tools-list");\n const labelElement = this._recorder.document.createElement("label");\n labelElement.textContent = options.label;\n toolbarElement.appendChild(labelElement);\n toolbarElement.appendChild(this._recorder.document.createElement("x-spacer"));\n if (options.onCommit)\n toolbarElement.appendChild(acceptButton);\n toolbarElement.appendChild(cancelButton);\n this._dialogElement.appendChild(toolbarElement);\n const bodyElement = this._recorder.document.createElement("x-pw-dialog-body");\n bodyElement.appendChild(options.body);\n this._dialogElement.appendChild(bodyElement);\n this._recorder.highlight.appendChild(this._dialogElement);\n this._recorder.highlight.onGlassPaneClick(this._onGlassPaneClickHandler);\n this._recorder.document.addEventListener("keydown", this._keyboardListener, true);\n return this._dialogElement;\n }\n moveTo(top, left) {\n if (!this._dialogElement)\n return;\n this._dialogElement.style.top = top + "px";\n this._dialogElement.style.left = left + "px";\n }\n close() {\n if (!this._dialogElement)\n return;\n this._dialogElement.remove();\n this._recorder.highlight.offGlassPaneClick(this._onGlassPaneClickHandler);\n this._recorder.document.removeEventListener("keydown", this._keyboardListener);\n this._dialogElement = null;\n }\n};\nfunction deepActiveElement(document) {\n let activeElement = document.activeElement;\n while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)\n activeElement = activeElement.shadowRoot.activeElement;\n return activeElement;\n}\nfunction modifiersForEvent(event) {\n return (event.altKey ? 1 : 0) | (event.ctrlKey ? 2 : 0) | (event.metaKey ? 4 : 0) | (event.shiftKey ? 8 : 0);\n}\nfunction buttonForEvent(event) {\n switch (event.which) {\n case 1:\n return "left";\n case 2:\n return "middle";\n case 3:\n return "right";\n }\n return "left";\n}\nfunction positionForEvent(event) {\n const targetElement = event.target;\n if (targetElement.nodeName !== "CANVAS")\n return;\n return {\n x: event.offsetX,\n y: event.offsetY\n };\n}\nfunction consumeEvent(e) {\n e.preventDefault();\n e.stopPropagation();\n e.stopImmediatePropagation();\n}\nfunction asCheckbox(node) {\n if (!node || node.nodeName !== "INPUT")\n return null;\n const inputElement = node;\n return ["checkbox", "radio"].includes(inputElement.type) ? inputElement : null;\n}\nfunction isRangeInput(node) {\n if (!node || node.nodeName !== "INPUT")\n return false;\n const inputElement = node;\n return inputElement.type.toLowerCase() === "range";\n}\nvar kNativePickerInputTypes = /* @__PURE__ */ new Set(["color", "date", "datetime-local", "file", "month", "range", "time", "week"]);\nfunction shouldIgnoreMouseEvent(target) {\n const nodeName = target.nodeName;\n if (nodeName === "SELECT" || nodeName === "OPTION")\n return true;\n if (nodeName === "INPUT" && kNativePickerInputTypes.has(target.type))\n return true;\n return false;\n}\nfunction addEventListener(target, eventName, listener, useCapture) {\n target.addEventListener(eventName, listener, useCapture);\n const remove = () => {\n target.removeEventListener(eventName, listener, useCapture);\n };\n return remove;\n}\nfunction removeEventListeners(listeners) {\n for (const listener of listeners)\n listener();\n listeners.splice(0, listeners.length);\n}\nfunction entriesForSelectorHighlight(injectedScript, language, selector, ownerDocument) {\n try {\n const parsedSelector = injectedScript.parseSelector(selector);\n const elements = injectedScript.querySelectorAll(parsedSelector, ownerDocument);\n const color = elements.length > 1 ? HighlightColors.multiple : HighlightColors.single;\n const locator = injectedScript.utils.asLocator(language, selector);\n return elements.map((element, index) => {\n const suffix = elements.length > 1 ? ` [${index + 1} of ${elements.length}]` : "";\n return { element, color, tooltipText: locator + suffix };\n });\n } catch (e) {\n return [];\n }\n}\nfunction createSvgElement(doc, { tagName, attrs, children }) {\n const elem = doc.createElementNS("http://www.w3.org/2000/svg", tagName);\n if (attrs) {\n for (const [k, v] of Object.entries(attrs))\n elem.setAttribute(k, v);\n }\n if (children) {\n for (const c of children)\n elem.appendChild(createSvgElement(doc, c));\n }\n return elem;\n}\nfunction isAssertAction(action) {\n return action.name.startsWith("assert");\n}\n\n// packages/injected/src/recorder/pollingRecorder.ts\nvar PollingRecorder = class {\n constructor(injectedScript, options) {\n this._recorder = new Recorder(injectedScript, options);\n this._embedder = injectedScript.window;\n injectedScript.onGlobalListenersRemoved.add(() => this._recorder.installListeners());\n const refreshOverlay = () => {\n this._lastStateJSON = void 0;\n this._pollRecorderMode().catch((e) => console.log(e));\n };\n this._embedder.__pw_refreshOverlay = refreshOverlay;\n refreshOverlay();\n }\n async _pollRecorderMode() {\n const pollPeriod = 1e3;\n if (this._pollRecorderModeTimer)\n this._recorder.injectedScript.utils.builtins.clearTimeout(this._pollRecorderModeTimer);\n const state = await this._embedder.__pw_recorderState().catch(() => null);\n if (!state) {\n this._pollRecorderModeTimer = this._recorder.injectedScript.utils.builtins.setTimeout(() => this._pollRecorderMode(), pollPeriod);\n return;\n }\n const stringifiedState = JSON.stringify(state);\n if (this._lastStateJSON !== stringifiedState) {\n this._lastStateJSON = stringifiedState;\n const win = this._recorder.document.defaultView;\n if (win.top !== win) {\n state.actionPoint = void 0;\n }\n this._recorder.setUIState(state, this);\n }\n this._pollRecorderModeTimer = this._recorder.injectedScript.utils.builtins.setTimeout(() => this._pollRecorderMode(), pollPeriod);\n }\n async performAction(action) {\n await this._embedder.__pw_recorderPerformAction(action);\n }\n async recordAction(action) {\n await this._embedder.__pw_recorderRecordAction(action);\n }\n async elementPicked(elementInfo) {\n await this._embedder.__pw_recorderElementPicked(elementInfo);\n }\n async setMode(mode) {\n await this._embedder.__pw_recorderSetMode(mode);\n }\n async setOverlayState(state) {\n await this._embedder.__pw_recorderSetOverlayState(state);\n }\n};\nvar pollingRecorder_default = PollingRecorder;\n'; + } +}); + +// packages/playwright-core/src/server/recorder/recorderRunner.ts +async function performAction(progress2, pageAliases, actionInContext) { + const mainFrame = mainFrameForAction(pageAliases, actionInContext); + return await performActionImpl(progress2, mainFrame, actionInContext); +} +async function performActionImpl(progress2, mainFrame, actionInContext) { + const { action } = actionInContext; + if (action.name === "navigate") { + await mainFrame.goto(progress2, action.url); + return; + } + if (action.name === "openPage") + throw Error("Not reached"); + if (action.name === "closePage") { + await mainFrame._page.close(progress2); + return; + } + const selector = buildFullSelector(actionInContext.frame.framePath, action.selector); + if (action.name === "click") { + const options = toClickOptions(action); + await mainFrame.click(progress2, selector, { ...options, strict: true }); + return; + } + if (action.name === "hover") { + await mainFrame.hover(progress2, selector, { position: action.position, strict: true }); + return; + } + if (action.name === "press") { + const modifiers = toKeyboardModifiers(action.modifiers); + const shortcut = [...modifiers, action.key].join("+"); + await mainFrame.press(progress2, selector, shortcut, { strict: true }); + return; + } + if (action.name === "fill") { + await mainFrame.fill(progress2, selector, action.text, { strict: true }); + return; + } + if (action.name === "setInputFiles") { + await mainFrame.setInputFiles(progress2, selector, { selector, payloads: [], strict: true }); + return; + } + if (action.name === "check") { + await mainFrame.check(progress2, selector, { strict: true }); + return; + } + if (action.name === "uncheck") { + await mainFrame.uncheck(progress2, selector, { strict: true }); + return; + } + if (action.name === "select") { + const values = action.options.map((value2) => ({ value: value2 })); + await mainFrame.selectOption(progress2, selector, [], values, { strict: true }); + return; + } + if (action.name === "assertChecked") { + await mainFrame.expect(progress2, selector, { + selector, + expression: "to.be.checked", + expectedValue: { checked: action.checked }, + isNot: !action.checked + }); + return; + } + if (action.name === "assertText") { + await mainFrame.expect(progress2, selector, { + selector, + expression: "to.have.text", + expectedText: serializeExpectedTextValues([action.text], { matchSubstring: true, normalizeWhiteSpace: true }), + isNot: false + }); + return; + } + if (action.name === "assertValue") { + await mainFrame.expect(progress2, selector, { + selector, + expression: "to.have.value", + expectedValue: action.value, + isNot: false + }); + return; + } + if (action.name === "assertVisible") { + await mainFrame.expect(progress2, selector, { + selector, + expression: "to.be.visible", + isNot: false + }); + return; + } + throw new Error("Internal error: unexpected action " + action.name); +} +function toClickOptions(action) { + const modifiers = toKeyboardModifiers(action.modifiers); + const options = {}; + if (action.button !== "left") + options.button = action.button; + if (modifiers.length) + options.modifiers = modifiers; + if (action.clickCount > 1) + options.clickCount = action.clickCount; + if (action.position) + options.position = action.position; + return options; +} +var init_recorderRunner = __esm({ + "packages/playwright-core/src/server/recorder/recorderRunner.ts"() { + "use strict"; + init_expectUtils(); + init_language(); + init_recorderUtils(); + } +}); + +// packages/playwright-core/src/server/recorder.ts +function isScreenshotCommand(metadata) { + return metadata.method.toLowerCase().includes("screenshot"); +} +function languageForFile(file) { + if (file.endsWith(".py")) + return "python"; + if (file.endsWith(".java")) + return "java"; + if (file.endsWith(".cs")) + return "csharp"; + return "javascript"; +} +var import_events6, import_fs22, recorderSymbol, RecorderEvent, Recorder; +var init_recorder = __esm({ + "packages/playwright-core/src/server/recorder.ts"() { + "use strict"; + import_events6 = __toESM(require("events")); + import_fs22 = __toESM(require("fs")); + init_locatorParser(); + init_selectorParser(); + init_manualPromise(); + init_debug(); + init_eventsHelper(); + init_time(); + init_browserContext(); + init_debugger(); + init_recorderUtils(); + init_progress(); + init_recorderSignalProcessor(); + init_pollingRecorderSource(); + init_frames(); + init_page(); + init_recorderRunner(); + recorderSymbol = Symbol("recorderSymbol"); + RecorderEvent = { + PausedStateChanged: "pausedStateChanged", + ModeChanged: "modeChanged", + ElementPicked: "elementPicked", + CallLogsUpdated: "callLogsUpdated", + UserSourcesChanged: "userSourcesChanged", + ActionAdded: "actionAdded", + SignalAdded: "signalAdded", + PageNavigated: "pageNavigated", + ContextClosed: "contextClosed" + }; + Recorder = class _Recorder extends import_events6.default { + constructor(context2, params2) { + super(); + this._highlightedElement = {}; + this._overlayState = { offsetX: 0 }; + this._currentCallsMetadata = /* @__PURE__ */ new Map(); + this._actionPoints = /* @__PURE__ */ new Map(); + this._userSources = /* @__PURE__ */ new Map(); + this._omitCallTracking = false; + this._currentLanguage = "javascript"; + this._pageAliases = /* @__PURE__ */ new Map(); + this._lastPopupOrdinal = 0; + this._lastDialogOrdinal = -1; + this._lastDownloadOrdinal = -1; + this._listeners = []; + this._enabled = false; + this._callLogs = []; + this._context = context2; + this._params = params2; + this._mode = params2.mode || "none"; + this._recorderMode = params2.recorderMode ?? "default"; + this.handleSIGINT = params2.handleSIGINT; + this._signalProcessor = new RecorderSignalProcessor({ + addAction: (actionInContext) => { + if (this._enabled) + this.emit(RecorderEvent.ActionAdded, actionInContext); + }, + addSignal: (signal) => { + if (this._enabled) + this.emit(RecorderEvent.SignalAdded, signal); + } + }); + context2.on(BrowserContext.Events.BeforeClose, () => { + this.emit(RecorderEvent.ContextClosed); + }); + this._listeners.push(eventsHelper.addEventListener(process, "exit", () => { + this.emit(RecorderEvent.ContextClosed); + })); + this._setEnabled(params2.mode === "recording"); + this._omitCallTracking = !!params2.omitCallTracking; + this._debugger = context2.debugger(); + context2.instrumentation.addListener(this, context2); + if (isUnderTest()) { + this._overlayState.offsetX = 200; + } + } + static forContext(context2, params2) { + let recorderPromise = context2[recorderSymbol]; + if (!recorderPromise) { + recorderPromise = _Recorder._create(context2, params2); + context2[recorderSymbol] = recorderPromise; + } + return recorderPromise; + } + static async existingForContext(context2) { + const recorderPromise = context2[recorderSymbol]; + return await recorderPromise; + } + static async _create(context2, params2 = {}) { + const recorder = new _Recorder(context2, params2); + await recorder._install(); + return recorder; + } + async _install() { + this.emit(RecorderEvent.ModeChanged, this._mode); + this.emit(RecorderEvent.PausedStateChanged, this._debugger.isPaused()); + this._context.once(BrowserContext.Events.Close, () => { + eventsHelper.removeEventListeners(this._listeners); + this._context.instrumentation.removeListener(this); + this.emit(RecorderEvent.ContextClosed); + }); + const controller = new ProgressController(); + await controller.run(async (progress2) => { + await this._context.exposeBinding(progress2, "__pw_recorderState", async (source11) => { + let actionSelector; + let actionPoint; + const hasActiveScreenshotCommand = [...this._currentCallsMetadata.keys()].some(isScreenshotCommand); + if (!hasActiveScreenshotCommand) { + actionSelector = await this._scopeHighlightedSelectorToFrame(source11.frame); + for (const [metadata, sdkObject] of this._currentCallsMetadata) { + if (source11.page === sdkObject.attribution.page) { + actionPoint = this._actionPoints.get(metadata.id) || actionPoint; + actionSelector = actionSelector || metadata.params.selector; + } + } + } + let mode = this._mode; + if (this._pickLocatorPage && source11.page !== this._pickLocatorPage) + mode = "none"; + const uiState = { + mode, + actionPoint, + actionSelector, + ariaTemplate: this._highlightedElement.ariaTemplate, + language: this._currentLanguage, + testIdAttributeName: this._testIdAttributeName(), + overlay: this._overlayState + }; + return uiState; + }); + await this._context.exposeBinding(progress2, "__pw_recorderElementPicked", async ({ frame }, elementInfo) => { + const selectorChain = await generateFrameSelector(progress2, frame); + this.emit(RecorderEvent.ElementPicked, { selector: buildFullSelector(selectorChain, elementInfo.selector), ariaSnapshot: elementInfo.ariaSnapshot }, true); + }); + await this._context.exposeBinding(progress2, "__pw_recorderSetMode", async ({ frame }, mode) => { + if (frame.parentFrame()) + return; + await this.setMode(mode); + }); + await this._context.exposeBinding(progress2, "__pw_recorderSetOverlayState", async ({ frame }, state) => { + if (frame.parentFrame()) + return; + this._overlayState = state; + }); + await this._context.exposeBinding(progress2, "__pw_resume", () => { + this._debugger.resume(); + }); + this._context.on(BrowserContext.Events.Page, (page) => this._onPage(page)); + for (const page of this._context.pages()) + this._onPage(page); + this._context.dialogManager.addDialogHandler((dialog) => { + this._onDialog(dialog.page()); + return false; + }); + await this._context.exposeBinding( + progress2, + "__pw_recorderPerformAction", + (source11, action) => this._performAction(progress2, source11.frame, action) + ); + await this._context.exposeBinding( + progress2, + "__pw_recorderRecordAction", + (source11, action) => this._recordAction(progress2, source11.frame, action) + ); + await progress2.race(this._context.extendInjectedScript(source6, { recorderMode: this._recorderMode, hideToolbar: !!this._params.hideToolbar })); + }); + if (this._debugger.isPaused()) + this._pausedStateChanged(); + this._debugger.on(Debugger.Events.PausedStateChanged, () => this._pausedStateChanged()); + } + _pausedStateChanged() { + const pausedDetails = this._debugger.pausedDetails(); + if (pausedDetails && !this._currentCallsMetadata.has(pausedDetails.metadata)) + this.onBeforeCall(pausedDetails.sdkObject, pausedDetails.metadata); + this.emit(RecorderEvent.PausedStateChanged, this._debugger.isPaused()); + this._updateUserSources(); + this._updateCallLog([...this._currentCallsMetadata.keys()]); + } + mode() { + return this._mode; + } + async setMode(mode) { + if (this._mode === mode) + return; + this._highlightedElement = {}; + this._mode = mode; + this.emit(RecorderEvent.ModeChanged, this._mode); + this._setEnabled(this._isRecording()); + this._debugger.setMuted(this._isRecording()); + if (this._mode !== "none" && this._mode !== "standby") { + let pageToFocus = this._pickLocatorPage; + if (!pageToFocus && this._context.pages().length === 1) + pageToFocus = this._context.pages()[0]; + pageToFocus?.bringToFront(nullProgress).catch(() => { + }); + } + await this._refreshOverlay(); + } + async pickLocator(progress2, page) { + if (this._mode !== "none") + await progress2.race(this.setMode("none")); + const selectorPromise = new ManualPromise(); + let recorderChangedState = false; + const onElementPicked = (elementInfo) => { + selectorPromise.resolve(elementInfo.selector); + }; + const onModeChanged = () => { + if (this._mode === "inspecting") + return; + recorderChangedState = true; + selectorPromise.reject(new Error("Locator picking was cancelled")); + }; + const listeners = [ + eventsHelper.addEventListener(this, RecorderEvent.ElementPicked, onElementPicked), + eventsHelper.addEventListener(this, RecorderEvent.ModeChanged, onModeChanged) + ]; + try { + const doPickLocator = async () => { + selectorPromise.catch(() => { + }); + this._pickLocatorPage = page; + await this.setMode("inspecting"); + return await selectorPromise; + }; + return await progress2.race(page.openScope.race(doPickLocator())); + } finally { + eventsHelper.removeEventListeners(listeners); + this._pickLocatorPage = void 0; + if (!recorderChangedState) + await progress2.race(this.setMode("none")); + } + } + url() { + const page = this._context.pages()[0]; + return page?.mainFrame().url(); + } + async setHighlightedSelector(selector) { + this._highlightedElement = { selector: locatorOrSelectorAsSelector(this._currentLanguage, selector, this._context.selectors().testIdAttributeName()) }; + await this._refreshOverlay(); + } + async setHighlightedAriaTemplate(ariaTemplate) { + this._highlightedElement = { ariaTemplate }; + await this._refreshOverlay(); + } + step() { + this._debugger.setPauseAt({ next: true }); + this._debugger.resume(); + } + async setLanguage(language) { + this._currentLanguage = language; + await this._refreshOverlay(); + } + resume() { + this._debugger.resume(); + } + pause() { + this._debugger.setPauseAt({ next: true }); + } + paused() { + return this._debugger.isPaused(); + } + close() { + this._debugger.resume(); + } + async hideHighlightedSelector() { + this._highlightedElement = {}; + await this._refreshOverlay(); + } + pausedSourceId() { + const pausedDetails = this._debugger.pausedDetails(); + if (pausedDetails?.metadata.location) { + const source11 = this._userSources.get(pausedDetails.metadata.location.file); + if (source11) + return source11.id; + } + } + userSources() { + return [...this._userSources.values()]; + } + callLog() { + return this._callLogs; + } + async _scopeHighlightedSelectorToFrame(frame) { + if (!this._highlightedElement.selector) + return; + try { + const mainFrame = frame._page.mainFrame(); + const resolved = await mainFrame.selectors.resolveFrameForSelector(this._highlightedElement.selector); + if (!resolved) + return ""; + if (resolved?.frame === mainFrame) + return stringifySelector(resolved.info.parsed); + if (resolved?.frame === frame) + return stringifySelector(resolved.info.parsed); + return ""; + } catch { + return ""; + } + } + async _refreshOverlay() { + await Promise.all(this._context.pages().map( + (page) => page.safeNonStallingEvaluateInAllFrames("window.__pw_refreshOverlay()", "main") + )); + } + async onBeforeCall(sdkObject, metadata) { + if (this._omitCallTracking || this._isRecording()) + return; + this._currentCallsMetadata.set(metadata, sdkObject); + this._updateUserSources(); + this._updateCallLog([metadata]); + if (isScreenshotCommand(metadata)) + this.hideHighlightedSelector(); + else if (metadata.params && metadata.params.selector) + this._highlightedElement = { selector: metadata.params.selector }; + } + async onAfterCall(sdkObject, metadata) { + this._actionPoints.delete(metadata.id); + if (this._omitCallTracking || this._isRecording()) + return; + if (!metadata.error) + this._currentCallsMetadata.delete(metadata); + this._updateUserSources(); + this._updateCallLog([metadata]); + } + async onBeforeInputAction(sdkObject, metadata, point) { + if (point) + this._actionPoints.set(metadata.id, point); + } + _updateUserSources() { + for (const source11 of this._userSources.values()) { + source11.highlight = []; + source11.revealLine = void 0; + } + for (const metadata of this._currentCallsMetadata.keys()) { + if (!metadata.location) + continue; + const { file, line } = metadata.location; + let source11 = this._userSources.get(file); + if (!source11) { + source11 = { isRecorded: false, label: file, id: file, text: this._readSource(file), highlight: [], language: languageForFile(file) }; + this._userSources.set(file, source11); + } + if (line) { + const paused = this._debugger.isPaused(metadata); + source11.highlight.push({ line, type: metadata.error ? "error" : paused ? "paused" : "running" }); + source11.revealLine = line; + } + } + this.emit(RecorderEvent.UserSourcesChanged, this.userSources(), this.pausedSourceId()); + } + async onCallLog(sdkObject, metadata, logName, message) { + this._updateCallLog([metadata]); + } + _updateCallLog(metadatas) { + if (this._isRecording()) + return; + const logs = []; + for (const metadata of metadatas) { + if (!metadata.method || metadata.internal) + continue; + let status = "done"; + if (this._currentCallsMetadata.has(metadata)) + status = "in-progress"; + if (this._debugger.isPaused(metadata)) + status = "paused"; + logs.push(metadataToCallLog(metadata, status)); + } + this._callLogs = logs; + this.emit(RecorderEvent.CallLogsUpdated, logs); + } + _isRecording() { + return ["recording", "assertingText", "assertingVisibility", "assertingValue", "assertingSnapshot"].includes(this._mode); + } + _readSource(fileName) { + try { + return import_fs22.default.readFileSync(fileName, "utf-8"); + } catch (e) { + return "// No source available"; + } + } + _setEnabled(enabled) { + this._enabled = enabled; + } + async _onPage(page) { + const frame = page.mainFrame(); + page.on(Page.Events.Close, () => { + this._signalProcessor.addAction({ + frame: this._describeMainFrame(page), + action: { + name: "closePage", + signals: [] + }, + startTime: monotonicTime() + }); + this._pageAliases.delete(page); + this._filePrimaryURLChanged(); + }); + frame.on(Frame.Events.InternalNavigation, (event) => { + if (event.isPublic && !event.error) { + this._onFrameNavigated(frame, page); + this._filePrimaryURLChanged(); + } + }); + page.on(Page.Events.Download, () => this._onDownload(page)); + const suffix = this._pageAliases.size ? String(++this._lastPopupOrdinal) : ""; + const pageAlias = "page" + suffix; + this._pageAliases.set(page, pageAlias); + if (page.opener()) { + this._onPopup(page.opener(), page); + } else { + this._signalProcessor.addAction({ + frame: this._describeMainFrame(page), + action: { + name: "openPage", + url: page.mainFrame().url(), + signals: [] + }, + startTime: monotonicTime() + }); + } + this._filePrimaryURLChanged(); + } + _filePrimaryURLChanged() { + const page = this._context.pages()[0]; + this.emit(RecorderEvent.PageNavigated, page?.mainFrame().url()); + } + clear() { + if (this._params.mode === "recording") { + for (const page of this._context.pages()) + this._onFrameNavigated(page.mainFrame(), page); + } + } + _describeMainFrame(page) { + return { + pageGuid: page.guid, + pageAlias: this._pageAliases.get(page), + framePath: [] + }; + } + async _describeFrame(progress2, frame) { + return { + pageGuid: frame._page.guid, + pageAlias: this._pageAliases.get(frame._page), + framePath: await generateFrameSelector(progress2, frame) + }; + } + _testIdAttributeName() { + return this._params.testIdAttributeName || this._context.selectors().testIdAttributeName() || "data-testid"; + } + async _createActionInContext(progress2, frame, action) { + const frameDescription = await this._describeFrame(progress2, frame); + const actionInContext = { + frame: frameDescription, + action, + description: void 0, + startTime: monotonicTime() + }; + return actionInContext; + } + async _performAction(progress2, frame, action) { + const actionInContext = await this._createActionInContext(progress2, frame, action); + this._signalProcessor.addAction(actionInContext); + try { + if (actionInContext.action.name !== "openPage" && actionInContext.action.name !== "closePage") + await performAction(progress2, this._pageAliases, actionInContext); + } finally { + actionInContext.endTime = monotonicTime(); + } + } + async _recordAction(progress2, frame, action) { + const actionInContext = await this._createActionInContext(progress2, frame, action); + this._signalProcessor.addAction(actionInContext); + } + _onFrameNavigated(frame, page) { + const pageAlias = this._pageAliases.get(page); + this._signalProcessor.signal(pageAlias, frame, { name: "navigation", url: frame.url() }); + } + _onPopup(page, popup) { + const pageAlias = this._pageAliases.get(page); + const popupAlias = this._pageAliases.get(popup); + this._signalProcessor.signal(pageAlias, page.mainFrame(), { name: "popup", popupAlias }); + } + _onDownload(page) { + const pageAlias = this._pageAliases.get(page); + ++this._lastDownloadOrdinal; + this._signalProcessor.signal(pageAlias, page.mainFrame(), { name: "download", downloadAlias: this._lastDownloadOrdinal ? String(this._lastDownloadOrdinal) : "" }); + } + _onDialog(page) { + const pageAlias = this._pageAliases.get(page); + ++this._lastDialogOrdinal; + this._signalProcessor.signal(pageAlias, page.mainFrame(), { name: "dialog", dialogAlias: this._lastDialogOrdinal ? String(this._lastDialogOrdinal) : "" }); + } + }; + } +}); + +// packages/utils/pipeTransport.ts +var PipeTransport; +var init_pipeTransport = __esm({ + "packages/utils/pipeTransport.ts"() { + "use strict"; + init_task(); + PipeTransport = class { + constructor(pipeWrite, pipeRead, closeable, endian = "le") { + this._data = Buffer.from([]); + this._waitForNextTask = makeWaitForNextTask(); + this._closed = false; + this._bytesLeft = 0; + this._pipeWrite = pipeWrite; + this._endian = endian; + this._closeableStream = closeable; + pipeRead.on("data", (buffer) => this._dispatch(buffer)); + pipeRead.on("close", () => { + this._closed = true; + if (this.onclose) + this.onclose(); + }); + this.onmessage = void 0; + this.onclose = void 0; + } + send(message) { + if (this._closed) + throw new Error("Pipe has been closed"); + const data = Buffer.from(message, "utf-8"); + const dataLength = Buffer.alloc(4); + if (this._endian === "be") + dataLength.writeUInt32BE(data.length, 0); + else + dataLength.writeUInt32LE(data.length, 0); + this._pipeWrite.write(dataLength); + this._pipeWrite.write(data); + } + close() { + this._closeableStream.close(); + } + _dispatch(buffer) { + this._data = Buffer.concat([this._data, buffer]); + while (true) { + if (!this._bytesLeft && this._data.length < 4) { + break; + } + if (!this._bytesLeft) { + this._bytesLeft = this._endian === "be" ? this._data.readUInt32BE(0) : this._data.readUInt32LE(0); + this._data = this._data.slice(4); + } + if (!this._bytesLeft || this._data.length < this._bytesLeft) { + break; + } + const message = this._data.slice(0, this._bytesLeft); + this._data = this._data.slice(this._bytesLeft); + this._bytesLeft = 0; + this._waitForNextTask(() => { + if (this.onmessage) + this.onmessage(message.toString("utf-8")); + }); + } + } + }; + } +}); + +// packages/playwright-core/src/server/chromium/chromiumSwitches.ts +var disabledFeatures, chromiumSwitches; +var init_chromiumSwitches = __esm({ + "packages/playwright-core/src/server/chromium/chromiumSwitches.ts"() { + "use strict"; + disabledFeatures = [ + // See https://github.com/microsoft/playwright/issues/14047 + "AvoidUnnecessaryBeforeUnloadCheckSync", + // See https://github.com/microsoft/playwright/issues/38568 + "BoundaryEventDispatchTracksNodeRemoval", + "DestroyProfileOnBrowserClose", + // See https://github.com/microsoft/playwright/pull/13854 + "DialMediaRouteProvider", + "GlobalMediaControls", + // See https://github.com/microsoft/playwright/pull/27605 + "HttpsUpgrades", + // Hides the Lens feature in the URL address bar. Its not working in unofficial builds. + "LensOverlay", + // See https://github.com/microsoft/playwright/pull/8162 + "MediaRouter", + // See https://github.com/microsoft/playwright/issues/28023 + "PaintHolding", + // See https://github.com/microsoft/playwright/issues/32230 + "ThirdPartyStoragePartitioning", + // See https://github.com/microsoft/playwright/issues/16126 + "Translate", + // See https://issues.chromium.org/u/1/issues/435410220 + "AutoDeElevate", + // See https://github.com/microsoft/playwright/issues/37714 + "RenderDocument", + // Prevents downloading optimization hints on startup. + "OptimizationHints", + // Disables forced sign-in in Edge. + "msForceBrowserSignIn", + // Disables updating the preferred version in LaunchServices preferences on mac. + "msEdgeUpdateLaunchServicesPreferredVersion" + ].filter(Boolean); + chromiumSwitches = (options) => [ + "--disable-field-trial-config", + // https://source.chromium.org/chromium/chromium/src/+/main:testing/variations/README.md + "--disable-background-networking", + "--disable-background-timer-throttling", + "--disable-backgrounding-occluded-windows", + "--disable-back-forward-cache", + // Avoids surprises like main request not being intercepted during page.goBack(). + "--disable-breakpad", + "--disable-client-side-phishing-detection", + "--disable-component-extensions-with-background-pages", + "--disable-component-update", + // Avoids unneeded network activity after startup. + "--no-default-browser-check", + "--disable-default-apps", + "--disable-dev-shm-usage", + "--disable-edgeupdater", + // Disables Edge-specific updater on mac. + "--disable-extensions", + "--disable-features=" + disabledFeatures.join(","), + process.env.PLAYWRIGHT_LEGACY_SCREENSHOT ? "" : "--enable-features=CDPScreenshotNewSurface", + "--allow-pre-commit-input", + "--disable-hang-monitor", + "--disable-ipc-flooding-protection", + "--disable-popup-blocking", + "--disable-prompt-on-repost", + "--disable-renderer-backgrounding", + "--force-color-profile=srgb", + "--metrics-recording-only", + "--no-first-run", + "--password-store=basic", + "--use-mock-keychain", + // See https://chromium-review.googlesource.com/c/chromium/src/+/2436773 + "--no-service-autorun", + "--export-tagged-pdf", + // https://chromium-review.googlesource.com/c/chromium/src/+/4853540 + "--disable-search-engine-choice-screen", + // https://issues.chromium.org/41491762 + "--unsafely-disable-devtools-self-xss-warnings", + // Edge can potentially restart on Windows (msRelaunchNoCompatLayer) which looses its file descriptors (stdout/stderr) and CDP (3/4). Disable until fixed upstream. + "--edge-skip-compat-layer-relaunch", + // This disables Chrome for Testing infobar that is visible in the persistent context. + // The switch is ignored everywhere else, including Chromium/Chrome/Edge. + "--disable-infobars", + // Less annoying popups. + "--disable-search-engine-choice-screen", + // Prevents the "three dots" menu crash in IdentityManager::HasPrimaryAccount for ephemeral contexts. + options?.android ? "" : "--disable-sync" + ].filter(Boolean); + } +}); + +// packages/playwright-core/src/server/chromium/crConnection.ts +var ConnectionEvents, kBrowserCloseMessageId, CRConnection, CRSession, CDPSession; +var init_crConnection = __esm({ + "packages/playwright-core/src/server/chromium/crConnection.ts"() { + "use strict"; + init_debugLogger(); + init_assert(); + init_eventsHelper(); + init_helper(); + init_protocolError(); + init_instrumentation(); + ConnectionEvents = { + Disconnected: Symbol("ConnectionEvents.Disconnected") + }; + kBrowserCloseMessageId = -9999; + CRConnection = class extends SdkObject { + constructor(parent, transport, protocolLogger, browserLogsCollector) { + super(parent, "cr-connection"); + this._lastId = 0; + this._sessions = /* @__PURE__ */ new Map(); + this._closed = false; + this.setMaxListeners(0); + this._transport = transport; + this._protocolLogger = protocolLogger; + this._browserLogsCollector = browserLogsCollector; + this.rootSession = new CRSession(this, null, ""); + this._sessions.set("", this.rootSession); + this._transport.onmessage = this._onMessage.bind(this); + this._transport.onclose = this._onClose.bind(this); + } + _rawSend(sessionId, method, params2) { + const id = ++this._lastId; + const message = { id, method, params: params2 }; + if (sessionId) + message.sessionId = sessionId; + this._protocolLogger("send", message); + this._transport.send(message); + return id; + } + async _onMessage(message) { + this._protocolLogger("receive", message); + if (message.id === kBrowserCloseMessageId) + return; + const session2 = this._sessions.get(message.sessionId || ""); + if (session2) + session2._onMessage(message); + } + _onClose(reason) { + this._closed = true; + this._transport.onmessage = void 0; + this._transport.onclose = void 0; + this._browserDisconnectedLogs = helper.formatBrowserLogs(this._browserLogsCollector.recentLogs(), reason); + this.rootSession.dispose(); + Promise.resolve().then(() => this.emit(ConnectionEvents.Disconnected)); + } + close() { + if (!this._closed) + this._transport.close(); + } + async createBrowserSession() { + const { sessionId } = await this.rootSession.send("Target.attachToBrowserTarget"); + return new CDPSession(this.rootSession, sessionId); + } + }; + CRSession = class _CRSession extends SdkObject { + constructor(connection, parentSession, sessionId, eventListener) { + super(connection, "cr-session"); + this._callbacks = /* @__PURE__ */ new Map(); + this._crashed = false; + this._closed = false; + this.setMaxListeners(0); + this._connection = connection; + this._parentSession = parentSession; + this._sessionId = sessionId; + this._eventListener = eventListener; + } + _markAsCrashed() { + this._crashed = true; + } + createChildSession(sessionId, eventListener) { + const session2 = new _CRSession(this._connection, this, sessionId, eventListener); + this._connection._sessions.set(sessionId, session2); + return session2; + } + async send(method, params2) { + if (this._crashed || this._closed || this._connection._closed || this._connection._browserDisconnectedLogs) + throw new ProtocolError(this._crashed ? "crashed" : "closed", void 0, this._connection._browserDisconnectedLogs); + const id = this._connection._rawSend(this._sessionId, method, params2); + return new Promise((resolve, reject) => { + this._callbacks.set(id, { resolve, reject, error: new ProtocolError("error", method) }); + }); + } + _sendMayFail(method, params2) { + return this.send(method, params2).catch((error) => debugLogger.log("error", error)); + } + _onMessage(object) { + if (object.id && this._callbacks.has(object.id)) { + const callback = this._callbacks.get(object.id); + this._callbacks.delete(object.id); + if (object.error) { + callback.error.setMessage(object.error.message); + callback.reject(callback.error); + } else { + callback.resolve(object.result); + } + } else if (object.id && object.error?.code === -32001) { + } else { + assert(!object.id, object?.error?.message || void 0); + Promise.resolve().then(() => { + if (this._eventListener) + this._eventListener(object.method, object.params); + this.emit(object.method, object.params); + }); + } + } + async detach() { + if (this._closed) + throw new Error(`Session already detached. Most likely the page has been closed.`); + if (!this._parentSession) + throw new Error("Root session cannot be closed"); + await this._sendMayFail("Runtime.runIfWaitingForDebugger"); + await this._parentSession.send("Target.detachFromTarget", { sessionId: this._sessionId }); + this.dispose(); + } + dispose() { + this._closed = true; + this._connection._sessions.delete(this._sessionId); + for (const callback of this._callbacks.values()) { + callback.error.setMessage(`Internal server error, session closed.`); + callback.error.type = this._crashed ? "crashed" : "closed"; + callback.error.logs = this._connection._browserDisconnectedLogs; + callback.reject(callback.error); + } + this._callbacks.clear(); + } + }; + CDPSession = class _CDPSession extends SdkObject { + constructor(parentSession, sessionId) { + super(parentSession, "cdp-session"); + this._listeners = []; + this._session = parentSession.createChildSession(sessionId, (method, params2) => this.emit(_CDPSession.Events.Event, { method, params: params2 })); + this._listeners = [eventsHelper.addEventListener(parentSession, "Target.detachedFromTarget", (event) => { + if (event.sessionId === sessionId) + this._onClose(); + })]; + } + static { + this.Events = { + Event: "event", + Closed: "close" + }; + } + async send(progress2, method, params2) { + return await progress2.race(this._send(method, params2)); + } + async detach(progress2) { + return await progress2.race(this._session.detach()); + } + async _send(method, params2) { + return await this._session.send(method, params2); + } + async attachToTarget(targetId) { + const { sessionId } = await this._send("Target.attachToTarget", { targetId, flatten: true }); + return new _CDPSession(this._session, sessionId); + } + _onClose() { + eventsHelper.removeEventListeners(this._listeners); + this._session.dispose(); + this.emit(_CDPSession.Events.Closed); + } + }; + } +}); + +// packages/playwright-core/src/server/chromium/crCoverage.ts +function convertToDisjointRanges(nestedRanges) { + const points = []; + for (const range of nestedRanges) { + points.push({ offset: range.startOffset, type: 0, range }); + points.push({ offset: range.endOffset, type: 1, range }); + } + points.sort((a, b) => { + if (a.offset !== b.offset) + return a.offset - b.offset; + if (a.type !== b.type) + return b.type - a.type; + const aLength = a.range.endOffset - a.range.startOffset; + const bLength = b.range.endOffset - b.range.startOffset; + if (a.type === 0) + return bLength - aLength; + return aLength - bLength; + }); + const hitCountStack = []; + const results = []; + let lastOffset = 0; + for (const point of points) { + if (hitCountStack.length && lastOffset < point.offset && hitCountStack[hitCountStack.length - 1] > 0) { + const lastResult = results.length ? results[results.length - 1] : null; + if (lastResult && lastResult.end === lastOffset) + lastResult.end = point.offset; + else + results.push({ start: lastOffset, end: point.offset }); + } + lastOffset = point.offset; + if (point.type === 0) + hitCountStack.push(point.range.count); + else + hitCountStack.pop(); + } + return results.filter((range) => range.end - range.start > 1); +} +var CRCoverage, JSCoverage, CSSCoverage; +var init_crCoverage = __esm({ + "packages/playwright-core/src/server/chromium/crCoverage.ts"() { + "use strict"; + init_eventsHelper(); + init_assert(); + init_progress(); + CRCoverage = class { + constructor(client) { + this._jsCoverage = new JSCoverage(client); + this._cssCoverage = new CSSCoverage(client); + } + async startJSCoverage(progress2, options) { + await raceUncancellableOperationWithCleanup(progress2, () => this._jsCoverage.start(options), () => this._jsCoverage.stop()); + } + async stopJSCoverage() { + return await this._jsCoverage.stop(); + } + async startCSSCoverage(progress2, options) { + await raceUncancellableOperationWithCleanup(progress2, () => this._cssCoverage.start(options), () => this._cssCoverage.stop()); + } + async stopCSSCoverage() { + return await this._cssCoverage.stop(); + } + }; + JSCoverage = class { + constructor(client) { + this._reportAnonymousScripts = false; + this._client = client; + this._enabled = false; + this._scriptIds = /* @__PURE__ */ new Set(); + this._scriptSources = /* @__PURE__ */ new Map(); + this._eventListeners = []; + this._resetOnNavigation = false; + } + async start(options) { + assert(!this._enabled, "JSCoverage is already enabled"); + const { + resetOnNavigation = true, + reportAnonymousScripts = false + } = options; + this._resetOnNavigation = resetOnNavigation; + this._reportAnonymousScripts = reportAnonymousScripts; + this._enabled = true; + this._scriptIds.clear(); + this._scriptSources.clear(); + this._eventListeners = [ + eventsHelper.addEventListener(this._client, "Debugger.scriptParsed", this._onScriptParsed.bind(this)), + eventsHelper.addEventListener(this._client, "Runtime.executionContextsCleared", this._onExecutionContextsCleared.bind(this)), + eventsHelper.addEventListener(this._client, "Debugger.paused", this._onDebuggerPaused.bind(this)) + ]; + await Promise.all([ + this._client.send("Profiler.enable"), + this._client.send("Profiler.startPreciseCoverage", { callCount: true, detailed: true }), + this._client.send("Debugger.enable"), + this._client.send("Debugger.setSkipAllPauses", { skip: true }) + ]); + } + _onDebuggerPaused() { + this._client.send("Debugger.resume"); + } + _onExecutionContextsCleared() { + if (!this._resetOnNavigation) + return; + this._scriptIds.clear(); + this._scriptSources.clear(); + } + async _onScriptParsed(event) { + this._scriptIds.add(event.scriptId); + if (!event.url && !this._reportAnonymousScripts) + return; + const response2 = await this._client._sendMayFail("Debugger.getScriptSource", { scriptId: event.scriptId }); + if (response2) + this._scriptSources.set(event.scriptId, response2.scriptSource); + } + async stop() { + if (!this._enabled) + return { entries: [] }; + const [profileResponse] = await Promise.all([ + this._client.send("Profiler.takePreciseCoverage"), + this._client.send("Profiler.stopPreciseCoverage"), + this._client.send("Profiler.disable"), + this._client.send("Debugger.disable") + ]); + eventsHelper.removeEventListeners(this._eventListeners); + this._enabled = false; + const coverage = { entries: [] }; + for (const entry of profileResponse.result) { + if (!this._scriptIds.has(entry.scriptId)) + continue; + if (!entry.url && !this._reportAnonymousScripts) + continue; + const source11 = this._scriptSources.get(entry.scriptId); + if (source11) + coverage.entries.push({ ...entry, source: source11 }); + else + coverage.entries.push(entry); + } + return coverage; + } + }; + CSSCoverage = class { + constructor(client) { + this._client = client; + this._enabled = false; + this._stylesheetURLs = /* @__PURE__ */ new Map(); + this._stylesheetSources = /* @__PURE__ */ new Map(); + this._eventListeners = []; + this._resetOnNavigation = false; + } + async start(options) { + assert(!this._enabled, "CSSCoverage is already enabled"); + const { resetOnNavigation = true } = options; + this._resetOnNavigation = resetOnNavigation; + this._enabled = true; + this._stylesheetURLs.clear(); + this._stylesheetSources.clear(); + this._eventListeners = [ + eventsHelper.addEventListener(this._client, "CSS.styleSheetAdded", this._onStyleSheet.bind(this)), + eventsHelper.addEventListener(this._client, "Runtime.executionContextsCleared", this._onExecutionContextsCleared.bind(this)) + ]; + await Promise.all([ + this._client.send("DOM.enable"), + this._client.send("CSS.enable"), + this._client.send("CSS.startRuleUsageTracking") + ]); + } + _onExecutionContextsCleared() { + if (!this._resetOnNavigation) + return; + this._stylesheetURLs.clear(); + this._stylesheetSources.clear(); + } + async _onStyleSheet(event) { + const header = event.header; + if (!header.sourceURL) + return; + const response2 = await this._client._sendMayFail("CSS.getStyleSheetText", { styleSheetId: header.styleSheetId }); + if (response2) { + this._stylesheetURLs.set(header.styleSheetId, header.sourceURL); + this._stylesheetSources.set(header.styleSheetId, response2.text); + } + } + async stop() { + if (!this._enabled) + return { entries: [] }; + const ruleTrackingResponse = await this._client.send("CSS.stopRuleUsageTracking"); + await Promise.all([ + this._client.send("CSS.disable"), + this._client.send("DOM.disable") + ]); + eventsHelper.removeEventListeners(this._eventListeners); + this._enabled = false; + const styleSheetIdToCoverage = /* @__PURE__ */ new Map(); + for (const entry of ruleTrackingResponse.ruleUsage) { + let ranges = styleSheetIdToCoverage.get(entry.styleSheetId); + if (!ranges) { + ranges = []; + styleSheetIdToCoverage.set(entry.styleSheetId, ranges); + } + ranges.push({ + startOffset: entry.startOffset, + endOffset: entry.endOffset, + count: entry.used ? 1 : 0 + }); + } + const coverage = { entries: [] }; + for (const styleSheetId of this._stylesheetURLs.keys()) { + const url2 = this._stylesheetURLs.get(styleSheetId); + const text2 = this._stylesheetSources.get(styleSheetId); + const ranges = convertToDisjointRanges(styleSheetIdToCoverage.get(styleSheetId) || []); + coverage.entries.push({ url: url2, ranges, text: text2 }); + } + return coverage; + } + }; + } +}); + +// packages/playwright-core/src/server/chromium/crProtocolHelper.ts +function getExceptionMessage(exceptionDetails) { + if (exceptionDetails.exception) + return exceptionDetails.exception.description || String(exceptionDetails.exception.value); + let message = exceptionDetails.text; + if (exceptionDetails.stackTrace) { + for (const callframe of exceptionDetails.stackTrace.callFrames) { + const location2 = callframe.url + ":" + callframe.lineNumber + ":" + callframe.columnNumber; + const functionName = callframe.functionName || ""; + message += ` + at ${functionName} (${location2})`; + } + } + return message; +} +async function releaseObject(client, objectId) { + await client.send("Runtime.releaseObject", { objectId }).catch((error) => { + }); +} +async function saveProtocolStream(client, handle, path59) { + let eof = false; + await mkdirIfNeeded(path59); + const fd = await import_fs23.default.promises.open(path59, "w"); + try { + while (!eof) { + const response2 = await client.send("IO.read", { handle }); + eof = response2.eof; + const buf = Buffer.from(response2.data, response2.base64Encoded ? "base64" : void 0); + await fd.write(buf); + } + } finally { + await fd.close().catch(() => { + }); + } + await client.send("IO.close", { handle }); +} +async function readProtocolStream(client, handle) { + let eof = false; + const chunks = []; + while (!eof) { + const response2 = await client.send("IO.read", { handle }); + eof = response2.eof; + const buf = Buffer.from(response2.data, response2.base64Encoded ? "base64" : void 0); + chunks.push(buf); + } + await client.send("IO.close", { handle }); + return Buffer.concat(chunks); +} +function stackTraceToLocation(stackTrace) { + return stackTrace && stackTrace.callFrames.length ? { + url: stackTrace.callFrames[0].url, + lineNumber: stackTrace.callFrames[0].lineNumber, + columnNumber: stackTrace.callFrames[0].columnNumber + } : { url: "", lineNumber: 0, columnNumber: 0 }; +} +function exceptionToError(exceptionDetails) { + const messageWithStack = getExceptionMessage(exceptionDetails); + const lines = messageWithStack.split("\n"); + const firstStackTraceLine = lines.findIndex((line) => line.startsWith(" at")); + let messageWithName = ""; + let stack = ""; + if (firstStackTraceLine === -1) { + messageWithName = messageWithStack; + } else { + messageWithName = lines.slice(0, firstStackTraceLine).join("\n"); + stack = messageWithStack; + } + const { name, message } = splitErrorMessage(messageWithName); + const err = new Error(message); + err.stack = stack; + const nameOverride = exceptionDetails.exception?.preview?.properties.find((o) => o.name === "name"); + err.name = nameOverride ? nameOverride.value ?? "Error" : name; + return err; +} +function toModifiersMask(modifiers) { + let mask = 0; + if (modifiers.has("Alt")) + mask |= 1; + if (modifiers.has("Control")) + mask |= 2; + if (modifiers.has("Meta")) + mask |= 4; + if (modifiers.has("Shift")) + mask |= 8; + return mask; +} +function toButtonsMask(buttons) { + let mask = 0; + if (buttons.has("left")) + mask |= 1; + if (buttons.has("right")) + mask |= 2; + if (buttons.has("middle")) + mask |= 4; + return mask; +} +var import_fs23; +var init_crProtocolHelper = __esm({ + "packages/playwright-core/src/server/chromium/crProtocolHelper.ts"() { + "use strict"; + import_fs23 = __toESM(require("fs")); + init_stackTrace(); + init_fileUtils(); + } +}); + +// packages/playwright-core/src/server/chromium/crDragDrop.ts +var DragManager; +var init_crDragDrop = __esm({ + "packages/playwright-core/src/server/chromium/crDragDrop.ts"() { + "use strict"; + init_assert(); + init_crProtocolHelper(); + DragManager = class { + constructor(page) { + this._dragState = null; + this._lastPosition = { x: 0, y: 0 }; + this._crPage = page; + } + async cancelDrag() { + if (!this._dragState) + return false; + await this._crPage._mainFrameSession._client.send("Input.dispatchDragEvent", { + type: "dragCancel", + x: this._lastPosition.x, + y: this._lastPosition.y, + data: { + items: [], + dragOperationsMask: 65535 + } + }); + this._dragState = null; + return true; + } + async interceptDragCausedByMove(progress2, x, y, button, buttons, modifiers, moveCallback) { + this._lastPosition = { x, y }; + if (this._dragState) { + await progress2.race(this._crPage._mainFrameSession._client.send("Input.dispatchDragEvent", { + type: "dragOver", + x, + y, + data: this._dragState, + modifiers: toModifiersMask(modifiers) + })); + return; + } + if (button !== "left") + return moveCallback(progress2); + const client = this._crPage._mainFrameSession._client; + let onDragIntercepted; + const dragInterceptedPromise = new Promise((x2) => onDragIntercepted = x2); + function setupDragListeners() { + let didStartDrag = Promise.resolve(false); + let dragEvent = null; + const dragListener = (event) => dragEvent = event; + const mouseListener = () => { + didStartDrag = new Promise((callback) => { + window.addEventListener("dragstart", dragListener, { once: true, capture: true }); + setTimeout(() => callback(dragEvent ? !dragEvent.defaultPrevented : false), 0); + }); + }; + window.addEventListener("mousemove", mouseListener, { once: true, capture: true }); + window.__cleanupDrag = async () => { + const val = await didStartDrag; + window.removeEventListener("mousemove", mouseListener, { capture: true }); + window.removeEventListener("dragstart", dragListener, { capture: true }); + delete window.__cleanupDrag; + return val; + }; + } + try { + let expectingDrag = false; + await progress2.race(this._crPage._page.safeNonStallingEvaluateInAllFrames(`(${setupDragListeners.toString()})()`, "utility")); + client.on("Input.dragIntercepted", onDragIntercepted); + await progress2.race(client.send("Input.setInterceptDrags", { enabled: true })); + try { + await moveCallback(progress2); + expectingDrag = (await progress2.race(Promise.all(this._crPage._page.frames().map(async (frame) => { + return frame.nonStallingEvaluateInExistingContext("window.__cleanupDrag?.()", "utility").catch(() => false); + })))).some((x2) => x2); + } finally { + client.off("Input.dragIntercepted", onDragIntercepted); + await progress2.race(client.send("Input.setInterceptDrags", { enabled: false })); + } + this._dragState = expectingDrag ? (await dragInterceptedPromise).data : null; + } catch (error) { + this._crPage._page.safeNonStallingEvaluateInAllFrames("window.__cleanupDrag?.()", "utility").catch(() => { + }); + throw error; + } + if (this._dragState) { + await progress2.race(this._crPage._mainFrameSession._client.send("Input.dispatchDragEvent", { + type: "dragEnter", + x, + y, + data: this._dragState, + modifiers: toModifiersMask(modifiers) + })); + } + } + isDragging() { + return !!this._dragState; + } + async drop(progress2, x, y, modifiers) { + assert(this._dragState, "missing drag state"); + await progress2.race(this._crPage._mainFrameSession._client.send("Input.dispatchDragEvent", { + type: "drop", + x, + y, + data: this._dragState, + modifiers: toModifiersMask(modifiers) + })); + this._dragState = null; + } + }; + } +}); + +// packages/playwright-core/src/server/chromium/crExecutionContext.ts +function rewriteError(error) { + if (error.message.includes("Object reference chain is too long") || error.message.includes("CBOR: stack limit exceeded")) + throw new Error("Cannot serialize result: object reference chain is too long."); + if (error.message.includes("Object couldn't be returned by value")) + return { result: { type: "undefined" } }; + if (error instanceof TypeError && error.message.startsWith("Converting circular structure to JSON")) + rewriteErrorMessage(error, error.message + " Are you passing a nested JSHandle?"); + if (!isJavaScriptErrorInEvaluate(error) && !isSessionClosedError(error)) + throw new Error("Execution context was destroyed, most likely because of a navigation."); + throw error; +} +function potentiallyUnserializableValue(remoteObject) { + const value2 = remoteObject.value; + const unserializableValue = remoteObject.unserializableValue; + return unserializableValue ? parseUnserializableValue(unserializableValue) : value2; +} +function renderPreview(object) { + if (object.type === "undefined") + return "undefined"; + if ("value" in object) + return String(object.value); + if (object.unserializableValue) + return String(object.unserializableValue); + if (object.description === "Object" && object.preview) { + const tokens = []; + for (const { name, value: value2 } of object.preview.properties) + tokens.push(`${name}: ${value2}`); + return `{${tokens.join(", ")}}`; + } + if (object.subtype === "array" && object.preview) + return sparseArrayToString(object.preview.properties); + return object.description; +} +function createHandle(context2, remoteObject) { + if (remoteObject.subtype === "node") { + assert(context2 instanceof FrameExecutionContext); + return new ElementHandle(context2, remoteObject.objectId); + } + return new JSHandle(context2, remoteObject.subtype || remoteObject.type, renderPreview(remoteObject), remoteObject.objectId, potentiallyUnserializableValue(remoteObject)); +} +var CRExecutionContext; +var init_crExecutionContext = __esm({ + "packages/playwright-core/src/server/chromium/crExecutionContext.ts"() { + "use strict"; + init_assert(); + init_stackTrace(); + init_utilityScriptSerializers(); + init_crProtocolHelper(); + init_javascript(); + init_dom(); + init_protocolError(); + CRExecutionContext = class { + constructor(client, contextPayload) { + this._client = client; + this._contextId = contextPayload.id; + } + async rawEvaluateJSON(expression2) { + const { exceptionDetails, result: remoteObject } = await this._client.send("Runtime.evaluate", { + expression: expression2, + contextId: this._contextId, + returnByValue: true + }).catch(rewriteError); + if (exceptionDetails) + throw new JavaScriptErrorInEvaluate(getExceptionMessage(exceptionDetails)); + return remoteObject.value; + } + async rawEvaluateHandle(context2, expression2) { + const { exceptionDetails, result: remoteObject } = await this._client.send("Runtime.evaluate", { + expression: expression2, + contextId: this._contextId + }).catch(rewriteError); + if (exceptionDetails) + throw new JavaScriptErrorInEvaluate(getExceptionMessage(exceptionDetails)); + return createHandle(context2, remoteObject); + } + async evaluateWithArguments(expression2, returnByValue, utilityScript, values, handles) { + const { exceptionDetails, result: remoteObject } = await this._client.send("Runtime.callFunctionOn", { + functionDeclaration: expression2, + objectId: utilityScript._objectId, + arguments: [ + { objectId: utilityScript._objectId }, + ...values.map((value2) => ({ value: value2 })), + ...handles.map((handle) => ({ objectId: handle._objectId })) + ], + returnByValue, + awaitPromise: true, + userGesture: true + }).catch(rewriteError); + if (exceptionDetails) + throw new JavaScriptErrorInEvaluate(getExceptionMessage(exceptionDetails)); + return returnByValue ? parseEvaluationResultValue(remoteObject.value) : createHandle(utilityScript._context, remoteObject); + } + async getProperties(object) { + const response2 = await this._client.send("Runtime.getProperties", { + objectId: object._objectId, + ownProperties: true + }); + const result2 = /* @__PURE__ */ new Map(); + for (const property of response2.result) { + if (!property.enumerable || !property.value) + continue; + result2.set(property.name, createHandle(object._context, property.value)); + } + return result2; + } + async releaseHandle(handle) { + if (!handle._objectId) + return; + await releaseObject(this._client, handle._objectId); + } + shouldPrependErrorPrefix() { + return false; + } + }; + } +}); + +// packages/playwright-core/src/server/macEditingCommands.ts +var macEditingCommands; +var init_macEditingCommands = __esm({ + "packages/playwright-core/src/server/macEditingCommands.ts"() { + "use strict"; + macEditingCommands = { + "Backspace": "deleteBackward:", + "Enter": "insertNewline:", + "NumpadEnter": "insertNewline:", + "Escape": "cancelOperation:", + "ArrowUp": "moveUp:", + "ArrowDown": "moveDown:", + "ArrowLeft": "moveLeft:", + "ArrowRight": "moveRight:", + "F5": "complete:", + "Delete": "deleteForward:", + "Home": "scrollToBeginningOfDocument:", + "End": "scrollToEndOfDocument:", + "PageUp": "scrollPageUp:", + "PageDown": "scrollPageDown:", + "Shift+Backspace": "deleteBackward:", + "Shift+Enter": "insertNewline:", + "Shift+NumpadEnter": "insertNewline:", + "Shift+Escape": "cancelOperation:", + "Shift+ArrowUp": "moveUpAndModifySelection:", + "Shift+ArrowDown": "moveDownAndModifySelection:", + "Shift+ArrowLeft": "moveLeftAndModifySelection:", + "Shift+ArrowRight": "moveRightAndModifySelection:", + "Shift+F5": "complete:", + "Shift+Delete": "deleteForward:", + "Shift+Home": "moveToBeginningOfDocumentAndModifySelection:", + "Shift+End": "moveToEndOfDocumentAndModifySelection:", + "Shift+PageUp": "pageUpAndModifySelection:", + "Shift+PageDown": "pageDownAndModifySelection:", + "Shift+Numpad5": "delete:", + "Control+Tab": "selectNextKeyView:", + "Control+Enter": "insertLineBreak:", + "Control+NumpadEnter": "insertLineBreak:", + "Control+Quote": "insertSingleQuoteIgnoringSubstitution:", + "Control+KeyA": "moveToBeginningOfParagraph:", + "Control+KeyB": "moveBackward:", + "Control+KeyD": "deleteForward:", + "Control+KeyE": "moveToEndOfParagraph:", + "Control+KeyF": "moveForward:", + "Control+KeyH": "deleteBackward:", + "Control+KeyK": "deleteToEndOfParagraph:", + "Control+KeyL": "centerSelectionInVisibleArea:", + "Control+KeyN": "moveDown:", + "Control+KeyO": ["insertNewlineIgnoringFieldEditor:", "moveBackward:"], + "Control+KeyP": "moveUp:", + "Control+KeyT": "transpose:", + "Control+KeyV": "pageDown:", + "Control+KeyY": "yank:", + "Control+Backspace": "deleteBackwardByDecomposingPreviousCharacter:", + "Control+ArrowUp": "scrollPageUp:", + "Control+ArrowDown": "scrollPageDown:", + "Control+ArrowLeft": "moveToLeftEndOfLine:", + "Control+ArrowRight": "moveToRightEndOfLine:", + "Shift+Control+Enter": "insertLineBreak:", + "Shift+Control+NumpadEnter": "insertLineBreak:", + "Shift+Control+Tab": "selectPreviousKeyView:", + "Shift+Control+Quote": "insertDoubleQuoteIgnoringSubstitution:", + "Shift+Control+KeyA": "moveToBeginningOfParagraphAndModifySelection:", + "Shift+Control+KeyB": "moveBackwardAndModifySelection:", + "Shift+Control+KeyE": "moveToEndOfParagraphAndModifySelection:", + "Shift+Control+KeyF": "moveForwardAndModifySelection:", + "Shift+Control+KeyN": "moveDownAndModifySelection:", + "Shift+Control+KeyP": "moveUpAndModifySelection:", + "Shift+Control+KeyV": "pageDownAndModifySelection:", + "Shift+Control+Backspace": "deleteBackwardByDecomposingPreviousCharacter:", + "Shift+Control+ArrowUp": "scrollPageUp:", + "Shift+Control+ArrowDown": "scrollPageDown:", + "Shift+Control+ArrowLeft": "moveToLeftEndOfLineAndModifySelection:", + "Shift+Control+ArrowRight": "moveToRightEndOfLineAndModifySelection:", + "Alt+Backspace": "deleteWordBackward:", + "Alt+Enter": "insertNewlineIgnoringFieldEditor:", + "Alt+NumpadEnter": "insertNewlineIgnoringFieldEditor:", + "Alt+Escape": "complete:", + "Alt+ArrowUp": ["moveBackward:", "moveToBeginningOfParagraph:"], + "Alt+ArrowDown": ["moveForward:", "moveToEndOfParagraph:"], + "Alt+ArrowLeft": "moveWordLeft:", + "Alt+ArrowRight": "moveWordRight:", + "Alt+Delete": "deleteWordForward:", + "Alt+PageUp": "pageUp:", + "Alt+PageDown": "pageDown:", + "Shift+Alt+Backspace": "deleteWordBackward:", + "Shift+Alt+Enter": "insertNewlineIgnoringFieldEditor:", + "Shift+Alt+NumpadEnter": "insertNewlineIgnoringFieldEditor:", + "Shift+Alt+Escape": "complete:", + "Shift+Alt+ArrowUp": "moveParagraphBackwardAndModifySelection:", + "Shift+Alt+ArrowDown": "moveParagraphForwardAndModifySelection:", + "Shift+Alt+ArrowLeft": "moveWordLeftAndModifySelection:", + "Shift+Alt+ArrowRight": "moveWordRightAndModifySelection:", + "Shift+Alt+Delete": "deleteWordForward:", + "Shift+Alt+PageUp": "pageUp:", + "Shift+Alt+PageDown": "pageDown:", + "Control+Alt+KeyB": "moveWordBackward:", + "Control+Alt+KeyF": "moveWordForward:", + "Control+Alt+Backspace": "deleteWordBackward:", + "Shift+Control+Alt+KeyB": "moveWordBackwardAndModifySelection:", + "Shift+Control+Alt+KeyF": "moveWordForwardAndModifySelection:", + "Shift+Control+Alt+Backspace": "deleteWordBackward:", + "Meta+NumpadSubtract": "cancel:", + "Meta+Backspace": "deleteToBeginningOfLine:", + "Meta+ArrowUp": "moveToBeginningOfDocument:", + "Meta+ArrowDown": "moveToEndOfDocument:", + "Meta+ArrowLeft": "moveToLeftEndOfLine:", + "Meta+ArrowRight": "moveToRightEndOfLine:", + "Shift+Meta+NumpadSubtract": "cancel:", + "Shift+Meta+Backspace": "deleteToBeginningOfLine:", + "Shift+Meta+ArrowUp": "moveToBeginningOfDocumentAndModifySelection:", + "Shift+Meta+ArrowDown": "moveToEndOfDocumentAndModifySelection:", + "Shift+Meta+ArrowLeft": "moveToLeftEndOfLineAndModifySelection:", + "Shift+Meta+ArrowRight": "moveToRightEndOfLineAndModifySelection:", + "Meta+KeyA": "selectAll:", + "Meta+KeyC": "copy:", + "Meta+KeyX": "cut:", + "Meta+KeyV": "paste:", + "Meta+KeyZ": "undo:", + "Shift+Meta+KeyZ": "redo:" + }; + } +}); + +// packages/playwright-core/src/server/chromium/crInput.ts +var RawKeyboardImpl, RawMouseImpl, RawTouchscreenImpl; +var init_crInput = __esm({ + "packages/playwright-core/src/server/chromium/crInput.ts"() { + "use strict"; + init_stringUtils(); + init_input(); + init_macEditingCommands(); + init_crProtocolHelper(); + RawKeyboardImpl = class { + constructor(_client, _isMac, _dragManger) { + this._client = _client; + this._isMac = _isMac; + this._dragManger = _dragManger; + } + _commandsForCode(code, modifiers) { + if (!this._isMac) + return []; + const parts = []; + for (const modifier of ["Shift", "Control", "Alt", "Meta"]) { + if (modifiers.has(modifier)) + parts.push(modifier); + } + parts.push(code); + const shortcut = parts.join("+"); + let commands2 = macEditingCommands[shortcut] || []; + if (isString(commands2)) + commands2 = [commands2]; + commands2 = commands2.filter((x) => !x.startsWith("insert")); + return commands2.map((c) => c.substring(0, c.length - 1)); + } + async keydown(progress2, modifiers, keyName, description, autoRepeat) { + const { code, key, location: location2, text: text2 } = description; + if (code === "Escape" && await progress2.race(this._dragManger.cancelDrag())) + return; + const commands2 = this._commandsForCode(code, modifiers); + await progress2.race(this._client.send("Input.dispatchKeyEvent", { + type: text2 ? "keyDown" : "rawKeyDown", + modifiers: toModifiersMask(modifiers), + windowsVirtualKeyCode: description.keyCodeWithoutLocation, + code, + commands: commands2, + key, + text: text2, + unmodifiedText: text2, + autoRepeat, + location: location2, + isKeypad: location2 === keypadLocation2 + })); + } + async keyup(progress2, modifiers, keyName, description) { + const { code, key, location: location2 } = description; + await progress2.race(this._client.send("Input.dispatchKeyEvent", { + type: "keyUp", + modifiers: toModifiersMask(modifiers), + key, + windowsVirtualKeyCode: description.keyCodeWithoutLocation, + code, + location: location2 + })); + } + async sendText(progress2, text2) { + await progress2.race(this._client.send("Input.insertText", { text: text2 })); + } + }; + RawMouseImpl = class { + constructor(page, client, dragManager) { + this._page = page; + this._client = client; + this._dragManager = dragManager; + } + async move(progress2, x, y, button, buttons, modifiers, forClick) { + const actualMove = async (progress3) => { + await progress3.race(this._client.send("Input.dispatchMouseEvent", { + type: "mouseMoved", + button, + buttons: toButtonsMask(buttons), + x, + y, + modifiers: toModifiersMask(modifiers), + force: buttons.size > 0 ? 0.5 : 0 + })); + }; + if (forClick) { + await actualMove(progress2); + return; + } + await this._dragManager.interceptDragCausedByMove(progress2, x, y, button, buttons, modifiers, actualMove); + } + async down(progress2, x, y, button, buttons, modifiers, clickCount) { + if (this._dragManager.isDragging()) + return; + await progress2.race(this._client.send("Input.dispatchMouseEvent", { + type: "mousePressed", + button, + buttons: toButtonsMask(buttons), + x, + y, + modifiers: toModifiersMask(modifiers), + clickCount, + force: buttons.size > 0 ? 0.5 : 0 + })); + } + async up(progress2, x, y, button, buttons, modifiers, clickCount) { + if (this._dragManager.isDragging()) { + await this._dragManager.drop(progress2, x, y, modifiers); + return; + } + await progress2.race(this._client.send("Input.dispatchMouseEvent", { + type: "mouseReleased", + button, + buttons: toButtonsMask(buttons), + x, + y, + modifiers: toModifiersMask(modifiers), + clickCount + })); + } + async wheel(progress2, x, y, buttons, modifiers, deltaX, deltaY) { + await progress2.race(this._client.send("Input.dispatchMouseEvent", { + type: "mouseWheel", + x, + y, + modifiers: toModifiersMask(modifiers), + deltaX, + deltaY + })); + } + }; + RawTouchscreenImpl = class { + constructor(client) { + this._client = client; + } + async tap(progress2, x, y, modifiers) { + await progress2.race(Promise.all([ + this._client.send("Input.dispatchTouchEvent", { + type: "touchStart", + modifiers: toModifiersMask(modifiers), + touchPoints: [{ + x, + y + }] + }), + this._client.send("Input.dispatchTouchEvent", { + type: "touchEnd", + modifiers: toModifiersMask(modifiers), + touchPoints: [] + }) + ])); + } + }; + } +}); + +// packages/playwright-core/src/server/chromium/crNetworkManager.ts +async function catchDisallowedErrors(callback) { + try { + return await callback(); + } catch (e) { + if (isProtocolError(e) && e.message.includes("Invalid http status code or phrase")) + throw e; + if (isProtocolError(e) && e.message.includes("Unsafe header")) + throw e; + } +} +function splitSetCookieHeader(headers) { + const index = headers.findIndex(({ name }) => name.toLowerCase() === "set-cookie"); + if (index === -1) + return headers; + const header = headers[index]; + const values = header.value.split("\n"); + if (values.length === 1) + return headers; + const result2 = headers.slice(); + result2.splice(index, 1, ...values.map((value2) => ({ name: header.name, value: value2 }))); + return result2; +} +function toResourceType(type3) { + switch (type3) { + case "Document": + return "document"; + case "Stylesheet": + return "stylesheet"; + case "Image": + return "image"; + case "Media": + return "media"; + case "Font": + return "font"; + case "Script": + return "script"; + case "TextTrack": + return "texttrack"; + case "XHR": + return "xhr"; + case "Fetch": + return "fetch"; + case "EventSource": + return "eventsource"; + case "WebSocket": + return "websocket"; + case "Manifest": + return "manifest"; + case "Ping": + return "ping"; + case "CSPViolationReport": + return "cspreport"; + case "Prefetch": + case "SignedExchange": + case "Preflight": + case "FedCM": + default: + return "other"; + } +} +var CRNetworkManager, InterceptableRequest, RouteImpl, errorReasons, ResponseExtraInfoTracker; +var init_crNetworkManager = __esm({ + "packages/playwright-core/src/server/chromium/crNetworkManager.ts"() { + "use strict"; + init_eventsHelper(); + init_assert(); + init_headers(); + init_helper(); + init_network2(); + init_protocolError(); + CRNetworkManager = class { + constructor(page, serviceWorker) { + this._requestIdToRequest = /* @__PURE__ */ new Map(); + this._requestIdToRequestWillBeSentEvent = /* @__PURE__ */ new Map(); + this._credentials = null; + this._attemptedAuthentications = /* @__PURE__ */ new Set(); + this._userRequestInterceptionEnabled = false; + this._protocolRequestInterceptionEnabled = false; + this._offline = false; + this._extraHTTPHeaders = []; + this._requestIdToRequestPausedEvent = /* @__PURE__ */ new Map(); + this._responseExtraInfoTracker = new ResponseExtraInfoTracker(); + this._sessions = /* @__PURE__ */ new Map(); + this._timestampBaselineForWebSocket = /* @__PURE__ */ new Map(); + this._page = page; + this._serviceWorker = serviceWorker; + } + async addSession(session2, workerFrame, isMain) { + const sessionInfo = { session: session2, isMain, workerFrame, eventListeners: [] }; + sessionInfo.eventListeners = [ + eventsHelper.addEventListener(session2, "Fetch.requestPaused", this._onRequestPaused.bind(this, sessionInfo)), + eventsHelper.addEventListener(session2, "Fetch.authRequired", this._onAuthRequired.bind(this, sessionInfo)), + eventsHelper.addEventListener(session2, "Network.requestWillBeSent", this._onRequestWillBeSent.bind(this, sessionInfo)), + eventsHelper.addEventListener(session2, "Network.requestWillBeSentExtraInfo", this._onRequestWillBeSentExtraInfo.bind(this)), + eventsHelper.addEventListener(session2, "Network.requestServedFromCache", this._onRequestServedFromCache.bind(this)), + eventsHelper.addEventListener(session2, "Network.responseReceived", this._onResponseReceived.bind(this, sessionInfo)), + eventsHelper.addEventListener(session2, "Network.responseReceivedExtraInfo", this._onResponseReceivedExtraInfo.bind(this)), + eventsHelper.addEventListener(session2, "Network.loadingFinished", this._onLoadingFinished.bind(this, sessionInfo)), + eventsHelper.addEventListener(session2, "Network.loadingFailed", this._onLoadingFailed.bind(this, sessionInfo)) + ]; + if (this._page) { + sessionInfo.eventListeners.push(...[ + eventsHelper.addEventListener(session2, "Network.webSocketCreated", (e) => this._page.frameManager.onWebSocketCreated(e.requestId, e.url)), + eventsHelper.addEventListener(session2, "Network.webSocketWillSendHandshakeRequest", (event) => this._onWebSocketWillSendHandshakeRequest(event)), + eventsHelper.addEventListener(session2, "Network.webSocketHandshakeResponseReceived", (e) => this._page.frameManager.onWebSocketResponse(e.requestId, e.response.status, e.response.statusText, headersObjectToArray(e.response.headers, "\n"))), + eventsHelper.addEventListener(session2, "Network.webSocketFrameSent", (e) => e.response.payloadData && this._page.frameManager.onWebSocketFrameSent(e.requestId, e.response.opcode, e.response.payloadData, this._timestampToWallTimeMsForWebSocket(e.requestId, e.timestamp))), + eventsHelper.addEventListener(session2, "Network.webSocketFrameReceived", (e) => e.response.payloadData && this._page.frameManager.webSocketFrameReceived(e.requestId, e.response.opcode, e.response.payloadData, this._timestampToWallTimeMsForWebSocket(e.requestId, e.timestamp))), + eventsHelper.addEventListener(session2, "Network.webSocketClosed", (event) => this._onWebSocketClosed(event)), + eventsHelper.addEventListener(session2, "Network.webSocketFrameError", (e) => this._page.frameManager.webSocketError(e.requestId, e.errorMessage)) + ]); + } + this._sessions.set(session2, sessionInfo); + await Promise.all([ + session2.send("Network.enable"), + this._updateProtocolRequestInterceptionForSession( + sessionInfo, + true + /* initial */ + ), + this._setOfflineForSession( + sessionInfo, + true + /* initial */ + ), + this._setExtraHTTPHeadersForSession( + sessionInfo, + true + /* initial */ + ) + ]); + } + removeSession(session2) { + const info = this._sessions.get(session2); + if (info) + eventsHelper.removeEventListeners(info.eventListeners); + this._sessions.delete(session2); + } + async _forEachSession(cb) { + await Promise.all([...this._sessions.values()].map((info) => { + if (info.isMain) + return cb(info); + return cb(info).catch((e) => { + if (isSessionClosedError(e)) + return; + throw e; + }); + })); + } + async authenticate(credentials) { + this._credentials = credentials; + await this._updateProtocolRequestInterception(); + } + async setOffline(offline) { + if (offline === this._offline) + return; + this._offline = offline; + await this._forEachSession((info) => this._setOfflineForSession(info)); + } + async _setOfflineForSession(info, initial) { + if (initial && !this._offline) + return; + if (info.workerFrame) + return; + await info.session.send("Network.emulateNetworkConditions", { + offline: this._offline, + // values of 0 remove any active throttling. crbug.com/456324#c9 + latency: 0, + downloadThroughput: -1, + uploadThroughput: -1 + }); + } + async setRequestInterception(value2) { + this._userRequestInterceptionEnabled = value2; + await this._updateProtocolRequestInterception(); + } + async _updateProtocolRequestInterception() { + const enabled = this._userRequestInterceptionEnabled || !!this._credentials; + if (enabled === this._protocolRequestInterceptionEnabled) + return; + this._protocolRequestInterceptionEnabled = enabled; + await this._forEachSession((info) => this._updateProtocolRequestInterceptionForSession(info)); + } + async _updateProtocolRequestInterceptionForSession(info, initial) { + const enabled = this._protocolRequestInterceptionEnabled; + if (initial && !enabled) + return; + const cachePromise = info.session.send("Network.setCacheDisabled", { cacheDisabled: enabled }); + let fetchPromise = Promise.resolve(void 0); + if (!info.workerFrame) { + if (enabled) + fetchPromise = info.session.send("Fetch.enable", { handleAuthRequests: true, patterns: [{ urlPattern: "*", requestStage: "Request" }] }); + else + fetchPromise = info.session.send("Fetch.disable"); + } + await Promise.all([cachePromise, fetchPromise]); + } + async setExtraHTTPHeaders(extraHTTPHeaders) { + if (!this._extraHTTPHeaders.length && !extraHTTPHeaders.length) + return; + this._extraHTTPHeaders = extraHTTPHeaders; + await this._forEachSession((info) => this._setExtraHTTPHeadersForSession(info)); + } + async _setExtraHTTPHeadersForSession(info, initial) { + if (initial && !this._extraHTTPHeaders.length) + return; + await info.session.send("Network.setExtraHTTPHeaders", { headers: headersArrayToObject( + this._extraHTTPHeaders, + false + /* lowerCase */ + ) }); + } + async clearCache() { + await this._forEachSession(async (info) => { + await info.session.send("Network.setCacheDisabled", { cacheDisabled: true }); + if (!this._protocolRequestInterceptionEnabled) + await info.session.send("Network.setCacheDisabled", { cacheDisabled: false }); + if (!info.workerFrame) + await info.session.send("Network.clearBrowserCache"); + }); + } + _onRequestWillBeSent(sessionInfo, event) { + if (this._protocolRequestInterceptionEnabled && !event.request.url.startsWith("data:")) { + const requestId = event.requestId; + const requestPausedEvent = this._requestIdToRequestPausedEvent.get(requestId); + if (requestPausedEvent) { + this._onRequest(sessionInfo, event, requestPausedEvent.sessionInfo, requestPausedEvent.event); + this._requestIdToRequestPausedEvent.delete(requestId); + } else { + this._requestIdToRequestWillBeSentEvent.set(event.requestId, { sessionInfo, event }); + } + } else { + this._onRequest(sessionInfo, event, void 0, void 0); + } + } + _onRequestServedFromCache(event) { + this._responseExtraInfoTracker.requestServedFromCache(event); + } + _onRequestWillBeSentExtraInfo(event) { + this._responseExtraInfoTracker.requestWillBeSentExtraInfo(event); + } + _onAuthRequired(sessionInfo, event) { + let response2 = "Default"; + const shouldProvideCredentials = this._shouldProvideCredentials(event.request.url); + if (this._attemptedAuthentications.has(event.requestId)) { + response2 = "CancelAuth"; + } else if (shouldProvideCredentials) { + response2 = "ProvideCredentials"; + this._attemptedAuthentications.add(event.requestId); + } + const { username, password } = shouldProvideCredentials && this._credentials ? this._credentials : { username: void 0, password: void 0 }; + sessionInfo.session._sendMayFail("Fetch.continueWithAuth", { + requestId: event.requestId, + authChallengeResponse: { response: response2, username, password } + }); + } + _shouldProvideCredentials(url2) { + if (!this._credentials) + return false; + return !this._credentials.origin || new URL(url2).origin.toLowerCase() === this._credentials.origin.toLowerCase(); + } + _onRequestPaused(sessionInfo, event) { + if (!event.networkId) { + sessionInfo.session._sendMayFail("Fetch.continueRequest", { requestId: event.requestId }); + return; + } + if (event.request.url.startsWith("data:")) + return; + const requestId = event.networkId; + const requestWillBeSentEvent = this._requestIdToRequestWillBeSentEvent.get(requestId); + if (requestWillBeSentEvent) { + this._onRequest(requestWillBeSentEvent.sessionInfo, requestWillBeSentEvent.event, sessionInfo, event); + this._requestIdToRequestWillBeSentEvent.delete(requestId); + } else { + const existingRequest = this._requestIdToRequest.get(requestId); + const alreadyContinuedParams = existingRequest?._originalRequestRoute?._alreadyContinuedParams; + if (alreadyContinuedParams && !event.redirectedRequestId) { + sessionInfo.session._sendMayFail("Fetch.continueRequest", { + ...alreadyContinuedParams, + requestId: event.requestId + }); + return; + } + this._requestIdToRequestPausedEvent.set(requestId, { sessionInfo, event }); + } + } + _onRequest(requestWillBeSentSessionInfo, requestWillBeSentEvent, requestPausedSessionInfo, requestPausedEvent) { + if (requestWillBeSentEvent.request.url.startsWith("data:")) + return; + let redirectedFrom = null; + if (requestWillBeSentEvent.redirectResponse) { + const request3 = this._requestIdToRequest.get(requestWillBeSentEvent.requestId); + if (request3) { + this._handleRequestRedirect(request3, requestWillBeSentEvent.redirectResponse, requestWillBeSentEvent.timestamp, requestWillBeSentEvent.redirectHasExtraInfo); + redirectedFrom = request3; + } + } + let frame = requestWillBeSentEvent.frameId ? this._page?.frameManager.frame(requestWillBeSentEvent.frameId) : requestWillBeSentSessionInfo.workerFrame; + if (!frame && this._page && requestPausedEvent && requestPausedEvent.frameId) + frame = this._page.frameManager.frame(requestPausedEvent.frameId); + if (!frame && this._page && requestWillBeSentEvent.frameId === (this._page?.delegate)._targetId) { + frame = this._page.frameManager.frameAttached(requestWillBeSentEvent.frameId, null); + } + const isInterceptedOptionsPreflight = !!requestPausedEvent && requestPausedEvent.request.method === "OPTIONS" && requestWillBeSentEvent.initiator.type === "preflight"; + if (isInterceptedOptionsPreflight && (this._page || this._serviceWorker).needsRequestInterception()) { + const requestHeaders = requestPausedEvent.request.headers; + const responseHeaders = [ + { name: "Access-Control-Allow-Origin", value: requestHeaders["Origin"] || "*" }, + { name: "Access-Control-Allow-Methods", value: requestHeaders["Access-Control-Request-Method"] || "GET, POST, OPTIONS, DELETE" }, + { name: "Access-Control-Allow-Credentials", value: "true" } + ]; + if (requestHeaders["Access-Control-Request-Headers"]) + responseHeaders.push({ name: "Access-Control-Allow-Headers", value: requestHeaders["Access-Control-Request-Headers"] }); + requestPausedSessionInfo.session._sendMayFail("Fetch.fulfillRequest", { + requestId: requestPausedEvent.requestId, + responseCode: 204, + responsePhrase: statusText(204), + responseHeaders, + body: "" + }); + return; + } + if (!frame && !this._serviceWorker) { + if (requestPausedEvent) + requestPausedSessionInfo.session._sendMayFail("Fetch.continueRequest", { requestId: requestPausedEvent.requestId }); + return; + } + let route2 = null; + let headersOverride; + if (requestPausedEvent) { + if (redirectedFrom || !this._userRequestInterceptionEnabled && this._protocolRequestInterceptionEnabled) { + headersOverride = redirectedFrom?._originalRequestRoute?._alreadyContinuedParams?.headers; + if (headersOverride) { + const originalHeaders = Object.entries(requestPausedEvent.request.headers).map(([name, value2]) => ({ name, value: value2 })); + headersOverride = applyHeadersOverrides(originalHeaders, headersOverride); + } + requestPausedSessionInfo.session._sendMayFail("Fetch.continueRequest", { requestId: requestPausedEvent.requestId, headers: headersOverride }); + } else { + route2 = new RouteImpl(requestPausedSessionInfo.session, requestPausedEvent.requestId); + } + } + const isNavigationRequest = requestWillBeSentEvent.requestId === requestWillBeSentEvent.loaderId && requestWillBeSentEvent.type === "Document"; + const documentId = isNavigationRequest ? requestWillBeSentEvent.loaderId : void 0; + const request2 = new InterceptableRequest({ + session: requestWillBeSentSessionInfo.session, + context: (this._page || this._serviceWorker).browserContext, + frame: frame || null, + serviceWorker: this._serviceWorker || null, + documentId, + route: route2, + requestWillBeSentEvent, + requestPausedEvent, + redirectedFrom, + headersOverride: headersOverride || null + }); + this._requestIdToRequest.set(requestWillBeSentEvent.requestId, request2); + if (route2) { + request2.request.setRawRequestHeaders(headersObjectToArray(requestPausedEvent.request.headers, "\n")); + } + (this._page?.frameManager || this._serviceWorker).requestStarted(request2.request, route2 || void 0); + } + _createResponse(request2, responsePayload, hasExtraInfo) { + const getResponseBody = async () => { + const contentLengthHeader = Object.entries(responsePayload.headers).find((header) => header[0].toLowerCase() === "content-length"); + const expectedLength = contentLengthHeader ? +contentLengthHeader[1] : void 0; + const session2 = request2.session; + const response3 = await session2.send("Network.getResponseBody", { requestId: request2._requestId }); + if (response3.body || !expectedLength) + return Buffer.from(response3.body, response3.base64Encoded ? "base64" : "utf8"); + if (request2._originalRequestRoute?._fulfilled) + return Buffer.from(""); + const resource = await session2.send("Network.loadNetworkResource", { url: request2.request.url(), frameId: this._serviceWorker ? void 0 : request2.request.frame()._id, options: { disableCache: false, includeCredentials: true } }); + const chunks = []; + while (resource.resource.stream) { + const chunk = await session2.send("IO.read", { handle: resource.resource.stream }); + chunks.push(Buffer.from(chunk.data, chunk.base64Encoded ? "base64" : "utf-8")); + if (chunk.eof) { + await session2.send("IO.close", { handle: resource.resource.stream }); + break; + } + } + return Buffer.concat(chunks); + }; + const timingPayload = responsePayload.timing; + let timing; + if (timingPayload && !this._responseExtraInfoTracker.servedFromCache(request2._requestId)) { + timing = { + startTime: (timingPayload.requestTime - request2._timestamp + request2._wallTime) * 1e3, + domainLookupStart: timingPayload.dnsStart, + domainLookupEnd: timingPayload.dnsEnd, + connectStart: timingPayload.connectStart, + secureConnectionStart: timingPayload.sslStart, + connectEnd: timingPayload.connectEnd, + requestStart: timingPayload.sendStart, + responseStart: timingPayload.receiveHeadersEnd + }; + } else { + timing = { + startTime: request2._wallTime * 1e3, + domainLookupStart: -1, + domainLookupEnd: -1, + connectStart: -1, + secureConnectionStart: -1, + connectEnd: -1, + requestStart: -1, + responseStart: -1 + }; + } + const response2 = new Response2(request2.request, responsePayload.status, responsePayload.statusText, headersObjectToArray(responsePayload.headers), timing, getResponseBody, !!responsePayload.fromServiceWorker); + response2._setHttpVersion(responsePayload?.protocol ?? null); + if (responsePayload?.remoteIPAddress && typeof responsePayload?.remotePort === "number") { + response2._serverAddrFinished({ + ipAddress: responsePayload.remoteIPAddress, + port: responsePayload.remotePort + }); + } else { + response2._serverAddrFinished(); + } + response2._securityDetailsFinished({ + protocol: responsePayload?.securityDetails?.protocol, + subjectName: responsePayload?.securityDetails?.subjectName, + issuer: responsePayload?.securityDetails?.issuer, + validFrom: responsePayload?.securityDetails?.validFrom, + validTo: responsePayload?.securityDetails?.validTo + }); + this._responseExtraInfoTracker.processResponse(request2._requestId, response2, hasExtraInfo); + return response2; + } + _deleteRequest(request2) { + this._requestIdToRequest.delete(request2._requestId); + if (request2._interceptionId) + this._attemptedAuthentications.delete(request2._interceptionId); + } + _handleRequestRedirect(request2, responsePayload, timestamp, hasExtraInfo) { + const response2 = this._createResponse(request2, responsePayload, hasExtraInfo); + response2.setTransferSize(null); + response2.setEncodedBodySize(null); + response2._requestFinished((timestamp - request2._timestamp) * 1e3); + this._deleteRequest(request2); + (this._page?.frameManager || this._serviceWorker).requestReceivedResponse(response2); + (this._page?.frameManager || this._serviceWorker).reportRequestFinished(request2.request, response2); + } + _onResponseReceivedExtraInfo(event) { + this._responseExtraInfoTracker.responseReceivedExtraInfo(event); + } + _onResponseReceived(sessionInfo, event) { + let request2 = this._requestIdToRequest.get(event.requestId); + if (!request2 && event.response.fromServiceWorker) { + const requestWillBeSentEvent = this._requestIdToRequestWillBeSentEvent.get(event.requestId); + if (requestWillBeSentEvent) { + this._requestIdToRequestWillBeSentEvent.delete(event.requestId); + this._onRequest(sessionInfo, requestWillBeSentEvent.event, void 0, void 0); + request2 = this._requestIdToRequest.get(event.requestId); + } + } + if (!request2) + return; + const response2 = this._createResponse(request2, event.response, event.hasExtraInfo); + (this._page?.frameManager || this._serviceWorker).requestReceivedResponse(response2); + } + _onLoadingFinished(sessionInfo, event) { + this._responseExtraInfoTracker.loadingFinished(event); + const request2 = this._requestIdToRequest.get(event.requestId); + if (!request2) + return; + this._maybeUpdateRequestSession(sessionInfo, request2); + const response2 = request2.request._existingResponse(); + if (response2) { + response2.setTransferSize(event.encodedDataLength); + response2.responseHeadersSize().then((size) => response2.setEncodedBodySize(event.encodedDataLength - size)); + response2._requestFinished(helper.secondsToRoundishMillis(event.timestamp - request2._timestamp)); + } + this._deleteRequest(request2); + (this._page?.frameManager || this._serviceWorker).reportRequestFinished(request2.request, response2); + } + _onLoadingFailed(sessionInfo, event) { + this._responseExtraInfoTracker.loadingFailed(event); + let request2 = this._requestIdToRequest.get(event.requestId); + if (!request2) { + const requestWillBeSentEvent = this._requestIdToRequestWillBeSentEvent.get(event.requestId); + if (requestWillBeSentEvent) { + this._requestIdToRequestWillBeSentEvent.delete(event.requestId); + this._onRequest(sessionInfo, requestWillBeSentEvent.event, void 0, void 0); + request2 = this._requestIdToRequest.get(event.requestId); + } + } + if (!request2) + return; + this._maybeUpdateRequestSession(sessionInfo, request2); + const response2 = request2.request._existingResponse(); + if (response2) { + response2.setTransferSize(null); + response2.setEncodedBodySize(null); + response2._requestFinished(helper.secondsToRoundishMillis(event.timestamp - request2._timestamp)); + } else { + request2.request.setRawRequestHeaders(null); + } + this._deleteRequest(request2); + request2.request._setFailureText(event.errorText || event.blockedReason || ""); + (this._page?.frameManager || this._serviceWorker).requestFailed(request2.request, !!event.canceled); + } + _onWebSocketWillSendHandshakeRequest(event) { + const wallTimeMs = event.wallTime * 1e3; + this._timestampBaselineForWebSocket.set(event.requestId, wallTimeMs - event.timestamp * 1e3); + this._page.frameManager.onWebSocketRequest(event.requestId, headersObjectToArray(event.request.headers, "\n"), wallTimeMs); + } + _onWebSocketClosed(event) { + this._timestampBaselineForWebSocket.delete(event.requestId); + this._page.frameManager.webSocketClosed(event.requestId); + } + _timestampToWallTimeMsForWebSocket(requestId, timestamp) { + return this._timestampBaselineForWebSocket.get(requestId) + timestamp * 1e3; + } + _maybeUpdateRequestSession(sessionInfo, request2) { + if (request2.session !== sessionInfo.session && !sessionInfo.isMain && (request2._documentId === request2._requestId || sessionInfo.workerFrame)) + request2.session = sessionInfo.session; + } + }; + InterceptableRequest = class { + constructor(options) { + const { session: session2, context: context2, frame, documentId, route: route2, requestWillBeSentEvent, requestPausedEvent, redirectedFrom, serviceWorker, headersOverride } = options; + this.session = session2; + this._timestamp = requestWillBeSentEvent.timestamp; + this._wallTime = requestWillBeSentEvent.wallTime; + this._requestId = requestWillBeSentEvent.requestId; + this._interceptionId = requestPausedEvent && requestPausedEvent.requestId; + this._documentId = documentId; + this._originalRequestRoute = route2 ?? redirectedFrom?._originalRequestRoute; + const { + headers, + method, + url: url2, + postDataEntries = null + } = requestPausedEvent ? requestPausedEvent.request : requestWillBeSentEvent.request; + let postDataBuffer = null; + const entries = postDataEntries?.filter((entry) => entry.bytes); + if (entries && entries.length) + postDataBuffer = Buffer.concat(entries.map((entry) => Buffer.from(entry.bytes, "base64"))); + this.request = new Request(context2, frame, serviceWorker, redirectedFrom?.request || null, documentId, url2, toResourceType(requestWillBeSentEvent.type || "Other"), method, postDataBuffer, headersOverride || headersObjectToArray(headers), requestWillBeSentEvent.wallTime * 1e3); + } + }; + RouteImpl = class { + constructor(session2, interceptionId) { + this._fulfilled = false; + this._session = session2; + this._interceptionId = interceptionId; + } + async continue(overrides) { + this._alreadyContinuedParams = { + requestId: this._interceptionId, + url: overrides.url, + headers: overrides.headers, + method: overrides.method, + postData: overrides.postData ? overrides.postData.toString("base64") : void 0 + }; + await catchDisallowedErrors(async () => { + await this._session.send("Fetch.continueRequest", this._alreadyContinuedParams); + }); + } + async fulfill(response2) { + this._fulfilled = true; + const body = response2.isBase64 ? response2.body : Buffer.from(response2.body).toString("base64"); + const responseHeaders = splitSetCookieHeader(response2.headers); + await catchDisallowedErrors(async () => { + await this._session.send("Fetch.fulfillRequest", { + requestId: this._interceptionId, + responseCode: response2.status, + responsePhrase: statusText(response2.status), + responseHeaders, + body + }); + }); + } + async abort(errorCode = "failed") { + const errorReason = errorReasons[errorCode]; + assert(errorReason, "Unknown error code: " + errorCode); + await catchDisallowedErrors(async () => { + await this._session.send("Fetch.failRequest", { + requestId: this._interceptionId, + errorReason + }); + }); + } + }; + errorReasons = { + "aborted": "Aborted", + "accessdenied": "AccessDenied", + "addressunreachable": "AddressUnreachable", + "blockedbyclient": "BlockedByClient", + "blockedbyresponse": "BlockedByResponse", + "connectionaborted": "ConnectionAborted", + "connectionclosed": "ConnectionClosed", + "connectionfailed": "ConnectionFailed", + "connectionrefused": "ConnectionRefused", + "connectionreset": "ConnectionReset", + "internetdisconnected": "InternetDisconnected", + "namenotresolved": "NameNotResolved", + "timedout": "TimedOut", + "failed": "Failed" + }; + ResponseExtraInfoTracker = class { + constructor() { + this._requests = /* @__PURE__ */ new Map(); + } + requestWillBeSentExtraInfo(event) { + const info = this._getOrCreateEntry(event.requestId); + info.requestWillBeSentExtraInfo.push(event); + this._patchHeaders(info, info.requestWillBeSentExtraInfo.length - 1); + this._checkFinished(info); + } + requestServedFromCache(event) { + const info = this._getOrCreateEntry(event.requestId); + info.servedFromCache = true; + } + servedFromCache(requestId) { + const info = this._requests.get(requestId); + return !!info?.servedFromCache; + } + responseReceivedExtraInfo(event) { + const info = this._getOrCreateEntry(event.requestId); + info.responseReceivedExtraInfo.push(event); + this._patchHeaders(info, info.responseReceivedExtraInfo.length - 1); + this._checkFinished(info); + } + processResponse(requestId, response2, hasExtraInfo) { + let info = this._requests.get(requestId); + if (!hasExtraInfo || info?.servedFromCache) { + response2.request().setRawRequestHeaders(null); + response2.setResponseHeadersSize(null); + response2.setRawResponseHeaders(null); + return; + } + info = this._getOrCreateEntry(requestId); + info.responses.push(response2); + this._patchHeaders(info, info.responses.length - 1); + } + loadingFinished(event) { + const info = this._requests.get(event.requestId); + if (!info) + return; + info.loadingFinished = event; + this._checkFinished(info); + } + loadingFailed(event) { + const info = this._requests.get(event.requestId); + if (!info) + return; + info.loadingFailed = event; + this._checkFinished(info); + } + _getOrCreateEntry(requestId) { + let info = this._requests.get(requestId); + if (!info) { + info = { + requestId, + requestWillBeSentExtraInfo: [], + responseReceivedExtraInfo: [], + responses: [] + }; + this._requests.set(requestId, info); + } + return info; + } + _patchHeaders(info, index) { + const response2 = info.responses[index]; + const requestExtraInfo = info.requestWillBeSentExtraInfo[index]; + if (response2 && requestExtraInfo) { + response2.request().setRawRequestHeaders(headersObjectToArray(requestExtraInfo.headers, "\n")); + info.requestWillBeSentExtraInfo[index] = void 0; + } + const responseExtraInfo = info.responseReceivedExtraInfo[index]; + if (response2 && responseExtraInfo) { + response2.setResponseHeadersSize(responseExtraInfo.headersText?.length || 0); + response2.setRawResponseHeaders(headersObjectToArray(responseExtraInfo.headers, "\n")); + info.responseReceivedExtraInfo[index] = void 0; + } + } + _checkFinished(info) { + if (!info.loadingFinished && !info.loadingFailed) + return; + if (info.responses.length <= info.responseReceivedExtraInfo.length) { + this._stopTracking(info.requestId); + return; + } + } + _stopTracking(requestId) { + this._requests.delete(requestId); + } + }; + } +}); + +// packages/playwright-core/src/server/chromium/crPdf.ts +function convertPrintParameterToInches(text2) { + if (text2 === void 0) + return void 0; + let unit = text2.substring(text2.length - 2).toLowerCase(); + let valueText = ""; + if (unitToPixels.hasOwnProperty(unit)) { + valueText = text2.substring(0, text2.length - 2); + } else { + unit = "px"; + valueText = text2; + } + const value2 = Number(valueText); + assert(!isNaN(value2), "Failed to parse parameter value: " + text2); + const pixels = value2 * unitToPixels[unit]; + return pixels / 96; +} +var PagePaperFormats, unitToPixels, CRPDF; +var init_crPdf = __esm({ + "packages/playwright-core/src/server/chromium/crPdf.ts"() { + "use strict"; + init_assert(); + init_crProtocolHelper(); + PagePaperFormats = { + letter: { width: 8.5, height: 11 }, + legal: { width: 8.5, height: 14 }, + tabloid: { width: 11, height: 17 }, + ledger: { width: 17, height: 11 }, + a0: { width: 33.1, height: 46.8 }, + a1: { width: 23.4, height: 33.1 }, + a2: { width: 16.54, height: 23.4 }, + a3: { width: 11.7, height: 16.54 }, + a4: { width: 8.27, height: 11.7 }, + a5: { width: 5.83, height: 8.27 }, + a6: { width: 4.13, height: 5.83 } + }; + unitToPixels = { + "px": 1, + "in": 96, + "cm": 37.8, + "mm": 3.78 + }; + CRPDF = class { + constructor(client) { + this._client = client; + } + async generate(options) { + const { + scale = 1, + displayHeaderFooter = false, + headerTemplate = "", + footerTemplate = "", + printBackground = false, + landscape = false, + pageRanges = "", + preferCSSPageSize = false, + margin = {}, + tagged = false, + outline = false + } = options; + let paperWidth = 8.5; + let paperHeight = 11; + if (options.format) { + const format2 = PagePaperFormats[options.format.toLowerCase()]; + assert(format2, "Unknown paper format: " + options.format); + paperWidth = format2.width; + paperHeight = format2.height; + } else { + paperWidth = convertPrintParameterToInches(options.width) || paperWidth; + paperHeight = convertPrintParameterToInches(options.height) || paperHeight; + } + const marginTop = convertPrintParameterToInches(margin.top) || 0; + const marginLeft = convertPrintParameterToInches(margin.left) || 0; + const marginBottom = convertPrintParameterToInches(margin.bottom) || 0; + const marginRight = convertPrintParameterToInches(margin.right) || 0; + const generateDocumentOutline = outline; + const generateTaggedPDF = tagged; + const result2 = await this._client.send("Page.printToPDF", { + transferMode: "ReturnAsStream", + landscape, + displayHeaderFooter, + headerTemplate, + footerTemplate, + printBackground, + scale, + paperWidth, + paperHeight, + marginTop, + marginBottom, + marginLeft, + marginRight, + pageRanges, + preferCSSPageSize, + generateTaggedPDF, + generateDocumentOutline + }); + return await readProtocolStream(this._client, result2.stream); + } + }; + } +}); + +// packages/playwright-core/src/server/chromium/defaultFontFamilies.ts +var platformToFontFamilies; +var init_defaultFontFamilies = __esm({ + "packages/playwright-core/src/server/chromium/defaultFontFamilies.ts"() { + "use strict"; + platformToFontFamilies = { + "linux": { + "fontFamilies": { + "standard": "Times New Roman", + "fixed": "Monospace", + "serif": "Times New Roman", + "sansSerif": "Arial", + "cursive": "Comic Sans MS", + "fantasy": "Impact" + } + }, + "mac": { + "fontFamilies": { + "standard": "Times", + "fixed": "Courier", + "serif": "Times", + "sansSerif": "Helvetica", + "cursive": "Apple Chancery", + "fantasy": "Papyrus" + }, + "forScripts": [ + { + "script": "jpan", + "fontFamilies": { + "standard": "Hiragino Kaku Gothic ProN", + "fixed": "Osaka-Mono", + "serif": "Hiragino Mincho ProN", + "sansSerif": "Hiragino Kaku Gothic ProN" + } + }, + { + "script": "hang", + "fontFamilies": { + "standard": "Apple SD Gothic Neo", + "serif": "AppleMyungjo", + "sansSerif": "Apple SD Gothic Neo" + } + }, + { + "script": "hans", + "fontFamilies": { + "standard": ",PingFang SC,STHeiti", + "serif": "Songti SC", + "sansSerif": ",PingFang SC,STHeiti", + "cursive": "Kaiti SC" + } + }, + { + "script": "hant", + "fontFamilies": { + "standard": ",PingFang TC,Heiti TC", + "serif": "Songti TC", + "sansSerif": ",PingFang TC,Heiti TC", + "cursive": "Kaiti TC" + } + } + ] + }, + "win": { + "fontFamilies": { + "standard": "Times New Roman", + "fixed": "Consolas", + "serif": "Times New Roman", + "sansSerif": "Arial", + "cursive": "Comic Sans MS", + "fantasy": "Impact" + }, + "forScripts": [ + { + "script": "cyrl", + "fontFamilies": { + "standard": "Times New Roman", + "fixed": "Courier New", + "serif": "Times New Roman", + "sansSerif": "Arial" + } + }, + { + "script": "arab", + "fontFamilies": { + "fixed": "Courier New", + "sansSerif": "Segoe UI" + } + }, + { + "script": "grek", + "fontFamilies": { + "standard": "Times New Roman", + "fixed": "Courier New", + "serif": "Times New Roman", + "sansSerif": "Arial" + } + }, + { + "script": "jpan", + "fontFamilies": { + "standard": ",Meiryo,Yu Gothic", + "fixed": "MS Gothic", + "serif": ",Yu Mincho,MS PMincho", + "sansSerif": ",Meiryo,Yu Gothic" + } + }, + { + "script": "hang", + "fontFamilies": { + "standard": "Malgun Gothic", + "fixed": "Gulimche", + "serif": "Batang", + "sansSerif": "Malgun Gothic", + "cursive": "Gungsuh" + } + }, + { + "script": "hans", + "fontFamilies": { + "standard": "Microsoft YaHei", + "fixed": "NSimsun", + "serif": "Simsun", + "sansSerif": "Microsoft YaHei", + "cursive": "KaiTi" + } + }, + { + "script": "hant", + "fontFamilies": { + "standard": "Microsoft JhengHei", + "fixed": "MingLiU", + "serif": "PMingLiU", + "sansSerif": "Microsoft JhengHei", + "cursive": "DFKai-SB" + } + } + ] + } + }; + } +}); + +// packages/playwright-core/src/server/videoRecorder.ts +function startAutomaticVideoRecording(page) { + const recordVideo = page.browserContext._options.recordVideo; + if (!recordVideo) + return; + const recorder = new VideoRecorder(page.screencast); + if (page.browserContext._options.recordVideo?.showActions) + page.screencast.showActions(page.browserContext._options.recordVideo?.showActions); + const dir = recordVideo.dir ?? page.browserContext._browser.options.artifactsDir; + const artifact = recorder.start({ size: recordVideo.size, fileName: import_path21.default.join(dir, page.guid + ".webm") }); + page.video = artifact; +} +function createWhiteImage(width, height) { + const data = Buffer.alloc(width * height * 4, 255); + return jpegjs2.encode({ data, width, height }, 80).data; +} +var import_path21, jpegjs2, fps, VideoRecorder, FfmpegVideoRecorder; +var init_videoRecorder = __esm({ + "packages/playwright-core/src/server/videoRecorder.ts"() { + "use strict"; + import_path21 = __toESM(require("path")); + init_processLauncher(); + init_assert(); + init_crypto(); + init_debugLogger(); + init_fileUtils(); + init_time(); + init_artifact(); + init_registry(); + jpegjs2 = require("./utilsBundle").jpegjs; + fps = 25; + VideoRecorder = class { + constructor(screencast) { + this._screencast = screencast; + } + start(options) { + assert(!this._artifact); + const ffmpegPath = registry.findExecutable("ffmpeg").executablePathOrDie(this._screencast.page.browserContext._browser.sdkLanguage()); + const outputFile2 = options.fileName ?? import_path21.default.join(this._screencast.page.browserContext._browser.options.artifactsDir, createGuid() + ".webm"); + this._client = { + onFrame: (frame) => this._videoRecorder.writeFrame(frame.buffer, frame.frameSwapWallTime / 1e3), + gracefulClose: () => this.stop(), + dispose: () => this.stop().catch((e) => debugLogger.log("error", `Failed to stop video recorder: ${String(e)}`)), + size: options.size + }; + const { size } = this._screencast.addClient(this._client); + const videoSize = options.size ?? size; + this._videoRecorder = new FfmpegVideoRecorder(ffmpegPath, videoSize, outputFile2); + this._artifact = new Artifact(this._screencast.page.browserContext, outputFile2); + return this._artifact; + } + async stop() { + if (!this._artifact) + return; + const artifact = this._artifact; + this._artifact = void 0; + const client = this._client; + this._client = void 0; + const videoRecorder = this._videoRecorder; + this._videoRecorder = void 0; + this._screencast.removeClient(client); + await videoRecorder._stop(); + await artifact.reportFinished(); + } + }; + FfmpegVideoRecorder = class { + constructor(ffmpegPath, size, outputFile2) { + this._process = null; + this._gracefullyClose = null; + this._lastWritePromise = Promise.resolve(); + this._firstFrameTimestamp = 0; + this._lastFrame = null; + this._lastWriteNodeTime = 0; + this._frameQueue = []; + this._isStopped = false; + if (!outputFile2.endsWith(".webm")) + throw new Error("File must have .webm extension"); + this._outputFile = outputFile2; + this._ffmpegPath = ffmpegPath; + this._size = size; + this._launchPromise = this._launch().catch((e) => e); + } + async _launch() { + await mkdirIfNeeded(this._outputFile); + const w = this._size.width; + const h = this._size.height; + const args = `-loglevel error -f image2pipe -avioflags direct -fpsprobesize 0 -probesize 32 -analyzeduration 0 -c:v mjpeg -i pipe:0 -y -an -r ${fps} -c:v vp8 -qmin 0 -qmax 50 -crf 8 -deadline realtime -speed 8 -b:v 1M -threads 1 -vf pad=${w}:${h}:0:0:gray,crop=${w}:${h}:0:0`.split(" "); + args.push(this._outputFile); + const { launchedProcess, gracefullyClose } = await launchProcess({ + command: this._ffmpegPath, + args, + stdio: "stdin", + log: (message) => debugLogger.log("browser", message), + tempDirectories: [], + attemptToGracefullyClose: async () => { + debugLogger.log("browser", "Closing stdin..."); + launchedProcess.stdin.end(); + }, + onExit: (exitCode, signal) => { + debugLogger.log("browser", `ffmpeg onkill exitCode=${exitCode} signal=${signal}`); + } + }); + launchedProcess.stdin.on("finish", () => { + debugLogger.log("browser", "ffmpeg finished input."); + }); + launchedProcess.stdin.on("error", () => { + debugLogger.log("browser", "ffmpeg error."); + }); + this._process = launchedProcess; + this._gracefullyClose = gracefullyClose; + } + writeFrame(frame, timestamp) { + this._launchPromise.then((error) => { + if (error) + return; + this._writeFrame(frame, timestamp); + }); + } + _writeFrame(frame, timestamp) { + assert(this._process); + if (this._isStopped) + return; + if (!this._firstFrameTimestamp) + this._firstFrameTimestamp = timestamp; + const frameNumber = Math.floor((timestamp - this._firstFrameTimestamp) * fps); + if (this._lastFrame) { + const repeatCount = frameNumber - this._lastFrame.frameNumber; + for (let i = 0; i < repeatCount; ++i) + this._frameQueue.push(this._lastFrame.buffer); + this._lastWritePromise = this._lastWritePromise.then(() => this._sendFrames()); + } + this._lastFrame = { buffer: frame, timestamp, frameNumber }; + this._lastWriteNodeTime = monotonicTime(); + } + async _sendFrames() { + while (this._frameQueue.length) + await this._sendFrame(this._frameQueue.shift()); + } + async _sendFrame(frame) { + return new Promise((f) => this._process.stdin.write(frame, f)).then((error) => { + if (error) + debugLogger.log("browser", `ffmpeg failed to write: ${String(error)}`); + }); + } + async _stop() { + const error = await this._launchPromise; + if (error) + throw error; + if (this._isStopped) + return; + if (!this._lastFrame) { + this._writeFrame(createWhiteImage(this._size.width, this._size.height), monotonicTime()); + } + const addTime = Math.max((monotonicTime() - this._lastWriteNodeTime) / 1e3, 1); + this._writeFrame(Buffer.from([]), this._lastFrame.timestamp + addTime); + this._isStopped = true; + try { + await this._lastWritePromise; + await this._gracefullyClose(); + } catch (e) { + debugLogger.log("error", `ffmpeg failed to stop: ${String(e)}`); + } + } + }; + } +}); + +// packages/playwright-core/src/server/chromium/crPage.ts +async function emulateLocale(session2, locale) { + try { + await session2.send("Emulation.setLocaleOverride", { locale }); + } catch (exception) { + if (exception.message.includes("Another locale override is already in effect")) + return; + throw exception; + } +} +async function emulateTimezone(session2, timezoneId) { + try { + await session2.send("Emulation.setTimezoneOverride", { timezoneId }); + } catch (exception) { + if (exception.message.includes("Timezone override is already in effect")) + return; + if (exception.message.includes("Invalid timezone")) + throw new Error(`Invalid timezone ID: ${timezoneId}`); + throw exception; + } +} +function calculateUserAgentMetadata(options) { + const ua = options.userAgent; + if (!ua) + return void 0; + const metadata = { + mobile: !!options.isMobile, + model: "", + architecture: "x86", + platform: "Windows", + platformVersion: "" + }; + const androidMatch = ua.match(/Android (\d+(\.\d+)?(\.\d+)?)/); + const iPhoneMatch = ua.match(/iPhone OS (\d+(_\d+)?)/); + const iPadMatch = ua.match(/iPad; CPU OS (\d+(_\d+)?)/); + const macOSMatch = ua.match(/Mac OS X (\d+(_\d+)?(_\d+)?)/); + const windowsMatch = ua.match(/Windows\D+(\d+(\.\d+)?(\.\d+)?)/); + if (androidMatch) { + metadata.platform = "Android"; + metadata.platformVersion = androidMatch[1]; + metadata.architecture = "arm"; + } else if (iPhoneMatch) { + metadata.platform = "iOS"; + metadata.platformVersion = iPhoneMatch[1]; + metadata.architecture = "arm"; + } else if (iPadMatch) { + metadata.platform = "iOS"; + metadata.platformVersion = iPadMatch[1]; + metadata.architecture = "arm"; + } else if (macOSMatch) { + metadata.platform = "macOS"; + metadata.platformVersion = macOSMatch[1]; + if (!ua.includes("Intel")) + metadata.architecture = "arm"; + } else if (windowsMatch) { + metadata.platform = "Windows"; + metadata.platformVersion = windowsMatch[1]; + } else if (ua.toLowerCase().includes("linux")) { + metadata.platform = "Linux"; + } + if (ua.includes("ARM")) + metadata.architecture = "arm"; + return metadata; +} +var CRPage, FrameSession; +var init_crPage = __esm({ + "packages/playwright-core/src/server/chromium/crPage.ts"() { + "use strict"; + init_assert(); + init_stackTrace(); + init_eventsHelper(); + init_dialog(); + init_dom(); + init_frames(); + init_helper(); + init_network2(); + init_page(); + init_crCoverage(); + init_crDragDrop(); + init_crExecutionContext(); + init_crInput(); + init_crNetworkManager(); + init_crPdf(); + init_crProtocolHelper(); + init_defaultFontFamilies(); + init_errors(); + init_protocolError(); + init_videoRecorder(); + init_progress(); + CRPage = class { + constructor(client, targetId, browserContext, opener, bits) { + this._sessions = /* @__PURE__ */ new Map(); + // Holds window features for the next popup being opened via window.open, + // until the popup target arrives. This could be racy if two oopifs + // simultaneously call window.open with window features: the order + // of their Page.windowOpen events is not guaranteed to match the order + // of new popup targets. + this._nextWindowOpenPopupFeatures = []; + this._targetId = targetId; + this._opener = opener; + const dragManager = new DragManager(this); + this.rawKeyboard = new RawKeyboardImpl(client, browserContext._browser._platform() === "mac", dragManager); + this.rawMouse = new RawMouseImpl(this, client, dragManager); + this.rawTouchscreen = new RawTouchscreenImpl(client); + this._pdf = new CRPDF(client); + this._coverage = new CRCoverage(client); + this._browserContext = browserContext; + this._page = new Page(this, browserContext); + this.utilityWorldName = `__playwright_utility_world_${this._page.guid}`; + this._networkManager = new CRNetworkManager(this._page, null); + this.updateOffline(); + this.updateExtraHTTPHeaders(); + this.updateHttpCredentials(); + this.updateRequestInterception(); + this._mainFrameSession = new FrameSession(this, client, targetId, null); + this._sessions.set(targetId, this._mainFrameSession); + if (opener && !browserContext._options.noDefaultViewport) { + const features = opener._nextWindowOpenPopupFeatures.shift() || []; + const viewportSize = helper.getViewportSizeFromWindowFeatures(features); + if (viewportSize) + this._page.setEmulatedSizeFromWindowOpen({ viewport: viewportSize, screen: viewportSize }); + } + this._mainFrameSession._initialize(bits.hasUIWindow).then( + () => this._page.reportAsNew(this._opener?._page, void 0), + (error) => this._page.reportAsNew(this._opener?._page, error) + ); + } + static mainFrameSession(page) { + const crPage = page.delegate; + return crPage._mainFrameSession; + } + async _forAllFrameSessions(cb) { + const frameSessions = Array.from(this._sessions.values()); + await Promise.all(frameSessions.map((frameSession) => { + if (frameSession._isMainFrame()) + return cb(frameSession); + return cb(frameSession).catch((e) => { + if (isSessionClosedError(e)) + return; + throw e; + }); + })); + } + _sessionForFrame(frame) { + while (!this._sessions.has(frame._id)) { + const parent = frame.parentFrame(); + if (!parent) + throw new Error(`Frame has been detached.`); + frame = parent; + } + return this._sessions.get(frame._id); + } + _sessionForHandle(handle) { + const frame = handle._context.frame; + return this._sessionForFrame(frame); + } + willBeginDownload() { + this._mainFrameSession._willBeginDownload(); + } + didClose() { + for (const session2 of this._sessions.values()) + session2.dispose(); + this._page._didClose(); + } + async navigateFrame(frame, url2, referrer) { + return this._sessionForFrame(frame)._navigate(frame, url2, referrer); + } + async updateExtraHTTPHeaders() { + const headers = mergeHeaders([ + this._browserContext._options.extraHTTPHeaders, + this._page.extraHTTPHeaders() + ]); + await this._networkManager.setExtraHTTPHeaders(headers); + } + async updateGeolocation() { + await this._forAllFrameSessions((frame) => frame._updateGeolocation(false)); + } + async updateOffline() { + await this._networkManager.setOffline(!!this._browserContext._options.offline); + } + async updateHttpCredentials() { + await this._networkManager.authenticate(this._browserContext._options.httpCredentials || null); + } + async updateEmulatedViewportSize(preserveWindowBoundaries) { + await this._mainFrameSession._updateViewport(preserveWindowBoundaries); + } + async bringToFront() { + await this._mainFrameSession._client.send("Page.bringToFront"); + } + async updateEmulateMedia() { + await this._forAllFrameSessions((frame) => frame._updateEmulateMedia()); + } + async updateUserAgent() { + await this._forAllFrameSessions((frame) => frame._updateUserAgent()); + } + async updateRequestInterception() { + await this._networkManager.setRequestInterception(this._page.needsRequestInterception()); + } + async updateFileChooserInterception() { + await this._forAllFrameSessions((frame) => frame._updateFileChooserInterception(false)); + } + async reload() { + await this._mainFrameSession._client.send("Page.reload"); + } + async _go(delta) { + const history = await this._mainFrameSession._client.send("Page.getNavigationHistory"); + const entry = history.entries[history.currentIndex + delta]; + if (!entry) + return false; + await this._mainFrameSession._client.send("Page.navigateToHistoryEntry", { entryId: entry.id }); + return true; + } + goBack() { + return this._go(-1); + } + goForward() { + return this._go(1); + } + async requestGC() { + await this._mainFrameSession._client.send("HeapProfiler.collectGarbage"); + } + async addInitScript(initScript, world = "main") { + await this._forAllFrameSessions((frame) => frame._evaluateOnNewDocument(initScript, world)); + } + async exposePlaywrightBinding() { + await this._forAllFrameSessions((frame) => frame.exposePlaywrightBinding()); + } + async removeInitScripts(initScripts) { + await this._forAllFrameSessions((frame) => frame._removeEvaluatesOnNewDocument(initScripts)); + } + async closePage(runBeforeUnload) { + if (runBeforeUnload) + await this._mainFrameSession._client.send("Page.close"); + else + await this._browserContext._browser._closePage(this); + } + async setBackgroundColor(color) { + await this._mainFrameSession._client.send("Emulation.setDefaultBackgroundColorOverride", { color }); + } + async takeScreenshot(progress2, format2, documentRect, viewportRect, quality, fitsViewport, scale) { + const { visualViewport, contentSize, cssContentSize } = await progress2.race(this._mainFrameSession._client.send("Page.getLayoutMetrics")); + if (!documentRect) { + documentRect = { + x: visualViewport.pageX + viewportRect.x, + y: visualViewport.pageY + viewportRect.y, + ...helper.enclosingIntSize({ + width: viewportRect.width / visualViewport.scale, + height: viewportRect.height / visualViewport.scale + }) + }; + } + const clip = { ...documentRect, scale: viewportRect ? visualViewport.scale : 1 }; + if (scale === "css") { + const deviceScaleFactor = this._mainFrameSession._metricsOverride?.deviceScaleFactor || contentSize.width / cssContentSize.width || 1; + clip.scale /= deviceScaleFactor; + } + const result2 = await progress2.race(this._mainFrameSession._client.send("Page.captureScreenshot", { format: format2, quality, clip, captureBeyondViewport: !fitsViewport })); + return Buffer.from(result2.data, "base64"); + } + async getContentFrame(handle) { + return this._sessionForHandle(handle)._getContentFrame(handle); + } + async getOwnerFrame(handle) { + return this._sessionForHandle(handle)._getOwnerFrame(handle); + } + async getBoundingBox(handle) { + return this._sessionForHandle(handle)._getBoundingBox(handle); + } + async scrollRectIntoViewIfNeeded(handle, rect) { + return this._sessionForHandle(handle)._scrollRectIntoViewIfNeeded(handle, rect); + } + startScreencast(options) { + this._mainFrameSession._client.send("Page.startScreencast", { + format: "jpeg", + quality: options.quality, + maxWidth: options.width, + maxHeight: options.height + }).catch(() => { + }); + } + stopScreencast() { + this._mainFrameSession._client._sendMayFail("Page.stopScreencast").catch(() => { + }); + } + rafCountForStablePosition() { + return 1; + } + async getContentQuads(handle) { + return this._sessionForHandle(handle)._getContentQuads(handle); + } + async setInputFilePaths(progress2, handle, files) { + const frame = await handle.ownerFrame(progress2); + if (!frame) + throw new Error("Cannot set input files to detached input element"); + const parentSession = this._sessionForFrame(frame); + await progress2.race(parentSession._client.send("DOM.setFileInputFiles", { + objectId: handle._objectId, + files + })); + } + async adoptElementHandle(handle, to) { + return this._sessionForHandle(handle)._adoptElementHandle(handle, to); + } + async inputActionEpilogue() { + await this._mainFrameSession._client.send("Page.enable").catch((e) => { + }); + } + async resetForReuse(progress2) { + await this.rawMouse.move(progress2, -1, -1, "none", /* @__PURE__ */ new Set(), /* @__PURE__ */ new Set(), true); + } + async pdf(options) { + return this._pdf.generate(options); + } + coverage() { + return this._coverage; + } + async getFrameElement(frame) { + let parent = frame.parentFrame(); + if (!parent) + throw new Error("Frame has been detached."); + const parentSession = this._sessionForFrame(parent); + const { backendNodeId } = await parentSession._client.send("DOM.getFrameOwner", { frameId: frame._id }).catch((e) => { + if (e instanceof Error && e.message.includes("Frame with the given id was not found.")) + rewriteErrorMessage(e, "Frame has been detached."); + throw e; + }); + parent = frame.parentFrame(); + if (!parent) + throw new Error("Frame has been detached."); + return parentSession._adoptBackendNodeId(backendNodeId, await parent.mainContext()); + } + shouldToggleStyleSheetToSyncAnimations() { + return false; + } + async setDockTile(image) { + await this._mainFrameSession._client.send("Browser.setDockTile", { image: image.toString("base64") }); + } + }; + FrameSession = class _FrameSession { + constructor(crPage, client, targetId, parentSession) { + this._childSessions = /* @__PURE__ */ new Set(); + this._contextIdToContext = /* @__PURE__ */ new Map(); + this._eventListeners = []; + this._firstNonInitialNavigationCommittedFulfill = () => { + }; + this._firstNonInitialNavigationCommittedReject = (e) => { + }; + // Marks the oopif session that remote -> local transition has happened in the parent. + // See Target.detachedFromTarget handler for details. + this._swappedIn = false; + this._workerSessions = /* @__PURE__ */ new Map(); + this._initScriptIds = /* @__PURE__ */ new Map(); + this._client = client; + this._crPage = crPage; + this._page = crPage._page; + this._targetId = targetId; + this._parentSession = parentSession; + if (parentSession) + parentSession._childSessions.add(this); + this._firstNonInitialNavigationCommittedPromise = new Promise((f, r) => { + this._firstNonInitialNavigationCommittedFulfill = f; + this._firstNonInitialNavigationCommittedReject = r; + }); + this._firstNonInitialNavigationCommittedPromise.catch(() => { + }); + } + _isMainFrame() { + return this._targetId === this._crPage._targetId; + } + _addRendererListeners() { + this._eventListeners.push(...[ + eventsHelper.addEventListener(this._client, "Log.entryAdded", (event) => this._onLogEntryAdded(event)), + eventsHelper.addEventListener(this._client, "Page.fileChooserOpened", (event) => this._onFileChooserOpened(event)), + eventsHelper.addEventListener(this._client, "Page.frameAttached", (event) => this._onFrameAttached(event.frameId, event.parentFrameId)), + eventsHelper.addEventListener(this._client, "Page.frameDetached", (event) => this._onFrameDetached(event.frameId, event.reason)), + eventsHelper.addEventListener(this._client, "Page.frameNavigated", (event) => this._onFrameNavigated(event.frame, false)), + eventsHelper.addEventListener(this._client, "Page.frameRequestedNavigation", (event) => this._onFrameRequestedNavigation(event)), + eventsHelper.addEventListener(this._client, "Page.javascriptDialogOpening", (event) => this._onDialog(event)), + eventsHelper.addEventListener(this._client, "Page.navigatedWithinDocument", (event) => this._onFrameNavigatedWithinDocument(event.frameId, event.url)), + eventsHelper.addEventListener(this._client, "Runtime.bindingCalled", (event) => this._onBindingCalled(event)), + eventsHelper.addEventListener(this._client, "Runtime.consoleAPICalled", (event) => this._onConsoleAPI(event)), + eventsHelper.addEventListener(this._client, "Runtime.exceptionThrown", (exception) => this._handleException(exception.exceptionDetails)), + eventsHelper.addEventListener(this._client, "Runtime.executionContextCreated", (event) => this._onExecutionContextCreated(event.context)), + eventsHelper.addEventListener(this._client, "Runtime.executionContextDestroyed", (event) => this._onExecutionContextDestroyed(event.executionContextId)), + eventsHelper.addEventListener(this._client, "Runtime.executionContextsCleared", (event) => this._onExecutionContextsCleared()) + ]); + } + _addBrowserListeners() { + this._eventListeners.push(...[ + eventsHelper.addEventListener(this._client, "Target.attachedToTarget", (event) => this._onAttachedToTarget(event)), + eventsHelper.addEventListener(this._client, "Target.detachedFromTarget", (event) => this._onDetachedFromTarget(event)), + eventsHelper.addEventListener(this._client, "Inspector.targetCrashed", (event) => this._onTargetCrashed()), + eventsHelper.addEventListener(this._client, "Page.screencastFrame", (event) => this._onScreencastFrame(event)), + eventsHelper.addEventListener(this._client, "Page.windowOpen", (event) => this._onWindowOpen(event)) + ]); + } + async _initialize(hasUIWindow) { + if (!this._page.isStorageStatePage && hasUIWindow && !this._crPage._browserContext._browser.isClank() && !this._crPage._browserContext._options.noDefaultViewport) { + try { + const { windowId } = await this._client.send("Browser.getWindowForTarget"); + this._windowId = windowId; + } catch { + } + } + if (this._isMainFrame() && hasUIWindow && !this._page.isStorageStatePage) + startAutomaticVideoRecording(this._crPage._page); + let lifecycleEventsEnabled; + if (!this._isMainFrame()) + this._addRendererListeners(); + this._addBrowserListeners(); + this._bufferedAttachedToTargetEvents = []; + const promises = [ + this._client.send("Page.enable"), + this._client.send("Page.getFrameTree").then(({ frameTree }) => { + if (this._isMainFrame()) { + this._handleFrameTree(frameTree); + this._addRendererListeners(); + } + const attachedToTargetEvents = this._bufferedAttachedToTargetEvents || []; + this._bufferedAttachedToTargetEvents = void 0; + for (const event of attachedToTargetEvents) + this._onAttachedToTarget(event); + const localFrames = this._isMainFrame() ? this._page.frames() : [this._page.frameManager.frame(this._targetId)]; + for (const frame of localFrames) { + this._client._sendMayFail("Page.createIsolatedWorld", { + frameId: frame._id, + grantUniveralAccess: true, + worldName: this._crPage.utilityWorldName + }); + } + const isInitialEmptyPage = this._isMainFrame() && this._page.mainFrame().url() === ":"; + if (isInitialEmptyPage) { + lifecycleEventsEnabled.catch((e) => { + }).then(() => { + this._eventListeners.push(eventsHelper.addEventListener(this._client, "Page.lifecycleEvent", (event) => this._onLifecycleEvent(event))); + }); + } else { + this._firstNonInitialNavigationCommittedFulfill(); + this._eventListeners.push(eventsHelper.addEventListener(this._client, "Page.lifecycleEvent", (event) => this._onLifecycleEvent(event))); + } + }), + this._client.send("Log.enable", {}), + lifecycleEventsEnabled = this._client.send("Page.setLifecycleEventsEnabled", { enabled: true }), + this._client.send("Runtime.enable", {}), + this._client.send("Page.addScriptToEvaluateOnNewDocument", { + source: "", + worldName: this._crPage.utilityWorldName + }), + this._crPage._networkManager.addSession(this._client, void 0, this._isMainFrame()), + this._client.send("Target.setAutoAttach", { autoAttach: true, waitForDebuggerOnStart: true, flatten: true }) + ]; + if (!this._page.isStorageStatePage) { + const skipDefaultOverrides = this._crPage._browserContext._browser.options.noDefaults && this._crPage._browserContext === this._crPage._browserContext._browser._defaultContext; + if (this._crPage._browserContext.needsPlaywrightBinding()) + promises.push(this.exposePlaywrightBinding()); + if (this._isMainFrame() && !skipDefaultOverrides) + promises.push(this._client.send("Emulation.setFocusEmulationEnabled", { enabled: true })); + const options = this._crPage._browserContext._options; + if (options.bypassCSP) + promises.push(this._client.send("Page.setBypassCSP", { enabled: true })); + if (options.ignoreHTTPSErrors || options.internalIgnoreHTTPSErrors) + promises.push(this._client.send("Security.setIgnoreCertificateErrors", { ignore: true })); + if (this._isMainFrame()) + promises.push(this._updateViewport()); + if (options.hasTouch) + promises.push(this._client.send("Emulation.setTouchEmulationEnabled", { enabled: true })); + if (options.javaScriptEnabled === false) + promises.push(this._client.send("Emulation.setScriptExecutionDisabled", { value: true })); + if (options.userAgent || options.locale) + promises.push(this._updateUserAgent()); + if (options.locale) + promises.push(emulateLocale(this._client, options.locale)); + if (options.timezoneId) + promises.push(emulateTimezone(this._client, options.timezoneId)); + if (!this._crPage._browserContext._browser.options.headful) + promises.push(this._setDefaultFontFamilies(this._client)); + promises.push(this._updateGeolocation(true)); + if (!skipDefaultOverrides) + promises.push(this._updateEmulateMedia()); + promises.push(this._updateFileChooserInterception(true)); + for (const initScript of this._crPage._page.allInitScripts()) + promises.push(this._evaluateOnNewDocument( + initScript, + "main", + true + /* runImmediately */ + )); + } + promises.push(this._client.send("Runtime.runIfWaitingForDebugger")); + promises.push(this._firstNonInitialNavigationCommittedPromise); + await Promise.all(promises); + } + dispose() { + this._firstNonInitialNavigationCommittedReject(new TargetClosedError(this._page.closeReason())); + for (const childSession of this._childSessions) + childSession.dispose(); + if (this._parentSession) + this._parentSession._childSessions.delete(this); + eventsHelper.removeEventListeners(this._eventListeners); + this._crPage._networkManager.removeSession(this._client); + this._crPage._sessions.delete(this._targetId); + this._client.dispose(); + } + async _navigate(frame, url2, referrer) { + const response2 = await this._client.send("Page.navigate", { url: url2, referrer, frameId: frame._id, referrerPolicy: "unsafeUrl" }); + if (response2.isDownload) + throw new NavigationAbortedError(response2.loaderId, "Download is starting"); + if (response2.errorText) + throw new NavigationAbortedError(response2.loaderId, `${response2.errorText} at ${url2}`); + return { newDocumentId: response2.loaderId }; + } + _onLifecycleEvent(event) { + if (this._eventBelongsToStaleFrame(event.frameId)) + return; + if (event.name === "load") + this._page.frameManager.frameLifecycleEvent(event.frameId, "load"); + else if (event.name === "DOMContentLoaded") + this._page.frameManager.frameLifecycleEvent(event.frameId, "domcontentloaded"); + } + _handleFrameTree(frameTree) { + this._onFrameAttached(frameTree.frame.id, frameTree.frame.parentId || null); + this._onFrameNavigated(frameTree.frame, true); + if (!frameTree.childFrames) + return; + for (const child of frameTree.childFrames) + this._handleFrameTree(child); + } + _eventBelongsToStaleFrame(frameId) { + const frame = this._page.frameManager.frame(frameId); + if (!frame) + return true; + const session2 = this._crPage._sessionForFrame(frame); + return session2 && session2 !== this && !session2._swappedIn; + } + _onFrameAttached(frameId, parentFrameId) { + const frameSession = this._crPage._sessions.get(frameId); + if (frameSession && frameId !== this._targetId) { + frameSession._swappedIn = true; + const frame = this._page.frameManager.frame(frameId); + if (frame) + this._page.frameManager.removeChildFramesRecursively(frame); + return; + } + if (parentFrameId && !this._page.frameManager.frame(parentFrameId)) { + return; + } + this._page.frameManager.frameAttached(frameId, parentFrameId); + } + _onFrameNavigated(framePayload, initial) { + if (this._eventBelongsToStaleFrame(framePayload.id)) + return; + this._page.frameManager.frameCommittedNewDocumentNavigation(framePayload.id, framePayload.url + (framePayload.urlFragment || ""), framePayload.name || "", framePayload.loaderId, initial); + if (!initial) + this._firstNonInitialNavigationCommittedFulfill(); + } + _onFrameRequestedNavigation(payload) { + if (this._eventBelongsToStaleFrame(payload.frameId)) + return; + if (payload.disposition === "currentTab") + this._page.frameManager.frameRequestedNavigation(payload.frameId); + } + _onFrameNavigatedWithinDocument(frameId, url2) { + if (this._eventBelongsToStaleFrame(frameId)) + return; + this._page.frameManager.frameCommittedSameDocumentNavigation(frameId, url2); + } + _onFrameDetached(frameId, reason) { + if (this._crPage._sessions.has(frameId)) { + return; + } + if (reason === "swap") { + const frame = this._page.frameManager.frame(frameId); + if (frame) + this._page.frameManager.removeChildFramesRecursively(frame); + return; + } + this._page.frameManager.frameDetached(frameId); + } + _onExecutionContextCreated(contextPayload) { + const frame = contextPayload.auxData ? this._page.frameManager.frame(contextPayload.auxData.frameId) : null; + if (!frame || this._eventBelongsToStaleFrame(frame._id)) + return; + const delegate = new CRExecutionContext(this._client, contextPayload); + let worldName = null; + if (contextPayload.auxData && !!contextPayload.auxData.isDefault) + worldName = "main"; + else if (contextPayload.name === this._crPage.utilityWorldName) + worldName = "utility"; + const context2 = new FrameExecutionContext(delegate, frame, worldName); + if (worldName) + frame.contextCreated(worldName, context2); + this._contextIdToContext.set(contextPayload.id, context2); + } + _onExecutionContextDestroyed(executionContextId) { + const context2 = this._contextIdToContext.get(executionContextId); + if (!context2) + return; + this._contextIdToContext.delete(executionContextId); + context2.frame.contextDestroyed(context2); + } + _onExecutionContextsCleared() { + for (const contextId4 of Array.from(this._contextIdToContext.keys())) + this._onExecutionContextDestroyed(contextId4); + } + _onAttachedToTarget(event) { + if (this._bufferedAttachedToTargetEvents) { + this._bufferedAttachedToTargetEvents.push(event); + return; + } + const session2 = this._client.createChildSession(event.sessionId); + if (event.targetInfo.type === "iframe") { + const targetId = event.targetInfo.targetId; + let frame = this._page.frameManager.frame(targetId); + if (!frame && event.targetInfo.parentFrameId) { + frame = this._page.frameManager.frameAttached(targetId, event.targetInfo.parentFrameId); + } + if (!frame) + return; + this._page.frameManager.removeChildFramesRecursively(frame); + for (const [contextId4, context2] of this._contextIdToContext) { + if (context2.frame === frame) + this._onExecutionContextDestroyed(contextId4); + } + const frameSession = new _FrameSession(this._crPage, session2, targetId, this); + this._crPage._sessions.set(targetId, frameSession); + frameSession._initialize(false).catch((e) => e); + return; + } + if (event.targetInfo.type !== "worker") { + session2.detach().catch(() => { + }); + return; + } + const url2 = event.targetInfo.url; + const worker = new Worker(this._page, url2); + this._page.addWorker(event.sessionId, worker); + this._workerSessions.set(event.sessionId, session2); + session2.once("Runtime.executionContextCreated", async (event2) => { + worker.createExecutionContext(new CRExecutionContext(session2, event2.context)); + }); + if (this._crPage._browserContext._browser.majorVersion() >= 143) + session2.on("Inspector.workerScriptLoaded", () => worker.workerScriptLoaded()); + else + worker.workerScriptLoaded(); + session2._sendMayFail("Runtime.enable"); + this._crPage._networkManager.addSession(session2, this._page.frameManager.frame(event.targetInfo.parentFrameId ?? this._targetId) ?? void 0).catch(() => { + }); + session2._sendMayFail("Runtime.runIfWaitingForDebugger"); + session2._sendMayFail("Target.setAutoAttach", { autoAttach: true, waitForDebuggerOnStart: true, flatten: true }); + session2.on("Target.attachedToTarget", (event2) => this._onAttachedToTarget(event2)); + session2.on("Target.detachedFromTarget", (event2) => this._onDetachedFromTarget(event2)); + session2.on("Runtime.consoleAPICalled", (event2) => { + const args = event2.args.map((o) => createHandle(worker.existingExecutionContext, o)); + this._page.addConsoleMessage(worker, event2.type, args, stackTraceToLocation(event2.stackTrace), void 0, event2.timestamp); + }); + session2.on("Runtime.exceptionThrown", (exception) => this._page.addPageError(exceptionToError(exception.exceptionDetails), stackTraceToLocation(exception.exceptionDetails.stackTrace))); + } + _onDetachedFromTarget(event) { + const workerSession = this._workerSessions.get(event.sessionId); + if (workerSession) { + workerSession.dispose(); + this._page.removeWorker(event.sessionId); + return; + } + const childFrameSession = this._crPage._sessions.get(event.targetId); + if (!childFrameSession) + return; + if (childFrameSession._swappedIn) { + childFrameSession.dispose(); + return; + } + this._client.send("Page.enable").catch((e) => null).then(() => { + if (!childFrameSession._swappedIn) + this._page.frameManager.frameDetached(event.targetId); + childFrameSession.dispose(); + }); + } + _onWindowOpen(event) { + this._crPage._nextWindowOpenPopupFeatures.push(event.windowFeatures); + } + async _onConsoleAPI(event) { + if (event.executionContextId === 0) { + return; + } + const context2 = this._contextIdToContext.get(event.executionContextId); + if (!context2) + return; + const values = event.args.map((arg) => createHandle(context2, arg)); + this._page.addConsoleMessage(null, event.type, values, stackTraceToLocation(event.stackTrace), void 0, event.timestamp); + } + async _onBindingCalled(event) { + const pageOrError = await this._crPage._page.waitForInitializedOrError(); + if (!(pageOrError instanceof Error)) { + const context2 = this._contextIdToContext.get(event.executionContextId); + if (context2) + await this._page.onBindingCalled(event.payload, context2); + } + } + _onDialog(event) { + if (!this._page.frameManager.frame(this._targetId)) + return; + this._page.browserContext.dialogManager.dialogDidOpen(new Dialog( + this._page, + event.type, + event.message, + async (accept, promptText) => { + if (this._isMainFrame() && event.type === "beforeunload" && !accept) + this._page.frameManager.frameAbortedNavigation(this._page.mainFrame()._id, "navigation cancelled by beforeunload dialog"); + await this._client.send("Page.handleJavaScriptDialog", { accept, promptText }); + }, + event.defaultPrompt + )); + } + _handleException(exceptionDetails) { + this._page.addPageError(exceptionToError(exceptionDetails), stackTraceToLocation(exceptionDetails.stackTrace)); + } + async _onTargetCrashed() { + this._client._markAsCrashed(); + this._page._didCrash(); + } + _onLogEntryAdded(event) { + const { level, text: text2, args, source: source11, url: url2, lineNumber } = event.entry; + if (args) + args.map((arg) => releaseObject(this._client, arg.objectId)); + if (source11 !== "worker") { + const location2 = { + url: url2 || "", + lineNumber: lineNumber || 0, + columnNumber: 0 + }; + this._page.addConsoleMessage(null, level, [], location2, text2, event.entry.timestamp); + } + } + async _onFileChooserOpened(event) { + if (!event.backendNodeId) + return; + const frame = this._page.frameManager.frame(event.frameId); + if (!frame) + return; + let handle; + try { + const utilityContext = await frame.utilityContext(); + handle = await this._adoptBackendNodeId(event.backendNodeId, utilityContext); + } catch (e) { + return; + } + await this._page._onFileChooserOpened(handle); + } + _willBeginDownload() { + if (!this._crPage._page.initializedOrUndefined()) { + this._firstNonInitialNavigationCommittedReject(new Error("Starting new page download")); + } + } + _onScreencastFrame(payload) { + const buffer = Buffer.from(payload.data, "base64"); + this._page.screencast.onScreencastFrame({ + buffer, + frameSwapWallTime: payload.metadata.timestamp ? payload.metadata.timestamp * 1e3 : Date.now(), + viewportWidth: payload.metadata.deviceWidth, + viewportHeight: payload.metadata.deviceHeight + }, () => { + this._client._sendMayFail("Page.screencastFrameAck", { sessionId: payload.sessionId }); + }); + } + async _updateGeolocation(initial) { + const geolocation = this._crPage._browserContext._options.geolocation; + if (!initial || geolocation) + await this._client.send("Emulation.setGeolocationOverride", geolocation || {}); + } + async _updateViewport(preserveWindowBoundaries) { + if (this._crPage._browserContext._browser.isClank()) + return; + assert(this._isMainFrame()); + const options = this._crPage._browserContext._options; + const emulatedSize = this._page.emulatedSize(); + if (!emulatedSize) + return; + const viewportSize = emulatedSize.viewport; + const screenSize = emulatedSize.screen; + const isLandscape = screenSize.width > screenSize.height; + const metricsOverride = { + mobile: !!options.isMobile, + width: viewportSize.width, + height: viewportSize.height, + screenWidth: screenSize.width, + screenHeight: screenSize.height, + deviceScaleFactor: options.deviceScaleFactor || 1, + screenOrientation: !!options.isMobile ? isLandscape ? { angle: 90, type: "landscapePrimary" } : { angle: 0, type: "portraitPrimary" } : { angle: 0, type: "landscapePrimary" }, + dontSetVisibleSize: preserveWindowBoundaries + }; + if (JSON.stringify(this._metricsOverride) === JSON.stringify(metricsOverride)) + return; + const promises = []; + if (!preserveWindowBoundaries && this._windowId) { + let insets = { width: 0, height: 0 }; + if (this._crPage._browserContext._browser.options.headful) { + insets = { width: 24, height: 88 }; + if (process.platform === "win32") + insets = { width: 16, height: 88 }; + else if (process.platform === "linux") + insets = { width: 8, height: 85 }; + else if (process.platform === "darwin") + insets = { width: 2, height: 80 }; + if (this._crPage._browserContext.isPersistentContext()) { + insets.height += 46; + } + } + promises.push(this.setWindowBounds({ + width: viewportSize.width + insets.width, + height: viewportSize.height + insets.height + })); + } + promises.push(this._client.send("Emulation.setDeviceMetricsOverride", metricsOverride)); + await Promise.all(promises); + this._metricsOverride = metricsOverride; + } + async windowBounds() { + const { bounds } = await this._client.send("Browser.getWindowBounds", { + windowId: this._windowId + }); + return bounds; + } + async setWindowBounds(bounds) { + return await this._client.send("Browser.setWindowBounds", { + windowId: this._windowId, + bounds + }); + } + async _updateEmulateMedia() { + const emulatedMedia = this._page.emulatedMedia(); + const media = emulatedMedia.media === "no-override" ? "" : emulatedMedia.media; + const colorScheme = emulatedMedia.colorScheme === "no-override" ? "" : emulatedMedia.colorScheme; + const reducedMotion = emulatedMedia.reducedMotion === "no-override" ? "" : emulatedMedia.reducedMotion; + const forcedColors = emulatedMedia.forcedColors === "no-override" ? "" : emulatedMedia.forcedColors; + const contrast = emulatedMedia.contrast === "no-override" ? "" : emulatedMedia.contrast; + const features = [ + { name: "prefers-color-scheme", value: colorScheme }, + { name: "prefers-reduced-motion", value: reducedMotion }, + { name: "forced-colors", value: forcedColors }, + { name: "prefers-contrast", value: contrast } + ]; + await this._client.send("Emulation.setEmulatedMedia", { media, features }); + } + async _updateUserAgent() { + const options = this._crPage._browserContext._options; + await this._client.send("Emulation.setUserAgentOverride", { + userAgent: options.userAgent || "", + acceptLanguage: options.locale, + userAgentMetadata: calculateUserAgentMetadata(options) + }); + } + async _setDefaultFontFamilies(session2) { + const fontFamilies = platformToFontFamilies[this._crPage._browserContext._browser._platform()]; + await session2.send("Page.setFontFamilies", fontFamilies); + } + async _updateFileChooserInterception(initial) { + const enabled = this._page.fileChooserIntercepted(); + if (initial && !enabled) + return; + await this._client.send("Page.setInterceptFileChooserDialog", { enabled }).catch(() => { + }); + } + async _evaluateOnNewDocument(initScript, world, runImmediately) { + const worldName = world === "utility" ? this._crPage.utilityWorldName : void 0; + const { identifier } = await this._client.send("Page.addScriptToEvaluateOnNewDocument", { source: initScript.source, worldName, runImmediately }); + this._initScriptIds.set(initScript, identifier); + } + async _removeEvaluatesOnNewDocument(initScripts) { + const ids = []; + for (const script of initScripts) { + const id = this._initScriptIds.get(script); + if (id) + ids.push(id); + this._initScriptIds.delete(script); + } + await Promise.all(ids.map((identifier) => this._client.send("Page.removeScriptToEvaluateOnNewDocument", { identifier }).catch(() => { + }))); + } + async exposePlaywrightBinding() { + await this._client.send("Runtime.addBinding", { name: PageBinding.kBindingName }); + } + async _getContentFrame(handle) { + const nodeInfo = await this._client.send("DOM.describeNode", { + objectId: handle._objectId + }); + if (!nodeInfo || typeof nodeInfo.node.frameId !== "string") + return null; + return this._page.frameManager.frame(nodeInfo.node.frameId); + } + async _getOwnerFrame(handle) { + const documentElement = await handle.evaluateHandle((node) => { + const doc = node; + if (doc.documentElement && doc.documentElement.ownerDocument === doc) + return doc.documentElement; + return node.ownerDocument ? node.ownerDocument.documentElement : null; + }); + if (!documentElement) + return null; + if (!documentElement._objectId) + return null; + const nodeInfo = await this._client.send("DOM.describeNode", { + objectId: documentElement._objectId + }); + const frameId = nodeInfo && typeof nodeInfo.node.frameId === "string" ? nodeInfo.node.frameId : null; + documentElement.dispose(); + return frameId; + } + async _getBoundingBox(handle) { + const result2 = await this._client._sendMayFail("DOM.getBoxModel", { + objectId: handle._objectId + }); + if (!result2) + return null; + const quad = result2.model.border; + const x = Math.min(quad[0], quad[2], quad[4], quad[6]); + const y = Math.min(quad[1], quad[3], quad[5], quad[7]); + const width = Math.max(quad[0], quad[2], quad[4], quad[6]) - x; + const height = Math.max(quad[1], quad[3], quad[5], quad[7]) - y; + const position = await this._framePosition(); + if (!position) + return null; + return { x: x + position.x, y: y + position.y, width, height }; + } + async _framePosition() { + const frame = this._page.frameManager.frame(this._targetId); + if (!frame) + return null; + if (frame === this._page.mainFrame()) + return { x: 0, y: 0 }; + const element2 = await frame.frameElement(nullProgress); + const box = await element2.boundingBox(nullProgress); + return box; + } + async _scrollRectIntoViewIfNeeded(handle, rect) { + return await this._client.send("DOM.scrollIntoViewIfNeeded", { + objectId: handle._objectId, + rect + }).then(() => "done").catch((e) => { + if (e instanceof Error && e.message.includes("Node does not have a layout object")) + return "error:notvisible"; + if (e instanceof Error && e.message.includes("Node is detached from document")) + return "error:notconnected"; + throw e; + }); + } + async _getContentQuads(handle) { + const result2 = await this._client._sendMayFail("DOM.getContentQuads", { + objectId: handle._objectId + }); + if (!result2) + return null; + const position = await this._framePosition(); + if (!position) + return null; + return result2.quads.map((quad) => [ + { x: quad[0] + position.x, y: quad[1] + position.y }, + { x: quad[2] + position.x, y: quad[3] + position.y }, + { x: quad[4] + position.x, y: quad[5] + position.y }, + { x: quad[6] + position.x, y: quad[7] + position.y } + ]); + } + async _adoptElementHandle(handle, to) { + const nodeInfo = await this._client.send("DOM.describeNode", { + objectId: handle._objectId + }); + return this._adoptBackendNodeId(nodeInfo.node.backendNodeId, to); + } + async _adoptBackendNodeId(backendNodeId, to) { + const result2 = await this._client._sendMayFail("DOM.resolveNode", { + backendNodeId, + executionContextId: to.delegate._contextId + }); + if (!result2 || result2.object.subtype === "null") + throw new Error(kUnableToAdoptErrorMessage); + return createHandle(to, result2.object).asElement(); + } + }; + } +}); + +// packages/playwright-core/src/server/chromium/crServiceWorker.ts +var CRServiceWorker; +var init_crServiceWorker = __esm({ + "packages/playwright-core/src/server/chromium/crServiceWorker.ts"() { + "use strict"; + init_page(); + init_crExecutionContext(); + init_crNetworkManager(); + init_browserContext(); + init_network2(); + init_console(); + init_crProtocolHelper(); + init_crPage(); + CRServiceWorker = class extends Worker { + constructor(browserContext, session2, url2) { + super(browserContext, url2); + this._session = session2; + this.browserContext = browserContext; + if (!process.env.PLAYWRIGHT_DISABLE_SERVICE_WORKER_NETWORK) + this._networkManager = new CRNetworkManager(null, this); + session2.on("Inspector.targetCrashed", () => this.destroyExecutionContext("Service worker restarted")); + session2.on("Runtime.executionContextCreated", (event) => { + this.createExecutionContext(new CRExecutionContext(session2, event.context)); + if (this.browserContext._browser.majorVersion() < 143) + this.workerScriptLoaded(); + }); + if (this.browserContext._browser.majorVersion() >= 143) + session2.on("Inspector.workerScriptLoaded", () => this.workerScriptLoaded()); + if (this._networkManager && this._isNetworkInspectionEnabled()) { + this.updateRequestInterception(); + this.updateExtraHTTPHeaders(); + this.updateHttpCredentials(); + this.updateOffline(); + this.updateUserAgent(); + this._networkManager.addSession( + session2, + void 0, + true + /* isMain */ + ).catch(() => { + }); + } + session2.on("Runtime.consoleAPICalled", (event) => { + if (!this.existingExecutionContext || process.env.PLAYWRIGHT_DISABLE_SERVICE_WORKER_CONSOLE) + return; + const args = event.args.map((o) => createHandle(this.existingExecutionContext, o)); + const message = new ConsoleMessage(null, this, event.type, void 0, args, stackTraceToLocation(event.stackTrace), event.timestamp); + this.browserContext.emit(BrowserContext.Events.Console, message); + }); + session2.send("Runtime.enable", {}).catch((e) => { + }); + session2.send("Runtime.runIfWaitingForDebugger").catch((e) => { + }); + session2.on("Inspector.targetReloadedAfterCrash", () => { + session2._sendMayFail("Runtime.runIfWaitingForDebugger", {}); + }); + } + didClose() { + this._networkManager?.removeSession(this._session); + this._session.dispose(); + super.didClose(); + } + async updateOffline() { + if (!this._isNetworkInspectionEnabled()) + return; + await this._networkManager?.setOffline(!!this.browserContext._options.offline).catch(() => { + }); + } + async updateHttpCredentials() { + if (!this._isNetworkInspectionEnabled()) + return; + await this._networkManager?.authenticate(this.browserContext._options.httpCredentials || null).catch(() => { + }); + } + async updateExtraHTTPHeaders() { + if (!this._isNetworkInspectionEnabled()) + return; + await this._networkManager?.setExtraHTTPHeaders(this.browserContext._options.extraHTTPHeaders || []).catch(() => { + }); + } + async updateUserAgent() { + const options = this.browserContext._options; + await this._session.send("Emulation.setUserAgentOverride", { + userAgent: options.userAgent || "", + acceptLanguage: options.locale, + userAgentMetadata: calculateUserAgentMetadata(options) + }).catch(() => { + }); + } + async updateRequestInterception() { + if (!this._isNetworkInspectionEnabled()) + return; + await this._networkManager?.setRequestInterception(this.needsRequestInterception()).catch(() => { + }); + } + needsRequestInterception() { + return this._isNetworkInspectionEnabled() && this.browserContext.requestInterceptors.length > 0; + } + reportRequestFinished(request2, response2) { + this.browserContext.emit(BrowserContext.Events.RequestFinished, { request: request2, response: response2 }); + } + requestFailed(request2, _canceled) { + this.browserContext.emit(BrowserContext.Events.RequestFailed, request2); + } + requestReceivedResponse(response2) { + this.browserContext.emit(BrowserContext.Events.Response, response2); + } + requestStarted(request2, route2) { + this.browserContext.emit(BrowserContext.Events.Request, request2); + if (route2) + new Route(request2, route2).handle(this.browserContext.requestInterceptors); + } + _isNetworkInspectionEnabled() { + return this.browserContext._options.serviceWorkers !== "block"; + } + }; + } +}); + +// packages/playwright-core/src/server/chromium/crBrowser.ts +function shouldProxyLoopback(bypass) { + if (process.env.PLAYWRIGHT_DISABLE_FORCED_CHROMIUM_PROXIED_LOOPBACK) + return false; + const hosts = (bypass || "").split(",").map((s) => s.trim()); + const shouldBypassSomeLoopback = ["localhost", "127.0.0.1", "::1", "[::]", "[::1]", "", "<-loopback>"].some((host) => hosts.includes(host)); + return !shouldBypassSomeLoopback; +} +var import_path22, CRBrowser, CREvents, CRBrowserContext; +var init_crBrowser = __esm({ + "packages/playwright-core/src/server/chromium/crBrowser.ts"() { + "use strict"; + import_path22 = __toESM(require("path")); + init_assert(); + init_crypto(); + init_artifact(); + init_browser(); + init_browserContext(); + init_frames(); + init_network2(); + init_page(); + init_crConnection(); + init_crPage(); + init_crProtocolHelper(); + init_crServiceWorker(); + CRBrowser = class _CRBrowser extends Browser { + constructor(parent, connection, options) { + super(parent, options); + this._clientRootSessionPromise = null; + this._contexts = /* @__PURE__ */ new Map(); + this._crPages = /* @__PURE__ */ new Map(); + this._serviceWorkers = /* @__PURE__ */ new Map(); + this._version = ""; + this._majorVersion = 0; + this._revision = ""; + this._tracingRecording = false; + this._userAgent = ""; + this._connection = connection; + this._session = this._connection.rootSession; + this._connection.on(ConnectionEvents.Disconnected, () => this._didDisconnect()); + this._session.on("Target.attachedToTarget", this._onAttachedToTarget.bind(this)); + this._session.on("Target.detachedFromTarget", this._onDetachedFromTarget.bind(this)); + this._session.on("Browser.downloadWillBegin", this._onDownloadWillBegin.bind(this)); + this._session.on("Browser.downloadProgress", this._onDownloadProgress.bind(this)); + } + static async connect(parent, transport, options, devtools) { + options = { ...options }; + const connection = new CRConnection(parent, transport, options.protocolLogger, options.browserLogsCollector); + const browser = new _CRBrowser(parent, connection, options); + browser._devtools = devtools; + if (browser.isClank()) + browser._isBrowserCollocatedWithServer = false; + const session2 = connection.rootSession; + if (options.__testHookOnConnectToBrowser) + await options.__testHookOnConnectToBrowser(); + const version3 = await session2.send("Browser.getVersion"); + browser._revision = version3.revision; + browser._version = version3.product.substring(version3.product.indexOf("/") + 1); + try { + browser._majorVersion = +browser._version.split(".")[0]; + } catch { + } + browser._userAgent = version3.userAgent; + browser.options.headful = !version3.userAgent.includes("Headless"); + if (!options.persistent) { + await session2.send("Target.setAutoAttach", { autoAttach: true, waitForDebuggerOnStart: true, flatten: true }); + return browser; + } + browser._defaultContext = new CRBrowserContext(browser, void 0, options.persistent); + await Promise.all([ + session2.send("Target.setAutoAttach", { autoAttach: true, waitForDebuggerOnStart: true, flatten: true }).then(async () => { + await session2.send("Target.getTargetInfo"); + }), + browser._defaultContext.initialize() + ]); + await browser._waitForAllPagesToBeInitialized(); + return browser; + } + async doCreateNewContext(options) { + const proxy = options.proxyOverride || options.proxy; + let proxyBypassList = void 0; + if (proxy) { + if (shouldProxyLoopback(proxy.bypass)) + proxyBypassList = "<-loopback>" + (proxy.bypass ? `,${proxy.bypass}` : ""); + else + proxyBypassList = proxy.bypass; + } + const { browserContextId } = await this._session.send("Target.createBrowserContext", { + disposeOnDetach: true, + proxyServer: proxy ? proxy.server : void 0, + proxyBypassList + }); + const context2 = new CRBrowserContext(this, browserContextId, options); + await context2.initialize(); + this._contexts.set(browserContextId, context2); + return context2; + } + contexts() { + return Array.from(this._contexts.values()); + } + version() { + return this._version; + } + majorVersion() { + return this._majorVersion; + } + userAgent() { + return this._userAgent; + } + _platform() { + if (this._userAgent.includes("Windows")) + return "win"; + if (this._userAgent.includes("Macintosh")) + return "mac"; + return "linux"; + } + isClank() { + return this.options.name === "clank"; + } + async _waitForAllPagesToBeInitialized() { + await Promise.all([...this._crPages.values()].map((crPage) => crPage._page.waitForInitializedOrError())); + } + _onAttachedToTarget({ targetInfo, sessionId, waitingForDebugger }) { + if (targetInfo.type === "browser") + return; + const session2 = this._session.createChildSession(sessionId); + assert(targetInfo.browserContextId, "targetInfo: " + JSON.stringify(targetInfo, null, 2)); + let context2 = this._contexts.get(targetInfo.browserContextId) || null; + if (!context2) { + context2 = this._defaultContext; + } + if (targetInfo.type === "other" && targetInfo.url.startsWith("devtools://devtools") && this._devtools) { + this._devtools.install(session2); + return; + } + const treatOtherAsPage = targetInfo.type === "other" && process.env.PW_CHROMIUM_ATTACH_TO_OTHER; + if (!context2 || targetInfo.type === "other" && !treatOtherAsPage) { + session2.detach().catch(() => { + }); + return; + } + assert(!this._crPages.has(targetInfo.targetId), "Duplicate target " + targetInfo.targetId); + assert(!this._serviceWorkers.has(targetInfo.targetId), "Duplicate target " + targetInfo.targetId); + if (targetInfo.type === "page" || treatOtherAsPage) { + const opener = targetInfo.openerId ? this._crPages.get(targetInfo.openerId) || null : null; + const crPage = new CRPage(session2, targetInfo.targetId, context2, opener, { hasUIWindow: targetInfo.type === "page" }); + this._crPages.set(targetInfo.targetId, crPage); + return; + } + if (targetInfo.type === "service_worker") { + const serviceWorker = new CRServiceWorker(context2, session2, targetInfo.url); + this._serviceWorkers.set(targetInfo.targetId, serviceWorker); + context2.emit(CRBrowserContext.CREvents.ServiceWorker, serviceWorker); + return; + } + session2.detach().catch(() => { + }); + } + _onDetachedFromTarget(payload) { + const targetId = payload.targetId; + const crPage = this._crPages.get(targetId); + if (crPage) { + this._crPages.delete(targetId); + crPage.didClose(); + return; + } + const serviceWorker = this._serviceWorkers.get(targetId); + if (serviceWorker) { + this._serviceWorkers.delete(targetId); + serviceWorker.didClose(); + return; + } + } + _didDisconnect() { + for (const crPage of this._crPages.values()) + crPage.didClose(); + this._crPages.clear(); + for (const serviceWorker of this._serviceWorkers.values()) + serviceWorker.didClose(); + this._serviceWorkers.clear(); + this.didClose(); + } + _findOwningPage(frameId) { + for (const crPage of this._crPages.values()) { + const frame = crPage._page.frameManager.frame(frameId); + if (frame) + return crPage; + } + return null; + } + _onDownloadWillBegin(payload) { + const page = this._findOwningPage(payload.frameId); + if (!page) { + return; + } + page.willBeginDownload(); + let originPage = page._page.initializedOrUndefined(); + if (!originPage && page._opener) + originPage = page._opener._page.initializedOrUndefined(); + if (!originPage) + return; + this.downloadCreated(originPage, payload.guid, payload.url, payload.suggestedFilename); + } + _onDownloadProgress(payload) { + if (payload.state === "completed") + this.downloadFinished(payload.guid, ""); + if (payload.state === "canceled") + this.downloadFinished(payload.guid, this._closeReason || "canceled"); + } + async _closePage(crPage) { + await this._session.send("Target.closeTarget", { targetId: crPage._targetId }); + } + async newBrowserCDPSession() { + return await this._connection.createBrowserSession(); + } + async startTracing(page, options = {}) { + assert(!this._tracingRecording, "Cannot start recording trace while already recording trace."); + this._tracingClient = page ? page.delegate._mainFrameSession._client : this._session; + const defaultCategories = [ + "-*", + "devtools.timeline", + "v8.execute", + "disabled-by-default-devtools.timeline", + "disabled-by-default-devtools.timeline.frame", + "toplevel", + "blink.console", + "blink.user_timing", + "latencyInfo", + "disabled-by-default-devtools.timeline.stack", + "disabled-by-default-v8.cpu_profiler", + "disabled-by-default-v8.cpu_profiler.hires" + ]; + const { + screenshots = false, + categories: categories2 = defaultCategories + } = options; + if (screenshots) + categories2.push("disabled-by-default-devtools.screenshot"); + this._tracingRecording = true; + await this._tracingClient.send("Tracing.start", { + transferMode: "ReturnAsStream", + categories: categories2.join(",") + }); + } + async stopTracing() { + assert(this._tracingClient, "Tracing was not started."); + const [event] = await Promise.all([ + new Promise((f) => this._tracingClient.once("Tracing.tracingComplete", f)), + this._tracingClient.send("Tracing.end") + ]); + const tracingPath = import_path22.default.join(this.options.artifactsDir, createGuid() + ".crtrace"); + await saveProtocolStream(this._tracingClient, event.stream, tracingPath); + this._tracingRecording = false; + const artifact = new Artifact(this, tracingPath); + artifact.reportFinished(); + return artifact; + } + isConnected() { + return !this._connection._closed; + } + async _clientRootSession() { + if (!this._clientRootSessionPromise) + this._clientRootSessionPromise = this._connection.createBrowserSession(); + return this._clientRootSessionPromise; + } + }; + CREvents = { + ServiceWorker: "serviceworker" + }; + CRBrowserContext = class extends BrowserContext { + static { + this.CREvents = CREvents; + } + constructor(browser, browserContextId, options) { + super(browser, options, browserContextId); + this.authenticateProxyViaCredentials(); + } + async initialize() { + assert(!Array.from(this._browser._crPages.values()).some((page) => page._browserContext === this)); + const promises = [super.initialize()]; + if (this._browser.options.name !== "clank" && this._options.acceptDownloads !== "internal-browser-default") { + promises.push(this._browser._session.send("Browser.setDownloadBehavior", { + behavior: this._options.acceptDownloads === "accept" ? "allowAndName" : "deny", + browserContextId: this._browserContextId, + downloadPath: this._browser.options.downloadsPath, + eventsEnabled: true + })); + } + await Promise.all(promises); + } + _crPages() { + return [...this._browser._crPages.values()].filter((crPage) => crPage._browserContext === this); + } + possiblyUninitializedPages() { + return this._crPages().map((crPage) => crPage._page); + } + async doCreateNewPage() { + const { targetId } = await this._browser._session.send("Target.createTarget", { url: "about:blank", browserContextId: this._browserContextId }); + return this._browser._crPages.get(targetId)._page; + } + async doGetCookies(urls) { + const { cookies } = await this._browser._session.send("Storage.getCookies", { browserContextId: this._browserContextId }); + return filterCookies(cookies.map((c) => { + const { name, value: value2, domain, path: path59, expires, httpOnly, secure, sameSite } = c; + const copy = { + name, + value: value2, + domain, + path: path59, + expires, + httpOnly, + secure, + sameSite: sameSite ?? "Lax" + }; + if (c.partitionKey) { + copy._crHasCrossSiteAncestor = c.partitionKey.hasCrossSiteAncestor; + copy.partitionKey = c.partitionKey.topLevelSite; + } + return copy; + }), urls); + } + async addCookies(cookies) { + function toChromiumCookie(cookie) { + const { name, value: value2, url: url2, domain, path: path59, expires, httpOnly, secure, sameSite, partitionKey, _crHasCrossSiteAncestor } = cookie; + const copy = { + name, + value: value2, + url: url2, + domain, + path: path59, + expires, + httpOnly, + secure, + sameSite + }; + if (partitionKey) { + copy.partitionKey = { + topLevelSite: partitionKey, + // _crHasCrossSiteAncestor is non-standard, set it true by default if the cookie is partitioned. + hasCrossSiteAncestor: _crHasCrossSiteAncestor ?? true + }; + } + return copy; + } + await this._browser._session.send("Storage.setCookies", { + cookies: rewriteCookies(cookies).map(toChromiumCookie), + browserContextId: this._browserContextId + }); + } + async doClearCookies() { + await this._browser._session.send("Storage.clearCookies", { browserContextId: this._browserContextId }); + } + async doGrantPermissions(origin, permissions) { + const webPermissionToProtocol = /* @__PURE__ */ new Map([ + ["geolocation", "geolocation"], + ["midi", "midi"], + ["notifications", "notifications"], + ["camera", "videoCapture"], + ["microphone", "audioCapture"], + ["background-sync", "backgroundSync"], + ["ambient-light-sensor", "sensors"], + ["accelerometer", "sensors"], + ["gyroscope", "sensors"], + ["magnetometer", "sensors"], + ["clipboard-read", "clipboardReadWrite"], + ["clipboard-write", "clipboardSanitizedWrite"], + ["payment-handler", "paymentHandler"], + // chrome-specific permissions we have. + ["midi-sysex", "midiSysex"], + ["storage-access", "storageAccess"], + ["local-fonts", "localFonts"], + ["local-network-access", ["localNetworkAccess", "localNetwork", "loopbackNetwork"]], + ["screen-wake-lock", "wakeLockScreen"] + ]); + const grantPermissions = async (mapping) => { + const filtered = permissions.flatMap((permission) => { + const protocolPermission = mapping.get(permission); + if (!protocolPermission) + throw new Error("Unknown permission: " + permission); + return typeof protocolPermission === "string" ? [protocolPermission] : protocolPermission; + }); + await this._browser._session.send("Browser.grantPermissions", { origin: origin === "*" ? void 0 : origin, browserContextId: this._browserContextId, permissions: filtered }); + }; + try { + await grantPermissions(webPermissionToProtocol); + } catch (e) { + const fallbackMapping = new Map(webPermissionToProtocol); + fallbackMapping.set("local-network-access", ["localNetworkAccess"]); + await grantPermissions(fallbackMapping); + } + } + async doClearPermissions() { + await this._browser._session.send("Browser.resetPermissions", { browserContextId: this._browserContextId }); + } + async setGeolocation(geolocation) { + verifyGeolocation(geolocation); + this._options.geolocation = geolocation; + for (const page of this.pages()) + await page.delegate.updateGeolocation(); + } + async doUpdateExtraHTTPHeaders() { + for (const page of this.pages()) + await page.delegate.updateExtraHTTPHeaders(); + for (const sw of this.serviceWorkers()) + await sw.updateExtraHTTPHeaders(); + } + async setUserAgent(userAgent) { + this._options.userAgent = userAgent; + await Promise.all([ + ...this.pages().map((page) => page.delegate.updateUserAgent()), + ...this.serviceWorkers().map((sw) => sw.updateUserAgent()) + ]); + } + async doUpdateOffline() { + for (const page of this.pages()) + await page.delegate.updateOffline(); + for (const sw of this.serviceWorkers()) + await sw.updateOffline(); + } + async doSetHTTPCredentials(httpCredentials) { + this._options.httpCredentials = httpCredentials; + for (const page of this.pages()) + await page.delegate.updateHttpCredentials(); + for (const sw of this.serviceWorkers()) + await sw.updateHttpCredentials(); + } + async doAddInitScript(initScript) { + for (const page of this.pages()) + await page.delegate.addInitScript(initScript); + } + async doRemoveInitScripts(initScripts) { + for (const page of this.pages()) + await page.delegate.removeInitScripts(initScripts); + } + async doUpdateRequestInterception() { + for (const page of this.pages()) + await page.delegate.updateRequestInterception(); + for (const sw of this.serviceWorkers()) + await sw.updateRequestInterception(); + } + async doUpdateDefaultViewport() { + } + async doUpdateDefaultEmulatedMedia() { + } + async doExposePlaywrightBinding() { + for (const page of this._crPages()) + await page.exposePlaywrightBinding(); + } + async doClose(reason) { + await this.dialogManager.closeBeforeUnloadDialogs(); + if (!this._browserContextId) { + return "close-browser"; + } + await Promise.all([...this._downloads].map((download) => download.cancel().catch(() => { + }))); + await this._browser._session.send("Target.disposeBrowserContext", { browserContextId: this._browserContextId }); + this._browser._contexts.delete(this._browserContextId); + for (const [targetId, serviceWorker] of this._browser._serviceWorkers) { + if (serviceWorker.browserContext !== this) + continue; + serviceWorker.didClose(); + this._browser._serviceWorkers.delete(targetId); + } + } + onClosePersistent() { + } + async clearCache() { + for (const page of this._crPages()) + await page._networkManager.clearCache(); + } + async cancelDownload(guid) { + await this._browser._session.send("Browser.cancelDownload", { + guid, + browserContextId: this._browserContextId + }); + } + serviceWorkers() { + return Array.from(this._browser._serviceWorkers.values()).filter((serviceWorker) => serviceWorker.browserContext === this); + } + async newCDPSession(page) { + let targetId = null; + if (page instanceof Page) { + targetId = page.delegate._targetId; + } else if (page instanceof Frame) { + const session2 = page._page.delegate._sessions.get(page._id); + if (!session2) + throw new Error(`This frame does not have a separate CDP session, it is a part of the parent frame's session`); + targetId = session2._targetId; + } else { + throw new Error("page: expected Page or Frame"); + } + const rootSession = await this._browser._clientRootSession(); + return rootSession.attachToTarget(targetId); + } + }; + } +}); + +// packages/playwright-core/src/server/android/android.ts +function encodeWebFrame(data) { + return import_utilsBundle.wsSender.frame(Buffer.from(data), { + opcode: 1, + mask: true, + fin: true, + readOnly: true + })[0]; +} +var import_events7, import_fs24, import_os9, import_path23, import_utilsBundle, debug3, ARTIFACTS_FOLDER, Android, AndroidDevice, AndroidBrowser, ClankBrowserProcess; +var init_android = __esm({ + "packages/playwright-core/src/server/android/android.ts"() { + "use strict"; + import_events7 = require("events"); + import_fs24 = __toESM(require("fs")); + import_os9 = __toESM(require("os")); + import_path23 = __toESM(require("path")); + init_pipeTransport(); + init_crypto(); + init_debug(); + init_env(); + init_task(); + init_debugLogger(); + init_fileUtils(); + init_processLauncher(); + import_utilsBundle = require("./utilsBundle"); + init_browserContext(); + init_chromiumSwitches(); + init_crBrowser(); + init_helper(); + init_instrumentation(); + init_progress(); + init_registry(); + debug3 = require("./utilsBundle").debug; + ARTIFACTS_FOLDER = import_path23.default.join(import_os9.default.tmpdir(), "playwright-artifacts-"); + Android = class extends SdkObject { + constructor(parent, backend) { + super(parent, "android"); + this._devices = /* @__PURE__ */ new Map(); + this._backend = backend; + } + async devices(progress2, options) { + const devices = (await progress2.race(this._backend.devices(options))).filter((d) => d.status === "device"); + const newSerials = /* @__PURE__ */ new Set(); + for (const d of devices) { + newSerials.add(d.serial); + if (this._devices.has(d.serial)) + continue; + await progress2.race(AndroidDevice.create(this, d, options).then((device) => this._devices.set(d.serial, device))); + } + for (const d of this._devices.keys()) { + if (!newSerials.has(d)) + this._devices.delete(d); + } + return [...this._devices.values()]; + } + _deviceClosed(device) { + this._devices.delete(device.serial); + } + }; + AndroidDevice = class _AndroidDevice extends SdkObject { + constructor(android, backend, model, options) { + super(android, "android-device"); + this._lastId = 0; + this._callbacks = /* @__PURE__ */ new Map(); + this._webViews = /* @__PURE__ */ new Map(); + this._browserConnections = /* @__PURE__ */ new Set(); + this._isClosed = false; + this._android = android; + this._backend = backend; + this.model = model; + this.serial = backend.serial; + this._options = options; + this.logName = "browser"; + } + static { + this.Events = { + WebViewAdded: "webViewAdded", + WebViewRemoved: "webViewRemoved", + Close: "close" + }; + } + static async create(android, backend, options) { + await backend.init(); + const model = await backend.runCommand("shell:getprop ro.product.model"); + const device = new _AndroidDevice(android, backend, model.toString().trim(), options); + await device._init(); + return device; + } + async _init() { + await this._refreshWebViews(); + const poll = () => { + this._pollingWebViews = setTimeout(() => this._refreshWebViews().then(poll).catch(() => { + this._close().catch(() => { + }); + }), 500); + }; + poll(); + } + async shell(progress2, command) { + return await progress2.race(this._shell(command)); + } + async _shell(command) { + const result2 = await this._backend.runCommand(`shell:${command}`); + await this._refreshWebViews(); + return result2; + } + async open(progress2, command) { + return await this._open(progress2, command); + } + async screenshot(progress2) { + return await progress2.race(this._backend.runCommand(`shell:screencap -p`)); + } + async _driver() { + if (this._isClosed) + return; + if (!this._driverPromise) { + const controller = new ProgressController(); + this._driverPromise = controller.run((progress2) => this._installDriver(progress2)); + } + return this._driverPromise; + } + async _installDriver(progress2) { + debug3("pw:android")("Stopping the old driver"); + await progress2.race(this._shell(`am force-stop com.microsoft.playwright.androiddriver`)); + if (!this._options.omitDriverInstall) { + debug3("pw:android")("Uninstalling the old driver"); + await this.shell(progress2, `cmd package uninstall com.microsoft.playwright.androiddriver`); + await this.shell(progress2, `cmd package uninstall com.microsoft.playwright.androiddriver.test`); + debug3("pw:android")("Installing the new driver"); + const executable = registry.findExecutable("android"); + const packageManagerCommand = getPackageManagerExecCommand(); + for (const file of ["android-driver.apk", "android-driver-target.apk"]) { + const fullName = import_path23.default.join(executable.directory, file); + if (!import_fs24.default.existsSync(fullName)) + throw new Error(`Please install Android driver apk using '${packageManagerCommand} playwright install android'`); + await this.installApk(progress2, await progress2.race(import_fs24.default.promises.readFile(fullName))); + } + } else { + debug3("pw:android")("Skipping the driver installation"); + } + debug3("pw:android")("Starting the new driver"); + this._shell("am instrument -w com.microsoft.playwright.androiddriver.test/androidx.test.runner.AndroidJUnitRunner").catch((e) => debug3("pw:android")(e)); + const socket = await this._waitForLocalAbstract(progress2, "playwright_android_driver_socket"); + const transport = new PipeTransport(socket, socket, socket, "be"); + transport.onmessage = (message) => { + const response2 = JSON.parse(message); + const { id, result: result2, error } = response2; + const callback = this._callbacks.get(id); + if (!callback) + return; + if (error) + callback.reject(new Error(error)); + else + callback.fulfill(result2); + this._callbacks.delete(id); + }; + return transport; + } + async _waitForLocalAbstract(progress2, socketName) { + let socket; + debug3("pw:android")(`Polling the socket localabstract:${socketName}`); + while (!socket) { + try { + socket = await this._open(progress2, `localabstract:${socketName}`); + } catch (e) { + if (isAbortError(e)) + throw e; + await progress2.wait(250); + } + } + debug3("pw:android")(`Connected to localabstract:${socketName}`); + return socket; + } + async send(progress2, method, params2 = {}) { + return await progress2.race(this._send(method, params2)); + } + async _send(method, params2 = {}) { + params2 = { + ...params2, + // Patch the timeout in, just in case it's missing in one of the commands. + timeout: params2.timeout || 0 + }; + if (params2.androidSelector) { + params2.selector = params2.androidSelector; + delete params2.androidSelector; + } + const driver = await this._driver(); + if (!driver || this._isClosed) + throw new Error("Device is closed"); + const id = ++this._lastId; + const result2 = new Promise((fulfill, reject) => this._callbacks.set(id, { fulfill, reject })); + driver.send(JSON.stringify({ id, method, params: params2 })); + return result2; + } + async close(progress2) { + await progress2.race(this._close()); + } + async _close() { + if (this._isClosed) + return; + this._isClosed = true; + if (this._pollingWebViews) + clearTimeout(this._pollingWebViews); + for (const connection of this._browserConnections) + await connection.close(); + for (const callback of this._callbacks.values()) + callback.reject(new Error("Device closed")); + this._callbacks.clear(); + if (this._driverPromise) { + const driver = await this._driver(); + driver?.close(); + } + await this._backend.close(); + this._android._deviceClosed(this); + this.emit(_AndroidDevice.Events.Close); + } + async launchBrowser(progress2, pkg = "com.android.chrome", options) { + debug3("pw:android")("Force-stopping", pkg); + await progress2.race(this._backend.runCommand(`shell:am force-stop ${pkg}`)); + const socketName = isUnderTest() ? "webview_devtools_remote_playwright_test" : "playwright_" + createGuid() + "_devtools_remote"; + const commandLine = this._defaultArgs(options, socketName).join(" "); + debug3("pw:android")("Starting", pkg, commandLine); + await progress2.race(this._backend.runCommand(`shell:echo "${Buffer.from(commandLine).toString("base64")}" | base64 -d > /data/local/tmp/chrome-command-line`)); + await progress2.race(this._backend.runCommand(`shell:am start -a android.intent.action.VIEW -d about:blank ${pkg}`)); + const browserContext = await this._connectToBrowser(progress2, socketName, options); + try { + await progress2.race(this._backend.runCommand(`shell:rm /data/local/tmp/chrome-command-line`)); + return browserContext; + } catch (error) { + await browserContext.close(progress2, { reason: "Failed to launch" }).catch(() => { + }); + throw error; + } + } + _defaultArgs(options, socketName) { + const chromeArguments = [ + "_", + "--disable-fre", + "--no-default-browser-check", + `--remote-debugging-socket-name=${socketName}`, + ...chromiumSwitches({ android: true }), + ...this._innerDefaultArgs(options) + ]; + return chromeArguments; + } + _innerDefaultArgs(options) { + const { args = [], proxy } = options; + const chromeArguments = []; + if (proxy) { + chromeArguments.push(`--proxy-server=${proxy.server}`); + const proxyBypassRules = []; + if (proxy.bypass) + proxyBypassRules.push(...proxy.bypass.split(",").map((t) => t.trim()).map((t) => t.startsWith(".") ? "*" + t : t)); + if (shouldProxyLoopback(proxy.bypass)) + proxyBypassRules.push("<-loopback>"); + if (proxyBypassRules.length > 0) + chromeArguments.push(`--proxy-bypass-list=${proxyBypassRules.join(";")}`); + } + chromeArguments.push(...args); + return chromeArguments; + } + async connectToWebView(progress2, socketName) { + const webView = this._webViews.get(socketName); + if (!webView) + throw new Error("WebView has been closed"); + return await this._connectToBrowser(progress2, socketName); + } + async _connectToBrowser(progress2, socketName, options = {}) { + const socket = await this._waitForLocalAbstract(progress2, socketName); + try { + const androidBrowser = new AndroidBrowser(this, socket); + await progress2.race(androidBrowser._init()); + this._browserConnections.add(androidBrowser); + const artifactsDir = await progress2.race(import_fs24.default.promises.mkdtemp(ARTIFACTS_FOLDER)); + const cleanupArtifactsDir = async () => { + const errors = (await removeFolders([artifactsDir])).filter(Boolean); + for (let i = 0; i < (errors || []).length; ++i) + debug3("pw:android")(`exception while removing ${artifactsDir}: ${errors[i]}`); + }; + gracefullyCloseSet.add(cleanupArtifactsDir); + socket.on("close", async () => { + gracefullyCloseSet.delete(cleanupArtifactsDir); + cleanupArtifactsDir().catch((e) => debug3("pw:android")(`could not cleanup artifacts dir: ${e}`)); + }); + const browserOptions = { + name: "clank", + browserType: "chromium", + slowMo: 0, + persistent: { ...options, noDefaultViewport: true }, + artifactsDir, + downloadsPath: artifactsDir, + tracesDir: artifactsDir, + browserProcess: new ClankBrowserProcess(androidBrowser), + proxy: options.proxy, + protocolLogger: helper.debugProtocolLogger(), + browserLogsCollector: new RecentLogsCollector(), + originalLaunchOptions: {} + }; + validateBrowserContextOptions(options, browserOptions); + const browser = await progress2.race(CRBrowser.connect(this.attribution.playwright, androidBrowser, browserOptions)); + const defaultContext = browser._defaultContext; + await defaultContext.loadDefaultContextAsIs(progress2); + return defaultContext; + } catch (error) { + socket.close(); + throw error; + } + } + _open(progress2, command) { + return raceUncancellableOperationWithCleanup(progress2, () => this._backend.open(command), (socket) => socket.close()); + } + webViews() { + return [...this._webViews.values()]; + } + async installApk(progress2, content, options) { + const args = options && options.args ? options.args : ["-r", "-t", "-S"]; + debug3("pw:android")("Opening install socket"); + const installSocket = await this._open(progress2, `shell:cmd package install ${args.join(" ")} ${content.length}`); + debug3("pw:android")("Writing driver bytes: " + content.length); + await progress2.race(installSocket.write(content)); + const success = await progress2.race(new Promise((f) => installSocket.on("data", f))); + debug3("pw:android")("Written driver bytes: " + success); + installSocket.close(); + } + async push(progress2, content, path59, mode = 420) { + const socket = await this._open(progress2, `sync:`); + const sendHeader = async (progress3, command, length) => { + const buffer = Buffer.alloc(command.length + 4); + buffer.write(command, 0); + buffer.writeUInt32LE(length, command.length); + await progress3.race(socket.write(buffer)); + }; + const send = async (progress3, command, data) => { + await sendHeader(progress3, command, data.length); + await progress3.race(socket.write(data)); + }; + await send(progress2, "SEND", Buffer.from(`${path59},${mode}`)); + const maxChunk = 65535; + for (let i = 0; i < content.length; i += maxChunk) + await send(progress2, "DATA", content.slice(i, i + maxChunk)); + await sendHeader(progress2, "DONE", Date.now() / 1e3 | 0); + const result2 = await progress2.race(new Promise((f) => socket.once("data", f))); + const code = result2.slice(0, 4).toString(); + if (code !== "OKAY") + throw new Error("Could not push: " + code); + socket.close(); + } + async _refreshWebViews() { + const sockets = (await this._backend.runCommand(`shell:cat /proc/net/unix | grep webview_devtools_remote`)).toString().split("\n"); + if (this._isClosed) + return; + const socketNames = /* @__PURE__ */ new Set(); + for (const line of sockets) { + const matchSocketName = line.match(/[^@]+@(.*?webview_devtools_remote_?.*)/); + if (!matchSocketName) + continue; + const socketName = matchSocketName[1]; + socketNames.add(socketName); + if (this._webViews.has(socketName)) + continue; + const match = line.match(/[^@]+@.*?webview_devtools_remote_?(\d*)/); + let pid = -1; + if (match && match[1]) + pid = +match[1]; + const pkg = await this._extractPkg(pid); + if (this._isClosed) + return; + const webView = { pid, pkg, socketName }; + this._webViews.set(socketName, webView); + this.emit(_AndroidDevice.Events.WebViewAdded, webView); + } + for (const p of this._webViews.keys()) { + if (!socketNames.has(p)) { + this._webViews.delete(p); + this.emit(_AndroidDevice.Events.WebViewRemoved, p); + } + } + } + async _extractPkg(pid) { + let pkg = ""; + if (pid === -1) + return pkg; + const procs = (await this._backend.runCommand(`shell:ps -A | grep ${pid}`)).toString().split("\n"); + for (const proc of procs) { + const match = proc.match(/[^\s]+\s+(\d+).*$/); + if (!match) + continue; + pkg = proc.substring(proc.lastIndexOf(" ") + 1); + } + return pkg; + } + }; + AndroidBrowser = class extends import_events7.EventEmitter { + constructor(device, socket) { + super(); + this._waitForNextTask = makeWaitForNextTask(); + this.setMaxListeners(0); + this.device = device; + this._socket = socket; + this._socket.on("close", () => { + this._waitForNextTask(() => { + if (this.onclose) + this.onclose(); + }); + }); + this._receiver = new import_utilsBundle.wsReceiver(); + this._receiver.on("message", (message) => { + this._waitForNextTask(() => { + if (this.onmessage) + this.onmessage(JSON.parse(message)); + }); + }); + } + async _init() { + await this._socket.write(Buffer.from(`GET /devtools/browser HTTP/1.1\r +Upgrade: WebSocket\r +Connection: Upgrade\r +Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r +Sec-WebSocket-Version: 13\r +\r +`)); + await new Promise((f) => this._socket.once("data", f)); + this._socket.on("data", (data) => this._receiver._write(data, "binary", () => { + })); + } + async send(s) { + await this._socket.write(encodeWebFrame(JSON.stringify(s))); + } + async close() { + this._socket.close(); + } + }; + ClankBrowserProcess = class { + constructor(browser) { + this._browser = browser; + } + async kill() { + } + async close() { + await this._browser.close(); + } + }; + } +}); + +// packages/playwright-core/src/server/android/backendAdb.ts +async function runCommand(command, host = "127.0.0.1", port = 5037, serial) { + debug4("pw:adb:runCommand")(command, serial); + const socket = new BufferedSocketWrapper(command, import_net4.default.createConnection({ host, port })); + try { + if (serial) { + await socket.write(encodeMessage(`host:transport:${serial}`)); + const status2 = await socket.read(4); + assert(status2.toString() === "OKAY", status2.toString()); + } + await socket.write(encodeMessage(command)); + const status = await socket.read(4); + assert(status.toString() === "OKAY", status.toString()); + let commandOutput; + if (!command.startsWith("shell:")) { + const remainingLength = parseInt((await socket.read(4)).toString(), 16); + commandOutput = await socket.read(remainingLength); + } else { + commandOutput = await socket.readAll(); + } + return commandOutput; + } finally { + socket.close(); + } +} +async function open(command, host = "127.0.0.1", port = 5037, serial) { + const socket = new BufferedSocketWrapper(command, import_net4.default.createConnection({ host, port })); + if (serial) { + await socket.write(encodeMessage(`host:transport:${serial}`)); + const status2 = await socket.read(4); + assert(status2.toString() === "OKAY", status2.toString()); + } + await socket.write(encodeMessage(command)); + const status = await socket.read(4); + assert(status.toString() === "OKAY", status.toString()); + return socket; +} +function encodeMessage(message) { + let lenHex = message.length.toString(16); + lenHex = "0".repeat(4 - lenHex.length) + lenHex; + return Buffer.from(lenHex + message); +} +var import_events8, import_net4, debug4, AdbBackend, AdbDevice, BufferedSocketWrapper; +var init_backendAdb = __esm({ + "packages/playwright-core/src/server/android/backendAdb.ts"() { + "use strict"; + import_events8 = require("events"); + import_net4 = __toESM(require("net")); + init_assert(); + debug4 = require("./utilsBundle").debug; + AdbBackend = class { + async devices(options = {}) { + const result2 = await runCommand("host:devices", options.host, options.port); + const lines = result2.toString().trim().split("\n"); + return lines.map((line) => { + const [serial, status] = line.trim().split(" "); + return new AdbDevice(serial, status, options.host, options.port); + }); + } + }; + AdbDevice = class { + constructor(serial, status, host, port) { + this._closed = false; + this.serial = serial; + this.status = status; + this.host = host; + this.port = port; + } + async init() { + } + async close() { + this._closed = true; + } + runCommand(command) { + if (this._closed) + throw new Error("Device is closed"); + return runCommand(command, this.host, this.port, this.serial); + } + async open(command) { + if (this._closed) + throw new Error("Device is closed"); + const result2 = await open(command, this.host, this.port, this.serial); + result2.becomeSocket(); + return result2; + } + }; + BufferedSocketWrapper = class extends import_events8.EventEmitter { + constructor(command, socket) { + super(); + this._buffer = Buffer.from([]); + this._isSocket = false; + this._isClosed = false; + this._command = command; + this._socket = socket; + this._connectPromise = new Promise((f) => this._socket.on("connect", f)); + this._socket.on("data", (data) => { + debug4("pw:adb:data")(data.toString()); + if (this._isSocket) { + this.emit("data", data); + return; + } + this._buffer = Buffer.concat([this._buffer, data]); + if (this._notifyReader) + this._notifyReader(); + }); + this._socket.on("close", () => { + this._isClosed = true; + if (this._notifyReader) + this._notifyReader(); + this.close(); + this.emit("close"); + }); + this._socket.on("error", (error) => this.emit("error", error)); + } + async write(data) { + debug4("pw:adb:send")(data.toString().substring(0, 100) + "..."); + await this._connectPromise; + await new Promise((f) => this._socket.write(data, f)); + } + close() { + if (this._isClosed) + return; + debug4("pw:adb")("Close " + this._command); + this._socket.destroy(); + } + async read(length) { + await this._connectPromise; + assert(!this._isSocket, "Can not read by length in socket mode"); + while (this._buffer.length < length) + await new Promise((f) => this._notifyReader = f); + const result2 = this._buffer.slice(0, length); + this._buffer = this._buffer.slice(length); + debug4("pw:adb:recv")(result2.toString().substring(0, 100) + "..."); + return result2; + } + async readAll() { + while (!this._isClosed) + await new Promise((f) => this._notifyReader = f); + return this._buffer; + } + becomeSocket() { + assert(!this._buffer.length); + this._isSocket = true; + } + }; + } +}); + +// packages/playwright-core/src/server/pipeTransport.ts +var PipeTransport2; +var init_pipeTransport2 = __esm({ + "packages/playwright-core/src/server/pipeTransport.ts"() { + "use strict"; + init_debugLogger(); + init_task(); + PipeTransport2 = class { + constructor(pipeWrite, pipeRead) { + this._pendingBuffers = []; + this._waitForNextTask = makeWaitForNextTask(); + this._closed = false; + this._pipeRead = pipeRead; + this._pipeWrite = pipeWrite; + pipeRead.on("data", (buffer) => this._dispatch(buffer)); + pipeRead.on("close", () => { + this._closed = true; + if (this._onclose) + this._onclose.call(null); + }); + pipeRead.on("error", (e) => debugLogger.log("error", e)); + pipeWrite.on("error", (e) => debugLogger.log("error", e)); + this.onmessage = void 0; + } + get onclose() { + return this._onclose; + } + set onclose(onclose) { + this._onclose = onclose; + if (onclose && !this._pipeRead.readable) + onclose(); + } + send(message) { + if (this._closed) + throw new Error("Pipe has been closed"); + this._pipeWrite.write(JSON.stringify(message)); + this._pipeWrite.write("\0"); + } + close() { + throw new Error("unimplemented"); + } + _dispatch(buffer) { + let end = buffer.indexOf("\0"); + if (end === -1) { + this._pendingBuffers.push(buffer); + return; + } + this._pendingBuffers.push(buffer.slice(0, end)); + const message = Buffer.concat(this._pendingBuffers).toString(); + this._waitForNextTask(() => { + if (this.onmessage) + this.onmessage.call(null, JSON.parse(message)); + }); + let start3 = end + 1; + end = buffer.indexOf("\0", start3); + while (end !== -1) { + const message2 = buffer.toString(void 0, start3, end); + this._waitForNextTask(() => { + if (this.onmessage) + this.onmessage.call(null, JSON.parse(message2)); + }); + start3 = end + 1; + end = buffer.indexOf("\0", start3); + } + this._pendingBuffers = [buffer.slice(start3)]; + } + }; + } +}); + +// packages/playwright-core/src/server/transport.ts +function stripQueryParams(url2) { + try { + const u = new URL(url2); + u.search = ""; + u.hash = ""; + return u.toString(); + } catch { + return url2; + } +} +var ws, perMessageDeflate2, WebSocketTransport; +var init_transport = __esm({ + "packages/playwright-core/src/server/transport.ts"() { + "use strict"; + init_happyEyeballs(); + init_task(); + ws = require("./utilsBundle").ws; + perMessageDeflate2 = { + clientNoContextTakeover: true, + zlibDeflateOptions: { + level: 3 + }, + zlibInflateOptions: { + chunkSize: 10 * 1024 + }, + threshold: 10 * 1024 + }; + WebSocketTransport = class _WebSocketTransport { + constructor(progress2, url2, logUrl, options) { + this.headers = []; + this.wsEndpoint = url2; + this._logUrl = logUrl; + this._ws = new ws(url2, [], { + maxPayload: 256 * 1024 * 1024, + // 256Mb, + headers: options.headers, + followRedirects: options.followRedirects, + agent: /^(https|wss):\/\//.test(url2) ? httpsHappyEyeballsAgent : httpHappyEyeballsAgent, + perMessageDeflate: perMessageDeflate2 + }); + this._ws.on("upgrade", (response2) => { + for (let i = 0; i < response2.rawHeaders.length; i += 2) { + this.headers.push({ name: response2.rawHeaders[i], value: response2.rawHeaders[i + 1] }); + if (options.debugLogHeader && response2.rawHeaders[i] === options.debugLogHeader) + progress2?.log(response2.rawHeaders[i + 1]); + } + }); + this._progress = progress2; + const messageWrap = makeWaitForNextTask(); + this._ws.addEventListener("message", (event) => { + messageWrap(() => { + const eventData = event.data; + let parsedJson; + try { + parsedJson = JSON.parse(eventData); + } catch (e) { + this._progress?.log(` Closing websocket due to malformed JSON. eventData=${eventData} e=${e?.message}`); + this._ws.close(); + return; + } + try { + if (this.onmessage) + this.onmessage.call(null, parsedJson); + } catch (e) { + this._progress?.log(` Closing websocket due to failed onmessage callback. eventData=${eventData} e=${e?.message}`); + this._ws.close(); + } + }); + }); + this._ws.addEventListener("close", (event) => { + this._progress?.log(` ${logUrl} code=${event.code} reason=${event.reason}`); + if (this.onclose) + this.onclose.call(null, event.reason); + }); + this._ws.addEventListener("error", (error) => this._progress?.log(` ${logUrl} ${error.type} ${error.message}`)); + } + static async connect(progress2, url2, options = {}) { + return await _WebSocketTransport._connect( + progress2, + url2, + options, + false + /* hadRedirects */ + ); + } + static async _connect(progress2, url2, options, hadRedirects) { + const logUrl = stripQueryParams(url2); + progress2?.log(` ${logUrl}`); + const transport = new _WebSocketTransport(progress2, url2, logUrl, { ...options, followRedirects: !!options.followRedirects && hadRedirects }); + const resultPromise = new Promise((fulfill, reject) => { + transport._ws.on("open", async () => { + progress2?.log(` ${logUrl}`); + fulfill({}); + }); + transport._ws.on("error", (event) => { + progress2?.log(` ${logUrl} ${event.message}`); + reject(new Error("WebSocket error: " + event.message)); + transport._ws.close(); + }); + transport._ws.on("unexpected-response", (request2, response2) => { + if (options.followRedirects && !hadRedirects && (response2.statusCode === 301 || response2.statusCode === 302 || response2.statusCode === 307 || response2.statusCode === 308)) { + fulfill({ redirect: response2 }); + transport._ws.close(); + return; + } + for (let i = 0; i < response2.rawHeaders.length; i += 2) { + if (options.debugLogHeader && response2.rawHeaders[i] === options.debugLogHeader) + progress2?.log(response2.rawHeaders[i + 1]); + } + const chunks = []; + const errorPrefix = `${logUrl} ${response2.statusCode} ${response2.statusMessage}`; + response2.on("data", (chunk) => chunks.push(chunk)); + response2.on("close", () => { + const error = chunks.length ? `${errorPrefix} +${Buffer.concat(chunks)}` : errorPrefix; + progress2?.log(` ${error}`); + reject(new Error("WebSocket error: " + error)); + transport._ws.close(); + }); + }); + }); + try { + const result2 = progress2 ? await progress2.race(resultPromise) : await resultPromise; + if (result2.redirect) { + const newHeaders = Object.fromEntries(Object.entries(options.headers || {}).filter(([name]) => { + return !name.includes("access-key") && name.toLowerCase() !== "authorization"; + })); + return _WebSocketTransport._connect( + progress2, + result2.redirect.headers.location, + { ...options, headers: newHeaders }, + true + /* hadRedirects */ + ); + } + return transport; + } catch (error) { + await transport.closeAndWait(); + throw error; + } + } + send(message) { + this._ws.send(JSON.stringify(message)); + } + close() { + this._progress?.log(` ${this._logUrl}`); + this._ws.close(); + } + async closeAndWait() { + if (this._ws.readyState === ws.CLOSED) + return; + const promise = new Promise((f) => this._ws.once("close", f)); + this.close(); + await promise; + } + }; + } +}); + +// packages/playwright-core/src/server/browserType.ts +function copyTestHooks(from, to) { + for (const [key, value2] of Object.entries(from)) { + if (key.startsWith("__testHook")) + to[key] = value2; + } +} +var import_fs25, import_os10, import_path24, kNoXServerRunningError, BrowserType; +var init_browserType = __esm({ + "packages/playwright-core/src/server/browserType.ts"() { + "use strict"; + import_fs25 = __toESM(require("fs")); + import_os10 = __toESM(require("os")); + import_path24 = __toESM(require("path")); + init_assert(); + init_manualPromise(); + init_time(); + init_debug(); + init_fileUtils(); + init_processLauncher(); + init_debugLogger(); + init_browserContext(); + init_helper(); + init_instrumentation(); + init_pipeTransport2(); + init_protocolError(); + init_registry(); + init_socksClientCertificatesInterceptor(); + init_transport(); + kNoXServerRunningError = "Looks like you launched a headed browser without having a XServer running.\nSet either 'headless: true' or use 'xvfb-run ' before running Playwright.\n\n<3 Playwright Team"; + BrowserType = class extends SdkObject { + constructor(parent, browserName) { + super(parent, "browser-type"); + this.attribution.browserType = this; + this._name = browserName; + this.logName = "browser"; + } + executablePath() { + return registry.findExecutable(this._name).executablePath() || ""; + } + name() { + return this._name; + } + async launch(progress2, options, protocolLogger) { + options = this._validateLaunchOptions(options); + const seleniumHubUrl = options.__testHookSeleniumRemoteURL || process.env.SELENIUM_REMOTE_URL; + if (seleniumHubUrl) + return this.launchWithSeleniumHub(progress2, seleniumHubUrl, options); + return this._innerLaunchWithRetries(progress2, options, void 0, helper.debugProtocolLogger(protocolLogger)).catch((e) => { + throw this._rewriteStartupLog(e); + }); + } + async launchPersistentContext(progress2, userDataDir, options) { + const launchOptions = this._validateLaunchOptions(options); + let clientCertificatesProxy; + if (options.clientCertificates?.length) { + clientCertificatesProxy = await ClientCertificatesProxy.create(progress2, options); + launchOptions.proxyOverride = clientCertificatesProxy.proxySettings(); + options = { ...options }; + options.internalIgnoreHTTPSErrors = true; + } + try { + const browser = await this._innerLaunchWithRetries(progress2, launchOptions, options, helper.debugProtocolLogger(), userDataDir).catch((e) => { + throw this._rewriteStartupLog(e); + }); + browser._defaultContext._clientCertificatesProxy = clientCertificatesProxy; + return browser._defaultContext; + } catch (error) { + await clientCertificatesProxy?.close().catch(() => { + }); + throw error; + } + } + async _innerLaunchWithRetries(progress2, options, persistent, protocolLogger, userDataDir) { + try { + return await this._innerLaunch(progress2, options, persistent, protocolLogger, userDataDir); + } catch (error) { + const errorMessage = typeof error === "object" && typeof error.message === "string" ? error.message : ""; + if (errorMessage.includes("Inconsistency detected by ld.so")) { + progress2.log(``); + return this._innerLaunch(progress2, options, persistent, protocolLogger, userDataDir); + } + throw error; + } + } + async _innerLaunch(progress2, options, persistent, protocolLogger, maybeUserDataDir) { + options.proxy = options.proxy ? normalizeProxySettings(options.proxy) : void 0; + const browserLogsCollector = new RecentLogsCollector(); + const { browserProcess, userDataDir, artifactsDir, transport, wsEndpoint } = await this._launchProcess(progress2, options, !!persistent, browserLogsCollector, maybeUserDataDir); + try { + if (options.__testHookBeforeCreateBrowser) + await progress2.race(options.__testHookBeforeCreateBrowser()); + const browserOptions = { + name: this._name, + browserType: this._name, + channel: options.channel, + slowMo: options.slowMo, + persistent, + headful: !options.headless, + artifactsDir, + downloadsPath: options.downloadsPath || artifactsDir, + tracesDir: options.tracesDir || artifactsDir, + browserProcess, + customExecutablePath: options.executablePath, + proxy: options.proxy, + protocolLogger, + browserLogsCollector, + wsEndpoint, + originalLaunchOptions: options, + userDataDir: persistent ? userDataDir : void 0 + }; + if (persistent) + validateBrowserContextOptions(persistent, browserOptions); + copyTestHooks(options, browserOptions); + const browser = await progress2.race(this.connectToTransport(transport, browserOptions, browserLogsCollector)); + browser._userDataDirForTest = userDataDir; + if (persistent && !options.ignoreAllDefaultArgs) + await browser._defaultContext.loadDefaultContext(progress2); + return browser; + } catch (error) { + await progress2.race(browserProcess.close().catch(() => { + })); + throw error; + } + } + async _prepareToLaunch(options, isPersistent, userDataDir) { + const { + ignoreDefaultArgs, + ignoreAllDefaultArgs, + args = [], + executablePath = null + } = options; + const tempDirectories = []; + let artifactsDir; + if (options.artifactsDir) { + artifactsDir = options.artifactsDir; + } else { + artifactsDir = await import_fs25.default.promises.mkdtemp(import_path24.default.join(import_os10.default.tmpdir(), "playwright-artifacts-")); + tempDirectories.push(artifactsDir); + } + if (userDataDir) { + assert(import_path24.default.isAbsolute(userDataDir), "userDataDir must be an absolute path"); + if (!await existsAsync(userDataDir)) + await import_fs25.default.promises.mkdir(userDataDir, { recursive: true, mode: 448 }); + } else { + userDataDir = await import_fs25.default.promises.mkdtemp(import_path24.default.join(import_os10.default.tmpdir(), `playwright_${this._name}dev_profile-`)); + tempDirectories.push(userDataDir); + } + await this.prepareUserDataDir(options, userDataDir); + const browserArguments = []; + if (ignoreAllDefaultArgs) + browserArguments.push(...args); + else if (ignoreDefaultArgs) + browserArguments.push(...(await this.defaultArgs(options, isPersistent, userDataDir)).filter((arg) => ignoreDefaultArgs.indexOf(arg) === -1)); + else + browserArguments.push(...await this.defaultArgs(options, isPersistent, userDataDir)); + let executable; + if (executablePath) { + if (!await existsAsync(executablePath)) + throw new Error(`Failed to launch ${this._name} because executable doesn't exist at ${executablePath}`); + executable = executablePath; + } else { + const registryExecutable = registry.findExecutable(this.getExecutableName(options)); + if (!registryExecutable || registryExecutable.browserName !== this._name) + throw new Error(`Unsupported ${this._name} channel "${options.channel}"`); + executable = registryExecutable.executablePathOrDie(this.attribution.playwright.options.sdkLanguage); + await registry.validateHostRequirementsForExecutablesIfNeeded([registryExecutable], this.attribution.playwright.options.sdkLanguage); + } + return { executable, browserArguments, userDataDir, artifactsDir, tempDirectories }; + } + async _launchProcess(progress2, options, isPersistent, browserLogsCollector, userDataDir) { + const { + handleSIGINT = true, + handleSIGTERM = true, + handleSIGHUP = true + } = options; + const env = options.env ? envArrayToObject(options.env) : process.env; + const prepared = await progress2.race(this._prepareToLaunch(options, isPersistent, userDataDir)); + let transport = void 0; + let browserProcess = void 0; + const exitPromise = new ManualPromise(); + const { launchedProcess, gracefullyClose, kill } = await progress2.race(launchProcess({ + command: prepared.executable, + args: prepared.browserArguments, + env: this.amendEnvironment(env, prepared.userDataDir, isPersistent, options), + handleSIGINT, + handleSIGTERM, + handleSIGHUP, + log: (message) => { + progress2.log(message); + browserLogsCollector.log(message); + }, + stdio: "pipe", + tempDirectories: prepared.tempDirectories, + attemptToGracefullyClose: async () => { + if (options.__testHookGracefullyClose) + await options.__testHookGracefullyClose(); + if (transport) { + this.attemptToGracefullyCloseBrowser(transport); + } else { + throw new Error("Force-killing the browser because no transport is available to gracefully close it."); + } + }, + onExit: (exitCode, signal) => { + exitPromise.resolve(); + if (browserProcess && browserProcess.onclose) + browserProcess.onclose(exitCode, signal); + } + })); + async function closeOrKill(timeout) { + let timer; + try { + await Promise.race([ + gracefullyClose(), + new Promise((resolve, reject) => timer = setTimeout(reject, timeout)) + ]); + } catch (ignored) { + await kill().catch((ignored2) => { + }); + } finally { + clearTimeout(timer); + } + } + browserProcess = { + onclose: void 0, + process: launchedProcess, + close: () => closeOrKill(options.__testHookBrowserCloseTimeout || DEFAULT_PLAYWRIGHT_TIMEOUT), + kill + }; + try { + const { wsEndpoint } = await progress2.race([ + this.waitForReadyState(options, browserLogsCollector), + exitPromise.then(() => ({ wsEndpoint: void 0 })) + ]); + if (exitPromise.isDone()) { + const log2 = helper.formatBrowserLogs(browserLogsCollector.recentLogs()); + const updatedLog = this.doRewriteStartupLog(log2); + throw new Error(`Failed to launch the browser process. +Browser logs: +${updatedLog}`); + } + if (!this.supportsPipeTransport()) { + transport = await WebSocketTransport.connect(progress2, wsEndpoint); + } else { + const stdio = launchedProcess.stdio; + transport = new PipeTransport2(stdio[3], stdio[4]); + } + return { browserProcess, artifactsDir: prepared.artifactsDir, userDataDir: prepared.userDataDir, transport, wsEndpoint }; + } catch (error) { + await progress2.race(closeOrKill(DEFAULT_PLAYWRIGHT_TIMEOUT).catch(() => { + })); + throw error; + } + } + async connectOverCDP(progress2, params2) { + throw new Error("CDP connections are only supported by Chromium"); + } + async connectToWorker(progress2, endpoint) { + throw new Error("CDP connections are only supported by Chromium"); + } + async launchWithSeleniumHub(progress2, hubUrl, options) { + throw new Error("Connecting to SELENIUM_REMOTE_URL is only supported by Chromium"); + } + _validateLaunchOptions(options) { + let { headless = true, downloadsPath, proxy } = options; + if (debugMode() === "inspector") + headless = false; + if (downloadsPath && !import_path24.default.isAbsolute(downloadsPath)) + downloadsPath = import_path24.default.join(process.cwd(), downloadsPath); + if (options.socksProxyPort) + proxy = { server: `socks5://127.0.0.1:${options.socksProxyPort}` }; + return { ...options, headless, downloadsPath, proxy }; + } + _createUserDataDirArgMisuseError(userDataDirArg) { + switch (this.attribution.playwright.options.sdkLanguage) { + case "java": + return new Error(`Pass userDataDir parameter to 'BrowserType.launchPersistentContext(userDataDir, options)' instead of specifying '${userDataDirArg}' argument`); + case "python": + return new Error(`Pass user_data_dir parameter to 'browser_type.launch_persistent_context(user_data_dir, **kwargs)' instead of specifying '${userDataDirArg}' argument`); + case "csharp": + return new Error(`Pass userDataDir parameter to 'BrowserType.LaunchPersistentContextAsync(userDataDir, options)' instead of specifying '${userDataDirArg}' argument`); + default: + return new Error(`Pass userDataDir parameter to 'browserType.launchPersistentContext(userDataDir, options)' instead of specifying '${userDataDirArg}' argument`); + } + } + _rewriteStartupLog(error) { + if (!isProtocolError(error)) + return error; + if (error.logs) + error.logs = this.doRewriteStartupLog(error.logs); + return error; + } + async waitForReadyState(options, browserLogsCollector) { + return {}; + } + async prepareUserDataDir(options, userDataDir) { + } + supportsPipeTransport() { + return true; + } + getExecutableName(options) { + return options.channel || this._name; + } + }; + } +}); + +// packages/playwright-core/src/server/bidi/bidiConnection.ts +var import_events9, kBrowserCloseMessageId2, kShutdownSessionNewMessageId, BidiConnection, BidiSession; +var init_bidiConnection = __esm({ + "packages/playwright-core/src/server/bidi/bidiConnection.ts"() { + "use strict"; + import_events9 = require("events"); + init_debugLogger(); + init_helper(); + init_protocolError(); + kBrowserCloseMessageId2 = Number.MAX_SAFE_INTEGER - 1; + kShutdownSessionNewMessageId = kBrowserCloseMessageId2 - 1; + BidiConnection = class { + constructor(transport, onDisconnect, protocolLogger, browserLogsCollector) { + this._lastId = 0; + this._closed = false; + this._browsingContextToSession = /* @__PURE__ */ new Map(); + this._realmToBrowsingContext = /* @__PURE__ */ new Map(); + // TODO: shared/service workers might have multiple owner realms. + this._realmToOwnerRealm = /* @__PURE__ */ new Map(); + this._transport = transport; + this._onDisconnect = onDisconnect; + this._protocolLogger = protocolLogger; + this._browserLogsCollector = browserLogsCollector; + this.browserSession = new BidiSession(this, "", (message) => { + this.rawSend(message); + }); + this._transport.onmessage = this._dispatchMessage.bind(this); + this._transport.onclose = this._onClose.bind(this); + } + nextMessageId() { + return ++this._lastId; + } + rawSend(message) { + this._protocolLogger("send", message); + this._transport.send(message); + } + _dispatchMessage(message) { + this._protocolLogger("receive", message); + const object = message; + if (object.type === "event") { + if (object.method === "script.realmCreated") { + if ("context" in object.params) + this._realmToBrowsingContext.set(object.params.realm, object.params.context); + if (object.params.type === "dedicated-worker") + this._realmToOwnerRealm.set(object.params.realm, object.params.owners[0]); + } else if (object.method === "script.realmDestroyed") { + this._realmToBrowsingContext.delete(object.params.realm); + this._realmToOwnerRealm.delete(object.params.realm); + } + let context2; + let realm; + if ("context" in object.params) { + context2 = object.params.context; + } else if (object.method === "log.entryAdded" || object.method === "script.message") { + context2 = object.params.source?.context; + realm = object.params.source?.realm; + } else if (object.method === "script.realmCreated" && object.params.type === "dedicated-worker") { + realm = object.params.owners[0]; + } + if (!context2 && realm) { + while (this._realmToOwnerRealm.get(realm)) + realm = this._realmToOwnerRealm.get(realm); + context2 = this._realmToBrowsingContext.get(realm); + } + if (context2) { + const session2 = this._browsingContextToSession.get(context2); + if (session2) { + session2.dispatchMessage(message); + return; + } + } + } else if (message.id) { + for (const session2 of this._browsingContextToSession.values()) { + if (session2.hasCallback(message.id)) { + session2.dispatchMessage(message); + return; + } + } + } + this.browserSession.dispatchMessage(message); + } + _onClose(reason) { + this._closed = true; + this._transport.onmessage = void 0; + this._transport.onclose = void 0; + this._browserDisconnectedLogs = helper.formatBrowserLogs(this._browserLogsCollector.recentLogs(), reason); + this.browserSession.dispose(); + this._onDisconnect(); + } + isClosed() { + return this._closed; + } + close() { + if (!this._closed) + this._transport.close(); + } + createMainFrameBrowsingContextSession(bowsingContextId) { + const result2 = new BidiSession(this, bowsingContextId, (message) => this.rawSend(message)); + this._browsingContextToSession.set(bowsingContextId, result2); + return result2; + } + }; + BidiSession = class extends import_events9.EventEmitter { + constructor(connection, sessionId, rawSend) { + super(); + this._disposed = false; + this._callbacks = /* @__PURE__ */ new Map(); + this._crashed = false; + this._browsingContexts = /* @__PURE__ */ new Set(); + this.setMaxListeners(0); + this.connection = connection; + this.sessionId = sessionId; + this._rawSend = rawSend; + this.on = super.on; + this.off = super.removeListener; + this.addListener = super.addListener; + this.removeListener = super.removeListener; + this.once = super.once; + } + addFrameBrowsingContext(context2) { + this._browsingContexts.add(context2); + this.connection._browsingContextToSession.set(context2, this); + } + removeFrameBrowsingContext(context2) { + this._browsingContexts.delete(context2); + this.connection._browsingContextToSession.delete(context2); + } + async send(method, params2) { + if (this._crashed || this._disposed || this.connection._browserDisconnectedLogs) + throw new ProtocolError(this._crashed ? "crashed" : "closed", void 0, this.connection._browserDisconnectedLogs); + const id = this.connection.nextMessageId(); + const messageObj = { id, method, params: params2 }; + this._rawSend(messageObj); + return new Promise((resolve, reject) => { + this._callbacks.set(id, { resolve, reject, error: new ProtocolError("error", method) }); + }); + } + sendMayFail(method, params2) { + return this.send(method, params2).catch((error) => debugLogger.log("error", error)); + } + markAsCrashed() { + this._crashed = true; + } + isDisposed() { + return this._disposed; + } + dispose() { + this._disposed = true; + this.connection._browsingContextToSession.delete(this.sessionId); + for (const context2 of this._browsingContexts) + this.connection._browsingContextToSession.delete(context2); + this._browsingContexts.clear(); + for (const callback of this._callbacks.values()) { + callback.error.type = this._crashed ? "crashed" : "closed"; + callback.error.setMessage(`Internal server error, session ${callback.error.type}.`); + callback.error.logs = this.connection._browserDisconnectedLogs; + callback.reject(callback.error); + } + this._callbacks.clear(); + } + hasCallback(id) { + return this._callbacks.has(id); + } + dispatchMessage(message) { + const object = message; + if (object.id === kBrowserCloseMessageId2 || object.id === kShutdownSessionNewMessageId) + return; + if (object.id && this._callbacks.has(object.id)) { + const callback = this._callbacks.get(object.id); + this._callbacks.delete(object.id); + if (object.type === "error") { + callback.error.setMessage(object.error + "\nMessage: " + object.message); + callback.reject(callback.error); + } else if (object.type === "success") { + callback.resolve(object.result); + } else { + callback.error.setMessage("Internal error, unexpected response type: " + JSON.stringify(object)); + callback.reject(callback.error); + } + } else if (object.id) { + } else { + Promise.resolve().then(() => this.emit(object.method, object.params)); + } + } + }; + } +}); + +// packages/playwright-core/src/server/bidi/third_party/bidiProtocolCore.ts +var Session, BrowsingContext, Emulation, Network, Script, Log, Input; +var init_bidiProtocolCore = __esm({ + "packages/playwright-core/src/server/bidi/third_party/bidiProtocolCore.ts"() { + "use strict"; + ((Session3) => { + let UserPromptHandlerType; + ((UserPromptHandlerType2) => { + UserPromptHandlerType2["Accept"] = "accept"; + UserPromptHandlerType2["Dismiss"] = "dismiss"; + UserPromptHandlerType2["Ignore"] = "ignore"; + })(UserPromptHandlerType = Session3.UserPromptHandlerType || (Session3.UserPromptHandlerType = {})); + })(Session || (Session = {})); + ((BrowsingContext2) => { + let ReadinessState; + ((ReadinessState2) => { + ReadinessState2["None"] = "none"; + ReadinessState2["Interactive"] = "interactive"; + ReadinessState2["Complete"] = "complete"; + })(ReadinessState = BrowsingContext2.ReadinessState || (BrowsingContext2.ReadinessState = {})); + })(BrowsingContext || (BrowsingContext = {})); + ((BrowsingContext2) => { + let UserPromptType; + ((UserPromptType2) => { + UserPromptType2["Alert"] = "alert"; + UserPromptType2["Beforeunload"] = "beforeunload"; + UserPromptType2["Confirm"] = "confirm"; + UserPromptType2["Prompt"] = "prompt"; + })(UserPromptType = BrowsingContext2.UserPromptType || (BrowsingContext2.UserPromptType = {})); + })(BrowsingContext || (BrowsingContext = {})); + ((BrowsingContext2) => { + let CreateType; + ((CreateType2) => { + CreateType2["Tab"] = "tab"; + CreateType2["Window"] = "window"; + })(CreateType = BrowsingContext2.CreateType || (BrowsingContext2.CreateType = {})); + })(BrowsingContext || (BrowsingContext = {})); + ((Emulation2) => { + let ForcedColorsModeTheme; + ((ForcedColorsModeTheme2) => { + ForcedColorsModeTheme2["Light"] = "light"; + ForcedColorsModeTheme2["Dark"] = "dark"; + })(ForcedColorsModeTheme = Emulation2.ForcedColorsModeTheme || (Emulation2.ForcedColorsModeTheme = {})); + })(Emulation || (Emulation = {})); + ((Emulation2) => { + let ScreenOrientationNatural; + ((ScreenOrientationNatural2) => { + ScreenOrientationNatural2["Portrait"] = "portrait"; + ScreenOrientationNatural2["Landscape"] = "landscape"; + })(ScreenOrientationNatural = Emulation2.ScreenOrientationNatural || (Emulation2.ScreenOrientationNatural = {})); + })(Emulation || (Emulation = {})); + ((Network3) => { + let CollectorType; + ((CollectorType2) => { + CollectorType2["Blob"] = "blob"; + })(CollectorType = Network3.CollectorType || (Network3.CollectorType = {})); + })(Network || (Network = {})); + ((Network3) => { + let SameSite; + ((SameSite2) => { + SameSite2["Strict"] = "strict"; + SameSite2["Lax"] = "lax"; + SameSite2["None"] = "none"; + SameSite2["Default"] = "default"; + })(SameSite = Network3.SameSite || (Network3.SameSite = {})); + })(Network || (Network = {})); + ((Network3) => { + let DataType; + ((DataType2) => { + DataType2["Request"] = "request"; + DataType2["Response"] = "response"; + })(DataType = Network3.DataType || (Network3.DataType = {})); + })(Network || (Network = {})); + ((Network3) => { + let InterceptPhase; + ((InterceptPhase2) => { + InterceptPhase2["BeforeRequestSent"] = "beforeRequestSent"; + InterceptPhase2["ResponseStarted"] = "responseStarted"; + InterceptPhase2["AuthRequired"] = "authRequired"; + })(InterceptPhase = Network3.InterceptPhase || (Network3.InterceptPhase = {})); + })(Network || (Network = {})); + ((Script2) => { + let ResultOwnership; + ((ResultOwnership2) => { + ResultOwnership2["Root"] = "root"; + ResultOwnership2["None"] = "none"; + })(ResultOwnership = Script2.ResultOwnership || (Script2.ResultOwnership = {})); + })(Script || (Script = {})); + ((Log2) => { + let Level; + ((Level2) => { + Level2["Debug"] = "debug"; + Level2["Info"] = "info"; + Level2["Warn"] = "warn"; + Level2["Error"] = "error"; + })(Level = Log2.Level || (Log2.Level = {})); + })(Log || (Log = {})); + ((Input2) => { + let PointerType; + ((PointerType2) => { + PointerType2["Mouse"] = "mouse"; + PointerType2["Pen"] = "pen"; + PointerType2["Touch"] = "touch"; + })(PointerType = Input2.PointerType || (Input2.PointerType = {})); + })(Input || (Input = {})); + } +}); + +// packages/playwright-core/src/server/bidi/third_party/bidiProtocolPermissions.ts +var Permissions; +var init_bidiProtocolPermissions = __esm({ + "packages/playwright-core/src/server/bidi/third_party/bidiProtocolPermissions.ts"() { + "use strict"; + ((Permissions2) => { + let PermissionState; + ((PermissionState2) => { + PermissionState2["Granted"] = "granted"; + PermissionState2["Denied"] = "denied"; + PermissionState2["Prompt"] = "prompt"; + })(PermissionState = Permissions2.PermissionState || (Permissions2.PermissionState = {})); + })(Permissions || (Permissions = {})); + } +}); + +// packages/playwright-core/src/server/bidi/third_party/bidiProtocol.ts +var init_bidiProtocol = __esm({ + "packages/playwright-core/src/server/bidi/third_party/bidiProtocol.ts"() { + "use strict"; + init_bidiProtocolCore(); + init_bidiProtocolPermissions(); + } +}); + +// packages/playwright-core/src/server/bidi/bidiNetworkManager.ts +function fromBidiHeaders(bidiHeaders) { + const result2 = []; + for (const { name, value: value2 } of bidiHeaders) + result2.push({ name, value: bidiBytesValueToString(value2) }); + return result2; +} +function toBidiRequestHeaders(allHeaders) { + const bidiHeaders = toBidiHeaders(allHeaders); + return { headers: bidiHeaders }; +} +function toBidiResponseHeaders(headers) { + const setCookieHeaders = headers.filter((h) => h.name.toLowerCase() === "set-cookie"); + const otherHeaders = headers.filter((h) => h.name.toLowerCase() !== "set-cookie"); + const rawCookies = setCookieHeaders.map((h) => parseRawCookie(h.value)); + const cookies = rawCookies.filter(Boolean).map((c) => { + return { + ...c, + value: { type: "string", value: c.value }, + sameSite: toBidiSameSite(c.sameSite) + }; + }); + return { cookies, headers: toBidiHeaders(otherHeaders) }; +} +function toBidiHeaders(headers) { + return headers.map(({ name, value: value2 }) => ({ name, value: { type: "string", value: value2 } })); +} +function bidiBytesValueToString(value2) { + if (value2.type === "string") + return value2.value; + if (value2.type === "base64") + return Buffer.from(value2.type, "base64").toString("binary"); + return "unknown value type: " + value2.type; +} +function toBidiSameSite(sameSite) { + if (!sameSite) + return void 0; + if (sameSite === "Strict") + return Network.SameSite.Strict; + if (sameSite === "Lax") + return Network.SameSite.Lax; + return Network.SameSite.None; +} +function resourceTypeFromBidi(requestDestination, requestInitiatorType, eventInitiatorType) { + switch (requestDestination) { + case "audio": + return "media"; + case "audioworklet": + return "script"; + case "document": + return "document"; + case "font": + return "font"; + case "frame": + return "document"; + case "iframe": + return "document"; + case "image": + return "image"; + case "object": + return "other"; + case "paintworklet": + return "script"; + case "script": + return "script"; + case "serviceworker": + return "script"; + case "sharedworker": + return "script"; + case "style": + return "stylesheet"; + case "track": + return "texttrack"; + case "video": + return "media"; + case "worker": + return "script"; + case "": + switch (requestInitiatorType) { + case "fetch": + return "fetch"; + case "font": + return "font"; + case "xmlhttprequest": + return "xhr"; + case null: + return eventInitiatorType === "script" ? "xhr" : "document"; + default: + return "other"; + } + default: + return "other"; + } +} +var REQUEST_BODY_HEADERS, BidiNetworkManager, BidiRequest, BidiRouteImpl; +var init_bidiNetworkManager = __esm({ + "packages/playwright-core/src/server/bidi/bidiNetworkManager.ts"() { + "use strict"; + init_eventsHelper(); + init_cookieStore(); + init_network2(); + init_bidiProtocol(); + REQUEST_BODY_HEADERS = /* @__PURE__ */ new Set(["content-encoding", "content-language", "content-location", "content-type"]); + BidiNetworkManager = class { + constructor(bidiSession, page) { + this._userRequestInterceptionEnabled = false; + this._protocolRequestInterceptionEnabled = false; + this._attemptedAuthentications = /* @__PURE__ */ new Set(); + this._session = bidiSession; + this._requests = /* @__PURE__ */ new Map(); + this._page = page; + this._eventListeners = [ + eventsHelper.addEventListener(bidiSession, "network.beforeRequestSent", this._onBeforeRequestSent.bind(this)), + eventsHelper.addEventListener(bidiSession, "network.responseStarted", this._onResponseStarted.bind(this)), + eventsHelper.addEventListener(bidiSession, "network.responseCompleted", this._onResponseCompleted.bind(this)), + eventsHelper.addEventListener(bidiSession, "network.fetchError", this._onFetchError.bind(this)), + eventsHelper.addEventListener(bidiSession, "network.authRequired", this._onAuthRequired.bind(this)) + ]; + } + dispose() { + eventsHelper.removeEventListeners(this._eventListeners); + } + _onBeforeRequestSent(param) { + if (param.request.url.startsWith("data:")) + return; + const redirectedFrom = param.redirectCount ? this._requests.get(param.request.request) || null : null; + const frame = redirectedFrom ? redirectedFrom.request.frame() : param.context ? this._page.frameManager.frame(param.context) : null; + if (!frame) + return; + if (redirectedFrom) + this._deleteRequest(redirectedFrom._id); + if (param.request.method === "OPTIONS") { + const requestHeaders = Object.fromEntries(param.request.headers.map((h) => [h.name.toLowerCase(), bidiBytesValueToString(h.value)])); + if (param.initiator?.type === "preflight" || requestHeaders["access-control-request-method"]) { + if (param.intercepts) { + const responseHeaders = [ + { name: "Access-Control-Allow-Origin", value: requestHeaders["origin"] || "*" }, + { name: "Access-Control-Allow-Methods", value: requestHeaders["access-control-request-method"] }, + { name: "Access-Control-Allow-Credentials", value: "true" } + ]; + if (requestHeaders["access-control-request-headers"]) + responseHeaders.push({ name: "Access-Control-Allow-Headers", value: requestHeaders["access-control-request-headers"] }); + this._session.sendMayFail("network.provideResponse", { + request: param.request.request, + statusCode: 204, + headers: toBidiHeaders(responseHeaders) + }); + } + return; + } + } + let route2; + let headersOverride; + if (param.intercepts) { + if (redirectedFrom) { + let params2 = {}; + if (redirectedFrom._originalRequestRoute?._alreadyContinuedHeaders) { + const originalHeaders = fromBidiHeaders(param.request.headers); + headersOverride = applyHeadersOverrides(originalHeaders, redirectedFrom._originalRequestRoute._alreadyContinuedHeaders); + if (redirectedFrom.request.method() === "POST" && param.request.method === "GET") + headersOverride = headersOverride.filter(({ name }) => !REQUEST_BODY_HEADERS.has(name.toLowerCase())); + params2 = toBidiRequestHeaders(headersOverride); + } + this._session.sendMayFail("network.continueRequest", { + request: param.request.request, + ...params2 + }); + } else { + route2 = new BidiRouteImpl(this._session, param.request.request); + } + } + const request2 = new BidiRequest(frame, redirectedFrom, param, route2, headersOverride); + this._requests.set(request2._id, request2); + this._page.frameManager.requestStarted(request2.request, route2); + } + _onResponseStarted(params2) { + const request2 = this._requests.get(params2.request.request); + if (!request2) + return; + const getResponseBody = async () => { + const { bytes } = await this._session.send("network.getData", { request: params2.request.request, dataType: Network.DataType.Response }); + const encoding = bytes.type === "base64" ? "base64" : "utf8"; + return Buffer.from(bytes.value, encoding); + }; + const timings = params2.request.timings; + const startTime = timings.requestTime; + function relativeToStart(time) { + if (!time) + return -1; + return time - startTime; + } + const timing = { + startTime, + requestStart: relativeToStart(timings.requestStart), + responseStart: relativeToStart(timings.responseStart), + domainLookupStart: relativeToStart(timings.dnsStart), + domainLookupEnd: relativeToStart(timings.dnsEnd), + connectStart: relativeToStart(timings.connectStart), + secureConnectionStart: relativeToStart(timings.tlsStart), + connectEnd: relativeToStart(timings.connectEnd) + }; + const response2 = new Response2(request2.request, params2.response.status, params2.response.statusText, fromBidiHeaders(params2.response.headers), timing, getResponseBody, false); + response2._serverAddrFinished(); + response2._securityDetailsFinished(); + response2._setHttpVersion(params2.response.protocol); + response2.setRawResponseHeaders(null); + response2.setResponseHeadersSize(params2.response.headersSize); + this._page.frameManager.requestReceivedResponse(response2); + } + _onResponseCompleted(params2) { + const request2 = this._requests.get(params2.request.request); + if (!request2) + return; + const response2 = request2.request._existingResponse(); + response2.setTransferSize(params2.response.bodySize); + response2.setEncodedBodySize(params2.response.bodySize); + const isRedirected = response2.status() >= 300 && response2.status() <= 399; + const responseEndTime = params2.request.timings.responseEnd - response2.timing().startTime; + if (isRedirected) { + response2._requestFinished(responseEndTime); + } else { + this._deleteRequest(request2._id); + response2._requestFinished(responseEndTime); + } + this._page.frameManager.reportRequestFinished(request2.request, response2); + } + _onFetchError(params2) { + const request2 = this._requests.get(params2.request.request); + if (!request2) + return; + this._deleteRequest(request2._id); + const response2 = request2.request._existingResponse(); + if (response2) { + response2.setTransferSize(null); + response2.setEncodedBodySize(null); + response2._requestFinished(-1); + } + request2.request._setFailureText(params2.errorText); + this._page.frameManager.requestFailed(request2.request, params2.errorText === "NS_BINDING_ABORTED"); + } + _onAuthRequired(params2) { + const isBasic = params2.response.authChallenges?.some((challenge) => challenge.scheme.startsWith("Basic")); + const credentials = this._page.browserContext._options.httpCredentials; + if (isBasic && credentials && (!credentials.origin || new URL(params2.request.url).origin.toLowerCase() === credentials.origin.toLowerCase())) { + if (this._attemptedAuthentications.has(params2.request.request)) { + this._session.sendMayFail("network.continueWithAuth", { + request: params2.request.request, + action: "cancel" + }); + } else { + this._attemptedAuthentications.add(params2.request.request); + this._session.sendMayFail("network.continueWithAuth", { + request: params2.request.request, + action: "provideCredentials", + credentials: { + type: "password", + username: credentials.username, + password: credentials.password + } + }); + } + } else { + this._session.sendMayFail("network.continueWithAuth", { + request: params2.request.request, + action: "cancel" + }); + } + } + _deleteRequest(requestId) { + this._requests.delete(requestId); + this._attemptedAuthentications.delete(requestId); + } + async setRequestInterception(value2) { + this._userRequestInterceptionEnabled = value2; + await this._updateProtocolRequestInterception(); + } + async setCredentials(credentials) { + this._credentials = credentials; + await this._updateProtocolRequestInterception(); + } + async _updateProtocolRequestInterception(initial) { + const enabled = this._userRequestInterceptionEnabled || !!this._credentials; + if (enabled === this._protocolRequestInterceptionEnabled) + return; + this._protocolRequestInterceptionEnabled = enabled; + if (initial && !enabled) + return; + const contexts = [this._page.delegate._session.sessionId]; + const cachePromise = this._session.send("network.setCacheBehavior", { cacheBehavior: enabled ? "bypass" : "default", contexts }); + let interceptPromise = Promise.resolve(void 0); + if (enabled) { + interceptPromise = this._session.send("network.addIntercept", { + phases: [Network.InterceptPhase.BeforeRequestSent], + contexts + }).then((r) => { + this._intercepId = r.intercept; + }); + } else if (this._intercepId) { + interceptPromise = this._session.send("network.removeIntercept", { intercept: this._intercepId }); + this._intercepId = void 0; + } + await Promise.all([cachePromise, interceptPromise]); + } + }; + BidiRequest = class { + constructor(frame, redirectedFrom, payload, route2, headersOverride) { + this._id = payload.request.request; + if (redirectedFrom) + redirectedFrom._redirectedTo = this; + const postDataBuffer = null; + this.request = new Request( + frame._page.browserContext, + frame, + null, + redirectedFrom ? redirectedFrom.request : null, + payload.navigation ?? void 0, + payload.request.url, + resourceTypeFromBidi(payload.request.destination, payload.request.initiatorType, payload.initiator?.type), + payload.request.method, + postDataBuffer, + headersOverride || fromBidiHeaders(payload.request.headers), + payload.timestamp + ); + this.request.setRawRequestHeaders(null); + this.request._setBodySize(payload.request.bodySize || 0); + this._originalRequestRoute = route2 ?? redirectedFrom?._originalRequestRoute; + route2?._setRequest(this.request); + } + _finalRequest() { + let request2 = this; + while (request2._redirectedTo) + request2 = request2._redirectedTo; + return request2; + } + }; + BidiRouteImpl = class { + constructor(session2, requestId) { + this._session = session2; + this._requestId = requestId; + } + _setRequest(request2) { + this._request = request2; + } + async continue(overrides) { + let headers = overrides.headers || this._request.headers(); + if (overrides.postData && headers) { + headers = headers.map((header) => { + if (header.name.toLowerCase() === "content-length") + return { name: header.name, value: overrides.postData.byteLength.toString() }; + return header; + }); + } + this._alreadyContinuedHeaders = headers; + await this._session.sendMayFail("network.continueRequest", { + request: this._requestId, + url: overrides.url, + method: overrides.method, + ...toBidiRequestHeaders(this._alreadyContinuedHeaders), + body: overrides.postData ? { type: "base64", value: Buffer.from(overrides.postData).toString("base64") } : void 0 + }); + } + async fulfill(response2) { + const base64body = response2.isBase64 ? response2.body : Buffer.from(response2.body).toString("base64"); + const headers = response2.headers.filter((h) => h.name.toLowerCase() !== "content-encoding"); + await this._session.sendMayFail("network.provideResponse", { + request: this._requestId, + statusCode: response2.status, + reasonPhrase: statusText(response2.status), + ...toBidiResponseHeaders(headers), + body: { type: "base64", value: base64body } + }); + } + async abort(errorCode) { + await this._session.sendMayFail("network.failRequest", { + request: this._requestId + }); + } + }; + } +}); + +// packages/playwright-core/src/server/bidi/third_party/bidiSerializer.ts +var UnserializableError, BidiSerializer, isPlainObject, isRegExp6, isDate3; +var init_bidiSerializer = __esm({ + "packages/playwright-core/src/server/bidi/third_party/bidiSerializer.ts"() { + "use strict"; + UnserializableError = class extends Error { + }; + BidiSerializer = class _BidiSerializer { + static serialize(arg) { + switch (typeof arg) { + case "symbol": + case "function": + throw new UnserializableError(`Unable to serializable ${typeof arg}`); + case "object": + return _BidiSerializer._serializeObject(arg); + case "undefined": + return { + type: "undefined" + }; + case "number": + return _BidiSerializer._serializeNumber(arg); + case "bigint": + return { + type: "bigint", + value: arg.toString() + }; + case "string": + return { + type: "string", + value: arg + }; + case "boolean": + return { + type: "boolean", + value: arg + }; + } + } + static _serializeNumber(arg) { + let value2; + if (Object.is(arg, -0)) { + value2 = "-0"; + } else if (Object.is(arg, Infinity)) { + value2 = "Infinity"; + } else if (Object.is(arg, -Infinity)) { + value2 = "-Infinity"; + } else if (Object.is(arg, NaN)) { + value2 = "NaN"; + } else { + value2 = arg; + } + return { + type: "number", + value: value2 + }; + } + static _serializeObject(arg) { + if (arg === null) { + return { + type: "null" + }; + } else if (Array.isArray(arg)) { + const parsedArray = arg.map((subArg) => { + return _BidiSerializer.serialize(subArg); + }); + return { + type: "array", + value: parsedArray + }; + } else if (isPlainObject(arg)) { + try { + JSON.stringify(arg); + } catch (error) { + if (error instanceof TypeError && error.message.startsWith("Converting circular structure to JSON")) { + error.message += " Recursive objects are not allowed."; + } + throw error; + } + const parsedObject = []; + for (const key in arg) { + parsedObject.push([_BidiSerializer.serialize(key), _BidiSerializer.serialize(arg[key])]); + } + return { + type: "object", + value: parsedObject + }; + } else if (isRegExp6(arg)) { + return { + type: "regexp", + value: { + pattern: arg.source, + flags: arg.flags + } + }; + } else if (isDate3(arg)) { + return { + type: "date", + value: arg.toISOString() + }; + } + throw new UnserializableError( + "Custom object serialization not possible. Use plain objects instead." + ); + } + }; + isPlainObject = (obj) => { + return typeof obj === "object" && obj?.constructor === Object; + }; + isRegExp6 = (obj) => { + return typeof obj === "object" && obj?.constructor === RegExp; + }; + isDate3 = (obj) => { + return typeof obj === "object" && obj?.constructor === Date; + }; + } +}); + +// packages/playwright-core/src/server/bidi/bidiDeserializer.ts +function deserializeBidiValue(result2, internalIdMap = /* @__PURE__ */ new Map()) { + switch (result2.type) { + case "undefined": + return void 0; + case "null": + return null; + case "number": + return typeof result2.value === "number" ? result2.value : parseUnserializableValue(result2.value); + case "boolean": + return Boolean(result2.value); + case "string": + return result2.value; + case "bigint": + return BigInt(result2.value); + case "array": + return deserializeBidiList(result2, internalIdMap); + case "arraybuffer": + return getValue(result2, internalIdMap, () => ({})); + case "date": + return getValue(result2, internalIdMap, () => new Date(result2.value)); + case "error": + return getValue(result2, internalIdMap, () => { + const error = new Error(); + error.stack = ""; + return error; + }); + case "function": + return void 0; + case "generator": + return getValue(result2, internalIdMap, () => ({})); + case "htmlcollection": + return { ...deserializeBidiList(result2, internalIdMap) }; + case "map": + return getValue(result2, internalIdMap, () => ({})); + case "node": + return "ref: "; + case "nodelist": + return { ...deserializeBidiList(result2, internalIdMap) }; + case "object": + return deserializeBidiMapping(result2, internalIdMap); + case "promise": + return getValue(result2, internalIdMap, () => ({})); + case "proxy": + return getValue(result2, internalIdMap, () => ({})); + case "regexp": + return getValue(result2, internalIdMap, () => new RegExp(result2.value.pattern, result2.value.flags)); + case "set": + return getValue(result2, internalIdMap, () => ({})); + case "symbol": + return void 0; + case "typedarray": + return void 0; + case "weakmap": + return getValue(result2, internalIdMap, () => ({})); + case "weakset": + return getValue(result2, internalIdMap, () => ({})); + case "window": + return "ref: "; + } +} +function getValue(bidiValue, internalIdMap, defaultValue) { + if ("internalId" in bidiValue && bidiValue.internalId) { + if (internalIdMap.has(bidiValue.internalId)) { + return internalIdMap.get(bidiValue.internalId); + } else { + const value2 = defaultValue(); + internalIdMap.set(bidiValue.internalId, value2); + return value2; + } + } else { + return defaultValue(); + } +} +function deserializeBidiList(bidiValue, internalIdMap) { + const result2 = getValue(bidiValue, internalIdMap, () => []); + for (const val of bidiValue.value || []) + result2.push(deserializeBidiValue(val, internalIdMap)); + return result2; +} +function deserializeBidiMapping(bidiValue, internalIdMap) { + const result2 = getValue(bidiValue, internalIdMap, () => ({})); + for (const [serializedKey, serializedValue] of bidiValue.value || []) { + const key = typeof serializedKey === "string" ? serializedKey : deserializeBidiValue(serializedKey, internalIdMap); + const value2 = deserializeBidiValue(serializedValue, internalIdMap); + result2[key] = value2; + } + return result2; +} +var init_bidiDeserializer = __esm({ + "packages/playwright-core/src/server/bidi/bidiDeserializer.ts"() { + "use strict"; + init_javascript(); + } +}); + +// packages/playwright-core/src/server/bidi/bidiExecutionContext.ts +function rewriteError2(error) { + if (error.message.includes("too much recursion") || error.message.includes("stack limit exceeded")) + throw new Error("Cannot serialize result: object reference chain is too long."); + throw error; +} +function renderPreview2(remoteObject, nested = false) { + switch (remoteObject.type) { + case "undefined": + case "null": + return remoteObject.type; + case "number": + case "boolean": + case "string": + return String(remoteObject.value); + case "bigint": + return `${remoteObject.value}n`; + case "date": + return String(new Date(remoteObject.value)); + case "regexp": + return String(new RegExp(remoteObject.value.pattern, remoteObject.value.flags)); + case "node": + return remoteObject.value?.localName || "Node"; + case "object": + if (nested) + return "Object"; + const tokens = []; + for (const [name, value2] of remoteObject.value || []) { + if (typeof name === "string") + tokens.push(`${name}: ${renderPreview2(value2, true)}`); + } + return `{${tokens.join(", ")}}`; + case "array": + case "htmlcollection": + case "nodelist": + if (nested || !remoteObject.value) + return remoteObject.value ? `Array(${remoteObject.value.length})` : "Array"; + return `[${remoteObject.value.map((v) => renderPreview2(v, true)).join(", ")}]`; + case "map": + return remoteObject.value ? `Map(${remoteObject.value.length})` : "Map"; + case "set": + return remoteObject.value ? `Set(${remoteObject.value.length})` : "Set"; + case "arraybuffer": + return "ArrayBuffer"; + case "error": + return "Error"; + case "function": + return "Function"; + case "generator": + return "Generator"; + case "promise": + return "Promise"; + case "proxy": + return "Proxy"; + case "symbol": + return "Symbol()"; + case "typedarray": + return "TypedArray"; + case "weakmap": + return "WeakMap"; + case "weakset": + return "WeakSet"; + case "window": + return "Window"; + } +} +function createHandle2(context2, remoteObject) { + if (remoteObject.type === "node") { + assert(context2 instanceof FrameExecutionContext); + return new ElementHandle(context2, remoteObject.handle); + } + const objectId = "handle" in remoteObject ? remoteObject.handle : void 0; + const preview = renderPreview2(remoteObject); + const handle = new JSHandle(context2, remoteObject.type, preview, objectId, deserializeBidiValue(remoteObject)); + handle._setPreview(preview); + return handle; +} +var BidiExecutionContext; +var init_bidiExecutionContext = __esm({ + "packages/playwright-core/src/server/bidi/bidiExecutionContext.ts"() { + "use strict"; + init_utilityScriptSerializers(); + init_assert(); + init_javascript(); + init_dom(); + init_bidiProtocol(); + init_bidiSerializer(); + init_bidiDeserializer(); + BidiExecutionContext = class { + constructor(session2, realmInfo) { + this._session = session2; + if (realmInfo.type === "window") { + this._target = { + context: realmInfo.context, + sandbox: realmInfo.sandbox + }; + } else { + this._target = { + realm: realmInfo.realm + }; + } + } + async rawEvaluateJSON(expression2) { + const response2 = await this._session.send("script.evaluate", { + expression: expression2, + target: this._target, + serializationOptions: { + maxObjectDepth: 10, + maxDomDepth: 10 + }, + awaitPromise: true, + userActivation: true + }); + if (response2.type === "success") + return deserializeBidiValue(response2.result); + if (response2.type === "exception") + throw new JavaScriptErrorInEvaluate(response2.exceptionDetails.text); + throw new JavaScriptErrorInEvaluate("Unexpected response type: " + JSON.stringify(response2)); + } + async rawEvaluateHandle(context2, expression2) { + const response2 = await this._session.send("script.evaluate", { + expression: expression2, + target: this._target, + resultOwnership: Script.ResultOwnership.Root, + // Necessary for the handle to be returned. + serializationOptions: { maxObjectDepth: 0, maxDomDepth: 0 }, + awaitPromise: true, + userActivation: true + }); + if (response2.type === "success") { + if ("handle" in response2.result) + return createHandle2(context2, response2.result); + throw new JavaScriptErrorInEvaluate("Cannot get handle: " + JSON.stringify(response2.result)); + } + if (response2.type === "exception") + throw new JavaScriptErrorInEvaluate(response2.exceptionDetails.text); + throw new JavaScriptErrorInEvaluate("Unexpected response type: " + JSON.stringify(response2)); + } + async evaluateWithArguments(functionDeclaration, returnByValue, utilityScript, values, handles) { + const response2 = await this._session.send("script.callFunction", { + functionDeclaration, + target: this._target, + arguments: [ + { handle: utilityScript._objectId }, + ...values.map(BidiSerializer.serialize), + ...handles.map((handle) => ({ handle: handle._objectId })) + ], + resultOwnership: returnByValue ? void 0 : Script.ResultOwnership.Root, + // Necessary for the handle to be returned. + serializationOptions: returnByValue ? {} : { maxObjectDepth: 0, maxDomDepth: 0 }, + awaitPromise: true, + userActivation: true + }).catch(rewriteError2); + if (response2.type === "exception") + throw new JavaScriptErrorInEvaluate(response2.exceptionDetails.text); + if (response2.type === "success") { + if (returnByValue) + return parseEvaluationResultValue(deserializeBidiValue(response2.result)); + return createHandle2(utilityScript._context, response2.result); + } + throw new JavaScriptErrorInEvaluate("Unexpected response type: " + JSON.stringify(response2)); + } + async getProperties(handle) { + const names = await handle.evaluate((object) => { + const names2 = []; + const descriptors = Object.getOwnPropertyDescriptors(object); + for (const name in descriptors) { + if (descriptors[name]?.enumerable) + names2.push(name); + } + return names2; + }); + const values = await Promise.all(names.map(async (name) => { + const value2 = await this._rawCallFunction("(object, name) => object[name]", [{ handle: handle._objectId }, { type: "string", value: name }], true, false); + return createHandle2(handle._context, value2); + })); + const map = /* @__PURE__ */ new Map(); + for (let i = 0; i < names.length; i++) + map.set(names[i], values[i]); + return map; + } + async releaseHandle(handle) { + if (!handle._objectId) + return; + await this._session.send("script.disown", { + target: this._target, + handles: [handle._objectId] + }); + } + shouldPrependErrorPrefix() { + return false; + } + async nodeIdForElementHandle(handle) { + const shared = await this._remoteValueForReference({ handle: handle._objectId }); + if (!("sharedId" in shared)) + throw new Error("Element is not a node"); + return { + sharedId: shared.sharedId + }; + } + async remoteObjectForNodeId(context2, nodeId) { + const result2 = await this._remoteValueForReference(nodeId, true); + if (!("handle" in result2)) + throw new Error("Can't get remote object for nodeId"); + return createHandle2(context2, result2); + } + async contentFrameIdForFrame(handle) { + const contentWindow = await this._rawCallFunction("e => e.contentWindow", [{ handle: handle._objectId }]); + if (contentWindow?.type === "window") + return contentWindow.value.context; + return null; + } + async frameIdForWindowHandle(handle) { + if (!handle._objectId) + throw new Error("JSHandle is not a DOM node handle"); + const contentWindow = await this._remoteValueForReference({ handle: handle._objectId }); + if (contentWindow.type === "window") + return contentWindow.value.context; + return null; + } + async _remoteValueForReference(reference, createHandle6) { + return await this._rawCallFunction("e => e", [reference], createHandle6); + } + async _rawCallFunction(functionDeclaration, args, createHandle6, awaitPromise = true) { + const response2 = await this._session.send("script.callFunction", { + functionDeclaration, + target: this._target, + arguments: args, + // "Root" is necessary for the handle to be returned. + resultOwnership: createHandle6 ? Script.ResultOwnership.Root : Script.ResultOwnership.None, + serializationOptions: { maxObjectDepth: 0, maxDomDepth: 0 }, + awaitPromise, + userActivation: true + }); + if (response2.type === "exception") + throw new JavaScriptErrorInEvaluate(response2.exceptionDetails.text); + if (response2.type === "success") + return response2.result; + throw new JavaScriptErrorInEvaluate("Unexpected response type: " + JSON.stringify(response2)); + } + }; + } +}); + +// packages/playwright-core/src/server/bidi/third_party/bidiKeyboard.ts +var getBidiKeyValue; +var init_bidiKeyboard = __esm({ + "packages/playwright-core/src/server/bidi/third_party/bidiKeyboard.ts"() { + "use strict"; + getBidiKeyValue = (keyName) => { + switch (keyName) { + case "\r": + case "\n": + keyName = "Enter"; + break; + } + if ([...keyName].length === 1) { + return keyName; + } + switch (keyName) { + case "Cancel": + return "\uE001"; + case "Help": + return "\uE002"; + case "Backspace": + return "\uE003"; + case "Tab": + return "\uE004"; + case "Clear": + return "\uE005"; + case "Enter": + return "\uE007"; + case "Shift": + case "ShiftLeft": + return "\uE008"; + case "Control": + case "ControlLeft": + return "\uE009"; + case "Alt": + case "AltLeft": + return "\uE00A"; + case "Pause": + return "\uE00B"; + case "Escape": + return "\uE00C"; + case "PageUp": + return "\uE00E"; + case "PageDown": + return "\uE00F"; + case "End": + return "\uE010"; + case "Home": + return "\uE011"; + case "ArrowLeft": + return "\uE012"; + case "ArrowUp": + return "\uE013"; + case "ArrowRight": + return "\uE014"; + case "ArrowDown": + return "\uE015"; + case "Insert": + return "\uE016"; + case "Delete": + return "\uE017"; + case "NumpadEqual": + return "\uE019"; + case "Numpad0": + return "\uE01A"; + case "Numpad1": + return "\uE01B"; + case "Numpad2": + return "\uE01C"; + case "Numpad3": + return "\uE01D"; + case "Numpad4": + return "\uE01E"; + case "Numpad5": + return "\uE01F"; + case "Numpad6": + return "\uE020"; + case "Numpad7": + return "\uE021"; + case "Numpad8": + return "\uE022"; + case "Numpad9": + return "\uE023"; + case "NumpadMultiply": + return "\uE024"; + case "NumpadAdd": + return "\uE025"; + case "NumpadSubtract": + return "\uE027"; + case "NumpadDecimal": + return "\uE028"; + case "NumpadDivide": + return "\uE029"; + case "F1": + return "\uE031"; + case "F2": + return "\uE032"; + case "F3": + return "\uE033"; + case "F4": + return "\uE034"; + case "F5": + return "\uE035"; + case "F6": + return "\uE036"; + case "F7": + return "\uE037"; + case "F8": + return "\uE038"; + case "F9": + return "\uE039"; + case "F10": + return "\uE03A"; + case "F11": + return "\uE03B"; + case "F12": + return "\uE03C"; + case "Meta": + case "MetaLeft": + return "\uE03D"; + case "ShiftRight": + return "\uE050"; + case "ControlRight": + return "\uE051"; + case "AltRight": + return "\uE052"; + case "MetaRight": + return "\uE053"; + case "Space": + return " "; + case "Digit0": + return "0"; + case "Digit1": + return "1"; + case "Digit2": + return "2"; + case "Digit3": + return "3"; + case "Digit4": + return "4"; + case "Digit5": + return "5"; + case "Digit6": + return "6"; + case "Digit7": + return "7"; + case "Digit8": + return "8"; + case "Digit9": + return "9"; + case "KeyA": + return "a"; + case "KeyB": + return "b"; + case "KeyC": + return "c"; + case "KeyD": + return "d"; + case "KeyE": + return "e"; + case "KeyF": + return "f"; + case "KeyG": + return "g"; + case "KeyH": + return "h"; + case "KeyI": + return "i"; + case "KeyJ": + return "j"; + case "KeyK": + return "k"; + case "KeyL": + return "l"; + case "KeyM": + return "m"; + case "KeyN": + return "n"; + case "KeyO": + return "o"; + case "KeyP": + return "p"; + case "KeyQ": + return "q"; + case "KeyR": + return "r"; + case "KeyS": + return "s"; + case "KeyT": + return "t"; + case "KeyU": + return "u"; + case "KeyV": + return "v"; + case "KeyW": + return "w"; + case "KeyX": + return "x"; + case "KeyY": + return "y"; + case "KeyZ": + return "z"; + case "Semicolon": + return ";"; + case "Equal": + return "="; + case "Comma": + return ","; + case "Minus": + return "-"; + case "Period": + return "."; + case "Slash": + return "/"; + case "Backquote": + return "`"; + case "BracketLeft": + return "["; + case "Backslash": + return "\\"; + case "BracketRight": + return "]"; + case "Quote": + return '"'; + default: + throw new Error(`Unknown key: "${keyName}"`); + } + }; + } +}); + +// packages/playwright-core/src/server/bidi/bidiInput.ts +function toBidiButton(button) { + switch (button) { + case "left": + return 0; + case "right": + return 2; + case "middle": + return 1; + } + throw new Error("Unknown button: " + button); +} +var RawKeyboardImpl2, RawMouseImpl2, RawTouchscreenImpl2; +var init_bidiInput = __esm({ + "packages/playwright-core/src/server/bidi/bidiInput.ts"() { + "use strict"; + init_input(); + init_bidiKeyboard(); + init_bidiProtocol(); + RawKeyboardImpl2 = class { + constructor(session2) { + this._session = session2; + } + setSession(session2) { + this._session = session2; + } + async keydown(progress2, modifiers, keyName, description, autoRepeat) { + keyName = resolveSmartModifierString(keyName); + const actions = []; + actions.push({ type: "keyDown", value: getBidiKeyValue(keyName) }); + await this._performActions(progress2, actions); + } + async keyup(progress2, modifiers, keyName, description) { + keyName = resolveSmartModifierString(keyName); + const actions = []; + actions.push({ type: "keyUp", value: getBidiKeyValue(keyName) }); + await this._performActions(progress2, actions); + } + async sendText(progress2, text2) { + const actions = []; + for (const char of text2) { + const value2 = getBidiKeyValue(char); + actions.push({ type: "keyDown", value: value2 }); + actions.push({ type: "keyUp", value: value2 }); + } + await this._performActions(progress2, actions); + } + async _performActions(progress2, actions) { + await progress2.race(this._session.send("input.performActions", { + context: this._session.sessionId, + actions: [ + { + type: "key", + id: "pw_keyboard", + actions + } + ] + })); + } + }; + RawMouseImpl2 = class { + constructor(session2) { + this._session = session2; + } + async move(progress2, x, y, button, buttons, modifiers, forClick) { + await this._performActions(progress2, [{ type: "pointerMove", x, y }]); + } + async down(progress2, x, y, button, buttons, modifiers, clickCount) { + await this._performActions(progress2, [{ type: "pointerDown", button: toBidiButton(button) }]); + } + async up(progress2, x, y, button, buttons, modifiers, clickCount) { + await this._performActions(progress2, [{ type: "pointerUp", button: toBidiButton(button) }]); + } + async wheel(progress2, x, y, buttons, modifiers, deltaX, deltaY) { + x = Math.floor(x); + y = Math.floor(y); + await progress2.race(this._session.send("input.performActions", { + context: this._session.sessionId, + actions: [ + { + type: "wheel", + id: "pw_mouse_wheel", + actions: [{ type: "scroll", x, y, deltaX, deltaY }] + } + ] + })); + } + async _performActions(progress2, actions) { + await progress2.race(this._session.send("input.performActions", { + context: this._session.sessionId, + actions: [ + { + type: "pointer", + id: "pw_mouse", + parameters: { + pointerType: Input.PointerType.Mouse + }, + actions + } + ] + })); + } + }; + RawTouchscreenImpl2 = class { + constructor(session2) { + this._session = session2; + } + async tap(progress2, x, y, modifiers) { + } + }; + } +}); + +// packages/playwright-core/src/server/bidi/bidiPdf.ts +function convertPrintParameterToInches2(text2) { + if (text2 === void 0) + return void 0; + let unit = text2.substring(text2.length - 2).toLowerCase(); + let valueText = ""; + if (unitToPixels2.hasOwnProperty(unit)) { + valueText = text2.substring(0, text2.length - 2); + } else { + unit = "px"; + valueText = text2; + } + const value2 = Number(valueText); + assert(!isNaN(value2), "Failed to parse parameter value: " + text2); + const pixels = value2 * unitToPixels2[unit]; + return pixels / 96; +} +var PagePaperFormats2, unitToPixels2, BidiPDF; +var init_bidiPdf = __esm({ + "packages/playwright-core/src/server/bidi/bidiPdf.ts"() { + "use strict"; + init_assert(); + PagePaperFormats2 = { + letter: { width: 8.5, height: 11 }, + legal: { width: 8.5, height: 14 }, + tabloid: { width: 11, height: 17 }, + ledger: { width: 17, height: 11 }, + a0: { width: 33.1, height: 46.8 }, + a1: { width: 23.4, height: 33.1 }, + a2: { width: 16.54, height: 23.4 }, + a3: { width: 11.7, height: 16.54 }, + a4: { width: 8.27, height: 11.7 }, + a5: { width: 5.83, height: 8.27 }, + a6: { width: 4.13, height: 5.83 } + }; + unitToPixels2 = { + "px": 1, + "in": 96, + "cm": 37.8, + "mm": 3.78 + }; + BidiPDF = class { + constructor(session2) { + this._session = session2; + } + async generate(options) { + const { + scale = 1, + printBackground = false, + landscape = false, + pageRanges = "", + margin = {} + } = options; + let paperWidth = 8.5; + let paperHeight = 11; + if (options.format) { + const format2 = PagePaperFormats2[options.format.toLowerCase()]; + assert(format2, "Unknown paper format: " + options.format); + paperWidth = format2.width; + paperHeight = format2.height; + } else { + paperWidth = convertPrintParameterToInches2(options.width) || paperWidth; + paperHeight = convertPrintParameterToInches2(options.height) || paperHeight; + } + const { data } = await this._session.send("browsingContext.print", { + context: this._session.sessionId, + background: printBackground, + margin: { + bottom: convertPrintParameterToInches2(margin.bottom) || 0, + left: convertPrintParameterToInches2(margin.left) || 0, + right: convertPrintParameterToInches2(margin.right) || 0, + top: convertPrintParameterToInches2(margin.top) || 0 + }, + orientation: landscape ? "landscape" : "portrait", + page: { + width: paperWidth, + height: paperHeight + }, + pageRanges: pageRanges ? pageRanges.split(",").map((r) => r.trim()) : void 0, + scale + }); + return Buffer.from(data, "base64"); + } + }; + } +}); + +// packages/playwright-core/src/server/bidi/bidiPage.ts +function toBidiExecutionContext(executionContext) { + return executionContext.delegate; +} +var UTILITY_WORLD_NAME, kPlaywrightBindingChannel, BidiPage; +var init_bidiPage = __esm({ + "packages/playwright-core/src/server/bidi/bidiPage.ts"() { + "use strict"; + init_debugLogger(); + init_eventsHelper(); + init_dialog(); + init_dom(); + init_bidiBrowser(); + init_page(); + init_bidiExecutionContext(); + init_bidiInput(); + init_bidiNetworkManager(); + init_bidiPdf(); + init_bidiProtocol(); + init_progress(); + init_network2(); + UTILITY_WORLD_NAME = "__playwright_utility_world__"; + kPlaywrightBindingChannel = "playwrightChannel"; + BidiPage = class { + constructor(browserContext, bidiSession, opener) { + this._realmToWorkerContext = /* @__PURE__ */ new Map(); + this._sessionListeners = []; + this._initScriptIds = /* @__PURE__ */ new Map(); + this._fragmentNavigations = /* @__PURE__ */ new Set(); + this._session = bidiSession; + this._opener = opener; + this.rawKeyboard = new RawKeyboardImpl2(bidiSession); + this.rawMouse = new RawMouseImpl2(bidiSession); + this.rawTouchscreen = new RawTouchscreenImpl2(bidiSession); + this._contextIdToContext = /* @__PURE__ */ new Map(); + this._page = new Page(this, browserContext); + this._browserContext = browserContext; + this._networkManager = new BidiNetworkManager(this._session, this._page); + this._pdf = new BidiPDF(this._session); + this._page.on(Page.Events.FrameDetached, (frame) => this._removeContextsForFrame(frame, false)); + this._sessionListeners = [ + eventsHelper.addEventListener(bidiSession, "script.realmCreated", this._onRealmCreated.bind(this)), + eventsHelper.addEventListener(bidiSession, "script.message", this._onScriptMessage.bind(this)), + eventsHelper.addEventListener(bidiSession, "browsingContext.contextDestroyed", this._onBrowsingContextDestroyed.bind(this)), + eventsHelper.addEventListener(bidiSession, "browsingContext.navigationStarted", this._onNavigationStarted.bind(this)), + eventsHelper.addEventListener(bidiSession, "browsingContext.navigationCommitted", this._onNavigationCommitted.bind(this)), + eventsHelper.addEventListener(bidiSession, "browsingContext.navigationAborted", this._onNavigationAborted.bind(this)), + eventsHelper.addEventListener(bidiSession, "browsingContext.navigationFailed", this._onNavigationFailed.bind(this)), + eventsHelper.addEventListener(bidiSession, "browsingContext.fragmentNavigated", this._onFragmentNavigated.bind(this)), + eventsHelper.addEventListener(bidiSession, "browsingContext.historyUpdated", this._onHistoryUpdated.bind(this)), + eventsHelper.addEventListener(bidiSession, "browsingContext.domContentLoaded", this._onDomContentLoaded.bind(this)), + eventsHelper.addEventListener(bidiSession, "browsingContext.load", this._onLoad.bind(this)), + eventsHelper.addEventListener(bidiSession, "browsingContext.downloadWillBegin", this._onDownloadWillBegin.bind(this)), + eventsHelper.addEventListener(bidiSession, "browsingContext.downloadEnd", this._onDownloadEnded.bind(this)), + eventsHelper.addEventListener(bidiSession, "browsingContext.userPromptOpened", this._onUserPromptOpened.bind(this)), + eventsHelper.addEventListener(bidiSession, "log.entryAdded", this._onLogEntryAdded.bind(this)), + eventsHelper.addEventListener(bidiSession, "input.fileDialogOpened", this._onFileDialogOpened.bind(this)) + ]; + this._initialize().then( + () => this._page.reportAsNew(this._opener?._page), + (error) => this._page.reportAsNew(this._opener?._page, error) + ); + } + async _initialize() { + this._onFrameAttached(this._session.sessionId, null); + await Promise.all([ + this.updateHttpCredentials() + // If the page is created by the Playwright client's call, some initialization + // may be pending. Wait for it to complete before reporting the page as new. + ]); + } + didClose() { + this._session.dispose(); + eventsHelper.removeEventListeners(this._sessionListeners); + this._page._didClose(); + } + _onFrameAttached(frameId, parentFrameId) { + return this._page.frameManager.frameAttached(frameId, parentFrameId); + } + _removeContextsForFrame(frame, notifyFrame) { + for (const [contextId4, context2] of this._contextIdToContext) { + if (context2.frame === frame) { + this._contextIdToContext.delete(contextId4); + if (notifyFrame) + frame.contextDestroyed(context2); + } + } + } + _onRealmCreated(realmInfo) { + if (realmInfo.type === "dedicated-worker") { + const delegate2 = new BidiExecutionContext(this._session, realmInfo); + const worker = new Worker(this._page, realmInfo.origin); + this._realmToWorkerContext.set(realmInfo.realm, worker.createExecutionContext(delegate2)); + worker.workerScriptLoaded(); + this._page.addWorker(realmInfo.realm, worker); + return; + } + if (this._contextIdToContext.has(realmInfo.realm)) + return; + if (realmInfo.type !== "window") + return; + const frame = this._page.frameManager.frame(realmInfo.context); + if (!frame) + return; + let worldName; + if (!realmInfo.sandbox) { + worldName = "main"; + this._touchUtilityWorld(realmInfo.context); + } else if (realmInfo.sandbox === UTILITY_WORLD_NAME) { + worldName = "utility"; + } else { + return; + } + const delegate = new BidiExecutionContext(this._session, realmInfo); + const context2 = new FrameExecutionContext(delegate, frame, worldName); + frame.contextCreated(worldName, context2); + this._contextIdToContext.set(realmInfo.realm, context2); + } + async _touchUtilityWorld(context2) { + await this._session.sendMayFail("script.evaluate", { + expression: "1 + 1", + target: { + context: context2, + sandbox: UTILITY_WORLD_NAME + }, + serializationOptions: { + maxObjectDepth: 10, + maxDomDepth: 10 + }, + awaitPromise: true, + userActivation: true + }); + } + _onRealmDestroyed(params2) { + const context2 = this._contextIdToContext.get(params2.realm); + if (context2) { + this._contextIdToContext.delete(params2.realm); + context2.frame.contextDestroyed(context2); + return true; + } + const existed = this._realmToWorkerContext.delete(params2.realm); + if (existed) { + this._page.removeWorker(params2.realm); + return true; + } + return false; + } + // TODO: route the message directly to the browser + _onBrowsingContextDestroyed(params2) { + this._browserContext._browser._onBrowsingContextDestroyed(params2); + } + _onNavigationStarted(params2) { + const frameId = params2.context; + this._page.frameManager.frameRequestedNavigation(frameId, params2.navigation); + } + _onNavigationCommitted(params2) { + const frameId = params2.context; + const frame = this._page.frameManager.frame(frameId); + this._browserContext.doGrantGlobalPermissionsForURL(params2.url).catch((error) => debugLogger.log("error", error)); + this._page.frameManager.frameCommittedNewDocumentNavigation( + frameId, + params2.url, + frame._name, + params2.navigation, + /* initial */ + false + ); + } + _onDomContentLoaded(params2) { + const frameId = params2.context; + this._page.frameManager.frameLifecycleEvent(frameId, "domcontentloaded"); + } + _onLoad(params2) { + this._page.frameManager.frameLifecycleEvent(params2.context, "load"); + } + _onNavigationAborted(params2) { + this._page.frameManager.frameAbortedNavigation(params2.context, "Navigation aborted", params2.navigation || void 0); + } + _onNavigationFailed(params2) { + this._page.frameManager.frameAbortedNavigation(params2.context, "Navigation failed", params2.navigation || void 0); + } + _onFragmentNavigated(params2) { + if (params2.navigation) + this._fragmentNavigations.add(params2.navigation); + this._page.frameManager.frameCommittedSameDocumentNavigation(params2.context, params2.url); + } + _onHistoryUpdated(params2) { + this._page.frameManager.frameCommittedSameDocumentNavigation(params2.context, params2.url); + } + _onUserPromptOpened(event) { + this._page.browserContext.dialogManager.dialogDidOpen(new Dialog( + this._page, + event.type, + event.message, + async (accept, userText) => { + await this._session.send("browsingContext.handleUserPrompt", { context: event.context, accept, userText }); + }, + event.defaultValue + )); + } + _onDownloadWillBegin(event) { + if (!event.navigation) + return; + this._page.frameManager.frameAbortedNavigation(event.context, "Download is starting"); + let originPage = this._page.initializedOrUndefined(); + if (!originPage && this._opener) + originPage = this._opener._page.initializedOrUndefined(); + if (!originPage) + return; + this._browserContext._browser.downloadCreated(originPage, event.navigation, event.url, event.suggestedFilename, event.suggestedFilename); + } + _onDownloadEnded(event) { + if (!event.navigation) + return; + this._browserContext._browser.downloadFinished(event.navigation, event.status === "canceled" ? "canceled" : void 0); + } + _onLogEntryAdded(params2) { + if (params2.type === "javascript" && params2.level === "error") { + let errorName = ""; + let errorMessage; + if (params2.text?.includes(": ")) { + const index = params2.text.indexOf(": "); + errorName = params2.text.substring(0, index); + errorMessage = params2.text.substring(index + 2); + } else { + errorMessage = params2.text ?? void 0; + } + const error = new Error(errorMessage); + error.name = errorName; + error.stack = `${params2.text} +${params2.stackTrace?.callFrames.map((f) => { + const location4 = `${f.url}:${f.lineNumber + 1}:${f.columnNumber + 1}`; + return f.functionName ? ` at ${f.functionName} (${location4})` : ` at ${location4}`; + }).join("\n")}`; + const callFrame2 = params2.stackTrace?.callFrames[0]; + const location3 = callFrame2 ?? { url: "", lineNumber: 1, columnNumber: 1 }; + this._page.addPageError(error, location3); + return; + } + if (params2.type !== "console") + return; + const entry = params2; + const context2 = this._contextIdToContext.get(params2.source.realm) ?? this._realmToWorkerContext.get(params2.source.realm); + if (!context2) + return; + const callFrame = params2.stackTrace?.callFrames[0]; + const location2 = callFrame ?? { url: "", lineNumber: 1, columnNumber: 1 }; + const type3 = entry.method === "warn" ? "warning" : entry.method; + const text2 = (entry.method === "timeLog" || entry.method === "timeEnd") && entry.text ? entry.text : void 0; + this._page.addConsoleMessage(null, type3, entry.args.map((arg) => createHandle2(context2, arg)), location2, text2, params2.timestamp); + } + async _onFileDialogOpened(params2) { + if (!params2.element) + return; + const frame = this._page.frameManager.frame(params2.context); + if (!frame) + return; + const executionContext = await frame.mainContext(); + try { + const handle = await toBidiExecutionContext(executionContext).remoteObjectForNodeId(executionContext, { sharedId: params2.element.sharedId }); + await this._page._onFileChooserOpened(handle); + } catch { + } + } + async navigateFrame(frame, url2, referrer) { + const { navigation } = await this._session.send("browsingContext.navigate", { + context: frame._id, + url: url2 + }); + if (navigation && this._fragmentNavigations.has(navigation)) { + this._fragmentNavigations.delete(navigation); + return {}; + } + return { newDocumentId: navigation || void 0 }; + } + async updateExtraHTTPHeaders() { + const allHeaders = mergeHeaders([ + this._browserContext._options.extraHTTPHeaders, + this._page.extraHTTPHeaders() + ]); + await this._session.send("network.setExtraHeaders", { + headers: allHeaders.map(({ name, value: value2 }) => ({ name, value: { type: "string", value: value2 } })), + contexts: [this._session.sessionId] + }); + } + async updateEmulateMedia() { + } + async updateUserAgent() { + } + async bringToFront() { + await this._session.send("browsingContext.activate", { + context: this._session.sessionId + }); + } + async updateEmulatedViewportSize() { + const options = this._browserContext._options; + const emulatedSize = this._page.emulatedSize(); + if (!emulatedSize) + return; + const screenSize = emulatedSize.screen; + const viewportSize = emulatedSize.viewport; + await Promise.all([ + this._session.send("browsingContext.setViewport", { + context: this._session.sessionId, + viewport: { + width: viewportSize.width, + height: viewportSize.height + }, + devicePixelRatio: options.deviceScaleFactor || 1 + }), + this._session.send("emulation.setScreenOrientationOverride", { + contexts: [this._session.sessionId], + screenOrientation: getScreenOrientation(!!options.isMobile, screenSize) + }), + this._session.send("emulation.setScreenSettingsOverride", { + contexts: [this._session.sessionId], + screenArea: { + width: screenSize.width, + height: screenSize.height + } + }) + ]); + } + async updateRequestInterception() { + await this._networkManager.setRequestInterception(this._page.requestInterceptors.length > 0); + } + async updateOffline() { + } + async updateHttpCredentials() { + await this._networkManager.setCredentials(this._browserContext._options.httpCredentials); + } + async updateFileChooserInterception() { + } + async reload() { + await this._session.send("browsingContext.reload", { + context: this._session.sessionId, + // ignoreCache: true, + wait: BrowsingContext.ReadinessState.Interactive + }); + } + async goBack() { + return await this._session.send("browsingContext.traverseHistory", { + context: this._session.sessionId, + delta: -1 + }).then(() => true).catch(() => false); + } + async goForward() { + return await this._session.send("browsingContext.traverseHistory", { + context: this._session.sessionId, + delta: 1 + }).then(() => true).catch(() => false); + } + async requestGC() { + const result2 = await this._session.send("script.evaluate", { + expression: "TestUtils.gc()", + target: { context: this._session.sessionId }, + awaitPromise: true + }); + if (result2.type === "exception") + throw new Error("Method not implemented."); + } + async _onScriptMessage(event) { + if (event.channel !== kPlaywrightBindingChannel) + return; + const pageOrError = await this._page.waitForInitializedOrError(); + if (pageOrError instanceof Error) + return; + const context2 = this._contextIdToContext.get(event.source.realm); + if (!context2) + return; + if (event.data.type !== "string") + return; + await this._page.onBindingCalled(event.data.value, context2); + } + async addInitScript(initScript) { + const { script } = await this._session.send("script.addPreloadScript", { + // TODO: remove function call from the source. + functionDeclaration: `() => { return ${initScript.source} }`, + // TODO: push to iframes? + contexts: [this._session.sessionId] + }); + this._initScriptIds.set(initScript, script); + } + async removeInitScripts(initScripts) { + const ids = []; + for (const script of initScripts) { + const id = this._initScriptIds.get(script); + if (id) + ids.push(id); + this._initScriptIds.delete(script); + } + await Promise.all(ids.map((script) => this._session.send("script.removePreloadScript", { script }))); + } + async closePage(runBeforeUnload) { + if (runBeforeUnload) { + this._session.sendMayFail("browsingContext.close", { + context: this._session.sessionId, + promptUnload: runBeforeUnload + }); + } else { + await this._session.send("browsingContext.close", { + context: this._session.sessionId, + promptUnload: runBeforeUnload + }); + } + } + async setBackgroundColor(color) { + if (color) + throw new Error("Not implemented"); + } + async takeScreenshot(progress2, format2, documentRect, viewportRect, quality, fitsViewport, scale) { + const rect = documentRect || viewportRect; + const { data } = await progress2.race(this._session.send("browsingContext.captureScreenshot", { + context: this._session.sessionId, + format: { + type: `image/${format2 === "png" ? "png" : "jpeg"}`, + quality: quality !== void 0 ? quality / 100 : void 0 + }, + origin: documentRect ? "document" : "viewport", + clip: { + type: "box", + ...rect + } + })); + return Buffer.from(data, "base64"); + } + async getContentFrame(handle) { + const executionContext = toBidiExecutionContext(handle._context); + const frameId = await executionContext.contentFrameIdForFrame(handle); + if (!frameId) + return null; + return this._page.frameManager.frame(frameId); + } + async getOwnerFrame(handle) { + const windowHandle = await handle.evaluateHandle((node) => { + const doc = node.ownerDocument ?? node; + return doc.defaultView; + }); + if (!windowHandle) + return null; + const executionContext = toBidiExecutionContext(handle._context); + return executionContext.frameIdForWindowHandle(windowHandle); + } + async getBoundingBox(handle) { + const box = await handle.evaluate((element2) => { + if (!(element2 instanceof Element) || element2.getClientRects().length === 0) + return null; + const rect = element2.getBoundingClientRect(); + return { x: rect.x, y: rect.y, width: rect.width, height: rect.height }; + }); + if (!box) + return null; + const position = await this._framePosition(handle._frame); + if (!position) + return null; + box.x += position.x; + box.y += position.y; + return box; + } + // TODO: move to Frame. + async _framePosition(frame) { + if (frame === this._page.mainFrame()) + return { x: 0, y: 0 }; + const element2 = await frame.frameElement(nullProgress); + const box = await element2.boundingBox(nullProgress); + if (!box) + return null; + const style = await element2.evaluateInUtility(([injected, iframe]) => injected.describeIFrameStyle(iframe), {}).catch((e) => "error:notconnected"); + if (style === "error:notconnected" || style === "transformed") + return null; + box.x += style.left; + box.y += style.top; + return box; + } + async scrollRectIntoViewIfNeeded(handle, rect) { + return await handle.evaluateInUtility(([injected, node]) => { + node.scrollIntoView({ + block: "center", + inline: "center", + behavior: "instant" + }); + }, null).then(() => "done").catch((e) => { + if (e instanceof Error && e.message.includes("Node is detached from document")) + return "error:notconnected"; + if (e instanceof Error && e.message.includes("Node does not have a layout object")) + return "error:notvisible"; + throw e; + }); + } + startScreencast(options) { + } + stopScreencast() { + } + rafCountForStablePosition() { + return 1; + } + async getContentQuads(handle) { + const quads = await handle.evaluateInUtility(([injected, node]) => { + if (!node.isConnected) + return "error:notconnected"; + const rects = node.getClientRects(); + if (!rects) + return null; + return [...rects].map((rect) => [ + { x: rect.left, y: rect.top }, + { x: rect.right, y: rect.top }, + { x: rect.right, y: rect.bottom }, + { x: rect.left, y: rect.bottom } + ]); + }, null); + if (!quads || quads === "error:notconnected") + return quads; + const position = await this._framePosition(handle._frame); + if (!position) + return null; + quads.forEach((quad) => quad.forEach((point) => { + point.x += position.x; + point.y += position.y; + })); + return quads; + } + async setInputFilePaths(progress2, handle, paths) { + const fromContext = toBidiExecutionContext(handle._context); + await progress2.race(this._session.send("input.setFiles", { + context: this._session.sessionId, + element: await progress2.race(fromContext.nodeIdForElementHandle(handle)), + files: paths + })); + } + async adoptElementHandle(handle, to) { + const fromContext = toBidiExecutionContext(handle._context); + const nodeId = await fromContext.nodeIdForElementHandle(handle); + const executionContext = toBidiExecutionContext(to); + try { + return await executionContext.remoteObjectForNodeId(to, nodeId); + } catch { + throw new Error(kUnableToAdoptErrorMessage); + } + } + async inputActionEpilogue() { + } + async resetForReuse(progress2) { + } + async pdf(options) { + return this._pdf.generate(options); + } + async getFrameElement(frame) { + const parent = frame.parentFrame(); + if (!parent) + throw new Error("Frame has been detached."); + const node = await this._getFrameNode(frame); + if (!node?.sharedId) + throw new Error("Frame has been detached."); + const parentFrameExecutionContext = await parent.mainContext(); + return await toBidiExecutionContext(parentFrameExecutionContext).remoteObjectForNodeId(parentFrameExecutionContext, { sharedId: node.sharedId }); + } + async _getFrameNode(frame) { + const parent = frame.parentFrame(); + if (!parent) + return void 0; + const result2 = await this._session.send("browsingContext.locateNodes", { + context: parent._id, + locator: { type: "context", value: { context: frame._id } } + }); + const node = result2.nodes[0]; + return node; + } + shouldToggleStyleSheetToSyncAnimations() { + return true; + } + async setDockTile(image) { + } + }; + } +}); + +// packages/playwright-core/src/server/bidi/bidiBrowser.ts +function fromBidiSameSite(sameSite) { + switch (sameSite) { + case "strict": + return "Strict"; + case "lax": + return "Lax"; + case "none": + return "None"; + case "default": + return "Lax"; + } + return "None"; +} +function toBidiSameSite2(sameSite) { + switch (sameSite) { + case "Strict": + return Network.SameSite.Strict; + case "Lax": + return Network.SameSite.Lax; + case "None": + return Network.SameSite.None; + } + return Network.SameSite.None; +} +function getProxyConfiguration(proxySettings) { + if (!proxySettings) + return void 0; + const proxy = { + proxyType: "manual" + }; + const url2 = new URL(proxySettings.server); + switch (url2.protocol) { + case "http:": + proxy.httpProxy = url2.host; + break; + case "https:": + proxy.sslProxy = url2.host; + break; + case "socks4:": + proxy.socksProxy = url2.host; + proxy.socksVersion = 4; + break; + case "socks5:": + proxy.socksProxy = url2.host; + proxy.socksVersion = 5; + break; + default: + throw new Error("Invalid proxy server protocol: " + proxySettings.server); + } + const bypass = proxySettings.bypass ?? process.env.PLAYWRIGHT_PROXY_BYPASS_FOR_TESTING; + if (bypass) + proxy.noProxy = bypass.split(","); + return proxy; +} +function getScreenOrientation(isMobile, viewportSize) { + const screenOrientation = { + type: "landscape-primary", + natural: Emulation.ScreenOrientationNatural.Landscape + }; + if (isMobile) { + screenOrientation.natural = Emulation.ScreenOrientationNatural.Portrait; + if (viewportSize.width <= viewportSize.height) + screenOrientation.type = "portrait-primary"; + } + return screenOrientation; +} +var BidiBrowser, BidiBrowserContext2, Network2; +var init_bidiBrowser = __esm({ + "packages/playwright-core/src/server/bidi/bidiBrowser.ts"() { + "use strict"; + init_eventsHelper(); + init_browser(); + init_browserContext(); + init_network2(); + init_bidiConnection(); + init_bidiNetworkManager(); + init_bidiPage(); + init_page(); + init_bidiProtocol(); + BidiBrowser = class _BidiBrowser extends Browser { + constructor(parent, transport, options) { + super(parent, options); + this._contexts = /* @__PURE__ */ new Map(); + this._bidiPages = /* @__PURE__ */ new Map(); + this._cacheBehavior = "default"; + this._connection = new BidiConnection(transport, this._onDisconnect.bind(this), options.protocolLogger, options.browserLogsCollector); + this._browserSession = this._connection.browserSession; + this._eventListeners = [ + eventsHelper.addEventListener(this._browserSession, "browsingContext.contextCreated", this._onBrowsingContextCreated.bind(this)), + eventsHelper.addEventListener(this._browserSession, "script.realmDestroyed", this._onScriptRealmDestroyed.bind(this)) + ]; + } + static async connect(parent, transport, options) { + const browser = new _BidiBrowser(parent, transport, options); + if (options.__testHookOnConnectToBrowser) + await options.__testHookOnConnectToBrowser(); + browser._bidiSessionInfo = await browser._browserSession.send("session.new", { + capabilities: { + alwaysMatch: { + "acceptInsecureCerts": options.persistent?.internalIgnoreHTTPSErrors || options.persistent?.ignoreHTTPSErrors, + "proxy": getProxyConfiguration(options.originalLaunchOptions.proxyOverride ?? options.proxy), + "unhandledPromptBehavior": { + default: Session.UserPromptHandlerType.Ignore + }, + "webSocketUrl": true, + // Chrome with WebDriver BiDi does not support prerendering + // yet because WebDriver BiDi behavior is not specified. See + // https://github.com/w3c/webdriver-bidi/issues/321. + "goog:prerenderingDisabled": true + } + } + }); + await browser._browserSession.send("session.subscribe", { + events: [ + "browsingContext", + "network", + "log", + "script", + "input" + ] + }); + await browser._browserSession.send("network.addIntercept", { phases: [Network.InterceptPhase.AuthRequired] }); + await browser._browserSession.send("network.addDataCollector", { + dataTypes: [Network.DataType.Response], + maxEncodedDataSize: 2e7 + // same default as in CDP: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/inspector/inspector_network_agent.cc;l=134;drc=4128411589187a396829a827f59a655bed876aa7 + }); + if (options.persistent) { + const context2 = new BidiBrowserContext2(browser, void 0, options.persistent); + browser._defaultContext = context2; + await context2.initialize(); + const page = await browser._defaultContext.doCreateNewPage(); + await page.waitForInitializedOrError(); + } + return browser; + } + _onDisconnect() { + this.didClose(); + } + async doCreateNewContext(options) { + const proxy = options.proxyOverride || options.proxy; + const { userContext } = await this._browserSession.send("browser.createUserContext", { + acceptInsecureCerts: options.internalIgnoreHTTPSErrors || options.ignoreHTTPSErrors, + proxy: getProxyConfiguration(proxy) + }); + const context2 = new BidiBrowserContext2(this, userContext, options); + await context2.initialize(); + this._contexts.set(userContext, context2); + return context2; + } + contexts() { + return Array.from(this._contexts.values()); + } + version() { + return this._bidiSessionInfo.capabilities.browserVersion; + } + userAgent() { + return this._bidiSessionInfo.capabilities.userAgent; + } + isConnected() { + return !this._connection.isClosed(); + } + async updateCacheBehavior() { + const cacheBehavior = [...this._contexts.values()].some((context2) => context2.requestInterceptors.length > 0) ? "bypass" : "default"; + if (this._cacheBehavior !== cacheBehavior) { + await this._browserSession.send("network.setCacheBehavior", { cacheBehavior }); + this._cacheBehavior = cacheBehavior; + } + } + _onBrowsingContextCreated(event) { + if (event.parent) { + const parentFrameId = event.parent; + const page2 = this._findPageForFrame(parentFrameId); + if (page2) { + page2._session.addFrameBrowsingContext(event.context); + const frame = page2._page.frameManager.frameAttached(event.context, parentFrameId); + frame._url = event.url; + page2._getFrameNode(frame).then((node) => { + const attributes = node?.value?.attributes; + frame._name = attributes?.name ?? attributes?.id ?? ""; + }, () => { + }); + return; + } + return; + } + let context2 = this._contexts.get(event.userContext); + if (!context2) + context2 = this._defaultContext; + if (!context2) + return; + context2.doGrantGlobalPermissionsForURL(event.url); + const session2 = this._connection.createMainFrameBrowsingContextSession(event.context); + const opener = event.originalOpener && this._findPageForFrame(event.originalOpener); + const page = new BidiPage(context2, session2, opener || null); + page._page.mainFrame()._url = event.url; + this._bidiPages.set(event.context, page); + } + _onBrowsingContextDestroyed(event) { + if (event.parent) { + this._browserSession.removeFrameBrowsingContext(event.context); + const parentFrameId = event.parent; + for (const page of this._bidiPages.values()) { + const parentFrame = page._page.frameManager.frame(parentFrameId); + if (!parentFrame) + continue; + page._page.frameManager.frameDetached(event.context); + return; + } + return; + } + const bidiPage = this._bidiPages.get(event.context); + if (!bidiPage) + return; + bidiPage.didClose(); + this._bidiPages.delete(event.context); + } + _onScriptRealmDestroyed(event) { + for (const page of this._bidiPages.values()) { + if (page._onRealmDestroyed(event)) + return; + } + } + _findPageForFrame(frameId) { + for (const page of this._bidiPages.values()) { + if (page._page.frameManager.frame(frameId)) + return page; + } + } + }; + BidiBrowserContext2 = class extends BrowserContext { + constructor(browser, browserContextId, options) { + super(browser, options, browserContextId); + this._originToPermissions = /* @__PURE__ */ new Map(); + this._initScriptIds = /* @__PURE__ */ new Map(); + this.authenticateProxyViaHeader(); + } + _bidiPages() { + return [...this._browser._bidiPages.values()].filter((bidiPage) => bidiPage._browserContext === this); + } + async initialize() { + const promises = [ + super.initialize() + ]; + const downloadBehavior = this._options.acceptDownloads === "accept" ? { type: "allowed", destinationFolder: this._browser.options.downloadsPath } : { type: "denied" }; + promises.push(this._browser._browserSession.send("browser.setDownloadBehavior", { + downloadBehavior, + userContexts: [this._userContextId()] + })); + promises.push(this.doUpdateDefaultViewport()); + if (this._options.geolocation) + promises.push(this.setGeolocation(this._options.geolocation)); + if (this._options.locale) { + promises.push(this._browser._browserSession.send("emulation.setLocaleOverride", { + locale: this._options.locale, + userContexts: [this._userContextId()] + })); + } + if (this._options.timezoneId) { + promises.push(this._browser._browserSession.send("emulation.setTimezoneOverride", { + timezone: this._options.timezoneId, + userContexts: [this._userContextId()] + })); + } + if (this._options.userAgent) { + promises.push(this._browser._browserSession.send("emulation.setUserAgentOverride", { + userAgent: this._options.userAgent, + userContexts: [this._userContextId()] + })); + } + if (this._options.extraHTTPHeaders) + promises.push(this.doUpdateExtraHTTPHeaders()); + if (this._options.permissions) + promises.push(this.doGrantPermissions("*", this._options.permissions)); + if (this._options.offline) + promises.push(this.doUpdateOffline()); + await Promise.all(promises); + } + possiblyUninitializedPages() { + return this._bidiPages().map((bidiPage) => bidiPage._page); + } + async doCreateNewPage() { + const { context: context2 } = await this._browser._browserSession.send("browsingContext.create", { + type: BrowsingContext.CreateType.Window, + userContext: this._browserContextId + }); + const page = this._browser._bidiPages.get(context2)._page; + page.frameManager.frameLifecycleEvent(context2, "domcontentloaded"); + page.frameManager.frameLifecycleEvent(context2, "load"); + return page; + } + async doGetCookies(urls) { + const { cookies } = await this._browser._browserSession.send( + "storage.getCookies", + { partition: { type: "storageKey", userContext: this._browserContextId } } + ); + return filterCookies(cookies.map((c) => { + const copy = { + name: c.name, + value: bidiBytesValueToString(c.value), + domain: c.domain, + path: c.path, + httpOnly: c.httpOnly, + secure: c.secure, + expires: c.expiry ?? -1, + sameSite: c.sameSite ? fromBidiSameSite(c.sameSite) : "None" + }; + return copy; + }), urls); + } + async addCookies(cookies) { + cookies = rewriteCookies(cookies); + const promises = cookies.map(async (c) => { + const cookie = { + name: c.name, + value: { type: "string", value: c.value }, + domain: c.domain, + path: c.path, + httpOnly: c.httpOnly, + secure: c.secure, + sameSite: c.sameSite && toBidiSameSite2(c.sameSite), + expiry: c.expires === -1 || c.expires === void 0 ? void 0 : Math.round(c.expires) + }; + try { + return await this._browser._browserSession.send( + "storage.setCookie", + { cookie, partition: { type: "storageKey", userContext: this._browserContextId, sourceOrigin: c.partitionKey } } + ); + } catch (e) { + if (!e.message.startsWith("Protocol error (storage.setCookie): unable to set cookie")) + throw e; + } + }); + await Promise.all(promises); + } + async doClearCookies() { + await this._browser._browserSession.send( + "storage.deleteCookies", + { partition: { type: "storageKey", userContext: this._browserContextId } } + ); + } + async doGrantPermissions(origin, permissions) { + if (origin === "null") + return; + const currentPermissions = this._originToPermissions.get(origin) || []; + const toGrant = permissions.filter((permission) => !currentPermissions.includes(permission)); + this._originToPermissions.set(origin, [...currentPermissions, ...toGrant]); + if (origin === "*") { + await Promise.all(this._bidiPages().flatMap( + (page) => page._page.frames().map( + (frame) => this.doGrantPermissions(new URL(frame._url).origin, permissions) + ) + )); + } else { + await Promise.all(toGrant.map((permission) => this._setPermission(origin, permission, Permissions.PermissionState.Granted))); + } + } + async doGrantGlobalPermissionsForURL(url2) { + const permissions = this._originToPermissions.get("*"); + if (!permissions) + return; + await this.doGrantPermissions(new URL(url2).origin, permissions); + } + async doClearPermissions() { + const currentPermissions = [...this._originToPermissions.entries()]; + this._originToPermissions = /* @__PURE__ */ new Map(); + await Promise.all(currentPermissions.flatMap(([origin, permissions]) => { + if (origin !== "*") + return permissions.map((p) => this._setPermission(origin, p, Permissions.PermissionState.Prompt)); + })); + } + async _setPermission(origin, permission, state) { + await this._browser._browserSession.send("permissions.setPermission", { + descriptor: { + name: permission + }, + state, + origin, + userContext: this._userContextId() + }); + } + async setGeolocation(geolocation) { + verifyGeolocation(geolocation); + this._options.geolocation = geolocation; + await this._browser._browserSession.send("emulation.setGeolocationOverride", { + coordinates: geolocation ? { + latitude: geolocation.latitude, + longitude: geolocation.longitude, + accuracy: geolocation.accuracy + } : null, + userContexts: [this._userContextId()] + }); + } + async doUpdateExtraHTTPHeaders() { + const allHeaders = this._options.extraHTTPHeaders || []; + await this._browser._browserSession.send("network.setExtraHeaders", { + headers: allHeaders.map(({ name, value: value2 }) => ({ name, value: { type: "string", value: value2 } })), + userContexts: [this._userContextId()] + }); + } + async setUserAgent(userAgent) { + this._options.userAgent = userAgent; + await this._browser._browserSession.send("emulation.setUserAgentOverride", { + userAgent: userAgent ?? null, + userContexts: [this._userContextId()] + }); + } + async doUpdateOffline() { + await this._browser._browserSession.send("emulation.setNetworkConditions", { + networkConditions: this._options.offline ? { type: "offline" } : null, + userContexts: [this._userContextId()] + }); + } + async doSetHTTPCredentials(httpCredentials) { + this._options.httpCredentials = httpCredentials; + for (const page of this.pages()) + await page.delegate.updateHttpCredentials(); + } + async doAddInitScript(initScript) { + const { script } = await this._browser._browserSession.send("script.addPreloadScript", { + // TODO: remove function call from the source. + functionDeclaration: `() => { return ${initScript.source} }`, + userContexts: [this._userContextId()] + }); + this._initScriptIds.set(initScript, script); + } + async doRemoveInitScripts(initScripts) { + const ids = []; + for (const script of initScripts) { + const id = this._initScriptIds.get(script); + if (id) + ids.push(id); + this._initScriptIds.delete(script); + } + await Promise.all(ids.map((script) => this._browser._browserSession.send("script.removePreloadScript", { script }))); + } + async doUpdateRequestInterception() { + let interceptPromise = Promise.resolve(void 0); + if (this.requestInterceptors.length > 0 && !this._interceptId) { + interceptPromise = this._browser._browserSession.send("network.addIntercept", { + phases: [Network.InterceptPhase.BeforeRequestSent] + }).then(({ intercept }) => this._interceptId = intercept); + } + if (this.requestInterceptors.length === 0 && this._interceptId) { + const intercept = this._interceptId; + this._interceptId = void 0; + interceptPromise = this._browser._browserSession.send("network.removeIntercept", { intercept }); + } + await Promise.all([this._browser.updateCacheBehavior(), interceptPromise]); + } + async doUpdateDefaultViewport() { + if (!this._options.viewport && !this._options.screen) + return; + const screenSize = this._options.screen || this._options.viewport; + const viewportSize = this._options.viewport || this._options.screen; + await Promise.all([ + this._browser._browserSession.send("browsingContext.setViewport", { + viewport: { + width: viewportSize.width, + height: viewportSize.height + }, + devicePixelRatio: this._options.deviceScaleFactor || 1, + userContexts: [this._userContextId()] + }), + this._browser._browserSession.send("emulation.setScreenOrientationOverride", { + screenOrientation: getScreenOrientation(!!this._options.isMobile, screenSize), + userContexts: [this._userContextId()] + }), + this._browser._browserSession.send("emulation.setScreenSettingsOverride", { + screenArea: { + width: screenSize.width, + height: screenSize.height + }, + userContexts: [this._userContextId()] + }) + ]); + } + async doUpdateDefaultEmulatedMedia() { + } + async doExposePlaywrightBinding() { + const args = [{ + type: "channel", + value: { + channel: kPlaywrightBindingChannel, + ownership: Script.ResultOwnership.Root + } + }]; + const functionDeclaration = `function addMainBinding(callback) { globalThis['${PageBinding.kBindingName}'] = callback; }`; + const promises = []; + promises.push(this._browser._browserSession.send("script.addPreloadScript", { + functionDeclaration, + arguments: args, + userContexts: [this._userContextId()] + })); + promises.push(...this._bidiPages().map((page) => { + const realms = [...page._contextIdToContext].filter(([realm, context2]) => context2.world === "main").map(([realm, context2]) => realm); + return Promise.all(realms.map((realm) => { + return page._session.send("script.callFunction", { + functionDeclaration, + arguments: args, + target: { realm }, + awaitPromise: false, + userActivation: false + }); + })); + })); + await Promise.all(promises); + } + onClosePersistent() { + } + async clearCache() { + } + async doClose(reason) { + if (!this._browserContextId) { + return "close-browser"; + } + await this._browser._browserSession.send("browser.removeUserContext", { + userContext: this._browserContextId + }); + await Promise.all(this._bidiPages().map((bidiPage) => bidiPage._page.closedPromise)); + this._browser._contexts.delete(this._browserContextId); + } + async cancelDownload(uuid) { + } + _userContextId() { + if (this._browserContextId) + return this._browserContextId; + return "default"; + } + }; + ((Network3) => { + let SameSite; + ((SameSite2) => { + SameSite2["Strict"] = "strict"; + SameSite2["Lax"] = "lax"; + SameSite2["None"] = "none"; + })(SameSite = Network3.SameSite || (Network3.SameSite = {})); + })(Network2 || (Network2 = {})); + } +}); + +// packages/playwright-core/src/server/chromium/crDevTools.ts +var import_fs26, kBindingName2, CRDevTools; +var init_crDevTools = __esm({ + "packages/playwright-core/src/server/chromium/crDevTools.ts"() { + "use strict"; + import_fs26 = __toESM(require("fs")); + kBindingName2 = "__pw_devtools__"; + CRDevTools = class { + constructor(preferencesPath) { + this._preferencesPath = preferencesPath; + this._savePromise = Promise.resolve(); + } + install(session2) { + session2.on("Runtime.bindingCalled", async (event) => { + if (event.name !== kBindingName2) + return; + const parsed = JSON.parse(event.payload); + let result2 = void 0; + if (parsed.method === "getPreferences") { + if (this._prefs === void 0) { + try { + const json = await import_fs26.default.promises.readFile(this._preferencesPath, "utf8"); + this._prefs = JSON.parse(json); + } catch (e) { + this._prefs = {}; + } + } + result2 = this._prefs; + } else if (parsed.method === "setPreference") { + this._prefs[parsed.params[0]] = parsed.params[1]; + this._save(); + } else if (parsed.method === "removePreference") { + delete this._prefs[parsed.params[0]]; + this._save(); + } else if (parsed.method === "clearPreferences") { + this._prefs = {}; + this._save(); + } + session2.send("Runtime.evaluate", { + expression: `window.DevToolsAPI.embedderMessageAck(${parsed.id}, ${JSON.stringify(result2)})`, + contextId: event.executionContextId + }).catch((e) => null); + }); + Promise.all([ + session2.send("Runtime.enable"), + session2.send("Runtime.addBinding", { name: kBindingName2 }), + session2.send("Page.enable"), + session2.send("Page.addScriptToEvaluateOnNewDocument", { source: ` + (() => { + const init = () => { + // Lazy init happens when InspectorFrontendHost is initialized. + // At this point DevToolsHost is ready to be used. + const host = window.DevToolsHost; + const old = host.sendMessageToEmbedder.bind(host); + host.sendMessageToEmbedder = message => { + if (['getPreferences', 'setPreference', 'removePreference', 'clearPreferences'].includes(JSON.parse(message).method)) + window.${kBindingName2}(message); + else + old(message); + }; + }; + let value; + Object.defineProperty(window, 'InspectorFrontendHost', { + configurable: true, + enumerable: true, + get() { return value; }, + set(v) { value = v; init(); }, + }); + })() + ` }), + session2.send("Runtime.runIfWaitingForDebugger") + ]).catch((e) => null); + } + _save() { + this._savePromise = this._savePromise.then(async () => { + await import_fs26.default.promises.writeFile(this._preferencesPath, JSON.stringify(this._prefs)).catch((e) => null); + }); + } + }; + } +}); + +// packages/playwright-core/src/server/chromium/chromium.ts +function profileInUseError(logs) { + const markers = [ + "Failed to create a ProcessSingleton for your profile directory.", + "Opening in existing browser session." + ]; + for (const log2 of logs) { + const marker = markers.find((m) => log2.includes(m)); + if (marker) { + return new Error( + `${marker} This usually means that the profile is already in use by another instance of Chromium.` + ); + } + } +} +async function waitForReadyState(options, browserLogsCollector) { + if (!options.args?.some((a) => a.startsWith("--remote-debugging-port"))) + return {}; + const result2 = new ManualPromise(); + browserLogsCollector.onMessage((message) => { + const error = profileInUseError([message]); + if (error) + result2.reject(error); + const match = message.match(/DevTools listening on (.*)/); + if (match) + result2.resolve({ wsEndpoint: match[1] }); + }); + return result2; +} +async function urlToWSEndpoint(progress2, endpointURL, headers) { + if (endpointURL.startsWith("ws")) + return endpointURL; + progress2.log(` retrieving websocket url from ${endpointURL}`); + const url2 = new URL(endpointURL); + if (!url2.pathname.endsWith("/")) + url2.pathname += "/"; + url2.pathname += "json/version/"; + const httpURL = url2.toString(); + const json = await fetchData( + progress2, + { + url: httpURL, + headers + }, + async (_, resp) => new Error(`Unexpected status ${resp.statusCode} when connecting to ${httpURL}. +This does not look like a DevTools server, try connecting via ws://.`) + ); + return JSON.parse(json).webSocketDebuggerUrl; +} +async function resolveChannelEndpoint(progress2, channel) { + const userDataDir = defaultUserDataDirForChannel(channel); + if (!userDataDir) + throw new Error(`Connecting to ${channel} by channel name is not supported on ${process.platform}.`); + const devToolsActivePortPath = import_path25.default.join(userDataDir, "DevToolsActivePort"); + progress2.log(` reading ${devToolsActivePortPath}`); + const contents = await progress2.race(import_fs27.default.promises.readFile(devToolsActivePortPath, "utf-8").catch(() => void 0)); + if (!contents) { + throw new Error( + `Could not connect to ${channel}: DevToolsActivePort file not found at ${devToolsActivePortPath}. +` + remoteDebuggingHint(channel) + ); + } + const port = parseInt(contents.trim(), 10); + if (isNaN(port)) + throw new Error(`Could not connect to ${channel}: invalid DevToolsActivePort file at ${devToolsActivePortPath}.`); + const endpoint = `ws://localhost:${port}/devtools/browser`; + progress2.log(` resolved channel "${channel}" to ${endpoint}`); + return endpoint; +} +async function seleniumErrorHandler(params2, response2) { + const body = await streamToString(response2); + let message = body; + try { + const json = JSON.parse(body); + message = json.value.localizedMessage || json.value.message; + } catch (e) { + } + return new Error(`Error connecting to Selenium at ${params2.url}: ${message}`); +} +function addProtocol(url2) { + if (!["ws://", "wss://", "http://", "https://"].some((protocol) => url2.startsWith(protocol))) + return "http://" + url2; + return url2; +} +function streamToString(stream3) { + return new Promise((resolve, reject) => { + const chunks = []; + stream3.on("data", (chunk) => chunks.push(Buffer.from(chunk))); + stream3.on("error", reject); + stream3.on("end", () => resolve(Buffer.concat(chunks).toString("utf8"))); + }); +} +function parseSeleniumRemoteParams(env, progress2) { + try { + const parsed = JSON.parse(env.value); + progress2.log(` using additional ${env.name} "${env.value}"`); + return parsed; + } catch (e) { + progress2.log(` ignoring additional ${env.name} "${env.value}": ${e}`); + } +} +function remoteDebuggingHint(channel) { + return `Make sure ${channel} is running with remote debugging enabled. Navigate to + + chrome://inspect/#remote-debugging + +and check "Allow remote debugging for this browser instance". +`; +} +var import_fs27, import_os11, import_path25, ARTIFACTS_FOLDER2, Chromium; +var init_chromium = __esm({ + "packages/playwright-core/src/server/chromium/chromium.ts"() { + "use strict"; + import_fs27 = __toESM(require("fs")); + import_os11 = __toESM(require("os")); + import_path25 = __toESM(require("path")); + init_manualPromise(); + init_ascii(); + init_chromiumChannels(); + init_debugLogger(); + init_fileUtils(); + init_processLauncher(); + init_debug(); + init_headers(); + init_utils2(); + init_userAgent(); + init_chromiumSwitches(); + init_crBrowser(); + init_crConnection(); + init_browserContext(); + init_browserType(); + init_helper(); + init_registry(); + init_transport(); + init_crDevTools(); + init_browser(); + init_page(); + init_crExecutionContext(); + init_console(); + init_crProtocolHelper(); + ARTIFACTS_FOLDER2 = import_path25.default.join(import_os11.default.tmpdir(), "playwright-artifacts-"); + Chromium = class extends BrowserType { + constructor(parent, bidiChromium) { + super(parent, "chromium"); + this._bidiChromium = bidiChromium; + if (debugMode() === "inspector") + this._devtools = this._createDevTools(); + } + launch(progress2, options, protocolLogger) { + if (options.channel?.startsWith("bidi-")) + return this._bidiChromium.launch(progress2, options, protocolLogger); + return super.launch(progress2, options, protocolLogger); + } + async launchPersistentContext(progress2, userDataDir, options) { + if (options.channel?.startsWith("bidi-")) + return this._bidiChromium.launchPersistentContext(progress2, userDataDir, options); + return super.launchPersistentContext(progress2, userDataDir, options); + } + async connectOverCDP(progress2, params2) { + if (params2.transport) + return this._connectOverCDPImpl(progress2, params2.transport, async () => params2.transport.close(), { ...params2, isLocal: true }); + return await this._connectOverCDPInternal(progress2, params2.endpointURL, params2); + } + async _connectOverCDPInternal(progress2, endpointURL, options, onClose) { + let headersMap; + if (options.headers) + headersMap = headersArrayToObject(options.headers, false); + if (!headersMap) + headersMap = { "User-Agent": getUserAgent() }; + else if (headersMap && !Object.keys(headersMap).some((key) => key.toLowerCase() === "user-agent")) + headersMap["User-Agent"] = getUserAgent(); + const channel = isChromiumChannelName(endpointURL) ? endpointURL : void 0; + if (channel) + endpointURL = await resolveChannelEndpoint(progress2, endpointURL); + let wsEndpoint; + let chromeTransport; + try { + wsEndpoint = await urlToWSEndpoint(progress2, endpointURL, headersMap); + chromeTransport = await WebSocketTransport.connect(progress2, wsEndpoint, { headers: headersMap, followRedirects: true, debugLogHeader: "x-playwright-debug-log" }); + } catch (e) { + if (channel) + throw new Error(`Could not connect to ${channel}. +${remoteDebuggingHint(channel)}`); + throw e; + } + const closeAndWait = async () => await chromeTransport.closeAndWait(); + return this._connectOverCDPImpl(progress2, chromeTransport, closeAndWait, options, onClose); + } + async _connectOverCDPImpl(progress2, transport, closeAndWait, options, onClose) { + let artifactsDir; + const tempDirectories = []; + if (options.artifactsDir) { + artifactsDir = options.artifactsDir; + } else { + artifactsDir = await progress2.race(import_fs27.default.promises.mkdtemp(ARTIFACTS_FOLDER2)); + tempDirectories.push(artifactsDir); + } + const doCleanup = async () => { + await removeFolders(tempDirectories); + const cb = onClose; + onClose = void 0; + await cb?.(); + }; + const doClose = async () => { + await closeAndWait(); + await doCleanup(); + }; + try { + const browserProcess = { close: doClose, kill: doClose }; + const persistent = { + noDefaultViewport: true, + ...options.noDefaults ? { acceptDownloads: "internal-browser-default" } : {} + }; + const browserOptions = { + slowMo: options.slowMo, + name: "chromium", + browserType: "chromium", + persistent, + browserProcess, + protocolLogger: helper.debugProtocolLogger(), + browserLogsCollector: new RecentLogsCollector(), + artifactsDir, + downloadsPath: options.downloadsPath || artifactsDir, + tracesDir: options.tracesDir || artifactsDir, + originalLaunchOptions: {}, + noDefaults: options.noDefaults + }; + validateBrowserContextOptions(persistent, browserOptions); + const browser = await progress2.race(CRBrowser.connect(this.attribution.playwright, transport, browserOptions)); + if (!options.isLocal) + browser._isBrowserCollocatedWithServer = false; + browser.on(Browser.Events.Disconnected, doCleanup); + return browser; + } catch (error) { + await progress2.race(doClose().catch(() => { + })); + throw error; + } + } + async connectToWorker(progress2, endpoint) { + const wsEndpoint = await urlToWSEndpoint(progress2, endpoint, {}); + const transport = await WebSocketTransport.connect(progress2, wsEndpoint); + try { + const connection = new CRConnection(this, transport, helper.debugProtocolLogger(), new RecentLogsCollector()); + const session2 = connection.rootSession; + const worker = new Worker(this, "", () => transport.closeAndWait()); + session2.on("Runtime.executionContextCreated", (event) => { + const isDefault = event.context.auxData?.isDefault; + if (isDefault === false) { + return; + } + worker.workerScriptLoaded(); + worker.createExecutionContext(new CRExecutionContext(session2, event.context)); + }); + session2.on("Runtime.executionContextDestroyed", () => worker.destroyExecutionContext("Execution context was destroyed")); + session2.on("Runtime.consoleAPICalled", (event) => { + if (!worker.existingExecutionContext) + return; + const args = event.args.map((o) => createHandle(worker.existingExecutionContext, o)); + const message = new ConsoleMessage(null, worker, event.type, void 0, args, stackTraceToLocation(event.stackTrace), event.timestamp); + worker.emit(Worker.Events.Console, message); + }); + connection.on(ConnectionEvents.Disconnected, () => worker.didClose()); + session2._sendMayFail("Runtime.enable"); + session2._sendMayFail("Runtime.runIfWaitingForDebugger"); + return worker; + } catch (error) { + await progress2.race(transport.closeAndWait().catch(() => { + })); + throw error; + } + } + _createDevTools() { + const directory = registry.findExecutable("chromium").directory; + return directory ? new CRDevTools(import_path25.default.join(directory, "devtools-preferences.json")) : void 0; + } + async connectToTransport(transport, options, browserLogsCollector) { + try { + return await CRBrowser.connect(this.attribution.playwright, transport, options, this._devtools); + } catch (e) { + const error = profileInUseError(browserLogsCollector.recentLogs()); + if (error) + throw error; + throw e; + } + } + doRewriteStartupLog(logs) { + if (logs.includes("Missing X server")) + logs = "\n" + wrapInASCIIBox(kNoXServerRunningError, 1); + if (!logs.includes("crbug.com/357670") && !logs.includes("No usable sandbox!") && !logs.includes("crbug.com/638180")) + return logs; + return [ + `Chromium sandboxing failed!`, + `================================`, + `To avoid the sandboxing issue, do either of the following:`, + ` - (preferred): Configure your environment to support sandboxing`, + ` - (alternative): Launch Chromium without sandbox using 'chromiumSandbox: false' option`, + `================================`, + `` + ].join("\n"); + } + amendEnvironment(env) { + return env; + } + attemptToGracefullyCloseBrowser(transport) { + const message = { method: "Browser.close", id: kBrowserCloseMessageId, params: {} }; + transport.send(message); + } + async launchWithSeleniumHub(progress2, hubUrl, options) { + if (!hubUrl.endsWith("/")) + hubUrl = hubUrl + "/"; + const args = this._innerDefaultArgs(options); + args.push("--remote-debugging-port=0"); + const isEdge = options.channel && options.channel.startsWith("msedge"); + let desiredCapabilities = { + "browserName": isEdge ? "MicrosoftEdge" : "chrome", + [isEdge ? "ms:edgeOptions" : "goog:chromeOptions"]: { args } + }; + if (process.env.SELENIUM_REMOTE_CAPABILITIES) { + const remoteCapabilities = parseSeleniumRemoteParams({ name: "capabilities", value: process.env.SELENIUM_REMOTE_CAPABILITIES }, progress2); + if (remoteCapabilities) + desiredCapabilities = { ...desiredCapabilities, ...remoteCapabilities }; + } + let headers = {}; + if (process.env.SELENIUM_REMOTE_HEADERS) { + const remoteHeaders = parseSeleniumRemoteParams({ name: "headers", value: process.env.SELENIUM_REMOTE_HEADERS }, progress2); + if (remoteHeaders) + headers = remoteHeaders; + } + progress2.log(` connecting to ${hubUrl}`); + const response2 = await fetchData(progress2, { + url: hubUrl + "session", + method: "POST", + headers: { + "Content-Type": "application/json; charset=utf-8", + ...headers + }, + data: JSON.stringify({ + capabilities: { alwaysMatch: desiredCapabilities } + }) + }, seleniumErrorHandler); + const value2 = JSON.parse(response2).value; + const sessionId = value2.sessionId; + progress2.log(` connected to sessionId=${sessionId}`); + const disconnectFromSelenium = async () => { + progress2.log(` disconnecting from sessionId=${sessionId}`); + await fetchData(void 0, { + url: hubUrl + "session/" + sessionId, + method: "DELETE", + headers + }).catch((error) => progress2.log(`: ${error}`)); + progress2.log(` disconnected from sessionId=${sessionId}`); + gracefullyCloseSet.delete(disconnectFromSelenium); + }; + gracefullyCloseSet.add(disconnectFromSelenium); + try { + const capabilities = value2.capabilities; + let endpointURL; + if (capabilities["se:cdp"]) { + progress2.log(` using selenium v4`); + const endpointURLString = addProtocol(capabilities["se:cdp"]); + endpointURL = new URL(endpointURLString); + if (endpointURL.hostname === "localhost" || endpointURL.hostname === "127.0.0.1") + endpointURL.hostname = new URL(hubUrl).hostname; + progress2.log(` retrieved endpoint ${endpointURL.toString()} for sessionId=${sessionId}`); + } else { + progress2.log(` using selenium v3`); + const maybeChromeOptions = capabilities["goog:chromeOptions"]; + const chromeOptions = maybeChromeOptions && typeof maybeChromeOptions === "object" ? maybeChromeOptions : void 0; + const debuggerAddress = chromeOptions && typeof chromeOptions.debuggerAddress === "string" ? chromeOptions.debuggerAddress : void 0; + const chromeOptionsURL = typeof maybeChromeOptions === "string" ? maybeChromeOptions : void 0; + const endpointURLString = addProtocol(debuggerAddress || chromeOptionsURL).replace("localhost", "127.0.0.1"); + progress2.log(` retrieved endpoint ${endpointURLString} for sessionId=${sessionId}`); + endpointURL = new URL(endpointURLString); + if (endpointURL.hostname === "localhost" || endpointURL.hostname === "127.0.0.1") { + const sessionInfoUrl = new URL(hubUrl).origin + "/grid/api/testsession?session=" + sessionId; + try { + const sessionResponse = await fetchData(progress2, { + url: sessionInfoUrl, + method: "GET", + headers + }, seleniumErrorHandler); + const proxyId = JSON.parse(sessionResponse).proxyId; + endpointURL.hostname = new URL(proxyId).hostname; + progress2.log(` resolved endpoint ip ${endpointURL.toString()} for sessionId=${sessionId}`); + } catch (e) { + progress2.log(` unable to resolve endpoint ip for sessionId=${sessionId}, running in standalone?`); + } + } + } + return await this._connectOverCDPInternal(progress2, endpointURL.toString(), { + ...options, + headers: headersObjectToArray(headers) + }, disconnectFromSelenium); + } catch (e) { + await progress2.race(disconnectFromSelenium()); + throw e; + } + } + async defaultArgs(options, isPersistent, userDataDir) { + const chromeArguments = this._innerDefaultArgs(options); + chromeArguments.push(`--user-data-dir=${userDataDir}`); + chromeArguments.push("--remote-debugging-pipe"); + if (isPersistent) + chromeArguments.push("about:blank"); + else + chromeArguments.push("--no-startup-window"); + return chromeArguments; + } + _innerDefaultArgs(options) { + const { args = [] } = options; + const userDataDirArg = args.find((arg) => arg.startsWith("--user-data-dir")); + if (userDataDirArg) + throw this._createUserDataDirArgMisuseError("--user-data-dir"); + if (args.find((arg) => arg.startsWith("--remote-debugging-pipe"))) + throw new Error("Playwright manages remote debugging connection itself."); + if (args.find((arg) => !arg.startsWith("-"))) + throw new Error("Arguments can not specify page to be opened"); + const chromeArguments = [...chromiumSwitches()]; + chromeArguments.push("--enable-unsafe-swiftshader"); + if (options.headless) { + chromeArguments.push("--headless"); + chromeArguments.push( + "--hide-scrollbars", + "--mute-audio", + "--blink-settings=primaryHoverType=2,availableHoverTypes=2,primaryPointerType=4,availablePointerTypes=4" + ); + } + if (options.chromiumSandbox !== true) + chromeArguments.push("--no-sandbox"); + const proxy = options.proxyOverride || options.proxy; + if (proxy) { + const proxyURL = new URL(proxy.server); + const isSocks = proxyURL.protocol === "socks5:"; + if (isSocks && !options.socksProxyPort) { + chromeArguments.push(`--host-resolver-rules="MAP * ~NOTFOUND , EXCLUDE ${proxyURL.hostname}"`); + } + chromeArguments.push(`--proxy-server=${proxy.server}`); + const proxyBypassRules = []; + if (proxy.bypass) + proxyBypassRules.push(...proxy.bypass.split(",").map((t) => t.trim()).map((t) => t.startsWith(".") ? "*" + t : t)); + if (options.socksProxyPort || shouldProxyLoopback(proxy.bypass)) + proxyBypassRules.push("<-loopback>"); + if (proxyBypassRules.length > 0) + chromeArguments.push(`--proxy-bypass-list=${proxyBypassRules.join(";")}`); + } + chromeArguments.push(...args); + return chromeArguments; + } + async waitForReadyState(options, browserLogsCollector) { + return waitForReadyState(options, browserLogsCollector); + } + getExecutableName(options) { + if (options.channel && registry.isChromiumAlias(options.channel)) + return "chromium"; + if (options.channel === "chromium-tip-of-tree") + return options.headless ? "chromium-tip-of-tree-headless-shell" : "chromium-tip-of-tree"; + if (options.channel) + return options.channel; + return options.headless ? "chromium-headless-shell" : "chromium"; + } + }; + } +}); + +// packages/playwright-core/src/server/bidi/bidiOverCdp.ts +var bidiOverCdp_exports = {}; +__export(bidiOverCdp_exports, { + connectBidiOverCdp: () => connectBidiOverCdp +}); +async function connectBidiOverCdp(cdp) { + let server = void 0; + const bidiTransport = new BidiTransportImpl(); + const bidiConnection = new BidiConnection2(bidiTransport, () => server?.close()); + const cdpTransportImpl = new CdpTransportImpl(cdp); + const cdpConnection = new bidiCdpConnection.MapperCdpConnection(cdpTransportImpl, bidiServerLogger); + cdp.onclose = () => bidiConnection.onclose?.(); + server = await bidiMapper.BidiServer.createAndStart( + bidiTransport, + cdpConnection, + await cdpConnection.createBrowserSession(), + /* selfTargetId= */ + "", + void 0, + bidiServerLogger + ); + return bidiConnection; +} +var bidiMapper, bidiCdpConnection, bidiServerLogger, BidiTransportImpl, BidiConnection2, CdpTransportImpl; +var init_bidiOverCdp = __esm({ + "packages/playwright-core/src/server/bidi/bidiOverCdp.ts"() { + "use strict"; + bidiMapper = __toESM(require("chromium-bidi/lib/cjs/bidiMapper/BidiMapper")); + bidiCdpConnection = __toESM(require("chromium-bidi/lib/cjs/cdp/CdpConnection")); + init_debugLogger(); + bidiServerLogger = (prefix, ...args) => { + debugLogger.log(prefix, args); + }; + BidiTransportImpl = class { + setOnMessage(handler) { + this._handler = handler; + } + sendMessage(message) { + return this._bidiConnection.onmessage?.(message); + } + close() { + this._bidiConnection.onclose?.(); + } + }; + BidiConnection2 = class { + constructor(bidiTransport, closeCallback) { + this._bidiTransport = bidiTransport; + this._bidiTransport._bidiConnection = this; + this._closeCallback = closeCallback; + } + send(s) { + this._bidiTransport._handler?.(s); + } + close() { + this._closeCallback(); + } + }; + CdpTransportImpl = class { + constructor(connection) { + this._connection = connection; + this._connection.onmessage = (message) => { + this._handler?.(JSON.stringify(message)); + }; + } + setOnMessage(handler) { + this._handler = handler; + } + sendMessage(message) { + return this._connection.send(JSON.parse(message)); + } + close() { + this._connection.close(); + } + }; + } +}); + +// packages/playwright-core/src/server/bidi/bidiChromium.ts +var import_os12, BidiChromium, kBidiOverCdpWrapper; +var init_bidiChromium = __esm({ + "packages/playwright-core/src/server/bidi/bidiChromium.ts"() { + "use strict"; + import_os12 = __toESM(require("os")); + init_ascii(); + init_browserType(); + init_bidiBrowser(); + init_bidiConnection(); + init_chromiumSwitches(); + init_chromium(); + init_crBrowser(); + BidiChromium = class extends BrowserType { + constructor(parent) { + super(parent, "chromium"); + } + async connectToTransport(transport, options, browserLogsCollector) { + const bidiOverCdp = (init_bidiOverCdp(), __toCommonJS(bidiOverCdp_exports)); + const bidiTransport = await bidiOverCdp.connectBidiOverCdp(transport); + transport[kBidiOverCdpWrapper] = bidiTransport; + try { + return BidiBrowser.connect(this.attribution.playwright, bidiTransport, options); + } catch (e) { + const error = profileInUseError(browserLogsCollector.recentLogs()); + if (error) + throw error; + throw e; + } + } + doRewriteStartupLog(logs) { + if (logs.includes("Missing X server")) + logs = "\n" + wrapInASCIIBox(kNoXServerRunningError, 1); + if (!logs.includes("crbug.com/357670") && !logs.includes("No usable sandbox!") && !logs.includes("crbug.com/638180")) + return logs; + return [ + `Chromium sandboxing failed!`, + `================================`, + `To avoid the sandboxing issue, do either of the following:`, + ` - (preferred): Configure your environment to support sandboxing`, + ` - (alternative): Launch Chromium without sandbox using 'chromiumSandbox: false' option`, + `================================`, + `` + ].join("\n"); + } + amendEnvironment(env) { + return env; + } + attemptToGracefullyCloseBrowser(transport) { + const bidiTransport = transport[kBidiOverCdpWrapper]; + if (bidiTransport) + transport = bidiTransport; + transport.send({ method: "browser.close", params: {}, id: kBrowserCloseMessageId2 }); + } + supportsPipeTransport() { + return false; + } + async defaultArgs(options, isPersistent, userDataDir) { + const chromeArguments = this._innerDefaultArgs(options); + chromeArguments.push(`--user-data-dir=${userDataDir}`); + chromeArguments.push("--remote-debugging-port=0"); + if (isPersistent) + chromeArguments.push("about:blank"); + else + chromeArguments.push("--no-startup-window"); + return chromeArguments; + } + async waitForReadyState(options, browserLogsCollector) { + return waitForReadyState({ ...options, args: ["--remote-debugging-port=0"] }, browserLogsCollector); + } + _innerDefaultArgs(options) { + const { args = [] } = options; + const userDataDirArg = args.find((arg) => arg.startsWith("--user-data-dir")); + if (userDataDirArg) + throw this._createUserDataDirArgMisuseError("--user-data-dir"); + if (args.find((arg) => arg.startsWith("--remote-debugging-pipe"))) + throw new Error("Playwright manages remote debugging connection itself."); + if (args.find((arg) => !arg.startsWith("-"))) + throw new Error("Arguments can not specify page to be opened"); + const chromeArguments = [...chromiumSwitches()]; + if (import_os12.default.platform() === "darwin") { + chromeArguments.push("--enable-unsafe-swiftshader"); + } + if (options.headless) { + chromeArguments.push("--headless"); + chromeArguments.push( + "--hide-scrollbars", + "--mute-audio", + "--blink-settings=primaryHoverType=2,availableHoverTypes=2,primaryPointerType=4,availablePointerTypes=4" + ); + } + if (options.chromiumSandbox !== true) + chromeArguments.push("--no-sandbox"); + const proxy = options.proxyOverride || options.proxy; + if (proxy) { + const proxyURL = new URL(proxy.server); + const isSocks = proxyURL.protocol === "socks5:"; + if (isSocks && !options.socksProxyPort) { + chromeArguments.push(`--host-resolver-rules="MAP * ~NOTFOUND , EXCLUDE ${proxyURL.hostname}"`); + } + chromeArguments.push(`--proxy-server=${proxy.server}`); + const proxyBypassRules = []; + if (proxy.bypass) + proxyBypassRules.push(...proxy.bypass.split(",").map((t) => t.trim()).map((t) => t.startsWith(".") ? "*" + t : t)); + if (options.socksProxyPort || shouldProxyLoopback(proxy.bypass)) + proxyBypassRules.push("<-loopback>"); + if (proxyBypassRules.length > 0) + chromeArguments.push(`--proxy-bypass-list=${proxyBypassRules.join(";")}`); + } + chromeArguments.push(...args); + return chromeArguments; + } + getExecutableName(options) { + switch (options.channel) { + case "bidi-chromium": + return "chromium"; + case "bidi-chrome": + return "chrome"; + case "bidi-chrome-beta": + return "chrome-beta"; + case "bidi-chrome-dev": + return "chrome-dev"; + case "bidi-chrome-canary": + return "chrome-canary"; + } + throw new Error(`Unsupported Bidi Chromium channel: ${options.channel}`); + } + }; + kBidiOverCdpWrapper = Symbol("kBidiConnectionWrapper"); + } +}); + +// packages/playwright-core/src/server/bidi/third_party/firefoxPrefs.ts +async function createProfile(options) { + if (!import_fs28.default.existsSync(options.path)) { + await import_fs28.default.promises.mkdir(options.path, { + recursive: true + }); + } + await writePreferences({ + preferences: { + ...defaultProfilePreferences(options.preferences), + ...options.preferences + }, + path: options.path + }); +} +function defaultProfilePreferences(extraPrefs) { + const server = "dummy.test"; + const defaultPrefs = { + // Make sure Shield doesn't hit the network. + "app.normandy.api_url": "", + // Disable Firefox old build background check + "app.update.checkInstallTime": false, + // Disable automatically upgrading Firefox + "app.update.disabledForTesting": true, + // Increase the APZ content response timeout to 1 minute + "apz.content_response_timeout": 6e4, + // Prevent various error message on the console + // jest-puppeteer asserts that no error message is emitted by the console + "browser.contentblocking.features.standard": "-tp,tpPrivate,cookieBehavior0,-cm,-fp", + // Enable the dump function: which sends messages to the system + // console + // https://bugzilla.mozilla.org/show_bug.cgi?id=1543115 + "browser.dom.window.dump.enabled": true, + // Make sure newtab weather doesn't hit the network to retrieve weather data. + "browser.newtabpage.activity-stream.discoverystream.region-weather-config": "", + // Make sure newtab wallpapers don't hit the network to retrieve wallpaper data. + "browser.newtabpage.activity-stream.newtabWallpapers.enabled": false, + "browser.newtabpage.activity-stream.newtabWallpapers.v2.enabled": false, + // Make sure Topsites doesn't hit the network to retrieve sponsored tiles. + "browser.newtabpage.activity-stream.showSponsoredTopSites": false, + // Disable topstories + "browser.newtabpage.activity-stream.feeds.system.topstories": false, + // Always display a blank page + "browser.newtabpage.enabled": false, + // Background thumbnails in particular cause grief: and disabling + // thumbnails in general cannot hurt + "browser.pagethumbnails.capturing_disabled": true, + // Disable safebrowsing components. + "browser.safebrowsing.blockedURIs.enabled": false, + "browser.safebrowsing.downloads.enabled": false, + "browser.safebrowsing.malware.enabled": false, + "browser.safebrowsing.phishing.enabled": false, + // Disable updates to search engines. + "browser.search.update": false, + // Do not restore the last open set of tabs if the browser has crashed + "browser.sessionstore.resume_from_crash": false, + // Skip check for default browser on startup + "browser.shell.checkDefaultBrowser": false, + // Disable newtabpage + "browser.startup.homepage": "about:blank", + // Do not redirect user when a milstone upgrade of Firefox is detected + "browser.startup.homepage_override.mstone": "ignore", + // Start with a blank page about:blank + "browser.startup.page": 0, + // Do not allow background tabs to be zombified on Android: otherwise for + // tests that open additional tabs: the test harness tab itself might get + // unloaded + "browser.tabs.disableBackgroundZombification": false, + // Do not warn when closing all other open tabs + "browser.tabs.warnOnCloseOtherTabs": false, + // Do not warn when multiple tabs will be opened + "browser.tabs.warnOnOpen": false, + // Do not automatically offer translations, as tests do not expect this. + "browser.translations.automaticallyPopup": false, + // Disable the UI tour. + "browser.uitour.enabled": false, + // Turn off search suggestions in the location bar so as not to trigger + // network connections. + "browser.urlbar.suggest.searches": false, + // Disable first run splash page on Windows 10 + "browser.usedOnWindows10.introURL": "", + // Do not warn on quitting Firefox + "browser.warnOnQuit": false, + // Defensively disable data reporting systems + "datareporting.healthreport.documentServerURI": `http://${server}/dummy/healthreport/`, + "datareporting.healthreport.logging.consoleEnabled": false, + "datareporting.healthreport.service.enabled": false, + "datareporting.healthreport.service.firstRun": false, + "datareporting.healthreport.uploadEnabled": false, + "datareporting.usage.uploadEnabled": false, + "telemetry.fog.test.localhost_port": -1, + // Do not show datareporting policy notifications which can interfere with tests + "datareporting.policy.dataSubmissionEnabled": false, + "datareporting.policy.dataSubmissionPolicyBypassNotification": true, + // DevTools JSONViewer sometimes fails to load dependencies with its require.js. + // This doesn't affect Puppeteer but spams console (Bug 1424372) + "devtools.jsonview.enabled": false, + // Disable popup-blocker + "dom.disable_open_during_load": false, + // Enable the support for File object creation in the content process + // Required for |Page.setFileInputFiles| protocol method. + "dom.file.createInChild": true, + // Disable the ProcessHangMonitor + "dom.ipc.reportProcessHangs": false, + // Disable slow script dialogues + "dom.max_chrome_script_run_time": 0, + "dom.max_script_run_time": 0, + // Disable background timer throttling to allow tests to run in parallel + // without a decrease in performance. + "dom.min_background_timeout_value": 0, + "dom.min_background_timeout_value_without_budget_throttling": 0, + "dom.timeout.enable_budget_timer_throttling": false, + // Disable HTTPS-First upgrades + "dom.security.https_first": false, + // Only load extensions from the application and user profile + // AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_APPLICATION + "extensions.autoDisableScopes": 0, + "extensions.enabledScopes": 5, + // Disable metadata caching for installed add-ons by default + "extensions.getAddons.cache.enabled": false, + // Disable installing any distribution extensions or add-ons. + "extensions.installDistroAddons": false, + // Disabled screenshots extension + "extensions.screenshots.disabled": true, + // Turn off extension updates so they do not bother tests + "extensions.update.enabled": false, + // Turn off extension updates so they do not bother tests + "extensions.update.notifyUser": false, + // Make sure opening about:addons will not hit the network + "extensions.webservice.discoverURL": `http://${server}/dummy/discoveryURL`, + // Allow the application to have focus even it runs in the background + "focusmanager.testmode": true, + // Disable useragent updates + "general.useragent.updates.enabled": false, + // Always use network provider for geolocation tests so we bypass the + // macOS dialog raised by the corelocation provider + "geo.provider.testing": true, + // Do not scan Wifi + "geo.wifi.scan": false, + // No hang monitor + "hangmonitor.timeout": 0, + // Show chrome errors and warnings in the error console + "javascript.options.showInConsole": true, + // Do not throttle rendering (requestAnimationFrame) in background tabs + "layout.testing.top-level-always-active": true, + // Disable download and usage of OpenH264: and Widevine plugins + "media.gmp-manager.updateEnabled": false, + // Disable the GFX sanity window + "media.sanity-test.disabled": true, + // Disable connectivity service pings + "network.connectivity-service.enabled": false, + // Disable experimental feature that is only available in Nightly + "network.cookie.sameSite.laxByDefault": false, + // Do not prompt for temporary redirects + "network.http.prompt-temp-redirect": false, + // Disable speculative connections so they are not reported as leaking + // when they are hanging around + "network.http.speculative-parallel-limit": 0, + // Do not automatically switch between offline and online + "network.manage-offline-status": false, + // Make sure SNTP requests do not hit the network + "network.sntp.pools": server, + // Disable Flash. + "plugin.state.flash": 0, + "privacy.trackingprotection.enabled": false, + // Can be removed once Firefox 89 is no longer supported + // https://bugzilla.mozilla.org/show_bug.cgi?id=1710839 + "remote.enabled": true, + // Don't do network connections for mitm priming + "security.certerrors.mitm.priming.enabled": false, + // Local documents have access to all other local documents, + // including directory listings + "security.fileuri.strict_origin_policy": false, + // Do not wait for the notification button security delay + "security.notification_enable_delay": 0, + // Do not automatically fill sign-in forms with known usernames and + // passwords + "signon.autofillForms": false, + // Disable password capture, so that tests that include forms are not + // influenced by the presence of the persistent doorhanger notification + "signon.rememberSignons": false, + // Disable first-run welcome page + "startup.homepage_welcome_url": "about:blank", + // Disable first-run welcome page + "startup.homepage_welcome_url.additional": "", + // Disable browser animations (tabs, fullscreen, sliding alerts) + "toolkit.cosmeticAnimations.enabled": false, + // Prevent starting into safe mode after application crashes + "toolkit.startup.max_resumed_crashes": -1, + // Enable TestUtils + "dom.testing.testutils.enabled": true + }; + return Object.assign(defaultPrefs, extraPrefs); +} +async function writePreferences(options) { + const prefsPath = import_path26.default.join(options.path, "prefs.js"); + const lines = Object.entries(options.preferences).map(([key, value2]) => { + return `user_pref(${JSON.stringify(key)}, ${JSON.stringify(value2)});`; + }); + const result2 = await Promise.allSettled([ + import_fs28.default.promises.writeFile(import_path26.default.join(options.path, "user.js"), lines.join("\n")), + // Create a backup of the preferences file if it already exitsts. + import_fs28.default.promises.access(prefsPath, import_fs28.default.constants.F_OK).then( + async () => { + await import_fs28.default.promises.copyFile( + prefsPath, + import_path26.default.join(options.path, "prefs.js.playwright") + ); + }, + // Swallow only if file does not exist + () => { + } + ) + ]); + for (const command of result2) { + if (command.status === "rejected") { + throw command.reason; + } + } +} +var import_fs28, import_path26; +var init_firefoxPrefs = __esm({ + "packages/playwright-core/src/server/bidi/third_party/firefoxPrefs.ts"() { + "use strict"; + import_fs28 = __toESM(require("fs")); + import_path26 = __toESM(require("path")); + } +}); + +// packages/playwright-core/src/server/bidi/bidiFirefox.ts +var import_os13, import_path27, BidiFirefox; +var init_bidiFirefox = __esm({ + "packages/playwright-core/src/server/bidi/bidiFirefox.ts"() { + "use strict"; + import_os13 = __toESM(require("os")); + import_path27 = __toESM(require("path")); + init_manualPromise(); + init_ascii(); + init_browserType(); + init_bidiBrowser(); + init_bidiConnection(); + init_firefoxPrefs(); + BidiFirefox = class extends BrowserType { + constructor(parent) { + super(parent, "firefox"); + } + executablePath() { + return ""; + } + async connectToTransport(transport, options) { + return BidiBrowser.connect(this.attribution.playwright, transport, options); + } + doRewriteStartupLog(logs) { + if (logs.includes(`as root in a regular user's session is not supported.`)) + logs = "\n" + wrapInASCIIBox(`Firefox is unable to launch if the $HOME folder isn't owned by the current user. +Workaround: Set the HOME=/root environment variable${process.env.GITHUB_ACTION ? " in your GitHub Actions workflow file" : ""} when running Playwright.`, 1); + if (logs.includes("no DISPLAY environment variable specified")) + logs = "\n" + wrapInASCIIBox(kNoXServerRunningError, 1); + return logs; + } + amendEnvironment(env) { + if (!import_path27.default.isAbsolute(import_os13.default.homedir())) + throw new Error(`Cannot launch Firefox with relative home directory. Did you set ${import_os13.default.platform() === "win32" ? "USERPROFILE" : "HOME"} to a relative path?`); + env = { + ...env, + "MOZ_CRASHREPORTER": "1", + "MOZ_CRASHREPORTER_NO_REPORT": "1", + "MOZ_CRASHREPORTER_SHUTDOWN": "1" + }; + if (import_os13.default.platform() === "linux") { + return { ...env, SNAP_NAME: void 0, SNAP_INSTANCE_NAME: void 0 }; + } + return env; + } + attemptToGracefullyCloseBrowser(transport) { + this._attemptToGracefullyCloseBrowser(transport).catch(() => { + }); + } + async _attemptToGracefullyCloseBrowser(transport) { + if (!transport.onmessage) { + transport.send({ method: "session.new", params: { capabilities: {} }, id: kShutdownSessionNewMessageId }); + await new Promise((resolve) => { + transport.onmessage = (message) => { + if (message.id === kShutdownSessionNewMessageId) + resolve(true); + }; + }); + } + transport.send({ method: "browser.close", params: {}, id: kBrowserCloseMessageId2 }); + } + supportsPipeTransport() { + return false; + } + async prepareUserDataDir(options, userDataDir) { + await createProfile({ + path: userDataDir, + preferences: options.firefoxUserPrefs || {} + }); + } + async defaultArgs(options, isPersistent, userDataDir) { + const { args = [], headless } = options; + const userDataDirArg = args.find((arg) => arg.startsWith("-profile") || arg.startsWith("--profile")); + if (userDataDirArg) + throw this._createUserDataDirArgMisuseError("--profile"); + if (args.find((arg) => !arg.startsWith("-"))) + throw new Error("Arguments can not specify page to be opened"); + const firefoxArguments = ["--remote-debugging-port=0"]; + if (headless) + firefoxArguments.push("--headless"); + else + firefoxArguments.push("--foreground"); + firefoxArguments.push(`--profile`, userDataDir); + firefoxArguments.push(...args); + return firefoxArguments; + } + async waitForReadyState(options, browserLogsCollector) { + const result2 = new ManualPromise(); + browserLogsCollector.onMessage((message) => { + const match = message.match(/WebDriver BiDi listening on (ws:\/\/.*)$/); + if (match) + result2.resolve({ wsEndpoint: match[1] + "/session" }); + }); + return result2; + } + }; + } +}); + +// packages/playwright-core/src/server/debugController.ts +function wireListeners(recorder, debugController) { + if (recorder[wiredSymbol]) + return; + recorder[wiredSymbol] = true; + const actions = []; + const languageGenerator = new JavaScriptLanguageGenerator( + /* isPlaywrightTest */ + true + ); + const actionsChanged = () => { + const aa = collapseActions(actions); + const { header, footer, text: text2, actionTexts } = generateCode(aa, languageGenerator, { + browserName: "chromium", + launchOptions: {}, + contextOptions: {}, + generateAutoExpect: debugController._generateAutoExpect + }); + debugController.emit(DebugController.Events.SourceChanged, { text: text2, header, footer, actions: actionTexts }); + }; + recorder.on(RecorderEvent.ElementPicked, (elementInfo) => { + const locator2 = asLocator(debugController._sdkLanguage, elementInfo.selector); + debugController.emit(DebugController.Events.InspectRequested, { selector: elementInfo.selector, locator: locator2, ariaSnapshot: elementInfo.ariaSnapshot }); + }); + recorder.on(RecorderEvent.PausedStateChanged, (paused) => { + debugController.emit(DebugController.Events.Paused, { paused }); + }); + recorder.on(RecorderEvent.ModeChanged, (mode) => { + debugController.emit(DebugController.Events.SetModeRequested, { mode }); + }); + recorder.on(RecorderEvent.ActionAdded, (action) => { + actions.push(action); + actionsChanged(); + }); + recorder.on(RecorderEvent.SignalAdded, (signal) => { + const lastAction = actions.findLast((a) => a.frame.pageGuid === signal.frame.pageGuid); + if (lastAction) + lastAction.action.signals.push(signal.signal); + actionsChanged(); + }); +} +var yaml2, DebugController, wiredSymbol; +var init_debugController = __esm({ + "packages/playwright-core/src/server/debugController.ts"() { + "use strict"; + init_ariaSnapshot(); + init_locatorParser(); + init_processLauncher(); + init_locatorGenerators(); + init_instrumentation(); + init_recorder(); + init_language(); + init_recorderUtils(); + init_javascript2(); + yaml2 = require("./utilsBundle").yaml; + DebugController = class _DebugController extends SdkObject { + constructor(playwright2) { + super({ attribution: { isInternalPlaywright: true }, instrumentation: createInstrumentation() }, void 0, "DebugController"); + this._sdkLanguage = "javascript"; + this._generateAutoExpect = false; + this._playwright = playwright2; + } + static { + this.Events = { + StateChanged: "stateChanged", + InspectRequested: "inspectRequested", + SourceChanged: "sourceChanged", + Paused: "paused", + SetModeRequested: "setModeRequested" + }; + } + initialize(progress2, codegenId2, sdkLanguage) { + this._sdkLanguage = sdkLanguage; + } + dispose() { + this._setReportStateChanged(false); + } + setReportStateChanged(progress2, enabled) { + this._setReportStateChanged(enabled); + } + _setReportStateChanged(enabled) { + if (enabled && !this._trackHierarchyListener) { + this._trackHierarchyListener = { + onPageOpen: () => this._emitSnapshot(false), + onPageClose: () => this._emitSnapshot(false) + }; + this._playwright.instrumentation.addListener(this._trackHierarchyListener, null); + this._emitSnapshot(true); + } else if (!enabled && this._trackHierarchyListener) { + this._playwright.instrumentation.removeListener(this._trackHierarchyListener); + this._trackHierarchyListener = void 0; + } + } + async setRecorderMode(progress2, params2) { + await this._closeBrowsersWithoutPages(progress2); + this._generateAutoExpect = !!params2.generateAutoExpect; + if (params2.mode === "none") { + const promises = []; + for (const recorder of await progress2.race(this._allRecorders())) { + promises.push(recorder.hideHighlightedSelector()); + promises.push(recorder.setMode("none")); + } + await progress2.race(Promise.all(promises)); + return; + } + if (!this._playwright.allBrowsers().length) + await this._playwright.chromium.launch(progress2, { headless: !!process.env.PW_DEBUG_CONTROLLER_HEADLESS }); + const pages = this._playwright.allPages(); + if (!pages.length) { + const [browser] = this._playwright.allBrowsers(); + const context2 = await browser.newContextForReuse(progress2, {}); + await context2.newPage(progress2); + } + if (params2.testIdAttributeName) { + for (const page of this._playwright.allPages()) + page.browserContext.selectors().setTestIdAttributeName(params2.testIdAttributeName); + } + for (const recorder of await progress2.race(this._allRecorders())) { + recorder.hideHighlightedSelector(); + recorder.setMode(params2.mode); + } + } + async highlight(progress2, params2) { + if (params2.selector) + unsafeLocatorOrSelectorAsSelector(this._sdkLanguage, params2.selector, "data-testid"); + const ariaTemplate = params2.ariaTemplate ? parseAriaSnapshotUnsafe(yaml2, params2.ariaTemplate) : void 0; + const promises = []; + for (const recorder of await progress2.race(this._allRecorders())) { + if (ariaTemplate) + promises.push(recorder.setHighlightedAriaTemplate(ariaTemplate)); + else if (params2.selector) + promises.push(recorder.setHighlightedSelector(params2.selector)); + } + await progress2.race(Promise.all(promises)); + } + async hideHighlight(progress2) { + const promises = []; + for (const recorder of await progress2.race(this._allRecorders())) + promises.push(recorder.hideHighlightedSelector()); + promises.push(...this._playwright.allPages().map((p) => p.hideHighlight().catch(() => { + }))); + await progress2.race(Promise.all(promises)); + } + async resume(progress2) { + for (const recorder of await progress2.race(this._allRecorders())) + recorder.resume(); + } + kill(progress2) { + gracefullyProcessExitDoNotHang(0); + } + _emitSnapshot(initial) { + const pageCount = this._playwright.allPages().length; + if (initial && !pageCount) + return; + this.emit(_DebugController.Events.StateChanged, { pageCount }); + } + async _allRecorders() { + const contexts = /* @__PURE__ */ new Set(); + for (const page of this._playwright.allPages()) + contexts.add(page.browserContext); + const recorders = await Promise.all([...contexts].map((c) => Recorder.forContext(c, { omitCallTracking: true }))); + const nonNullRecorders = recorders.filter(Boolean); + for (const recorder of recorders) + wireListeners(recorder, this); + return nonNullRecorders; + } + async _closeBrowsersWithoutPages(progress2) { + for (const browser of this._playwright.allBrowsers()) { + for (const context2 of browser.contexts()) { + if (!context2.pages().length) + await context2.close(progress2, { reason: "Browser collected" }); + } + if (!browser.contexts()) + await browser.close(progress2, { reason: "Browser collected" }); + } + } + }; + wiredSymbol = Symbol("wired"); + } +}); + +// packages/playwright-core/src/server/electron/electron.ts +async function waitForLine(progress2, process2, regex) { + const promise = new ManualPromise(); + const rl = readline2.createInterface({ input: process2.stderr }); + const failError = new Error("Process failed to launch!"); + const listeners = [ + eventsHelper.addEventListener(rl, "line", onLine), + eventsHelper.addEventListener(rl, "close", () => promise.reject(failError)), + eventsHelper.addEventListener(process2, "exit", () => promise.reject(failError)), + // It is Ok to remove error handler because we did not create process and there is another listener. + eventsHelper.addEventListener(process2, "error", () => promise.reject(failError)) + ]; + function onLine(line) { + const match = line.match(regex); + if (match) + promise.resolve(match); + } + try { + return await progress2.race(promise); + } finally { + eventsHelper.removeEventListeners(listeners); + } +} +function escapeDoubleQuotes(str) { + return str.replace(/"/g, '\\"'); +} +var import_fs29, import_os14, import_path28, readline2, ARTIFACTS_FOLDER3, ElectronApplication, Electron; +var init_electron = __esm({ + "packages/playwright-core/src/server/electron/electron.ts"() { + "use strict"; + import_fs29 = __toESM(require("fs")); + import_os14 = __toESM(require("os")); + import_path28 = __toESM(require("path")); + readline2 = __toESM(require("readline")); + init_ascii(); + init_debugLogger(); + init_eventsHelper(); + init_processLauncher(); + init_manualPromise(); + init_package(); + init_browserContext(); + init_crBrowser(); + init_crConnection(); + init_crExecutionContext(); + init_crProtocolHelper(); + init_console(); + init_helper(); + init_instrumentation(); + init_javascript(); + init_transport(); + init_progress(); + ARTIFACTS_FOLDER3 = import_path28.default.join(import_os14.default.tmpdir(), "playwright-artifacts-"); + ElectronApplication = class _ElectronApplication extends SdkObject { + constructor(parent, browser, nodeConnection, process2) { + super(parent, "electron-app"); + this._nodeElectronHandlePromise = new ManualPromise(); + this._process = process2; + this._browserContext = browser._defaultContext; + this._nodeConnection = nodeConnection; + this._nodeSession = nodeConnection.rootSession; + this._nodeSession.on("Runtime.executionContextCreated", async (event) => { + if (!event.context.auxData || !event.context.auxData.isDefault) + return; + const crExecutionContext = new CRExecutionContext(this._nodeSession, event.context); + this._nodeExecutionContext = new ExecutionContext(this, crExecutionContext, "electron"); + const { result: remoteObject } = await crExecutionContext._client.send("Runtime.evaluate", { + expression: `require('electron')`, + contextId: event.context.id, + // Needed after Electron 28 to get access to require: https://github.com/microsoft/playwright/issues/28048 + includeCommandLineAPI: true + }); + this._nodeElectronHandlePromise.resolve(new JSHandle(this._nodeExecutionContext, "object", "ElectronModule", remoteObject.objectId)); + }); + this._nodeSession.on("Runtime.consoleAPICalled", (event) => this._onConsoleAPI(event)); + const appClosePromise = new Promise((f) => this.once(_ElectronApplication.Events.Close, f)); + this._browserContext.setCustomCloseHandler(async () => { + const electronHandle = await this._nodeElectronHandlePromise; + await electronHandle.evaluate(({ app }) => app.quit()).catch(() => { + }); + this._nodeConnection.close(); + await appClosePromise; + }); + } + static { + this.Events = { + Close: "close", + Console: "console" + }; + } + async _onConsoleAPI(event) { + if (event.executionContextId === 0) { + return; + } + if (!this._nodeExecutionContext) + return; + const args = event.args.map((arg) => createHandle(this._nodeExecutionContext, arg)); + const message = new ConsoleMessage(null, null, event.type, void 0, args, stackTraceToLocation(event.stackTrace), event.timestamp); + this.emit(_ElectronApplication.Events.Console, message); + } + async initialize() { + await this._nodeSession.send("Runtime.enable", {}); + await this._nodeSession.send("Runtime.evaluate", { expression: "__playwright_run()" }); + } + process() { + return this._process; + } + context() { + return this._browserContext; + } + async close(progress2) { + await this._browserContext.close(progress2, { reason: "Application exited" }); + } + async browserWindow(progress2, page) { + const targetId = page.delegate._targetId; + const electronHandle = await progress2.race(this._nodeElectronHandlePromise); + return await progress2.race(electronHandle.evaluateHandle(({ BrowserWindow, webContents }, targetId2) => { + const wc = webContents.fromDevToolsTargetId(targetId2); + return BrowserWindow.fromWebContents(wc); + }, targetId)); + } + }; + Electron = class extends SdkObject { + constructor(playwright2) { + super(playwright2, "electron"); + this.logName = "browser"; + } + async launch(progress2, options) { + let app = void 0; + let electronArguments = ["--inspect=0", "--remote-debugging-port=0", ...options.args || []]; + if (import_os14.default.platform() === "linux") { + if (!options.chromiumSandbox && electronArguments.indexOf("--no-sandbox") === -1) + electronArguments.unshift("--no-sandbox"); + } + let artifactsDir; + const tempDirectories = []; + if (options.artifactsDir) { + artifactsDir = options.artifactsDir; + } else { + artifactsDir = await progress2.race(import_fs29.default.promises.mkdtemp(ARTIFACTS_FOLDER3)); + tempDirectories.push(artifactsDir); + } + const browserLogsCollector = new RecentLogsCollector(); + const env = options.env ? envArrayToObject(options.env) : process.env; + let command; + if (options.executablePath) { + command = options.executablePath; + } else { + try { + command = require("electron/index.js"); + } catch (error) { + if (error?.code === "MODULE_NOT_FOUND") { + throw new Error("\n" + wrapInASCIIBox([ + "Electron executablePath not found!", + "Please install it using `npm install -D electron` or set the executablePath to your Electron executable." + ].join("\n"), 1)); + } + throw error; + } + electronArguments.unshift("-r", libPath("server", "electron", "loader.js")); + } + let shell = false; + if (process.platform === "win32") { + shell = true; + command = [command, ...electronArguments].map((arg) => `"${escapeDoubleQuotes(arg)}"`).join(" "); + electronArguments = []; + } + delete env.NODE_OPTIONS; + const { launchedProcess, gracefullyClose, kill } = await progress2.race(launchProcess({ + command, + args: electronArguments, + env, + log: (message) => { + progress2.log(message); + browserLogsCollector.log(message); + }, + shell, + stdio: "pipe", + cwd: options.cwd, + tempDirectories, + attemptToGracefullyClose: () => app.close(nullProgress), + handleSIGINT: true, + handleSIGTERM: true, + handleSIGHUP: true, + onExit: () => app?.emit(ElectronApplication.Events.Close) + })); + const waitForXserverError = waitForLine(progress2, launchedProcess, /Unable to open X display/).then(() => { + throw new Error([ + "Unable to open X display!", + `================================`, + "Most likely this is because there is no X server available.", + "Use 'xvfb-run' on Linux to launch your tests with an emulated display server.", + "For example: 'xvfb-run npm run test:e2e'", + `================================`, + progress2.metadata.log + ].join("\n")); + }); + const nodeMatchPromise = waitForLine(progress2, launchedProcess, /^Debugger listening on (ws:\/\/.*)$/); + const chromeMatchPromise = waitForLine(progress2, launchedProcess, /^DevTools listening on (ws:\/\/.*)$/); + const debuggerDisconnectPromise = waitForLine(progress2, launchedProcess, /Waiting for the debugger to disconnect\.\.\./); + try { + const nodeMatch = await nodeMatchPromise; + const nodeTransport = await WebSocketTransport.connect(progress2, nodeMatch[1]); + const nodeConnection = new CRConnection(this, nodeTransport, helper.debugProtocolLogger(), browserLogsCollector); + debuggerDisconnectPromise.then(() => { + nodeTransport.close(); + }).catch(() => { + }); + const chromeMatch = await progress2.race(Promise.race([ + chromeMatchPromise, + waitForXserverError + ])); + const chromeTransport = await WebSocketTransport.connect(progress2, chromeMatch[1]); + const browserProcess = { + onclose: void 0, + process: launchedProcess, + close: gracefullyClose, + kill + }; + const contextOptions = { + ...options, + noDefaultViewport: true + }; + const browserOptions = { + name: "electron", + browserType: "chromium", + headful: true, + persistent: contextOptions, + browserProcess, + protocolLogger: helper.debugProtocolLogger(), + browserLogsCollector, + artifactsDir, + downloadsPath: artifactsDir, + tracesDir: options.tracesDir || artifactsDir, + originalLaunchOptions: {} + }; + validateBrowserContextOptions(contextOptions, browserOptions); + const browser = await progress2.race(CRBrowser.connect(this.attribution.playwright, chromeTransport, browserOptions)); + app = new ElectronApplication(this, browser, nodeConnection, launchedProcess); + await progress2.race(app.initialize()); + return app; + } catch (error) { + await progress2.race(kill()); + throw error; + } + } + }; + } +}); + +// packages/playwright-core/src/server/firefox/ffConnection.ts +var import_events10, ConnectionEvents2, kBrowserCloseMessageId3, FFConnection, FFSession; +var init_ffConnection = __esm({ + "packages/playwright-core/src/server/firefox/ffConnection.ts"() { + "use strict"; + import_events10 = require("events"); + init_debugLogger(); + init_helper(); + init_protocolError(); + ConnectionEvents2 = { + Disconnected: Symbol("Disconnected") + }; + kBrowserCloseMessageId3 = -9999; + FFConnection = class extends import_events10.EventEmitter { + constructor(transport, protocolLogger, browserLogsCollector) { + super(); + this.setMaxListeners(0); + this._transport = transport; + this._protocolLogger = protocolLogger; + this._browserLogsCollector = browserLogsCollector; + this._lastId = 0; + this._sessions = /* @__PURE__ */ new Map(); + this._closed = false; + this.rootSession = new FFSession(this, "", (message) => this._rawSend(message)); + this._sessions.set("", this.rootSession); + this._transport.onmessage = this._onMessage.bind(this); + this._transport.onclose = this._onClose.bind(this); + } + nextMessageId() { + return ++this._lastId; + } + _rawSend(message) { + this._protocolLogger("send", message); + this._transport.send(message); + } + async _onMessage(message) { + this._protocolLogger("receive", message); + if (message.id === kBrowserCloseMessageId3) + return; + const session2 = this._sessions.get(message.sessionId || ""); + if (session2) + session2.dispatchMessage(message); + } + _onClose(reason) { + this._closed = true; + this._transport.onmessage = void 0; + this._transport.onclose = void 0; + this._browserDisconnectedLogs = helper.formatBrowserLogs(this._browserLogsCollector.recentLogs(), reason); + this.rootSession.dispose(); + Promise.resolve().then(() => this.emit(ConnectionEvents2.Disconnected)); + } + close() { + if (!this._closed) + this._transport.close(); + } + createSession(sessionId) { + const session2 = new FFSession(this, sessionId, (message) => this._rawSend({ ...message, sessionId })); + this._sessions.set(sessionId, session2); + return session2; + } + }; + FFSession = class extends import_events10.EventEmitter { + constructor(connection, sessionId, rawSend) { + super(); + this._disposed = false; + this._crashed = false; + this.setMaxListeners(0); + this._callbacks = /* @__PURE__ */ new Map(); + this._connection = connection; + this._sessionId = sessionId; + this._rawSend = rawSend; + } + markAsCrashed() { + this._crashed = true; + } + async send(method, params2) { + if (this._crashed || this._disposed || this._connection._closed || this._connection._browserDisconnectedLogs) + throw new ProtocolError(this._crashed ? "crashed" : "closed", void 0, this._connection._browserDisconnectedLogs); + const id = this._connection.nextMessageId(); + this._rawSend({ method, params: params2, id }); + return new Promise((resolve, reject) => { + this._callbacks.set(id, { resolve, reject, error: new ProtocolError("error", method) }); + }); + } + sendMayFail(method, params2) { + return this.send(method, params2).catch((error) => debugLogger.log("error", error)); + } + dispatchMessage(object) { + if (object.id) { + const callback = this._callbacks.get(object.id); + if (callback) { + this._callbacks.delete(object.id); + if (object.error) { + callback.error.setMessage(object.error.message); + callback.reject(callback.error); + } else { + callback.resolve(object.result); + } + } + } else { + Promise.resolve().then(() => this.emit(object.method, object.params)); + } + } + dispose() { + this._disposed = true; + this._connection._sessions.delete(this._sessionId); + for (const callback of this._callbacks.values()) { + callback.error.type = this._crashed ? "crashed" : "closed"; + callback.error.logs = this._connection._browserDisconnectedLogs; + callback.reject(callback.error); + } + this._callbacks.clear(); + } + }; + } +}); + +// packages/playwright-core/src/server/firefox/ffExecutionContext.ts +function checkException(exceptionDetails) { + if (!exceptionDetails) + return; + if (exceptionDetails.value) + throw new JavaScriptErrorInEvaluate(JSON.stringify(exceptionDetails.value)); + else + throw new JavaScriptErrorInEvaluate(exceptionDetails.text + (exceptionDetails.stack ? "\n" + exceptionDetails.stack : "")); +} +function rewriteError3(error) { + if (error.message.includes("cyclic object value") || error.message.includes("Object is not serializable")) + return { result: { type: "undefined", value: void 0 } }; + if (error instanceof TypeError && error.message.startsWith("Converting circular structure to JSON")) + rewriteErrorMessage(error, error.message + " Are you passing a nested JSHandle?"); + if (!isJavaScriptErrorInEvaluate(error) && !isSessionClosedError(error)) + throw new Error("Execution context was destroyed, most likely because of a navigation."); + throw error; +} +function potentiallyUnserializableValue2(remoteObject) { + const value2 = remoteObject.value; + const unserializableValue = remoteObject.unserializableValue; + return unserializableValue ? parseUnserializableValue(unserializableValue) : value2; +} +function renderPreview3(object) { + if (object.type === "undefined") + return "undefined"; + if (object.unserializableValue) + return String(object.unserializableValue); + if (object.type === "symbol") + return "Symbol()"; + if (object.subtype === "regexp") + return "RegExp"; + if (object.subtype === "weakmap") + return "WeakMap"; + if (object.subtype === "weakset") + return "WeakSet"; + if (object.subtype) + return object.subtype[0].toUpperCase() + object.subtype.slice(1); + if ("value" in object) + return String(object.value); +} +function createHandle3(context2, remoteObject) { + if (remoteObject.subtype === "node") { + assert(context2 instanceof FrameExecutionContext); + return new ElementHandle(context2, remoteObject.objectId); + } + return new JSHandle(context2, remoteObject.subtype || remoteObject.type || "", renderPreview3(remoteObject), remoteObject.objectId, potentiallyUnserializableValue2(remoteObject)); +} +var FFExecutionContext; +var init_ffExecutionContext = __esm({ + "packages/playwright-core/src/server/firefox/ffExecutionContext.ts"() { + "use strict"; + init_assert(); + init_stackTrace(); + init_utilityScriptSerializers(); + init_javascript(); + init_dom(); + init_protocolError(); + FFExecutionContext = class { + constructor(session2, executionContextId) { + this._session = session2; + this._executionContextId = executionContextId; + } + async rawEvaluateJSON(expression2) { + const payload = await this._session.send("Runtime.evaluate", { + expression: expression2, + returnByValue: true, + executionContextId: this._executionContextId + }).catch(rewriteError3); + checkException(payload.exceptionDetails); + return payload.result.value; + } + async rawEvaluateHandle(context2, expression2) { + const payload = await this._session.send("Runtime.evaluate", { + expression: expression2, + returnByValue: false, + executionContextId: this._executionContextId + }).catch(rewriteError3); + checkException(payload.exceptionDetails); + return createHandle3(context2, payload.result); + } + async evaluateWithArguments(expression2, returnByValue, utilityScript, values, handles) { + const payload = await this._session.send("Runtime.callFunction", { + functionDeclaration: expression2, + args: [ + { objectId: utilityScript._objectId, value: void 0 }, + ...values.map((value2) => ({ value: value2 })), + ...handles.map((handle) => ({ objectId: handle._objectId, value: void 0 })) + ], + returnByValue, + executionContextId: this._executionContextId + }).catch(rewriteError3); + checkException(payload.exceptionDetails); + if (returnByValue) + return parseEvaluationResultValue(payload.result.value); + return createHandle3(utilityScript._context, payload.result); + } + async getProperties(object) { + const response2 = await this._session.send("Runtime.getObjectProperties", { + executionContextId: this._executionContextId, + objectId: object._objectId + }); + const result2 = /* @__PURE__ */ new Map(); + for (const property of response2.properties) + result2.set(property.name, createHandle3(object._context, property.value)); + return result2; + } + async releaseHandle(handle) { + if (!handle._objectId) + return; + await this._session.send("Runtime.disposeObject", { + executionContextId: this._executionContextId, + objectId: handle._objectId + }); + } + shouldPrependErrorPrefix() { + return true; + } + }; + } +}); + +// packages/playwright-core/src/server/firefox/ffInput.ts +function toFirefoxKeyDescription(description) { + const override = kFirefoxKeyOverrides.get(description.key); + if (!override) + return description; + return { + ...description, + ...override + }; +} +function toModifiersMask2(modifiers) { + let mask = 0; + if (modifiers.has("Alt")) + mask |= 1; + if (modifiers.has("Control")) + mask |= 2; + if (modifiers.has("Shift")) + mask |= 4; + if (modifiers.has("Meta")) + mask |= 8; + return mask; +} +function toButtonNumber(button) { + if (button === "left") + return 0; + if (button === "middle") + return 1; + if (button === "right") + return 2; + return 0; +} +function toButtonsMask2(buttons) { + let mask = 0; + if (buttons.has("left")) + mask |= 1; + if (buttons.has("right")) + mask |= 2; + if (buttons.has("middle")) + mask |= 4; + return mask; +} +var kFirefoxKeyOverrides, RawKeyboardImpl3, RawMouseImpl3, RawTouchscreenImpl3; +var init_ffInput = __esm({ + "packages/playwright-core/src/server/firefox/ffInput.ts"() { + "use strict"; + kFirefoxKeyOverrides = /* @__PURE__ */ new Map([ + ["AudioVolumeMute", { code: "VolumeMute", keyCodeWithoutLocation: 181 }], + ["AudioVolumeDown", { code: "VolumeDown", keyCodeWithoutLocation: 182 }], + ["AudioVolumeUp", { code: "VolumeUp", keyCodeWithoutLocation: 183 }] + ]); + RawKeyboardImpl3 = class { + constructor(client) { + this._client = client; + } + async keydown(progress2, modifiers, keyName, description, autoRepeat) { + const keyDescription = toFirefoxKeyDescription(description); + let text2 = keyDescription.text; + if (text2 === "\r") + text2 = ""; + const { code, key, location: location2 } = keyDescription; + await progress2.race(this._client.send("Page.dispatchKeyEvent", { + type: "keydown", + keyCode: keyDescription.keyCodeWithoutLocation, + code, + key, + repeat: autoRepeat, + location: location2, + text: text2 + })); + } + async keyup(progress2, modifiers, keyName, description) { + const keyDescription = toFirefoxKeyDescription(description); + const { code, key, location: location2 } = keyDescription; + await progress2.race(this._client.send("Page.dispatchKeyEvent", { + type: "keyup", + key, + keyCode: keyDescription.keyCodeWithoutLocation, + code, + location: location2, + repeat: false + })); + } + async sendText(progress2, text2) { + await progress2.race(this._client.send("Page.insertText", { text: text2 })); + } + }; + RawMouseImpl3 = class { + constructor(client) { + this._client = client; + } + async move(progress2, x, y, button, buttons, modifiers, forClick) { + await progress2.race(this._client.send("Page.dispatchMouseEvent", { + type: "mousemove", + button: 0, + buttons: toButtonsMask2(buttons), + x: Math.floor(x), + y: Math.floor(y), + modifiers: toModifiersMask2(modifiers) + })); + } + async down(progress2, x, y, button, buttons, modifiers, clickCount) { + await progress2.race(this._client.send("Page.dispatchMouseEvent", { + type: "mousedown", + button: toButtonNumber(button), + buttons: toButtonsMask2(buttons), + x: Math.floor(x), + y: Math.floor(y), + modifiers: toModifiersMask2(modifiers), + clickCount + })); + } + async up(progress2, x, y, button, buttons, modifiers, clickCount) { + await progress2.race(this._client.send("Page.dispatchMouseEvent", { + type: "mouseup", + button: toButtonNumber(button), + buttons: toButtonsMask2(buttons), + x: Math.floor(x), + y: Math.floor(y), + modifiers: toModifiersMask2(modifiers), + clickCount + })); + } + async wheel(progress2, x, y, buttons, modifiers, deltaX, deltaY) { + await this._page.mainFrame().evaluateExpression(progress2, `new Promise(requestAnimationFrame)`, { world: "utility" }); + await progress2.race(this._client.send("Page.dispatchWheelEvent", { + deltaX, + deltaY, + x: Math.floor(x), + y: Math.floor(y), + deltaZ: 0, + modifiers: toModifiersMask2(modifiers) + })); + } + setPage(page) { + this._page = page; + } + }; + RawTouchscreenImpl3 = class { + constructor(client) { + this._client = client; + } + async tap(progress2, x, y, modifiers) { + await progress2.race(this._client.send("Page.dispatchTapEvent", { + x, + y, + modifiers: toModifiersMask2(modifiers) + })); + } + }; + } +}); + +// packages/playwright-core/src/server/firefox/ffNetworkManager.ts +function parseMultivalueHeaders(headers) { + const result2 = []; + for (const header of headers) { + const separator = header.name.toLowerCase() === "set-cookie" ? "\n" : ","; + const tokens = header.value.split(separator).map((s) => s.trim()); + for (const token of tokens) + result2.push({ name: header.name, value: token }); + } + return result2; +} +var FFNetworkManager, causeToResourceType, internalCauseToResourceType, InterceptableRequest2, FFRouteImpl; +var init_ffNetworkManager = __esm({ + "packages/playwright-core/src/server/firefox/ffNetworkManager.ts"() { + "use strict"; + init_eventsHelper(); + init_network2(); + FFNetworkManager = class { + constructor(session2, page) { + this._webSocketRequestIds = /* @__PURE__ */ new Set(); + this._session = session2; + this._requests = /* @__PURE__ */ new Map(); + this._page = page; + this._eventListeners = [ + eventsHelper.addEventListener(session2, "Network.requestWillBeSent", this._onRequestWillBeSent.bind(this)), + eventsHelper.addEventListener(session2, "Network.responseReceived", this._onResponseReceived.bind(this)), + eventsHelper.addEventListener(session2, "Network.requestFinished", this._onRequestFinished.bind(this)), + eventsHelper.addEventListener(session2, "Network.requestFailed", this._onRequestFailed.bind(this)) + ]; + } + dispose() { + eventsHelper.removeEventListeners(this._eventListeners); + } + async setRequestInterception(enabled) { + await Promise.all([ + this._session.send("Network.setRequestInterception", { enabled }), + this._session.send("Page.setCacheDisabled", { cacheDisabled: enabled }) + ]); + } + _onRequestWillBeSent(event) { + const redirectedFrom = event.redirectedFrom ? this._requests.get(event.redirectedFrom) || null : null; + const frame = redirectedFrom ? redirectedFrom.request.frame() : event.frameId ? this._page._page.frameManager.frame(event.frameId) : null; + if (!frame) + return; + if (event.method === "OPTIONS" && !event.isIntercepted) + return; + if (redirectedFrom) + this._requests.delete(redirectedFrom._id); + if (event.cause === "TYPE_WEBSOCKET") { + this._webSocketRequestIds.add(event.requestId); + this._page._onWebSocketRequestWillBeSent(event.requestId, event.url, event.headers); + return; + } + const request2 = new InterceptableRequest2(frame, redirectedFrom, event); + let route2; + if (event.isIntercepted) + route2 = new FFRouteImpl(this._session, request2); + this._requests.set(request2._id, request2); + this._page._page.frameManager.requestStarted(request2.request, route2); + } + _onResponseReceived(event) { + const request2 = this._requests.get(event.requestId); + if (!request2) { + if (this._webSocketRequestIds.has(event.requestId)) + this._page._onWebSocketResponseReceived(event.requestId, event.status, event.statusText, event.headers); + return; + } + const getResponseBody = async () => { + const response3 = await this._session.send("Network.getResponseBody", { + requestId: request2._id + }); + if (response3.evicted) + throw new Error(`Response body for ${request2.request.method()} ${request2.request.url()} was evicted!`); + return Buffer.from(response3.base64body, "base64"); + }; + const startTime = event.timing.startTime; + function relativeToStart(time) { + if (!time) + return -1; + return (time - startTime) / 1e3; + } + const timing = { + startTime: startTime / 1e3, + domainLookupStart: relativeToStart(event.timing.domainLookupStart), + domainLookupEnd: relativeToStart(event.timing.domainLookupEnd), + connectStart: relativeToStart(event.timing.connectStart), + secureConnectionStart: relativeToStart(event.timing.secureConnectionStart), + connectEnd: relativeToStart(event.timing.connectEnd), + requestStart: relativeToStart(event.timing.requestStart), + responseStart: relativeToStart(event.timing.responseStart) + }; + const response2 = new Response2(request2.request, event.status, event.statusText, parseMultivalueHeaders(event.headers), timing, getResponseBody, event.fromServiceWorker); + if (event?.remoteIPAddress && typeof event?.remotePort === "number") { + response2._serverAddrFinished({ + ipAddress: event.remoteIPAddress, + port: event.remotePort + }); + } else { + response2._serverAddrFinished(); + } + response2._securityDetailsFinished({ + protocol: event?.securityDetails?.protocol, + subjectName: event?.securityDetails?.subjectName, + issuer: event?.securityDetails?.issuer, + validFrom: event?.securityDetails?.validFrom, + validTo: event?.securityDetails?.validTo + }); + response2.setRawResponseHeaders(null); + response2.setResponseHeadersSize(null); + this._page._page.frameManager.requestReceivedResponse(response2); + } + _onRequestFinished(event) { + const request2 = this._requests.get(event.requestId); + if (!request2) { + if (this._webSocketRequestIds.has(event.requestId)) { + this._webSocketRequestIds.delete(event.requestId); + this._page._onWebSocketRequestFinished(event.requestId); + } + return; + } + const response2 = request2.request._existingResponse(); + response2.setTransferSize(event.transferSize); + response2.setEncodedBodySize(event.encodedBodySize); + const isRedirected = response2.status() >= 300 && response2.status() <= 399; + const responseEndTime = event.responseEndTime ? event.responseEndTime / 1e3 - response2.timing().startTime : -1; + if (isRedirected) { + response2._requestFinished(responseEndTime); + } else { + this._requests.delete(request2._id); + response2._requestFinished(responseEndTime); + } + response2._setHttpVersion(event.protocolVersion ?? null); + this._page._page.frameManager.reportRequestFinished(request2.request, response2); + } + _onRequestFailed(event) { + const request2 = this._requests.get(event.requestId); + if (!request2) + return; + this._requests.delete(request2._id); + const response2 = request2.request._existingResponse(); + if (response2) { + response2.setTransferSize(null); + response2.setEncodedBodySize(null); + response2._requestFinished(-1); + response2._setHttpVersion(null); + } + request2.request._setFailureText(event.errorCode); + this._page._page.frameManager.requestFailed(request2.request, event.errorCode === "NS_BINDING_ABORTED"); + } + }; + causeToResourceType = { + TYPE_INVALID: "other", + TYPE_OTHER: "other", + TYPE_SCRIPT: "script", + TYPE_IMAGE: "image", + TYPE_STYLESHEET: "stylesheet", + TYPE_OBJECT: "other", + TYPE_DOCUMENT: "document", + TYPE_SUBDOCUMENT: "document", + TYPE_REFRESH: "document", + TYPE_XBL: "other", + TYPE_PING: "other", + TYPE_XMLHTTPREQUEST: "xhr", + TYPE_OBJECT_SUBREQUEST: "other", + TYPE_DTD: "other", + TYPE_FONT: "font", + TYPE_MEDIA: "media", + TYPE_WEBSOCKET: "websocket", + TYPE_CSP_REPORT: "cspreport", + TYPE_XSLT: "other", + TYPE_BEACON: "beacon", + TYPE_FETCH: "fetch", + TYPE_IMAGESET: "image", + TYPE_WEB_MANIFEST: "manifest" + }; + internalCauseToResourceType = { + TYPE_INTERNAL_EVENTSOURCE: "eventsource" + }; + InterceptableRequest2 = class { + constructor(frame, redirectedFrom, payload) { + this._id = payload.requestId; + if (redirectedFrom) + redirectedFrom._redirectedTo = this; + let postDataBuffer = null; + if (payload.postData) + postDataBuffer = Buffer.from(payload.postData, "base64"); + this.request = new Request( + frame._page.browserContext, + frame, + null, + redirectedFrom ? redirectedFrom.request : null, + payload.navigationId, + payload.url, + internalCauseToResourceType[payload.internalCause] || causeToResourceType[payload.cause] || "other", + payload.method, + postDataBuffer, + payload.headers + ); + this.request.setRawRequestHeaders(null); + } + _finalRequest() { + let request2 = this; + while (request2._redirectedTo) + request2 = request2._redirectedTo; + return request2; + } + }; + FFRouteImpl = class { + constructor(session2, request2) { + this._session = session2; + this._request = request2; + } + async continue(overrides) { + await this._session.sendMayFail("Network.resumeInterceptedRequest", { + requestId: this._request._id, + url: overrides.url, + method: overrides.method, + headers: overrides.headers, + postData: overrides.postData ? Buffer.from(overrides.postData).toString("base64") : void 0 + }); + } + async fulfill(response2) { + const base64body = response2.isBase64 ? response2.body : Buffer.from(response2.body).toString("base64"); + await this._session.sendMayFail("Network.fulfillInterceptedRequest", { + requestId: this._request._id, + status: response2.status, + statusText: statusText(response2.status), + headers: response2.headers, + base64body + }); + } + async abort(errorCode) { + await this._session.sendMayFail("Network.abortInterceptedRequest", { + requestId: this._request._id, + errorCode + }); + } + }; + } +}); + +// packages/playwright-core/src/server/firefox/ffPage.ts +function webSocketId(frameId, wsid) { + return `${frameId}---${wsid}`; +} +var UTILITY_WORLD_NAME2, FFPage; +var init_ffPage = __esm({ + "packages/playwright-core/src/server/firefox/ffPage.ts"() { + "use strict"; + init_assert(); + init_stackTrace(); + init_eventsHelper(); + init_dialog(); + init_dom(); + init_network2(); + init_page(); + init_page(); + init_ffConnection(); + init_ffExecutionContext(); + init_ffInput(); + init_ffNetworkManager(); + init_errors(); + init_videoRecorder(); + UTILITY_WORLD_NAME2 = "__playwright_utility_world__"; + FFPage = class { + constructor(session2, browserContext, opener) { + this.cspErrorsAsynchronousForInlineScripts = true; + this._reportedAsNew = false; + this._workers = /* @__PURE__ */ new Map(); + this._initScripts = []; + this._webSocketRequests = /* @__PURE__ */ new Map(); + this._webSocketResponses = /* @__PURE__ */ new Map(); + this._session = session2; + this._opener = opener; + this.rawKeyboard = new RawKeyboardImpl3(session2); + this.rawMouse = new RawMouseImpl3(session2); + this.rawTouchscreen = new RawTouchscreenImpl3(session2); + this._contextIdToContext = /* @__PURE__ */ new Map(); + this._browserContext = browserContext; + this._page = new Page(this, browserContext); + this.rawMouse.setPage(this._page); + this._networkManager = new FFNetworkManager(session2, this); + this._page.on(Page.Events.FrameDetached, (frame) => this._removeContextsForFrame(frame)); + this._eventListeners = [ + eventsHelper.addEventListener(this._session, "Page.eventFired", this._onEventFired.bind(this)), + eventsHelper.addEventListener(this._session, "Page.frameAttached", this._onFrameAttached.bind(this)), + eventsHelper.addEventListener(this._session, "Page.frameDetached", this._onFrameDetached.bind(this)), + eventsHelper.addEventListener(this._session, "Page.navigationAborted", this._onNavigationAborted.bind(this)), + eventsHelper.addEventListener(this._session, "Page.navigationCommitted", this._onNavigationCommitted.bind(this)), + eventsHelper.addEventListener(this._session, "Page.navigationStarted", this._onNavigationStarted.bind(this)), + eventsHelper.addEventListener(this._session, "Page.sameDocumentNavigation", this._onSameDocumentNavigation.bind(this)), + eventsHelper.addEventListener(this._session, "Runtime.executionContextCreated", this._onExecutionContextCreated.bind(this)), + eventsHelper.addEventListener(this._session, "Runtime.executionContextDestroyed", this._onExecutionContextDestroyed.bind(this)), + eventsHelper.addEventListener(this._session, "Runtime.executionContextsCleared", this._onExecutionContextsCleared.bind(this)), + eventsHelper.addEventListener(this._session, "Page.linkClicked", (event) => this._onLinkClicked(event.phase)), + eventsHelper.addEventListener(this._session, "Page.uncaughtError", this._onUncaughtError.bind(this)), + eventsHelper.addEventListener(this._session, "Runtime.console", this._onConsole.bind(this)), + eventsHelper.addEventListener(this._session, "Page.dialogOpened", this._onDialogOpened.bind(this)), + eventsHelper.addEventListener(this._session, "Page.bindingCalled", this._onBindingCalled.bind(this)), + eventsHelper.addEventListener(this._session, "Page.fileChooserOpened", this._onFileChooserOpened.bind(this)), + eventsHelper.addEventListener(this._session, "Page.workerCreated", this._onWorkerCreated.bind(this)), + eventsHelper.addEventListener(this._session, "Page.workerDestroyed", this._onWorkerDestroyed.bind(this)), + eventsHelper.addEventListener(this._session, "Page.dispatchMessageFromWorker", this._onDispatchMessageFromWorker.bind(this)), + eventsHelper.addEventListener(this._session, "Page.crashed", this._onCrashed.bind(this)), + eventsHelper.addEventListener(this._session, "Page.webSocketCreated", this._onWebSocketCreated.bind(this)), + eventsHelper.addEventListener(this._session, "Page.webSocketOpened", this._onWebSocketOpened.bind(this)), + eventsHelper.addEventListener(this._session, "Page.webSocketClosed", this._onWebSocketClosed.bind(this)), + eventsHelper.addEventListener(this._session, "Page.webSocketFrameReceived", this._onWebSocketFrameReceived.bind(this)), + eventsHelper.addEventListener(this._session, "Page.webSocketFrameSent", this._onWebSocketFrameSent.bind(this)), + eventsHelper.addEventListener(this._session, "Page.screencastFrame", this._onScreencastFrame.bind(this)) + ]; + const promises = []; + if (!this._page.isStorageStatePage) + startAutomaticVideoRecording(this._page); + promises.push(new Promise((f) => this._session.once("Page.ready", f))); + Promise.all(promises).then(() => this._reportAsNew(), (error) => this._reportAsNew(error)); + this.addInitScript(new InitScript(this._page, ""), UTILITY_WORLD_NAME2).catch((e) => this._reportAsNew(e)); + } + _reportAsNew(error) { + if (this._reportedAsNew) + return; + this._reportedAsNew = true; + this._page.reportAsNew(this._opener?._page, error); + } + _onWebSocketCreated(event) { + this._page.frameManager.onWebSocketCreated(webSocketId(event.frameId, event.wsid), event.requestURL); + } + _onWebSocketRequestWillBeSent(requestId, url2, headers) { + this._webSocketRequests.set(requestId, { url: url2, headers }); + } + _onWebSocketResponseReceived(requestId, status, statusText2, headers) { + this._webSocketResponses.set(requestId, { status, statusText: statusText2, headers }); + } + _onWebSocketRequestFinished(requestId) { + const response2 = this._webSocketResponses.get(requestId); + assert(response2); + if (response2.status >= 400) { + const request2 = this._webSocketRequests.get(requestId); + assert(request2); + this._webSocketRequests.delete(requestId); + this._webSocketResponses.delete(requestId); + const url2 = parseURL2(request2.url); + assert(url2); + url2.protocol = url2.protocol === "https" ? "wss" : "ws"; + this._page.frameManager.onWebSocketCreated(requestId, url2.toString()); + this._page.frameManager.onWebSocketRequest(requestId, request2.headers); + this._page.frameManager.onWebSocketResponse(requestId, response2.status, response2.statusText, response2.headers); + this._page.frameManager.webSocketClosed(requestId); + return; + } + } + _onWebSocketOpened(event) { + const request2 = this._webSocketRequests.get(event.requestId); + assert(request2); + const response2 = this._webSocketResponses.get(event.requestId); + assert(response2); + this._webSocketRequests.delete(event.requestId); + this._webSocketResponses.delete(event.requestId); + this._page.frameManager.onWebSocketRequest(webSocketId(event.frameId, event.wsid), request2.headers); + this._page.frameManager.onWebSocketResponse(webSocketId(event.frameId, event.wsid), response2.status, response2.statusText, response2.headers); + } + _onWebSocketClosed(event) { + if (event.error) + this._page.frameManager.webSocketError(webSocketId(event.frameId, event.wsid), event.error); + this._page.frameManager.webSocketClosed(webSocketId(event.frameId, event.wsid)); + } + _onWebSocketFrameReceived(event) { + this._page.frameManager.webSocketFrameReceived(webSocketId(event.frameId, event.wsid), event.opcode, event.data, event.timestamp * 1e3); + } + _onWebSocketFrameSent(event) { + this._page.frameManager.onWebSocketFrameSent(webSocketId(event.frameId, event.wsid), event.opcode, event.data, event.timestamp * 1e3); + } + _onExecutionContextCreated(payload) { + const { executionContextId, auxData } = payload; + const frame = this._page.frameManager.frame(auxData.frameId); + if (!frame) + return; + const delegate = new FFExecutionContext(this._session, executionContextId); + let worldName = null; + if (auxData.name === UTILITY_WORLD_NAME2) + worldName = "utility"; + else if (!auxData.name) + worldName = "main"; + const context2 = new FrameExecutionContext(delegate, frame, worldName); + if (worldName) + frame.contextCreated(worldName, context2); + this._contextIdToContext.set(executionContextId, context2); + } + _onExecutionContextDestroyed(payload) { + const { executionContextId } = payload; + const context2 = this._contextIdToContext.get(executionContextId); + if (!context2) + return; + this._contextIdToContext.delete(executionContextId); + context2.frame.contextDestroyed(context2); + } + _onExecutionContextsCleared() { + for (const executionContextId of Array.from(this._contextIdToContext.keys())) + this._onExecutionContextDestroyed({ executionContextId }); + } + _removeContextsForFrame(frame) { + for (const [contextId4, context2] of this._contextIdToContext) { + if (context2.frame === frame) + this._contextIdToContext.delete(contextId4); + } + } + _onLinkClicked(phase) { + if (phase === "before") + this._page.frameManager.frameWillPotentiallyRequestNavigation(); + else + this._page.frameManager.frameDidPotentiallyRequestNavigation(); + } + _onNavigationStarted(params2) { + this._page.frameManager.frameRequestedNavigation(params2.frameId, params2.navigationId); + } + _onNavigationAborted(params2) { + this._page.frameManager.frameAbortedNavigation(params2.frameId, params2.errorText, params2.navigationId); + } + _onNavigationCommitted(params2) { + for (const [workerId, worker] of this._workers) { + if (worker.frameId === params2.frameId) + this._onWorkerDestroyed({ workerId }); + } + this._page.frameManager.frameCommittedNewDocumentNavigation(params2.frameId, params2.url, params2.name || "", params2.navigationId || "", false); + } + _onSameDocumentNavigation(params2) { + this._page.frameManager.frameCommittedSameDocumentNavigation(params2.frameId, params2.url); + } + _onFrameAttached(params2) { + this._page.frameManager.frameAttached(params2.frameId, params2.parentFrameId); + } + _onFrameDetached(params2) { + this._page.frameManager.frameDetached(params2.frameId); + } + _onEventFired(payload) { + const { frameId, name } = payload; + if (name === "load") + this._page.frameManager.frameLifecycleEvent(frameId, "load"); + if (name === "DOMContentLoaded") + this._page.frameManager.frameLifecycleEvent(frameId, "domcontentloaded"); + } + _onUncaughtError(params2) { + const { name, message } = splitErrorMessage(params2.message); + const error = new Error(message); + error.stack = params2.message + "\n" + params2.stack.split("\n").filter(Boolean).map((a) => a.replace(/([^@]*)@(.*)/, " at $1 ($2)")).join("\n"); + error.name = name; + this._page.addPageError(error, params2.location); + } + _onConsole(payload) { + const { type: type3, args, executionContextId, location: location2 } = payload; + const context2 = this._contextIdToContext.get(executionContextId); + if (!context2) + return; + const timestamp = Date.now(); + this._page.addConsoleMessage(null, type3 === "warn" ? "warning" : type3, args.map((arg) => createHandle3(context2, arg)), location2, void 0, timestamp); + } + _onDialogOpened(params2) { + this._page.browserContext.dialogManager.dialogDidOpen(new Dialog( + this._page, + params2.type, + params2.message, + async (accept, promptText) => { + await this._session.sendMayFail("Page.handleDialog", { dialogId: params2.dialogId, accept, promptText }); + }, + params2.defaultValue + )); + } + async _onBindingCalled(event) { + const pageOrError = await this._page.waitForInitializedOrError(); + if (!(pageOrError instanceof Error)) { + const context2 = this._contextIdToContext.get(event.executionContextId); + if (context2) + await this._page.onBindingCalled(event.payload, context2); + } + } + async _onFileChooserOpened(payload) { + const { executionContextId, element: element2 } = payload; + const context2 = this._contextIdToContext.get(executionContextId); + if (!context2) + return; + const handle = createHandle3(context2, element2).asElement(); + await this._page._onFileChooserOpened(handle); + } + async _onWorkerCreated(event) { + const workerId = event.workerId; + const worker = new Worker(this._page, event.url); + const workerSession = new FFSession(this._session._connection, workerId, (message) => { + this._session.send("Page.sendMessageToWorker", { + frameId: event.frameId, + workerId, + message: JSON.stringify(message) + }).catch((e) => { + workerSession.dispatchMessage({ id: message.id, method: "", params: {}, error: { message: e.message, data: void 0 } }); + }); + }); + this._workers.set(workerId, { session: workerSession, frameId: event.frameId }); + this._page.addWorker(workerId, worker); + workerSession.once("Runtime.executionContextCreated", (event2) => { + worker.createExecutionContext(new FFExecutionContext(workerSession, event2.executionContextId)); + worker.workerScriptLoaded(); + }); + workerSession.on("Runtime.console", (event2) => { + const { type: type3, args, location: location2 } = event2; + const context2 = worker.existingExecutionContext; + this._page.addConsoleMessage(worker, type3, args.map((arg) => createHandle3(context2, arg)), location2, void 0, Date.now()); + }); + } + _onWorkerDestroyed(event) { + const workerId = event.workerId; + const worker = this._workers.get(workerId); + if (!worker) + return; + worker.session.dispose(); + this._workers.delete(workerId); + this._page.removeWorker(workerId); + } + async _onDispatchMessageFromWorker(event) { + const worker = this._workers.get(event.workerId); + if (!worker) + return; + worker.session.dispatchMessage(JSON.parse(event.message)); + } + async _onCrashed(event) { + this._session.markAsCrashed(); + this._page._didCrash(); + } + didClose() { + this._reportAsNew(new TargetClosedError(this._page.closeReason())); + this._session.dispose(); + eventsHelper.removeEventListeners(this._eventListeners); + this._networkManager.dispose(); + this._page._didClose(); + } + async navigateFrame(frame, url2, referer) { + const response2 = await this._session.send("Page.navigate", { url: url2, referer, frameId: frame._id }); + return { newDocumentId: response2.navigationId || void 0 }; + } + async updateExtraHTTPHeaders() { + await this._session.send("Network.setExtraHTTPHeaders", { headers: this._page.extraHTTPHeaders() || [] }); + } + async updateEmulatedViewportSize() { + const emulatedSize = this._page.emulatedSize(); + await this._session.send("Page.setViewportSize", { + viewportSize: emulatedSize?.viewport ?? null, + screenSize: emulatedSize?.screen, + isMobile: !!this._browserContext._options.isMobile + }); + } + async bringToFront() { + await this._session.send("Page.bringToFront", {}); + } + async updateEmulateMedia() { + const emulatedMedia = this._page.emulatedMedia(); + const colorScheme = emulatedMedia.colorScheme === "no-override" ? void 0 : emulatedMedia.colorScheme; + const reducedMotion = emulatedMedia.reducedMotion === "no-override" ? void 0 : emulatedMedia.reducedMotion; + const forcedColors = emulatedMedia.forcedColors === "no-override" ? void 0 : emulatedMedia.forcedColors; + const contrast = emulatedMedia.contrast === "no-override" ? void 0 : emulatedMedia.contrast; + await this._session.send("Page.setEmulatedMedia", { + // Empty string means reset. + type: emulatedMedia.media === "no-override" ? "" : emulatedMedia.media, + colorScheme, + reducedMotion, + forcedColors, + contrast + }); + } + async updateRequestInterception() { + await this._networkManager.setRequestInterception(this._page.needsRequestInterception()); + } + async updateFileChooserInterception() { + const enabled = this._page.fileChooserIntercepted(); + await this._session.send("Page.setInterceptFileChooserDialog", { enabled }).catch(() => { + }); + } + async reload() { + await this._session.send("Page.reload"); + } + async goBack() { + const { success } = await this._session.send("Page.goBack", { frameId: this._page.mainFrame()._id }); + return success; + } + async goForward() { + const { success } = await this._session.send("Page.goForward", { frameId: this._page.mainFrame()._id }); + return success; + } + async requestGC() { + await this._session.send("Heap.collectGarbage"); + } + async addInitScript(initScript, worldName) { + this._initScripts.push({ initScript, worldName }); + await this._updateInitScripts(); + } + async removeInitScripts(initScripts) { + const set = new Set(initScripts); + this._initScripts = this._initScripts.filter((s) => !set.has(s.initScript)); + await this._updateInitScripts(); + } + async _updateInitScripts() { + await this._session.send("Page.setInitScripts", { scripts: this._initScripts.map((s) => ({ script: s.initScript.source, worldName: s.worldName })) }); + } + async closePage(runBeforeUnload) { + await this._session.send("Page.close", { runBeforeUnload }); + } + async setBackgroundColor(color) { + if (color) + throw new Error("Not implemented"); + } + async takeScreenshot(progress2, format2, documentRect, viewportRect, quality, fitsViewport, scale) { + if (!documentRect) { + const scrollOffset = await this._page.mainFrame().waitForFunctionValueInUtility(progress2, () => ({ x: window.scrollX, y: window.scrollY })); + documentRect = { + x: viewportRect.x + scrollOffset.x, + y: viewportRect.y + scrollOffset.y, + width: viewportRect.width, + height: viewportRect.height + }; + } + const { data } = await progress2.race(this._session.send("Page.screenshot", { + mimeType: "image/" + format2, + clip: documentRect, + quality, + omitDeviceScaleFactor: scale === "css" + })); + return Buffer.from(data, "base64"); + } + async getContentFrame(handle) { + const { contentFrameId } = await this._session.send("Page.describeNode", { + frameId: handle._context.frame._id, + objectId: handle._objectId + }); + if (!contentFrameId) + return null; + return this._page.frameManager.frame(contentFrameId); + } + async getOwnerFrame(handle) { + const { ownerFrameId } = await this._session.send("Page.describeNode", { + frameId: handle._context.frame._id, + objectId: handle._objectId + }); + return ownerFrameId || null; + } + async getBoundingBox(handle) { + const quads = await this.getContentQuads(handle); + if (!quads || !quads.length) + return null; + let minX = Infinity; + let maxX = -Infinity; + let minY = Infinity; + let maxY = -Infinity; + for (const quad of quads) { + for (const point of quad) { + minX = Math.min(minX, point.x); + maxX = Math.max(maxX, point.x); + minY = Math.min(minY, point.y); + maxY = Math.max(maxY, point.y); + } + } + return { x: minX, y: minY, width: maxX - minX, height: maxY - minY }; + } + async scrollRectIntoViewIfNeeded(handle, rect) { + return await this._session.send("Page.scrollIntoViewIfNeeded", { + frameId: handle._context.frame._id, + objectId: handle._objectId, + rect + }).then(() => "done").catch((e) => { + if (e instanceof Error && e.message.includes("Node is detached from document")) + return "error:notconnected"; + if (e instanceof Error && e.message.includes("Node does not have a layout object")) + return "error:notvisible"; + throw e; + }); + } + startScreencast(options) { + this._session.sendMayFail("Page.startScreencast", { width: options.width, height: options.height, quality: options.quality }); + } + stopScreencast() { + this._session.sendMayFail("Page.stopScreencast"); + } + _onScreencastFrame(event) { + const buffer = Buffer.from(event.data, "base64"); + this._page.screencast.onScreencastFrame({ + buffer, + frameSwapWallTime: event.timestamp * 1e3, + // timestamp is in seconds, we need to convert to milliseconds. + viewportWidth: event.deviceWidth, + viewportHeight: event.deviceHeight + }, () => { + this._session.sendMayFail("Page.screencastFrameAck"); + }); + } + rafCountForStablePosition() { + return 1; + } + async getContentQuads(handle) { + const result2 = await this._session.sendMayFail("Page.getContentQuads", { + frameId: handle._context.frame._id, + objectId: handle._objectId + }); + if (!result2) + return null; + return result2.quads.map((quad) => [quad.p1, quad.p2, quad.p3, quad.p4]); + } + async setInputFilePaths(progress2, handle, files) { + await progress2.race(this._session.send("Page.setFileInputFiles", { + frameId: handle._context.frame._id, + objectId: handle._objectId, + files + })); + } + async adoptElementHandle(handle, to) { + const result2 = await this._session.send("Page.adoptNode", { + frameId: handle._context.frame._id, + objectId: handle._objectId, + executionContextId: to.delegate._executionContextId + }); + if (!result2.remoteObject) + throw new Error(kUnableToAdoptErrorMessage); + return createHandle3(to, result2.remoteObject); + } + async inputActionEpilogue() { + } + async resetForReuse(progress2) { + await this.rawMouse.move(progress2, -1, -1, "none", /* @__PURE__ */ new Set(), /* @__PURE__ */ new Set(), false); + } + async getFrameElement(frame) { + const parent = frame.parentFrame(); + if (!parent) + throw new Error("Frame has been detached."); + const context2 = await parent.mainContext(); + const result2 = await this._session.send("Page.adoptNode", { + frameId: frame._id, + executionContextId: context2.delegate._executionContextId + }); + if (!result2.remoteObject) + throw new Error("Frame has been detached."); + return createHandle3(context2, result2.remoteObject); + } + shouldToggleStyleSheetToSyncAnimations() { + return false; + } + async setDockTile(image) { + } + }; + } +}); + +// packages/playwright-core/src/server/firefox/ffBrowser.ts +function toJugglerProxyOptions(proxy) { + const proxyServer = new URL(proxy.server); + let port = parseInt(proxyServer.port, 10); + let type3 = "http"; + if (proxyServer.protocol === "socks5:") + type3 = "socks"; + else if (proxyServer.protocol === "socks4:") + type3 = "socks4"; + else if (proxyServer.protocol === "https:") + type3 = "https"; + if (proxyServer.port === "") { + if (proxyServer.protocol === "http:") + port = 80; + else if (proxyServer.protocol === "https:") + port = 443; + } + return { + type: type3, + bypass: proxy.bypass ? proxy.bypass.split(",").map((domain) => domain.trim()) : [], + host: proxyServer.hostname, + port, + username: proxy.username, + password: proxy.password + }; +} +var FFBrowser, FFBrowserContext, kBandaidFirefoxUserPrefs; +var init_ffBrowser = __esm({ + "packages/playwright-core/src/server/firefox/ffBrowser.ts"() { + "use strict"; + init_assert(); + init_browser(); + init_browserContext(); + init_network2(); + init_ffConnection(); + init_ffPage(); + init_page(); + FFBrowser = class _FFBrowser extends Browser { + constructor(parent, connection, options) { + super(parent, options); + this._version = ""; + this._userAgent = ""; + this._connection = connection; + this.session = connection.rootSession; + this._ffPages = /* @__PURE__ */ new Map(); + this._contexts = /* @__PURE__ */ new Map(); + this._connection.on(ConnectionEvents2.Disconnected, () => this._onDisconnect()); + this.session.on("Browser.attachedToTarget", this._onAttachedToTarget.bind(this)); + this.session.on("Browser.detachedFromTarget", this._onDetachedFromTarget.bind(this)); + this.session.on("Browser.downloadCreated", this._onDownloadCreated.bind(this)); + this.session.on("Browser.downloadFinished", this._onDownloadFinished.bind(this)); + } + static async connect(parent, transport, options) { + const connection = new FFConnection(transport, options.protocolLogger, options.browserLogsCollector); + const browser = new _FFBrowser(parent, connection, options); + if (options.__testHookOnConnectToBrowser) + await options.__testHookOnConnectToBrowser(); + let firefoxUserPrefs = options.originalLaunchOptions.firefoxUserPrefs ?? {}; + if (Object.keys(kBandaidFirefoxUserPrefs).length) + firefoxUserPrefs = { ...kBandaidFirefoxUserPrefs, ...firefoxUserPrefs }; + const promises = [ + browser.session.send("Browser.enable", { + attachToDefaultContext: !!options.persistent, + userPrefs: Object.entries(firefoxUserPrefs).map(([name, value2]) => ({ name, value: value2 })) + }), + browser._initVersion() + ]; + if (options.persistent) { + browser._defaultContext = new FFBrowserContext(browser, void 0, options.persistent); + promises.push(browser._defaultContext.initialize()); + } + const proxy = options.originalLaunchOptions.proxyOverride || options.proxy; + if (proxy) + promises.push(browser.session.send("Browser.setBrowserProxy", toJugglerProxyOptions(proxy))); + await Promise.all(promises); + return browser; + } + async _initVersion() { + const result2 = await this.session.send("Browser.getInfo"); + this._version = result2.version.substring(result2.version.indexOf("/") + 1); + this._userAgent = result2.userAgent; + } + isConnected() { + return !this._connection._closed; + } + async doCreateNewContext(options) { + const { browserContextId } = await this.session.send("Browser.createBrowserContext", { removeOnDetach: true }); + const context2 = new FFBrowserContext(this, browserContextId, options); + await context2.initialize(); + this._contexts.set(browserContextId, context2); + return context2; + } + contexts() { + return Array.from(this._contexts.values()); + } + version() { + return this._version; + } + userAgent() { + return this._userAgent; + } + _onDetachedFromTarget(payload) { + const ffPage = this._ffPages.get(payload.targetId); + this._ffPages.delete(payload.targetId); + ffPage.didClose(); + } + _onAttachedToTarget(payload) { + const { targetId, browserContextId, openerId, type: type3 } = payload.targetInfo; + assert(type3 === "page"); + const context2 = browserContextId ? this._contexts.get(browserContextId) : this._defaultContext; + assert(context2, `Unknown context id:${browserContextId}, _defaultContext: ${this._defaultContext}`); + const session2 = this._connection.createSession(payload.sessionId); + const opener = openerId ? this._ffPages.get(openerId) : null; + const ffPage = new FFPage(session2, context2, opener); + this._ffPages.set(targetId, ffPage); + } + _onDownloadCreated(payload) { + const ffPage = this._ffPages.get(payload.pageTargetId); + if (!ffPage) + return; + ffPage._page.frameManager.frameAbortedNavigation(payload.frameId, "Download is starting"); + let originPage = ffPage._page.initializedOrUndefined(); + if (!originPage) { + ffPage._reportAsNew(new Error("Starting new page download")); + if (ffPage._opener) + originPage = ffPage._opener._page.initializedOrUndefined(); + } + if (!originPage) + return; + this.downloadCreated(originPage, payload.uuid, payload.url, payload.suggestedFileName); + } + _onDownloadFinished(payload) { + const error = payload.canceled ? "canceled" : payload.error; + this.downloadFinished(payload.uuid, error); + } + _onDisconnect() { + for (const ffPage of this._ffPages.values()) + ffPage.didClose(); + this._ffPages.clear(); + this.didClose(); + } + }; + FFBrowserContext = class extends BrowserContext { + constructor(browser, browserContextId, options) { + super(browser, options, browserContextId); + } + async initialize() { + assert(!this._ffPages().length); + const browserContextId = this._browserContextId; + const promises = [ + super.initialize(), + this._updateInitScripts() + ]; + if (this._options.acceptDownloads !== "internal-browser-default") { + promises.push(this._browser.session.send("Browser.setDownloadOptions", { + browserContextId, + downloadOptions: { + behavior: this._options.acceptDownloads === "accept" ? "saveToDisk" : "cancel", + downloadsDir: this._browser.options.downloadsPath + } + })); + } + promises.push(this.doUpdateDefaultViewport()); + if (this._options.hasTouch) + promises.push(this._browser.session.send("Browser.setTouchOverride", { browserContextId, hasTouch: true })); + if (this._options.userAgent) + promises.push(this._browser.session.send("Browser.setUserAgentOverride", { browserContextId, userAgent: this._options.userAgent })); + if (this._options.bypassCSP) + promises.push(this._browser.session.send("Browser.setBypassCSP", { browserContextId, bypassCSP: true })); + if (this._options.ignoreHTTPSErrors || this._options.internalIgnoreHTTPSErrors) + promises.push(this._browser.session.send("Browser.setIgnoreHTTPSErrors", { browserContextId, ignoreHTTPSErrors: true })); + if (this._options.javaScriptEnabled === false) + promises.push(this._browser.session.send("Browser.setJavaScriptDisabled", { browserContextId, javaScriptDisabled: true })); + if (this._options.locale) + promises.push(this._browser.session.send("Browser.setLocaleOverride", { browserContextId, locale: this._options.locale })); + if (this._options.timezoneId) + promises.push(this._browser.session.send("Browser.setTimezoneOverride", { browserContextId, timezoneId: this._options.timezoneId })); + if (this._options.extraHTTPHeaders || this._options.locale) + promises.push(this.doUpdateExtraHTTPHeaders()); + if (this._options.httpCredentials) + promises.push(this.innerSetHTTPCredentials(this._options.httpCredentials)); + if (this._options.geolocation) + promises.push(this.setGeolocation(this._options.geolocation)); + if (this._options.offline) + promises.push(this.doUpdateOffline()); + promises.push(this.doUpdateDefaultEmulatedMedia()); + const proxy = this._options.proxyOverride || this._options.proxy; + if (proxy) { + promises.push(this._browser.session.send("Browser.setContextProxy", { + browserContextId: this._browserContextId, + ...toJugglerProxyOptions(proxy) + })); + } + await Promise.all(promises); + } + _ffPages() { + return Array.from(this._browser._ffPages.values()).filter((ffPage) => ffPage._browserContext === this); + } + possiblyUninitializedPages() { + return this._ffPages().map((ffPage) => ffPage._page); + } + async doCreateNewPage() { + const { targetId } = await this._browser.session.send("Browser.newPage", { + browserContextId: this._browserContextId + }).catch((e) => { + if (e.message.includes("Failed to override timezone")) + throw new Error(`Invalid timezone ID: ${this._options.timezoneId}`); + throw e; + }); + return this._browser._ffPages.get(targetId)._page; + } + async doGetCookies(urls) { + const { cookies } = await this._browser.session.send("Browser.getCookies", { browserContextId: this._browserContextId }); + return filterCookies(cookies.map((c) => { + const { name, value: value2, domain, path: path59, expires, httpOnly, secure, sameSite } = c; + return { + name, + value: value2, + domain, + path: path59, + expires, + httpOnly, + secure, + sameSite + }; + }), urls); + } + async addCookies(cookies) { + const cc = rewriteCookies(cookies).map((c) => { + const { name, value: value2, url: url2, domain, path: path59, expires, httpOnly, secure, sameSite } = c; + return { + name, + value: value2, + url: url2, + domain, + path: path59, + expires: expires === -1 ? void 0 : expires, + httpOnly, + secure, + sameSite + }; + }); + await this._browser.session.send("Browser.setCookies", { browserContextId: this._browserContextId, cookies: cc }); + } + async doClearCookies() { + await this._browser.session.send("Browser.clearCookies", { browserContextId: this._browserContextId }); + } + async doGrantPermissions(origin, permissions) { + const webPermissionToProtocol = /* @__PURE__ */ new Map([ + ["geolocation", "geo"], + ["persistent-storage", "persistent-storage"], + ["push", "push"], + ["notifications", "desktop-notification"], + ["screen-wake-lock", "screen-wake-lock"] + ]); + const filtered = permissions.map((permission) => { + const protocolPermission = webPermissionToProtocol.get(permission); + if (!protocolPermission) + throw new Error("Unknown permission: " + permission); + return protocolPermission; + }); + await this._browser.session.send("Browser.grantPermissions", { origin, browserContextId: this._browserContextId, permissions: filtered }); + } + async doClearPermissions() { + await this._browser.session.send("Browser.resetPermissions", { browserContextId: this._browserContextId }); + } + async setGeolocation(geolocation) { + verifyGeolocation(geolocation); + this._options.geolocation = geolocation; + await this._browser.session.send("Browser.setGeolocationOverride", { browserContextId: this._browserContextId, geolocation: geolocation || null }); + } + async doUpdateExtraHTTPHeaders() { + let allHeaders = this._options.extraHTTPHeaders || []; + if (this._options.locale) + allHeaders = mergeHeaders([allHeaders, singleHeader("Accept-Language", this._options.locale)]); + await this._browser.session.send("Browser.setExtraHTTPHeaders", { browserContextId: this._browserContextId, headers: allHeaders }); + } + async setUserAgent(userAgent) { + await this._browser.session.send("Browser.setUserAgentOverride", { browserContextId: this._browserContextId, userAgent: userAgent || null }); + } + async doUpdateOffline() { + await this._browser.session.send("Browser.setOnlineOverride", { browserContextId: this._browserContextId, override: this._options.offline ? "offline" : "online" }); + } + async doSetHTTPCredentials(httpCredentials) { + this._options.httpCredentials = httpCredentials; + let credentials = null; + if (httpCredentials) { + const { username, password, origin } = httpCredentials; + credentials = { username, password, origin }; + } + await this._browser.session.send("Browser.setHTTPCredentials", { browserContextId: this._browserContextId, credentials }); + } + async doAddInitScript(initScript) { + await this._updateInitScripts(); + } + async doRemoveInitScripts(initScripts) { + await this._updateInitScripts(); + } + async _updateInitScripts() { + const bindingScripts = [...this._pageBindings.values()].map((binding) => binding.initScript.source); + if (this.bindingsInitScript) + bindingScripts.unshift(this.bindingsInitScript.source); + const initScripts = this.initScripts.map((script) => script.source); + await this._browser.session.send("Browser.setInitScripts", { browserContextId: this._browserContextId, scripts: [...bindingScripts, ...initScripts].map((script) => ({ script })) }); + } + async doUpdateRequestInterception() { + await Promise.all([ + this._browser.session.send("Browser.setRequestInterception", { browserContextId: this._browserContextId, enabled: this.requestInterceptors.length > 0 }), + this._browser.session.send("Browser.setCacheDisabled", { browserContextId: this._browserContextId, cacheDisabled: this.requestInterceptors.length > 0 }) + ]); + } + async doUpdateDefaultViewport() { + if (!this._options.viewport) + return; + const viewport = { + viewportSize: { width: this._options.viewport.width, height: this._options.viewport.height }, + screenSize: this._options.screen, + deviceScaleFactor: this._options.deviceScaleFactor || 1, + isMobile: !!this._options.isMobile + }; + await this._browser.session.send("Browser.setDefaultViewport", { browserContextId: this._browserContextId, viewport }); + } + async doUpdateDefaultEmulatedMedia() { + if (this._options.colorScheme !== "no-override") { + await this._browser.session.send("Browser.setColorScheme", { + browserContextId: this._browserContextId, + colorScheme: this._options.colorScheme !== void 0 ? this._options.colorScheme : "light" + }); + } + if (this._options.reducedMotion !== "no-override") { + await this._browser.session.send("Browser.setReducedMotion", { + browserContextId: this._browserContextId, + reducedMotion: this._options.reducedMotion !== void 0 ? this._options.reducedMotion : "no-preference" + }); + } + if (this._options.forcedColors !== "no-override") { + await this._browser.session.send("Browser.setForcedColors", { + browserContextId: this._browserContextId, + forcedColors: this._options.forcedColors !== void 0 ? this._options.forcedColors : "none" + }); + } + if (this._options.contrast !== "no-override") { + await this._browser.session.send("Browser.setContrast", { + browserContextId: this._browserContextId, + contrast: this._options.contrast !== void 0 ? this._options.contrast : "no-preference" + }); + } + } + async doExposePlaywrightBinding() { + this._browser.session.send("Browser.addBinding", { browserContextId: this._browserContextId, name: PageBinding.kBindingName, script: "" }); + } + onClosePersistent() { + } + async clearCache() { + await this._browser.session.send("Browser.clearCache"); + } + async doClose(reason) { + if (!this._browserContextId) { + return "close-browser"; + } else { + await this._browser.session.send("Browser.removeBrowserContext", { browserContextId: this._browserContextId }); + this._browser._contexts.delete(this._browserContextId); + } + } + async cancelDownload(uuid) { + await this._browser.session.send("Browser.cancelDownload", { uuid }); + } + }; + kBandaidFirefoxUserPrefs = {}; + } +}); + +// packages/playwright-core/src/server/firefox/firefox.ts +var import_os15, import_path29, Firefox; +var init_firefox = __esm({ + "packages/playwright-core/src/server/firefox/firefox.ts"() { + "use strict"; + import_os15 = __toESM(require("os")); + import_path29 = __toESM(require("path")); + init_manualPromise(); + init_ascii(); + init_ffBrowser(); + init_ffConnection(); + init_browserType(); + Firefox = class extends BrowserType { + constructor(parent, bidiFirefox) { + super(parent, "firefox"); + this._bidiFirefox = bidiFirefox; + } + launch(progress2, options, protocolLogger) { + if (options.channel?.startsWith("moz-")) + return this._bidiFirefox.launch(progress2, options, protocolLogger); + return super.launch(progress2, options, protocolLogger); + } + async launchPersistentContext(progress2, userDataDir, options) { + if (options.channel?.startsWith("moz-")) + return this._bidiFirefox.launchPersistentContext(progress2, userDataDir, options); + return super.launchPersistentContext(progress2, userDataDir, options); + } + connectToTransport(transport, options) { + return FFBrowser.connect(this.attribution.playwright, transport, options); + } + doRewriteStartupLog(logs) { + if (logs.includes(`as root in a regular user's session is not supported.`)) + logs = "\n" + wrapInASCIIBox(`Firefox is unable to launch if the $HOME folder isn't owned by the current user. +Workaround: Set the HOME=/root environment variable${process.env.GITHUB_ACTION ? " in your GitHub Actions workflow file" : ""} when running Playwright.`, 1); + if (logs.includes("no DISPLAY environment variable specified")) + logs = "\n" + wrapInASCIIBox(kNoXServerRunningError, 1); + return logs; + } + amendEnvironment(env) { + if (!import_path29.default.isAbsolute(import_os15.default.homedir())) + throw new Error(`Cannot launch Firefox with relative home directory. Did you set ${import_os15.default.platform() === "win32" ? "USERPROFILE" : "HOME"} to a relative path?`); + if (import_os15.default.platform() === "linux") { + return { ...env, SNAP_NAME: void 0, SNAP_INSTANCE_NAME: void 0 }; + } + return env; + } + attemptToGracefullyCloseBrowser(transport) { + const message = { method: "Browser.close", params: {}, id: kBrowserCloseMessageId3 }; + transport.send(message); + } + async defaultArgs(options, isPersistent, userDataDir) { + const { args = [], headless } = options; + const userDataDirArg = args.find((arg) => arg.startsWith("-profile") || arg.startsWith("--profile")); + if (userDataDirArg) + throw this._createUserDataDirArgMisuseError("--profile"); + if (args.find((arg) => arg.startsWith("-juggler"))) + throw new Error("Use the port parameter instead of -juggler argument"); + const firefoxArguments = ["-no-remote"]; + if (headless) { + firefoxArguments.push("-headless"); + } else { + firefoxArguments.push("-wait-for-browser"); + firefoxArguments.push("-foreground"); + } + firefoxArguments.push(`-profile`, userDataDir); + firefoxArguments.push("-juggler-pipe"); + firefoxArguments.push(...args); + if (isPersistent) + firefoxArguments.push("about:blank"); + else + firefoxArguments.push("-silent"); + return firefoxArguments; + } + waitForReadyState(options, browserLogsCollector) { + const result2 = new ManualPromise(); + browserLogsCollector.onMessage((message) => { + if (message.includes("Juggler listening to the pipe")) + result2.resolve({}); + }); + return result2; + } + }; + } +}); + +// packages/playwright-core/src/server/webkit/wkConnection.ts +var import_events11, kBrowserCloseMessageId4, kPageProxyMessageReceived, WKConnection, WKSession; +var init_wkConnection = __esm({ + "packages/playwright-core/src/server/webkit/wkConnection.ts"() { + "use strict"; + import_events11 = require("events"); + init_debugLogger(); + init_assert(); + init_helper(); + init_protocolError(); + kBrowserCloseMessageId4 = -9999; + kPageProxyMessageReceived = Symbol("kPageProxyMessageReceived"); + WKConnection = class { + constructor(transport, onDisconnect, protocolLogger, browserLogsCollector) { + this._lastId = 0; + this._closed = false; + this._transport = transport; + this._onDisconnect = onDisconnect; + this._protocolLogger = protocolLogger; + this._browserLogsCollector = browserLogsCollector; + this.browserSession = new WKSession(this, "", (message) => { + this.rawSend(message); + }); + this._transport.onmessage = this._dispatchMessage.bind(this); + this._transport.onclose = this._onClose.bind(this); + } + nextMessageId() { + return ++this._lastId; + } + rawSend(message) { + this._protocolLogger("send", message); + this._transport.send(message); + } + _dispatchMessage(message) { + this._protocolLogger("receive", message); + if (message.id === kBrowserCloseMessageId4) + return; + if (message.pageProxyId) { + const payload = { message, pageProxyId: message.pageProxyId }; + this.browserSession.dispatchMessage({ method: kPageProxyMessageReceived, params: payload }); + return; + } + this.browserSession.dispatchMessage(message); + } + _onClose(reason) { + this._closed = true; + this._transport.onmessage = void 0; + this._transport.onclose = void 0; + this._browserDisconnectedLogs = helper.formatBrowserLogs(this._browserLogsCollector.recentLogs(), reason); + this.browserSession.dispose(); + this._onDisconnect(); + } + isClosed() { + return this._closed; + } + close() { + if (!this._closed) + this._transport.close(); + } + }; + WKSession = class extends import_events11.EventEmitter { + constructor(connection, sessionId, rawSend) { + super(); + this._disposed = false; + this._callbacks = /* @__PURE__ */ new Map(); + this._crashed = false; + this.setMaxListeners(0); + this.connection = connection; + this.sessionId = sessionId; + this._rawSend = rawSend; + } + async send(method, params2) { + if (this._crashed || this._disposed || this.connection._browserDisconnectedLogs) + throw new ProtocolError(this._crashed ? "crashed" : "closed", void 0, this.connection._browserDisconnectedLogs); + const id = this.connection.nextMessageId(); + const messageObj = { id, method, params: params2 }; + this._rawSend(messageObj); + return new Promise((resolve, reject) => { + this._callbacks.set(id, { resolve, reject, error: new ProtocolError("error", method) }); + }); + } + sendMayFail(method, params2) { + return this.send(method, params2).catch((error) => debugLogger.log("error", error)); + } + markAsCrashed() { + this._crashed = true; + } + isDisposed() { + return this._disposed; + } + dispose() { + for (const callback of this._callbacks.values()) { + callback.error.type = this._crashed ? "crashed" : "closed"; + callback.error.logs = this.connection._browserDisconnectedLogs; + callback.reject(callback.error); + } + this._callbacks.clear(); + this._disposed = true; + } + dispatchMessage(object) { + if (object.id && this._callbacks.has(object.id)) { + const callback = this._callbacks.get(object.id); + this._callbacks.delete(object.id); + if (object.error) { + callback.error.setMessage(object.error.message); + callback.reject(callback.error); + } else { + callback.resolve(object.result); + } + } else if (object.id && !object.error) { + assert(this.isDisposed(), JSON.stringify(object)); + } else { + Promise.resolve().then(() => this.emit(object.method, object.params)); + } + } + }; + } +}); + +// packages/playwright-core/src/server/webkit/wkExecutionContext.ts +function potentiallyUnserializableValue3(remoteObject) { + const value2 = remoteObject.value; + const isUnserializable = remoteObject.type === "number" && ["NaN", "-Infinity", "Infinity", "-0"].includes(remoteObject.description); + return isUnserializable ? parseUnserializableValue(remoteObject.description) : value2; +} +function rewriteError4(error) { + if (error.message.includes("Object has too long reference chain")) + throw new Error("Cannot serialize result: object reference chain is too long."); + if (!isJavaScriptErrorInEvaluate(error) && !isSessionClosedError(error)) + return new Error("Execution context was destroyed, most likely because of a navigation."); + return error; +} +function renderPreview4(object) { + if (object.type === "undefined") + return "undefined"; + if ("value" in object) + return String(object.value); + if (object.description === "Object" && object.preview) { + const tokens = []; + for (const { name, value: value2 } of object.preview.properties) + tokens.push(`${name}: ${value2}`); + return `{${tokens.join(", ")}}`; + } + if (object.subtype === "array" && object.preview) + return sparseArrayToString(object.preview.properties); + return object.description; +} +function createHandle4(context2, remoteObject) { + if (remoteObject.subtype === "node") { + assert(context2 instanceof FrameExecutionContext); + return new ElementHandle(context2, remoteObject.objectId); + } + const isPromise = remoteObject.className === "Promise"; + return new JSHandle(context2, isPromise ? "promise" : remoteObject.subtype || remoteObject.type, renderPreview4(remoteObject), remoteObject.objectId, potentiallyUnserializableValue3(remoteObject)); +} +var WKExecutionContext; +var init_wkExecutionContext = __esm({ + "packages/playwright-core/src/server/webkit/wkExecutionContext.ts"() { + "use strict"; + init_assert(); + init_utilityScriptSerializers(); + init_javascript(); + init_dom(); + init_protocolError(); + WKExecutionContext = class { + constructor(session2, contextId4) { + this._session = session2; + this._contextId = contextId4; + } + async rawEvaluateJSON(expression2) { + try { + const response2 = await this._session.send("Runtime.evaluate", { + expression: expression2, + contextId: this._contextId, + returnByValue: true + }); + if (response2.wasThrown) + throw new JavaScriptErrorInEvaluate(response2.result.description); + return response2.result.value; + } catch (error) { + throw rewriteError4(error); + } + } + async rawEvaluateHandle(context2, expression2) { + try { + const response2 = await this._session.send("Runtime.evaluate", { + expression: expression2, + contextId: this._contextId, + returnByValue: false + }); + if (response2.wasThrown) + throw new JavaScriptErrorInEvaluate(response2.result.description); + return createHandle4(context2, response2.result); + } catch (error) { + throw rewriteError4(error); + } + } + async evaluateWithArguments(expression2, returnByValue, utilityScript, values, handles) { + try { + const response2 = await this._session.send("Runtime.callFunctionOn", { + functionDeclaration: expression2, + objectId: utilityScript._objectId, + arguments: [ + { objectId: utilityScript._objectId }, + ...values.map((value2) => ({ value: value2 })), + ...handles.map((handle) => ({ objectId: handle._objectId })) + ], + returnByValue, + emulateUserGesture: true, + awaitPromise: true + }); + if (response2.wasThrown) + throw new JavaScriptErrorInEvaluate(response2.result.description); + if (returnByValue) + return parseEvaluationResultValue(response2.result.value); + return createHandle4(utilityScript._context, response2.result); + } catch (error) { + throw rewriteError4(error); + } + } + async getProperties(object) { + const response2 = await this._session.send("Runtime.getProperties", { + objectId: object._objectId, + ownProperties: true + }); + const result2 = /* @__PURE__ */ new Map(); + for (const property of response2.properties) { + if (!property.enumerable || !property.value) + continue; + result2.set(property.name, createHandle4(object._context, property.value)); + } + return result2; + } + async releaseHandle(handle) { + if (!handle._objectId) + return; + await this._session.send("Runtime.releaseObject", { objectId: handle._objectId }); + } + shouldPrependErrorPrefix() { + return false; + } + }; + } +}); + +// packages/playwright-core/src/server/webkit/wkInput.ts +function toModifiersMask3(modifiers) { + let mask = 0; + if (modifiers.has("Shift")) + mask |= 1; + if (modifiers.has("Control")) + mask |= 2; + if (modifiers.has("Alt")) + mask |= 4; + if (modifiers.has("Meta")) + mask |= 8; + return mask; +} +function toButtonsMask3(buttons) { + let mask = 0; + if (buttons.has("left")) + mask |= 1; + if (buttons.has("right")) + mask |= 2; + if (buttons.has("middle")) + mask |= 4; + return mask; +} +var RawKeyboardImpl4, RawMouseImpl4, RawTouchscreenImpl4; +var init_wkInput = __esm({ + "packages/playwright-core/src/server/webkit/wkInput.ts"() { + "use strict"; + init_stringUtils(); + init_input(); + init_macEditingCommands(); + RawKeyboardImpl4 = class { + constructor(session2) { + this._pageProxySession = session2; + } + setSession(session2) { + this._session = session2; + } + async keydown(progress2, modifiers, keyName, description, autoRepeat) { + const parts = []; + for (const modifier of ["Shift", "Control", "Alt", "Meta"]) { + if (modifiers.has(modifier)) + parts.push(modifier); + } + const { code, keyCode, key, text: text2 } = description; + parts.push(code); + const shortcut = parts.join("+"); + let commands2 = macEditingCommands[shortcut]; + if (isString(commands2)) + commands2 = [commands2]; + await progress2.race(this._pageProxySession.send("Input.dispatchKeyEvent", { + type: "keyDown", + modifiers: toModifiersMask3(modifiers), + windowsVirtualKeyCode: keyCode, + code, + key, + text: text2, + unmodifiedText: text2, + autoRepeat, + macCommands: commands2, + isKeypad: description.location === keypadLocation2 + })); + } + async keyup(progress2, modifiers, keyName, description) { + const { code, key } = description; + await progress2.race(this._pageProxySession.send("Input.dispatchKeyEvent", { + type: "keyUp", + modifiers: toModifiersMask3(modifiers), + key, + windowsVirtualKeyCode: description.keyCode, + code, + isKeypad: description.location === keypadLocation2 + })); + } + async sendText(progress2, text2) { + await progress2.race(this._session.send("Page.insertText", { text: text2 })); + } + }; + RawMouseImpl4 = class { + constructor(session2) { + this._pageProxySession = session2; + } + setSession(session2) { + this._session = session2; + } + async move(progress2, x, y, button, buttons, modifiers, forClick) { + await progress2.race(this._pageProxySession.send("Input.dispatchMouseEvent", { + type: "move", + button, + buttons: toButtonsMask3(buttons), + x, + y, + modifiers: toModifiersMask3(modifiers) + })); + } + async down(progress2, x, y, button, buttons, modifiers, clickCount) { + await progress2.race(this._pageProxySession.send("Input.dispatchMouseEvent", { + type: "down", + button, + buttons: toButtonsMask3(buttons), + x, + y, + modifiers: toModifiersMask3(modifiers), + clickCount + })); + } + async up(progress2, x, y, button, buttons, modifiers, clickCount) { + await progress2.race(this._pageProxySession.send("Input.dispatchMouseEvent", { + type: "up", + button, + buttons: toButtonsMask3(buttons), + x, + y, + modifiers: toModifiersMask3(modifiers), + clickCount + })); + } + async wheel(progress2, x, y, buttons, modifiers, deltaX, deltaY) { + if (this._page?.browserContext._options.isMobile) + throw new Error("Mouse wheel is not supported in mobile WebKit"); + await progress2.race(this._session.send("Page.updateScrollingState")); + await this._page.mainFrame().evaluateExpression(progress2, `new Promise(requestAnimationFrame)`, { world: "utility" }); + await progress2.race(this._pageProxySession.send("Input.dispatchWheelEvent", { + x, + y, + deltaX, + deltaY, + modifiers: toModifiersMask3(modifiers) + })); + } + setPage(page) { + this._page = page; + } + }; + RawTouchscreenImpl4 = class { + constructor(session2) { + this._pageProxySession = session2; + } + async tap(progress2, x, y, modifiers) { + await progress2.race(this._pageProxySession.send("Input.dispatchTapEvent", { + x, + y, + modifiers: toModifiersMask3(modifiers) + })); + } + }; + } +}); + +// packages/playwright-core/src/server/webkit/wkInterceptableRequest.ts +function wkMillisToRoundishMillis(value2) { + if (value2 === -1e3) + return -1; + if (value2 <= 0) { + return -1; + } + return (value2 * 1e3 | 0) / 1e3; +} +function toResourceType2(type3) { + switch (type3) { + case "Document": + return "document"; + case "StyleSheet": + return "stylesheet"; + case "Image": + return "image"; + case "Font": + return "font"; + case "Script": + return "script"; + case "XHR": + return "xhr"; + case "Fetch": + return "fetch"; + case "Ping": + return "ping"; + case "Beacon": + return "beacon"; + case "WebSocket": + return "websocket"; + case "EventSource": + return "eventsource"; + default: + return "other"; + } +} +var errorReasons2, wkSetCookieSeparator, WKInterceptableRequest, WKRouteImpl; +var init_wkInterceptableRequest = __esm({ + "packages/playwright-core/src/server/webkit/wkInterceptableRequest.ts"() { + "use strict"; + init_assert(); + init_headers(); + init_network2(); + errorReasons2 = { + "aborted": "Cancellation", + "accessdenied": "AccessControl", + "addressunreachable": "General", + "blockedbyclient": "Cancellation", + "blockedbyresponse": "General", + "connectionaborted": "General", + "connectionclosed": "General", + "connectionfailed": "General", + "connectionrefused": "General", + "connectionreset": "General", + "internetdisconnected": "General", + "namenotresolved": "General", + "timedout": "Timeout", + "failed": "General" + }; + wkSetCookieSeparator = process.platform === "darwin" ? "," : "playwright-set-cookie-separator"; + WKInterceptableRequest = class { + constructor(session2, frame, event, redirectedFrom, documentId) { + this._session = session2; + this._requestId = event.requestId; + const resourceType = event.type ? toResourceType2(event.type) : redirectedFrom ? redirectedFrom.request.resourceType() : "other"; + let postDataBuffer = null; + this._timestamp = event.timestamp; + this._wallTime = event.walltime * 1e3; + if (event.request.postData) + postDataBuffer = Buffer.from(event.request.postData, "base64"); + this.request = new Request( + frame._page.browserContext, + frame, + null, + redirectedFrom?.request || null, + documentId, + event.request.url, + resourceType, + event.request.method, + postDataBuffer, + headersObjectToArray(event.request.headers), + this._wallTime + ); + } + adoptRequestFromNewProcess(newSession, requestId) { + this._session = newSession; + this._requestId = requestId; + } + createResponse(responsePayload) { + const getResponseBody = async () => { + const response3 = await this._session.send("Network.getResponseBody", { requestId: this._requestId }); + return Buffer.from(response3.body, response3.base64Encoded ? "base64" : "utf8"); + }; + const timingPayload = responsePayload.timing; + const timing = { + startTime: this._wallTime, + domainLookupStart: timingPayload ? wkMillisToRoundishMillis(timingPayload.domainLookupStart) : -1, + domainLookupEnd: timingPayload ? wkMillisToRoundishMillis(timingPayload.domainLookupEnd) : -1, + connectStart: timingPayload ? wkMillisToRoundishMillis(timingPayload.connectStart) : -1, + secureConnectionStart: timingPayload ? wkMillisToRoundishMillis(timingPayload.secureConnectionStart) : -1, + connectEnd: timingPayload ? wkMillisToRoundishMillis(timingPayload.connectEnd) : -1, + requestStart: timingPayload ? wkMillisToRoundishMillis(timingPayload.requestStart) : -1, + responseStart: timingPayload ? wkMillisToRoundishMillis(timingPayload.responseStart) : -1 + }; + const response2 = new Response2(this.request, responsePayload.status, responsePayload.statusText, headersObjectToArray(responsePayload.headers, ",", wkSetCookieSeparator), timing, getResponseBody, responsePayload.source === "service-worker"); + response2.setRawResponseHeaders(null); + response2.setTransferSize(null); + if (responsePayload.requestHeaders && Object.keys(responsePayload.requestHeaders).length) { + const headers = { ...responsePayload.requestHeaders }; + if (!headers["host"]) + headers["Host"] = new URL(this.request.url()).host; + this.request.setRawRequestHeaders(headersObjectToArray(headers)); + } else { + this.request.setRawRequestHeaders(null); + } + return response2; + } + }; + WKRouteImpl = class { + constructor(session2, requestId) { + this._session = session2; + this._requestId = requestId; + } + async abort(errorCode) { + const errorType = errorReasons2[errorCode]; + assert(errorType, "Unknown error code: " + errorCode); + await this._session.sendMayFail("Network.interceptRequestWithError", { requestId: this._requestId, errorType }); + } + async fulfill(response2) { + if (300 <= response2.status && response2.status < 400) + throw new Error("Cannot fulfill with redirect status: " + response2.status); + let mimeType = response2.isBase64 ? "application/octet-stream" : "text/plain"; + const headers = headersArrayToObject( + response2.headers, + true + /* lowerCase */ + ); + const contentType = headers["content-type"]; + if (contentType) + mimeType = contentType.split(";")[0].trim(); + await this._session.sendMayFail("Network.interceptRequestWithResponse", { + requestId: this._requestId, + status: response2.status, + statusText: statusText(response2.status), + mimeType, + headers, + base64Encoded: response2.isBase64, + content: response2.body + }); + } + async continue(overrides) { + await this._session.sendMayFail("Network.interceptWithRequest", { + requestId: this._requestId, + url: overrides.url, + method: overrides.method, + headers: overrides.headers ? headersArrayToObject( + overrides.headers, + false + /* lowerCase */ + ) : void 0, + postData: overrides.postData ? Buffer.from(overrides.postData).toString("base64") : void 0 + }); + } + }; + } +}); + +// packages/playwright-core/src/server/webkit/wkProvisionalPage.ts +var WKProvisionalPage; +var init_wkProvisionalPage = __esm({ + "packages/playwright-core/src/server/webkit/wkProvisionalPage.ts"() { + "use strict"; + init_eventsHelper(); + init_assert(); + WKProvisionalPage = class { + constructor(session2, page) { + this._sessionListeners = []; + this._mainFrameId = null; + this._session = session2; + this._wkPage = page; + this._coopNavigationRequest = page._page.mainFrame().pendingDocument()?.request; + const overrideFrameId = (handler) => { + return (payload) => { + if (payload.frameId) + payload.frameId = this._wkPage._page.frameManager.mainFrame()._id; + handler(payload); + }; + }; + const wkPage = this._wkPage; + this._sessionListeners = [ + eventsHelper.addEventListener(session2, "Network.requestWillBeSent", overrideFrameId((e) => this._onRequestWillBeSent(e))), + eventsHelper.addEventListener(session2, "Network.requestIntercepted", overrideFrameId((e) => wkPage._onRequestIntercepted(session2, e))), + eventsHelper.addEventListener(session2, "Network.responseReceived", overrideFrameId((e) => wkPage._onResponseReceived(session2, e))), + eventsHelper.addEventListener(session2, "Network.loadingFinished", overrideFrameId((e) => this._onLoadingFinished(e))), + eventsHelper.addEventListener(session2, "Network.loadingFailed", overrideFrameId((e) => this._onLoadingFailed(e))) + ]; + this.initializationPromise = this._wkPage._initializeSession(session2, true, ({ frameTree }) => this._handleFrameTree(frameTree)); + } + coopNavigationRequest() { + return this._coopNavigationRequest; + } + dispose() { + eventsHelper.removeEventListeners(this._sessionListeners); + } + commit() { + assert(this._mainFrameId); + this._wkPage._onFrameAttached(this._mainFrameId, null); + } + _onRequestWillBeSent(event) { + if (this._coopNavigationRequest && this._coopNavigationRequest.url() === event.request.url) { + this._wkPage._adoptRequestFromNewProcess(this._coopNavigationRequest, this._session, event.requestId); + return; + } + this._wkPage._onRequestWillBeSent(this._session, event); + } + _onLoadingFinished(event) { + this._coopNavigationRequest = void 0; + this._wkPage._onLoadingFinished(event); + } + _onLoadingFailed(event) { + this._coopNavigationRequest = void 0; + this._wkPage._onLoadingFailed(this._session, event); + } + _handleFrameTree(frameTree) { + assert(!frameTree.frame.parentId); + this._mainFrameId = frameTree.frame.id; + } + }; + } +}); + +// packages/playwright-core/src/server/webkit/wkWorkers.ts +var WKWorkers; +var init_wkWorkers = __esm({ + "packages/playwright-core/src/server/webkit/wkWorkers.ts"() { + "use strict"; + init_eventsHelper(); + init_page(); + init_wkConnection(); + init_wkExecutionContext(); + WKWorkers = class { + constructor(page) { + this._sessionListeners = []; + this._workerSessions = /* @__PURE__ */ new Map(); + this._page = page; + } + setSession(session2) { + eventsHelper.removeEventListeners(this._sessionListeners); + this.clear(); + this._sessionListeners = [ + eventsHelper.addEventListener(session2, "Worker.workerCreated", (event) => { + const worker = new Worker(this._page, event.url); + const workerSession = new WKSession(session2.connection, event.workerId, (message) => { + session2.send("Worker.sendMessageToWorker", { + workerId: event.workerId, + message: JSON.stringify(message) + }).catch((e) => { + workerSession.dispatchMessage({ id: message.id, error: { message: e.message } }); + }); + }); + this._workerSessions.set(event.workerId, workerSession); + worker.createExecutionContext(new WKExecutionContext(workerSession, void 0)); + worker.workerScriptLoaded(); + this._page.addWorker(event.workerId, worker); + workerSession.on("Console.messageAdded", (event2) => this._onConsoleMessage(worker, event2)); + Promise.all([ + workerSession.send("Runtime.enable"), + workerSession.send("Console.enable"), + session2.send("Worker.initialized", { workerId: event.workerId }) + ]).catch((e) => { + this._page.removeWorker(event.workerId); + }); + }), + eventsHelper.addEventListener(session2, "Worker.dispatchMessageFromWorker", (event) => { + const workerSession = this._workerSessions.get(event.workerId); + if (!workerSession) + return; + workerSession.dispatchMessage(JSON.parse(event.message)); + }), + eventsHelper.addEventListener(session2, "Worker.workerTerminated", (event) => { + const workerSession = this._workerSessions.get(event.workerId); + if (!workerSession) + return; + workerSession.dispose(); + this._workerSessions.delete(event.workerId); + this._page.removeWorker(event.workerId); + }) + ]; + } + clear() { + this._page.clearWorkers(); + this._workerSessions.clear(); + } + async initializeSession(session2) { + await session2.send("Worker.enable"); + } + async _onConsoleMessage(worker, event) { + const { type: type3, level, text: text2, parameters, url: url2, line: lineNumber, column: columnNumber } = event.message; + let derivedType = type3 || ""; + if (type3 === "log") + derivedType = level; + else if (type3 === "timing") + derivedType = "timeEnd"; + const handles = (parameters || []).map((p) => { + return createHandle4(worker.existingExecutionContext, p); + }); + const location2 = { + url: url2 || "", + lineNumber: (lineNumber || 1) - 1, + columnNumber: (columnNumber || 1) - 1 + }; + const timestamp = event.message.timestamp ? event.message.timestamp * 1e3 : Date.now(); + this._page.addConsoleMessage(worker, derivedType, handles, location2, handles.length ? void 0 : text2, timestamp); + } + }; + } +}); + +// packages/playwright-core/src/server/webkit/wkPage.ts +function parseRemoteAddress(value2) { + if (!value2) + return; + try { + const colon = value2.lastIndexOf(":"); + const dot = value2.lastIndexOf("."); + if (dot < 0) { + return { + ipAddress: `[${value2.slice(0, colon)}]`, + port: +value2.slice(colon + 1) + }; + } + if (colon > dot) { + const [address, port] = value2.split(":"); + return { + ipAddress: address, + port: +port + }; + } else { + const [address, port] = value2.split("."); + return { + ipAddress: `[${address}]`, + port: +port + }; + } + } catch (_) { + } +} +function isLoadedSecurely(url2, timing) { + try { + const u = new URL(url2); + if (u.protocol !== "https:" && u.protocol !== "wss:" && u.protocol !== "sftp:") + return false; + if (timing.secureConnectionStart === -1 && timing.connectStart !== -1) + return false; + return true; + } catch (_) { + } +} +var PNG2, jpegjs3, UTILITY_WORLD_NAME3, enableFrameSessions, WKPage, WKFrame; +var init_wkPage = __esm({ + "packages/playwright-core/src/server/webkit/wkPage.ts"() { + "use strict"; + init_headers(); + init_stackTrace(); + init_eventsHelper(); + init_hostPlatform(); + init_assert(); + init_dialog(); + init_dom(); + init_errors(); + init_helper(); + init_network2(); + init_page(); + init_wkConnection(); + init_wkExecutionContext(); + init_wkInput(); + init_wkInterceptableRequest(); + init_wkProvisionalPage(); + init_wkWorkers(); + init_webkit(); + init_registry(); + init_videoRecorder(); + init_progress(); + ({ PNG: PNG2 } = require("./utilsBundle")); + jpegjs3 = require("./utilsBundle").jpegjs; + UTILITY_WORLD_NAME3 = "__playwright_utility_world__"; + enableFrameSessions = !process.env.WK_DISABLE_FRAME_SESSIONS && parseInt(registry.findExecutable("webkit").revision, 10) >= 2245 && parseInt(registry.findExecutable("webkit").revision, 10) <= 2255; + WKPage = class _WKPage { + constructor(browserContext, pageProxySession, opener) { + this._provisionalPage = null; + this._targetIdToFrameSession = /* @__PURE__ */ new Map(); + this._requestIdToRequest = /* @__PURE__ */ new Map(); + this._requestIdToRequestWillBeSentEvent = /* @__PURE__ */ new Map(); + this._sessionListeners = []; + this._firstNonInitialNavigationCommittedFulfill = () => { + }; + this._firstNonInitialNavigationCommittedReject = (e) => { + }; + this._lastConsoleMessage = null; + this._requestIdToResponseReceivedPayloadEvent = /* @__PURE__ */ new Map(); + this._timestampBaselineForWebSocket = /* @__PURE__ */ new Map(); + this._screencastGeneration = 0; + this._pageProxySession = pageProxySession; + this._opener = opener; + this.rawKeyboard = new RawKeyboardImpl4(pageProxySession); + this.rawMouse = new RawMouseImpl4(pageProxySession); + this.rawTouchscreen = new RawTouchscreenImpl4(pageProxySession); + this._contextIdToContext = /* @__PURE__ */ new Map(); + this._page = new Page(this, browserContext); + this.rawMouse.setPage(this._page); + this._workers = new WKWorkers(this._page); + this._session = void 0; + this._browserContext = browserContext; + this._page.on(Page.Events.FrameDetached, (frame) => this._removeContextsForFrame(frame, false)); + this._eventListeners = [ + eventsHelper.addEventListener(this._pageProxySession, "Target.targetCreated", this._onTargetCreated.bind(this)), + eventsHelper.addEventListener(this._pageProxySession, "Target.targetDestroyed", this._onTargetDestroyed.bind(this)), + eventsHelper.addEventListener(this._pageProxySession, "Target.dispatchMessageFromTarget", this._onDispatchMessageFromTarget.bind(this)), + eventsHelper.addEventListener(this._pageProxySession, "Target.didCommitProvisionalTarget", this._onDidCommitProvisionalTarget.bind(this)), + eventsHelper.addEventListener(this._pageProxySession, "Screencast.screencastFrame", this._onScreencastFrame.bind(this)) + ]; + this._firstNonInitialNavigationCommittedPromise = new Promise((f, r) => { + this._firstNonInitialNavigationCommittedFulfill = f; + this._firstNonInitialNavigationCommittedReject = r; + }); + this._firstNonInitialNavigationCommittedPromise.catch(() => { + }); + if (opener && !browserContext._options.noDefaultViewport && opener._nextWindowOpenPopupFeatures) { + const viewportSize = helper.getViewportSizeFromWindowFeatures(opener._nextWindowOpenPopupFeatures); + opener._nextWindowOpenPopupFeatures = void 0; + if (viewportSize) + this._page.setEmulatedSizeFromWindowOpen({ viewport: viewportSize, screen: viewportSize }); + } + } + async _initializePageProxySession() { + if (this._page.isStorageStatePage) + return; + const promises = [ + this._pageProxySession.send("Dialog.enable"), + this._pageProxySession.send("Emulation.setActiveAndFocused", { active: true }) + ]; + const contextOptions = this._browserContext._options; + if (contextOptions.javaScriptEnabled === false) + promises.push(this._pageProxySession.send("Emulation.setJavaScriptEnabled", { enabled: false })); + promises.push(this._updateViewport()); + promises.push(this.updateHttpCredentials()); + if (this._browserContext._permissions.size) { + for (const [key, value2] of this._browserContext._permissions) + promises.push(this._grantPermissions(key, value2)); + } + startAutomaticVideoRecording(this._page); + await Promise.all(promises); + } + _setSession(session2) { + eventsHelper.removeEventListeners(this._sessionListeners); + this._session = session2; + this.rawKeyboard.setSession(session2); + this.rawMouse.setSession(session2); + this._addSessionListeners(); + this._workers.setSession(session2); + } + // This method is called for provisional targets as well. The session passed as the parameter + // may be different from the current session and may be destroyed without becoming current. + async _initializeSession(session2, provisional, resourceTreeHandler) { + await this._initializeSessionMayThrow(session2, resourceTreeHandler).catch((e) => { + if (provisional && session2.isDisposed()) + return; + if (this._session === session2) + throw e; + }); + } + async _initializeSessionMayThrow(session2, resourceTreeHandler) { + const [, frameTree] = await Promise.all([ + // Page agent must be enabled before Runtime. + session2.send("Page.enable"), + session2.send("Page.getResourceTree") + ]); + resourceTreeHandler(frameTree); + const promises = [ + // Resource tree should be received before first execution context. + session2.send("Runtime.enable"), + session2.send("Page.createUserWorld", { name: UTILITY_WORLD_NAME3 }).catch((_) => { + }), + // Worlds are per-process + session2.send("Network.enable"), + this._workers.initializeSession(session2) + ]; + if (enableFrameSessions) + this._initializeFrameSessions(frameTree.frameTree, promises); + else + promises.push(session2.send("Console.enable")); + if (this._page.browserContext.needsPlaywrightBinding()) + promises.push(session2.send("Runtime.addBinding", { name: PageBinding.kBindingName })); + if (this._page.needsRequestInterception()) { + promises.push(session2.send("Network.setInterceptionEnabled", { enabled: true })); + promises.push(session2.send("Network.setResourceCachingDisabled", { disabled: true })); + promises.push(session2.send("Network.addInterception", { url: ".*", stage: "request", isRegex: true })); + } + if (this._page.isStorageStatePage) { + await Promise.all(promises); + return; + } + const contextOptions = this._browserContext._options; + if (contextOptions.userAgent) + promises.push(this.updateUserAgent()); + const emulatedMedia = this._page.emulatedMedia(); + if (emulatedMedia.media || emulatedMedia.colorScheme || emulatedMedia.reducedMotion || emulatedMedia.forcedColors || emulatedMedia.contrast) + promises.push(_WKPage._setEmulateMedia(session2, emulatedMedia.media, emulatedMedia.colorScheme, emulatedMedia.reducedMotion, emulatedMedia.forcedColors, emulatedMedia.contrast)); + const bootstrapScript = this._calculateBootstrapScript(); + if (bootstrapScript.length) + promises.push(session2.send("Page.setBootstrapScript", { source: bootstrapScript })); + this._page.frames().map((frame) => frame.evaluateExpression(nullProgress, bootstrapScript).catch((e) => { + })); + if (contextOptions.bypassCSP) + promises.push(session2.send("Page.setBypassCSP", { enabled: true })); + const emulatedSize = this._page.emulatedSize(); + if (emulatedSize) { + promises.push(session2.send("Page.setScreenSizeOverride", { + width: emulatedSize.screen.width, + height: emulatedSize.screen.height + })); + } + promises.push(this.updateEmulateMedia()); + promises.push(session2.send("Network.setExtraHTTPHeaders", { headers: headersArrayToObject( + this._calculateExtraHTTPHeaders(), + false + /* lowerCase */ + ) })); + if (contextOptions.offline) + promises.push(session2.send("Network.setEmulateOfflineState", { offline: true })); + promises.push(session2.send("Page.setTouchEmulationEnabled", { enabled: !!contextOptions.hasTouch })); + if (contextOptions.timezoneId) { + promises.push(session2.send("Page.setTimeZone", { timeZone: contextOptions.timezoneId }).catch((e) => { + throw new Error(`Invalid timezone ID: ${contextOptions.timezoneId}`); + })); + } + if (this._page.fileChooserIntercepted()) + promises.push(session2.send("Page.setInterceptFileChooserDialog", { enabled: true })); + promises.push(session2.send("Page.overrideSetting", { setting: "DeviceOrientationEventEnabled", value: contextOptions.isMobile })); + promises.push(session2.send("Page.overrideSetting", { setting: "FullScreenEnabled", value: !contextOptions.isMobile })); + promises.push(session2.send("Page.overrideSetting", { setting: "NotificationsEnabled", value: !contextOptions.isMobile })); + promises.push(session2.send("Page.overrideSetting", { setting: "PointerLockEnabled", value: !contextOptions.isMobile })); + promises.push(session2.send("Page.overrideSetting", { setting: "InputTypeMonthEnabled", value: contextOptions.isMobile })); + promises.push(session2.send("Page.overrideSetting", { setting: "InputTypeWeekEnabled", value: contextOptions.isMobile })); + promises.push(session2.send("Page.overrideSetting", { setting: "FixedBackgroundsPaintRelativeToDocument", value: contextOptions.isMobile })); + await Promise.all(promises); + } + _initializeFrameSessions(frame, promises) { + const session2 = this._targetIdToFrameSession.get(`frame-${frame.frame.id}`); + if (session2) + promises.push(session2.initialize()); + for (const childFrame of frame.childFrames || []) + this._initializeFrameSessions(childFrame, promises); + } + _onDidCommitProvisionalTarget(event) { + const { oldTargetId, newTargetId } = event; + assert(this._provisionalPage); + assert(this._provisionalPage._session.sessionId === newTargetId, "Unknown new target: " + newTargetId); + assert(this._session.sessionId === oldTargetId, "Unknown old target: " + oldTargetId); + const newSession = this._provisionalPage._session; + this._provisionalPage.commit(); + this._provisionalPage.dispose(); + this._provisionalPage = null; + this._setSession(newSession); + } + _onTargetDestroyed(event) { + const { targetId, crashed } = event; + if (this._provisionalPage && this._provisionalPage._session.sessionId === targetId) { + this._maybeCancelCoopNavigationRequest(this._provisionalPage); + this._provisionalPage._session.dispose(); + this._provisionalPage.dispose(); + this._provisionalPage = null; + } else if (this._session.sessionId === targetId) { + this._session.dispose(); + eventsHelper.removeEventListeners(this._sessionListeners); + if (crashed) { + this._session.markAsCrashed(); + this._page._didCrash(); + } + } else if (this._targetIdToFrameSession.has(targetId)) { + this._targetIdToFrameSession.get(targetId).dispose(); + this._targetIdToFrameSession.delete(targetId); + } + } + didClose() { + this._pageProxySession.dispose(); + eventsHelper.removeEventListeners(this._sessionListeners); + eventsHelper.removeEventListeners(this._eventListeners); + if (this._session) + this._session.dispose(); + if (this._provisionalPage) { + this._provisionalPage._session.dispose(); + this._provisionalPage.dispose(); + this._provisionalPage = null; + } + this._firstNonInitialNavigationCommittedReject(new TargetClosedError(this._page.closeReason())); + this._page._didClose(); + } + dispatchMessageToSession(message) { + this._pageProxySession.dispatchMessage(message); + } + handleProvisionalLoadFailed(event) { + if (!this._page.initializedOrUndefined()) { + this._firstNonInitialNavigationCommittedReject(new Error("Initial load failed")); + return; + } + if (!this._provisionalPage) + return; + let errorText = event.error; + if (errorText.includes("cancelled")) + errorText += "; maybe frame was detached?"; + this._page.frameManager.frameAbortedNavigation(this._page.mainFrame()._id, errorText, event.loaderId); + } + handleWindowOpen(event) { + this._nextWindowOpenPopupFeatures = event.windowFeatures; + } + async _onTargetCreated(event) { + const { targetInfo } = event; + const session2 = new WKSession(this._pageProxySession.connection, targetInfo.targetId, (message) => { + this._pageProxySession.send("Target.sendMessageToTarget", { + message: JSON.stringify(message), + targetId: targetInfo.targetId + }).catch((e) => { + session2.dispatchMessage({ id: message.id, error: { message: e.message } }); + }); + }); + if (targetInfo.type === "frame") { + if (enableFrameSessions) { + const wkFrame = new WKFrame(this, session2); + this._targetIdToFrameSession.set(targetInfo.targetId, wkFrame); + await wkFrame.initialize().catch((e) => { + }); + } + return; + } + assert(targetInfo.type === "page", "Only page targets are expected in WebKit, received: " + targetInfo.type); + if (!targetInfo.isProvisional) { + assert(!this._page.initializedOrUndefined()); + let pageOrError; + try { + this._setSession(session2); + await Promise.all([ + this._initializePageProxySession(), + this._initializeSession(session2, false, ({ frameTree }) => this._handleFrameTree(frameTree)) + ]); + pageOrError = this._page; + } catch (e) { + pageOrError = e; + } + if (targetInfo.isPaused) + this._pageProxySession.sendMayFail("Target.resume", { targetId: targetInfo.targetId }); + if (pageOrError instanceof Page && this._page.mainFrame().url() === "") { + try { + await this._firstNonInitialNavigationCommittedPromise; + } catch (e) { + pageOrError = e; + } + } + this._page.reportAsNew(this._opener?._page, pageOrError instanceof Page ? void 0 : pageOrError); + } else { + assert(targetInfo.isProvisional); + assert(!this._provisionalPage); + this._provisionalPage = new WKProvisionalPage(session2, this); + if (targetInfo.isPaused) { + this._provisionalPage.initializationPromise.then(() => { + this._pageProxySession.sendMayFail("Target.resume", { targetId: targetInfo.targetId }); + }); + } + } + } + _onDispatchMessageFromTarget(event) { + const { targetId, message } = event; + if (this._provisionalPage && this._provisionalPage._session.sessionId === targetId) + this._provisionalPage._session.dispatchMessage(JSON.parse(message)); + else if (this._session.sessionId === targetId) + this._session.dispatchMessage(JSON.parse(message)); + else if (this._targetIdToFrameSession.has(targetId)) + this._targetIdToFrameSession.get(targetId)._session.dispatchMessage(JSON.parse(message)); + else + throw new Error("Unknown target: " + targetId); + } + _addSessionListeners() { + this._sessionListeners = [ + eventsHelper.addEventListener(this._session, "Page.frameNavigated", (event) => this._onFrameNavigated(event.frame, false)), + eventsHelper.addEventListener(this._session, "Page.navigatedWithinDocument", (event) => this._onFrameNavigatedWithinDocument(event.frameId, event.url)), + eventsHelper.addEventListener(this._session, "Page.frameAttached", (event) => this._onFrameAttached(event.frameId, event.parentFrameId)), + eventsHelper.addEventListener(this._session, "Page.frameDetached", (event) => this._onFrameDetached(event.frameId)), + eventsHelper.addEventListener(this._session, "Page.willCheckNavigationPolicy", (event) => this._onWillCheckNavigationPolicy(event.frameId)), + eventsHelper.addEventListener(this._session, "Page.didCheckNavigationPolicy", (event) => this._onDidCheckNavigationPolicy(event.frameId, event.cancel)), + eventsHelper.addEventListener(this._session, "Page.loadEventFired", (event) => this._page.frameManager.frameLifecycleEvent(event.frameId, "load")), + eventsHelper.addEventListener(this._session, "Page.domContentEventFired", (event) => this._page.frameManager.frameLifecycleEvent(event.frameId, "domcontentloaded")), + eventsHelper.addEventListener(this._session, "Runtime.executionContextCreated", (event) => this._onExecutionContextCreated(event.context)), + eventsHelper.addEventListener(this._session, "Runtime.bindingCalled", (event) => this._onBindingCalled(event.contextId, event.argument)), + eventsHelper.addEventListener(this._session, "Console.messageAdded", (event) => this._onConsoleMessage(event)), + eventsHelper.addEventListener(this._session, "Console.messageRepeatCountUpdated", (event) => this._onConsoleRepeatCountUpdated(event)), + eventsHelper.addEventListener(this._pageProxySession, "Dialog.javascriptDialogOpening", (event) => this._onDialog(event)), + eventsHelper.addEventListener(this._session, "Page.fileChooserOpened", (event) => this._onFileChooserOpened(event)), + eventsHelper.addEventListener(this._session, "Network.requestWillBeSent", (e) => this._onRequestWillBeSent(this._session, e)), + eventsHelper.addEventListener(this._session, "Network.requestIntercepted", (e) => this._onRequestIntercepted(this._session, e)), + eventsHelper.addEventListener(this._session, "Network.responseReceived", (e) => this._onResponseReceived(this._session, e)), + eventsHelper.addEventListener(this._session, "Network.loadingFinished", (e) => this._onLoadingFinished(e)), + eventsHelper.addEventListener(this._session, "Network.loadingFailed", (e) => this._onLoadingFailed(this._session, e)), + eventsHelper.addEventListener(this._session, "Network.webSocketCreated", (e) => this._page.frameManager.onWebSocketCreated(e.requestId, e.url)), + eventsHelper.addEventListener(this._session, "Network.webSocketWillSendHandshakeRequest", (event) => this._onWebSocketWillSendHandshakeRequest(event)), + eventsHelper.addEventListener(this._session, "Network.webSocketHandshakeResponseReceived", (e) => this._page.frameManager.onWebSocketResponse(e.requestId, e.response.status, e.response.statusText, headersObjectToArray(e.response.headers, ",", wkSetCookieSeparator))), + eventsHelper.addEventListener(this._session, "Network.webSocketFrameSent", (e) => e.response.payloadData && this._page.frameManager.onWebSocketFrameSent(e.requestId, e.response.opcode, e.response.payloadData, this._timestampToWallTimeMsForWebSocket(e.requestId, e.timestamp))), + eventsHelper.addEventListener(this._session, "Network.webSocketFrameReceived", (e) => e.response.payloadData && this._page.frameManager.webSocketFrameReceived(e.requestId, e.response.opcode, e.response.payloadData, this._timestampToWallTimeMsForWebSocket(e.requestId, e.timestamp))), + eventsHelper.addEventListener(this._session, "Network.webSocketClosed", (event) => this._onWebSocketClosed(event)), + eventsHelper.addEventListener(this._session, "Network.webSocketFrameError", (e) => this._page.frameManager.webSocketError(e.requestId, e.errorMessage)) + ]; + } + async _updateState(method, params2) { + await this._forAllSessions((session2) => session2.send(method, params2).then()); + } + async _forAllSessions(callback) { + const sessions = [ + this._session + ]; + if (this._provisionalPage) + sessions.push(this._provisionalPage._session); + await Promise.all(sessions.map((session2) => callback(session2).catch((e) => { + }))); + } + _onWillCheckNavigationPolicy(frameId) { + if (this._provisionalPage) + return; + this._page.frameManager.frameRequestedNavigation(frameId); + } + _onDidCheckNavigationPolicy(frameId, cancel) { + if (!cancel) + return; + if (this._provisionalPage) + return; + this._page.frameManager.frameAbortedNavigation(frameId, "Navigation canceled by policy check"); + } + _handleFrameTree(frameTree) { + this._onFrameAttached(frameTree.frame.id, frameTree.frame.parentId || null); + this._onFrameNavigated(frameTree.frame, true); + this._page.frameManager.frameLifecycleEvent(frameTree.frame.id, "domcontentloaded"); + this._page.frameManager.frameLifecycleEvent(frameTree.frame.id, "load"); + if (!frameTree.childFrames) + return; + for (const child of frameTree.childFrames) + this._handleFrameTree(child); + } + _onFrameAttached(frameId, parentFrameId) { + return this._page.frameManager.frameAttached(frameId, parentFrameId); + } + _onFrameNavigated(framePayload, initial) { + const frame = this._page.frameManager.frame(framePayload.id); + assert(frame); + this._removeContextsForFrame(frame, true); + if (!framePayload.parentId) + this._workers.clear(); + this._page.frameManager.frameCommittedNewDocumentNavigation(framePayload.id, framePayload.url, framePayload.name || "", framePayload.loaderId, initial); + if (!initial) + this._firstNonInitialNavigationCommittedFulfill(); + } + _onFrameNavigatedWithinDocument(frameId, url2) { + this._page.frameManager.frameCommittedSameDocumentNavigation(frameId, url2); + } + _onFrameDetached(frameId) { + this._page.frameManager.frameDetached(frameId); + } + _removeContextsForFrame(frame, notifyFrame) { + for (const [contextId4, context2] of this._contextIdToContext) { + if (context2.frame === frame) { + this._contextIdToContext.delete(contextId4); + if (notifyFrame) + frame.contextDestroyed(context2); + } + } + } + _onExecutionContextCreated(contextPayload) { + if (this._contextIdToContext.has(contextPayload.id)) + return; + const frame = this._page.frameManager.frame(contextPayload.frameId); + if (!frame) + return; + const delegate = new WKExecutionContext(this._session, contextPayload.id); + let worldName = null; + if (contextPayload.type === "normal") + worldName = "main"; + else if (contextPayload.type === "user" && contextPayload.name === UTILITY_WORLD_NAME3) + worldName = "utility"; + const context2 = new FrameExecutionContext(delegate, frame, worldName); + if (worldName) + frame.contextCreated(worldName, context2); + this._contextIdToContext.set(contextPayload.id, context2); + } + async _onBindingCalled(contextId4, argument) { + const pageOrError = await this._page.waitForInitializedOrError(); + if (!(pageOrError instanceof Error)) { + const context2 = this._contextIdToContext.get(contextId4); + if (context2) + await this._page.onBindingCalled(argument, context2); + } + } + async navigateFrame(frame, url2, referrer) { + if (this._pageProxySession.isDisposed()) + throw new TargetClosedError(this._page.closeReason()); + const pageProxyId = this._pageProxySession.sessionId; + const result2 = await this._pageProxySession.connection.browserSession.send("Playwright.navigate", { url: url2, pageProxyId, frameId: frame._id, referrer }); + return { newDocumentId: result2.loaderId }; + } + _onConsoleMessage(event) { + const { type: type3, level, text: text2, parameters, url: url2, line: lineNumber, column: columnNumber, source: source11 } = event.message; + if (level === "error" && source11 === "javascript") { + const { name, message } = splitErrorMessage(text2); + let stack; + if (event.message.stackTrace) { + stack = text2 + "\n" + event.message.stackTrace.callFrames.map((callFrame) => { + return ` at ${callFrame.functionName || "unknown"} (${callFrame.url}:${callFrame.lineNumber}:${callFrame.columnNumber})`; + }).join("\n"); + } else { + stack = ""; + } + this._lastConsoleMessage = null; + const error = new Error(message); + error.stack = stack; + error.name = name; + this._page.addPageError(error, { + url: url2 || "", + lineNumber: (lineNumber || 1) - 1, + columnNumber: (columnNumber || 1) - 1 + }); + return; + } + let derivedType = type3 || ""; + if (type3 === "log") + derivedType = level; + else if (type3 === "timing") + derivedType = "timeEnd"; + const handles = []; + for (const p of parameters || []) { + let context2; + if (p.objectId) { + const objectId = JSON.parse(p.objectId); + context2 = this._contextIdToContext.get(objectId.injectedScriptId); + } else { + context2 = [...this._contextIdToContext.values()].find((c) => c.frame === this._page.mainFrame()); + } + if (!context2) + return; + handles.push(createHandle4(context2, p)); + } + this._lastConsoleMessage = { + derivedType, + text: text2, + handles, + count: 0, + location: { + url: url2 || "", + lineNumber: (lineNumber || 1) - 1, + columnNumber: (columnNumber || 1) - 1 + } + }; + this._onConsoleRepeatCountUpdated({ count: 1, timestamp: event.message.timestamp }); + } + _onConsoleRepeatCountUpdated(event) { + if (this._lastConsoleMessage) { + const { + derivedType, + text: text2, + handles, + count, + location: location2 + } = this._lastConsoleMessage; + const timestamp = event.timestamp ? event.timestamp * 1e3 : Date.now(); + for (let i = count; i < event.count; ++i) + this._page.addConsoleMessage(null, derivedType, handles, location2, handles.length ? void 0 : text2, timestamp); + this._lastConsoleMessage.count = event.count; + } + } + _onDialog(event) { + this._page.browserContext.dialogManager.dialogDidOpen(new Dialog( + this._page, + event.type, + event.message, + async (accept, promptText) => { + if (event.type === "beforeunload" && !accept) + this._page.frameManager.frameAbortedNavigation(this._page.mainFrame()._id, "navigation cancelled by beforeunload dialog"); + await this._pageProxySession.send("Dialog.handleJavaScriptDialog", { accept, promptText }); + }, + event.defaultPrompt + )); + } + async _onFileChooserOpened(event) { + let handle; + try { + const context2 = await this._page.frameManager.frame(event.frameId).mainContext(); + handle = createHandle4(context2, event.element).asElement(); + } catch (e) { + return; + } + await this._page._onFileChooserOpened(handle); + } + static async _setEmulateMedia(session2, mediaType, colorScheme, reducedMotion, forcedColors, contrast) { + const promises = []; + promises.push(session2.send("Page.setEmulatedMedia", { media: mediaType === "no-override" ? "" : mediaType })); + let appearance = void 0; + switch (colorScheme) { + case "light": + appearance = "Light"; + break; + case "dark": + appearance = "Dark"; + break; + case "no-override": + appearance = void 0; + break; + } + promises.push(session2.send("Page.overrideUserPreference", { name: "PrefersColorScheme", value: appearance })); + let reducedMotionWk = void 0; + switch (reducedMotion) { + case "reduce": + reducedMotionWk = "Reduce"; + break; + case "no-preference": + reducedMotionWk = "NoPreference"; + break; + case "no-override": + reducedMotionWk = void 0; + break; + } + promises.push(session2.send("Page.overrideUserPreference", { name: "PrefersReducedMotion", value: reducedMotionWk })); + let forcedColorsWk = void 0; + switch (forcedColors) { + case "active": + forcedColorsWk = "Active"; + break; + case "none": + forcedColorsWk = "None"; + break; + case "no-override": + forcedColorsWk = void 0; + break; + } + promises.push(session2.send("Page.setForcedColors", { forcedColors: forcedColorsWk })); + let contrastWk = void 0; + switch (contrast) { + case "more": + contrastWk = "More"; + break; + case "no-preference": + contrastWk = "NoPreference"; + break; + case "no-override": + contrastWk = void 0; + break; + } + promises.push(session2.send("Page.overrideUserPreference", { name: "PrefersContrast", value: contrastWk })); + await Promise.all(promises); + } + async updateExtraHTTPHeaders() { + await this._updateState("Network.setExtraHTTPHeaders", { headers: headersArrayToObject( + this._calculateExtraHTTPHeaders(), + false + /* lowerCase */ + ) }); + } + _calculateExtraHTTPHeaders() { + const locale = this._browserContext._options.locale; + const headers = mergeHeaders([ + this._browserContext._options.extraHTTPHeaders, + this._page.extraHTTPHeaders(), + locale ? singleHeader("Accept-Language", locale) : void 0 + ]); + return headers; + } + async updateEmulateMedia() { + const emulatedMedia = this._page.emulatedMedia(); + const colorScheme = emulatedMedia.colorScheme; + const reducedMotion = emulatedMedia.reducedMotion; + const forcedColors = emulatedMedia.forcedColors; + const contrast = emulatedMedia.contrast; + await this._forAllSessions((session2) => _WKPage._setEmulateMedia(session2, emulatedMedia.media, colorScheme, reducedMotion, forcedColors, contrast)); + } + async updateEmulatedViewportSize() { + this._browserContext._validateEmulatedViewport(this._page.emulatedSize()?.viewport); + await this._updateViewport(); + } + async updateUserAgent() { + const contextOptions = this._browserContext._options; + this._updateState("Page.overrideUserAgent", { value: contextOptions.userAgent }); + } + async bringToFront() { + await this._pageProxySession.send("Target.activate", { + targetId: this._session.sessionId + }); + } + async _updateViewport() { + const options = this._browserContext._options; + const emulatedSize = this._page.emulatedSize(); + if (!emulatedSize) + return; + const viewportSize = emulatedSize.viewport; + const screenSize = emulatedSize.screen; + const promises = [ + this._pageProxySession.send("Emulation.setDeviceMetricsOverride", { + width: viewportSize.width, + height: viewportSize.height, + fixedLayout: !!options.isMobile, + deviceScaleFactor: options.deviceScaleFactor || 1 + }), + this._session.send("Page.setScreenSizeOverride", { + width: screenSize.width, + height: screenSize.height + }) + ]; + if (options.isMobile) { + const angle = viewportSize.width > viewportSize.height ? 90 : 0; + promises.push(this._pageProxySession.send("Emulation.setOrientationOverride", { angle })); + } + await Promise.all(promises); + if (!this._browserContext._browser?.options.headful && (hostPlatform === "ubuntu22.04-x64" || hostPlatform.startsWith("debian12"))) + await new Promise((r) => setTimeout(r, 500)); + } + async updateRequestInterception() { + const enabled = this._page.needsRequestInterception(); + await Promise.all([ + this._updateState("Network.setInterceptionEnabled", { enabled }), + this._updateState("Network.setResourceCachingDisabled", { disabled: enabled }), + this._updateState("Network.addInterception", { url: ".*", stage: "request", isRegex: true }) + ]); + } + async updateOffline() { + await this._updateState("Network.setEmulateOfflineState", { offline: !!this._browserContext._options.offline }); + } + async updateHttpCredentials() { + const credentials = this._browserContext._options.httpCredentials || { username: "", password: "", origin: "" }; + await this._pageProxySession.send("Emulation.setAuthCredentials", { username: credentials.username, password: credentials.password, origin: credentials.origin }); + } + async updateFileChooserInterception() { + const enabled = this._page.fileChooserIntercepted(); + await this._session.send("Page.setInterceptFileChooserDialog", { enabled }).catch(() => { + }); + } + async reload() { + await this._session.send("Page.reload"); + } + goBack() { + return this._session.send("Page.goBack").then(() => true).catch((error) => { + if (error instanceof Error && error.message.includes(`Protocol error (Page.goBack): Failed to go`)) + return false; + throw error; + }); + } + goForward() { + return this._session.send("Page.goForward").then(() => true).catch((error) => { + if (error instanceof Error && error.message.includes(`Protocol error (Page.goForward): Failed to go`)) + return false; + throw error; + }); + } + async requestGC() { + await this._session.send("Heap.gc"); + } + async addInitScript(initScript) { + await this._updateBootstrapScript(); + } + async removeInitScripts(initScripts) { + await this._updateBootstrapScript(); + } + async exposePlaywrightBinding() { + await this._updateState("Runtime.addBinding", { name: PageBinding.kBindingName }); + } + _calculateBootstrapScript() { + const scripts = []; + if (!this._page.browserContext._options.isMobile) { + scripts.push("delete window.orientation"); + scripts.push("delete window.ondevicemotion"); + scripts.push("delete window.ondeviceorientation"); + } + scripts.push('if (!window.safari) window.safari = { pushNotification: { toString() { return "[object SafariRemoteNotification]"; } } };'); + scripts.push("if (!window.GestureEvent) window.GestureEvent = function GestureEvent() {};"); + scripts.push(this._publicKeyCredentialScript()); + scripts.push(...this._page.allInitScripts().map((script) => script.source)); + return scripts.join(";\n"); + } + _publicKeyCredentialScript() { + function polyfill() { + window.PublicKeyCredential ??= { + async getClientCapabilities() { + return {}; + }, + async isConditionalMediationAvailable() { + return false; + }, + async isUserVerifyingPlatformAuthenticatorAvailable() { + return false; + } + }; + } + return `(${polyfill.toString()})();`; + } + async _updateBootstrapScript() { + await this._updateState("Page.setBootstrapScript", { source: this._calculateBootstrapScript() }); + } + async closePage(runBeforeUnload) { + await this._pageProxySession.sendMayFail("Target.close", { + targetId: this._session.sessionId, + runBeforeUnload + }); + } + async setBackgroundColor(color) { + await this._session.send("Page.setDefaultBackgroundColorOverride", { color }); + } + _toolbarHeight() { + if (this._page.browserContext._browser?.options.headful) { + if (hostPlatform === "mac10.15") + return 55; + if (hostPlatform === "mac26-arm64" || hostPlatform === "mac26") + return 69; + return 59; + } + return 0; + } + validateScreenshotDimension(side, omitDeviceScaleFactor) { + if (process.platform === "darwin") + return; + if (!omitDeviceScaleFactor && this._page.browserContext._options.deviceScaleFactor) + side = Math.ceil(side * this._page.browserContext._options.deviceScaleFactor); + if (side > 32767) + throw new Error("Cannot take screenshot larger than 32767 pixels on any dimension"); + } + async takeScreenshot(progress2, format2, documentRect, viewportRect, quality, fitsViewport, scale) { + const rect = documentRect || viewportRect; + const omitDeviceScaleFactor = scale === "css"; + this.validateScreenshotDimension(rect.width, omitDeviceScaleFactor); + this.validateScreenshotDimension(rect.height, omitDeviceScaleFactor); + const result2 = await progress2.race(this._session.send("Page.snapshotRect", { ...rect, coordinateSystem: documentRect ? "Page" : "Viewport", omitDeviceScaleFactor })); + const prefix = "data:image/png;base64,"; + let buffer = Buffer.from(result2.dataURL.substr(prefix.length), "base64"); + if (format2 === "jpeg") + buffer = jpegjs3.encode(PNG2.sync.read(buffer), quality).data; + return buffer; + } + async getContentFrame(handle) { + const nodeInfo = await this._session.send("DOM.describeNode", { + objectId: handle._objectId + }); + if (!nodeInfo.contentFrameId) + return null; + return this._page.frameManager.frame(nodeInfo.contentFrameId); + } + async getOwnerFrame(handle) { + if (!handle._objectId) + return null; + const nodeInfo = await this._session.send("DOM.describeNode", { + objectId: handle._objectId + }); + return nodeInfo.ownerFrameId || null; + } + async getBoundingBox(handle) { + const quads = await this.getContentQuads(handle); + if (!quads || !quads.length) + return null; + let minX = Infinity; + let maxX = -Infinity; + let minY = Infinity; + let maxY = -Infinity; + for (const quad of quads) { + for (const point of quad) { + minX = Math.min(minX, point.x); + maxX = Math.max(maxX, point.x); + minY = Math.min(minY, point.y); + maxY = Math.max(maxY, point.y); + } + } + return { x: minX, y: minY, width: maxX - minX, height: maxY - minY }; + } + async scrollRectIntoViewIfNeeded(handle, rect) { + return await this._session.send("DOM.scrollIntoViewIfNeeded", { + objectId: handle._objectId, + rect + }).then(() => "done").catch((e) => { + if (e instanceof Error && e.message.includes("Node does not have a layout object")) + return "error:notvisible"; + if (e instanceof Error && e.message.includes("Node is detached from document")) + return "error:notconnected"; + throw e; + }); + } + startScreencast(options) { + this._pageProxySession.send("Screencast.startScreencast", { + quality: options.quality, + width: options.width, + height: options.height, + toolbarHeight: this._toolbarHeight() + }).then(({ generation }) => this._screencastGeneration = generation).catch(() => { + }); + } + stopScreencast() { + this._pageProxySession.sendMayFail("Screencast.stopScreencast"); + } + _onScreencastFrame(event) { + const generation = this._screencastGeneration; + const buffer = Buffer.from(event.data, "base64"); + this._page.screencast.onScreencastFrame({ + buffer, + frameSwapWallTime: event.timestamp ? event.timestamp * 1e3 : Date.now(), + viewportWidth: event.deviceWidth, + viewportHeight: event.deviceHeight + }, () => { + this._pageProxySession.sendMayFail("Screencast.screencastFrameAck", { generation }); + }); + } + rafCountForStablePosition() { + return process.platform === "win32" ? 5 : 1; + } + async getContentQuads(handle) { + const result2 = await this._session.sendMayFail("DOM.getContentQuads", { + objectId: handle._objectId + }); + if (!result2) + return null; + return result2.quads.map((quad) => [ + { x: quad[0], y: quad[1] }, + { x: quad[2], y: quad[3] }, + { x: quad[4], y: quad[5] }, + { x: quad[6], y: quad[7] } + ]); + } + async setInputFilePaths(progress2, handle, paths) { + const pageProxyId = this._pageProxySession.sessionId; + const objectId = handle._objectId; + if (this._browserContext._browser?.options.channel === "webkit-wsl") + paths = await progress2.race(Promise.all(paths.map((path59) => translatePathToWSL(path59)))); + await progress2.race(Promise.all([ + this._pageProxySession.connection.browserSession.send("Playwright.grantFileReadAccess", { pageProxyId, paths }), + this._session.send("DOM.setInputFiles", { objectId, paths }) + ])); + } + async adoptElementHandle(handle, to) { + const result2 = await this._session.sendMayFail("DOM.resolveNode", { + objectId: handle._objectId, + executionContextId: to.delegate._contextId + }); + if (!result2 || result2.object.subtype === "null") + throw new Error(kUnableToAdoptErrorMessage); + return createHandle4(to, result2.object); + } + async inputActionEpilogue() { + } + async resetForReuse(progress2) { + } + async getFrameElement(frame) { + const parent = frame.parentFrame(); + if (!parent) + throw new Error("Frame has been detached."); + const context2 = await parent.mainContext(); + const result2 = await this._session.send("DOM.resolveNode", { + frameId: frame._id, + executionContextId: context2.delegate._contextId + }); + if (!result2 || result2.object.subtype === "null") + throw new Error("Frame has been detached."); + return createHandle4(context2, result2.object); + } + _maybeCancelCoopNavigationRequest(provisionalPage) { + const navigationRequest = provisionalPage.coopNavigationRequest(); + for (const [requestId, request2] of this._requestIdToRequest) { + if (request2.request === navigationRequest) { + this._onLoadingFailed(provisionalPage._session, { + requestId, + errorText: "Provisiolal navigation canceled.", + timestamp: request2._timestamp, + canceled: true + }); + return; + } + } + } + _adoptRequestFromNewProcess(navigationRequest, newSession, newRequestId) { + for (const [requestId, request2] of this._requestIdToRequest) { + if (request2.request === navigationRequest) { + this._requestIdToRequest.delete(requestId); + request2.adoptRequestFromNewProcess(newSession, newRequestId); + this._requestIdToRequest.set(newRequestId, request2); + return; + } + } + } + _onRequestWillBeSent(session2, event) { + if (event.request.url.startsWith("data:")) + return; + if (event.request.url.startsWith("about:")) + return; + if (this._page.needsRequestInterception() && !event.redirectResponse) + this._requestIdToRequestWillBeSentEvent.set(event.requestId, event); + else + this._onRequest(session2, event, false); + } + _onRequest(session2, event, intercepted) { + let redirectedFrom = null; + if (event.redirectResponse) { + const request3 = this._requestIdToRequest.get(event.requestId); + if (request3) { + this._handleRequestRedirect(request3, event.requestId, event.redirectResponse, event.timestamp); + redirectedFrom = request3; + } + } + const frame = redirectedFrom ? redirectedFrom.request.frame() : this._page.frameManager.frame(event.frameId); + if (!frame) + return; + const isNavigationRequest = event.type === "Document"; + const documentId = isNavigationRequest ? event.loaderId : void 0; + const request2 = new WKInterceptableRequest(session2, frame, event, redirectedFrom, documentId); + let route2; + if (intercepted) { + route2 = new WKRouteImpl(session2, event.requestId); + request2.request.setRawRequestHeaders(null); + } + this._requestIdToRequest.set(event.requestId, request2); + this._page.frameManager.requestStarted(request2.request, route2); + } + _handleRequestRedirect(request2, requestId, responsePayload, timestamp) { + const response2 = request2.createResponse(responsePayload); + response2._setHttpVersion(null); + response2._securityDetailsFinished(); + response2._serverAddrFinished(); + response2.setResponseHeadersSize(null); + response2.setEncodedBodySize(null); + response2._requestFinished(responsePayload.timing ? helper.secondsToRoundishMillis(timestamp - request2._timestamp) : -1); + this._requestIdToRequest.delete(requestId); + this._page.frameManager.requestReceivedResponse(response2); + this._page.frameManager.reportRequestFinished(request2.request, response2); + } + _onRequestIntercepted(session2, event) { + const requestWillBeSentEvent = this._requestIdToRequestWillBeSentEvent.get(event.requestId); + if (!requestWillBeSentEvent) { + session2.sendMayFail("Network.interceptWithRequest", { requestId: event.requestId }); + return; + } + this._requestIdToRequestWillBeSentEvent.delete(event.requestId); + this._onRequest(session2, requestWillBeSentEvent, true); + } + _onResponseReceived(session2, event) { + const requestWillBeSentEvent = this._requestIdToRequestWillBeSentEvent.get(event.requestId); + if (requestWillBeSentEvent) { + this._requestIdToRequestWillBeSentEvent.delete(event.requestId); + this._onRequest(session2, requestWillBeSentEvent, false); + } + const request2 = this._requestIdToRequest.get(event.requestId); + if (!request2) + return; + this._requestIdToResponseReceivedPayloadEvent.set(event.requestId, event); + const response2 = request2.createResponse(event.response); + this._page.frameManager.requestReceivedResponse(response2); + if (response2.status() === 204 && request2.request.isNavigationRequest()) { + this._onLoadingFailed(session2, { + requestId: event.requestId, + errorText: "Aborted: 204 No Content", + timestamp: event.timestamp + }); + } + } + _onLoadingFinished(event) { + const request2 = this._requestIdToRequest.get(event.requestId); + if (!request2) + return; + const response2 = request2.request._existingResponse(); + if (response2) { + const responseReceivedPayload = this._requestIdToResponseReceivedPayloadEvent.get(event.requestId); + response2._serverAddrFinished(parseRemoteAddress(event?.metrics?.remoteAddress)); + response2._securityDetailsFinished({ + protocol: isLoadedSecurely(response2.url(), response2.timing()) ? event.metrics?.securityConnection?.protocol : void 0, + subjectName: responseReceivedPayload?.response.security?.certificate?.subject, + validFrom: responseReceivedPayload?.response.security?.certificate?.validFrom, + validTo: responseReceivedPayload?.response.security?.certificate?.validUntil + }); + response2._setHttpVersion(event.metrics?.protocol ?? null); + response2.setEncodedBodySize(event.metrics?.responseBodyBytesReceived ?? null); + response2.setResponseHeadersSize(event.metrics?.responseHeaderBytesReceived ?? null); + response2._requestFinished(helper.secondsToRoundishMillis(event.timestamp - request2._timestamp)); + } else { + request2.request.setRawRequestHeaders(null); + } + this._requestIdToResponseReceivedPayloadEvent.delete(event.requestId); + this._requestIdToRequest.delete(event.requestId); + this._page.frameManager.reportRequestFinished(request2.request, response2); + } + _onLoadingFailed(session2, event) { + const requestWillBeSentEvent = this._requestIdToRequestWillBeSentEvent.get(event.requestId); + if (requestWillBeSentEvent) { + this._requestIdToRequestWillBeSentEvent.delete(event.requestId); + this._onRequest(session2, requestWillBeSentEvent, false); + } + const request2 = this._requestIdToRequest.get(event.requestId); + if (!request2) + return; + const response2 = request2.request._existingResponse(); + if (response2) { + response2._serverAddrFinished(); + response2._securityDetailsFinished(); + response2._setHttpVersion(null); + response2.setResponseHeadersSize(null); + response2.setEncodedBodySize(null); + response2._requestFinished(helper.secondsToRoundishMillis(event.timestamp - request2._timestamp)); + } else { + request2.request.setRawRequestHeaders(null); + } + this._requestIdToRequest.delete(event.requestId); + request2.request._setFailureText(event.errorText); + this._page.frameManager.requestFailed(request2.request, event.errorText.includes("cancelled")); + } + _onWebSocketWillSendHandshakeRequest(event) { + const wallTimeMs = event.walltime * 1e3; + this._timestampBaselineForWebSocket.set(event.requestId, wallTimeMs - event.timestamp * 1e3); + this._page.frameManager.onWebSocketRequest(event.requestId, headersObjectToArray(event.request.headers), wallTimeMs); + } + _onWebSocketClosed(event) { + this._timestampBaselineForWebSocket.delete(event.requestId); + this._page.frameManager.webSocketClosed(event.requestId); + } + _timestampToWallTimeMsForWebSocket(requestId, timestamp) { + return this._timestampBaselineForWebSocket.get(requestId) + timestamp * 1e3; + } + async _grantPermissions(origin, permissions) { + const webPermissionToProtocol = /* @__PURE__ */ new Map([ + ["geolocation", "geolocation"], + ["notifications", "notifications"], + ["clipboard-read", "clipboard-read"], + ["screen-wake-lock", "screen-wake-lock"] + ]); + const filtered = permissions.map((permission) => { + const protocolPermission = webPermissionToProtocol.get(permission); + if (!protocolPermission) + throw new Error("Unknown permission: " + permission); + return protocolPermission; + }); + await this._pageProxySession.send("Emulation.grantPermissions", { origin, permissions: filtered }); + } + async _clearPermissions() { + await this._pageProxySession.send("Emulation.resetPermissions", {}); + } + shouldToggleStyleSheetToSyncAnimations() { + return true; + } + async setDockTile(image) { + } + }; + WKFrame = class { + constructor(page, session2) { + this._sessionListeners = []; + this._initializePromise = null; + this._page = page; + this._session = session2; + } + async initialize() { + if (this._initializePromise) + return this._initializePromise; + this._initializePromise = this._initializeImpl(); + return this._initializePromise; + } + async _initializeImpl() { + this._sessionListeners = [ + eventsHelper.addEventListener(this._session, "Console.messageAdded", (event) => this._page._onConsoleMessage(event)), + eventsHelper.addEventListener(this._session, "Console.messageRepeatCountUpdated", (event) => this._page._onConsoleRepeatCountUpdated(event)) + ]; + await this._session.send("Console.enable"); + } + dispose() { + eventsHelper.removeEventListeners(this._sessionListeners); + this._session.dispose(); + } + }; + } +}); + +// packages/playwright-core/src/server/webkit/wkBrowser.ts +var BROWSER_VERSION, DEFAULT_USER_AGENT, WKBrowser, WKBrowserContext; +var init_wkBrowser = __esm({ + "packages/playwright-core/src/server/webkit/wkBrowser.ts"() { + "use strict"; + init_assert(); + init_browser(); + init_browserContext(); + init_network2(); + init_wkConnection(); + init_wkPage(); + init_webkit(); + BROWSER_VERSION = "26.5"; + DEFAULT_USER_AGENT = `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/${BROWSER_VERSION} Safari/605.1.15`; + WKBrowser = class _WKBrowser extends Browser { + constructor(parent, transport, options) { + super(parent, options); + this._contexts = /* @__PURE__ */ new Map(); + this._wkPages = /* @__PURE__ */ new Map(); + this._connection = new WKConnection(transport, this._onDisconnect.bind(this), options.protocolLogger, options.browserLogsCollector); + this._browserSession = this._connection.browserSession; + this._browserSession.on("Playwright.pageProxyCreated", this._onPageProxyCreated.bind(this)); + this._browserSession.on("Playwright.pageProxyDestroyed", this._onPageProxyDestroyed.bind(this)); + this._browserSession.on("Playwright.provisionalLoadFailed", (event) => this._onProvisionalLoadFailed(event)); + this._browserSession.on("Playwright.windowOpen", (event) => this._onWindowOpen(event)); + this._browserSession.on("Playwright.downloadCreated", this._onDownloadCreated.bind(this)); + this._browserSession.on("Playwright.downloadFilenameSuggested", this._onDownloadFilenameSuggested.bind(this)); + this._browserSession.on("Playwright.downloadFinished", this._onDownloadFinished.bind(this)); + this._browserSession.on(kPageProxyMessageReceived, this._onPageProxyMessageReceived.bind(this)); + } + static async connect(parent, transport, options) { + const browser = new _WKBrowser(parent, transport, options); + if (options.__testHookOnConnectToBrowser) + await options.__testHookOnConnectToBrowser(); + const promises = [ + browser._browserSession.send("Playwright.enable") + ]; + if (options.persistent) { + options.persistent.userAgent ||= DEFAULT_USER_AGENT; + browser._defaultContext = new WKBrowserContext(browser, void 0, options.persistent); + promises.push(browser._defaultContext.initialize()); + } + await Promise.all(promises); + return browser; + } + _onDisconnect() { + for (const wkPage of this._wkPages.values()) + wkPage.didClose(); + this._wkPages.clear(); + this.didClose(); + } + async doCreateNewContext(options) { + const proxy = options.proxyOverride || options.proxy; + const createOptions = proxy ? { + // Enable socks5 hostname resolution on Windows. + // See https://github.com/microsoft/playwright/issues/20451 + proxyServer: process.platform === "win32" && this.attribution.browser?.options.channel !== "webkit-wsl" ? proxy.server.replace(/^socks5:\/\//, "socks5h://") : proxy.server, + proxyBypassList: proxy.bypass + } : void 0; + const { browserContextId } = await this._browserSession.send("Playwright.createContext", createOptions); + options.userAgent = options.userAgent || DEFAULT_USER_AGENT; + const context2 = new WKBrowserContext(this, browserContextId, options); + await context2.initialize(); + this._contexts.set(browserContextId, context2); + return context2; + } + contexts() { + return Array.from(this._contexts.values()); + } + version() { + return BROWSER_VERSION; + } + userAgent() { + return DEFAULT_USER_AGENT; + } + _onDownloadCreated(payload) { + const page = this._wkPages.get(payload.pageProxyId); + if (!page) + return; + let frameId = payload.frameId; + if (!page._page.frameManager.frame(frameId)) + frameId = page._page.mainFrame()._id; + page._page.frameManager.frameAbortedNavigation(frameId, "Download is starting"); + let originPage = page._page.initializedOrUndefined(); + if (!originPage) { + page._firstNonInitialNavigationCommittedReject(new Error("Starting new page download")); + if (page._opener) + originPage = page._opener._page.initializedOrUndefined(); + } + if (!originPage) + return; + this.downloadCreated(originPage, payload.uuid, payload.url); + } + _onDownloadFilenameSuggested(payload) { + this.downloadFilenameSuggested(payload.uuid, payload.suggestedFilename); + } + _onDownloadFinished(payload) { + this.downloadFinished(payload.uuid, payload.error); + } + _onPageProxyCreated(event) { + const pageProxyId = event.pageProxyId; + let context2 = null; + if (event.browserContextId) { + context2 = this._contexts.get(event.browserContextId) || null; + } + if (!context2) + context2 = this._defaultContext; + if (!context2) + return; + const pageProxySession = new WKSession(this._connection, pageProxyId, (message) => { + this._connection.rawSend({ ...message, pageProxyId }); + }); + const opener = event.openerId ? this._wkPages.get(event.openerId) : void 0; + const wkPage = new WKPage(context2, pageProxySession, opener || null); + this._wkPages.set(pageProxyId, wkPage); + } + _onPageProxyDestroyed(event) { + const pageProxyId = event.pageProxyId; + const wkPage = this._wkPages.get(pageProxyId); + if (!wkPage) + return; + this._wkPages.delete(pageProxyId); + wkPage.didClose(); + } + _onPageProxyMessageReceived(event) { + const wkPage = this._wkPages.get(event.pageProxyId); + if (!wkPage) + return; + wkPage.dispatchMessageToSession(event.message); + } + _onProvisionalLoadFailed(event) { + const wkPage = this._wkPages.get(event.pageProxyId); + if (!wkPage) + return; + wkPage.handleProvisionalLoadFailed(event); + } + _onWindowOpen(event) { + const wkPage = this._wkPages.get(event.pageProxyId); + if (!wkPage) + return; + wkPage.handleWindowOpen(event); + } + isConnected() { + return !this._connection.isClosed(); + } + }; + WKBrowserContext = class extends BrowserContext { + constructor(browser, browserContextId, options) { + super(browser, options, browserContextId); + this._validateEmulatedViewport(options.viewport); + this.authenticateProxyViaHeader(); + } + async initialize() { + assert(!this._wkPages().length); + const browserContextId = this._browserContextId; + const promises = [super.initialize()]; + promises.push(this._browser._browserSession.send("Playwright.setDownloadBehavior", { + behavior: this._options.acceptDownloads === "accept" ? "allow" : "deny", + downloadPath: this._browser.options.channel === "webkit-wsl" ? await translatePathToWSL(this._browser.options.downloadsPath) : this._browser.options.downloadsPath, + browserContextId + })); + if (this._options.ignoreHTTPSErrors || this._options.internalIgnoreHTTPSErrors) + promises.push(this._browser._browserSession.send("Playwright.setIgnoreCertificateErrors", { browserContextId, ignore: true })); + if (this._options.locale) + promises.push(this._browser._browserSession.send("Playwright.setLanguages", { browserContextId, languages: [this._options.locale] })); + if (this._options.geolocation) + promises.push(this.setGeolocation(this._options.geolocation)); + if (this._options.offline) + promises.push(this.doUpdateOffline()); + if (this._options.httpCredentials) + promises.push(this.innerSetHTTPCredentials(this._options.httpCredentials)); + await Promise.all(promises); + } + _wkPages() { + return Array.from(this._browser._wkPages.values()).filter((wkPage) => wkPage._browserContext === this); + } + possiblyUninitializedPages() { + return this._wkPages().map((wkPage) => wkPage._page); + } + async doCreateNewPage() { + const { pageProxyId } = await this._browser._browserSession.send("Playwright.createPage", { browserContextId: this._browserContextId }); + return this._browser._wkPages.get(pageProxyId)._page; + } + async doGetCookies(urls) { + const { cookies } = await this._browser._browserSession.send("Playwright.getAllCookies", { browserContextId: this._browserContextId }); + return filterCookies(cookies.map((c) => { + const { name, value: value2, domain, path: path59, expires, httpOnly, secure, sameSite } = c; + const copy = { + name, + value: value2, + domain, + path: path59, + expires: expires === -1 ? -1 : expires / 1e3, + httpOnly, + secure, + sameSite + }; + return copy; + }), urls); + } + async addCookies(cookies) { + const cc = rewriteCookies(cookies).map((c) => { + const { name, value: value2, domain, path: path59, expires, httpOnly, secure, sameSite } = c; + const copy = { + name, + value: value2, + domain, + path: path59, + expires: expires && expires !== -1 ? expires * 1e3 : expires, + httpOnly, + secure, + sameSite, + session: expires === -1 || expires === void 0 + }; + return copy; + }); + await this._browser._browserSession.send("Playwright.setCookies", { cookies: cc, browserContextId: this._browserContextId }); + } + async doClearCookies() { + await this._browser._browserSession.send("Playwright.deleteAllCookies", { browserContextId: this._browserContextId }); + } + async doGrantPermissions(origin, permissions) { + await Promise.all(this.pages().map((page) => page.delegate._grantPermissions(origin, permissions))); + } + async doClearPermissions() { + await Promise.all(this.pages().map((page) => page.delegate._clearPermissions())); + } + async setGeolocation(geolocation) { + verifyGeolocation(geolocation); + this._options.geolocation = geolocation; + const payload = geolocation ? { ...geolocation, timestamp: Date.now() } : void 0; + await this._browser._browserSession.send("Playwright.setGeolocationOverride", { browserContextId: this._browserContextId, geolocation: payload }); + } + async doUpdateExtraHTTPHeaders() { + for (const page of this.pages()) + await page.delegate.updateExtraHTTPHeaders(); + } + async setUserAgent(userAgent) { + this._options.userAgent = userAgent; + for (const page of this.pages()) + await page.delegate.updateUserAgent(); + } + async doUpdateOffline() { + for (const page of this.pages()) + await page.delegate.updateOffline(); + } + async doSetHTTPCredentials(httpCredentials) { + this._options.httpCredentials = httpCredentials; + for (const page of this.pages()) + await page.delegate.updateHttpCredentials(); + } + async doAddInitScript(initScript) { + for (const page of this.pages()) + await page.delegate._updateBootstrapScript(); + } + async doRemoveInitScripts(initScripts) { + for (const page of this.pages()) + await page.delegate._updateBootstrapScript(); + } + async doUpdateRequestInterception() { + for (const page of this.pages()) + await page.delegate.updateRequestInterception(); + } + async doUpdateDefaultViewport() { + } + async doUpdateDefaultEmulatedMedia() { + } + async doExposePlaywrightBinding() { + for (const page of this.pages()) + await page.delegate.exposePlaywrightBinding(); + } + onClosePersistent() { + } + async clearCache() { + await this._browser._browserSession.send("Playwright.clearMemoryCache", { + browserContextId: this._browserContextId + }); + } + async doClose(reason) { + if (!this._browserContextId) { + return "close-browser"; + } else { + await this._browser._browserSession.send("Playwright.deleteContext", { browserContextId: this._browserContextId }); + this._browser._contexts.delete(this._browserContextId); + } + } + async cancelDownload(uuid) { + await this._browser._browserSession.send("Playwright.cancelDownload", { uuid }); + } + _validateEmulatedViewport(viewportSize) { + if (!viewportSize) + return; + if (process.platform === "win32" && this._browser.options.headful && (viewportSize.width < 250 || viewportSize.height < 240)) + throw new Error(`WebKit on Windows has a minimal viewport of 250x240.`); + } + }; + } +}); + +// packages/playwright-core/src/server/webkit/webview/dialogBridge.ts +var DialogBridge; +var init_dialogBridge = __esm({ + "packages/playwright-core/src/server/webkit/webview/dialogBridge.ts"() { + "use strict"; + init_debugLogger(); + init_network(); + DialogBridge = class _DialogBridge { + constructor(server, baseUrl) { + this._handlers = /* @__PURE__ */ new Map(); + this._server = server; + this._baseUrl = baseUrl; + this._server.on("request", (req, res) => this._handleRequest(req, res)); + } + static async start() { + const server = createHttpServer(); + await new Promise((resolve, reject) => { + server.once("error", reject); + server.listen(0, "127.0.0.1", () => { + server.removeListener("error", reject); + resolve(); + }); + }); + const address = server.address(); + if (!address || typeof address === "string") + throw new Error("DialogBridge: failed to bind HTTP server"); + return new _DialogBridge(server, `http://127.0.0.1:${address.port}`); + } + endpointFor(pageId4) { + return `${this._baseUrl}/dialog?tab=${encodeURIComponent(pageId4)}`; + } + registerTab(pageId4, handler) { + this._handlers.set(pageId4, handler); + } + unregisterTab(pageId4) { + this._handlers.delete(pageId4); + } + async close() { + this._handlers.clear(); + await new Promise((resolve) => this._server.close(() => resolve())); + } + _writeCorsHeaders(res) { + res.setHeader("Access-Control-Allow-Origin", "*"); + res.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS"); + res.setHeader("Access-Control-Allow-Headers", "content-type"); + } + async _handleRequest(req, res) { + this._writeCorsHeaders(res); + if (req.method === "OPTIONS") { + res.statusCode = 204; + res.end(); + return; + } + const url2 = new URL(req.url || "/", this._baseUrl); + if (!(req.method === "POST" && url2.pathname === "/dialog")) { + res.statusCode = 404; + res.end(); + return; + } + const tab2 = url2.searchParams.get("tab") || ""; + const handler = this._handlers.get(tab2); + if (!handler) { + res.statusCode = 404; + res.end(); + return; + } + let body = ""; + req.setEncoding("utf8"); + req.on("data", (chunk) => { + body += chunk; + }); + req.on("end", async () => { + let parsed; + try { + const json = JSON.parse(body); + if (json.type !== "alert" && json.type !== "confirm" && json.type !== "prompt") + throw new Error(`Invalid dialog type: ${json.type}`); + parsed = { + type: json.type, + message: typeof json.message === "string" ? json.message : "", + defaultValue: typeof json.defaultValue === "string" ? json.defaultValue : "" + }; + } catch (e) { + debugLogger.log("error", `DialogBridge: bad request body: ${e.message}`); + res.statusCode = 400; + res.end(); + return; + } + try { + const result2 = await handler(parsed); + res.statusCode = 200; + res.setHeader("Content-Type", "application/json"); + res.end(JSON.stringify({ + accept: !!result2.accept, + promptText: result2.promptText + })); + } catch (e) { + debugLogger.log("error", `DialogBridge: handler error: ${e.message}`); + res.statusCode = 500; + res.end(); + } + }); + } + }; + } +}); + +// packages/playwright-core/src/server/webkit/webview/wvConnection.ts +var import_events12, WVConnection, WVSession; +var init_wvConnection = __esm({ + "packages/playwright-core/src/server/webkit/webview/wvConnection.ts"() { + "use strict"; + import_events12 = require("events"); + init_debugLogger(); + init_assert(); + init_helper(); + init_protocolError(); + WVConnection = class { + constructor(transport, onDisconnect, protocolLogger, browserLogsCollector) { + this._lastId = 0; + this._closed = false; + this._transport = transport; + this._onDisconnect = onDisconnect; + this._protocolLogger = protocolLogger; + this._browserLogsCollector = browserLogsCollector; + this.outerSession = new WVSession(this, "", (message) => { + this.rawSend(message); + }); + this._transport.onmessage = this._dispatchMessage.bind(this); + this._transport.onclose = this._onClose.bind(this); + } + nextMessageId() { + return ++this._lastId; + } + rawSend(message) { + this._protocolLogger("send", message); + this._transport.send(message); + } + _dispatchMessage(message) { + this._protocolLogger("receive", message); + this.outerSession.dispatchMessage(message); + } + _onClose(reason) { + this._closed = true; + this._transport.onmessage = void 0; + this._transport.onclose = void 0; + this._browserDisconnectedLogs = helper.formatBrowserLogs(this._browserLogsCollector.recentLogs(), reason); + this.outerSession.dispose(); + this._onDisconnect(); + } + isClosed() { + return this._closed; + } + close() { + if (!this._closed) + this._transport.close(); + } + }; + WVSession = class extends import_events12.EventEmitter { + constructor(connection, sessionId, rawSend) { + super(); + this._disposed = false; + this._callbacks = /* @__PURE__ */ new Map(); + this._crashed = false; + this.setMaxListeners(0); + this.connection = connection; + this.sessionId = sessionId; + this._rawSend = rawSend; + } + async send(method, params2) { + if (this._crashed || this._disposed || this.connection._browserDisconnectedLogs) + throw new ProtocolError(this._crashed ? "crashed" : "closed", void 0, this.connection._browserDisconnectedLogs); + const id = this.connection.nextMessageId(); + const messageObj = { id, method, params: params2 }; + this._rawSend(messageObj); + return new Promise((resolve, reject) => { + this._callbacks.set(id, { resolve, reject, error: new ProtocolError("error", method) }); + }); + } + sendMayFail(method, params2) { + return this.send(method, params2).catch((error) => debugLogger.log("error", error)); + } + markAsCrashed() { + this._crashed = true; + } + isDisposed() { + return this._disposed; + } + dispose() { + for (const callback of this._callbacks.values()) { + callback.error.type = this._crashed ? "crashed" : "closed"; + callback.error.logs = this.connection._browserDisconnectedLogs; + callback.reject(callback.error); + } + this._callbacks.clear(); + this._disposed = true; + } + dispatchMessage(object) { + if (object.id && this._callbacks.has(object.id)) { + const callback = this._callbacks.get(object.id); + this._callbacks.delete(object.id); + if (object.error) { + callback.error.setMessage(object.error.message); + callback.reject(callback.error); + } else { + callback.resolve(object.result); + } + } else if (object.id && !object.error) { + assert(this.isDisposed(), JSON.stringify(object)); + } else { + Promise.resolve().then(() => this.emit(object.method, object.params)); + } + } + }; + } +}); + +// packages/playwright-core/src/generated/webViewInputSource.ts +var source7; +var init_webViewInputSource = __esm({ + "packages/playwright-core/src/generated/webViewInputSource.ts"() { + "use strict"; + source7 = ` +var __commonJS = obj => { + let required = false; + let result; + return function __require() { + if (!required) { + required = true; + let fn; + for (const name in obj) { fn = obj[name]; break; } + const module = { exports: {} }; + fn(module.exports, module); + result = module.exports; + } + return result; + } +}; +var __export = (target, all) => {for (var name in all) target[name] = all[name];}; +var __toESM = mod => ({ ...mod, 'default': mod }); +var __toCommonJS = mod => ({ ...mod, __esModule: true }); + + +// packages/injected/src/webview/webViewInput.ts +var webViewInput_exports = {}; +__export(webViewInput_exports, { + WebViewInput: () => WebViewInput, + default: () => webViewInput_default +}); +module.exports = __toCommonJS(webViewInput_exports); +var kTrustedSynthetic = "__pwTrustedSynthetic"; +function markAndDispatch(node, event) { + Object.defineProperty(event, kTrustedSynthetic, { value: true }); + return node.dispatchEvent(event); +} +var kNamedKeyIdentifiers = { + 8: "U+0008", + // Backspace + 9: "U+0009", + // Tab + 13: "Enter", + 16: "Shift", + 17: "Control", + 18: "Alt", + 27: "U+001B", + // Escape + 33: "PageUp", + 34: "PageDown", + 35: "End", + 36: "Home", + 37: "Left", + 38: "Up", + 39: "Right", + 40: "Down", + 45: "Insert", + 46: "U+007F" + // Delete +}; +function keyIdentifierFor(keyCode, key) { + const named = kNamedKeyIdentifiers[keyCode]; + if (named !== void 0) + return named; + if (keyCode >= 112 && keyCode <= 135) + return "F" + (keyCode - 111); + if (key.length === 1) + return "U+" + key.toUpperCase().charCodeAt(0).toString(16).toUpperCase().padStart(4, "0"); + return ""; +} +function dispatchKeyEvent(node, type, init, keyCode, key) { + const event = new KeyboardEvent(type, init); + Object.defineProperty(event, "keyIdentifier", { value: keyIdentifierFor(keyCode, key), configurable: true }); + return markAndDispatch(node, event); +} +var WebViewInput = class { + constructor(window, document) { + this._hoverTarget = null; + this._window = window; + this._document = document; + this._setTimeout = (window.__pwSnapshotGlobals || window).setTimeout; + } + // Run each event in its own task (like real input). + _postTask(task) { + return new Promise((resolve) => { + this._setTimeout.call(this._window, () => { + try { + task(); + } finally { + resolve(); + } + }); + }); + } + // Descend through open shadow roots so synthetic events land on the actual + // element under the pointer rather than on the shadow host. + _deepElementFromPoint(x, y) { + let el = this._document.elementFromPoint(x, y); + while (el && el.shadowRoot) { + const inner = el.shadowRoot.elementFromPoint(x, y); + if (!inner || inner === el) + break; + el = inner; + } + return el; + } + // The focused element may live inside one or more shadow roots, where + // document.activeElement only reports the outermost shadow host. + _deepActiveElement() { + let active = this._document.activeElement; + while (active && active.shadowRoot && active.shadowRoot.activeElement) + active = active.shadowRoot.activeElement; + return active; + } + _insertText(target, text) { + var _a, _b; + if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement) { + const start = (_a = target.selectionStart) != null ? _a : target.value.length; + const end = (_b = target.selectionEnd) != null ? _b : target.value.length; + target.value = target.value.slice(0, start) + text + target.value.slice(end); + const pos = start + text.length; + try { + target.setSelectionRange(pos, pos); + } catch { + } + target.dispatchEvent(new InputEvent("input", { bubbles: true, cancelable: false, data: text, inputType: "insertText" })); + } else if (target && target.isContentEditable) { + this._document.execCommand("insertText", false, text); + } + } + keydown(params) { + const target = this._deepActiveElement() || this._document.body; + if (!target) + return Promise.resolve(); + const init = { + bubbles: true, + cancelable: true, + view: this._window, + code: params.code, + key: params.key, + keyCode: params.keyCode, + which: params.keyCode, + location: params.location, + repeat: params.repeat, + ctrlKey: params.ctrlKey, + shiftKey: params.shiftKey, + altKey: params.altKey, + metaKey: params.metaKey + }; + let notPrevented = true; + let charNotPrevented = true; + let lastTask = this._postTask(() => { + notPrevented = dispatchKeyEvent(target, "keydown", init, params.keyCode, params.key); + }); + if (params.text !== void 0) { + const text = params.text; + void this._postTask(() => { + if (!notPrevented) + return; + const charCode = text.charCodeAt(0); + charNotPrevented = markAndDispatch(target, new KeyboardEvent("keypress", { ...init, charCode, keyCode: charCode, which: charCode })); + }); + lastTask = this._postTask(() => { + if (!notPrevented || !charNotPrevented) + return; + this._dispatchTextInput(target, text === "\\r" ? "\\n" : text); + }); + } + return lastTask; + } + _dispatchTextInput(target, text) { + const event = this._document.createEvent("TextEvent"); + event.initTextEvent("textInput", true, true, this._window, text); + markAndDispatch(target, event); + } + keyup(params) { + const target = this._deepActiveElement() || this._document.body; + if (!target) + return Promise.resolve(); + return this._postTask(() => { + dispatchKeyEvent(target, "keyup", { + bubbles: true, + cancelable: true, + view: this._window, + code: params.code, + key: params.key, + keyCode: params.keyCode, + which: params.keyCode, + location: params.location, + ctrlKey: params.ctrlKey, + shiftKey: params.shiftKey, + altKey: params.altKey, + metaKey: params.metaKey + }, params.keyCode, params.key); + }); + } + insertText(text) { + return this._postTask(() => this._insertText(this._deepActiveElement(), text)); + } + mouseMove(params) { + const target = this._deepElementFromPoint(params.x, params.y) || this._document.documentElement; + const base = { + bubbles: true, + cancelable: true, + view: this._window, + clientX: params.x, + clientY: params.y, + screenX: params.x, + screenY: params.y, + button: params.button, + buttons: params.buttons, + ctrlKey: params.ctrlKey, + shiftKey: params.shiftKey, + altKey: params.altKey, + metaKey: params.metaKey + }; + const pointer = { ...base, pointerId: 1, pointerType: "mouse", isPrimary: true }; + const prev = this._hoverTarget; + if (prev !== target) { + if (prev && prev.isConnected) { + void this._postTask(() => markAndDispatch(prev, new PointerEvent("pointerout", { ...pointer, relatedTarget: target }))); + void this._postTask(() => markAndDispatch(prev, new MouseEvent("mouseout", { ...base, relatedTarget: target }))); + void this._postTask(() => markAndDispatch(prev, new PointerEvent("pointerleave", { ...pointer, bubbles: false, cancelable: false, relatedTarget: target }))); + void this._postTask(() => markAndDispatch(prev, new MouseEvent("mouseleave", { ...base, bubbles: false, cancelable: false, relatedTarget: target }))); + } + void this._postTask(() => markAndDispatch(target, new PointerEvent("pointerover", { ...pointer, relatedTarget: prev }))); + void this._postTask(() => markAndDispatch(target, new MouseEvent("mouseover", { ...base, relatedTarget: prev }))); + void this._postTask(() => markAndDispatch(target, new PointerEvent("pointerenter", { ...pointer, bubbles: false, cancelable: false, relatedTarget: prev }))); + void this._postTask(() => markAndDispatch(target, new MouseEvent("mouseenter", { ...base, bubbles: false, cancelable: false, relatedTarget: prev }))); + this._hoverTarget = target; + } + void this._postTask(() => markAndDispatch(target, new PointerEvent("pointermove", pointer))); + return this._postTask(() => markAndDispatch(target, new MouseEvent("mousemove", base))); + } + mouseEvent(params) { + return this._postTask(() => { + const target = this._deepElementFromPoint(params.x, params.y) || this._document.documentElement; + markAndDispatch(target, new MouseEvent(params.type, { + bubbles: true, + cancelable: true, + view: this._window, + clientX: params.x, + clientY: params.y, + screenX: params.x, + screenY: params.y, + button: params.button, + buttons: params.buttons, + detail: params.clickCount, + ctrlKey: params.ctrlKey, + shiftKey: params.shiftKey, + altKey: params.altKey, + metaKey: params.metaKey + })); + }); + } + wheel(params) { + return this._postTask(() => { + const target = this._deepElementFromPoint(params.x, params.y) || this._document.documentElement; + markAndDispatch(target, new WheelEvent("wheel", { + bubbles: true, + cancelable: true, + view: this._window, + clientX: params.x, + clientY: params.y, + screenX: params.x, + screenY: params.y, + deltaX: params.deltaX, + deltaY: params.deltaY, + deltaMode: 0, + ctrlKey: params.ctrlKey, + shiftKey: params.shiftKey, + altKey: params.altKey, + metaKey: params.metaKey + })); + this._window.scrollBy(params.deltaX, params.deltaY); + }); + } + tap(params) { + const target = this._deepElementFromPoint(params.x, params.y) || this._document.documentElement; + const init = { + bubbles: true, + cancelable: true, + view: this._window, + clientX: params.x, + clientY: params.y, + screenX: params.x, + screenY: params.y, + ctrlKey: params.ctrlKey, + shiftKey: params.shiftKey, + altKey: params.altKey, + metaKey: params.metaKey + }; + try { + const touch = new Touch({ identifier: 0, target, clientX: params.x, clientY: params.y, screenX: params.x, screenY: params.y, pageX: params.x, pageY: params.y, radiusX: 1, radiusY: 1, rotationAngle: 0, force: 1 }); + void this._postTask(() => markAndDispatch(target, new TouchEvent("touchstart", { ...init, touches: [touch], targetTouches: [touch], changedTouches: [touch] }))); + void this._postTask(() => markAndDispatch(target, new TouchEvent("touchend", { ...init, touches: [], targetTouches: [], changedTouches: [touch] }))); + } catch { + } + void this._postTask(() => markAndDispatch(target, new MouseEvent("mousedown", { ...init, button: 0, buttons: 1, detail: 1 }))); + void this._postTask(() => markAndDispatch(target, new MouseEvent("mouseup", { ...init, button: 0, buttons: 0, detail: 1 }))); + return this._postTask(() => markAndDispatch(target, new MouseEvent("click", { ...init, button: 0, buttons: 0, detail: 1 }))); + } +}; +var webViewInput_default = WebViewInput; +`; + } +}); + +// packages/playwright-core/src/generated/webViewDialogSource.ts +var source8; +var init_webViewDialogSource = __esm({ + "packages/playwright-core/src/generated/webViewDialogSource.ts"() { + "use strict"; + source8 = ` +var __commonJS = obj => { + let required = false; + let result; + return function __require() { + if (!required) { + required = true; + let fn; + for (const name in obj) { fn = obj[name]; break; } + const module = { exports: {} }; + fn(module.exports, module); + result = module.exports; + } + return result; + } +}; +var __export = (target, all) => {for (var name in all) target[name] = all[name];}; +var __toESM = mod => ({ ...mod, 'default': mod }); +var __toCommonJS = mod => ({ ...mod, __esModule: true }); + + +// packages/injected/src/webview/webViewDialog.ts +var webViewDialog_exports = {}; +__export(webViewDialog_exports, { + installDialogBridge: () => installDialogBridge +}); +module.exports = __toCommonJS(webViewDialog_exports); +function toMessage(message) { + return String(message === void 0 || message === null ? "" : message); +} +function installDialogBridge(window, endpoint) { + function post(type, message, defaultValue) { + const xhr = new window.XMLHttpRequest(); + try { + xhr.open("POST", endpoint, false); + } catch (e) { + return null; + } + try { + xhr.send(JSON.stringify({ type, message, defaultValue })); + } catch (e) { + return null; + } + if (xhr.status !== 200) + return null; + try { + return JSON.parse(xhr.responseText); + } catch (e) { + return null; + } + } + Object.defineProperty(window, "alert", { + configurable: true, + writable: false, + value: function(message) { + post("alert", toMessage(message), ""); + } + }); + Object.defineProperty(window, "confirm", { + configurable: true, + writable: false, + value: function(message) { + const r = post("confirm", toMessage(message), ""); + return !!(r && r.accept); + } + }); + Object.defineProperty(window, "prompt", { + configurable: true, + writable: false, + value: function(message, defaultValue) { + const def = toMessage(defaultValue); + const r = post("prompt", toMessage(message), def); + if (!r || !r.accept) + return null; + return typeof r.promptText === "string" ? r.promptText : def; + } + }); +} +`; + } +}); + +// packages/playwright-core/src/server/webkit/webview/wvExecutionContext.ts +function potentiallyUnserializableValue4(remoteObject) { + const value2 = remoteObject.value; + const isUnserializable = remoteObject.type === "number" && ["NaN", "-Infinity", "Infinity", "-0"].includes(remoteObject.description); + return isUnserializable ? parseUnserializableValue(remoteObject.description) : value2; +} +function rewriteError5(error) { + if (error.message.includes("Object has too long reference chain")) + throw new Error("Cannot serialize result: object reference chain is too long."); + if (!isJavaScriptErrorInEvaluate(error) && !isSessionClosedError(error)) + return new Error("Execution context was destroyed, most likely because of a navigation."); + return error; +} +function renderPreview5(object) { + if (object.type === "undefined") + return "undefined"; + if ("value" in object) + return String(object.value); + if (object.description === "Object" && object.preview) { + const tokens = []; + for (const { name, value: value2 } of object.preview.properties) + tokens.push(`${name}: ${value2}`); + return `{${tokens.join(", ")}}`; + } + if (object.subtype === "array" && object.preview) + return sparseArrayToString(object.preview.properties); + return object.description; +} +function createHandle5(context2, remoteObject) { + if (remoteObject.subtype === "node") { + assert(context2 instanceof FrameExecutionContext); + return new ElementHandle(context2, remoteObject.objectId); + } + const isPromise = remoteObject.className === "Promise"; + return new JSHandle(context2, isPromise ? "promise" : remoteObject.subtype || remoteObject.type, renderPreview5(remoteObject), remoteObject.objectId, potentiallyUnserializableValue4(remoteObject)); +} +var WVExecutionContext; +var init_wvExecutionContext = __esm({ + "packages/playwright-core/src/server/webkit/webview/wvExecutionContext.ts"() { + "use strict"; + init_assert(); + init_utilityScriptSerializers(); + init_javascript(); + init_dom(); + init_protocolError(); + WVExecutionContext = class { + constructor(session2, contextId4) { + this._session = session2; + this._contextId = void 0; + } + async rawEvaluateJSON(expression2) { + try { + const response2 = await this._session.send("Runtime.evaluate", { + expression: expression2, + contextId: this._contextId, + returnByValue: true + }); + if (response2.wasThrown) + throw new JavaScriptErrorInEvaluate(response2.result.description); + return response2.result.value; + } catch (error) { + throw rewriteError5(error); + } + } + async rawEvaluateHandle(context2, expression2) { + try { + const response2 = await this._session.send("Runtime.evaluate", { + expression: expression2, + contextId: this._contextId, + returnByValue: false + }); + if (response2.wasThrown) + throw new JavaScriptErrorInEvaluate(response2.result.description); + return createHandle5(context2, response2.result); + } catch (error) { + throw rewriteError5(error); + } + } + async evaluateWithArguments(expression2, returnByValue, utilityScript, values, handles) { + try { + const response2 = await this._session.send("Runtime.callFunctionOn", { + functionDeclaration: expression2, + objectId: utilityScript._objectId, + arguments: [ + { objectId: utilityScript._objectId }, + ...values.map((value2) => ({ value: value2 })), + ...handles.map((handle) => ({ objectId: handle._objectId })) + ], + returnByValue, + emulateUserGesture: true, + awaitPromise: true + }); + if (response2.wasThrown) + throw new JavaScriptErrorInEvaluate(response2.result.description); + if (returnByValue) + return parseEvaluationResultValue(response2.result.value); + return createHandle5(utilityScript._context, response2.result); + } catch (error) { + throw rewriteError5(error); + } + } + async getProperties(object) { + const response2 = await this._session.send("Runtime.getProperties", { + objectId: object._objectId, + ownProperties: true + }); + const result2 = /* @__PURE__ */ new Map(); + for (const property of response2.properties) { + if (!property.enumerable || !property.value) + continue; + result2.set(property.name, createHandle5(object._context, property.value)); + } + return result2; + } + async releaseHandle(handle) { + if (!handle._objectId) + return; + await this._session.send("Runtime.releaseObject", { objectId: handle._objectId }); + } + shouldPrependErrorPrefix() { + return false; + } + }; + } +}); + +// packages/playwright-core/src/server/webkit/webview/wvInput.ts +function modifierFlags(modifiers) { + return { + ctrlKey: modifiers.has("Control"), + shiftKey: modifiers.has("Shift"), + altKey: modifiers.has("Alt"), + metaKey: modifiers.has("Meta") + }; +} +function buttonToNumber(button) { + if (button === "left") + return 0; + if (button === "middle") + return 1; + if (button === "right") + return 2; + return 0; +} +function toButtonsMask4(buttons) { + let mask = 0; + if (buttons.has("left")) + mask |= 1; + if (buttons.has("right")) + mask |= 2; + if (buttons.has("middle")) + mask |= 4; + return mask; +} +async function callWebViewInput(progress2, session2, method, arg) { + if (!session2) + throw new Error("Page is not initialized"); + const expression2 = `window.__pwWebViewInput.${method}(${JSON.stringify(arg)})`; + const { result: result2 } = await progress2.race(session2.send("Runtime.evaluate", { expression: expression2, returnByValue: false })); + if (result2?.className === "Promise" && result2.objectId) { + await progress2.race(session2.send("Runtime.awaitPromise", { promiseObjectId: result2.objectId, returnByValue: true })); + session2.sendMayFail("Runtime.releaseObject", { objectId: result2.objectId }); + } +} +var RawKeyboardImpl5, RawMouseImpl5, RawTouchscreenImpl5; +var init_wvInput = __esm({ + "packages/playwright-core/src/server/webkit/webview/wvInput.ts"() { + "use strict"; + RawKeyboardImpl5 = class { + setSession(session2) { + this._session = session2; + } + async keydown(progress2, modifiers, keyName, description, autoRepeat) { + const { code, keyCode, key, text: text2, location: location2 } = description; + const params2 = { + code, + key, + keyCode, + location: location2, + repeat: autoRepeat, + ...modifierFlags(modifiers), + ...text2 ? { text: text2 } : {} + }; + await callWebViewInput(progress2, this._session, "keydown", params2); + } + async keyup(progress2, modifiers, keyName, description) { + const { code, keyCode, key, location: location2 } = description; + const params2 = { code, key, keyCode, location: location2, ...modifierFlags(modifiers) }; + await callWebViewInput(progress2, this._session, "keyup", params2); + } + async sendText(progress2, text2) { + await callWebViewInput(progress2, this._session, "insertText", text2); + } + }; + RawMouseImpl5 = class { + setSession(session2) { + this._session = session2; + } + async move(progress2, x, y, button, buttons, modifiers, forClick) { + await callWebViewInput(progress2, this._session, "mouseMove", { + x, + y, + button: buttonToNumber(button), + buttons: toButtonsMask4(buttons), + ...modifierFlags(modifiers) + }); + } + async down(progress2, x, y, button, buttons, modifiers, clickCount) { + const buttonCode = buttonToNumber(button); + const buttonsMask = toButtonsMask4(buttons); + await this._mouseEvent(progress2, "mousedown", x, y, buttonCode, buttonsMask, modifiers, clickCount); + if (button === "right") + await this._mouseEvent(progress2, "contextmenu", x, y, buttonCode, buttonsMask, modifiers, clickCount); + } + async up(progress2, x, y, button, buttons, modifiers, clickCount) { + const buttonCode = buttonToNumber(button); + const buttonsMask = toButtonsMask4(buttons); + await this._mouseEvent(progress2, "mouseup", x, y, buttonCode, buttonsMask, modifiers, clickCount); + if (clickCount > 0) { + const clickType = button === "left" ? "click" : "auxclick"; + await this._mouseEvent(progress2, clickType, x, y, buttonCode, buttonsMask, modifiers, clickCount); + if (clickCount === 2) + await this._mouseEvent(progress2, "dblclick", x, y, buttonCode, buttonsMask, modifiers, clickCount); + } + } + async wheel(progress2, x, y, buttons, modifiers, deltaX, deltaY) { + await callWebViewInput(progress2, this._session, "wheel", { x, y, deltaX, deltaY, ...modifierFlags(modifiers) }); + } + async _mouseEvent(progress2, type3, x, y, button, buttons, modifiers, clickCount) { + await callWebViewInput(progress2, this._session, "mouseEvent", { + type: type3, + x, + y, + button, + buttons, + clickCount, + ...modifierFlags(modifiers) + }); + } + }; + RawTouchscreenImpl5 = class { + setSession(session2) { + this._session = session2; + } + async tap(progress2, x, y, modifiers) { + await callWebViewInput(progress2, this._session, "tap", { x, y, ...modifierFlags(modifiers) }); + } + }; + } +}); + +// packages/playwright-core/src/server/webkit/webview/wvWorkers.ts +var WVWorkers; +var init_wvWorkers = __esm({ + "packages/playwright-core/src/server/webkit/webview/wvWorkers.ts"() { + "use strict"; + init_eventsHelper(); + init_page(); + init_wvConnection(); + init_wvExecutionContext(); + WVWorkers = class { + constructor(page, session2) { + this._sessionListeners = []; + this._workerSessions = /* @__PURE__ */ new Map(); + this._page = page; + this.setSession(session2); + } + setSession(session2) { + eventsHelper.removeEventListeners(this._sessionListeners); + this.clear(); + this._sessionListeners = [ + eventsHelper.addEventListener(session2, "Worker.workerCreated", (event) => { + const worker = new Worker(this._page, event.url); + const workerSession = new WVSession(session2.connection, event.workerId, (message) => { + session2.send("Worker.sendMessageToWorker", { + workerId: event.workerId, + message: JSON.stringify(message) + }).catch((e) => { + workerSession.dispatchMessage({ id: message.id, error: { message: e.message } }); + }); + }); + this._workerSessions.set(event.workerId, workerSession); + worker.createExecutionContext(new WVExecutionContext(workerSession, void 0)); + worker.workerScriptLoaded(); + this._page.addWorker(event.workerId, worker); + workerSession.on("Console.messageAdded", (event2) => this._onConsoleMessage(worker, event2)); + Promise.all([ + workerSession.send("Runtime.enable"), + workerSession.send("Console.enable"), + session2.send("Worker.initialized", { workerId: event.workerId }) + ]).catch((e) => { + this._page.removeWorker(event.workerId); + }); + }), + eventsHelper.addEventListener(session2, "Worker.dispatchMessageFromWorker", (event) => { + const workerSession = this._workerSessions.get(event.workerId); + if (!workerSession) + return; + workerSession.dispatchMessage(JSON.parse(event.message)); + }), + eventsHelper.addEventListener(session2, "Worker.workerTerminated", (event) => { + const workerSession = this._workerSessions.get(event.workerId); + if (!workerSession) + return; + workerSession.dispose(); + this._workerSessions.delete(event.workerId); + this._page.removeWorker(event.workerId); + }) + ]; + } + clear() { + this._page.clearWorkers(); + this._workerSessions.clear(); + } + async initializeSession(session2) { + await session2.send("Worker.enable"); + } + async _onConsoleMessage(worker, event) { + const { type: type3, level, text: text2, parameters, url: url2, line: lineNumber, column: columnNumber } = event.message; + let derivedType = type3 || ""; + if (type3 === "log") + derivedType = level; + else if (type3 === "timing") + derivedType = "timeEnd"; + const handles = (parameters || []).map((p) => { + return createHandle5(worker.existingExecutionContext, p); + }); + const location2 = { + url: url2 || "", + lineNumber: (lineNumber || 1) - 1, + columnNumber: (columnNumber || 1) - 1 + }; + const timestamp = event.message.timestamp ? event.message.timestamp * 1e3 : Date.now(); + this._page.addConsoleMessage(worker, derivedType, handles, location2, handles.length ? void 0 : text2, timestamp); + } + }; + } +}); + +// packages/playwright-core/src/server/webkit/webview/wvInterceptableRequest.ts +function wkMillisToRoundishMillis2(value2) { + if (value2 === -1e3) + return -1; + if (value2 <= 0) { + return -1; + } + return (value2 * 1e3 | 0) / 1e3; +} +function toResourceType3(type3) { + switch (type3) { + case "Document": + return "document"; + case "StyleSheet": + return "stylesheet"; + case "Image": + return "image"; + case "Font": + return "font"; + case "Script": + return "script"; + case "XHR": + return "xhr"; + case "Fetch": + return "fetch"; + case "Ping": + return "ping"; + case "Beacon": + return "beacon"; + case "WebSocket": + return "websocket"; + case "EventSource": + return "eventsource"; + default: + return "other"; + } +} +var errorReasons3, WVInterceptableRequest, WVRouteImpl; +var init_wvInterceptableRequest = __esm({ + "packages/playwright-core/src/server/webkit/webview/wvInterceptableRequest.ts"() { + "use strict"; + init_assert(); + init_headers(); + init_network2(); + errorReasons3 = { + "aborted": "Cancellation", + "accessdenied": "AccessControl", + "addressunreachable": "General", + "blockedbyclient": "Cancellation", + "blockedbyresponse": "General", + "connectionaborted": "General", + "connectionclosed": "General", + "connectionfailed": "General", + "connectionrefused": "General", + "connectionreset": "General", + "internetdisconnected": "General", + "namenotresolved": "General", + "timedout": "Timeout", + "failed": "General" + }; + WVInterceptableRequest = class { + constructor(session2, frame, event, redirectedFrom, documentId) { + this._session = session2; + this._requestId = event.requestId; + const resourceType = event.type ? toResourceType3(event.type) : redirectedFrom ? redirectedFrom.request.resourceType() : "other"; + let postDataBuffer = null; + this._timestamp = event.timestamp; + this._wallTime = event.walltime * 1e3; + if (event.request.postData) { + postDataBuffer = Buffer.from(event.request.postData, "utf8"); + } + this.request = new Request( + frame._page.browserContext, + frame, + null, + redirectedFrom?.request || null, + documentId, + event.request.url, + resourceType, + event.request.method, + postDataBuffer, + headersObjectToArray(event.request.headers), + this._wallTime + ); + } + adoptRequestFromNewProcess(newSession, requestId) { + this._session = newSession; + this._requestId = requestId; + } + createResponse(responsePayload) { + const getResponseBody = async () => { + const response3 = await this._session.send("Network.getResponseBody", { requestId: this._requestId }); + return Buffer.from(response3.body, response3.base64Encoded ? "base64" : "utf8"); + }; + const timingPayload = responsePayload.timing; + const timing = { + startTime: this._wallTime, + domainLookupStart: timingPayload ? wkMillisToRoundishMillis2(timingPayload.domainLookupStart) : -1, + domainLookupEnd: timingPayload ? wkMillisToRoundishMillis2(timingPayload.domainLookupEnd) : -1, + connectStart: timingPayload ? wkMillisToRoundishMillis2(timingPayload.connectStart) : -1, + secureConnectionStart: timingPayload ? wkMillisToRoundishMillis2(timingPayload.secureConnectionStart) : -1, + connectEnd: timingPayload ? wkMillisToRoundishMillis2(timingPayload.connectEnd) : -1, + requestStart: timingPayload ? wkMillisToRoundishMillis2(timingPayload.requestStart) : -1, + responseStart: timingPayload ? wkMillisToRoundishMillis2(timingPayload.responseStart) : -1 + }; + const setCookieSeparator = process.platform === "darwin" ? "," : "playwright-set-cookie-separator"; + const response2 = new Response2(this.request, responsePayload.status, responsePayload.statusText, headersObjectToArray(responsePayload.headers, ",", setCookieSeparator), timing, getResponseBody, responsePayload.source === "service-worker"); + response2.setRawResponseHeaders(null); + response2.setTransferSize(null); + if (responsePayload.requestHeaders && Object.keys(responsePayload.requestHeaders).length) { + const headers = { ...responsePayload.requestHeaders }; + if (!headers["host"]) + headers["Host"] = new URL(this.request.url()).host; + this.request.setRawRequestHeaders(headersObjectToArray(headers)); + } else { + this.request.setRawRequestHeaders(null); + } + return response2; + } + }; + WVRouteImpl = class { + constructor(session2, requestId) { + this._session = session2; + this._requestId = requestId; + } + async abort(errorCode) { + const errorType = errorReasons3[errorCode]; + assert(errorType, "Unknown error code: " + errorCode); + await this._session.sendMayFail("Network.interceptRequestWithError", { requestId: this._requestId, errorType }); + } + async fulfill(response2) { + if (300 <= response2.status && response2.status < 400) + throw new Error("Cannot fulfill with redirect status: " + response2.status); + let mimeType = response2.isBase64 ? "application/octet-stream" : "text/plain"; + const headers = headersArrayToObject( + response2.headers, + true + /* lowerCase */ + ); + const contentType = headers["content-type"]; + if (contentType) + mimeType = contentType.split(";")[0].trim(); + await this._session.sendMayFail("Network.interceptRequestWithResponse", { + requestId: this._requestId, + status: response2.status, + statusText: statusText(response2.status), + mimeType, + headers, + base64Encoded: response2.isBase64, + content: response2.body + }); + } + async continue(overrides) { + await this._session.sendMayFail("Network.interceptWithRequest", { + requestId: this._requestId, + url: overrides.url, + method: overrides.method, + headers: overrides.headers ? headersArrayToObject( + overrides.headers, + false + /* lowerCase */ + ) : void 0, + postData: overrides.postData ? Buffer.from(overrides.postData).toString("base64") : void 0 + }); + } + }; + } +}); + +// packages/playwright-core/src/server/webkit/webview/wvProvisionalPage.ts +var WVProvisionalPage; +var init_wvProvisionalPage = __esm({ + "packages/playwright-core/src/server/webkit/webview/wvProvisionalPage.ts"() { + "use strict"; + init_eventsHelper(); + init_assert(); + WVProvisionalPage = class { + constructor(session2, page) { + this._sessionListeners = []; + this._mainFrameId = null; + this._session = session2; + this._wvPage = page; + const overrideFrameId = (handler) => { + return (payload) => { + if (payload.frameId) + payload.frameId = this._wvPage._page.frameManager.mainFrame()._id; + handler(payload); + }; + }; + const wvPage = this._wvPage; + this._sessionListeners = [ + eventsHelper.addEventListener(session2, "Network.requestWillBeSent", overrideFrameId((e) => wvPage._onRequestWillBeSent(session2, e))), + eventsHelper.addEventListener(session2, "Network.requestIntercepted", overrideFrameId((e) => wvPage._onRequestIntercepted(session2, e))), + eventsHelper.addEventListener(session2, "Network.responseReceived", overrideFrameId((e) => wvPage._onResponseReceived(session2, e))), + eventsHelper.addEventListener(session2, "Network.loadingFinished", overrideFrameId((e) => wvPage._onLoadingFinished(e))), + eventsHelper.addEventListener(session2, "Network.loadingFailed", overrideFrameId((e) => wvPage._onLoadingFailed(session2, e))) + ]; + this.initializationPromise = this._wvPage._initializeSession(session2, true, ({ frameTree }) => this._handleFrameTree(frameTree)); + } + dispose() { + eventsHelper.removeEventListeners(this._sessionListeners); + } + commit() { + assert(this._mainFrameId); + this._wvPage._onFrameAttached(this._mainFrameId, null); + } + _handleFrameTree(frameTree) { + assert(!frameTree.frame.parentId); + this._mainFrameId = frameTree.frame.id; + } + }; + } +}); + +// packages/playwright-core/src/server/webkit/webview/wvPage.ts +function parseRemoteAddress2(value2) { + if (!value2) + return; + try { + const colon = value2.lastIndexOf(":"); + const dot = value2.lastIndexOf("."); + if (dot < 0) { + return { + ipAddress: `[${value2.slice(0, colon)}]`, + port: +value2.slice(colon + 1) + }; + } + if (colon > dot) { + const [address, port] = value2.split(":"); + return { + ipAddress: address, + port: +port + }; + } else { + const [address, port] = value2.split("."); + return { + ipAddress: `[${address}]`, + port: +port + }; + } + } catch (_) { + } +} +function isLoadedSecurely2(url2, timing) { + try { + const u = new URL(url2); + if (u.protocol !== "https:" && u.protocol !== "wss:" && u.protocol !== "sftp:") + return false; + if (timing.secureConnectionStart === -1 && timing.connectStart !== -1) + return false; + return true; + } catch (_) { + } +} +function dialogBridgeSource(endpoint) { + return `(() => { + const module = {}; + ${source8} + module.exports.installDialogBridge()(window, ${JSON.stringify(endpoint)}); +})()`; +} +var PNG3, jpegjs4, WVPage, BINDING_CALL_TAG, webViewInputBootstrapSource, bindingBridgeSource; +var init_wvPage = __esm({ + "packages/playwright-core/src/server/webkit/webview/wvPage.ts"() { + "use strict"; + init_assert(); + init_headers(); + init_manualPromise(); + init_stackTrace(); + init_debugLogger(); + init_eventsHelper(); + init_dialog(); + init_dom(); + init_errors(); + init_helper(); + init_javascript(); + init_webViewInputSource(); + init_webViewDialogSource(); + init_network2(); + init_page(); + init_wvConnection(); + init_wvExecutionContext(); + init_wvInput(); + init_wvWorkers(); + init_wvInterceptableRequest(); + init_wvProvisionalPage(); + ({ PNG: PNG3 } = require("./utilsBundle")); + jpegjs4 = require("./utilsBundle").jpegjs; + WVPage = class { + constructor(browserContext, outerSession, dialogEndpoint) { + this._provisionalPage = null; + this._requestIdToRequest = /* @__PURE__ */ new Map(); + this._requestIdToRequestWillBeSentEvent = /* @__PURE__ */ new Map(); + this._sessionListeners = []; + this._pendingMainFrameLoaderResolvers = /* @__PURE__ */ new Map(); + this._firstNonInitialNavigationCommittedFulfill = () => { + }; + this._firstNonInitialNavigationCommittedReject = (e) => { + }; + this._initializedPromise = new ManualPromise(); + this._lastConsoleMessage = null; + this._requestIdToResponseReceivedPayloadEvent = /* @__PURE__ */ new Map(); + this._timestampBaselineForWebSocket = /* @__PURE__ */ new Map(); + this._outerSession = outerSession; + this._dialogEndpoint = dialogEndpoint; + this.rawKeyboard = new RawKeyboardImpl5(); + this.rawMouse = new RawMouseImpl5(); + this.rawTouchscreen = new RawTouchscreenImpl5(); + this._contextIdToContext = /* @__PURE__ */ new Map(); + this._page = new Page(this, browserContext); + this._workers = new WVWorkers(this._page, outerSession); + this._browserContext = browserContext; + this._page.on(Page.Events.FrameDetached, (frame) => this._removeContextsForFrame(frame, false)); + this._eventListeners = [ + eventsHelper.addEventListener(outerSession, "Target.targetCreated", this._onTargetCreated.bind(this)), + eventsHelper.addEventListener(outerSession, "Target.targetDestroyed", this._onTargetDestroyed.bind(this)), + eventsHelper.addEventListener(outerSession, "Target.dispatchMessageFromTarget", this._onDispatchMessageFromTarget.bind(this)), + eventsHelper.addEventListener(outerSession, "Target.didCommitProvisionalTarget", this._onDidCommitProvisionalTarget.bind(this)) + ]; + this._firstNonInitialNavigationCommittedPromise = new Promise((f, r) => { + this._firstNonInitialNavigationCommittedFulfill = f; + this._firstNonInitialNavigationCommittedReject = r; + }); + this._firstNonInitialNavigationCommittedPromise.catch(() => { + }); + } + waitForInitialized() { + return this._initializedPromise; + } + updateEmulatedViewportSize(preserveWindowBoundaries) { + throw new Error("Method not implemented."); + } + updateEmulateMedia() { + throw new Error("Method not implemented."); + } + goBack() { + throw new Error("Method not implemented."); + } + goForward() { + throw new Error("Method not implemented."); + } + requestGC() { + throw new Error("Method not implemented."); + } + updateFileChooserInterception() { + throw new Error("Method not implemented."); + } + setInputFilePaths(progress2, handle, files) { + throw new Error("Method not implemented."); + } + startScreencast(options) { + throw new Error("Method not implemented."); + } + stopScreencast() { + throw new Error("Method not implemented."); + } + _setSession(session2) { + eventsHelper.removeEventListeners(this._sessionListeners); + this._session = session2; + this.rawKeyboard.setSession(session2); + this.rawMouse.setSession(session2); + this.rawTouchscreen.setSession(session2); + this._workers.setSession(session2); + this._addSessionListeners(); + } + // This method is called for provisional targets as well. The session passed as the parameter + // may be different from the current session and may be destroyed without becoming current. + async _initializeSession(session2, provisional, resourceTreeHandler) { + await this._initializeSessionMayThrow(session2, resourceTreeHandler).catch((e) => { + if (provisional && session2.isDisposed()) + return; + if (this._session === session2) + throw e; + }); + } + async _initializeSessionMayThrow(session2, resourceTreeHandler) { + const [, frameTree] = await Promise.all([ + session2.send("Page.enable"), + session2.send("Page.getResourceTree") + ]); + resourceTreeHandler(frameTree); + await Promise.all([ + session2.send("Runtime.enable"), + session2.sendMayFail("Network.enable"), + session2.sendMayFail("Console.enable"), + this._workers.initializeSession(session2), + session2.sendMayFail("Page.setBootstrapScript", { source: this._calculateBootstrapScript() }), + session2.sendMayFail("Runtime.evaluate", { expression: saveGlobalsSnapshotSource, returnByValue: true }) + ]); + if (this._page.needsRequestInterception()) { + await Promise.all([ + session2.sendMayFail("Network.setInterceptionEnabled", { enabled: true }), + session2.sendMayFail("Network.setResourceCachingDisabled", { disabled: true }), + session2.sendMayFail("Network.addInterception", { url: ".*", stage: "request", isRegex: true }) + ]); + } + await session2.sendMayFail("Runtime.evaluate", { expression: webViewInputBootstrapSource, returnByValue: true }); + if (this._dialogEndpoint) { + await session2.sendMayFail("Runtime.evaluate", { + expression: dialogBridgeSource(this._dialogEndpoint), + returnByValue: true + }); + } + } + _createSession(targetId) { + const session2 = new WVSession(this._outerSession.connection, targetId, (message) => { + this._outerSession.send("Target.sendMessageToTarget", { + targetId, + message: JSON.stringify(message) + }).catch((e) => { + session2.dispatchMessage({ id: message.id, error: { message: e.message } }); + }); + }); + return session2; + } + async _onTargetCreated(event) { + const { targetInfo } = event; + if (targetInfo.type !== "page") { + if (targetInfo.isPaused) + this._outerSession.sendMayFail("Target.resume", { targetId: targetInfo.targetId }); + return; + } + const session2 = this._createSession(targetInfo.targetId); + if (!targetInfo.isProvisional) { + let pageOrError; + try { + this._setSession(session2); + await this._initializeSession(session2, false, ({ frameTree }) => this._handleFrameTree(frameTree)); + pageOrError = this._page; + } catch (e) { + pageOrError = e; + } + if (targetInfo.isPaused) + this._outerSession.sendMayFail("Target.resume", { targetId: targetInfo.targetId }); + await this._page.reportAsNew(void 0, pageOrError instanceof Page ? void 0 : pageOrError); + this._initializedPromise.resolve(); + } else { + assert(!this._provisionalPage); + this._provisionalPage = new WVProvisionalPage(session2, this); + if (targetInfo.isPaused) { + this._provisionalPage.initializationPromise.then(() => { + this._outerSession.sendMayFail("Target.resume", { targetId: targetInfo.targetId }); + }); + } + } + } + _onDispatchMessageFromTarget(event) { + const { targetId, message } = event; + if (this._provisionalPage && this._provisionalPage._session.sessionId === targetId) + this._provisionalPage._session.dispatchMessage(JSON.parse(message)); + else if (this._session && this._session.sessionId === targetId) + this._session.dispatchMessage(JSON.parse(message)); + } + _onDidCommitProvisionalTarget(event) { + const { oldTargetId, newTargetId } = event; + assert(this._provisionalPage); + assert(this._provisionalPage._session.sessionId === newTargetId, "Unknown new target: " + newTargetId); + assert(this._session.sessionId === oldTargetId, "Unknown old target: " + oldTargetId); + const newSession = this._provisionalPage._session; + this._provisionalPage.commit(); + this._provisionalPage.dispose(); + this._provisionalPage = null; + this._setSession(newSession); + } + _onTargetDestroyed(event) { + const { targetId } = event; + if (this._provisionalPage && this._provisionalPage._session.sessionId === targetId) { + this._provisionalPage._session.dispose(); + this._provisionalPage.dispose(); + this._provisionalPage = null; + } else if (this._session && this._session.sessionId === targetId) { + this._session.dispose(); + eventsHelper.removeEventListeners(this._sessionListeners); + } + } + async onBridgeDialog(req) { + return await new Promise((resolve) => { + this._page.browserContext.dialogManager.dialogDidOpen(new Dialog( + this._page, + req.type, + req.message, + async (accept, promptText) => resolve({ accept, promptText }), + req.defaultValue + )); + }); + } + didClose() { + eventsHelper.removeEventListeners(this._sessionListeners); + eventsHelper.removeEventListeners(this._eventListeners); + if (this._session) + this._session.dispose(); + if (this._provisionalPage) { + this._provisionalPage._session.dispose(); + this._provisionalPage.dispose(); + this._provisionalPage = null; + } + this._firstNonInitialNavigationCommittedReject(new TargetClosedError(this._page.closeReason())); + this._page._didClose(); + } + _addSessionListeners() { + this._sessionListeners = this._buildSessionListeners(this._session); + } + _buildSessionListeners(session2) { + return [ + eventsHelper.addEventListener(session2, "Page.frameNavigated", (event) => this._onFrameNavigated(event.frame, false)), + eventsHelper.addEventListener(session2, "Page.navigatedWithinDocument", (event) => this._onFrameNavigatedWithinDocument(event.frameId, event.url)), + eventsHelper.addEventListener(session2, "Page.frameAttached", (event) => this._onFrameAttached(event.frameId, event.parentFrameId)), + eventsHelper.addEventListener(session2, "Page.frameDetached", (event) => this._onFrameDetached(event.frameId)), + eventsHelper.addEventListener(session2, "Page.loadEventFired", (event) => this._page.frameManager.frameLifecycleEvent(this._page.mainFrame()._id, "load")), + eventsHelper.addEventListener(session2, "Page.domContentEventFired", (event) => this._page.frameManager.frameLifecycleEvent(this._page.mainFrame()._id, "domcontentloaded")), + eventsHelper.addEventListener(session2, "Runtime.executionContextCreated", (event) => this._onExecutionContextCreated(event.context)), + eventsHelper.addEventListener(session2, "Console.messageAdded", (event) => this._onConsoleMessage(event)), + eventsHelper.addEventListener(session2, "Console.messageRepeatCountUpdated", (event) => this._onConsoleRepeatCountUpdated(event)), + eventsHelper.addEventListener(session2, "Network.requestWillBeSent", (e) => this._onRequestWillBeSent(session2, e)), + eventsHelper.addEventListener(session2, "Network.requestIntercepted", (e) => this._onRequestIntercepted(session2, e)), + eventsHelper.addEventListener(session2, "Network.responseReceived", (e) => this._onResponseReceived(session2, e)), + eventsHelper.addEventListener(session2, "Network.loadingFinished", (e) => this._onLoadingFinished(e)), + eventsHelper.addEventListener(session2, "Network.loadingFailed", (e) => this._onLoadingFailed(session2, e)), + eventsHelper.addEventListener(session2, "Network.webSocketCreated", (e) => this._page.frameManager.onWebSocketCreated(e.requestId, e.url)), + eventsHelper.addEventListener(session2, "Network.webSocketWillSendHandshakeRequest", (event) => this._onWebSocketWillSendHandshakeRequest(event)), + eventsHelper.addEventListener(session2, "Network.webSocketHandshakeResponseReceived", (e) => this._page.frameManager.onWebSocketResponse(e.requestId, e.response.status, e.response.statusText, headersObjectToArray(e.response.headers, ","))), + eventsHelper.addEventListener(session2, "Network.webSocketFrameSent", (e) => e.response.payloadData && this._page.frameManager.onWebSocketFrameSent(e.requestId, e.response.opcode, e.response.payloadData, this._timestampToWallTimeMsForWebSocket(e.requestId, e.timestamp))), + eventsHelper.addEventListener(session2, "Network.webSocketFrameReceived", (e) => e.response.payloadData && this._page.frameManager.webSocketFrameReceived(e.requestId, e.response.opcode, e.response.payloadData, this._timestampToWallTimeMsForWebSocket(e.requestId, e.timestamp))), + eventsHelper.addEventListener(session2, "Network.webSocketClosed", (event) => this._onWebSocketClosed(event)), + eventsHelper.addEventListener(session2, "Network.webSocketFrameError", (e) => this._page.frameManager.webSocketError(e.requestId, e.errorMessage)) + ]; + } + async _updateState(method, params2) { + await this._forAllSessions((session2) => session2.send(method, params2).then()); + } + async _forAllSessions(callback) { + const sessions = [ + this._session + ]; + await Promise.all(sessions.map((session2) => callback(session2).catch((e) => { + }))); + } + _handleFrameTree(frameTree) { + this._onFrameAttached(frameTree.frame.id, frameTree.frame.parentId || null); + this._onFrameNavigated(frameTree.frame, true); + this._page.frameManager.frameLifecycleEvent(frameTree.frame.id, "domcontentloaded"); + this._page.frameManager.frameLifecycleEvent(frameTree.frame.id, "load"); + if (!frameTree.childFrames) + return; + for (const child of frameTree.childFrames) + this._handleFrameTree(child); + } + _onFrameAttached(frameId, parentFrameId) { + return this._page.frameManager.frameAttached(frameId, parentFrameId); + } + _onFrameNavigated(framePayload, initial) { + if (!this._page.frameManager.frame(framePayload.id)) + this._onFrameAttached(framePayload.id, framePayload.parentId || null); + const frame = this._page.frameManager.frame(framePayload.id); + this._removeContextsForFrame(frame, true); + if (!framePayload.parentId) + this._workers.clear(); + this._page.frameManager.frameCommittedNewDocumentNavigation(framePayload.id, framePayload.url, framePayload.name || "", framePayload.loaderId, initial); + if (!initial) + this._firstNonInitialNavigationCommittedFulfill(); + const pending = this._pendingMainFrameLoaderResolvers.get(framePayload.id); + if (pending) { + this._pendingMainFrameLoaderResolvers.delete(framePayload.id); + pending(framePayload.loaderId); + } + } + _onFrameNavigatedWithinDocument(frameId, url2) { + this._page.frameManager.frameCommittedSameDocumentNavigation(frameId, url2); + } + _onFrameDetached(frameId) { + this._page.frameManager.frameDetached(frameId); + } + _removeContextsForFrame(frame, notifyFrame) { + for (const [contextId4, context2] of this._contextIdToContext) { + if (context2.frame === frame) { + this._contextIdToContext.delete(contextId4); + if (notifyFrame) + frame.contextDestroyed(context2); + } + } + } + _onExecutionContextCreated(contextPayload) { + if (this._contextIdToContext.has(contextPayload.id)) + return; + const frame = this._page.frameManager.frame(contextPayload.frameId); + if (!frame) + return; + if (contextPayload.type !== "normal") + return; + const delegate = new WVExecutionContext(this._session, contextPayload.id); + const context2 = new FrameExecutionContext(delegate, frame, "main"); + frame.contextCreated("main", context2); + this._contextIdToContext.set(contextPayload.id, context2); + } + async navigateFrame(frame, url2, referrer) { + if (frame.parentFrame()) + throw new Error("Navigating subframes is not supported on stock WebKit RDP \u2014 only the main frame can be navigated."); + if (url2.startsWith("data:")) + return await this._navigateToDataUrl(frame, url2); + return await this._navigateMainFrame(frame, url2); + } + async _navigateMainFrame(frame, url2) { + const loaderIdPromise = new Promise((resolve) => { + this._pendingMainFrameLoaderResolvers.set(frame._id, resolve); + }); + try { + this._session.sendMayFail("Runtime.evaluate", { + expression: `window.location.href = ${JSON.stringify(url2)}`, + returnByValue: true + }); + const loaderId = await loaderIdPromise; + return { newDocumentId: loaderId }; + } finally { + this._pendingMainFrameLoaderResolvers.delete(frame._id); + } + } + async _navigateToDataUrl(frame, url2) { + const match = /^data:([^,]*),([\s\S]*)$/.exec(url2); + if (!match) + throw new Error(`Invalid data URL: ${url2.slice(0, 80)}`); + const meta = match[1]; + const data = match[2]; + const content = /;base64$/i.test(meta) ? Buffer.from(data, "base64").toString("utf8") : decodeURIComponent(data); + const result2 = await this._navigateMainFrame(frame, "about:blank"); + await this._session.sendMayFail("Runtime.evaluate", { + expression: `(function() { document.open(); document.write(${JSON.stringify(content)}); document.close(); })()` + }); + return result2; + } + _onConsoleMessage(event) { + const { type: type3, level, text: text2, parameters, url: url2, line: lineNumber, column: columnNumber, source: source11 } = event.message; + if (level === "debug" && parameters && parameters.length >= 2 && parameters[0].type === "string" && parameters[0].value === BINDING_CALL_TAG && parameters[1].type === "string") { + const payload = parameters[1].value; + const context2 = [...this._contextIdToContext.values()].find((c) => c.frame === this._page.mainFrame()); + if (context2) + this._page.onBindingCalled(payload, context2).catch((e) => debugLogger.log("error", e)); + return; + } + if (level === "error" && source11 === "javascript") { + const { name, message } = splitErrorMessage(text2); + let stack; + if (event.message.stackTrace) { + stack = text2 + "\n" + event.message.stackTrace.callFrames.map((callFrame) => { + return ` at ${callFrame.functionName || "unknown"} (${callFrame.url}:${callFrame.lineNumber}:${callFrame.columnNumber})`; + }).join("\n"); + } else { + stack = ""; + } + this._lastConsoleMessage = null; + const error = new Error(message); + error.stack = stack; + error.name = name; + this._page.addPageError(error, { + url: url2 || "", + lineNumber: (lineNumber || 1) - 1, + columnNumber: (columnNumber || 1) - 1 + }); + return; + } + let derivedType = type3 || ""; + if (type3 === "log") + derivedType = level; + else if (type3 === "timing") + derivedType = "timeEnd"; + const handles = []; + for (const p of parameters || []) { + let context2; + if (p.objectId) { + const objectId = JSON.parse(p.objectId); + context2 = this._contextIdToContext.get(objectId.injectedScriptId); + } else { + context2 = [...this._contextIdToContext.values()].find((c) => c.frame === this._page.mainFrame()); + } + if (!context2) + return; + handles.push(createHandle5(context2, p)); + } + this._lastConsoleMessage = { + derivedType, + text: text2, + handles, + count: 0, + location: { + url: url2 || "", + lineNumber: (lineNumber || 1) - 1, + columnNumber: (columnNumber || 1) - 1 + } + }; + this._onConsoleRepeatCountUpdated({ count: 1, timestamp: event.message.timestamp }); + } + _onConsoleRepeatCountUpdated(event) { + if (this._lastConsoleMessage) { + const { + derivedType, + text: text2, + handles, + count, + location: location2 + } = this._lastConsoleMessage; + const timestamp = event.timestamp ? event.timestamp * 1e3 : Date.now(); + for (let i = count; i < event.count; ++i) + this._page.addConsoleMessage(null, derivedType, handles, location2, handles.length ? void 0 : text2, timestamp); + this._lastConsoleMessage.count = event.count; + } + } + async updateExtraHTTPHeaders() { + await this._updateState("Network.setExtraHTTPHeaders", { headers: headersArrayToObject( + this._calculateExtraHTTPHeaders(), + false + /* lowerCase */ + ) }); + } + _calculateExtraHTTPHeaders() { + const locale = this._browserContext._options.locale; + const headers = mergeHeaders([ + this._browserContext._options.extraHTTPHeaders, + this._page.extraHTTPHeaders(), + locale ? singleHeader("Accept-Language", locale) : void 0 + ]); + return headers; + } + async bringToFront() { + } + noUtilityWorld() { + return true; + } + async updateRequestInterception() { + const enabled = this._page.needsRequestInterception(); + await Promise.all([ + this._updateState("Network.setInterceptionEnabled", { enabled }), + this._updateState("Network.setResourceCachingDisabled", { disabled: enabled }), + this._updateState("Network.addInterception", { url: ".*", stage: "request", isRegex: true }) + ]); + } + async reload() { + await this._session.send("Page.reload"); + } + async addInitScript(initScript) { + await this._updateBootstrapScript(); + } + async removeInitScripts(initScripts) { + await this._updateBootstrapScript(); + } + async exposePlaywrightBinding() { + await this._updateBootstrapScript(); + await this._session.sendMayFail("Runtime.evaluate", { + expression: bindingBridgeSource, + returnByValue: true + }); + } + _calculateBootstrapScript() { + const scripts = []; + scripts.push(saveGlobalsSnapshotSource); + if (!this._page.browserContext._options.isMobile) { + scripts.push("delete window.orientation"); + scripts.push("delete window.ondevicemotion"); + scripts.push("delete window.ondeviceorientation"); + } + scripts.push('if (!window.safari) window.safari = { pushNotification: { toString() { return "[object SafariRemoteNotification]"; } } };'); + scripts.push("if (!window.GestureEvent) window.GestureEvent = function GestureEvent() {};"); + scripts.push(this._publicKeyCredentialScript()); + scripts.push(bindingBridgeSource); + scripts.push(webViewInputBootstrapSource); + if (this._dialogEndpoint) + scripts.push(dialogBridgeSource(this._dialogEndpoint)); + scripts.push(...this._page.allInitScripts().map((script) => script.source)); + return scripts.join(";\n"); + } + _publicKeyCredentialScript() { + function polyfill() { + window.PublicKeyCredential ??= { + async getClientCapabilities() { + return {}; + }, + async isConditionalMediationAvailable() { + return false; + }, + async isUserVerifyingPlatformAuthenticatorAvailable() { + return false; + } + }; + } + return `(${polyfill.toString()})();`; + } + async _updateBootstrapScript() { + await this._updateState("Page.setBootstrapScript", { source: this._calculateBootstrapScript() }); + } + async closePage() { + await this._session.sendMayFail("Runtime.evaluate", { + expression: "window.close()", + emulateUserGesture: true + }); + this._session.connection.close(); + } + async setBackgroundColor(color) { + throw new Error("Method not implemented"); + } + validateScreenshotDimension(side, omitDeviceScaleFactor) { + if (process.platform === "darwin") + return; + if (!omitDeviceScaleFactor && this._page.browserContext._options.deviceScaleFactor) + side = Math.ceil(side * this._page.browserContext._options.deviceScaleFactor); + if (side > 32767) + throw new Error("Cannot take screenshot larger than 32767 pixels on any dimension"); + } + async takeScreenshot(progress2, format2, documentRect, viewportRect, quality, fitsViewport, scale) { + const rect = documentRect || viewportRect; + const omitDeviceScaleFactor = scale === "css"; + if (omitDeviceScaleFactor) + throw new Error("css screenshots are not implemented"); + this.validateScreenshotDimension(rect.width, omitDeviceScaleFactor); + this.validateScreenshotDimension(rect.height, omitDeviceScaleFactor); + const result2 = await progress2.race(this._session.send("Page.snapshotRect", { ...rect, coordinateSystem: documentRect ? "Page" : "Viewport" })); + const prefix = "data:image/png;base64,"; + let buffer = Buffer.from(result2.dataURL.substr(prefix.length), "base64"); + if (format2 === "jpeg") + buffer = jpegjs4.encode(PNG3.sync.read(buffer), quality).data; + return buffer; + } + async getContentFrame(handle) { + throw new Error("Method not implemented"); + } + async getOwnerFrame(handle) { + throw new Error("Method not implemented"); + } + async getBoundingBox(handle) { + const quads = await this.getContentQuads(handle); + if (!quads || !quads.length) + return null; + let minX = Infinity; + let maxX = -Infinity; + let minY = Infinity; + let maxY = -Infinity; + for (const quad of quads) { + for (const point of quad) { + minX = Math.min(minX, point.x); + maxX = Math.max(maxX, point.x); + minY = Math.min(minY, point.y); + maxY = Math.max(maxY, point.y); + } + } + return { x: minX, y: minY, width: maxX - minX, height: maxY - minY }; + } + async scrollRectIntoViewIfNeeded(handle, rect) { + const result2 = await handle.evaluateInUtility(([, node, rect2]) => { + if (!node.isConnected) + return "error:notconnected"; + if (!(node instanceof Element)) + return "error:notvisible"; + const box = node.getBoundingClientRect(); + if (!box.width && !box.height && !node.getClientRects().length) + return "error:notvisible"; + const innerW = document.documentElement.clientWidth; + const innerH = document.documentElement.clientHeight; + const target = rect2 ? { left: box.left + rect2.x, top: box.top + rect2.y, right: box.left + rect2.x + rect2.width, bottom: box.top + rect2.y + rect2.height } : { left: box.left, top: box.top, right: box.right, bottom: box.bottom }; + const fullyVisible = target.left >= 0 && target.top >= 0 && target.right <= innerW && target.bottom <= innerH; + if (!fullyVisible) + node.scrollIntoView({ block: "center", inline: "center" }); + return "done"; + }, rect ?? null); + if (result2 === "error:notvisible" || result2 === "error:notconnected" || result2 === "done") + return result2; + return "error:notconnected"; + } + rafCountForStablePosition() { + return 1; + } + async getContentQuads(handle) { + const result2 = await handle.evaluateInUtility(([, node]) => { + let element2 = node; + while (element2 && element2.nodeType !== 1) + element2 = element2.parentNode; + if (!element2) + return null; + const rects = element2.getClientRects(); + if (!rects.length) + return null; + return Array.from(rects, (r) => [ + { x: r.left, y: r.top }, + { x: r.right, y: r.top }, + { x: r.right, y: r.bottom }, + { x: r.left, y: r.bottom } + ]); + }, {}); + if (!result2 || typeof result2 === "string") + return null; + return result2; + } + async adoptElementHandle(handle, to) { + throw new Error("Method not implemented"); + } + async inputActionEpilogue() { + } + async resetForReuse(progress2) { + } + async getFrameElement(frame) { + throw new Error("Method not implemented"); + } + _adoptRequestFromNewProcess(navigationRequest, newSession, newRequestId) { + for (const [requestId, request2] of this._requestIdToRequest) { + if (request2.request === navigationRequest) { + this._requestIdToRequest.delete(requestId); + request2.adoptRequestFromNewProcess(newSession, newRequestId); + this._requestIdToRequest.set(newRequestId, request2); + return; + } + } + } + _onRequestWillBeSent(session2, event) { + if (event.request.url.startsWith("data:")) + return; + if (event.request.url.startsWith("about:")) + return; + if (this._page.needsRequestInterception() && !event.redirectResponse) + this._requestIdToRequestWillBeSentEvent.set(event.requestId, event); + else + this._onRequest(session2, event, false); + } + _onRequest(session2, event, intercepted) { + let redirectedFrom = null; + if (event.redirectResponse) { + const request3 = this._requestIdToRequest.get(event.requestId); + if (request3) { + this._handleRequestRedirect(request3, event.requestId, event.redirectResponse, event.timestamp); + redirectedFrom = request3; + } + } + const frame = redirectedFrom ? redirectedFrom.request.frame() : this._page.frameManager.frame(event.frameId); + if (!frame) + return; + const isNavigationRequest = event.type === "Document"; + const documentId = isNavigationRequest ? event.loaderId : void 0; + const request2 = new WVInterceptableRequest(session2, frame, event, redirectedFrom, documentId); + let route2; + if (intercepted) { + route2 = new WVRouteImpl(session2, event.requestId); + request2.request.setRawRequestHeaders(null); + } + this._requestIdToRequest.set(event.requestId, request2); + this._page.frameManager.requestStarted(request2.request, route2); + } + _handleRequestRedirect(request2, requestId, responsePayload, timestamp) { + const response2 = request2.createResponse(responsePayload); + response2._setHttpVersion(null); + response2._securityDetailsFinished(); + response2._serverAddrFinished(); + response2.setResponseHeadersSize(null); + response2.setEncodedBodySize(null); + response2._requestFinished(responsePayload.timing ? helper.secondsToRoundishMillis(timestamp - request2._timestamp) : -1); + this._requestIdToRequest.delete(requestId); + this._page.frameManager.requestReceivedResponse(response2); + this._page.frameManager.reportRequestFinished(request2.request, response2); + } + _onRequestIntercepted(session2, event) { + const requestWillBeSentEvent = this._requestIdToRequestWillBeSentEvent.get(event.requestId); + if (!requestWillBeSentEvent) { + session2.sendMayFail("Network.interceptWithRequest", { requestId: event.requestId }); + return; + } + this._requestIdToRequestWillBeSentEvent.delete(event.requestId); + this._onRequest(session2, requestWillBeSentEvent, true); + } + _onResponseReceived(session2, event) { + const requestWillBeSentEvent = this._requestIdToRequestWillBeSentEvent.get(event.requestId); + if (requestWillBeSentEvent) { + this._requestIdToRequestWillBeSentEvent.delete(event.requestId); + this._onRequest(session2, requestWillBeSentEvent, false); + } + const request2 = this._requestIdToRequest.get(event.requestId); + if (!request2) + return; + this._requestIdToResponseReceivedPayloadEvent.set(event.requestId, event); + const response2 = request2.createResponse(event.response); + this._page.frameManager.requestReceivedResponse(response2); + if (response2.status() === 204 && request2.request.isNavigationRequest()) { + this._onLoadingFailed(session2, { + requestId: event.requestId, + errorText: "Aborted: 204 No Content", + timestamp: event.timestamp + }); + } + } + _onLoadingFinished(event) { + const request2 = this._requestIdToRequest.get(event.requestId); + if (!request2) + return; + const response2 = request2.request._existingResponse(); + if (response2) { + const responseReceivedPayload = this._requestIdToResponseReceivedPayloadEvent.get(event.requestId); + response2._serverAddrFinished(parseRemoteAddress2(event?.metrics?.remoteAddress)); + response2._securityDetailsFinished({ + protocol: isLoadedSecurely2(response2.url(), response2.timing()) ? event.metrics?.securityConnection?.protocol : void 0, + subjectName: responseReceivedPayload?.response.security?.certificate?.subject, + validFrom: responseReceivedPayload?.response.security?.certificate?.validFrom, + validTo: responseReceivedPayload?.response.security?.certificate?.validUntil + }); + response2._setHttpVersion(event.metrics?.protocol ?? null); + response2.setEncodedBodySize(event.metrics?.responseBodyBytesReceived ?? null); + response2.setResponseHeadersSize(event.metrics?.responseHeaderBytesReceived ?? null); + response2._requestFinished(helper.secondsToRoundishMillis(event.timestamp - request2._timestamp)); + } else { + request2.request.setRawRequestHeaders(null); + } + this._requestIdToResponseReceivedPayloadEvent.delete(event.requestId); + this._requestIdToRequest.delete(event.requestId); + this._page.frameManager.reportRequestFinished(request2.request, response2); + } + _onLoadingFailed(session2, event) { + const requestWillBeSentEvent = this._requestIdToRequestWillBeSentEvent.get(event.requestId); + if (requestWillBeSentEvent) { + this._requestIdToRequestWillBeSentEvent.delete(event.requestId); + this._onRequest(session2, requestWillBeSentEvent, false); + } + const request2 = this._requestIdToRequest.get(event.requestId); + if (!request2) + return; + const response2 = request2.request._existingResponse(); + if (response2) { + response2._serverAddrFinished(); + response2._securityDetailsFinished(); + response2._setHttpVersion(null); + response2.setResponseHeadersSize(null); + response2.setEncodedBodySize(null); + response2._requestFinished(helper.secondsToRoundishMillis(event.timestamp - request2._timestamp)); + } else { + request2.request.setRawRequestHeaders(null); + } + this._requestIdToRequest.delete(event.requestId); + request2.request._setFailureText(event.errorText); + this._page.frameManager.requestFailed(request2.request, event.errorText.includes("cancelled")); + } + _onWebSocketWillSendHandshakeRequest(event) { + const wallTimeMs = event.walltime * 1e3; + this._timestampBaselineForWebSocket.set(event.requestId, wallTimeMs - event.timestamp * 1e3); + this._page.frameManager.onWebSocketRequest(event.requestId, headersObjectToArray(event.request.headers), wallTimeMs); + } + _onWebSocketClosed(event) { + this._timestampBaselineForWebSocket.delete(event.requestId); + this._page.frameManager.webSocketClosed(event.requestId); + } + _timestampToWallTimeMsForWebSocket(requestId, timestamp) { + return this._timestampBaselineForWebSocket.get(requestId) + timestamp * 1e3; + } + shouldToggleStyleSheetToSyncAnimations() { + return true; + } + async setDockTile(image) { + } + }; + BINDING_CALL_TAG = "__pw_binding_call__"; + webViewInputBootstrapSource = `(() => { + const module = {}; + ${source7} + window.__pwWebViewInput = new (module.exports.default())(window, document); +})()`; + bindingBridgeSource = ` + if (!window['${PageBinding.kBindingName}']) { + Object.defineProperty(window, '${PageBinding.kBindingName}', { + configurable: true, + writable: false, + value: function(payload) { console.debug('${BINDING_CALL_TAG}', payload); }, + }); + } +`; + } +}); + +// packages/playwright-core/src/server/webkit/webview/wvBrowser.ts +function deriveProxyBase(endpointURL) { + const u = new URL(endpointURL); + if (u.protocol === "http:" || u.protocol === "https:") + return `${u.protocol}//${u.host}`; + const httpProto = u.protocol === "wss:" ? "https:" : "http:"; + return `${httpProto}//${u.host}`; +} +function pageIdFromWsUrl(wsUrl) { + const m = /\/devtools\/page\/([^/]+)/.exec(wsUrl); + return m ? m[1] : wsUrl; +} +async function listTabs(proxyBase, headers) { + const res = await fetch(`${proxyBase}/json`, { headers }); + if (!res.ok) + throw new Error(`ios_webkit_debug_proxy ${proxyBase}/json returned ${res.status}`); + const data = await res.json(); + return data.filter((t) => !!t.webSocketDebuggerUrl); +} +async function connectOverRDP(progress2, parent, params2) { + let headersMap; + if (params2.headers) + headersMap = headersArrayToObject(params2.headers, false); + if (!headersMap) + headersMap = { "User-Agent": getUserAgent() }; + else if (!Object.keys(headersMap).some((key) => key.toLowerCase() === "user-agent")) + headersMap["User-Agent"] = getUserAgent(); + const transport = params2.transport; + const proxyBase = transport ? "" : deriveProxyBase(params2.endpointURL); + const artifactsDir = params2.artifactsDir ?? import_path30.default.join(import_os16.default.tmpdir(), "playwright-artifacts-"); + const doCleanup = async () => { + await removeFolders([artifactsDir]); + }; + const browser = await progress2.race((async () => { + const dialogBridge = await DialogBridge.start(); + const created = new WVBrowser(parent, proxyBase, headersMap, dialogBridge, transport, { + slowMo: params2.slowMo, + name: "webkit", + browserType: "webkit", + browserProcess: { close: async () => { + }, kill: async () => { + } }, + protocolLogger: helper.debugProtocolLogger(), + browserLogsCollector: new RecentLogsCollector(), + artifactsDir, + downloadsPath: artifactsDir, + tracesDir: artifactsDir, + originalLaunchOptions: {} + }); + const shutdown = async () => { + await created._closeAllTabs(); + await dialogBridge.close().catch(() => { + }); + await doCleanup(); + }; + created.options.browserProcess = { close: shutdown, kill: shutdown }; + await created._initialize(); + return created; + })()); + if (!params2.isLocal) + browser._isBrowserCollocatedWithServer = false; + browser.on(Browser.Events.Disconnected, doCleanup); + return browser; +} +var import_os16, import_path30, ws2, DeferredWebSocketTransport, WVBrowser, WVBrowserContext; +var init_wvBrowser = __esm({ + "packages/playwright-core/src/server/webkit/webview/wvBrowser.ts"() { + "use strict"; + import_os16 = __toESM(require("os")); + import_path30 = __toESM(require("path")); + init_debugLogger(); + init_fileUtils(); + init_happyEyeballs(); + init_headers(); + init_browser(); + init_helper(); + init_transport(); + init_userAgent(); + init_browserContext(); + init_dialogBridge(); + init_wvConnection(); + init_wvPage(); + ws2 = require("./utilsBundle").ws; + DeferredWebSocketTransport = class { + constructor(url2, headers) { + this._closed = false; + this._url = url2; + this._headers = headers; + } + open() { + if (this._closed) + return; + const url2 = this._url; + this._ws = new ws2(url2, [], { + maxPayload: 256 * 1024 * 1024, + headers: this._headers, + followRedirects: true, + agent: /^(https|wss):\/\//.test(url2) ? httpsHappyEyeballsAgent : httpHappyEyeballsAgent, + perMessageDeflate: perMessageDeflate2, + allowSynchronousEvents: false + }); + this._ws.addEventListener("message", (event) => { + const eventData = event.data; + let parsedJson; + try { + parsedJson = JSON.parse(eventData); + this.onmessage?.(parsedJson); + } catch { + this._ws?.close(); + } + }); + this._ws.addEventListener("close", (event) => { + this.onclose?.(event.reason); + }); + this._ws.addEventListener("error", () => { + }); + } + send(message) { + this._ws?.send(JSON.stringify(message)); + } + close() { + this._closed = true; + this._ws?.close(); + } + }; + WVBrowser = class extends Browser { + constructor(parent, proxyBase, headers, dialogBridge, directPageTransport, options) { + super(parent, options); + this._tabs = /* @__PURE__ */ new Map(); + this._didCloseFired = false; + this._proxyBase = proxyBase; + this._headers = headers; + this._dialogBridge = dialogBridge; + this._directPageTransport = directPageTransport; + this._context = new WVBrowserContext(this); + } + async _initialize() { + await this._context.initialize(); + if (this._directPageTransport) { + await this._attachTab("rdp-transport", this._directPageTransport); + } else { + await this._syncTabs(); + if (!this._tabs.size) + throw new Error(`No Mobile Safari tabs found at ${this._proxyBase}/json \u2014 open Safari first.`); + } + this._page = this._firstTab().page; + } + _firstTab() { + return this._tabs.values().next().value; + } + async _syncTabs() { + const tabs = await listTabs(this._proxyBase, this._headers); + const seen = /* @__PURE__ */ new Set(); + for (const tab2 of tabs) { + const pageId4 = pageIdFromWsUrl(tab2.webSocketDebuggerUrl); + seen.add(pageId4); + if (this._tabs.has(pageId4)) + continue; + try { + await this._attachTab(pageId4, new DeferredWebSocketTransport(tab2.webSocketDebuggerUrl, this._headers)); + } catch (e) { + debugLogger.log("error", `webview: failed to attach to tab ${pageId4}: ${e.message}`); + } + } + for (const pageId4 of Array.from(this._tabs.keys())) { + if (!seen.has(pageId4)) + this._detachTab(pageId4); + } + } + async _attachTab(pageId4, transport) { + const connection = new WVConnection(transport, () => this._detachTab(pageId4), this.options.protocolLogger, this.options.browserLogsCollector); + const dialogEndpoint = this._dialogBridge.endpointFor(pageId4); + const page = new WVPage(this._context, connection.outerSession, dialogEndpoint); + this._dialogBridge.registerTab(pageId4, (req) => page.onBridgeDialog(req)); + this._tabs.set(pageId4, { pageId: pageId4, transport, connection, page }); + transport.open?.(); + connection.outerSession.sendMayFail("Target.setPauseOnStart", { pauseOnStart: true }); + await page.waitForInitialized(); + } + _detachTab(pageId4) { + const entry = this._tabs.get(pageId4); + if (!entry) + return; + this._tabs.delete(pageId4); + this._dialogBridge.unregisterTab(pageId4); + entry.connection.close(); + entry.page.didClose(); + } + async _closeAllTabs() { + for (const pageId4 of Array.from(this._tabs.keys())) + this._detachTab(pageId4); + this._fireDidCloseOnce(); + } + _fireDidCloseOnce() { + if (this._didCloseFired) + return; + this._didCloseFired = true; + this.didClose(); + } + async doCreateNewContext(options) { + throw new Error("Not supported"); + } + contexts() { + return [this._context]; + } + version() { + return ""; + } + userAgent() { + return ""; + } + isConnected() { + return this._tabs.size > 0; + } + }; + WVBrowserContext = class extends BrowserContext { + constructor(browser) { + super(browser, {}, ""); + this.authenticateProxyViaHeader(); + } + possiblyUninitializedPages() { + return Array.from(this._browser._tabs.values()).map((entry) => entry.page._page); + } + async doCreateNewPage() { + throw new Error("Not supported"); + } + async doGetCookies(urls) { + return []; + } + async addCookies(cookies) { + throw new Error("Method not implemented."); + } + async doClearCookies() { + throw new Error("Method not implemented."); + } + async doGrantPermissions(origin, permissions) { + throw new Error("Method not implemented."); + } + async doClearPermissions() { + throw new Error("Method not implemented."); + } + async setGeolocation(geolocation) { + throw new Error("Method not implemented."); + } + async doUpdateExtraHTTPHeaders() { + for (const page of this.pages()) + await page.delegate.updateExtraHTTPHeaders(); + } + async doAddInitScript(initScript) { + for (const page of this.pages()) + await page.delegate._updateBootstrapScript(); + } + async doRemoveInitScripts(initScripts) { + for (const page of this.pages()) + await page.delegate._updateBootstrapScript(); + } + async doUpdateRequestInterception() { + for (const page of this.pages()) + await page.delegate.updateRequestInterception(); + } + async doExposePlaywrightBinding() { + for (const page of this.pages()) + await page.delegate.exposePlaywrightBinding(); + } + async onClosePersistent() { + } + async doUpdateDefaultViewport() { + } + async doUpdateDefaultEmulatedMedia() { + } + async clearCache() { + throw new Error("Method not implemented."); + } + async doClose(reason) { + throw new Error("Method not implemented."); + } + async cancelDownload(uuid) { + throw new Error("Method not implemented."); + } + async setUserAgent(userAgent) { + throw new Error("Method not implemented."); + } + async doSetHTTPCredentials(httpCredentials) { + throw new Error("Method not implemented."); + } + async doUpdateOffline() { + throw new Error("Method not implemented."); + } + }; + } +}); + +// packages/playwright-core/src/server/webkit/webkit.ts +async function translatePathToWSL(path59) { + const { stdout } = await spawnAsync("wsl.exe", ["-d", "playwright", "--cd", "/home/pwuser", "wslpath", path59.replace(/\\/g, "\\\\")]); + return stdout.toString().trim(); +} +var import_path31, WebKit; +var init_webkit = __esm({ + "packages/playwright-core/src/server/webkit/webkit.ts"() { + "use strict"; + import_path31 = __toESM(require("path")); + init_ascii(); + init_spawnAsync(); + init_wkConnection(); + init_browserType(); + init_wkBrowser(); + init_wvBrowser(); + WebKit = class extends BrowserType { + constructor(parent) { + super(parent, "webkit"); + } + connectToTransport(transport, options) { + return WKBrowser.connect(this.attribution.playwright, transport, options); + } + async connectOverCDP(progress2, params2) { + return connectOverRDP(progress2, this, params2); + } + amendEnvironment(env, userDataDir, isPersistent, options) { + return { + ...env, + CURL_COOKIE_JAR_PATH: process.platform === "win32" && isPersistent ? import_path31.default.join(userDataDir, "cookiejar.db") : void 0 + }; + } + doRewriteStartupLog(logs) { + if (logs.includes("Failed to open display") || logs.includes("cannot open display")) + logs = "\n" + wrapInASCIIBox(kNoXServerRunningError, 1); + return logs; + } + attemptToGracefullyCloseBrowser(transport) { + transport.send({ method: "Playwright.close", params: {}, id: kBrowserCloseMessageId4 }); + } + async defaultArgs(options, isPersistent, userDataDir) { + const { args = [], headless } = options; + const userDataDirArg = args.find((arg) => arg.startsWith("--user-data-dir")); + if (userDataDirArg) + throw this._createUserDataDirArgMisuseError("--user-data-dir"); + if (args.find((arg) => !arg.startsWith("-"))) + throw new Error("Arguments can not specify page to be opened"); + const webkitArguments = ["--inspector-pipe"]; + if (process.platform === "win32" && options.channel !== "webkit-wsl") + webkitArguments.push("--disable-accelerated-compositing"); + if (headless) + webkitArguments.push("--headless"); + if (isPersistent) + webkitArguments.push(`--user-data-dir=${options.channel === "webkit-wsl" ? await translatePathToWSL(userDataDir) : userDataDir}`); + else + webkitArguments.push(`--no-startup-window`); + const proxy = options.proxyOverride || options.proxy; + if (proxy) { + if (process.platform === "darwin") { + webkitArguments.push(`--proxy=${proxy.server}`); + if (proxy.bypass) + webkitArguments.push(`--proxy-bypass-list=${proxy.bypass}`); + } else if (process.platform === "linux" || process.platform === "win32" && options.channel === "webkit-wsl") { + webkitArguments.push(`--proxy=${proxy.server}`); + if (proxy.bypass) + webkitArguments.push(...proxy.bypass.split(",").map((t) => `--ignore-host=${t}`)); + } else if (process.platform === "win32") { + webkitArguments.push(`--curl-proxy=${proxy.server.replace(/^socks5:\/\//, "socks5h://")}`); + if (proxy.bypass) + webkitArguments.push(`--curl-noproxy=${proxy.bypass}`); + } + } + webkitArguments.push(...args); + if (isPersistent) + webkitArguments.push("about:blank"); + return webkitArguments; + } + }; + } +}); + +// packages/playwright-core/src/server/playwright.ts +var playwright_exports = {}; +__export(playwright_exports, { + Playwright: () => Playwright, + createPlaywright: () => createPlaywright +}); +function createPlaywright(options) { + return new Playwright(options); +} +var Playwright; +var init_playwright = __esm({ + "packages/playwright-core/src/server/playwright.ts"() { + "use strict"; + init_android(); + init_backendAdb(); + init_bidiChromium(); + init_bidiFirefox(); + init_chromium(); + init_debugController(); + init_electron(); + init_firefox(); + init_instrumentation(); + init_webkit(); + Playwright = class extends SdkObject { + constructor(options) { + super(createRootSdkObject(), void 0, "Playwright"); + this._allPages = /* @__PURE__ */ new Set(); + this._allBrowsers = /* @__PURE__ */ new Set(); + this.options = options; + this.attribution.playwright = this; + this.instrumentation.addListener({ + onBrowserOpen: (browser) => this._allBrowsers.add(browser), + onBrowserClose: (browser) => this._allBrowsers.delete(browser), + onPageOpen: (page) => this._allPages.add(page), + onPageClose: (page) => this._allPages.delete(page) + }, null); + this.chromium = new Chromium(this, new BidiChromium(this)); + this.firefox = new Firefox(this, new BidiFirefox(this)); + this.webkit = new WebKit(this); + this.electron = new Electron(this); + this.android = new Android(this, new AdbBackend()); + this.debugController = new DebugController(this); + } + allBrowsers() { + return [...this._allBrowsers]; + } + allPages() { + return [...this._allPages]; + } + }; + } +}); + +// packages/playwright-core/src/server/recorder/recorderApp.ts +function determinePrimaryGeneratorId(sdkLanguage) { + for (const language of languageSet()) { + if (language.highlighter === sdkLanguage) + return language.id; + } + return sdkLanguage; +} +function findPageByGuid(context2, guid) { + return context2.pages().find((p) => p.guid === guid); +} +function createRecorderFrontend(page) { + return new Proxy({}, { + get: (_target, prop) => { + if (typeof prop !== "string") + return void 0; + return (params2) => { + page.mainFrame().evaluateExpression(nullProgress, ((event) => { + window.dispatch(event); + }).toString(), { isFunction: true }, { method: prop, params: params2 }).catch(() => { + }); + }; + } + }); +} +var import_fs30, import_path32, mime7, RecorderApp, ProgrammaticRecorderApp, recorderAppSymbol; +var init_recorderApp = __esm({ + "packages/playwright-core/src/server/recorder/recorderApp.ts"() { + "use strict"; + import_fs30 = __toESM(require("fs")); + import_path32 = __toESM(require("path")); + init_debug(); + init_package(); + init_launchApp(); + init_launchApp(); + init_progress(); + init_throttledFile(); + init_languages(); + init_recorderUtils(); + init_language(); + init_recorder(); + init_browserContext(); + mime7 = require("./utilsBundle").mime; + RecorderApp = class _RecorderApp { + constructor(recorder, params2, page, wsEndpointForTest) { + this._throttledOutputFile = null; + this._actions = []; + this._userSources = []; + this._recorderSources = []; + this._page = page; + this._recorder = recorder; + this._frontend = createRecorderFrontend(page); + this.wsEndpointForTest = wsEndpointForTest; + this._languageGeneratorOptions = { + browserName: params2.browserName, + launchOptions: { headless: false, ...params2.launchOptions, tracesDir: void 0 }, + contextOptions: { ...params2.contextOptions }, + deviceName: params2.device, + saveStorage: params2.saveStorage + }; + this._throttledOutputFile = params2.outputFile ? new ThrottledFile(params2.outputFile) : null; + this._primaryGeneratorId = process.env.TEST_INSPECTOR_LANGUAGE || params2.language || determinePrimaryGeneratorId(params2.sdkLanguage); + this._selectedGeneratorId = this._primaryGeneratorId; + for (const languageGenerator of languageSet()) { + if (languageGenerator.id === this._primaryGeneratorId) + this._recorder.setLanguage(languageGenerator.highlighter); + } + } + async _init(inspectedContext) { + await syncLocalStorageWithSettings(this._page, "recorder"); + const controller = new ProgressController(); + await controller.run(async (progress2) => { + await this._page.addRequestInterceptor(progress2, (route2) => { + if (!route2.request().url().startsWith("https://playwright/")) { + route2.continue({ isFallback: true }).catch(() => { + }); + return; + } + const uri = route2.request().url().substring("https://playwright/".length); + const file = import_path32.default.join(libPath("vite", "recorder"), uri); + import_fs30.default.promises.readFile(file).then((buffer) => { + route2.fulfill({ + status: 200, + headers: [ + { name: "Content-Type", value: mime7.getType(import_path32.default.extname(file)) || "application/octet-stream" } + ], + body: buffer.toString("base64"), + isBase64: true + }).catch(() => { + }); + }); + }); + await this._createDispatcher(progress2); + this._page.once("close", () => { + this._recorder.close(); + this._page.browserContext.close(nullProgress, { reason: "Recorder window closed" }).catch(() => { + }); + delete inspectedContext[recorderAppSymbol]; + }); + await this._page.mainFrame().goto(progress2, "https://playwright/index.html"); + }); + const url2 = this._recorder.url(); + if (url2) + this._frontend.pageNavigated({ url: url2 }); + this._frontend.modeChanged({ mode: this._recorder.mode() }); + this._frontend.pauseStateChanged({ paused: this._recorder.paused() }); + this._updateActions("reveal"); + this._onUserSourcesChanged(this._recorder.userSources(), this._recorder.pausedSourceId()); + this._frontend.callLogsUpdated({ callLogs: this._recorder.callLog() }); + this._wireListeners(this._recorder); + } + async _createDispatcher(progress2) { + const dispatcher = { + clear: async () => { + this._actions = []; + this._updateActions("reveal"); + this._recorder.clear(); + }, + fileChanged: async (params2) => { + const source11 = [...this._recorderSources, ...this._userSources].find((s) => s.id === params2.fileId); + if (source11) { + if (source11.isRecorded) + this._selectedGeneratorId = source11.id; + await this._recorder.setLanguage(source11.language); + } + }, + setAutoExpect: async (params2) => { + this._languageGeneratorOptions.generateAutoExpect = params2.autoExpect; + this._updateActions(); + }, + setMode: async (params2) => { + await this._recorder.setMode(params2.mode); + }, + resume: async () => { + this._recorder.resume(); + }, + pause: async () => { + this._recorder.pause(); + }, + step: async () => { + this._recorder.step(); + }, + highlightRequested: async (params2) => { + if (params2.selector) + await this._recorder.setHighlightedSelector(params2.selector); + if (params2.ariaTemplate) + await this._recorder.setHighlightedAriaTemplate(params2.ariaTemplate); + } + }; + await this._page.exposeBinding(progress2, "sendCommand", async (_, data) => { + const { method, params: params2 } = data; + return await dispatcher[method].call(dispatcher, params2); + }); + } + static async show(context2, params2) { + if (process.env.PW_CODEGEN_NO_INSPECTOR) + return; + const recorder = await Recorder.forContext(context2, params2); + if (params2.recorderMode === "api") { + const browserName = context2._browser.options.name; + await ProgrammaticRecorderApp.run(context2, recorder, browserName, params2); + return; + } + await _RecorderApp._show(recorder, context2, params2); + } + async close() { + await this._page.close(nullProgress); + } + static showInspectorNoReply(context2) { + if (process.env.PW_CODEGEN_NO_INSPECTOR) + return; + void Recorder.forContext(context2, {}).then((recorder) => _RecorderApp._show(recorder, context2, {})).catch(() => { + }); + } + static async _show(recorder, inspectedContext, params2) { + if (inspectedContext[recorderAppSymbol]) + return; + inspectedContext[recorderAppSymbol] = true; + const sdkLanguage = inspectedContext._browser.sdkLanguage(); + const isChromium = inspectedContext._browser.options.browserType === "chromium"; + const headed = !!inspectedContext._browser.options.headful; + const { createPlaywright: createPlaywright2 } = (init_playwright(), __toCommonJS(playwright_exports)); + const recorderPlaywright = createPlaywright2({ sdkLanguage: "javascript", isInternalPlaywright: true }); + const { context: appContext, page } = await launchApp(recorderPlaywright.chromium, { + sdkLanguage, + windowSize: { width: 600, height: 600 }, + windowPosition: { x: 1020, y: 10 }, + persistentContextOptions: { + noDefaultViewport: true, + headless: !!process.env.PWTEST_CLI_HEADLESS || isUnderTest() && !headed, + args: isUnderTest() ? ["--remote-debugging-port=0"] : void 0, + handleSIGINT: params2.handleSIGINT, + executablePath: isChromium ? inspectedContext._browser.options.customExecutablePath : void 0, + // Use the same channel as the inspected context to guarantee that the browser is installed. + channel: isChromium ? inspectedContext._browser.options.channel : void 0 + } + }); + const controller = new ProgressController(); + await controller.run(async (progress2) => { + await appContext._browser._defaultContext.loadDefaultContextAsIs(progress2); + }); + const appParams = { + browserName: inspectedContext._browser.options.name, + sdkLanguage: inspectedContext._browser.sdkLanguage(), + wsEndpointForTest: inspectedContext._browser.options.wsEndpoint, + headed: !!inspectedContext._browser.options.headful, + executablePath: isChromium ? inspectedContext._browser.options.customExecutablePath : void 0, + channel: isChromium ? inspectedContext._browser.options.channel : void 0, + ...params2 + }; + const recorderApp = new _RecorderApp(recorder, appParams, page, appContext._browser.options.wsEndpoint); + await recorderApp._init(inspectedContext); + inspectedContext.recorderAppForTest = recorderApp; + } + _wireListeners(recorder) { + recorder.on(RecorderEvent.ActionAdded, (action) => { + this._onActionAdded(action); + }); + recorder.on(RecorderEvent.SignalAdded, (signal) => { + this._onSignalAdded(signal); + }); + recorder.on(RecorderEvent.PageNavigated, (url2) => { + this._frontend.pageNavigated({ url: url2 }); + }); + recorder.on(RecorderEvent.ContextClosed, () => { + this._throttledOutputFile?.flush(); + this._page.browserContext.close(nullProgress, { reason: "Recorder window closed" }).catch(() => { + }); + }); + recorder.on(RecorderEvent.ModeChanged, (mode) => { + this._frontend.modeChanged({ mode }); + }); + recorder.on(RecorderEvent.PausedStateChanged, (paused) => { + this._frontend.pauseStateChanged({ paused }); + }); + recorder.on(RecorderEvent.UserSourcesChanged, (sources, pausedSourceId) => { + this._onUserSourcesChanged(sources, pausedSourceId); + }); + recorder.on(RecorderEvent.ElementPicked, (elementInfo, userGesture) => { + if (userGesture) + this._page.bringToFront(nullProgress).catch(() => { + }); + this._frontend.elementPicked({ elementInfo, userGesture }); + }); + recorder.on(RecorderEvent.CallLogsUpdated, (callLogs) => { + this._frontend.callLogsUpdated({ callLogs }); + }); + } + _onActionAdded(action) { + this._actions.push(action); + this._updateActions("reveal"); + } + _onSignalAdded(signal) { + const lastAction = this._actions.findLast((a) => a.frame.pageGuid === signal.frame.pageGuid); + if (lastAction) + lastAction.action.signals.push(signal.signal); + this._updateActions(); + } + _onUserSourcesChanged(sources, pausedSourceId) { + if (!sources.length && !this._userSources.length) + return; + this._userSources = sources; + this._pushAllSources(); + this._revealSource(pausedSourceId); + } + _pushAllSources() { + const sources = [...this._userSources, ...this._recorderSources]; + this._frontend.sourcesUpdated({ sources }); + } + _revealSource(sourceId) { + if (!sourceId) + return; + this._frontend.sourceRevealRequested({ sourceId }); + } + _updateActions(reveal) { + const recorderSources = []; + const actions = collapseActions(this._actions); + let revealSourceId; + for (const languageGenerator of languageSet()) { + const { header, footer, actionTexts, text: text2 } = generateCode(actions, languageGenerator, this._languageGeneratorOptions); + const source11 = { + isRecorded: true, + label: languageGenerator.name, + group: languageGenerator.groupName, + id: languageGenerator.id, + text: text2, + header, + footer, + actions: actionTexts, + language: languageGenerator.highlighter, + highlight: [] + }; + source11.revealLine = text2.split("\n").length - 1; + recorderSources.push(source11); + if (languageGenerator.id === this._primaryGeneratorId) + this._throttledOutputFile?.setContent(source11.text); + if (reveal === "reveal" && source11.id === this._selectedGeneratorId) + revealSourceId = source11.id; + } + this._recorderSources = recorderSources; + this._pushAllSources(); + this._revealSource(revealSourceId); + } + }; + ProgrammaticRecorderApp = class { + static async run(inspectedContext, recorder, browserName, params2) { + let lastAction = null; + const languages = [...languageSet()]; + const languageGeneratorOptions = { + browserName, + launchOptions: { headless: false, ...params2.launchOptions, tracesDir: void 0 }, + contextOptions: { ...params2.contextOptions }, + deviceName: params2.device, + saveStorage: params2.saveStorage + }; + const languageGenerator = languages.find((l) => l.id === params2.language) ?? languages.find((l) => l.id === "playwright-test"); + recorder.on(RecorderEvent.ActionAdded, (action) => { + const page = findPageByGuid(inspectedContext, action.frame.pageGuid); + if (!page) + return; + const { actionTexts } = generateCode([action], languageGenerator, languageGeneratorOptions); + if (!lastAction || !shouldMergeAction(action, lastAction)) + inspectedContext.emit(BrowserContext.Events.RecorderEvent, { event: "actionAdded", data: action, page, code: actionTexts.join("\n") }); + else + inspectedContext.emit(BrowserContext.Events.RecorderEvent, { event: "actionUpdated", data: action, page, code: actionTexts.join("\n") }); + lastAction = action; + }); + recorder.on(RecorderEvent.SignalAdded, (signal) => { + const page = findPageByGuid(inspectedContext, signal.frame.pageGuid); + if (!page) + return; + inspectedContext.emit(BrowserContext.Events.RecorderEvent, { event: "signalAdded", data: signal, page, code: "" }); + }); + } + }; + recorderAppSymbol = Symbol("recorderApp"); + } +}); + +// packages/playwright-core/src/server/selectors.ts +var Selectors; +var init_selectors = __esm({ + "packages/playwright-core/src/server/selectors.ts"() { + "use strict"; + init_selectorParser(); + init_crypto(); + Selectors = class { + constructor(engines, testIdAttributeName2) { + this.guid = `selectors@${createGuid()}`; + this._builtinEngines = /* @__PURE__ */ new Set([ + "css", + "css:light", + "xpath", + "xpath:light", + "_react", + "_vue", + "text", + "text:light", + "id", + "id:light", + "data-testid", + "data-testid:light", + "data-test-id", + "data-test-id:light", + "data-test", + "data-test:light", + "nth", + "visible", + "internal:control", + "internal:has", + "internal:has-not", + "internal:has-text", + "internal:has-not-text", + "internal:and", + "internal:or", + "internal:chain", + "role", + "internal:attr", + "internal:label", + "internal:text", + "internal:role", + "internal:testid", + "internal:describe", + "aria-ref" + ]); + this._builtinEnginesInMainWorld = /* @__PURE__ */ new Set([ + "_react", + "_vue" + ]); + this._engines = /* @__PURE__ */ new Map(); + this._testIdAttributeName = testIdAttributeName2 ?? "data-testid"; + for (const engine of engines) + this.register(engine); + } + register(engine) { + if (!engine.name.match(/^[a-zA-Z_0-9-]+$/)) + throw new Error("Selector engine name may only contain [a-zA-Z0-9_] characters"); + if (this._builtinEngines.has(engine.name) || engine.name === "zs" || engine.name === "zs:light") + throw new Error(`"${engine.name}" is a predefined selector engine`); + if (this._engines.has(engine.name)) + throw new Error(`"${engine.name}" selector engine has been already registered`); + this._engines.set(engine.name, engine); + } + testIdAttributeName() { + return this._testIdAttributeName; + } + setTestIdAttributeName(testIdAttributeName2) { + this._testIdAttributeName = testIdAttributeName2; + } + parseSelector(selector, strict) { + const parsed = typeof selector === "string" ? parseSelector(selector) : selector; + let needsMainWorld = false; + visitAllSelectorParts(parsed, (part) => { + const name = part.name; + const custom = this._engines.get(name); + if (!custom && !this._builtinEngines.has(name)) + throw new InvalidSelectorError(`Unknown engine "${name}" while parsing selector ${stringifySelector(parsed)}`); + if (custom && !custom.contentScript) + needsMainWorld = true; + if (this._builtinEnginesInMainWorld.has(name)) + needsMainWorld = true; + }); + return { + parsed, + world: needsMainWorld ? "main" : "utility", + strict + }; + } + }; + } +}); + +// packages/playwright-core/src/generated/storageScriptSource.ts +var source9; +var init_storageScriptSource = __esm({ + "packages/playwright-core/src/generated/storageScriptSource.ts"() { + "use strict"; + source9 = '\nvar __commonJS = obj => {\n let required = false;\n let result;\n return function __require() {\n if (!required) {\n required = true;\n let fn;\n for (const name in obj) { fn = obj[name]; break; }\n const module = { exports: {} };\n fn(module.exports, module);\n result = module.exports;\n }\n return result;\n }\n};\nvar __export = (target, all) => {for (var name in all) target[name] = all[name];};\nvar __toESM = mod => ({ ...mod, \'default\': mod });\nvar __toCommonJS = mod => ({ ...mod, __esModule: true });\n\n\n// packages/injected/src/storageScript.ts\nvar storageScript_exports = {};\n__export(storageScript_exports, {\n StorageScript: () => StorageScript\n});\nmodule.exports = __toCommonJS(storageScript_exports);\n\n// packages/isomorphic/utilityScriptSerializers.ts\nfunction isRegExp(obj) {\n try {\n return obj instanceof RegExp || Object.prototype.toString.call(obj) === "[object RegExp]";\n } catch (error) {\n return false;\n }\n}\nfunction isDate(obj) {\n try {\n return obj instanceof Date || Object.prototype.toString.call(obj) === "[object Date]";\n } catch (error) {\n return false;\n }\n}\nfunction isURL(obj) {\n try {\n return obj instanceof URL || Object.prototype.toString.call(obj) === "[object URL]";\n } catch (error) {\n return false;\n }\n}\nfunction isError(obj) {\n var _a;\n try {\n return obj instanceof Error || obj && ((_a = Object.getPrototypeOf(obj)) == null ? void 0 : _a.name) === "Error";\n } catch (error) {\n return false;\n }\n}\nfunction isTypedArray(obj, constructor) {\n try {\n return obj instanceof constructor || Object.prototype.toString.call(obj) === `[object ${constructor.name}]`;\n } catch (error) {\n return false;\n }\n}\nfunction isArrayBuffer(obj) {\n try {\n return obj instanceof ArrayBuffer || Object.prototype.toString.call(obj) === "[object ArrayBuffer]";\n } catch (error) {\n return false;\n }\n}\nvar typedArrayConstructors = {\n i8: Int8Array,\n ui8: Uint8Array,\n ui8c: Uint8ClampedArray,\n i16: Int16Array,\n ui16: Uint16Array,\n i32: Int32Array,\n ui32: Uint32Array,\n // TODO: add Float16Array once it\'s in baseline\n f32: Float32Array,\n f64: Float64Array,\n bi64: BigInt64Array,\n bui64: BigUint64Array\n};\nfunction typedArrayToBase64(array) {\n if ("toBase64" in array)\n return array.toBase64();\n const binary = Array.from(new Uint8Array(array.buffer, array.byteOffset, array.byteLength)).map((b) => String.fromCharCode(b)).join("");\n return btoa(binary);\n}\nfunction base64ToTypedArray(base64, TypedArrayConstructor) {\n const binary = atob(base64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++)\n bytes[i] = binary.charCodeAt(i);\n return new TypedArrayConstructor(bytes.buffer);\n}\nfunction parseEvaluationResultValue(value, handles = [], refs = /* @__PURE__ */ new Map()) {\n if (Object.is(value, void 0))\n return void 0;\n if (typeof value === "object" && value) {\n if ("ref" in value)\n return refs.get(value.ref);\n if ("v" in value) {\n if (value.v === "undefined")\n return void 0;\n if (value.v === "null")\n return null;\n if (value.v === "NaN")\n return NaN;\n if (value.v === "Infinity")\n return Infinity;\n if (value.v === "-Infinity")\n return -Infinity;\n if (value.v === "-0")\n return -0;\n return void 0;\n }\n if ("d" in value) {\n return new Date(value.d);\n }\n if ("u" in value)\n return new URL(value.u);\n if ("bi" in value)\n return BigInt(value.bi);\n if ("e" in value) {\n const error = new Error(value.e.m);\n error.name = value.e.n;\n error.stack = value.e.s;\n return error;\n }\n if ("r" in value)\n return new RegExp(value.r.p, value.r.f);\n if ("a" in value) {\n const result = [];\n refs.set(value.id, result);\n for (const a of value.a)\n result.push(parseEvaluationResultValue(a, handles, refs));\n return result;\n }\n if ("o" in value) {\n const result = {};\n refs.set(value.id, result);\n for (const { k, v } of value.o) {\n if (k === "__proto__")\n continue;\n result[k] = parseEvaluationResultValue(v, handles, refs);\n }\n return result;\n }\n if ("h" in value)\n return handles[value.h];\n if ("ta" in value)\n return base64ToTypedArray(value.ta.b, typedArrayConstructors[value.ta.k]);\n if ("ab" in value)\n return base64ToTypedArray(value.ab.b, Uint8Array).buffer;\n }\n return value;\n}\nfunction serializeAsCallArgument(value, handleSerializer) {\n return serialize(value, handleSerializer, { visited: /* @__PURE__ */ new Map(), lastId: 0 });\n}\nfunction serialize(value, handleSerializer, visitorInfo) {\n if (value && typeof value === "object") {\n if (typeof globalThis.Window === "function" && value instanceof globalThis.Window)\n return "ref: ";\n if (typeof globalThis.Document === "function" && value instanceof globalThis.Document)\n return "ref: ";\n if (typeof globalThis.Node === "function" && value instanceof globalThis.Node)\n return "ref: ";\n }\n return innerSerialize(value, handleSerializer, visitorInfo);\n}\nfunction innerSerialize(value, handleSerializer, visitorInfo) {\n var _a;\n const result = handleSerializer(value);\n if ("fallThrough" in result)\n value = result.fallThrough;\n else\n return result;\n if (typeof value === "symbol")\n return { v: "undefined" };\n if (Object.is(value, void 0))\n return { v: "undefined" };\n if (Object.is(value, null))\n return { v: "null" };\n if (Object.is(value, NaN))\n return { v: "NaN" };\n if (Object.is(value, Infinity))\n return { v: "Infinity" };\n if (Object.is(value, -Infinity))\n return { v: "-Infinity" };\n if (Object.is(value, -0))\n return { v: "-0" };\n if (typeof value === "boolean")\n return value;\n if (typeof value === "number")\n return value;\n if (typeof value === "string")\n return value;\n if (typeof value === "bigint")\n return { bi: value.toString() };\n if (isError(value)) {\n let stack;\n if ((_a = value.stack) == null ? void 0 : _a.startsWith(value.name + ": " + value.message)) {\n stack = value.stack;\n } else {\n stack = `${value.name}: ${value.message}\n${value.stack}`;\n }\n return { e: { n: value.name, m: value.message, s: stack } };\n }\n if (isDate(value))\n return { d: value.toJSON() };\n if (isURL(value))\n return { u: value.toJSON() };\n if (isRegExp(value))\n return { r: { p: value.source, f: value.flags } };\n for (const [k, ctor] of Object.entries(typedArrayConstructors)) {\n if (isTypedArray(value, ctor))\n return { ta: { b: typedArrayToBase64(value), k } };\n }\n if (isArrayBuffer(value))\n return { ab: { b: typedArrayToBase64(new Uint8Array(value)) } };\n const id = visitorInfo.visited.get(value);\n if (id)\n return { ref: id };\n if (Array.isArray(value)) {\n const a = [];\n const id2 = ++visitorInfo.lastId;\n visitorInfo.visited.set(value, id2);\n for (let i = 0; i < value.length; ++i)\n a.push(serialize(value[i], handleSerializer, visitorInfo));\n return { a, id: id2 };\n }\n if (typeof value === "object") {\n const o = [];\n const id2 = ++visitorInfo.lastId;\n visitorInfo.visited.set(value, id2);\n for (const name of Object.keys(value)) {\n let item;\n try {\n item = value[name];\n } catch (e) {\n continue;\n }\n if (name === "toJSON" && typeof item === "function")\n o.push({ k: name, v: { o: [], id: 0 } });\n else\n o.push({ k: name, v: serialize(item, handleSerializer, visitorInfo) });\n }\n let jsonWrapper;\n try {\n if (o.length === 0 && value.toJSON && typeof value.toJSON === "function")\n jsonWrapper = { value: value.toJSON() };\n } catch (e) {\n }\n if (jsonWrapper)\n return innerSerialize(jsonWrapper.value, handleSerializer, visitorInfo);\n return { o, id: id2 };\n }\n}\n\n// packages/injected/src/storageScript.ts\nvar StorageScript = class {\n constructor(isFirefox) {\n this._isFirefox = isFirefox;\n this._global = globalThis;\n }\n _idbRequestToPromise(request) {\n return new Promise((resolve, reject) => {\n request.addEventListener("success", () => resolve(request.result));\n request.addEventListener("error", () => reject(request.error));\n });\n }\n _isPlainObject(v) {\n const ctor = v == null ? void 0 : v.constructor;\n if (this._isFirefox) {\n const constructorImpl = ctor == null ? void 0 : ctor.toString();\n if ((constructorImpl == null ? void 0 : constructorImpl.startsWith("function Object() {")) && (constructorImpl == null ? void 0 : constructorImpl.includes("[native code]")))\n return true;\n }\n return ctor === Object;\n }\n _trySerialize(value) {\n let trivial = true;\n const encoded = serializeAsCallArgument(value, (v) => {\n const isTrivial = this._isPlainObject(v) || Array.isArray(v) || typeof v === "string" || typeof v === "number" || typeof v === "boolean" || Object.is(v, null);\n if (!isTrivial)\n trivial = false;\n return { fallThrough: v };\n });\n if (trivial)\n return { trivial: value };\n return { encoded };\n }\n async _collectDB(dbInfo) {\n if (!dbInfo.name)\n throw new Error("Database name is empty");\n if (!dbInfo.version)\n throw new Error("Database version is unset");\n const db = await this._idbRequestToPromise(indexedDB.open(dbInfo.name));\n if (db.objectStoreNames.length === 0)\n return { name: dbInfo.name, version: dbInfo.version, stores: [] };\n const transaction = db.transaction(db.objectStoreNames, "readonly");\n const stores = await Promise.all([...db.objectStoreNames].map(async (storeName) => {\n const objectStore = transaction.objectStore(storeName);\n const keys = await this._idbRequestToPromise(objectStore.getAllKeys());\n const records = await Promise.all(keys.map(async (key) => {\n const record = {};\n if (objectStore.keyPath === null) {\n const { encoded: encoded2, trivial: trivial2 } = this._trySerialize(key);\n if (trivial2)\n record.key = trivial2;\n else\n record.keyEncoded = encoded2;\n }\n const value = await this._idbRequestToPromise(objectStore.get(key));\n const { encoded, trivial } = this._trySerialize(value);\n if (trivial)\n record.value = trivial;\n else\n record.valueEncoded = encoded;\n return record;\n }));\n const indexes = [...objectStore.indexNames].map((indexName) => {\n const index = objectStore.index(indexName);\n return {\n name: index.name,\n keyPath: typeof index.keyPath === "string" ? index.keyPath : void 0,\n keyPathArray: Array.isArray(index.keyPath) ? index.keyPath : void 0,\n multiEntry: index.multiEntry,\n unique: index.unique\n };\n });\n return {\n name: storeName,\n records,\n indexes,\n autoIncrement: objectStore.autoIncrement,\n keyPath: typeof objectStore.keyPath === "string" ? objectStore.keyPath : void 0,\n keyPathArray: Array.isArray(objectStore.keyPath) ? objectStore.keyPath : void 0\n };\n }));\n return {\n name: dbInfo.name,\n version: dbInfo.version,\n stores\n };\n }\n async collect(recordIndexedDB) {\n const localStorage = Object.keys(this._global.localStorage).map((name) => ({ name, value: this._global.localStorage.getItem(name) }));\n if (!recordIndexedDB)\n return { localStorage };\n try {\n const databases = await this._global.indexedDB.databases();\n const indexedDB2 = await Promise.all(databases.map((db) => this._collectDB(db)));\n return { localStorage, indexedDB: indexedDB2 };\n } catch (e) {\n throw new Error("Unable to serialize IndexedDB: " + e.message);\n }\n }\n async _restoreDB(dbInfo) {\n const openRequest = this._global.indexedDB.open(dbInfo.name, dbInfo.version);\n openRequest.addEventListener("upgradeneeded", () => {\n var _a, _b;\n const db2 = openRequest.result;\n for (const store of dbInfo.stores) {\n const objectStore = db2.createObjectStore(store.name, { autoIncrement: store.autoIncrement, keyPath: (_a = store.keyPathArray) != null ? _a : store.keyPath });\n for (const index of store.indexes)\n objectStore.createIndex(index.name, (_b = index.keyPathArray) != null ? _b : index.keyPath, { unique: index.unique, multiEntry: index.multiEntry });\n }\n });\n const db = await this._idbRequestToPromise(openRequest);\n if (db.objectStoreNames.length === 0)\n return;\n const transaction = db.transaction(db.objectStoreNames, "readwrite");\n await Promise.all(dbInfo.stores.map(async (store) => {\n const objectStore = transaction.objectStore(store.name);\n await Promise.all(store.records.map(async (record) => {\n var _a, _b;\n await this._idbRequestToPromise(\n objectStore.add(\n (_a = record.value) != null ? _a : parseEvaluationResultValue(record.valueEncoded),\n (_b = record.key) != null ? _b : parseEvaluationResultValue(record.keyEncoded)\n )\n );\n }));\n }));\n }\n async restore(originState) {\n var _a, _b, _c;\n const registrations = this._global.navigator.serviceWorker ? await this._global.navigator.serviceWorker.getRegistrations() : [];\n await Promise.all(registrations.map(async (r) => {\n if (!r.installing && !r.waiting && !r.active)\n r.unregister().catch(() => {\n });\n else\n await r.unregister().catch(() => {\n });\n }));\n try {\n for (const db of await ((_b = (_a = this._global.indexedDB).databases) == null ? void 0 : _b.call(_a)) || []) {\n if (db.name)\n this._global.indexedDB.deleteDatabase(db.name);\n }\n await Promise.all(((_c = originState == null ? void 0 : originState.indexedDB) != null ? _c : []).map((dbInfo) => this._restoreDB(dbInfo)));\n } catch (e) {\n throw new Error("Unable to restore IndexedDB: " + e.message);\n }\n this._global.sessionStorage.clear();\n this._global.localStorage.clear();\n for (const { name, value } of (originState == null ? void 0 : originState.localStorage) || [])\n this._global.localStorage.setItem(name, value);\n }\n};\n'; + } +}); + +// packages/playwright-core/src/server/browserContext.ts +function validateBrowserContextOptions(options, browserOptions) { + if (options.noDefaultViewport && options.deviceScaleFactor !== void 0) + throw new Error(`"deviceScaleFactor" option is not supported with null "viewport"`); + if (options.noDefaultViewport && !!options.isMobile) + throw new Error(`"isMobile" option is not supported with null "viewport"`); + if (options.acceptDownloads === void 0 && browserOptions.name !== "electron") + options.acceptDownloads = "accept"; + else if (options.acceptDownloads === void 0 && browserOptions.name === "electron") + options.acceptDownloads = "internal-browser-default"; + if (!options.viewport && !options.noDefaultViewport) + options.viewport = { width: 1280, height: 720 }; + if (options.proxy) + options.proxy = normalizeProxySettings(options.proxy); + verifyGeolocation(options.geolocation); +} +function verifyGeolocation(geolocation) { + if (!geolocation) + return; + geolocation.accuracy = geolocation.accuracy || 0; + const { longitude, latitude, accuracy } = geolocation; + if (longitude < -180 || longitude > 180) + throw new Error(`geolocation.longitude: precondition -180 <= LONGITUDE <= 180 failed.`); + if (latitude < -90 || latitude > 90) + throw new Error(`geolocation.latitude: precondition -90 <= LATITUDE <= 90 failed.`); + if (accuracy < 0) + throw new Error(`geolocation.accuracy: precondition 0 <= ACCURACY failed.`); +} +function verifyClientCertificates(clientCertificates) { + if (!clientCertificates) + return; + for (const cert of clientCertificates) { + if (!cert.origin) + throw new Error(`clientCertificates.origin is required`); + if (!cert.cert && !cert.key && !cert.passphrase && !cert.pfx) + throw new Error("None of cert, key, passphrase or pfx is specified"); + if (cert.cert && !cert.key) + throw new Error("cert is specified without key"); + if (!cert.cert && cert.key) + throw new Error("key is specified without cert"); + if (cert.pfx && (cert.cert || cert.key)) + throw new Error("pfx is specified together with cert, key or passphrase"); + } +} +function normalizeProxySettings(proxy) { + let { server, bypass } = proxy; + let url2; + try { + url2 = new URL(server); + if (!url2.host || !url2.protocol) + url2 = new URL("http://" + server); + } catch (e) { + url2 = new URL("http://" + server); + } + if (url2.protocol === "socks4:" && (proxy.username || proxy.password)) + throw new Error(`Socks4 proxy protocol does not support authentication`); + if (url2.protocol === "socks5:" && (proxy.username || proxy.password)) + throw new Error(`Browser does not support socks5 proxy authentication`); + server = url2.protocol + "//" + url2.host; + if (bypass) + bypass = bypass.split(",").map((t) => t.trim()).join(","); + return { ...proxy, server, bypass }; +} +var import_fs31, BrowserContextEvent, BrowserContext, paramsThatAllowContextReuse, defaultNewContextParamValues; +var init_browserContext = __esm({ + "packages/playwright-core/src/server/browserContext.ts"() { + "use strict"; + import_fs31 = __toESM(require("fs")); + init_stackTrace(); + init_debug(); + init_clock(); + init_credentials(); + init_debugger(); + init_dialog(); + init_fetch(); + init_helper(); + init_instrumentation(); + init_network2(); + init_page(); + init_page(); + init_recorderApp(); + init_selectors(); + init_tracing(); + init_storageScriptSource(); + init_progress(); + BrowserContextEvent = { + Console: "console", + Close: "close", + Page: "page", + // Can't use just 'error' due to node.js special treatment of error events. + // @see https://nodejs.org/api/events.html#events_error_events + PageError: "pageerror", + Request: "request", + Response: "response", + RequestFailed: "requestfailed", + RequestFinished: "requestfinished", + RequestAborted: "requestaborted", + RequestFulfilled: "requestfulfilled", + RequestContinued: "requestcontinued", + BeforeClose: "beforeclose", + RecorderEvent: "recorderevent", + PageClosed: "pageclosed", + InternalFrameNavigatedToNewDocument: "internalframenavigatedtonewdocument", + FrameAttached: "frameattached", + WebSocket: "websocket" + }; + BrowserContext = class _BrowserContext extends SdkObject { + constructor(browser, options, browserContextId) { + super(browser, "browser-context"); + this._pageBindings = /* @__PURE__ */ new Map(); + this.requestInterceptors = []; + this._closedStatus = "open"; + this._permissions = /* @__PURE__ */ new Map(); + this._downloads = /* @__PURE__ */ new Set(); + this._origins = /* @__PURE__ */ new Set(); + this._tempDirs = []; + this._creatingStorageStatePage = false; + this.initScripts = []; + this._routesInFlight = /* @__PURE__ */ new Set(); + this._consoleApiExposed = false; + this.attribution.context = this; + this._browser = browser; + this._options = options; + this._browserContextId = browserContextId; + this._isPersistentContext = !browserContextId; + this._closePromise = new Promise((fulfill) => this._closePromiseFulfill = fulfill); + this._selectors = new Selectors(options.selectorEngines || [], options.testIdAttributeName); + this.fetchRequest = new BrowserContextAPIRequestContext(this); + this.tracing = new Tracing(this, browser.options.tracesDir); + this.clock = new Clock(this); + this.credentials = new Credentials(this); + this.dialogManager = new DialogManager(this.instrumentation); + } + static { + this.Events = BrowserContextEvent; + } + isPersistentContext() { + return this._isPersistentContext; + } + selectors() { + return this._selectors; + } + async initialize() { + if (this.attribution.playwright.options.isInternalPlaywright) + return; + this._debugger = new Debugger(this); + const shouldEnableDebugger = !this.attribution.playwright.options.isServer && (isUnderTest() || !!this._browser.options.headful); + if (shouldEnableDebugger) { + this._debugger.setPauseAt(); + this._debugger.on(Debugger.Events.PausedStateChanged, () => { + if (this._debugger.isPaused()) + RecorderApp.showInspectorNoReply(this); + }); + } + if (debugMode() === "inspector") { + this._debugger.setPauseAt({ next: true }); + await RecorderApp.show(this, { pauseOnNextStatement: true }); + } + if (debugMode() === "console") + await this._exposeConsoleApi(); + if (this._options.serviceWorkers === "block") + await this.addInitScript(nullProgress, ` +if (navigator.serviceWorker) navigator.serviceWorker.register = async () => { console.warn('Service Worker registration blocked by Playwright'); }; +`); + if (this._options.permissions) + await this.grantPermissions(this._options.permissions); + } + debugger() { + return this._debugger; + } + async exposeConsoleApi(progress2) { + await progress2.race(this._exposeConsoleApi()); + } + async _exposeConsoleApi() { + if (this._consoleApiExposed) + return; + this._consoleApiExposed = true; + await this.extendInjectedScript(` + function installConsoleApi(injectedScript) { injectedScript.consoleApi.install(); } + module.exports = { default: () => installConsoleApi }; + `); + } + canResetForReuse() { + if (this._closedStatus !== "open") + return false; + return true; + } + static reusableContextHash(params2) { + const paramsCopy = { ...params2 }; + if (paramsCopy.selectorEngines?.length === 0) + delete paramsCopy.selectorEngines; + for (const k of Object.keys(paramsCopy)) { + const key = k; + if (paramsCopy[key] === defaultNewContextParamValues[key]) + delete paramsCopy[key]; + } + for (const key of paramsThatAllowContextReuse) + delete paramsCopy[key]; + return JSON.stringify(paramsCopy); + } + async resetForReuse(progress2, params2) { + await this.tracing.resetForReuse(progress2); + if (params2) { + for (const key of paramsThatAllowContextReuse) + this._options[key] = params2[key]; + if (params2.testIdAttributeName) + this.selectors().setTestIdAttributeName(params2.testIdAttributeName); + } + let page = this.pages()[0]; + const otherPages = this.possiblyUninitializedPages().filter((p) => p !== page); + for (const p of otherPages) + await p.close(progress2); + if (page && page.hasCrashed()) { + await page.close(progress2); + page = void 0; + } + await page?.mainFrame().gotoImpl(progress2, "about:blank", {}); + await this.clock.uninstall(progress2); + await this.credentials.dispose(progress2); + await progress2.race(this.setUserAgent(this._options.userAgent)); + await progress2.race(this.doUpdateDefaultEmulatedMedia()); + await progress2.race(this.doUpdateDefaultViewport()); + await this.setStorageState(progress2, this._options.storageState, "resetForReuse"); + await page?.resetForReuse(progress2); + } + browserClosed() { + for (const page of this.pages()) + page._didClose(); + this._didCloseInternal(); + } + _didCloseInternal() { + if (this._closedStatus === "closed") { + return; + } + this._clientCertificatesProxy?.close().catch(() => { + }); + this.tracing.abort(); + if (this._isPersistentContext) + this.onClosePersistent(); + this._closePromiseFulfill(new Error("Context closed")); + this.emit(_BrowserContext.Events.Close); + } + pages() { + return this.possiblyUninitializedPages().filter((page) => page.initializedOrUndefined()); + } + async cookies(progress2, urls = []) { + return await progress2.race(this._cookies(urls)); + } + async _cookies(urls = []) { + if (urls && !Array.isArray(urls)) + urls = [urls]; + return await this.doGetCookies(urls); + } + async clearCookies(options) { + const hasFilter = options.name !== void 0 || options.domain !== void 0 || options.path !== void 0; + if (!hasFilter) { + await this.doClearCookies(); + return; + } + const matches = (cookie, prop, value2) => { + if (!value2) + return true; + if (value2 instanceof RegExp) { + value2.lastIndex = 0; + return value2.test(cookie[prop]); + } + return cookie[prop] === value2; + }; + const currentCookies = await this._cookies(); + const cookiesToExpire = currentCookies.filter((cookie) => { + return matches(cookie, "name", options.name) && matches(cookie, "domain", options.domain) && matches(cookie, "path", options.path); + }); + if (!cookiesToExpire.length) + return; + await this.addCookies(cookiesToExpire.map((cookie) => ({ + ...cookie, + value: "", + expires: 0 + }))); + } + setHTTPCredentials(progress2, httpCredentials) { + return progress2.race(this.innerSetHTTPCredentials(httpCredentials)); + } + innerSetHTTPCredentials(httpCredentials) { + return this.doSetHTTPCredentials(httpCredentials); + } + getBindingClient(name) { + return this._pageBindings.get(name)?.forClient; + } + async exposePlaywrightBindingIfNeeded() { + this._playwrightBindingExposed ??= (async () => { + await this.doExposePlaywrightBinding(); + this.bindingsInitScript = PageBinding.createInitScript(this); + this.initScripts.push(this.bindingsInitScript); + await this.doAddInitScript(this.bindingsInitScript); + await this.safeNonStallingEvaluateInAllFrames(this.bindingsInitScript.source, "main"); + })(); + return await this._playwrightBindingExposed; + } + needsPlaywrightBinding() { + return this._playwrightBindingExposed !== void 0; + } + async exposeBinding(progress2, name, playwrightBinding, forClient) { + if (this._pageBindings.has(name)) + throw new Error(`Function "${name}" has been already registered`); + for (const page of this.pages()) { + if (page.getBinding(name)) + throw new Error(`Function "${name}" has been already registered in one of the pages`); + } + await progress2.race(this.exposePlaywrightBindingIfNeeded()); + const binding = new PageBinding(this, name, playwrightBinding); + binding.forClient = forClient; + this._pageBindings.set(name, binding); + try { + await progress2.race(this.doAddInitScript(binding.initScript)); + await progress2.race(this.safeNonStallingEvaluateInAllFrames(binding.initScript.source, "main")); + return binding; + } catch (error) { + this._pageBindings.delete(name); + throw error; + } + } + async removeExposedBinding(binding) { + if (this._pageBindings.get(binding.name) !== binding) + return; + this._pageBindings.delete(binding.name); + await this.doRemoveInitScripts([binding.initScript]); + const cleanup = `{ ${binding.cleanupScript} };`; + await this.safeNonStallingEvaluateInAllFrames(cleanup, "main"); + } + async grantPermissions(permissions, origin) { + let resolvedOrigin = "*"; + if (origin) { + const url2 = new URL(origin); + resolvedOrigin = url2.origin; + } + const existing = new Set(this._permissions.get(resolvedOrigin) || []); + permissions.forEach((p) => existing.add(p)); + const list = [...existing.values()]; + this._permissions.set(resolvedOrigin, list); + await this.doGrantPermissions(resolvedOrigin, list); + } + async clearPermissions() { + this._permissions.clear(); + await this.doClearPermissions(); + } + async setExtraHTTPHeaders(progress2, headers) { + const oldHeaders = this._options.extraHTTPHeaders; + this._options.extraHTTPHeaders = headers; + try { + await progress2.race(this.doUpdateExtraHTTPHeaders()); + } catch (error) { + this._options.extraHTTPHeaders = oldHeaders; + this.doUpdateExtraHTTPHeaders().catch(() => { + }); + throw error; + } + } + async setOffline(progress2, offline) { + const oldOffline = this._options.offline; + this._options.offline = offline; + try { + await progress2.race(this.doUpdateOffline()); + } catch (error) { + this._options.offline = oldOffline; + this.doUpdateOffline().catch(() => { + }); + throw error; + } + } + async loadDefaultContextAsIs(progress2) { + if (!this.possiblyUninitializedPages().length) { + const waitForEvent2 = helper.waitForEvent(progress2, this, _BrowserContext.Events.Page); + await progress2.race(Promise.race([waitForEvent2.promise, this._closePromise])); + } + const page = this.possiblyUninitializedPages()[0]; + if (!page) + return; + const pageOrError = await progress2.race(page.waitForInitializedOrError()); + if (pageOrError instanceof Error) + throw pageOrError; + await page.mainFrame().waitForLoadState(progress2, "load"); + return page; + } + async loadDefaultContext(progress2) { + const defaultPage = await this.loadDefaultContextAsIs(progress2); + if (!defaultPage) + return; + const browserName = this._browser.options.name; + if (this._options.isMobile && browserName === "chromium" || this._options.locale && browserName === "webkit") { + await this.newPage(progress2); + await defaultPage.close(progress2); + } + } + authenticateProxyViaHeader() { + const proxy = this._options.proxy || this._browser.options.proxy || { username: void 0, password: void 0 }; + const { username, password } = proxy; + if (username) { + this._options.httpCredentials = { username, password }; + const token = Buffer.from(`${username}:${password}`).toString("base64"); + this._options.extraHTTPHeaders = mergeHeaders([ + this._options.extraHTTPHeaders, + singleHeader("Proxy-Authorization", `Basic ${token}`) + ]); + } + } + authenticateProxyViaCredentials() { + const proxy = this._options.proxy || this._browser.options.proxy; + if (!proxy) + return; + const { username, password } = proxy; + if (username) + this._options.httpCredentials = { username, password: password || "" }; + } + async addInitScript(progress2, source11) { + return await progress2.race(this._internalAddInitScript(source11)); + } + async _internalAddInitScript(source11) { + const initScript = new InitScript(this, source11); + this.initScripts.push(initScript); + try { + await this.doAddInitScript(initScript); + return initScript; + } catch (error) { + initScript.dispose().catch(() => { + }); + throw error; + } + } + async removeInitScript(initScript) { + this.initScripts = this.initScripts.filter((script) => initScript !== script); + await this.doRemoveInitScripts([initScript]); + } + async addRequestInterceptor(progress2, handler) { + this.requestInterceptors.push(handler); + await progress2.race(this.doUpdateRequestInterception()); + } + async removeRequestInterceptor(handler) { + const index = this.requestInterceptors.indexOf(handler); + if (index === -1) + return; + this.requestInterceptors.splice(index, 1); + await this.notifyRoutesInFlightAboutRemovedHandler(handler); + await this.doUpdateRequestInterception(); + } + isClosingOrClosed() { + return this._closedStatus !== "open"; + } + async _deleteAllDownloads() { + await Promise.all(Array.from(this._downloads).map((download) => download.artifact.deleteOnContextClose())); + } + async _deleteAllTempDirs() { + await Promise.all(this._tempDirs.map(async (dir) => await import_fs31.default.promises.rm(dir, { recursive: true, force: true }).catch((e) => { + }))); + } + setCustomCloseHandler(handler) { + this._customCloseHandler = handler; + } + async close(progress2, options) { + if (this._closedStatus === "open") { + if (options.reason) + this._closeReason = options.reason; + this.emit(_BrowserContext.Events.BeforeClose); + this._closedStatus = "closing"; + await progress2.race(this.tracing.flush()); + await progress2.race(Promise.all(this.pages().map((page) => page.screencast.handlePageOrContextClose()))); + if (this._customCloseHandler) { + await progress2.race(this._customCloseHandler()); + } else { + const disposition = await progress2.race(this.doClose(options.reason)); + if (disposition === "close-browser") + await this._browser.close(progress2, { reason: options.reason }); + } + const promises = []; + promises.push(this._deleteAllDownloads()); + promises.push(this._deleteAllTempDirs()); + await progress2.race(Promise.all(promises)); + if (!this._customCloseHandler) + this._didCloseInternal(); + } + await this._closePromise; + } + async newPage(progress2, forStorageState) { + let page; + try { + this._creatingStorageStatePage = !!forStorageState; + page = await progress2.race(this.doCreateNewPage()); + const pageOrError = await progress2.race(page.waitForInitializedOrError()); + if (pageOrError instanceof Page) { + if (pageOrError.isClosed()) + throw new Error("Page has been closed."); + return pageOrError; + } + throw pageOrError; + } catch (error) { + await page?.close(progress2, { reason: "Failed to create page" }).catch(() => { + }); + throw error; + } finally { + this._creatingStorageStatePage = false; + } + } + addVisitedOrigin(origin) { + this._origins.add(origin); + } + async storageState(progress2, indexedDB = false) { + const result2 = { + cookies: await this.cookies(progress2), + origins: [] + }; + const originsToSave = new Set(this._origins); + const collectScript = `(() => { + const module = {}; + ${source9} + const script = new (module.exports.StorageScript())(${this._browser.options.name === "firefox"}); + return script.collect(${indexedDB}); + })()`; + for (const page of this.pages()) { + const origin = page.mainFrame().origin(); + if (!origin || !originsToSave.has(origin)) + continue; + try { + const storage = await progress2.race(page.mainFrame().nonStallingEvaluateInExistingContext(collectScript, "utility")); + if (storage.localStorage.length || storage.indexedDB?.length) + result2.origins.push({ origin, localStorage: storage.localStorage, indexedDB: storage.indexedDB }); + originsToSave.delete(origin); + } catch { + } + } + if (originsToSave.size) { + const page = await this.newPage( + progress2, + true + /* forStorageState */ + ); + try { + await page.addRequestInterceptor(progress2, (route2) => { + route2.fulfill({ body: "" }).catch(() => { + }); + }, "prepend"); + for (const origin of originsToSave) { + const frame = page.mainFrame(); + await frame.gotoImpl(progress2, origin, {}); + const storage = await frame.evaluateExpression(progress2, collectScript, { world: "utility" }); + if (storage.localStorage.length || storage.indexedDB?.length) + result2.origins.push({ origin, localStorage: storage.localStorage, indexedDB: storage.indexedDB }); + } + } finally { + await page.close(progress2); + } + } + return result2; + } + isCreatingStorageStatePage() { + return this._creatingStorageStatePage; + } + async setStorageState(progress2, state, mode) { + let page; + let interceptor; + try { + if (mode !== "initial") { + await progress2.race(this.clearCache()); + await progress2.race(this.doClearCookies()); + } + if (state?.cookies) + await progress2.race(this.addCookies(state.cookies)); + const newOrigins = new Map(state?.origins?.map((p) => [p.origin, p]) || []); + const allOrigins = /* @__PURE__ */ new Set([...this._origins, ...newOrigins.keys()]); + if (allOrigins.size) { + if (mode === "resetForReuse") + page = this.pages()[0]; + if (!page) + page = await this.newPage( + progress2, + mode !== "resetForReuse" + /* forStorageState */ + ); + interceptor = (route2) => { + route2.fulfill({ body: "" }).catch(() => { + }); + }; + await page.addRequestInterceptor(progress2, interceptor, "prepend"); + for (const origin of allOrigins) { + const frame = page.mainFrame(); + await frame.gotoImpl(progress2, origin, {}); + const restoreScript = `(() => { + const module = {}; + ${source9} + const script = new (module.exports.StorageScript())(${this._browser.options.name === "firefox"}); + return script.restore(${JSON.stringify(newOrigins.get(origin))}); + })()`; + await frame.evaluateExpression(progress2, restoreScript, { world: "utility" }); + } + } + this._origins = /* @__PURE__ */ new Set([...newOrigins.keys()]); + } catch (error) { + rewriteErrorMessage(error, `Error setting storage state: +` + error.message); + throw error; + } finally { + if (mode !== "resetForReuse") + await page?.close(progress2); + else if (interceptor) + await page?.removeRequestInterceptor(interceptor); + } + } + async extendInjectedScript(source11, arg) { + const installInFrame = (frame) => frame.extendInjectedScript(source11, arg).catch(() => { + }); + const installInPage = (page) => { + page.on(Page.Events.InternalFrameNavigatedToNewDocument, installInFrame); + return Promise.all(page.frames().map(installInFrame)); + }; + this.on(_BrowserContext.Events.Page, installInPage); + return Promise.all(this.pages().map(installInPage)); + } + async safeNonStallingEvaluateInAllFrames(expression2, world, options = {}) { + await Promise.all(this.pages().map((page) => page.safeNonStallingEvaluateInAllFrames(expression2, world, options))); + } + addRouteInFlight(route2) { + this._routesInFlight.add(route2); + } + removeRouteInFlight(route2) { + this._routesInFlight.delete(route2); + } + async notifyRoutesInFlightAboutRemovedHandler(handler) { + await Promise.all([...this._routesInFlight].map((route2) => route2.removeHandler(handler))); + } + }; + paramsThatAllowContextReuse = [ + "colorScheme", + "forcedColors", + "reducedMotion", + "contrast", + "screen", + "userAgent", + "viewport", + "testIdAttributeName" + ]; + defaultNewContextParamValues = { + noDefaultViewport: false, + ignoreHTTPSErrors: false, + javaScriptEnabled: true, + bypassCSP: false, + offline: false, + isMobile: false, + hasTouch: false, + acceptDownloads: "accept", + strictSelectors: false, + serviceWorkers: "allow", + locale: "en-US" + }; + } +}); + +// packages/playwright-core/src/server/download.ts +var Download; +var init_download = __esm({ + "packages/playwright-core/src/server/download.ts"() { + "use strict"; + init_assert(); + init_fileUtils(); + init_page(); + init_artifact(); + Download = class { + constructor(page, downloadsPath, uuid, url2, suggestedFilename, downloadFilename) { + const unaccessibleErrorMessage = page.browserContext._options.acceptDownloads === "deny" ? "Pass { acceptDownloads: true } when you are creating your browser context." : void 0; + const downloadPath = resolveWithinRoot(downloadsPath, downloadFilename ?? uuid); + if (!downloadPath) + throw new Error(`Download filename '${downloadFilename}' escapes download directory`); + this.artifact = new Artifact(page, downloadPath, unaccessibleErrorMessage, () => this.cancel()); + this._page = page; + this.url = url2; + this._uuid = uuid; + this._suggestedFilename = suggestedFilename; + page.browserContext._downloads.add(this); + if (suggestedFilename !== void 0) + this._fireDownloadEvent(); + } + cancel() { + return this._page.browserContext.cancelDownload(this._uuid); + } + filenameSuggested(suggestedFilename) { + assert(this._suggestedFilename === void 0); + this._suggestedFilename = suggestedFilename; + this._fireDownloadEvent(); + } + suggestedFilename() { + return this._suggestedFilename; + } + _fireDownloadEvent() { + this._page.instrumentation.onDownload(this._page, this); + this._page.emit(Page.Events.Download, this); + } + }; + } +}); + +// packages/playwright-core/src/remote/serverTransport.ts +var import_events13, WebSocketServerTransport, SocketServerTransport; +var init_serverTransport = __esm({ + "packages/playwright-core/src/remote/serverTransport.ts"() { + "use strict"; + import_events13 = require("events"); + WebSocketServerTransport = class { + constructor(ws4) { + this._ws = ws4; + } + send(message) { + this._ws.send(message); + } + close(reason) { + this._ws.close(reason?.code, reason?.reason); + } + on(event, handler) { + this._ws.on(event, handler); + } + isClosed() { + return this._ws.readyState === this._ws.CLOSING || this._ws.readyState === this._ws.CLOSED; + } + }; + SocketServerTransport = class extends import_events13.EventEmitter { + constructor(socket) { + super(); + this._closed = false; + this._pendingBuffers = []; + this._socket = socket; + socket.on("data", (buffer) => this._dispatch(buffer)); + socket.on("close", () => { + this._closed = true; + super.emit("close"); + }); + socket.on("error", (error) => { + super.emit("error", error); + }); + } + send(message) { + if (this._closed) + return; + this._socket.write(message); + this._socket.write("\0"); + } + close(reason) { + if (this._closed) + return; + this._closed = true; + this._socket.end(); + } + isClosed() { + return this._closed; + } + _dispatch(buffer) { + let end = buffer.indexOf("\0"); + if (end === -1) { + this._pendingBuffers.push(buffer); + return; + } + this._pendingBuffers.push(buffer.slice(0, end)); + const message = Buffer.concat(this._pendingBuffers).toString(); + super.emit("message", message); + let start3 = end + 1; + end = buffer.indexOf("\0", start3); + while (end !== -1) { + super.emit("message", buffer.toString(void 0, start3, end)); + start3 = end + 1; + end = buffer.indexOf("\0", start3); + } + this._pendingBuffers = [buffer.slice(start3)]; + } + }; + } +}); + +// packages/playwright-core/src/remote/playwrightPipeServer.ts +var import_net5, import_fs32, PlaywrightPipeServer; +var init_playwrightPipeServer = __esm({ + "packages/playwright-core/src/remote/playwrightPipeServer.ts"() { + "use strict"; + import_net5 = __toESM(require("net")); + import_fs32 = __toESM(require("fs")); + init_debugLogger(); + init_network(); + init_semaphore(); + init_playwrightConnection(); + init_serverTransport(); + init_browser(); + PlaywrightPipeServer = class { + constructor(browser) { + this._connections = /* @__PURE__ */ new Set(); + this._connectionId = 0; + this._browser = browser; + browser.on(Browser.Events.Disconnected, () => this.close()); + } + async listen(pipeName) { + if (!pipeName.startsWith("\\\\.\\pipe\\")) { + try { + import_fs32.default.unlinkSync(pipeName); + } catch { + } + } + this._server = import_net5.default.createServer((socket) => { + const id = String(++this._connectionId); + debugLogger.log("server", `[${id}] pipe client connected`); + const transport = new SocketServerTransport(socket); + const connection = new PlaywrightConnection( + new Semaphore(1), + transport, + false, + this._browser.attribution.playwright, + () => this._initPreLaunchedBrowserMode(id), + id + ); + this._connections.add(connection); + transport.on("close", () => this._connections.delete(connection)); + }); + decorateServer(this._server); + await new Promise((resolve, reject) => { + this._server.listen(pipeName, () => resolve()); + this._server.on("error", reject); + }); + debugLogger.log("server", `Pipe server listening at ${pipeName}`); + } + async _initPreLaunchedBrowserMode(id) { + debugLogger.log("server", `[${id}] engaged pre-launched (browser) pipe mode`); + return { + preLaunchedBrowser: this._browser, + sharedBrowser: true, + denyLaunch: true + }; + } + async close() { + if (!this._server) + return; + debugLogger.log("server", "closing pipe server"); + for (const connection of this._connections) + await connection.close({ code: 1001, reason: "Server closing" }); + this._connections.clear(); + await new Promise((f) => this._server.close(() => f())); + this._server = void 0; + debugLogger.log("server", "closed pipe server"); + } + }; + } +}); + +// packages/playwright-core/src/remote/playwrightWebSocketServer.ts +var PlaywrightWebSocketServer; +var init_playwrightWebSocketServer = __esm({ + "packages/playwright-core/src/remote/playwrightWebSocketServer.ts"() { + "use strict"; + init_debugLogger(); + init_wsServer(); + init_semaphore(); + init_playwrightConnection(); + init_serverTransport(); + init_browser(); + PlaywrightWebSocketServer = class { + constructor(browser, path59) { + this._browser = browser; + browser.on(Browser.Events.Disconnected, () => this.close()); + const semaphore = new Semaphore(Infinity); + this._wsServer = new WSServer({ + onRequest: (request2, response2) => { + response2.end("Running"); + }, + onUpgrade: () => void 0, + onHeaders: () => { + }, + onConnection: (request2, url2, ws4, id) => { + debugLogger.log("server", `[${id}] ws client connected`); + return new PlaywrightConnection( + semaphore, + new WebSocketServerTransport(ws4), + false, + this._browser.attribution.playwright, + () => this._initPreLaunchedBrowserMode(id), + id + ); + } + }); + } + async _initPreLaunchedBrowserMode(id) { + debugLogger.log("server", `[${id}] engaged pre-launched (browser) ws mode`); + return { + preLaunchedBrowser: this._browser, + sharedBrowser: true, + denyLaunch: true + }; + } + async listen(port = 0, hostname, path59) { + return await this._wsServer.listen(port, hostname, path59 || "/"); + } + async close() { + await this._wsServer.close(); + } + }; + } +}); + +// packages/playwright-core/src/serverRegistry.ts +async function canConnectTo(descriptor) { + if (!descriptor.endpoint) + return false; + if (descriptor.endpoint.startsWith("ws://") || descriptor.endpoint.startsWith("wss://")) { + return await new Promise((resolve) => { + const url2 = new URL(descriptor.endpoint); + const socket = import_net6.default.createConnection(Number(url2.port), url2.hostname, () => { + socket.destroy(); + resolve(true); + }); + socket.on("error", () => resolve(false)); + }); + } + return await new Promise((resolve) => { + const socket = import_net6.default.createConnection(descriptor.endpoint ?? descriptor.pipeName, () => { + socket.destroy(); + resolve(true); + }); + socket.on("error", () => resolve(false)); + }); +} +var import_events14, import_fs33, import_net6, import_path33, import_os17, chokidar, packageVersion, ServerRegistry, defaultCacheDirectory2, registryDirectory2, serverRegistry; +var init_serverRegistry = __esm({ + "packages/playwright-core/src/serverRegistry.ts"() { + "use strict"; + import_events14 = require("events"); + import_fs33 = __toESM(require("fs")); + import_net6 = __toESM(require("net")); + import_path33 = __toESM(require("path")); + import_os17 = __toESM(require("os")); + init_package(); + chokidar = require("./utilsBundle").chokidar; + packageVersion = packageJSON.version; + ServerRegistry = class extends import_events14.EventEmitter { + constructor() { + super(...arguments); + this._descriptors = /* @__PURE__ */ new Map(); + this._watcherRefs = 0; + } + on(event, listener) { + return super.on(event, listener); + } + off(event, listener) { + return super.off(event, listener); + } + watch() { + this._watcherRefs++; + if (!this._watcher) + this._startWatcher(); + let disposed = false; + return () => { + if (disposed) + return; + disposed = true; + this._watcherRefs--; + if (this._watcherRefs === 0) + this._stopWatcher(); + }; + } + ready() { + return this._ready ?? Promise.resolve(); + } + async list() { + const ownWatcher = !this._watcher; + let dispose; + if (ownWatcher) + dispose = this.watch(); + try { + await this._ready; + const statuses = await Promise.all( + [...this._descriptors.values()].map(async (descriptor) => { + const canConnect = await canConnectTo(descriptor); + return { descriptor, canConnect }; + }) + ); + const result2 = /* @__PURE__ */ new Map(); + for (const { descriptor, canConnect } of statuses) { + if (!canConnect) { + await import_fs33.default.promises.unlink(import_path33.default.join(this._browsersDir(), descriptor.browser.guid)).catch(() => { + }); + continue; + } + const key = descriptor.workspaceDir ?? ""; + let list = result2.get(key); + if (!list) { + list = []; + result2.set(key, list); + } + list.push(descriptor); + } + return result2; + } finally { + dispose?.(); + } + } + async create(browser, endpoint) { + const file = import_path33.default.join(this._browsersDir(), browser.guid); + await import_fs33.default.promises.mkdir(this._browsersDir(), { recursive: true }); + const descriptor = { + playwrightVersion: packageVersion, + playwrightLib: packageRoot, + title: endpoint.title, + browser, + endpoint: endpoint.endpoint, + workspaceDir: endpoint.workspaceDir + }; + await import_fs33.default.promises.writeFile(file, JSON.stringify(descriptor, null, 2), "utf-8"); + } + async delete(guid) { + const file = import_path33.default.join(this._browsersDir(), guid); + await import_fs33.default.promises.unlink(file).catch(() => { + }); + } + async deleteUserData(guid) { + const filePath = import_path33.default.join(this._browsersDir(), guid); + const content = await import_fs33.default.promises.readFile(filePath, "utf-8"); + const descriptor = JSON.parse(content); + if (descriptor.browser.userDataDir) + await import_fs33.default.promises.rm(descriptor.browser.userDataDir, { recursive: true, force: true }); + await import_fs33.default.promises.unlink(filePath); + } + readDescriptor(guid) { + const cached = this._descriptors.get(guid); + if (cached) + return cached; + const filePath = import_path33.default.join(this._browsersDir(), guid); + const content = import_fs33.default.readFileSync(filePath, "utf-8"); + return JSON.parse(content); + } + async find(name) { + const entries = await this.list(); + for (const [, browsers] of entries) { + for (const browser of browsers) { + if (browser.title === name) + return browser; + } + } + return null; + } + _browsersDir() { + return process.env.PWTEST_SERVER_REGISTRY || registryDirectory2; + } + _startWatcher() { + const dir = this._browsersDir(); + try { + import_fs33.default.mkdirSync(dir, { recursive: true }); + } catch { + } + const watcher = chokidar.watch(dir, { + ignoreInitial: false, + depth: 0, + awaitWriteFinish: { stabilityThreshold: 50, pollInterval: 20 } + }); + this._watcher = watcher; + this._ready = new Promise((resolve, reject) => { + watcher.once("ready", () => resolve()); + watcher.once("error", reject); + }); + watcher.on("add", (file) => this._onAddOrChange(file, "added")); + watcher.on("change", (file) => this._onAddOrChange(file, "changed")); + watcher.on("unlink", (file) => { + const guid = import_path33.default.basename(file); + if (this._descriptors.delete(guid)) + this.emit("removed", guid); + }); + } + _onAddOrChange(file, event) { + const guid = import_path33.default.basename(file); + let descriptor; + try { + descriptor = JSON.parse(import_fs33.default.readFileSync(file, "utf-8")); + } catch { + return; + } + this._descriptors.set(guid, descriptor); + this.emit(event, descriptor); + } + _stopWatcher() { + const watcher = this._watcher; + this._watcher = void 0; + this._ready = void 0; + this._descriptors.clear(); + void watcher?.close(); + } + }; + defaultCacheDirectory2 = (() => { + if (process.platform === "linux") + return process.env.XDG_CACHE_HOME || import_path33.default.join(import_os17.default.homedir(), ".cache"); + if (process.platform === "darwin") + return import_path33.default.join(import_os17.default.homedir(), "Library", "Caches"); + if (process.platform === "win32") + return process.env.LOCALAPPDATA || import_path33.default.join(import_os17.default.homedir(), "AppData", "Local"); + throw new Error("Unsupported platform: " + process.platform); + })(); + registryDirectory2 = import_path33.default.join(defaultCacheDirectory2, "ms-playwright", "b"); + serverRegistry = new ServerRegistry(); + } +}); + +// packages/playwright-core/src/server/browser.ts +function asClientLaunchOptions(serverOptions) { + return { + ...serverOptions, + env: serverOptions.env ? Object.fromEntries(serverOptions.env.map(({ name, value: value2 }) => [name, value2])) : void 0 + }; +} +var import_fs34, Browser, BrowserServer; +var init_browser = __esm({ + "packages/playwright-core/src/server/browser.ts"() { + "use strict"; + import_fs34 = __toESM(require("fs")); + init_fileUtils(); + init_crypto(); + init_browserContext(); + init_download(); + init_instrumentation(); + init_socksClientCertificatesInterceptor(); + init_playwrightPipeServer(); + init_playwrightWebSocketServer(); + init_serverRegistry(); + init_progress(); + init_errors(); + Browser = class _Browser extends SdkObject { + constructor(parent, options) { + super(parent, "browser"); + this._downloads = /* @__PURE__ */ new Map(); + this._defaultContext = null; + this._startedClosing = false; + this._isBrowserCollocatedWithServer = true; + this.attribution.browser = this; + this.options = options; + this.instrumentation.onBrowserOpen(this); + this._server = new BrowserServer(this); + } + static { + this.Events = { + Context: "context", + Disconnected: "disconnected" + }; + } + sdkLanguage() { + return this.options.sdkLanguage || this.attribution.playwright.options.sdkLanguage; + } + async newContext(progress2, options) { + validateBrowserContextOptions(options, this.options); + let clientCertificatesProxy; + let context2; + try { + if (options.clientCertificates?.length) { + clientCertificatesProxy = await ClientCertificatesProxy.create(progress2, options); + options = { ...options }; + options.proxyOverride = clientCertificatesProxy.proxySettings(); + options.internalIgnoreHTTPSErrors = true; + } + context2 = await progress2.race(this.doCreateNewContext(options)); + context2._clientCertificatesProxy = clientCertificatesProxy; + if (options.__testHookBeforeSetStorageState) + await progress2.race(options.__testHookBeforeSetStorageState()); + await context2.setStorageState(progress2, options.storageState, "initial"); + this.emit(_Browser.Events.Context, context2); + return context2; + } catch (error) { + await context2?.close(progress2, { reason: "Failed to create context" }).catch(() => { + }); + await clientCertificatesProxy?.close().catch(() => { + }); + throw error; + } + } + async newContextForReuse(progress2, params2) { + const hash = BrowserContext.reusableContextHash(params2); + if (!this._contextForReuse || hash !== this._contextForReuse.hash || !this._contextForReuse.context.canResetForReuse()) { + if (this._contextForReuse) + await this._contextForReuse.context.close(progress2, { reason: "Context reused" }); + this._contextForReuse = { context: await this.newContext(progress2, params2), hash }; + return this._contextForReuse.context; + } + await this._contextForReuse.context.resetForReuse(progress2, params2); + return this._contextForReuse.context; + } + contextForReuse() { + return this._contextForReuse?.context; + } + downloadCreated(page, uuid, url2, suggestedFilename, downloadFilename) { + const download = new Download(page, this.options.downloadsPath || "", uuid, url2, suggestedFilename, downloadFilename); + this._downloads.set(uuid, download); + } + downloadFilenameSuggested(uuid, suggestedFilename) { + const download = this._downloads.get(uuid); + if (!download) + return; + download.filenameSuggested(suggestedFilename); + } + downloadFinished(uuid, error) { + const download = this._downloads.get(uuid); + if (!download) + return; + download.artifact.reportFinished(error ? new Error(error) : void 0); + this._downloads.delete(uuid); + } + async startServer(progress2, title, options) { + return await progress2.race(this._server.start(title, options)); + } + async stopServer(progress2) { + await progress2.race(this._server.stop()); + } + didClose() { + for (const context2 of this.contexts()) + context2.browserClosed(); + if (this._defaultContext) + this._defaultContext.browserClosed(); + for (const download of this._downloads.values()) + download.artifact.reportFinished(new TargetClosedError(void 0)); + this.stopServer(nullProgress).catch(() => { + }); + this.emit(_Browser.Events.Disconnected); + this.instrumentation.onBrowserClose(this); + } + async close(progress2, options) { + return await progress2.race(this._close(options)); + } + async _close(options) { + if (!this._startedClosing) { + if (options.reason) + this._closeReason = options.reason; + this._startedClosing = true; + await this.options.browserProcess.close(); + } + if (this.isConnected()) + await new Promise((x) => this.once(_Browser.Events.Disconnected, x)); + } + async killForTests(progress2) { + await progress2.race(this.options.browserProcess.kill()); + } + }; + BrowserServer = class { + constructor(browser) { + this._isStarted = false; + this._browser = browser; + } + async start(title, options) { + if (this._isStarted) + throw new Error(`Server is already started.`); + this._isStarted = true; + let endpoint; + if (options.host !== void 0 || options.port !== void 0) { + this._wsServer = new PlaywrightWebSocketServer(this._browser, "/"); + endpoint = await this._wsServer.listen(options.port ?? 0, options.host, "/" + createGuid()); + } else { + this._pipeServer = new PlaywrightPipeServer(this._browser); + this._pipeSocketPath = await this._socketPath(); + await this._pipeServer.listen(this._pipeSocketPath); + endpoint = this._pipeSocketPath; + } + const browserInfo2 = { + guid: this._browser.guid, + browserName: this._browser.options.browserType, + launchOptions: asClientLaunchOptions(this._browser.options.originalLaunchOptions), + userDataDir: this._browser.options.userDataDir + }; + await serverRegistry.create(browserInfo2, { + title, + endpoint, + workspaceDir: options.workspaceDir, + metadata: options.metadata + }); + return { endpoint }; + } + async stop() { + if (!this._browser.options.userDataDir) + await serverRegistry.delete(this._browser.guid); + if (this._pipeSocketPath && process.platform !== "win32") + await import_fs34.default.promises.unlink(this._pipeSocketPath).catch(() => { + }); + await this._pipeServer?.close(); + await this._wsServer?.close(); + this._pipeServer = void 0; + this._wsServer = void 0; + this._isStarted = false; + } + async _socketPath() { + return makeSocketPath("browser", this._browser.guid.slice(0, 14)); + } + }; + } +}); + +// packages/playwright-core/src/server/dispatchers/cdpSessionDispatcher.ts +var CDPSessionDispatcher; +var init_cdpSessionDispatcher = __esm({ + "packages/playwright-core/src/server/dispatchers/cdpSessionDispatcher.ts"() { + "use strict"; + init_dispatcher(); + init_crConnection(); + CDPSessionDispatcher = class extends Dispatcher { + constructor(scope, cdpSession) { + super(scope, cdpSession, "CDPSession", {}); + this._type_CDPSession = true; + this.addObjectListener(CDPSession.Events.Event, ({ method, params: params2 }) => this._dispatchEvent("event", { method, params: params2 })); + this.addObjectListener(CDPSession.Events.Closed, () => { + this._dispatchEvent("close"); + this._dispose(); + }); + } + async send(params2, progress2) { + return { result: await this._object.send(progress2, params2.method, params2.params) }; + } + async detach(_, progress2) { + await this._object.detach(progress2); + } + }; + } +}); + +// packages/playwright-core/src/server/dispatchers/debuggerDispatcher.ts +var DebuggerDispatcher; +var init_debuggerDispatcher = __esm({ + "packages/playwright-core/src/server/dispatchers/debuggerDispatcher.ts"() { + "use strict"; + init_protocolFormatter(); + init_dispatcher(); + init_debugger(); + DebuggerDispatcher = class _DebuggerDispatcher extends Dispatcher { + constructor(scope, debugger_) { + super(scope, debugger_, "Debugger", {}); + this._type_Debugger = true; + this.addObjectListener(Debugger.Events.PausedStateChanged, () => { + this._dispatchEvent("pausedStateChanged", { pausedDetails: this._serializePausedDetails() }); + }); + this._dispatchEvent("pausedStateChanged", { pausedDetails: this._serializePausedDetails() }); + } + static from(scope, debugger_) { + const result2 = scope.connection.existingDispatcher(debugger_); + return result2 || new _DebuggerDispatcher(scope, debugger_); + } + _serializePausedDetails() { + const details = this._object.pausedDetails(); + if (!details) + return void 0; + const { metadata } = details; + return { + location: { + file: metadata.location?.file ?? "", + line: metadata.location?.line, + column: metadata.location?.column + }, + title: renderTitleForCall(metadata) + }; + } + async requestPause(params2, progress2) { + this._object.requestPause(progress2); + } + async resume(params2, progress2) { + this._object.doResume(progress2); + } + async next(params2, progress2) { + this._object.next(progress2); + } + async runTo(params2, progress2) { + this._object.runTo(progress2, params2.location); + } + }; + } +}); + +// packages/playwright-core/src/server/dispatchers/streamDispatcher.ts +var StreamSdkObject, StreamDispatcher; +var init_streamDispatcher = __esm({ + "packages/playwright-core/src/server/dispatchers/streamDispatcher.ts"() { + "use strict"; + init_manualPromise(); + init_dispatcher(); + init_instrumentation(); + StreamSdkObject = class extends SdkObject { + constructor(parent, stream3) { + super(parent, "stream"); + this.stream = stream3; + } + }; + StreamDispatcher = class extends Dispatcher { + constructor(scope, stream3) { + super(scope, new StreamSdkObject(scope._object, stream3), "Stream", {}); + this._type_Stream = true; + this._ended = false; + stream3.once("end", () => this._ended = true); + stream3.once("error", () => this._ended = true); + } + async read(params2, progress2) { + const stream3 = this._object.stream; + if (this._ended) + return { binary: Buffer.from("") }; + if (!stream3.readableLength) { + const readyPromise = new ManualPromise(); + const done = () => readyPromise.resolve(); + stream3.on("readable", done); + stream3.on("end", done); + stream3.on("error", done); + try { + await progress2.race(readyPromise); + } finally { + stream3.off("readable", done); + stream3.off("end", done); + stream3.off("error", done); + } + } + const buffer = stream3.read(Math.min(stream3.readableLength, params2.size || stream3.readableLength)); + return { binary: buffer || Buffer.from("") }; + } + _onDispose() { + this._object.stream.destroy(); + } + async close(params2, progress2) { + this._object.stream.destroy(); + } + }; + } +}); + +// packages/playwright-core/src/server/dispatchers/artifactDispatcher.ts +var import_fs35, ArtifactDispatcher; +var init_artifactDispatcher = __esm({ + "packages/playwright-core/src/server/dispatchers/artifactDispatcher.ts"() { + "use strict"; + import_fs35 = __toESM(require("fs")); + init_fileUtils(); + init_dispatcher(); + init_streamDispatcher(); + ArtifactDispatcher = class _ArtifactDispatcher extends Dispatcher { + constructor(scope, artifact) { + super(scope, artifact, "Artifact", { + absolutePath: artifact.localPath() + }); + this._type_Artifact = true; + } + static from(parentScope, artifact) { + return _ArtifactDispatcher.fromNullable(parentScope, artifact); + } + static fromNullable(parentScope, artifact) { + if (!artifact) + return void 0; + const result2 = parentScope.connection.existingDispatcher(artifact); + return result2 || new _ArtifactDispatcher(parentScope, artifact); + } + async pathAfterFinished(params2, progress2) { + const path59 = await this._object.localPathAfterFinished(progress2); + return { value: path59 }; + } + async saveAs(params2, progress2) { + return await progress2.race(new Promise((resolve, reject) => { + this._object.saveAs(progress2, async (localPath, error) => { + if (error) { + reject(error); + return; + } + try { + await mkdirIfNeeded(params2.path); + await import_fs35.default.promises.copyFile(localPath, params2.path); + resolve(); + } catch (e) { + reject(e); + } + }); + })); + } + async saveAsStream(params2, progress2) { + return await progress2.race(new Promise((resolve, reject) => { + this._object.saveAs(progress2, async (localPath, error) => { + if (error) { + reject(error); + return; + } + try { + const readable = import_fs35.default.createReadStream(localPath, { highWaterMark: 1024 * 1024 }); + const stream3 = new StreamDispatcher(this, readable); + resolve({ stream: stream3 }); + await new Promise((resolve2) => { + readable.on("close", resolve2); + readable.on("end", resolve2); + readable.on("error", resolve2); + }); + } catch (e) { + reject(e); + } + }); + })); + } + async stream(params2, progress2) { + const fileName = await this._object.localPathAfterFinished(progress2); + const readable = import_fs35.default.createReadStream(fileName, { highWaterMark: 1024 * 1024 }); + return { stream: new StreamDispatcher(this, readable) }; + } + async failure(params2, progress2) { + const error = await this._object.failureError(progress2); + return { error: error || void 0 }; + } + async cancel(params2, progress2) { + await this._object.cancel(progress2); + } + async delete(params2, progress2) { + await this._object.delete(progress2); + this._dispose(); + } + }; + } +}); + +// packages/playwright-core/src/server/dispatchers/jsHandleDispatcher.ts +function parseArgument(arg) { + return parseSerializedValue(arg.value, arg.handles.map((a) => a._object)); +} +function serializeResult(arg) { + return serializeValue(arg, (value2) => ({ fallThrough: value2 })); +} +var JSHandleDispatcher; +var init_jsHandleDispatcher = __esm({ + "packages/playwright-core/src/server/dispatchers/jsHandleDispatcher.ts"() { + "use strict"; + init_dispatcher(); + init_elementHandlerDispatcher(); + init_serializers(); + JSHandleDispatcher = class _JSHandleDispatcher extends Dispatcher { + constructor(scope, jsHandle) { + super(scope, jsHandle, jsHandle.asElement() ? "ElementHandle" : "JSHandle", { + preview: jsHandle.toString() + }); + this._type_JSHandle = true; + jsHandle._setPreviewCallback((preview) => this._dispatchEvent("previewUpdated", { preview })); + } + static fromJSHandle(scope, handle) { + return scope.connection.existingDispatcher(handle) || new _JSHandleDispatcher(scope, handle); + } + async evaluateExpression(params2, progress2) { + const jsHandle = await this._object.evaluateExpression(progress2, params2.expression, { isFunction: params2.isFunction }, parseArgument(params2.arg)); + return { value: serializeResult(jsHandle) }; + } + async evaluateExpressionHandle(params2, progress2) { + const jsHandle = await this._object.evaluateExpressionHandle(progress2, params2.expression, { isFunction: params2.isFunction }, parseArgument(params2.arg)); + return { handle: ElementHandleDispatcher.fromJSOrElementHandle(this.parentScope(), jsHandle) }; + } + async getProperty(params2, progress2) { + const jsHandle = await this._object.getProperty(progress2, params2.name); + return { handle: ElementHandleDispatcher.fromJSOrElementHandle(this.parentScope(), jsHandle) }; + } + async getPropertyList(params2, progress2) { + const map = await this._object.getProperties(progress2); + const properties = []; + for (const [name, value2] of map) { + properties.push({ name, value: ElementHandleDispatcher.fromJSOrElementHandle(this.parentScope(), value2) }); + } + return { properties }; + } + async jsonValue(params2, progress2) { + return { value: serializeResult(await this._object.jsonValue(progress2)) }; + } + async dispose(_, progress2) { + this._object.dispose(); + this._dispose(); + } + }; + } +}); + +// packages/playwright-core/src/generated/webSocketMockSource.ts +var source10; +var init_webSocketMockSource = __esm({ + "packages/playwright-core/src/generated/webSocketMockSource.ts"() { + "use strict"; + source10 = ` +var __commonJS = obj => { + let required = false; + let result; + return function __require() { + if (!required) { + required = true; + let fn; + for (const name in obj) { fn = obj[name]; break; } + const module = { exports: {} }; + fn(module.exports, module); + result = module.exports; + } + return result; + } +}; +var __export = (target, all) => {for (var name in all) target[name] = all[name];}; +var __toESM = mod => ({ ...mod, 'default': mod }); +var __toCommonJS = mod => ({ ...mod, __esModule: true }); + + +// packages/injected/src/webSocketMock.ts +var webSocketMock_exports = {}; +__export(webSocketMock_exports, { + inject: () => inject +}); +module.exports = __toCommonJS(webSocketMock_exports); +function inject(globalThis) { + if (globalThis.__pwWebSocketDispatch) + return; + function generateId() { + const bytes = new Uint8Array(32); + globalThis.crypto.getRandomValues(bytes); + const hex = "0123456789abcdef"; + return [...bytes].map((value) => { + const high = Math.floor(value / 16); + const low = value % 16; + return hex[high] + hex[low]; + }).join(""); + } + function bufferToData(b) { + let s = ""; + for (let i = 0; i < b.length; i++) + s += String.fromCharCode(b[i]); + return { data: globalThis.btoa(s), isBase64: true }; + } + function stringToBuffer(s) { + s = globalThis.atob(s); + const b = new Uint8Array(s.length); + for (let i = 0; i < s.length; i++) + b[i] = s.charCodeAt(i); + return b.buffer; + } + function messageToData(message, cb) { + if (message instanceof globalThis.Blob) + return message.arrayBuffer().then((buffer) => cb(bufferToData(new Uint8Array(buffer)))); + if (typeof message === "string") + return cb({ data: message, isBase64: false }); + if (ArrayBuffer.isView(message)) + return cb(bufferToData(new Uint8Array(message.buffer, message.byteOffset, message.byteLength))); + return cb(bufferToData(new Uint8Array(message))); + } + function dataToMessage(data, binaryType) { + if (!data.isBase64) + return data.data; + const buffer = stringToBuffer(data.data); + return binaryType === "arraybuffer" ? buffer : new Blob([buffer]); + } + const binding = globalThis.__pwWebSocketBinding; + const NativeWebSocket = globalThis.WebSocket; + const idToWebSocket = /* @__PURE__ */ new Map(); + globalThis.__pwWebSocketDispatch = (request) => { + const ws = idToWebSocket.get(request.id); + if (!ws) + return; + if (request.type === "connect") + ws._apiConnect(); + if (request.type === "passthrough") + ws._apiPassThrough(); + if (request.type === "ensureOpened") + ws._apiEnsureOpened(); + if (request.type === "sendToPage") + ws._apiSendToPage(dataToMessage(request.data, ws.binaryType)); + if (request.type === "closePage") + ws._apiClosePage(request.code, request.reason, request.wasClean); + if (request.type === "sendToServer") + ws._apiSendToServer(dataToMessage(request.data, ws.binaryType)); + if (request.type === "closeServer") + ws._apiCloseServer(request.code, request.reason, request.wasClean); + }; + const _WebSocketMock = class _WebSocketMock extends EventTarget { + constructor(url, protocols) { + var _a, _b; + super(); + // WebSocket.CLOSED + this.CONNECTING = 0; + // WebSocket.CONNECTING + this.OPEN = 1; + // WebSocket.OPEN + this.CLOSING = 2; + // WebSocket.CLOSING + this.CLOSED = 3; + // WebSocket.CLOSED + this._oncloseListener = null; + this._onerrorListener = null; + this._onmessageListener = null; + this._onopenListener = null; + this.bufferedAmount = 0; + this.extensions = ""; + this.protocol = ""; + this.readyState = 0; + this._origin = ""; + this._passthrough = false; + this._wsBufferedMessages = []; + this._binaryType = "blob"; + this.url = new URL(url, globalThis.window.document.baseURI).href.replace(/^http/, "ws"); + this._origin = (_b = (_a = URL.parse(this.url)) == null ? void 0 : _a.origin) != null ? _b : ""; + this._protocols = protocols; + this._id = generateId(); + idToWebSocket.set(this._id, this); + const protocolsList = Array.isArray(protocols) ? [...protocols] : protocols ? [protocols] : []; + binding({ type: "onCreate", id: this._id, url: this.url, protocols: protocolsList }); + } + // --- native WebSocket implementation --- + get binaryType() { + return this._binaryType; + } + set binaryType(type) { + this._binaryType = type; + if (this._ws) + this._ws.binaryType = type; + } + get onclose() { + return this._oncloseListener; + } + set onclose(listener) { + if (this._oncloseListener) + this.removeEventListener("close", this._oncloseListener); + this._oncloseListener = listener; + if (this._oncloseListener) + this.addEventListener("close", this._oncloseListener); + } + get onerror() { + return this._onerrorListener; + } + set onerror(listener) { + if (this._onerrorListener) + this.removeEventListener("error", this._onerrorListener); + this._onerrorListener = listener; + if (this._onerrorListener) + this.addEventListener("error", this._onerrorListener); + } + get onopen() { + return this._onopenListener; + } + set onopen(listener) { + if (this._onopenListener) + this.removeEventListener("open", this._onopenListener); + this._onopenListener = listener; + if (this._onopenListener) + this.addEventListener("open", this._onopenListener); + } + get onmessage() { + return this._onmessageListener; + } + set onmessage(listener) { + if (this._onmessageListener) + this.removeEventListener("message", this._onmessageListener); + this._onmessageListener = listener; + if (this._onmessageListener) + this.addEventListener("message", this._onmessageListener); + } + send(message) { + if (this.readyState === _WebSocketMock.CONNECTING) + throw new DOMException(\`Failed to execute 'send' on 'WebSocket': Still in CONNECTING state.\`); + if (this.readyState !== _WebSocketMock.OPEN) + throw new DOMException(\`WebSocket is already in CLOSING or CLOSED state.\`); + if (this._passthrough) { + if (this._ws) + this._apiSendToServer(message); + } else { + messageToData(message, (data) => binding({ type: "onMessageFromPage", id: this._id, data })); + } + } + close(code, reason) { + if (code !== void 0 && code !== 1e3 && (code < 3e3 || code > 4999)) + throw new DOMException(\`Failed to execute 'close' on 'WebSocket': The close code must be either 1000, or between 3000 and 4999. \${code} is neither.\`); + if (this.readyState === _WebSocketMock.OPEN || this.readyState === _WebSocketMock.CONNECTING) + this.readyState = _WebSocketMock.CLOSING; + if (this._passthrough) + this._apiCloseServer(code, reason, true); + else + binding({ type: "onClosePage", id: this._id, code, reason, wasClean: true }); + } + // --- methods called from the routing API --- + _apiEnsureOpened() { + if (!this._ws) + this._ensureOpened(); + } + _apiSendToPage(message) { + this._ensureOpened(); + if (this.readyState !== _WebSocketMock.OPEN) + throw new DOMException(\`WebSocket is already in CLOSING or CLOSED state.\`); + this.dispatchEvent(new MessageEvent("message", { data: message, origin: this._origin, cancelable: true })); + } + _apiSendToServer(message) { + if (!this._ws) + throw new Error("Cannot send a message before connecting to the server"); + if (this._ws.readyState === _WebSocketMock.CONNECTING) + this._wsBufferedMessages.push(message); + else + this._ws.send(message); + } + _apiConnect() { + if (this._ws) + throw new Error("Can only connect to the server once"); + this._ws = new NativeWebSocket(this.url, this._protocols); + this._ws.binaryType = this._binaryType; + this._ws.onopen = () => { + for (const message of this._wsBufferedMessages) + this._ws.send(message); + this._wsBufferedMessages = []; + this._ensureOpened(); + }; + this._ws.onclose = (event) => { + this._onWSClose(event.code, event.reason, event.wasClean); + }; + this._ws.onmessage = (event) => { + if (this._passthrough) + this._apiSendToPage(event.data); + else + messageToData(event.data, (data) => binding({ type: "onMessageFromServer", id: this._id, data })); + }; + this._ws.onerror = () => { + const event = new Event("error", { cancelable: true }); + this.dispatchEvent(event); + }; + } + // This method connects to the server, and passes all messages through, + // as if WebSocketMock was not engaged. + _apiPassThrough() { + this._passthrough = true; + this._apiConnect(); + } + _apiCloseServer(code, reason, wasClean) { + if (!this._ws) { + this._onWSClose(code, reason, wasClean); + return; + } + if (this._ws.readyState === _WebSocketMock.CONNECTING || this._ws.readyState === _WebSocketMock.OPEN) + this._ws.close(code, reason); + } + _apiClosePage(code, reason, wasClean) { + if (this.readyState === _WebSocketMock.CLOSED) + return; + this.readyState = _WebSocketMock.CLOSED; + this.dispatchEvent(new CloseEvent("close", { code, reason, wasClean, cancelable: true })); + this._maybeCleanup(); + if (this._passthrough) + this._apiCloseServer(code, reason, wasClean); + else + binding({ type: "onClosePage", id: this._id, code, reason, wasClean }); + } + // --- internals --- + _ensureOpened() { + var _a; + if (this.readyState !== _WebSocketMock.CONNECTING) + return; + this.extensions = ((_a = this._ws) == null ? void 0 : _a.extensions) || ""; + if (this._ws) + this.protocol = this._ws.protocol; + else if (Array.isArray(this._protocols)) + this.protocol = this._protocols[0] || ""; + else + this.protocol = this._protocols || ""; + this.readyState = _WebSocketMock.OPEN; + this.dispatchEvent(new Event("open", { cancelable: true })); + } + _onWSClose(code, reason, wasClean) { + if (this._passthrough) + this._apiClosePage(code, reason, wasClean); + else + binding({ type: "onCloseServer", id: this._id, code, reason, wasClean }); + if (this._ws) { + this._ws.onopen = null; + this._ws.onclose = null; + this._ws.onmessage = null; + this._ws.onerror = null; + this._ws = void 0; + this._wsBufferedMessages = []; + } + this._maybeCleanup(); + } + _maybeCleanup() { + if (this.readyState === _WebSocketMock.CLOSED && !this._ws) + idToWebSocket.delete(this._id); + } + }; + _WebSocketMock.CONNECTING = 0; + // WebSocket.CONNECTING + _WebSocketMock.OPEN = 1; + // WebSocket.OPEN + _WebSocketMock.CLOSING = 2; + // WebSocket.CLOSING + _WebSocketMock.CLOSED = 3; + let WebSocketMock = _WebSocketMock; + globalThis.WebSocket = class WebSocket extends WebSocketMock { + }; +} +`; + } +}); + +// packages/playwright-core/src/server/dispatchers/webSocketRouteDispatcher.ts +function matchesPattern(dispatcher, baseURL, url2) { + for (const pattern of dispatcher._webSocketInterceptionPatterns || []) { + if (urlMatches(baseURL, url2, deserializeURLMatch(pattern), true)) + return true; + } + return false; +} +var WebSocketRouteDispatcher, kBindingName3; +var init_webSocketRouteDispatcher = __esm({ + "packages/playwright-core/src/server/dispatchers/webSocketRouteDispatcher.ts"() { + "use strict"; + init_urlMatch(); + init_eventsHelper(); + init_page(); + init_dispatcher(); + init_pageDispatcher(); + init_webSocketMockSource(); + init_instrumentation(); + WebSocketRouteDispatcher = class _WebSocketRouteDispatcher extends Dispatcher { + constructor(scope, id, url2, protocols, frame) { + super(scope, new SdkObject(scope._object, "webSocketRoute"), "WebSocketRoute", { url: url2, protocols }); + this._type_WebSocketRoute = true; + this._id = id; + this._frame = frame; + this._eventListeners.push( + // When the frame navigates or detaches, there will be no more communication + // from the mock websocket, so pretend like it was closed. + eventsHelper.addEventListener(frame._page, Page.Events.InternalFrameNavigatedToNewDocument, (frame2) => { + if (frame2 === this._frame) + this._executionContextGone(); + }), + eventsHelper.addEventListener(frame._page, Page.Events.FrameDetached, (frame2) => { + if (frame2 === this._frame) + this._executionContextGone(); + }), + eventsHelper.addEventListener(frame._page, Page.Events.Close, () => this._executionContextGone()), + eventsHelper.addEventListener(frame._page, Page.Events.Crash, () => this._executionContextGone()) + ); + _WebSocketRouteDispatcher._idToDispatcher.set(this._id, this); + scope._dispatchEvent("webSocketRoute", { webSocketRoute: this }); + } + static { + this._idToDispatcher = /* @__PURE__ */ new Map(); + } + static async install(progress2, connection, target) { + const context2 = target instanceof Page ? target.browserContext : target; + let data = context2.getBindingClient(kBindingName3); + if (data && data.connection !== connection) + throw new Error("Another client is already routing WebSockets"); + if (!data) { + data = { counter: 0, connection, binding: null }; + data.binding = await context2.exposeBinding(progress2, kBindingName3, (source11, payload) => { + if (payload.type === "onCreate") { + const contextDispatcher = connection.existingDispatcher(context2); + const pageDispatcher = contextDispatcher ? PageDispatcher.fromNullable(contextDispatcher, source11.page) : void 0; + let scope; + if (pageDispatcher && matchesPattern(pageDispatcher, context2._options.baseURL, payload.url)) + scope = pageDispatcher; + else if (contextDispatcher && matchesPattern(contextDispatcher, context2._options.baseURL, payload.url)) + scope = contextDispatcher; + if (scope) { + new _WebSocketRouteDispatcher(scope, payload.id, payload.url, payload.protocols, source11.frame); + } else { + const request2 = { id: payload.id, type: "passthrough" }; + source11.frame.evaluateExpression(progress2, `globalThis.__pwWebSocketDispatch(${JSON.stringify(request2)})`).catch(() => { + }); + } + return; + } + const dispatcher = _WebSocketRouteDispatcher._idToDispatcher.get(payload.id); + if (payload.type === "onMessageFromPage") + dispatcher?._dispatchEvent("messageFromPage", { message: payload.data.data, isBase64: payload.data.isBase64 }); + if (payload.type === "onMessageFromServer") + dispatcher?._dispatchEvent("messageFromServer", { message: payload.data.data, isBase64: payload.data.isBase64 }); + if (payload.type === "onClosePage") + dispatcher?._dispatchEvent("closePage", { code: payload.code, reason: payload.reason, wasClean: payload.wasClean }); + if (payload.type === "onCloseServer") + dispatcher?._dispatchEvent("closeServer", { code: payload.code, reason: payload.reason, wasClean: payload.wasClean }); + }, data); + } + ++data.counter; + return await target.addInitScript(progress2, ` + (() => { + const module = {}; + ${source10} + (module.exports.inject())(globalThis); + })(); + `); + } + static async uninstall(connection, target, initScript) { + const context2 = target instanceof Page ? target.browserContext : target; + const data = context2.getBindingClient(kBindingName3); + if (!data || data.connection !== connection) + return; + if (--data.counter <= 0) + await data.binding.dispose(); + await initScript.dispose(); + } + async connect(params2, progress2) { + await this._evaluateAPIRequest(progress2, { id: this._id, type: "connect" }); + } + async ensureOpened(params2, progress2) { + await this._evaluateAPIRequest(progress2, { id: this._id, type: "ensureOpened" }); + } + async sendToPage(params2, progress2) { + await this._evaluateAPIRequest(progress2, { id: this._id, type: "sendToPage", data: { data: params2.message, isBase64: params2.isBase64 } }); + } + async sendToServer(params2, progress2) { + await this._evaluateAPIRequest(progress2, { id: this._id, type: "sendToServer", data: { data: params2.message, isBase64: params2.isBase64 } }); + } + async closePage(params2, progress2) { + await this._evaluateAPIRequest(progress2, { id: this._id, type: "closePage", code: params2.code, reason: params2.reason, wasClean: params2.wasClean }); + } + async closeServer(params2, progress2) { + await this._evaluateAPIRequest(progress2, { id: this._id, type: "closeServer", code: params2.code, reason: params2.reason, wasClean: params2.wasClean }); + } + async _evaluateAPIRequest(progress2, request2) { + await this._frame.evaluateExpression(progress2, `globalThis.__pwWebSocketDispatch(${JSON.stringify(request2)})`).catch(() => { + }); + } + _onDispose() { + _WebSocketRouteDispatcher._idToDispatcher.delete(this._id); + } + _executionContextGone() { + if (!this._disposed) { + this._dispatchEvent("closePage", { wasClean: true }); + this._dispatchEvent("closeServer", { wasClean: true }); + } + } + }; + kBindingName3 = "__pwWebSocketBinding"; + } +}); + +// packages/playwright-core/src/server/dispatchers/disposableDispatcher.ts +var DisposableDispatcher; +var init_disposableDispatcher = __esm({ + "packages/playwright-core/src/server/dispatchers/disposableDispatcher.ts"() { + "use strict"; + init_dispatcher(); + DisposableDispatcher = class extends Dispatcher { + constructor(scope, disposable) { + super(scope, disposable, "Disposable", {}); + this._type_Disposable = true; + } + async dispose(_, progress2) { + await progress2.race(this._object.dispose()); + this._dispose(); + } + }; + } +}); + +// packages/playwright-core/src/server/dispatchers/pageDispatcher.ts +function createVideoDispatcher(parentScope, video) { + return ArtifactDispatcher.from(parentScope.parentScope(), video); +} +var PageDispatcher, WorkerDispatcher, BindingCallDispatcher; +var init_pageDispatcher = __esm({ + "packages/playwright-core/src/server/dispatchers/pageDispatcher.ts"() { + "use strict"; + init_protocolFormatter(); + init_urlMatch(); + init_page(); + init_dispatcher(); + init_errors(); + init_artifactDispatcher(); + init_elementHandlerDispatcher(); + init_frameDispatcher(); + init_jsHandleDispatcher(); + init_networkDispatchers(); + init_networkDispatchers(); + init_networkDispatchers(); + init_webSocketRouteDispatcher(); + init_disposableDispatcher(); + init_instrumentation(); + init_recorder(); + init_disposable2(); + init_videoRecorder(); + init_progress(); + PageDispatcher = class _PageDispatcher extends Dispatcher { + constructor(parentScope, page) { + const mainFrame = FrameDispatcher.from(parentScope, page.mainFrame()); + super(parentScope, page, "Page", { + mainFrame, + viewportSize: page.emulatedSize()?.viewport, + isClosed: page.isClosed(), + opener: _PageDispatcher.fromNullable(parentScope, page.opener()), + video: page.video ? createVideoDispatcher(parentScope, page.video) : void 0 + }); + this._type_Page = true; + this._subscriptions = /* @__PURE__ */ new Set(); + this._webSocketInterceptionPatterns = []; + this._disposables = []; + this._interceptionUrlMatchers = []; + this._locatorHandlers = /* @__PURE__ */ new Set(); + this._jsCoverageActive = false; + this._cssCoverageActive = false; + this.adopt(mainFrame); + this._page = page; + this._requestInterceptor = (route2, request2) => { + const matchesSome = this._interceptionUrlMatchers.some((urlMatch) => urlMatches(this._page.browserContext._options.baseURL, request2.url(), urlMatch)); + if (!matchesSome) { + route2.continue({ isFallback: true }).catch(() => { + }); + return; + } + this._dispatchEvent("route", { route: new RouteDispatcher(RequestDispatcher.from(this.parentScope(), request2), route2) }); + }; + this.addObjectListener(Page.Events.Close, () => { + this._dispatchEvent("close"); + this._dispose(); + }); + this.addObjectListener(Page.Events.Crash, () => this._dispatchEvent("crash")); + this.addObjectListener(Page.Events.Download, (download) => { + this._dispatchEvent("download", { url: download.url, suggestedFilename: download.suggestedFilename(), artifact: ArtifactDispatcher.from(parentScope, download.artifact) }); + }); + this.addObjectListener(Page.Events.EmulatedSizeChanged, () => this._dispatchEvent("viewportSizeChanged", { viewportSize: page.emulatedSize()?.viewport })); + this.addObjectListener(Page.Events.FileChooser, (fileChooser) => this._dispatchEvent("fileChooser", { + element: ElementHandleDispatcher.from(mainFrame, fileChooser.element()), + isMultiple: fileChooser.isMultiple() + })); + this.addObjectListener(Page.Events.FrameAttached, (frame) => this._onFrameAttached(frame)); + this.addObjectListener(Page.Events.FrameDetached, (frame) => this._onFrameDetached(frame)); + this.addObjectListener(Page.Events.LocatorHandlerTriggered, (uid) => this._dispatchEvent("locatorHandlerTriggered", { uid })); + this.addObjectListener(Page.Events.WebSocket, (webSocket) => this._dispatchEvent("webSocket", { webSocket: new WebSocketDispatcher(this, webSocket) })); + this.addObjectListener(Page.Events.Worker, (worker) => this._dispatchEvent("worker", { worker: new WorkerDispatcher(this, worker) })); + const frames = page.frameManager.frames(); + for (let i = 1; i < frames.length; i++) + this._onFrameAttached(frames[i]); + } + static from(parentScope, page) { + return _PageDispatcher.fromNullable(parentScope, page); + } + static fromNullable(parentScope, page) { + if (!page) + return void 0; + const result2 = parentScope.connection.existingDispatcher(page); + return result2 || new _PageDispatcher(parentScope, page); + } + page() { + return this._page; + } + async exposeBinding(params2, progress2) { + const binding = await this._page.exposeBinding(progress2, params2.name, (source11, ...args) => { + if (this._disposed) + return; + const binding2 = new BindingCallDispatcher(this, params2.name, source11, args); + this._dispatchEvent("bindingCall", { binding: binding2 }); + return binding2.promise(); + }); + this._disposables.push(binding); + return { disposable: new DisposableDispatcher(this, binding) }; + } + async setExtraHTTPHeaders(params2, progress2) { + await this._page.setExtraHTTPHeaders(progress2, params2.headers); + } + async reload(params2, progress2) { + return { response: ResponseDispatcher.fromNullable(this.parentScope(), await this._page.reload(progress2, params2)) }; + } + async goBack(params2, progress2) { + return { response: ResponseDispatcher.fromNullable(this.parentScope(), await this._page.goBack(progress2, params2)) }; + } + async goForward(params2, progress2) { + return { response: ResponseDispatcher.fromNullable(this.parentScope(), await this._page.goForward(progress2, params2)) }; + } + async requestGC(params2, progress2) { + await this._page.requestGC(progress2); + } + async registerLocatorHandler(params2, progress2) { + const uid = this._page.registerLocatorHandler(params2.selector, params2.noWaitAfter); + this._locatorHandlers.add(uid); + return { uid }; + } + async resolveLocatorHandlerNoReply(params2, progress2) { + this._page.resolveLocatorHandler(params2.uid, params2.remove); + } + async unregisterLocatorHandler(params2, progress2) { + this._page.unregisterLocatorHandler(params2.uid); + this._locatorHandlers.delete(params2.uid); + } + async emulateMedia(params2, progress2) { + await this._page.emulateMedia(progress2, { + media: params2.media, + colorScheme: params2.colorScheme, + reducedMotion: params2.reducedMotion, + forcedColors: params2.forcedColors, + contrast: params2.contrast + }); + } + async setViewportSize(params2, progress2) { + await this._page.setViewportSize(progress2, params2.viewportSize); + } + async addInitScript(params2, progress2) { + const initScript = await this._page.addInitScript(progress2, params2.source); + this._disposables.push(initScript); + return { disposable: new DisposableDispatcher(this, initScript) }; + } + async setNetworkInterceptionPatterns(params2, progress2) { + const hadMatchers = this._interceptionUrlMatchers.length > 0; + if (!params2.patterns.length) { + if (hadMatchers) + await progress2.race(this._page.removeRequestInterceptor(this._requestInterceptor)); + this._interceptionUrlMatchers = []; + } else { + this._interceptionUrlMatchers = params2.patterns.map(deserializeURLMatch); + if (!hadMatchers) + await this._page.addRequestInterceptor(progress2, this._requestInterceptor); + } + } + async setWebSocketInterceptionPatterns(params2, progress2) { + this._webSocketInterceptionPatterns = params2.patterns; + if (params2.patterns.length && !this._routeWebSocketInitScript) + this._routeWebSocketInitScript = await WebSocketRouteDispatcher.install(progress2, this.connection, this._page); + } + async expectScreenshot(params2, progress2) { + const mask = (params2.mask || []).map(({ frame, selector }) => ({ + frame: frame._object, + selector + })); + const locator2 = params2.locator ? { + frame: params2.locator.frame._object, + selector: params2.locator.selector + } : void 0; + progress2.log(`${renderTitleForCall(progress2.metadata)}${params2.timeout ? ` with timeout ${params2.timeout}ms` : ""}`); + return await this._page.expectScreenshot(progress2, { + ...params2, + locator: locator2, + mask + }); + } + async screenshot(params2, progress2) { + const mask = (params2.mask || []).map(({ frame, selector }) => ({ + frame: frame._object, + selector + })); + return { binary: await this._page.screenshot(progress2, { ...params2, mask }) }; + } + async close(params2, progress2) { + await this._page.close(progress2, params2); + } + async runBeforeUnload(params2, progress2) { + await this._page.runBeforeUnload(progress2); + } + async updateSubscription(params2, progress2) { + if (params2.event === "fileChooser") + await this._page.setFileChooserInterceptedBy(progress2, params2.enabled, this); + if (params2.enabled) + this._subscriptions.add(params2.event); + else + this._subscriptions.delete(params2.event); + } + async keyboardDown(params2, progress2) { + await this._page.keyboard.apiDown(progress2, params2.key); + } + async keyboardUp(params2, progress2) { + await this._page.keyboard.apiUp(progress2, params2.key); + } + async keyboardInsertText(params2, progress2) { + await this._page.keyboard.apiInsertText(progress2, params2.text); + } + async keyboardType(params2, progress2) { + await this._page.keyboard.apiType(progress2, params2.text, params2); + } + async keyboardPress(params2, progress2) { + await this._page.keyboard.apiPress(progress2, params2.key, params2); + } + async clearConsoleMessages(params2, progress2) { + this._page.clearConsoleMessages(); + } + async consoleMessages(params2, progress2) { + this._subscriptions.add("console"); + return { messages: this._page.consoleMessages(params2.filter).map((message) => this.parentScope().serializeConsoleMessage(message, this)) }; + } + async clearPageErrors(params2, progress2) { + this._page.clearPageErrors(); + } + async pageErrors(params2, progress2) { + return { errors: this._page.pageErrors(params2.filter).map((error) => serializeError(error)) }; + } + async mouseMove(params2, progress2) { + await this._page.mouse.apiMove(progress2, params2.x, params2.y, params2); + } + async mouseDown(params2, progress2) { + await this._page.mouse.apiDown(progress2, params2); + } + async mouseUp(params2, progress2) { + await this._page.mouse.apiUp(progress2, params2); + } + async mouseClick(params2, progress2) { + await this._page.mouse.apiClick(progress2, params2.x, params2.y, params2); + } + async mouseWheel(params2, progress2) { + await this._page.mouse.apiWheel(progress2, params2.deltaX, params2.deltaY); + } + async touchscreenTap(params2, progress2) { + await this._page.touchscreen.apiTap(progress2, params2.x, params2.y); + } + async pdf(params2, progress2) { + if (!this._page.pdf) + throw new Error("PDF generation is only supported for Headless Chromium"); + const buffer = await progress2.race(this._page.pdf(params2)); + return { pdf: buffer }; + } + async requests(params2, progress2) { + this._subscriptions.add("request"); + return { requests: this._page.networkRequests().map((request2) => RequestDispatcher.from(this.parentScope(), request2)) }; + } + async bringToFront(params2, progress2) { + await this._page.bringToFront(progress2); + } + async pickLocator(params2, progress2) { + const recorder = await progress2.race(Recorder.forContext(this._page.browserContext, { omitCallTracking: true, hideToolbar: true })); + const selector = await recorder.pickLocator(progress2, this._page); + return { selector }; + } + async cancelPickLocator(params2, progress2) { + const recorder = await progress2.race(Recorder.existingForContext(this._page.browserContext)); + if (recorder) + await progress2.race(recorder.setMode("none")); + } + async hideHighlight(params2, progress2) { + await progress2.race(this._page.hideHighlight()); + } + async screencastShowOverlay(params2) { + const id = await this._page.overlay.show(params2.html, params2.duration); + return { id }; + } + async screencastRemoveOverlay(params2) { + await this._page.overlay.remove(params2.id); + } + async screencastChapter(params2) { + await this._page.overlay.chapter(params2); + } + async screencastSetOverlayVisible(params2) { + await this._page.overlay.setVisible(params2.visible); + } + async screencastShowActions(params2) { + this._page.screencast.showActions({ duration: params2.duration, position: params2.position, fontSize: params2.fontSize, cursor: params2.cursor }); + } + async screencastHideActions() { + this._page.screencast.hideActions(); + } + async screencastStart(params2, progress2) { + if (this._screencastClient || this._videoRecorder) + throw new Error("Screencast is already running"); + if (params2.sendFrames) { + this._screencastClient = { + onFrame: (frame) => { + this._dispatchEvent("screencastFrame", { data: frame.buffer, timestamp: frame.frameSwapWallTime, viewportWidth: frame.viewportWidth, viewportHeight: frame.viewportHeight }); + }, + dispose: () => { + }, + size: params2.size, + quality: params2.quality + }; + this._page.screencast.addClient(this._screencastClient); + } + let artifact; + if (params2.record) { + this._videoRecorder = new VideoRecorder(this._page.screencast); + artifact = this._videoRecorder.start(params2); + } + return { artifact: artifact ? createVideoDispatcher(this.parentScope(), artifact) : void 0 }; + } + async screencastStop(params2, progress2) { + if (this._videoRecorder) { + await this._videoRecorder.stop(); + this._videoRecorder = void 0; + } + const client = this._screencastClient; + this._screencastClient = void 0; + if (client) + this._page.screencast.removeClient(client); + } + async startJSCoverage(params2, progress2) { + const coverage = this._page.coverage; + await coverage.startJSCoverage(progress2, params2); + this._jsCoverageActive = true; + } + async stopJSCoverage(params2, progress2) { + this._jsCoverageActive = false; + const coverage = this._page.coverage; + return await progress2.race(coverage.stopJSCoverage()); + } + async startCSSCoverage(params2, progress2) { + const coverage = this._page.coverage; + await coverage.startCSSCoverage(progress2, params2); + this._cssCoverageActive = true; + } + async stopCSSCoverage(params2, progress2) { + this._cssCoverageActive = false; + const coverage = this._page.coverage; + return await progress2.race(coverage.stopCSSCoverage()); + } + _onFrameAttached(frame) { + this._dispatchEvent("frameAttached", { frame: FrameDispatcher.from(this.parentScope(), frame) }); + } + _onFrameDetached(frame) { + this._dispatchEvent("frameDetached", { frame: FrameDispatcher.from(this.parentScope(), frame) }); + } + _onDispose() { + if (this._page.isClosedOrClosingOrCrashed()) + return; + this._interceptionUrlMatchers = []; + this._page.removeRequestInterceptor(this._requestInterceptor).catch(() => { + }); + disposeAll2(this._disposables).catch(() => { + }); + if (this._routeWebSocketInitScript) + WebSocketRouteDispatcher.uninstall(this.connection, this._page, this._routeWebSocketInitScript).catch(() => { + }); + this._routeWebSocketInitScript = void 0; + for (const uid of this._locatorHandlers) + this._page.unregisterLocatorHandler(uid); + this._locatorHandlers.clear(); + this._page.setFileChooserInterceptedBy(nullProgress, false, this).catch(() => { + }); + if (this._jsCoverageActive) + this._page.coverage.stopJSCoverage().catch(() => { + }); + this._jsCoverageActive = false; + if (this._cssCoverageActive) + this._page.coverage.stopCSSCoverage().catch(() => { + }); + this._cssCoverageActive = false; + this.screencastStop({}, void 0).catch(() => { + }); + } + async setDockTile(params2) { + await this._page.setDockTile(params2.image); + } + async webStorageItems(params2, progress2) { + return { items: await this._page.webStorageItems(progress2, params2.kind) }; + } + async webStorageGetItem(params2, progress2) { + return { value: await this._page.webStorageGetItem(progress2, params2.kind, params2.name) }; + } + async webStorageSetItem(params2, progress2) { + await this._page.webStorageSetItem(progress2, params2.kind, params2.name, params2.value); + } + async webStorageRemoveItem(params2, progress2) { + await this._page.webStorageRemoveItem(progress2, params2.kind, params2.name); + } + async webStorageClear(params2, progress2) { + await this._page.webStorageClear(progress2, params2.kind); + } + }; + WorkerDispatcher = class _WorkerDispatcher extends Dispatcher { + constructor(scope, worker) { + super(scope, worker, "Worker", { + url: worker.url + }); + this._type_Worker = true; + this._subscriptions = /* @__PURE__ */ new Set(); + this.addObjectListener(Worker.Events.Console, (message) => { + if (!this._subscriptions.has("console")) + return; + this._dispatchEvent("console", { + type: message.type(), + text: message.text(), + args: message.args().map((a) => JSHandleDispatcher.fromJSHandle(this, a)), + location: message.location(), + timestamp: message.timestamp() + }); + }); + this.addObjectListener(Worker.Events.Close, () => this._dispatchEvent("close")); + } + static fromNullable(scope, worker) { + if (!worker) + return void 0; + const result2 = scope.connection.existingDispatcher(worker); + return result2 || new _WorkerDispatcher(scope, worker); + } + async disconnect(params2, progress2) { + await this._object.disconnect(progress2, params2); + } + async evaluateExpression(params2, progress2) { + return { value: serializeResult(await this._object.evaluateExpression(progress2, params2.expression, params2.isFunction, parseArgument(params2.arg))) }; + } + async evaluateExpressionHandle(params2, progress2) { + return { handle: JSHandleDispatcher.fromJSHandle(this, await this._object.evaluateExpressionHandle(progress2, params2.expression, params2.isFunction, parseArgument(params2.arg))) }; + } + async updateSubscription(params2, progress2) { + if (params2.enabled) + this._subscriptions.add(params2.event); + else + this._subscriptions.delete(params2.event); + } + }; + BindingCallDispatcher = class extends Dispatcher { + constructor(scope, name, source11, args) { + const frameDispatcher = FrameDispatcher.from(scope.parentScope(), source11.frame); + super(scope, new SdkObject(scope._object, "bindingCall"), "BindingCall", { + frame: frameDispatcher, + name, + args: args.map(serializeResult) + }); + this._type_BindingCall = true; + this._promise = new Promise((resolve, reject) => { + this._resolve = resolve; + this._reject = reject; + }); + } + promise() { + return this._promise; + } + async resolve(params2, progress2) { + this._resolve(parseArgument(params2.result)); + this._dispose(); + } + async reject(params2, progress2) { + this._reject(parseError(params2.error)); + this._dispose(); + } + }; + } +}); + +// packages/playwright-core/src/server/dispatchers/dialogDispatcher.ts +var DialogDispatcher; +var init_dialogDispatcher = __esm({ + "packages/playwright-core/src/server/dispatchers/dialogDispatcher.ts"() { + "use strict"; + init_dispatcher(); + init_pageDispatcher(); + DialogDispatcher = class extends Dispatcher { + constructor(scope, dialog) { + const page = PageDispatcher.fromNullable(scope, dialog.page().initializedOrUndefined()); + super(page || scope, dialog, "Dialog", { + page, + type: dialog.type(), + message: dialog.message(), + defaultValue: dialog.defaultValue() + }); + this._type_Dialog = true; + } + async accept(params2, progress2) { + await this._object.accept(progress2, params2.promptText); + } + async dismiss(params2, progress2) { + await this._object.dismiss(progress2); + } + }; + } +}); + +// packages/playwright-core/src/server/dispatchers/tracingDispatcher.ts +var TracingDispatcher; +var init_tracingDispatcher = __esm({ + "packages/playwright-core/src/server/dispatchers/tracingDispatcher.ts"() { + "use strict"; + init_artifactDispatcher(); + init_dispatcher(); + init_progress(); + TracingDispatcher = class _TracingDispatcher extends Dispatcher { + constructor(scope, tracing) { + super(scope, tracing, "Tracing", {}); + this._type_Tracing = true; + this._started = false; + } + static from(scope, tracing) { + const result2 = scope.connection.existingDispatcher(tracing); + return result2 || new _TracingDispatcher(scope, tracing); + } + async tracingStart(params2, progress2) { + this._object.start(progress2, params2); + this._started = true; + } + async tracingStartChunk(params2, progress2) { + return await this._object.startChunk(progress2, params2); + } + async tracingGroup(params2, progress2) { + const { name, location: location2 } = params2; + this._object.group(progress2, name, location2); + } + async tracingGroupEnd(params2, progress2) { + this._object.groupEnd(progress2); + } + async tracingStopChunk(params2, progress2) { + const { artifact, entries } = await this._object.stopChunk(progress2, params2); + return { artifact: artifact ? ArtifactDispatcher.from(this, artifact) : void 0, entries }; + } + async tracingStop(params2, progress2) { + await this._object.stop(progress2); + } + async harStart(params2, progress2) { + const harId = this._object.harStart(params2.page ? params2.page._object : null, params2.options); + return { harId }; + } + async harExport(params2, progress2) { + const { artifact, entries } = await this._object.harExport(progress2, params2.harId, params2.mode); + return { + artifact: artifact ? ArtifactDispatcher.from(this, artifact) : void 0, + entries + }; + } + _onDispose() { + if (this._started) + this._object.stopChunk(nullProgress, { mode: "discard" }).then(() => this._object.stop(nullProgress)).catch(() => { + }); + this._started = false; + } + }; + } +}); + +// packages/playwright-core/src/server/dispatchers/writableStreamDispatcher.ts +var import_fs36, WritableStreamSdkObject, WritableStreamDispatcher; +var init_writableStreamDispatcher = __esm({ + "packages/playwright-core/src/server/dispatchers/writableStreamDispatcher.ts"() { + "use strict"; + import_fs36 = __toESM(require("fs")); + init_dispatcher(); + init_instrumentation(); + WritableStreamSdkObject = class extends SdkObject { + constructor(parent, streamOrDirectory, lastModifiedMs) { + super(parent, "stream"); + this.streamOrDirectory = streamOrDirectory; + this.lastModifiedMs = lastModifiedMs; + } + }; + WritableStreamDispatcher = class extends Dispatcher { + constructor(scope, streamOrDirectory, lastModifiedMs) { + super(scope, new WritableStreamSdkObject(scope._object, streamOrDirectory, lastModifiedMs), "WritableStream", {}); + this._type_WritableStream = true; + } + async write(params2, progress2) { + if (typeof this._object.streamOrDirectory === "string") + throw new Error("Cannot write to a directory"); + const stream3 = this._object.streamOrDirectory; + await progress2.race(new Promise((fulfill, reject) => { + stream3.write(params2.binary, (error) => { + if (error) + reject(error); + else + fulfill(); + }); + })); + } + async close(params2, progress2) { + if (typeof this._object.streamOrDirectory === "string") + throw new Error("Cannot close a directory"); + const stream3 = this._object.streamOrDirectory; + await progress2.race(new Promise((fulfill) => stream3.end(fulfill))); + if (this._object.lastModifiedMs) + await progress2.race(import_fs36.default.promises.utimes(this.path(), new Date(this._object.lastModifiedMs), new Date(this._object.lastModifiedMs))); + } + path() { + if (typeof this._object.streamOrDirectory === "string") + return this._object.streamOrDirectory; + return this._object.streamOrDirectory.path; + } + _onDispose() { + if (typeof this._object.streamOrDirectory !== "string") + this._object.streamOrDirectory.destroy(); + } + }; + } +}); + +// packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts +var import_fs37, import_path34, BrowserContextDispatcher; +var init_browserContextDispatcher = __esm({ + "packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts"() { + "use strict"; + import_fs37 = __toESM(require("fs")); + import_path34 = __toESM(require("path")); + init_urlMatch(); + init_crypto(); + init_fileUtils(); + init_browserContext(); + init_cdpSessionDispatcher(); + init_debuggerDispatcher(); + init_dialogDispatcher(); + init_dispatcher(); + init_frameDispatcher(); + init_networkDispatchers(); + init_pageDispatcher(); + init_crBrowser(); + init_errors(); + init_disposableDispatcher(); + init_tracingDispatcher(); + init_webSocketRouteDispatcher(); + init_writableStreamDispatcher(); + init_recorder(); + init_recorderApp(); + init_elementHandlerDispatcher(); + init_jsHandleDispatcher(); + init_disposable2(); + BrowserContextDispatcher = class _BrowserContextDispatcher extends Dispatcher { + constructor(parentScope, context2) { + const debugger_ = DebuggerDispatcher.from(parentScope, context2.debugger()); + const requestContext = APIRequestContextDispatcher.from(parentScope, context2.fetchRequest); + const tracing = TracingDispatcher.from(parentScope, context2.tracing); + super(parentScope, context2, "BrowserContext", { + debugger: debugger_, + requestContext, + tracing, + options: context2._options + }); + this._type_BrowserContext = true; + this._subscriptions = /* @__PURE__ */ new Set(); + this._webSocketInterceptionPatterns = []; + this._disposables = []; + this._clockPaused = false; + this._interceptionUrlMatchers = []; + this.adopt(debugger_); + this.adopt(requestContext); + this.adopt(tracing); + this._requestInterceptor = (route2, request2) => { + const matchesSome = this._interceptionUrlMatchers.some((urlMatch) => urlMatches(this._context._options.baseURL, request2.url(), urlMatch)); + const routeDispatcher = this.connection.existingDispatcher(route2); + if (!matchesSome || routeDispatcher) { + route2.continue({ isFallback: true }).catch(() => { + }); + return; + } + this._dispatchEvent("route", { route: new RouteDispatcher(RequestDispatcher.from(this, request2), route2) }); + }; + this._context = context2; + for (const page of context2.pages()) + this._dispatchEvent("page", { page: PageDispatcher.from(this, page) }); + this.addObjectListener(BrowserContext.Events.Page, (page) => { + this._dispatchEvent("page", { page: PageDispatcher.from(this, page) }); + }); + this.addObjectListener(BrowserContext.Events.Close, () => { + this._dispatchEvent("close"); + this._dispose(); + }); + this.addObjectListener(BrowserContext.Events.PageError, (pageError, page) => { + this._dispatchEvent("pageError", { + error: serializeError(pageError.error), + page: PageDispatcher.from(this, page), + location: { + url: pageError.location.url, + line: pageError.location.lineNumber, + column: pageError.location.columnNumber + } + }); + }); + this.addObjectListener(BrowserContext.Events.Console, (message) => { + const pageDispatcher = PageDispatcher.fromNullable(this, message.page()); + const workerDispatcher = WorkerDispatcher.fromNullable(this, message.worker()); + if (this._shouldDispatchEvent(message.page(), "console") || workerDispatcher?._subscriptions.has("console")) { + this._dispatchEvent("console", { + page: pageDispatcher, + worker: workerDispatcher, + ...this.serializeConsoleMessage(message, workerDispatcher || pageDispatcher) + }); + } + }); + this._dialogHandler = (dialog) => { + if (!this._shouldDispatchEvent(dialog.page(), "dialog")) + return false; + this._dispatchEvent("dialog", { dialog: new DialogDispatcher(this, dialog) }); + return true; + }; + context2.dialogManager.addDialogHandler(this._dialogHandler); + if (context2._browser.options.name === "chromium" && this._object._browser instanceof CRBrowser) { + for (const serviceWorker of context2.serviceWorkers()) + this._dispatchEvent("serviceWorker", { worker: new WorkerDispatcher(this, serviceWorker) }); + this.addObjectListener(CRBrowserContext.CREvents.ServiceWorker, (serviceWorker) => this._dispatchEvent("serviceWorker", { worker: new WorkerDispatcher(this, serviceWorker) })); + } + this.addObjectListener(BrowserContext.Events.Request, (request2) => { + const redirectFromDispatcher = request2.redirectedFrom() && this.connection.existingDispatcher(request2.redirectedFrom()); + if (!redirectFromDispatcher && !this._shouldDispatchNetworkEvent(request2, "request") && !request2.isNavigationRequest()) + return; + const requestDispatcher = RequestDispatcher.from(this, request2); + this._dispatchEvent("request", { + request: requestDispatcher, + page: PageDispatcher.fromNullable(this, request2.frame()?._page.initializedOrUndefined()) + }); + }); + this.addObjectListener(BrowserContext.Events.Response, (response2) => { + const requestDispatcher = this.connection.existingDispatcher(response2.request()); + if (!requestDispatcher && !this._shouldDispatchNetworkEvent(response2.request(), "response")) + return; + this._dispatchEvent("response", { + response: ResponseDispatcher.from(this, response2), + page: PageDispatcher.fromNullable(this, response2.frame()?._page.initializedOrUndefined()) + }); + }); + this.addObjectListener(BrowserContext.Events.RequestFailed, (request2) => { + const requestDispatcher = this.connection.existingDispatcher(request2); + if (!requestDispatcher && !this._shouldDispatchNetworkEvent(request2, "requestFailed")) + return; + this._dispatchEvent("requestFailed", { + request: RequestDispatcher.from(this, request2), + failureText: request2._failureText || void 0, + responseEndTiming: request2._responseEndTiming, + page: PageDispatcher.fromNullable(this, request2.frame()?._page.initializedOrUndefined()) + }); + }); + this.addObjectListener(BrowserContext.Events.RequestFinished, ({ request: request2, response: response2 }) => { + const requestDispatcher = this.connection.existingDispatcher(request2); + if (!requestDispatcher && !this._shouldDispatchNetworkEvent(request2, "requestFinished")) + return; + this._dispatchEvent("requestFinished", { + request: RequestDispatcher.from(this, request2), + response: ResponseDispatcher.fromNullable(this, response2), + responseEndTiming: request2._responseEndTiming, + page: PageDispatcher.fromNullable(this, request2.frame()?._page.initializedOrUndefined()) + }); + }); + this.addObjectListener(BrowserContext.Events.RecorderEvent, ({ event, data, page, code }) => { + this._dispatchEvent("recorderEvent", { event, data, code, page: PageDispatcher.from(this, page) }); + }); + } + static from(parentScope, context2) { + const result2 = parentScope.connection.existingDispatcher(context2); + return result2 || new _BrowserContextDispatcher(parentScope, context2); + } + _shouldDispatchNetworkEvent(request2, event) { + return this._shouldDispatchEvent(request2.frame()?._page?.initializedOrUndefined(), event); + } + _shouldDispatchEvent(page, event) { + if (this._subscriptions.has(event)) + return true; + const pageDispatcher = page ? this.connection.existingDispatcher(page) : void 0; + if (pageDispatcher?._subscriptions.has(event)) + return true; + return false; + } + serializeConsoleMessage(message, jsScope) { + return { + type: message.type(), + text: message.text(), + args: message.args().map((a) => { + const elementHandle = a.asElement(); + if (elementHandle) + return ElementHandleDispatcher.from(FrameDispatcher.from(this, elementHandle._frame), elementHandle); + return JSHandleDispatcher.fromJSHandle(jsScope, a); + }), + location: message.location(), + timestamp: message.timestamp() + }; + } + async createTempFiles(params2, progress2) { + const dir = this._context._browser.options.artifactsDir; + const tmpDir = import_path34.default.join(dir, "upload-" + createGuid()); + const tempDirWithRootName = params2.rootDirName ? import_path34.default.join(tmpDir, import_path34.default.basename(params2.rootDirName)) : tmpDir; + await progress2.race(import_fs37.default.promises.mkdir(tempDirWithRootName, { recursive: true })); + this._context._tempDirs.push(tmpDir); + return { + rootDir: params2.rootDirName ? new WritableStreamDispatcher(this, tempDirWithRootName) : void 0, + writableStreams: await Promise.all(params2.items.map(async (item) => { + const itemPath = throwingResolveWithinRoot(tempDirWithRootName, item.name); + await progress2.race(import_fs37.default.promises.mkdir(import_path34.default.dirname(itemPath), { recursive: true })); + const file = import_fs37.default.createWriteStream(itemPath); + return new WritableStreamDispatcher(this, file, item.lastModifiedMs); + })) + }; + } + async exposeBinding(params2, progress2) { + const binding = await this._context.exposeBinding(progress2, params2.name, (source11, ...args) => { + if (this._disposed) + return; + const pageDispatcher = PageDispatcher.from(this, source11.page); + const binding2 = new BindingCallDispatcher(pageDispatcher, params2.name, source11, args); + this._dispatchEvent("bindingCall", { binding: binding2 }); + return binding2.promise(); + }); + this._disposables.push(binding); + return { disposable: new DisposableDispatcher(this, binding) }; + } + async newPage(params2, progress2) { + return { page: PageDispatcher.from(this, await this._context.newPage(progress2)) }; + } + async cookies(params2, progress2) { + return { cookies: await this._context.cookies(progress2, params2.urls) }; + } + async addCookies(params2, progress2) { + await progress2.race(this._context.addCookies(params2.cookies)); + } + async clearCookies(params2, progress2) { + const nameRe = params2.nameRegexSource !== void 0 && params2.nameRegexFlags !== void 0 ? new RegExp(params2.nameRegexSource, params2.nameRegexFlags) : void 0; + const domainRe = params2.domainRegexSource !== void 0 && params2.domainRegexFlags !== void 0 ? new RegExp(params2.domainRegexSource, params2.domainRegexFlags) : void 0; + const pathRe = params2.pathRegexSource !== void 0 && params2.pathRegexFlags !== void 0 ? new RegExp(params2.pathRegexSource, params2.pathRegexFlags) : void 0; + await progress2.race(this._context.clearCookies({ + name: nameRe || params2.name, + domain: domainRe || params2.domain, + path: pathRe || params2.path + })); + } + async grantPermissions(params2, progress2) { + await progress2.race(this._context.grantPermissions(params2.permissions, params2.origin)); + } + async clearPermissions(params2, progress2) { + await progress2.race(this._context.clearPermissions()); + } + async setGeolocation(params2, progress2) { + await progress2.race(this._context.setGeolocation(params2.geolocation)); + } + async setExtraHTTPHeaders(params2, progress2) { + await this._context.setExtraHTTPHeaders(progress2, params2.headers); + } + async setOffline(params2, progress2) { + await this._context.setOffline(progress2, params2.offline); + } + async setHTTPCredentials(params2, progress2) { + await this._context.setHTTPCredentials(progress2, params2.httpCredentials); + } + async addInitScript(params2, progress2) { + const initScript = await this._context.addInitScript(progress2, params2.source); + this._disposables.push(initScript); + return { disposable: new DisposableDispatcher(this, initScript) }; + } + async setNetworkInterceptionPatterns(params2, progress2) { + const hadMatchers = this._interceptionUrlMatchers.length > 0; + if (!params2.patterns.length) { + if (hadMatchers) + await progress2.race(this._context.removeRequestInterceptor(this._requestInterceptor)); + this._interceptionUrlMatchers = []; + } else { + this._interceptionUrlMatchers = params2.patterns.map(deserializeURLMatch); + if (!hadMatchers) + await this._context.addRequestInterceptor(progress2, this._requestInterceptor); + } + } + async setWebSocketInterceptionPatterns(params2, progress2) { + this._webSocketInterceptionPatterns = params2.patterns; + if (params2.patterns.length && !this._routeWebSocketInitScript) + this._routeWebSocketInitScript = await WebSocketRouteDispatcher.install(progress2, this.connection, this._context); + } + async storageState(params2, progress2) { + return await this._context.storageState(progress2, params2.indexedDB); + } + async setStorageState(params2, progress2) { + await this._context.setStorageState(progress2, params2.storageState, "api"); + } + async close(params2, progress2) { + await this._context.close(progress2, params2); + } + async enableRecorder(params2, progress2) { + await progress2.race(RecorderApp.show(this._context, params2)); + } + async disableRecorder(params2, progress2) { + const recorder = await progress2.race(Recorder.existingForContext(this._context)); + if (recorder) + await progress2.race(recorder.setMode("none")); + } + async exposeConsoleApi(params2, progress2) { + await this._context.exposeConsoleApi(progress2); + } + async pause(params2, progress2) { + } + async newCDPSession(params2, progress2) { + if (this._object._browser.options.browserType !== "chromium") + throw new Error(`CDP session is only available in Chromium`); + if (!params2.page && !params2.frame || params2.page && params2.frame) + throw new Error(`CDP session must be initiated with either Page or Frame, not none or both`); + const crBrowserContext = this._object; + return { session: new CDPSessionDispatcher(this, await progress2.race(crBrowserContext.newCDPSession((params2.page ? params2.page : params2.frame)._object))) }; + } + async clockFastForward(params2, progress2) { + await progress2.race(this._context.clock.fastForward(params2.ticksString ?? params2.ticksNumber ?? 0)); + } + async clockInstall(params2, progress2) { + await progress2.race(this._context.clock.install(params2.timeString ?? params2.timeNumber ?? void 0)); + } + async clockPauseAt(params2, progress2) { + await progress2.race(this._context.clock.pauseAt(params2.timeString ?? params2.timeNumber ?? 0)); + this._clockPaused = true; + } + async clockResume(params2, progress2) { + await this._context.clock.resume(progress2); + this._clockPaused = false; + } + async clockRunFor(params2, progress2) { + await progress2.race(this._context.clock.runFor(params2.ticksString ?? params2.ticksNumber ?? 0)); + } + async clockSetFixedTime(params2, progress2) { + await progress2.race(this._context.clock.setFixedTime(params2.timeString ?? params2.timeNumber ?? 0)); + } + async clockSetSystemTime(params2, progress2) { + await progress2.race(this._context.clock.setSystemTime(params2.timeString ?? params2.timeNumber ?? 0)); + } + async credentialsInstall(params2, progress2) { + await this._context.credentials.install(progress2); + } + async credentialsCreate(params2, progress2) { + const credential = await progress2.race(this._context.credentials.create(params2)); + return { credential }; + } + async credentialsGet(params2, progress2) { + const credentials = await progress2.race(this._context.credentials.get(params2)); + return { credentials }; + } + async credentialsDelete(params2, progress2) { + await progress2.race(this._context.credentials.delete(params2.id)); + } + async updateSubscription(params2, progress2) { + if (params2.enabled) + this._subscriptions.add(params2.event); + else + this._subscriptions.delete(params2.event); + } + async registerSelectorEngine(params2, progress2) { + this._object.selectors().register(params2.selectorEngine); + } + async setTestIdAttributeName(params2, progress2) { + this._object.selectors().setTestIdAttributeName(params2.testIdAttributeName); + } + _onDispose() { + if (this._context.isClosingOrClosed()) + return; + this._context.dialogManager.removeDialogHandler(this._dialogHandler); + this._interceptionUrlMatchers = []; + this._context.removeRequestInterceptor(this._requestInterceptor).catch(() => { + }); + disposeAll2(this._disposables).catch(() => { + }); + if (this._routeWebSocketInitScript) + WebSocketRouteDispatcher.uninstall(this.connection, this._context, this._routeWebSocketInitScript).catch(() => { + }); + this._routeWebSocketInitScript = void 0; + if (this._clockPaused) + this._context.clock.resumeNoReply(); + this._clockPaused = false; + } + }; + } +}); + +// packages/playwright-core/src/server/dispatchers/elementHandlerDispatcher.ts +var ElementHandleDispatcher; +var init_elementHandlerDispatcher = __esm({ + "packages/playwright-core/src/server/dispatchers/elementHandlerDispatcher.ts"() { + "use strict"; + init_browserContextDispatcher(); + init_frameDispatcher(); + init_jsHandleDispatcher(); + ElementHandleDispatcher = class _ElementHandleDispatcher extends JSHandleDispatcher { + constructor(scope, elementHandle) { + super(scope, elementHandle); + this._type_ElementHandle = true; + this._elementHandle = elementHandle; + } + static from(scope, handle) { + return scope.connection.existingDispatcher(handle) || new _ElementHandleDispatcher(scope, handle); + } + static fromNullable(scope, handle) { + if (!handle) + return void 0; + return scope.connection.existingDispatcher(handle) || new _ElementHandleDispatcher(scope, handle); + } + static fromJSOrElementHandle(scope, handle) { + const result2 = scope.connection.existingDispatcher(handle); + if (result2) + return result2; + const elementHandle = handle.asElement(); + if (!elementHandle) + return new JSHandleDispatcher(scope, handle); + return new _ElementHandleDispatcher(scope, elementHandle); + } + async ownerFrame(params2, progress2) { + const frame = await this._elementHandle.ownerFrame(progress2); + return { frame: frame ? FrameDispatcher.from(this._browserContextDispatcher(), frame) : void 0 }; + } + async contentFrame(params2, progress2) { + const frame = await this._elementHandle.contentFrame(progress2); + return { frame: frame ? FrameDispatcher.from(this._browserContextDispatcher(), frame) : void 0 }; + } + async getAttribute(params2, progress2) { + const value2 = await this._elementHandle.getAttribute(progress2, params2.name); + return { value: value2 === null ? void 0 : value2 }; + } + async inputValue(params2, progress2) { + const value2 = await this._elementHandle.inputValue(progress2); + return { value: value2 }; + } + async textContent(params2, progress2) { + const value2 = await this._elementHandle.textContent(progress2); + return { value: value2 === null ? void 0 : value2 }; + } + async innerText(params2, progress2) { + return { value: await this._elementHandle.innerText(progress2) }; + } + async innerHTML(params2, progress2) { + return { value: await this._elementHandle.innerHTML(progress2) }; + } + async isChecked(params2, progress2) { + return { value: await this._elementHandle.isChecked(progress2) }; + } + async isDisabled(params2, progress2) { + return { value: await this._elementHandle.isDisabled(progress2) }; + } + async isEditable(params2, progress2) { + return { value: await this._elementHandle.isEditable(progress2) }; + } + async isEnabled(params2, progress2) { + return { value: await this._elementHandle.isEnabled(progress2) }; + } + async isHidden(params2, progress2) { + return { value: await this._elementHandle.isHidden(progress2) }; + } + async isVisible(params2, progress2) { + return { value: await this._elementHandle.isVisible(progress2) }; + } + async dispatchEvent(params2, progress2) { + await this._elementHandle.dispatchEvent(progress2, params2.type, parseArgument(params2.eventInit)); + } + async scrollIntoViewIfNeeded(params2, progress2) { + await this._elementHandle.scrollIntoViewIfNeeded(progress2); + } + async hover(params2, progress2) { + return await this._elementHandle.hover(progress2, params2); + } + async click(params2, progress2) { + return await this._elementHandle.click(progress2, params2); + } + async dblclick(params2, progress2) { + return await this._elementHandle.dblclick(progress2, params2); + } + async tap(params2, progress2) { + return await this._elementHandle.tap(progress2, params2); + } + async selectOption(params2, progress2) { + const elements = (params2.elements || []).map((e) => e._elementHandle); + return { values: await this._elementHandle.selectOption(progress2, elements, params2.options || [], params2) }; + } + async fill(params2, progress2) { + return await this._elementHandle.fill(progress2, params2.value, params2); + } + async selectText(params2, progress2) { + await this._elementHandle.selectText(progress2, params2); + } + async setInputFiles(params2, progress2) { + return await this._elementHandle.setInputFiles(progress2, params2); + } + async focus(params2, progress2) { + await this._elementHandle.focus(progress2); + } + async type(params2, progress2) { + return await this._elementHandle.type(progress2, params2.text, params2); + } + async press(params2, progress2) { + return await this._elementHandle.press(progress2, params2.key, params2); + } + async check(params2, progress2) { + return await this._elementHandle.check(progress2, params2); + } + async uncheck(params2, progress2) { + return await this._elementHandle.uncheck(progress2, params2); + } + async boundingBox(params2, progress2) { + const value2 = await this._elementHandle.boundingBox(progress2); + return { value: value2 || void 0 }; + } + async screenshot(params2, progress2) { + const mask = (params2.mask || []).map(({ frame, selector }) => ({ + frame: frame._object, + selector + })); + return { binary: await this._elementHandle.screenshot(progress2, { ...params2, mask }) }; + } + async querySelector(params2, progress2) { + const handle = await this._elementHandle.querySelector(progress2, params2.selector, params2); + return { element: _ElementHandleDispatcher.fromNullable(this.parentScope(), handle) }; + } + async querySelectorAll(params2, progress2) { + const elements = await this._elementHandle.querySelectorAll(progress2, params2.selector); + return { elements: elements.map((e) => _ElementHandleDispatcher.from(this.parentScope(), e)) }; + } + async evalOnSelector(params2, progress2) { + return { value: serializeResult(await this._elementHandle.evalOnSelector(progress2, params2.selector, !!params2.strict, params2.expression, params2.isFunction, parseArgument(params2.arg))) }; + } + async evalOnSelectorAll(params2, progress2) { + return { value: serializeResult(await this._elementHandle.evalOnSelectorAll(progress2, params2.selector, params2.expression, params2.isFunction, parseArgument(params2.arg))) }; + } + async waitForElementState(params2, progress2) { + await this._elementHandle.waitForElementState(progress2, params2.state); + } + async waitForSelector(params2, progress2) { + return { element: _ElementHandleDispatcher.fromNullable(this.parentScope(), await this._elementHandle.waitForSelector(progress2, params2.selector, params2)) }; + } + _browserContextDispatcher() { + const parentScope = this.parentScope().parentScope(); + if (parentScope instanceof BrowserContextDispatcher) + return parentScope; + return parentScope.parentScope(); + } + }; + } +}); + +// packages/playwright-core/src/server/dispatchers/frameDispatcher.ts +var FrameDispatcher; +var init_frameDispatcher = __esm({ + "packages/playwright-core/src/server/dispatchers/frameDispatcher.ts"() { + "use strict"; + init_protocolFormatter(); + init_frames(); + init_dispatcher(); + init_elementHandlerDispatcher(); + init_jsHandleDispatcher(); + init_networkDispatchers(); + init_networkDispatchers(); + FrameDispatcher = class _FrameDispatcher extends Dispatcher { + constructor(scope, frame) { + const gcBucket = frame._page.mainFrame() === frame ? "MainFrame" : "Frame"; + const pageDispatcher = scope.connection.existingDispatcher(frame._page); + super(pageDispatcher || scope, frame, "Frame", { + url: frame.url(), + name: frame.name(), + parentFrame: _FrameDispatcher.fromNullable(scope, frame.parentFrame()), + loadStates: Array.from(frame._firedLifecycleEvents) + }, gcBucket); + this._type_Frame = true; + this._browserContextDispatcher = scope; + this._frame = frame; + this.addObjectListener(Frame.Events.AddLifecycle, (lifecycleEvent) => { + this._dispatchEvent("loadstate", { add: lifecycleEvent }); + }); + this.addObjectListener(Frame.Events.RemoveLifecycle, (lifecycleEvent) => { + this._dispatchEvent("loadstate", { remove: lifecycleEvent }); + }); + this.addObjectListener(Frame.Events.InternalNavigation, (event) => { + if (!event.isPublic) + return; + const params2 = { url: event.url, name: event.name, error: event.error ? event.error.message : void 0 }; + if (event.newDocument) + params2.newDocument = { request: RequestDispatcher.fromNullable(this._browserContextDispatcher, event.newDocument.request || null) }; + this._dispatchEvent("navigated", params2); + }); + } + static from(scope, frame) { + const result2 = scope.connection.existingDispatcher(frame); + return result2 || new _FrameDispatcher(scope, frame); + } + static fromNullable(scope, frame) { + if (!frame) + return; + return _FrameDispatcher.from(scope, frame); + } + async goto(params2, progress2) { + return { response: ResponseDispatcher.fromNullable(this._browserContextDispatcher, await this._frame.goto(progress2, params2.url, params2)) }; + } + async frameElement(params2, progress2) { + return { element: ElementHandleDispatcher.from(this, await this._frame.frameElement(progress2)) }; + } + async evaluateExpression(params2, progress2) { + return { value: serializeResult(await this._frame.evaluateExpression(progress2, params2.expression, { isFunction: params2.isFunction }, parseArgument(params2.arg))) }; + } + async evaluateExpressionHandle(params2, progress2) { + return { handle: ElementHandleDispatcher.fromJSOrElementHandle(this, await this._frame.evaluateExpressionHandle(progress2, params2.expression, { isFunction: params2.isFunction }, parseArgument(params2.arg))) }; + } + async waitForSelector(params2, progress2) { + return { element: ElementHandleDispatcher.fromNullable(this, await this._frame.waitForSelector(progress2, params2.selector, true, params2)) }; + } + async dispatchEvent(params2, progress2) { + return this._frame.dispatchEvent(progress2, params2.selector, params2.type, parseArgument(params2.eventInit), params2); + } + async evalOnSelector(params2, progress2) { + return { value: serializeResult(await this._frame.evalOnSelector(progress2, params2.selector, !!params2.strict, params2.expression, params2.isFunction, parseArgument(params2.arg))) }; + } + async evalOnSelectorAll(params2, progress2) { + return { value: serializeResult(await this._frame.evalOnSelectorAll(progress2, params2.selector, params2.expression, params2.isFunction, parseArgument(params2.arg))) }; + } + async querySelector(params2, progress2) { + return { element: ElementHandleDispatcher.fromNullable(this, await this._frame.querySelector(progress2, params2.selector, params2)) }; + } + async querySelectorAll(params2, progress2) { + const elements = await this._frame.querySelectorAll(progress2, params2.selector); + return { elements: elements.map((e) => ElementHandleDispatcher.from(this, e)) }; + } + async queryCount(params2, progress2) { + return { value: await this._frame.queryCount(progress2, params2.selector, params2) }; + } + async content(params2, progress2) { + return { value: await this._frame.content(progress2) }; + } + async setContent(params2, progress2) { + return await this._frame.setContent(progress2, params2.html, params2); + } + async addScriptTag(params2, progress2) { + return { element: ElementHandleDispatcher.from(this, await this._frame.addScriptTag(progress2, params2)) }; + } + async addStyleTag(params2, progress2) { + return { element: ElementHandleDispatcher.from(this, await this._frame.addStyleTag(progress2, params2)) }; + } + async ariaSnapshot(params2, progress2) { + return await this._frame.ariaSnapshot(progress2, params2); + } + async click(params2, progress2) { + return await this._frame.click(progress2, params2.selector, params2); + } + async dblclick(params2, progress2) { + return await this._frame.dblclick(progress2, params2.selector, params2); + } + async dragAndDrop(params2, progress2) { + return await this._frame.dragAndDrop(progress2, params2.source, params2.target, params2); + } + async drop(params2, progress2) { + return await this._frame.drop(progress2, params2.selector, params2, params2); + } + async tap(params2, progress2) { + return await this._frame.tap(progress2, params2.selector, params2); + } + async fill(params2, progress2) { + return await this._frame.fill(progress2, params2.selector, params2.value, params2); + } + async focus(params2, progress2) { + await this._frame.focus(progress2, params2.selector, params2); + } + async blur(params2, progress2) { + await this._frame.blur(progress2, params2.selector, params2); + } + async textContent(params2, progress2) { + const value2 = await this._frame.textContent(progress2, params2.selector, params2); + return { value: value2 === null ? void 0 : value2 }; + } + async innerText(params2, progress2) { + return { value: await this._frame.innerText(progress2, params2.selector, params2) }; + } + async innerHTML(params2, progress2) { + return { value: await this._frame.innerHTML(progress2, params2.selector, params2) }; + } + async resolveSelector(params2, progress2) { + return await this._frame.resolveSelector(progress2, params2.selector); + } + async getAttribute(params2, progress2) { + const value2 = await this._frame.getAttribute(progress2, params2.selector, params2.name, params2); + return { value: value2 === null ? void 0 : value2 }; + } + async inputValue(params2, progress2) { + const value2 = await this._frame.inputValue(progress2, params2.selector, params2); + return { value: value2 }; + } + async isChecked(params2, progress2) { + return { value: await this._frame.isChecked(progress2, params2.selector, params2) }; + } + async isDisabled(params2, progress2) { + return { value: await this._frame.isDisabled(progress2, params2.selector, params2) }; + } + async isEditable(params2, progress2) { + return { value: await this._frame.isEditable(progress2, params2.selector, params2) }; + } + async isEnabled(params2, progress2) { + return { value: await this._frame.isEnabled(progress2, params2.selector, params2) }; + } + async isHidden(params2, progress2) { + return { value: await this._frame.isHidden(progress2, params2.selector, params2) }; + } + async isVisible(params2, progress2) { + return { value: await this._frame.isVisible(progress2, params2.selector, params2) }; + } + async hover(params2, progress2) { + return await this._frame.hover(progress2, params2.selector, params2); + } + async selectOption(params2, progress2) { + const elements = (params2.elements || []).map((e) => e._elementHandle); + return { values: await this._frame.selectOption(progress2, params2.selector, elements, params2.options || [], params2) }; + } + async setInputFiles(params2, progress2) { + return await this._frame.setInputFiles(progress2, params2.selector, params2); + } + async type(params2, progress2) { + return await this._frame.type(progress2, params2.selector, params2.text, params2); + } + async press(params2, progress2) { + return await this._frame.press(progress2, params2.selector, params2.key, params2); + } + async check(params2, progress2) { + return await this._frame.check(progress2, params2.selector, params2); + } + async uncheck(params2, progress2) { + return await this._frame.uncheck(progress2, params2.selector, params2); + } + async waitForTimeout(params2, progress2) { + return await this._frame.waitForTimeout(progress2, params2.waitTimeout); + } + async waitForFunction(params2, progress2) { + return { handle: ElementHandleDispatcher.fromJSOrElementHandle(this, await this._frame.waitForFunctionExpression(progress2, params2.expression, params2.isFunction, parseArgument(params2.arg), params2)) }; + } + async title(params2, progress2) { + return { value: await this._frame.title(progress2) }; + } + async highlight(params2, progress2) { + return await this._frame.addHighlight(progress2, params2.selector, params2.style); + } + async hideHighlight(params2, progress2) { + return await this._frame.removeHighlight(progress2, params2.selector); + } + async expect(params2, progress2) { + progress2.log(`${renderTitleForCall(progress2.metadata)}${params2.timeout ? ` with timeout ${params2.timeout}ms` : ""}`); + const expectedValue = params2.expectedValue ? parseArgument(params2.expectedValue) : void 0; + try { + await this._frame.expect(progress2, params2.selector, { ...params2, expectedValue }); + } catch (e) { + if (e instanceof ExpectError && e.details.received && "value" in e.details.received) + e.details.received.value = serializeResult(e.details.received.value); + throw e; + } + } + }; + } +}); + +// packages/playwright-core/src/server/dispatchers/networkDispatchers.ts +var RequestDispatcher, ResponseDispatcher, RouteDispatcher, WebSocketDispatcher, APIRequestContextDispatcher; +var init_networkDispatchers = __esm({ + "packages/playwright-core/src/server/dispatchers/networkDispatchers.ts"() { + "use strict"; + init_network2(); + init_dispatcher(); + init_frameDispatcher(); + init_pageDispatcher(); + init_tracingDispatcher(); + RequestDispatcher = class _RequestDispatcher extends Dispatcher { + static from(scope, request2) { + const result2 = scope.connection.existingDispatcher(request2); + return result2 || new _RequestDispatcher(scope, request2); + } + static fromNullable(scope, request2) { + return request2 ? _RequestDispatcher.from(scope, request2) : void 0; + } + constructor(scope, request2) { + const postData = request2.postDataBuffer(); + const frame = request2.frame(); + const page = request2.frame()?._page; + const pageDispatcher = page ? scope.connection.existingDispatcher(page) : null; + const frameDispatcher = FrameDispatcher.fromNullable(scope, frame); + super(pageDispatcher || frameDispatcher || scope, request2, "Request", { + frame: frameDispatcher, + serviceWorker: WorkerDispatcher.fromNullable(scope, request2.serviceWorker()), + url: request2.url(), + resourceType: request2.resourceType(), + method: request2.method(), + postData: postData === null ? void 0 : postData, + headers: request2.headers(), + isNavigationRequest: request2.isNavigationRequest(), + redirectedFrom: _RequestDispatcher.fromNullable(scope, request2.redirectedFrom()) + }); + this._type_Request = true; + this._browserContextDispatcher = scope; + ResponseDispatcher.fromNullable(scope, request2._existingResponse()); + } + async rawRequestHeaders(params2, progress2) { + return { headers: await this._object.rawRequestHeaders(progress2) }; + } + async response(params2, progress2) { + return { response: ResponseDispatcher.fromNullable(this._browserContextDispatcher, await this._object.response(progress2)) }; + } + }; + ResponseDispatcher = class _ResponseDispatcher extends Dispatcher { + constructor(scope, response2) { + super(scope, response2, "Response", { + // TODO: responses in popups can point to non-reported requests. + request: scope, + url: response2.url(), + status: response2.status(), + statusText: response2.statusText(), + headers: response2.headers(), + timing: response2.timing(), + fromServiceWorker: response2.fromServiceWorker() + }); + this._type_Response = true; + } + static from(scope, response2) { + const requestDispatcher = RequestDispatcher.from(scope, response2.request()); + const result2 = scope.connection.existingDispatcher(response2); + return result2 || new _ResponseDispatcher(requestDispatcher, response2); + } + static fromNullable(scope, response2) { + return response2 ? _ResponseDispatcher.from(scope, response2) : void 0; + } + async body(params2, progress2) { + return { binary: await this._object.body(progress2) }; + } + async securityDetails(params2, progress2) { + return { value: await this._object.securityDetails(progress2) || void 0 }; + } + async serverAddr(params2, progress2) { + return { value: await this._object.serverAddr(progress2) || void 0 }; + } + async rawResponseHeaders(params2, progress2) { + return { headers: await this._object.rawResponseHeaders(progress2) }; + } + async httpVersion(params2, progress2) { + return { value: await this._object.httpVersion(progress2) }; + } + async sizes(params2, progress2) { + return { sizes: await this._object.sizes(progress2) }; + } + }; + RouteDispatcher = class extends Dispatcher { + constructor(scope, route2) { + super(scope, route2, "Route", { + // Context route can point to a non-reported request, so we send the request in the initializer. + request: scope + }); + this._type_Route = true; + this._handled = false; + } + _checkNotHandled() { + if (this._handled) + throw new Error("Route is already handled!"); + this._handled = true; + } + async continue(params2, progress2) { + this._checkNotHandled(); + await progress2.race(this._object.continue({ + url: params2.url, + method: params2.method, + headers: params2.headers, + postData: params2.postData, + isFallback: params2.isFallback + })); + } + async fulfill(params2, progress2) { + this._checkNotHandled(); + await progress2.race(this._object.fulfill(params2)); + } + async abort(params2, progress2) { + this._checkNotHandled(); + await progress2.race(this._object.abort(params2.errorCode || "failed")); + } + async redirectNavigationRequest(params2, progress2) { + this._checkNotHandled(); + this._object.redirectNavigationRequest(params2.url); + } + }; + WebSocketDispatcher = class extends Dispatcher { + constructor(scope, webSocket) { + super(scope, webSocket, "WebSocket", { + url: webSocket.url() + }); + this._type_WebSocket = true; + this.addObjectListener(WebSocket.Events.FrameSent, (event) => this._dispatchEvent("frameSent", event)); + this.addObjectListener(WebSocket.Events.FrameReceived, (event) => this._dispatchEvent("frameReceived", event)); + this.addObjectListener(WebSocket.Events.SocketError, (error) => this._dispatchEvent("socketError", { error })); + this.addObjectListener(WebSocket.Events.Close, () => this._dispatchEvent("close", {})); + } + }; + APIRequestContextDispatcher = class _APIRequestContextDispatcher extends Dispatcher { + constructor(parentScope, request2) { + const tracing = TracingDispatcher.from(parentScope, request2.tracing()); + super(parentScope, request2, "APIRequestContext", { + tracing + }); + this._type_APIRequestContext = true; + this.adopt(tracing); + } + static from(scope, request2) { + const result2 = scope.connection.existingDispatcher(request2); + return result2 || new _APIRequestContextDispatcher(scope, request2); + } + static fromNullable(scope, request2) { + return request2 ? _APIRequestContextDispatcher.from(scope, request2) : void 0; + } + async storageState(params2, progress2) { + return await this._object.storageState(progress2, params2.indexedDB); + } + async dispose(params2, progress2) { + await progress2.race(this._object.dispose(params2)); + this._dispose(); + } + async fetch(params2, progress2) { + const response2 = await this._object.fetch(progress2, params2); + return { response: response2 }; + } + async fetchResponseBody(params2, progress2) { + return { binary: this._object.fetchResponseBody(progress2, params2.fetchUid) }; + } + async fetchLog(params2, progress2) { + return { log: this._object.fetchLogForUid(progress2, params2.fetchUid) }; + } + async disposeAPIResponse(params2, progress2) { + this._object.disposeResponse(progress2, params2.fetchUid); + } + }; + } +}); + +// packages/playwright-core/src/server/dispatchers/androidDispatcher.ts +function fixupAndroidElementInfo(info) { + info.clazz = info.clazz || ""; + info.pkg = info.pkg || ""; + info.res = info.res || ""; + info.desc = info.desc || ""; + info.text = info.text || ""; + for (const child of info.children || []) + fixupAndroidElementInfo(child); +} +var AndroidDispatcher, AndroidDeviceDispatcher, SocketSdkObject, AndroidSocketDispatcher, keyMap; +var init_androidDispatcher = __esm({ + "packages/playwright-core/src/server/dispatchers/androidDispatcher.ts"() { + "use strict"; + init_eventsHelper(); + init_browserContextDispatcher(); + init_dispatcher(); + init_android(); + init_instrumentation(); + AndroidDispatcher = class extends Dispatcher { + constructor(scope, android, denyLaunch) { + super(scope, android, "Android", {}); + this._type_Android = true; + this._denyLaunch = denyLaunch; + } + async devices(params2, progress2) { + if (this._denyLaunch) + throw new Error(`Connecting to Android devices is not allowed.`); + const devices = await this._object.devices(progress2, params2); + return { + devices: devices.map((d) => AndroidDeviceDispatcher.from(this, d)) + }; + } + }; + AndroidDeviceDispatcher = class _AndroidDeviceDispatcher extends Dispatcher { + constructor(scope, device) { + super(scope, device, "AndroidDevice", { + model: device.model, + serial: device.serial + }); + this._type_AndroidDevice = true; + for (const webView of device.webViews()) + this._dispatchEvent("webViewAdded", { webView }); + this.addObjectListener(AndroidDevice.Events.WebViewAdded, (webView) => this._dispatchEvent("webViewAdded", { webView })); + this.addObjectListener(AndroidDevice.Events.WebViewRemoved, (socketName) => this._dispatchEvent("webViewRemoved", { socketName })); + this.addObjectListener(AndroidDevice.Events.Close, () => this._dispatchEvent("close")); + } + static from(scope, device) { + const result2 = scope.connection.existingDispatcher(device); + return result2 || new _AndroidDeviceDispatcher(scope, device); + } + async wait(params2, progress2) { + await this._object.send(progress2, "wait", params2); + } + async fill(params2, progress2) { + await this._object.send(progress2, "click", { selector: params2.androidSelector }); + await this._object.send(progress2, "fill", params2); + } + async tap(params2, progress2) { + await this._object.send(progress2, "click", params2); + } + async drag(params2, progress2) { + await this._object.send(progress2, "drag", params2); + } + async fling(params2, progress2) { + await this._object.send(progress2, "fling", params2); + } + async longTap(params2, progress2) { + await this._object.send(progress2, "longClick", params2); + } + async pinchClose(params2, progress2) { + await this._object.send(progress2, "pinchClose", params2); + } + async pinchOpen(params2, progress2) { + await this._object.send(progress2, "pinchOpen", params2); + } + async scroll(params2, progress2) { + await this._object.send(progress2, "scroll", params2); + } + async swipe(params2, progress2) { + await this._object.send(progress2, "swipe", params2); + } + async info(params2, progress2) { + const info = await this._object.send(progress2, "info", params2); + fixupAndroidElementInfo(info); + return { info }; + } + async inputType(params2, progress2) { + const text2 = params2.text; + const keyCodes = []; + for (let i = 0; i < text2.length; ++i) { + const code = keyMap.get(text2[i].toUpperCase()); + if (code === void 0) + throw new Error("No mapping for " + text2[i] + " found"); + keyCodes.push(code); + } + await progress2.race(Promise.all(keyCodes.map((keyCode) => this._object.send(progress2, "inputPress", { keyCode })))); + } + async inputPress(params2, progress2) { + if (!keyMap.has(params2.key)) + throw new Error("Unknown key: " + params2.key); + await this._object.send(progress2, "inputPress", { keyCode: keyMap.get(params2.key) }); + } + async inputTap(params2, progress2) { + await this._object.send(progress2, "inputClick", params2); + } + async inputSwipe(params2, progress2) { + await this._object.send(progress2, "inputSwipe", params2); + } + async inputDrag(params2, progress2) { + await this._object.send(progress2, "inputDrag", params2); + } + async screenshot(params2, progress2) { + return { binary: await this._object.screenshot(progress2) }; + } + async shell(params2, progress2) { + return { result: await this._object.shell(progress2, params2.command) }; + } + async open(params2, progress2) { + const socket = await this._object.open(progress2, params2.command); + return { socket: new AndroidSocketDispatcher(this, new SocketSdkObject(this._object, socket)) }; + } + async installApk(params2, progress2) { + await this._object.installApk(progress2, params2.file, { args: params2.args }); + } + async push(params2, progress2) { + await this._object.push(progress2, params2.file, params2.path, params2.mode); + } + async launchBrowser(params2, progress2) { + const context2 = await this._object.launchBrowser(progress2, params2.pkg, params2); + return { context: BrowserContextDispatcher.from(this, context2) }; + } + async close(params2, progress2) { + await this._object.close(progress2); + } + async connectToWebView(params2, progress2) { + return { context: BrowserContextDispatcher.from(this, await this._object.connectToWebView(progress2, params2.socketName)) }; + } + }; + SocketSdkObject = class extends SdkObject { + constructor(parent, socket) { + super(parent, "socket"); + this._socket = socket; + this._eventListeners = [ + eventsHelper.addEventListener(socket, "data", (data) => this.emit("data", data)), + eventsHelper.addEventListener(socket, "close", () => { + eventsHelper.removeEventListeners(this._eventListeners); + this.emit("close"); + }) + ]; + } + async write(data) { + await this._socket.write(data); + } + close() { + this._socket.close(); + } + }; + AndroidSocketDispatcher = class extends Dispatcher { + constructor(scope, socket) { + super(scope, socket, "AndroidSocket", {}); + this._type_AndroidSocket = true; + this.addObjectListener("data", (data) => this._dispatchEvent("data", { data })); + this.addObjectListener("close", () => { + this._dispatchEvent("close"); + this._dispose(); + }); + } + async write(params2, progress2) { + await progress2.race(this._object.write(params2.data)); + } + async close(params2, progress2) { + this._object.close(); + } + }; + keyMap = /* @__PURE__ */ new Map([ + ["Unknown", 0], + ["SoftLeft", 1], + ["SoftRight", 2], + ["Home", 3], + ["Back", 4], + ["Call", 5], + ["EndCall", 6], + ["0", 7], + ["1", 8], + ["2", 9], + ["3", 10], + ["4", 11], + ["5", 12], + ["6", 13], + ["7", 14], + ["8", 15], + ["9", 16], + ["Star", 17], + ["*", 17], + ["Pound", 18], + ["#", 18], + ["DialUp", 19], + ["DialDown", 20], + ["DialLeft", 21], + ["DialRight", 22], + ["DialCenter", 23], + ["VolumeUp", 24], + ["VolumeDown", 25], + ["Power", 26], + ["Camera", 27], + ["Clear", 28], + ["A", 29], + ["B", 30], + ["C", 31], + ["D", 32], + ["E", 33], + ["F", 34], + ["G", 35], + ["H", 36], + ["I", 37], + ["J", 38], + ["K", 39], + ["L", 40], + ["M", 41], + ["N", 42], + ["O", 43], + ["P", 44], + ["Q", 45], + ["R", 46], + ["S", 47], + ["T", 48], + ["U", 49], + ["V", 50], + ["W", 51], + ["X", 52], + ["Y", 53], + ["Z", 54], + ["Comma", 55], + [",", 55], + ["Period", 56], + [".", 56], + ["AltLeft", 57], + ["AltRight", 58], + ["ShiftLeft", 59], + ["ShiftRight", 60], + ["Tab", 61], + [" ", 61], + ["Space", 62], + [" ", 62], + ["Sym", 63], + ["Explorer", 64], + ["Envelop", 65], + ["Enter", 66], + ["Del", 67], + ["Grave", 68], + ["Minus", 69], + ["-", 69], + ["Equals", 70], + ["=", 70], + ["LeftBracket", 71], + ["(", 71], + ["RightBracket", 72], + [")", 72], + ["Backslash", 73], + ["\\", 73], + ["Semicolon", 74], + [";", 74], + ["Apostrophe", 75], + ["`", 75], + ["Slash", 76], + ["/", 76], + ["At", 77], + ["@", 77], + ["Num", 78], + ["HeadsetHook", 79], + ["Focus", 80], + ["Plus", 81], + ["Menu", 82], + ["Notification", 83], + ["Search", 84], + ["MediaPlayPause", 85], + ["MediaStop", 86], + ["MediaNext", 87], + ["MediaPrevious", 88], + ["MediaRewind", 89], + ["MediaFastForward", 90], + ["MediaPlay", 126], + ["MediaPause", 127], + ["MediaClose", 128], + ["MediaEject", 129], + ["MediaRecord", 130], + ["ChannelUp", 166], + ["ChannelDown", 167], + ["AppSwitch", 187], + ["Assist", 219], + ["MediaAudioTrack", 222], + ["MediaTopMenu", 226], + ["MediaSkipForward", 272], + ["MediaSkipBackward", 273], + ["MediaStepForward", 274], + ["MediaStepBackward", 275], + ["Cut", 277], + ["Copy", 278], + ["Paste", 279] + ]); + } +}); + +// packages/playwright-core/src/server/dispatchers/browserDispatcher.ts +var BrowserDispatcher; +var init_browserDispatcher = __esm({ + "packages/playwright-core/src/server/dispatchers/browserDispatcher.ts"() { + "use strict"; + init_browser(); + init_browserContextDispatcher(); + init_cdpSessionDispatcher(); + init_dispatcher(); + init_browserContext(); + init_artifactDispatcher(); + init_progress(); + BrowserDispatcher = class extends Dispatcher { + constructor(scope, browser, options = {}) { + super(scope, browser, "Browser", { version: browser.version(), name: browser.options.name, browserName: browser.options.browserType }); + this._type_Browser = true; + this._isolatedContexts = /* @__PURE__ */ new Set(); + this._options = options; + if (!options.isolateContexts) { + this.addObjectListener(Browser.Events.Context, (context2) => this._dispatchEvent("context", { context: BrowserContextDispatcher.from(this, context2) })); + this.addObjectListener(Browser.Events.Disconnected, () => this._didClose()); + if (browser._defaultContext) + this._dispatchEvent("context", { context: BrowserContextDispatcher.from(this, browser._defaultContext) }); + for (const context2 of browser.contexts()) + this._dispatchEvent("context", { context: BrowserContextDispatcher.from(this, context2) }); + } + } + _didClose() { + this._dispatchEvent("close"); + this._dispose(); + } + async newContext(params2, progress2) { + if (params2.recordVideo && this._object.attribution.playwright.options.isServer) + params2.recordVideo.dir = void 0; + if (!this._options.isolateContexts) { + const context3 = await this._object.newContext(progress2, params2); + const contextDispatcher2 = BrowserContextDispatcher.from(this, context3); + return { context: contextDispatcher2 }; + } + const context2 = await this._object.newContext(progress2, params2); + this._isolatedContexts.add(context2); + context2.on(BrowserContext.Events.Close, () => this._isolatedContexts.delete(context2)); + const contextDispatcher = BrowserContextDispatcher.from(this, context2); + this._dispatchEvent("context", { context: contextDispatcher }); + return { context: contextDispatcher }; + } + async newContextForReuse(params2, progress2) { + const context2 = await this._object.newContextForReuse(progress2, params2); + const contextDispatcher = BrowserContextDispatcher.from(this, context2); + this._dispatchEvent("context", { context: contextDispatcher }); + return { context: contextDispatcher }; + } + async disconnectFromReusedContext(params2, progress2) { + const context2 = this._object.contextForReuse(); + const contextDispatcher = context2 ? this.connection.existingDispatcher(context2) : void 0; + if (contextDispatcher) { + await progress2.race(contextDispatcher.stopPendingOperations(new Error(params2.reason))); + contextDispatcher._dispose(); + } + } + async close(params2, progress2) { + if (this._options.ignoreStopAndKill) + return; + await this._object.close(progress2, params2); + } + async killForTests(params2, progress2) { + if (this._options.ignoreStopAndKill) + return; + await this._object.killForTests(progress2); + } + async defaultUserAgentForTest() { + return { userAgent: this._object.userAgent() }; + } + async newBrowserCDPSession(params2, progress2) { + if (this._object.options.browserType !== "chromium") + throw new Error(`CDP session is only available in Chromium`); + const crBrowser = this._object; + return { session: new CDPSessionDispatcher(this, await progress2.race(crBrowser.newBrowserCDPSession())) }; + } + async startTracing(params2, progress2) { + if (this._object.options.browserType !== "chromium") + throw new Error(`Tracing is only available in Chromium`); + const crBrowser = this._object; + await progress2.race(crBrowser.startTracing(params2.page ? params2.page._object : void 0, params2)); + } + async stopTracing(params2, progress2) { + if (this._object.options.browserType !== "chromium") + throw new Error(`Tracing is only available in Chromium`); + const crBrowser = this._object; + return { artifact: ArtifactDispatcher.from(this, await progress2.race(crBrowser.stopTracing())) }; + } + async startServer(params2, progress2) { + return await this._object.startServer(progress2, params2.title, params2); + } + async stopServer(params2, progress2) { + await this._object.stopServer(progress2); + } + async cleanupContexts() { + await Promise.all(Array.from(this._isolatedContexts).map((context2) => context2.close(nullProgress, { reason: "Global context cleanup (connection terminated)" }))); + } + }; + } +}); + +// packages/playwright-core/src/server/dispatchers/browserTypeDispatcher.ts +var BrowserTypeDispatcher; +var init_browserTypeDispatcher = __esm({ + "packages/playwright-core/src/server/dispatchers/browserTypeDispatcher.ts"() { + "use strict"; + init_browserContextDispatcher(); + init_browserDispatcher(); + init_pageDispatcher(); + init_dispatcher(); + BrowserTypeDispatcher = class extends Dispatcher { + constructor(scope, browserType, denyLaunch) { + super(scope, browserType, "BrowserType", { + executablePath: browserType.executablePath(), + name: browserType.name() + }); + this._type_BrowserType = true; + this._denyLaunch = denyLaunch; + } + async launch(params2, progress2) { + if (this._denyLaunch) + throw new Error(`Launching more browsers is not allowed.`); + const browser = await this._object.launch(progress2, params2); + return { browser: new BrowserDispatcher(this, browser) }; + } + async launchPersistentContext(params2, progress2) { + if (this._denyLaunch) + throw new Error(`Launching more browsers is not allowed.`); + const browserContext = await this._object.launchPersistentContext(progress2, params2.userDataDir, params2); + const browserDispatcher = new BrowserDispatcher(this, browserContext._browser); + const contextDispatcher = BrowserContextDispatcher.from(browserDispatcher, browserContext); + return { browser: browserDispatcher, context: contextDispatcher }; + } + async connectOverCDP(params2, progress2) { + if (this._denyLaunch) + throw new Error(`Launching more browsers is not allowed.`); + const browser = await this._object.connectOverCDP(progress2, params2); + const browserDispatcher = new BrowserDispatcher(this, browser); + return { + browser: browserDispatcher, + defaultContext: browser._defaultContext ? BrowserContextDispatcher.from(browserDispatcher, browser._defaultContext) : void 0 + }; + } + async connectToWorker(params2, progress2) { + if (this._denyLaunch) + throw new Error(`Launching more browsers is not allowed.`); + const worker = await this._object.connectToWorker(progress2, params2.endpoint); + return { worker: new WorkerDispatcher(this, worker) }; + } + }; + } +}); + +// packages/playwright-core/src/server/dispatchers/electronDispatcher.ts +var ElectronDispatcher, ElectronApplicationDispatcher; +var init_electronDispatcher = __esm({ + "packages/playwright-core/src/server/dispatchers/electronDispatcher.ts"() { + "use strict"; + init_browserContextDispatcher(); + init_dispatcher(); + init_jsHandleDispatcher(); + init_electron(); + ElectronDispatcher = class extends Dispatcher { + constructor(scope, electron, denyLaunch) { + super(scope, electron, "Electron", {}); + this._type_Electron = true; + this._denyLaunch = denyLaunch; + } + async launch(params2, progress2) { + if (this._denyLaunch) + throw new Error(`Launching more browsers is not allowed.`); + const electronApplication = await this._object.launch(progress2, params2); + return { electronApplication: new ElectronApplicationDispatcher(this, electronApplication) }; + } + }; + ElectronApplicationDispatcher = class extends Dispatcher { + constructor(scope, electronApplication) { + super(scope, electronApplication, "ElectronApplication", { + context: BrowserContextDispatcher.from(scope, electronApplication.context()) + }); + this._type_EventTarget = true; + this._type_ElectronApplication = true; + this._subscriptions = /* @__PURE__ */ new Set(); + this.addObjectListener(ElectronApplication.Events.Close, () => { + this._dispatchEvent("close"); + this._dispose(); + }); + this.addObjectListener(ElectronApplication.Events.Console, (message) => { + if (!this._subscriptions.has("console")) + return; + this._dispatchEvent("console", { + type: message.type(), + text: message.text(), + args: message.args().map((a) => JSHandleDispatcher.fromJSHandle(this, a)), + location: message.location(), + timestamp: message.timestamp() + }); + }); + } + async browserWindow(params2, progress2) { + const handle = await this._object.browserWindow(progress2, params2.page.page()); + return { handle: JSHandleDispatcher.fromJSHandle(this, handle) }; + } + async evaluateExpression(params2, progress2) { + const handle = await progress2.race(this._object._nodeElectronHandlePromise); + return { value: serializeResult(await handle.evaluateExpression(progress2, params2.expression, { isFunction: params2.isFunction }, parseArgument(params2.arg))) }; + } + async evaluateExpressionHandle(params2, progress2) { + const handle = await progress2.race(this._object._nodeElectronHandlePromise); + const result2 = await handle.evaluateExpressionHandle(progress2, params2.expression, { isFunction: params2.isFunction }, parseArgument(params2.arg)); + return { handle: JSHandleDispatcher.fromJSHandle(this, result2) }; + } + async updateSubscription(params2, progress2) { + if (params2.enabled) + this._subscriptions.add(params2.event); + else + this._subscriptions.delete(params2.event); + } + }; + } +}); + +// packages/playwright-core/src/server/harBackend.ts +function countMatchingHeaders(harHeaders, headers) { + const set = new Set(headers.map((h) => h.name.toLowerCase() + ":" + h.value)); + let matches = 0; + for (const h of harHeaders) { + if (set.has(h.name.toLowerCase() + ":" + h.value)) + ++matches; + } + return matches; +} +function multipartBoundary(headers) { + const contentType = headers.find((h) => h.name.toLowerCase() === "content-type"); + if (!contentType?.value.includes("multipart/form-data")) + return void 0; + const boundary = contentType.value.match(/boundary=(\S+)/); + if (boundary) + return boundary[1]; + return void 0; +} +var import_fs38, import_path35, redirectStatus2, HarBackend; +var init_harBackend = __esm({ + "packages/playwright-core/src/server/harBackend.ts"() { + "use strict"; + import_fs38 = __toESM(require("fs")); + import_path35 = __toESM(require("path")); + init_crypto(); + init_fileUtils(); + redirectStatus2 = [301, 302, 303, 307, 308]; + HarBackend = class { + constructor(harFile, baseDir, zipFile) { + this.id = createGuid(); + this._harFile = harFile; + this._baseDir = baseDir; + this._zipFile = zipFile; + } + async lookup(url2, method, headers, postData, isNavigationRequest) { + let entry; + try { + entry = await this._harFindResponse(url2, method, headers, postData); + } catch (e) { + return { action: "error", message: "HAR error: " + e.message }; + } + if (!entry) + return { action: "noentry" }; + if (entry.request.url !== url2 && isNavigationRequest) + return { action: "redirect", redirectURL: entry.request.url }; + const response2 = entry.response; + try { + const buffer = await this._loadContent(response2.content); + return { + action: "fulfill", + status: response2.status, + headers: response2.headers, + body: buffer + }; + } catch (e) { + return { action: "error", message: e.message }; + } + } + async _loadContent(content) { + const file = content._file; + let buffer; + if (file) { + if (this._zipFile) { + buffer = await this._zipFile.read(file); + } else { + const resolved = import_path35.default.resolve(this._baseDir, file); + if (!isPathInside(this._baseDir, resolved)) + throw new Error(`HAR entry _file escapes base directory: ${file}`); + buffer = await import_fs38.default.promises.readFile(resolved); + } + } else { + buffer = Buffer.from(content.text || "", content.encoding === "base64" ? "base64" : "utf-8"); + } + return buffer; + } + async _harFindResponse(url2, method, headers, postData) { + const harLog = this._harFile.log; + const visited = /* @__PURE__ */ new Set(); + while (true) { + const entries = []; + for (const candidate of harLog.entries) { + if (candidate.request.url !== url2 || candidate.request.method !== method) + continue; + if (method === "POST" && postData && candidate.request.postData) { + const buffer = await this._loadContent(candidate.request.postData); + if (!buffer.equals(postData)) { + const boundary = multipartBoundary(headers); + if (!boundary) + continue; + const candidataBoundary = multipartBoundary(candidate.request.headers); + if (!candidataBoundary) + continue; + if (postData.toString().replaceAll(boundary, "") !== buffer.toString().replaceAll(candidataBoundary, "")) + continue; + } + } + entries.push(candidate); + } + if (!entries.length) + return; + let entry = entries[0]; + if (entries.length > 1) { + const list = []; + for (const candidate of entries) { + const matchingHeaders = countMatchingHeaders(candidate.request.headers, headers); + list.push({ candidate, matchingHeaders }); + } + list.sort((a, b) => b.matchingHeaders - a.matchingHeaders); + entry = list[0].candidate; + } + if (visited.has(entry)) + throw new Error(`Found redirect cycle for ${url2}`); + visited.add(entry); + const locationHeader = entry.response.headers.find((h) => h.name.toLowerCase() === "location"); + if (redirectStatus2.includes(entry.response.status) && locationHeader) { + const locationURL = new URL(locationHeader.value, url2); + url2 = locationURL.toString(); + if ((entry.response.status === 301 || entry.response.status === 302) && method === "POST" || entry.response.status === 303 && !["GET", "HEAD"].includes(method)) { + method = "GET"; + } + continue; + } + return entry; + } + } + dispose() { + this._zipFile?.close(); + } + }; + } +}); + +// packages/playwright-core/src/server/localUtils.ts +async function zip(progress2, stackSessions, params2) { + const promise = new ManualPromise(); + const zipFile = new yazl2.ZipFile(); + zipFile.on("error", (error) => promise.reject(error)); + const addFile = (file, name) => { + try { + if (import_fs39.default.statSync(file).isFile()) + zipFile.addFile(file, name); + } catch (e) { + } + }; + for (const entry of params2.entries) + addFile(entry.value, entry.name); + const stackSession = params2.stacksId ? stackSessions.get(params2.stacksId) : void 0; + if (stackSession?.callStacks.length) { + await progress2.race(stackSession.writer); + const buffer = Buffer.from(JSON.stringify(serializeClientSideCallMetadata(stackSession.callStacks))); + zipFile.addBuffer(buffer, "trace.stacks"); + } + if (params2.includeSources) { + const sourceFiles = new Set(params2.additionalSources); + for (const { stack } of stackSession?.callStacks || []) { + if (!stack) + continue; + for (const { file } of stack) + sourceFiles.add(file); + } + for (const sourceFile of sourceFiles) + addFile(sourceFile, "resources/src@" + calculateSha1(sourceFile) + ".txt"); + } + if (params2.mode === "write") { + await progress2.race(import_fs39.default.promises.mkdir(import_path36.default.dirname(params2.zipFile), { recursive: true })); + zipFile.end(void 0, () => { + zipFile.outputStream.pipe(import_fs39.default.createWriteStream(params2.zipFile)).on("close", () => promise.resolve()).on("error", (error) => promise.reject(error)); + }); + await progress2.race(promise); + await deleteStackSession(progress2, stackSessions, params2.stacksId); + return; + } + const tempFile = params2.zipFile + ".tmp"; + await progress2.race(import_fs39.default.promises.rename(params2.zipFile, tempFile)); + yauzl3.open(tempFile, (err, inZipFile) => { + if (err) { + promise.reject(err); + return; + } + assert(inZipFile); + inZipFile.on("error", (error) => promise.reject(error)); + let pendingEntries = inZipFile.entryCount; + const finalizeRepack = () => { + zipFile.end(void 0, () => { + zipFile.outputStream.pipe(import_fs39.default.createWriteStream(params2.zipFile)).on("close", () => { + import_fs39.default.promises.unlink(tempFile).then(() => { + promise.resolve(); + }).catch((error) => promise.reject(error)); + }).on("error", (error) => promise.reject(error)); + }); + }; + if (pendingEntries === 0) { + finalizeRepack(); + return; + } + inZipFile.on("entry", (entry) => { + inZipFile.openReadStream(entry, (err2, readStream) => { + if (err2) { + promise.reject(err2); + return; + } + zipFile.addReadStream(readStream, entry.fileName); + if (--pendingEntries === 0) + finalizeRepack(); + }); + }); + }); + await progress2.race(promise); + await deleteStackSession(progress2, stackSessions, params2.stacksId); +} +async function deleteStackSession(progress2, stackSessions, stacksId) { + const session2 = stacksId ? stackSessions.get(stacksId) : void 0; + if (!session2) + return; + await progress2.race(session2.writer); + stackSessions.delete(stacksId); + if (session2.tmpDir) + await progress2.race(removeFolders([session2.tmpDir])); +} +async function harOpen(progress2, harBackends, params2) { + let harBackend; + if (params2.file.endsWith(".zip")) { + const zipFile = new ZipFile(params2.file); + try { + const entryNames = await progress2.race(zipFile.entries()); + const harEntryName = entryNames.find((e) => e.endsWith(".har")); + if (!harEntryName) + return { error: "Specified archive does not have a .har file" }; + const har = await progress2.race(zipFile.read(harEntryName)); + const harFile = JSON.parse(har.toString()); + harBackend = new HarBackend(harFile, null, zipFile); + } catch (error) { + zipFile.close(); + throw error; + } + } else { + const harFile = JSON.parse(await progress2.race(import_fs39.default.promises.readFile(params2.file, "utf-8"))); + harBackend = new HarBackend(harFile, import_path36.default.dirname(params2.file), null); + } + harBackends.set(harBackend.id, harBackend); + return { harId: harBackend.id }; +} +async function harLookup(progress2, harBackends, params2) { + const harBackend = harBackends.get(params2.harId); + if (!harBackend) + return { action: "error", message: `Internal error: har was not opened` }; + return await progress2.race(harBackend.lookup(params2.url, params2.method, params2.headers, params2.postData, params2.isNavigationRequest)); +} +function harClose(harBackends, params2) { + const harBackend = harBackends.get(params2.harId); + if (harBackend) { + harBackends.delete(harBackend.id); + harBackend.dispose(); + } +} +async function harUnzip(progress2, params2) { + const resourcesDir = params2.resourcesDir ?? import_path36.default.dirname(params2.zipFile); + const zipFile = new ZipFile(params2.zipFile); + let resourcesDirCreated = false; + try { + for (const entry of await progress2.race(zipFile.entries())) { + const buffer = await progress2.race(zipFile.read(entry)); + if (entry === "har.har") { + await progress2.race(import_fs39.default.promises.writeFile(params2.harFile, buffer)); + } else { + if (!resourcesDirCreated) { + await progress2.race(import_fs39.default.promises.mkdir(resourcesDir, { recursive: true })); + resourcesDirCreated = true; + } + const outPath = resolveWithinRoot(resourcesDir, entry); + if (!outPath) + throw new Error(`HAR zip entry '${entry}' escapes output directory`); + await progress2.race(import_fs39.default.promises.writeFile(outPath, buffer)); + } + } + await progress2.race(import_fs39.default.promises.unlink(params2.zipFile)); + } finally { + zipFile.close(); + } +} +async function tracingStarted(progress2, stackSessions, params2) { + let tmpDir = void 0; + if (!params2.tracesDir) + tmpDir = await progress2.race(import_fs39.default.promises.mkdtemp(import_path36.default.join(import_os18.default.tmpdir(), "playwright-tracing-"))); + const traceStacksFile = import_path36.default.join(params2.tracesDir || tmpDir, params2.traceName + ".stacks"); + await progress2.race(import_fs39.default.promises.mkdir(import_path36.default.dirname(traceStacksFile), { recursive: true })); + stackSessions.set(traceStacksFile, { callStacks: [], file: traceStacksFile, writer: Promise.resolve(), tmpDir, live: params2.live }); + return { stacksId: traceStacksFile }; +} +async function traceDiscarded(progress2, stackSessions, params2) { + await deleteStackSession(progress2, stackSessions, params2.stacksId); +} +function addStackToTracingNoReply(stackSessions, params2) { + for (const session2 of stackSessions.values()) { + session2.callStacks.push(params2.callData); + if (session2.live) { + session2.writer = session2.writer.then(() => { + const buffer = Buffer.from(JSON.stringify(serializeClientSideCallMetadata(session2.callStacks))); + return import_fs39.default.promises.writeFile(session2.file, buffer); + }); + } + } +} +var import_fs39, import_os18, import_path36, yauzl3, yazl2; +var init_localUtils = __esm({ + "packages/playwright-core/src/server/localUtils.ts"() { + "use strict"; + import_fs39 = __toESM(require("fs")); + import_os18 = __toESM(require("os")); + import_path36 = __toESM(require("path")); + init_manualPromise(); + init_traceUtils(); + init_assert(); + init_crypto(); + init_zipFile(); + init_fileUtils(); + init_harBackend(); + yauzl3 = require("./utilsBundle").yauzl; + yazl2 = require("./utilsBundle").yazl; + } +}); + +// packages/playwright-core/src/server/dispatchers/jsonPipeDispatcher.ts +var JsonPipeDispatcher; +var init_jsonPipeDispatcher = __esm({ + "packages/playwright-core/src/server/dispatchers/jsonPipeDispatcher.ts"() { + "use strict"; + init_dispatcher(); + init_instrumentation(); + JsonPipeDispatcher = class extends Dispatcher { + constructor(scope) { + super(scope, new SdkObject(scope._object, "jsonPipe"), "JsonPipe", {}); + this._type_JsonPipe = true; + } + async send(params2, progress2) { + this.emit("message", params2.message); + } + async close(params2, progress2) { + this.emit("close"); + if (!this._disposed) { + this._dispatchEvent("closed", {}); + this._dispose(); + } + } + dispatch(message) { + if (!this._disposed) + this._dispatchEvent("message", { message }); + } + wasClosed(reason) { + if (!this._disposed) { + this._dispatchEvent("closed", { reason }); + this._dispose(); + } + } + dispose() { + this._dispose(); + } + }; + } +}); + +// packages/playwright-core/src/server/socksInterceptor.ts +function tChannelForSocks(names, arg, path59, context2) { + throw new ValidationError(`${path59}: channels are not expected in SocksSupport`); +} +var import_events15, SocksInterceptor; +var init_socksInterceptor = __esm({ + "packages/playwright-core/src/server/socksInterceptor.ts"() { + "use strict"; + import_events15 = __toESM(require("events")); + init_socksProxy(); + init_debug(); + init_validator(); + SocksInterceptor = class { + constructor(transport, pattern, redirectPortForTest) { + this._ids = /* @__PURE__ */ new Set(); + this._handler = new SocksProxyHandler(pattern, redirectPortForTest); + let lastId = -1; + this._channel = new Proxy(new import_events15.default(), { + get: (obj, prop) => { + if (prop in obj || obj[prop] !== void 0 || typeof prop !== "string") + return obj[prop]; + return (params2) => { + try { + const id = --lastId; + this._ids.add(id); + const validator = findValidator("SocksSupport", prop, "Params"); + params2 = validator(params2, "", { tChannelImpl: tChannelForSocks, binary: "toBase64", isUnderTest }); + transport.send({ id, guid: this._socksSupportObjectGuid, method: prop, params: params2, metadata: { stack: [], apiName: "", internal: true } }); + } catch (e) { + } + }; + } + }); + this._handler.on(SocksProxyHandler.Events.SocksConnected, (payload) => this._channel.socksConnected(payload)); + this._handler.on(SocksProxyHandler.Events.SocksData, (payload) => this._channel.socksData(payload)); + this._handler.on(SocksProxyHandler.Events.SocksError, (payload) => this._channel.socksError(payload)); + this._handler.on(SocksProxyHandler.Events.SocksFailed, (payload) => this._channel.socksFailed(payload)); + this._handler.on(SocksProxyHandler.Events.SocksEnd, (payload) => this._channel.socksEnd(payload)); + this._channel.on("socksRequested", (payload) => this._handler.socketRequested(payload)); + this._channel.on("socksClosed", (payload) => this._handler.socketClosed(payload)); + this._channel.on("socksData", (payload) => this._handler.sendSocketData(payload)); + } + cleanup() { + this._handler.cleanup(); + } + interceptMessage(message) { + if (this._ids.has(message.id)) { + this._ids.delete(message.id); + return true; + } + if (message.method === "__create__" && message.params.type === "SocksSupport") { + this._socksSupportObjectGuid = message.params.guid; + return false; + } + if (this._socksSupportObjectGuid && message.guid === this._socksSupportObjectGuid) { + const validator = findValidator("SocksSupport", message.method, "Event"); + const params2 = validator(message.params, "", { tChannelImpl: tChannelForSocks, binary: "fromBase64", isUnderTest }); + this._channel.emit(message.method, params2); + return true; + } + return false; + } + }; + } +}); + +// packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts +async function urlToWSEndpoint2(progress2, endpointURL) { + if (endpointURL.startsWith("ws")) + return endpointURL; + progress2.log(` retrieving websocket url from ${endpointURL}`); + const fetchUrl = new URL(endpointURL); + if (!fetchUrl.pathname.endsWith("/")) + fetchUrl.pathname += "/"; + fetchUrl.pathname += "json"; + const json = await fetchData(progress2, { + url: fetchUrl.toString(), + method: "GET", + headers: { "User-Agent": getUserAgent() } + }, async (params2, response2) => { + return new Error(`Unexpected status ${response2.statusCode} when connecting to ${fetchUrl.toString()}. +This does not look like a Playwright server, try connecting via ws://.`); + }); + const wsUrl = new URL(endpointURL); + let wsEndpointPath = JSON.parse(json).wsEndpointPath; + if (wsEndpointPath.startsWith("/")) + wsEndpointPath = wsEndpointPath.substring(1); + if (!wsUrl.pathname.endsWith("/")) + wsUrl.pathname += "/"; + wsUrl.pathname += wsEndpointPath; + wsUrl.protocol = wsUrl.protocol === "https:" ? "wss:" : "ws:"; + return wsUrl.toString(); +} +var import_net7, LocalUtilsDispatcher; +var init_localUtilsDispatcher = __esm({ + "packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts"() { + "use strict"; + import_net7 = __toESM(require("net")); + init_urlMatch(); + init_utils2(); + init_userAgent(); + init_dispatcher(); + init_instrumentation(); + init_localUtils(); + init_deviceDescriptors(); + init_jsonPipeDispatcher(); + init_pipeTransport2(); + init_socksInterceptor(); + init_transport(); + LocalUtilsDispatcher = class extends Dispatcher { + constructor(scope, playwright2) { + const localUtils = new SdkObject(playwright2, "localUtils", "localUtils"); + localUtils.logName = "browser"; + const deviceDescriptors2 = Object.entries(deviceDescriptors).map(([name, descriptor]) => ({ name, descriptor })); + super(scope, localUtils, "LocalUtils", { + deviceDescriptors: deviceDescriptors2 + }); + this._harBackends = /* @__PURE__ */ new Map(); + this._stackSessions = /* @__PURE__ */ new Map(); + this._type_LocalUtils = true; + } + async zip(params2, progress2) { + return await zip(progress2, this._stackSessions, params2); + } + async harOpen(params2, progress2) { + return await harOpen(progress2, this._harBackends, params2); + } + async harLookup(params2, progress2) { + return await harLookup(progress2, this._harBackends, params2); + } + async harClose(params2, progress2) { + harClose(this._harBackends, params2); + } + async harUnzip(params2, progress2) { + return await harUnzip(progress2, params2); + } + async tracingStarted(params2, progress2) { + return await tracingStarted(progress2, this._stackSessions, params2); + } + async traceDiscarded(params2, progress2) { + return await traceDiscarded(progress2, this._stackSessions, params2); + } + async addStackToTracingNoReply(params2, progress2) { + addStackToTracingNoReply(this._stackSessions, params2); + } + async connect(params2, progress2) { + if (URL.canParse(params2.endpoint)) + return await this._connectOverWebSocket(progress2, params2); + return await progress2.race(this._connectOverPipe(params2)); + } + async _connectOverWebSocket(progress2, params2) { + const wsHeaders = { + "User-Agent": getUserAgent(), + "x-playwright-proxy": params2.exposeNetwork ?? "", + ...params2.headers + }; + const wsEndpoint = await urlToWSEndpoint2(progress2, params2.endpoint); + const transport = await WebSocketTransport.connect(progress2, wsEndpoint, { headers: wsHeaders, followRedirects: true, debugLogHeader: "x-playwright-debug-log" }); + const socksInterceptor = new SocksInterceptor(transport, params2.exposeNetwork, params2.socksProxyRedirectPortForTest); + const pipe = new JsonPipeDispatcher(this); + transport.onmessage = (json) => { + if (socksInterceptor.interceptMessage(json)) + return; + const cb = () => { + try { + pipe.dispatch(json); + } catch (e) { + transport.close(); + } + }; + if (params2.slowMo) + setTimeout(cb, params2.slowMo); + else + cb(); + }; + pipe.on("message", (message) => { + transport.send(message); + }); + transport.onclose = (reason) => { + socksInterceptor?.cleanup(); + pipe.wasClosed(reason); + }; + pipe.on("close", () => transport.close()); + return { pipe, headers: transport.headers }; + } + async _connectOverPipe(params2) { + const socket = await new Promise((resolve, reject) => { + const conn = import_net7.default.connect(params2.endpoint, () => resolve(conn)); + conn.on("error", reject); + }); + const transport = new PipeTransport2(socket, socket); + const pipe = new JsonPipeDispatcher(this); + transport.onmessage = (json) => { + const cb = () => { + try { + pipe.dispatch(json); + } catch (e) { + transport.close(); + } + }; + if (params2.slowMo) + setTimeout(cb, params2.slowMo); + else + cb(); + }; + pipe.on("message", (message) => { + transport.send(message); + }); + transport.onclose = (reason) => { + pipe.wasClosed(reason); + }; + pipe.on("close", () => socket.end()); + return { pipe, headers: [] }; + } + async globToRegex(params2, progress2) { + const regex = resolveGlobToRegexPattern(params2.baseURL, params2.glob, params2.webSocketUrl); + return { regex }; + } + }; + } +}); + +// packages/playwright-core/src/server/dispatchers/playwrightDispatcher.ts +var PlaywrightDispatcher, SocksSupportDispatcher; +var init_playwrightDispatcher = __esm({ + "packages/playwright-core/src/server/dispatchers/playwrightDispatcher.ts"() { + "use strict"; + init_socksProxy(); + init_eventsHelper(); + init_fetch(); + init_androidDispatcher(); + init_androidDispatcher(); + init_browserDispatcher(); + init_browserTypeDispatcher(); + init_dispatcher(); + init_electronDispatcher(); + init_localUtilsDispatcher(); + init_networkDispatchers(); + init_instrumentation(); + PlaywrightDispatcher = class extends Dispatcher { + constructor(scope, playwright2, options = {}) { + const denyLaunch = options.denyLaunch ?? false; + const chromium = new BrowserTypeDispatcher(scope, playwright2.chromium, denyLaunch); + const firefox = new BrowserTypeDispatcher(scope, playwright2.firefox, denyLaunch); + const webkit = new BrowserTypeDispatcher(scope, playwright2.webkit, denyLaunch); + const android = new AndroidDispatcher(scope, playwright2.android, denyLaunch); + const initializer = { + chromium, + firefox, + webkit, + android, + electron: new ElectronDispatcher(scope, playwright2.electron, denyLaunch), + utils: playwright2.options.isServer ? void 0 : new LocalUtilsDispatcher(scope, playwright2), + socksSupport: options.socksProxy ? new SocksSupportDispatcher(scope, playwright2, options.socksProxy) : void 0 + }; + let browserDispatcher; + if (options.preLaunchedBrowser) { + const browserTypeDispatcher = initializer[options.preLaunchedBrowser.options.name]; + browserDispatcher = new BrowserDispatcher(browserTypeDispatcher, options.preLaunchedBrowser, { + ignoreStopAndKill: true, + isolateContexts: !options.sharedBrowser + }); + initializer.preLaunchedBrowser = browserDispatcher; + } + if (options.preLaunchedAndroidDevice) + initializer.preConnectedAndroidDevice = new AndroidDeviceDispatcher(android, options.preLaunchedAndroidDevice); + super(scope, playwright2, "Playwright", initializer); + this._type_Playwright = true; + this._browserDispatcher = browserDispatcher; + this._denyLaunch = denyLaunch; + } + async newRequest(params2, progress2) { + if (this._denyLaunch) + throw new Error(`Creating new API request contexts is not allowed.`); + const request2 = new GlobalAPIRequestContext(this._object, params2); + return { request: APIRequestContextDispatcher.from(this.parentScope(), request2) }; + } + async cleanup() { + await this._browserDispatcher?.cleanupContexts(); + } + }; + SocksSupportDispatcher = class extends Dispatcher { + constructor(scope, parent, socksProxy) { + super(scope, new SdkObject(parent, "socksSupport"), "SocksSupport", {}); + this._type_SocksSupport = true; + this._socksProxy = socksProxy; + this._socksListeners = [ + eventsHelper.addEventListener(socksProxy, SocksProxy.Events.SocksRequested, (payload) => this._dispatchEvent("socksRequested", payload)), + eventsHelper.addEventListener(socksProxy, SocksProxy.Events.SocksData, (payload) => this._dispatchEvent("socksData", payload)), + eventsHelper.addEventListener(socksProxy, SocksProxy.Events.SocksClosed, (payload) => this._dispatchEvent("socksClosed", payload)) + ]; + } + async socksConnected(params2, progress2) { + this._socksProxy?.socketConnected(params2); + } + async socksFailed(params2, progress2) { + this._socksProxy?.socketFailed(params2); + } + async socksData(params2, progress2) { + this._socksProxy?.sendSocketData(params2); + } + async socksError(params2, progress2) { + this._socksProxy?.sendSocketError(params2); + } + async socksEnd(params2, progress2) { + this._socksProxy?.sendSocketEnd(params2); + } + _onDispose() { + eventsHelper.removeEventListeners(this._socksListeners); + } + }; + } +}); + +// packages/playwright-core/src/server/trace/viewer/traceViewer.ts +function validateTraceUrlOrPath(traceFileOrUrl) { + if (!traceFileOrUrl) + return traceFileOrUrl; + if (traceFileOrUrl.startsWith("http://") || traceFileOrUrl.startsWith("https://")) + return traceFileOrUrl; + let traceFile = traceFileOrUrl; + if (traceFile.endsWith(".json")) + return toFilePathUrl(traceFile); + try { + const stat = import_fs40.default.statSync(traceFile); + if (stat.isDirectory()) + traceFile = import_path37.default.join(traceFile, tracesDirMarker); + return toFilePathUrl(traceFile); + } catch { + throw new Error(`Trace file ${traceFileOrUrl} does not exist!`); + } +} +async function startTraceViewerServer(options) { + const server = new HttpServer(libPath("vite", "traceViewer")); + const isAllowed = (filePath) => options.allowedFileRoots().some((root) => isPathInside(import_path37.default.resolve(root), filePath)); + const serveTraceDataRoute = (request2, response2, relativePath) => { + if (!relativePath.startsWith("/file")) + return false; + const url2 = new URL("http://localhost" + request2.url); + try { + const filePath = import_path37.default.resolve(url2.searchParams.get("path")); + if (!isAllowed(filePath)) { + response2.statusCode = 403; + response2.end(); + return true; + } + if (import_fs40.default.existsSync(filePath)) + return server.serveFile(request2, response2, filePath, void 0, { skipRootCheck: true }); + if (filePath.endsWith(".json")) { + const fullPrefix = filePath.substring(0, filePath.length - ".json".length); + return sendTraceDescriptor(response2, import_path37.default.dirname(fullPrefix), import_path37.default.basename(fullPrefix)); + } + if (filePath.endsWith(tracesDirMarker)) + return sendTraceDescriptor(response2, import_path37.default.dirname(filePath)); + } catch { + } + response2.statusCode = 404; + response2.end(); + return true; + }; + if (false) { + const traceViewerRoot = import_path37.default.resolve(__dirname, "..", "..", "trace-viewer"); + const devServer = await server.createViteDevServer({ root: traceViewerRoot, base: "/trace/" }); + server.routePrefix("/trace", (request2, response2) => { + const url2 = new URL("http://localhost" + request2.url); + const relativePath = url2.pathname.slice("/trace".length); + if (relativePath.startsWith("/file")) + return serveTraceDataRoute(request2, response2, relativePath); + if (relativePath === "/sw.bundle.js") + return server.serveFile(request2, response2, import_path37.default.join(libPath("vite", "traceViewer"), "sw.bundle.js")); + devServer.middlewares(request2, response2, HttpServer.notFoundFallback(response2)); + return true; + }); + } else { + server.routePrefix("/trace", (request2, response2) => { + const url2 = new URL("http://localhost" + request2.url); + const relativePath = url2.pathname.slice("/trace".length); + if (relativePath.startsWith("/file")) + return serveTraceDataRoute(request2, response2, relativePath); + const absolutePath = import_path37.default.join(libPath("vite", "traceViewer"), ...relativePath.split("/")); + return server.serveFile(request2, response2, absolutePath); + }); + } + const transport = options.transport || (options.isServer ? new StdinServer() : void 0); + if (transport) + server.createWebSocket(() => transport); + const { host, port } = options; + await server.start({ preferredPort: port, host }); + return server; +} +async function installRootRedirect(server, traceUrl, options) { + const params2 = new URLSearchParams(); + if (import_path37.default.sep !== import_path37.default.posix.sep) + params2.set("pathSeparator", import_path37.default.sep); + if (traceUrl) + params2.append("trace", traceUrl); + if (server.wsGuid()) + params2.append("ws", server.wsGuid()); + if (options?.isServer) + params2.append("isServer", ""); + if (isUnderTest()) + params2.append("isUnderTest", "true"); + for (const arg of options.args || []) + params2.append("arg", arg); + if (options.grep) + params2.append("grep", options.grep); + if (options.grepInvert) + params2.append("grepInvert", options.grepInvert); + for (const project of options.project || []) + params2.append("project", project); + for (const reporter of options.reporter || []) + params2.append("reporter", reporter); + const urlPath = `./trace/${options.webApp || "index.html"}?${params2.toString()}`; + server.routePath("/", (_, response2) => { + response2.statusCode = 302; + response2.setHeader("Location", urlPath); + response2.end(); + return true; + }); +} +async function runTraceViewerApp(traceUrl, browserName, options) { + traceUrl = validateTraceUrlOrPath(traceUrl); + const allowedFileRoots = traceFileRoots(traceUrl); + const server = await startTraceViewerServer({ ...options, allowedFileRoots: () => allowedFileRoots }); + await installRootRedirect(server, traceUrl, options); + const page = await openTraceViewerApp(server.urlPrefix("precise"), browserName, options); + page.on("close", () => gracefullyProcessExitDoNotHang(0)); + return page; +} +async function runTraceInBrowser(traceUrl, options) { + traceUrl = validateTraceUrlOrPath(traceUrl); + const allowedFileRoots = traceFileRoots(traceUrl); + const server = await startTraceViewerServer({ ...options, allowedFileRoots: () => allowedFileRoots }); + await installRootRedirect(server, traceUrl, options); + await openTraceInBrowser(server.urlPrefix("human-readable")); +} +function traceFileRoots(traceUrl) { + if (traceUrl?.startsWith("file://")) { + try { + return [import_path37.default.dirname(import_url.default.fileURLToPath(traceUrl))]; + } catch { + } + } + if (traceUrl?.startsWith("file?path=")) { + try { + const url2 = new URL(traceUrl, "http://localhost"); + return [import_path37.default.dirname(url2.searchParams.get("path"))]; + } catch { + } + } + return [process.cwd()]; +} +async function openTraceViewerApp(url2, browserName, options) { + const traceViewerPlaywright = createPlaywright({ sdkLanguage: "javascript", isInternalPlaywright: true }); + const traceViewerBrowser = isUnderTest() ? "chromium" : browserName; + const { context: context2, page } = await launchApp(traceViewerPlaywright[traceViewerBrowser], { + sdkLanguage: traceViewerPlaywright.options.sdkLanguage, + windowSize: { width: 1280, height: 800 }, + persistentContextOptions: { + ...options?.persistentContextOptions, + args: isUnderTest() ? ["--remote-debugging-port=0"] : void 0, + headless: !!options?.headless, + colorScheme: isUnderTest() ? "light" : void 0 + } + }); + const controller = new ProgressController(); + await controller.run(async (progress2) => { + await context2._browser._defaultContext.loadDefaultContextAsIs(progress2); + if (process.env.PWTEST_PRINT_WS_ENDPOINT) { + process.stderr.write("DevTools listening on: " + context2._browser.options.wsEndpoint + "\n"); + } + if (!isUnderTest()) + await progress2.race(syncLocalStorageWithSettings(page, "traceviewer")); + if (isUnderTest()) + page.on("close", () => context2.close(nullProgress, { reason: "Trace viewer closed" }).catch(() => { + })); + await page.mainFrame().goto(progress2, url2); + }); + return page; +} +async function openTraceInBrowser(url2) { + console.log("\nListening on " + url2); + if (!isUnderTest() && !isCodingAgent()) + await open2(url2.replace("0.0.0.0", "localhost")).catch(() => { + }); +} +function sendTraceDescriptor(response2, traceDir2, tracePrefix) { + response2.statusCode = 200; + response2.setHeader("Content-Type", "application/json"); + response2.end(JSON.stringify(traceDescriptor(traceDir2, tracePrefix))); + return true; +} +function traceDescriptor(traceDir2, tracePrefix) { + const result2 = { + entries: [] + }; + for (const name of import_fs40.default.readdirSync(traceDir2)) { + if (!tracePrefix || name.startsWith(tracePrefix)) + result2.entries.push({ name, path: toFilePathUrl(import_path37.default.join(traceDir2, name)) }); + } + const resourcesDir = import_path37.default.join(traceDir2, "resources"); + if (import_fs40.default.existsSync(resourcesDir)) { + for (const name of import_fs40.default.readdirSync(resourcesDir)) + result2.entries.push({ name: "resources/" + name, path: toFilePathUrl(import_path37.default.join(resourcesDir, name)) }); + } + return result2; +} +function toFilePathUrl(filePath) { + return `file?path=${encodeURIComponent(filePath)}`; +} +var import_fs40, import_path37, import_url, open2, tracesDirMarker, StdinServer; +var init_traceViewer = __esm({ + "packages/playwright-core/src/server/trace/viewer/traceViewer.ts"() { + "use strict"; + import_fs40 = __toESM(require("fs")); + import_path37 = __toESM(require("path")); + import_url = __toESM(require("url")); + init_httpServer(); + init_processLauncher(); + init_debug(); + init_env(); + init_fileUtils(); + init_package(); + init_launchApp(); + init_launchApp(); + init_playwright(); + init_progress(); + init_progress(); + open2 = require("./utilsBundle").open; + tracesDirMarker = "traces.dir"; + StdinServer = class { + constructor() { + process.stdin.on("data", (data) => { + const url2 = validateTraceUrlOrPath(data.toString().trim()); + if (!url2 || url2 === this._traceUrl) + return; + if (url2.endsWith(".json")) + this._pollLoadTrace(url2); + else + this._loadTrace(url2); + }); + process.stdin.on("close", () => gracefullyProcessExitDoNotHang(0)); + } + onconnect() { + } + async dispatch(method, params2) { + if (method === "initialize") { + if (this._traceUrl) + this._loadTrace(this._traceUrl); + } + } + onclose() { + } + _loadTrace(traceUrl) { + this._traceUrl = traceUrl; + clearTimeout(this._pollTimer); + this.sendEvent?.("loadTraceRequested", { traceUrl }); + } + _pollLoadTrace(url2) { + this._loadTrace(url2); + this._pollTimer = setTimeout(() => { + this._pollLoadTrace(url2); + }, 500); + } + }; + } +}); + +// packages/playwright-core/src/server/index.ts +var server_exports = {}; +__export(server_exports, { + Browser: () => Browser, + BrowserContext: () => BrowserContext, + DispatcherConnection: () => DispatcherConnection, + Page: () => Page, + PlaywrightDispatcher: () => PlaywrightDispatcher, + Request: () => Request, + RequestDispatcher: () => RequestDispatcher, + Response: () => Response2, + ResponseDispatcher: () => ResponseDispatcher, + RootDispatcher: () => RootDispatcher, + WebSocketTransport: () => WebSocketTransport, + createPlaywright: () => createPlaywright, + deviceDescriptors: () => deviceDescriptors, + findRepeatedSubsequencesForTest: () => findRepeatedSubsequencesForTest, + installRootRedirect: () => installRootRedirect, + nullProgress: () => nullProgress, + openTraceInBrowser: () => openTraceInBrowser, + openTraceViewerApp: () => openTraceViewerApp, + runTraceViewerApp: () => runTraceViewerApp, + setMaxDispatchersForTest: () => setMaxDispatchersForTest, + startTraceViewerServer: () => startTraceViewerServer +}); +var init_server = __esm({ + "packages/playwright-core/src/server/index.ts"() { + "use strict"; + init_browser(); + init_browserContext(); + init_callLog(); + init_deviceDescriptors(); + init_dispatcher(); + init_networkDispatchers(); + init_playwrightDispatcher(); + init_network2(); + init_page(); + init_playwright(); + init_progress(); + init_transport(); + init_traceViewer(); + } +}); + +// packages/playwright-core/src/server/dispatchers/debugControllerDispatcher.ts +var DebugControllerDispatcher; +var init_debugControllerDispatcher = __esm({ + "packages/playwright-core/src/server/dispatchers/debugControllerDispatcher.ts"() { + "use strict"; + init_eventsHelper(); + init_debugController(); + init_dispatcher(); + DebugControllerDispatcher = class extends Dispatcher { + constructor(connection, debugController) { + super(connection, debugController, "DebugController", {}); + this._type_DebugController = true; + this._listeners = [ + eventsHelper.addEventListener(this._object, DebugController.Events.StateChanged, (params2) => { + this._dispatchEvent("stateChanged", params2); + }), + eventsHelper.addEventListener(this._object, DebugController.Events.InspectRequested, ({ selector, locator: locator2, ariaSnapshot }) => { + this._dispatchEvent("inspectRequested", { selector, locator: locator2, ariaSnapshot }); + }), + eventsHelper.addEventListener(this._object, DebugController.Events.SourceChanged, ({ text: text2, header, footer, actions }) => { + this._dispatchEvent("sourceChanged", { text: text2, header, footer, actions }); + }), + eventsHelper.addEventListener(this._object, DebugController.Events.Paused, ({ paused }) => { + this._dispatchEvent("paused", { paused }); + }), + eventsHelper.addEventListener(this._object, DebugController.Events.SetModeRequested, ({ mode }) => { + this._dispatchEvent("setModeRequested", { mode }); + }) + ]; + } + async initialize(params2, progress2) { + this._object.initialize(progress2, params2.codegenId, params2.sdkLanguage); + } + async setReportStateChanged(params2, progress2) { + this._object.setReportStateChanged(progress2, params2.enabled); + } + async setRecorderMode(params2, progress2) { + await this._object.setRecorderMode(progress2, params2); + } + async highlight(params2, progress2) { + await this._object.highlight(progress2, params2); + } + async hideHighlight(params2, progress2) { + await this._object.hideHighlight(progress2); + } + async resume(params2, progress2) { + await this._object.resume(progress2); + } + async kill(params2, progress2) { + this._object.kill(progress2); + } + _onDispose() { + eventsHelper.removeEventListeners(this._listeners); + this._object.dispose(); + } + }; + } +}); + +// packages/playwright-core/src/remote/playwrightConnection.ts +var PlaywrightConnection; +var init_playwrightConnection = __esm({ + "packages/playwright-core/src/remote/playwrightConnection.ts"() { + "use strict"; + init_profiler(); + init_debugLogger(); + init_time(); + init_server(); + init_android(); + init_browser(); + init_debugControllerDispatcher(); + PlaywrightConnection = class { + constructor(semaphore, transport, controller, playwright2, initialize, id) { + this._cleanups = []; + this._transport = transport; + this._semaphore = semaphore; + this._id = id; + this._profileName = (/* @__PURE__ */ new Date()).toISOString(); + const lock2 = this._semaphore.acquire(); + this._dispatcherConnection = new DispatcherConnection(); + this._dispatcherConnection.onmessage = async (message) => { + await lock2; + if (!transport.isClosed()) { + const messageString = JSON.stringify(message); + if (debugLogger.isEnabled("server:channel")) + debugLogger.log("server:channel", `[${this._id}] ${monotonicTime() * 1e3} SEND \u25BA ${messageString}`); + if (debugLogger.isEnabled("server:metadata")) + this.logServerMetadata(message, messageString, "SEND"); + transport.send(messageString); + } + }; + transport.on("message", async (message) => { + await lock2; + const messageString = Buffer.from(message).toString(); + let jsonMessage; + try { + jsonMessage = JSON.parse(messageString); + } catch (e) { + debugLogger.log("server", `[${this._id}] failed to parse message: ${e}`); + this.close({ code: 1007, reason: "Malformed message" }); + return; + } + if (debugLogger.isEnabled("server:channel")) + debugLogger.log("server:channel", `[${this._id}] ${monotonicTime() * 1e3} \u25C0 RECV ${messageString}`); + if (debugLogger.isEnabled("server:metadata")) + this.logServerMetadata(jsonMessage, messageString, "RECV"); + this._dispatcherConnection.dispatch(jsonMessage); + }); + transport.on("close", () => this._onDisconnect()); + transport.on("error", (error) => this._onDisconnect(error)); + if (controller) { + debugLogger.log("server", `[${this._id}] engaged reuse controller mode`); + this._root = new DebugControllerDispatcher(this._dispatcherConnection, playwright2.debugController); + return; + } + this._root = new RootDispatcher(this._dispatcherConnection, async (scope, params2) => { + await startProfiling(); + const options = await initialize(); + if (options.preLaunchedBrowser) { + const browser = options.preLaunchedBrowser; + browser.options.sdkLanguage = params2.sdkLanguage; + browser.on(Browser.Events.Disconnected, () => { + this.close({ code: 1001, reason: "Browser closed" }); + }); + } + if (options.preLaunchedAndroidDevice) { + const androidDevice = options.preLaunchedAndroidDevice; + androidDevice.on(AndroidDevice.Events.Close, () => { + this.close({ code: 1001, reason: "Android device disconnected" }); + }); + } + if (options.dispose) + this._cleanups.push(options.dispose); + const dispatcher = new PlaywrightDispatcher(scope, playwright2, options); + this._cleanups.push(() => dispatcher.cleanup()); + return dispatcher; + }); + } + _onDisconnect(error) { + if (!this._onDisconnectPromise) + this._onDisconnectPromise = this._doDisconnect(error); + return this._onDisconnectPromise; + } + async _doDisconnect(error) { + debugLogger.log("server", `[${this._id}] disconnected. error: ${error}`); + await this._root.stopPendingOperations(new Error("Disconnected")).catch(() => { + }); + this._root._dispose(); + debugLogger.log("server", `[${this._id}] starting cleanup`); + for (const cleanup of this._cleanups) + await cleanup().catch(() => { + }); + await stopProfiling(this._profileName); + this._semaphore.release(); + debugLogger.log("server", `[${this._id}] finished cleanup`); + } + logServerMetadata(message, messageString, direction) { + const serverLogMetadata = { + wallTime: Date.now(), + id: message.id, + guid: message.guid, + method: message.method, + payloadSizeInBytes: Buffer.byteLength(messageString, "utf-8") + }; + debugLogger.log("server:metadata", (direction === "SEND" ? "SEND \u25BA " : "\u25C0 RECV ") + JSON.stringify(serverLogMetadata)); + } + async close(reason) { + if (!this._onDisconnectPromise) { + debugLogger.log("server", `[${this._id}] force closing connection: ${reason?.reason || ""} (${reason?.code || 0})`); + try { + this._transport.close(reason); + } catch (e) { + } + } + await this._onDisconnect(); + } + }; + } +}); + +// packages/playwright-core/src/remote/playwrightServer.ts +var playwrightServer_exports = {}; +__export(playwrightServer_exports, { + PlaywrightServer: () => PlaywrightServer +}); +function userAgentVersionMatchesErrorMessage(userAgent) { + const match = userAgent.match(/^Playwright\/(\d+\.\d+\.\d+)/); + if (!match) { + return; + } + const received = match[1].split(".").slice(0, 2).join("."); + const expected = getPlaywrightVersion(true); + if (received !== expected) { + return wrapInASCIIBox([ + `Playwright version mismatch:`, + ` - server version: v${expected}`, + ` - client version: v${received}`, + ``, + `If you are using VSCode extension, restart VSCode.`, + ``, + `If you are connecting to a remote service,`, + `keep your local Playwright version in sync`, + `with the remote service version.`, + ``, + `<3 Playwright Team` + ].join("\n"), 1); + } +} +function launchOptionsHash(options) { + const copy = { ...options }; + for (const k of Object.keys(copy)) { + const key = k; + if (copy[key] === defaultLaunchOptions[key]) + delete copy[key]; + } + for (const key of optionsThatAllowBrowserReuse) + delete copy[key]; + return JSON.stringify(copy); +} +function filterLaunchOptions(options, allowUnsafe) { + return { + channel: options.channel, + args: allowUnsafe ? options.args : void 0, + ignoreAllDefaultArgs: allowUnsafe ? options.ignoreAllDefaultArgs : void 0, + ignoreDefaultArgs: allowUnsafe ? options.ignoreDefaultArgs : void 0, + timeout: options.timeout, + headless: options.headless, + proxy: options.proxy, + chromiumSandbox: allowUnsafe ? options.chromiumSandbox : void 0, + firefoxUserPrefs: isUnderTest() || allowUnsafe ? options.firefoxUserPrefs : void 0, + slowMo: options.slowMo, + executablePath: isUnderTest() || allowUnsafe ? options.executablePath : void 0, + downloadsPath: allowUnsafe ? options.downloadsPath : void 0, + artifactsDir: isUnderTest() || allowUnsafe ? options.artifactsDir : void 0 + }; +} +var PlaywrightServer, defaultLaunchOptions, optionsThatAllowBrowserReuse; +var init_playwrightServer = __esm({ + "packages/playwright-core/src/remote/playwrightServer.ts"() { + "use strict"; + init_semaphore(); + init_time(); + init_wsServer(); + init_ascii(); + init_socksProxy(); + init_debugLogger(); + init_debug(); + init_userAgent(); + init_playwrightConnection(); + init_serverTransport(); + init_playwright(); + init_browser(); + init_progress(); + PlaywrightServer = class { + constructor(options) { + this._dontReuseBrowsers = /* @__PURE__ */ new Set(); + this._options = options; + if (options.preLaunchedBrowser) { + this._playwright = options.preLaunchedBrowser.attribution.playwright; + this._dontReuse(options.preLaunchedBrowser); + } + if (options.preLaunchedAndroidDevice) + this._playwright = options.preLaunchedAndroidDevice._android.attribution.playwright; + this._playwright ??= createPlaywright({ sdkLanguage: "javascript", isServer: true }); + const browserSemaphore = new Semaphore(this._options.maxConnections); + const controllerSemaphore = new Semaphore(1); + const reuseBrowserSemaphore = new Semaphore(1); + this._wsServer = new WSServer({ + onRequest: (request2, response2) => { + if (request2.method === "GET" && request2.url === "/json") { + response2.setHeader("Content-Type", "application/json"); + response2.end(JSON.stringify({ + wsEndpointPath: this._options.path + })); + return; + } + response2.end("Running"); + }, + onUpgrade: (request2, socket) => { + const uaError = userAgentVersionMatchesErrorMessage(request2.headers["user-agent"] || ""); + if (uaError) + return { error: `HTTP/${request2.httpVersion} 428 Precondition Required\r +\r +${uaError}` }; + }, + onHeaders: (headers) => { + if (process.env.PWTEST_SERVER_WS_HEADERS) + headers.push(process.env.PWTEST_SERVER_WS_HEADERS); + }, + onConnection: (request2, url2, ws4, id) => { + const browserHeader = request2.headers["x-playwright-browser"]; + const browserName = url2.searchParams.get("browser") || (Array.isArray(browserHeader) ? browserHeader[0] : browserHeader) || null; + const proxyHeader = request2.headers["x-playwright-proxy"]; + const proxyValue = url2.searchParams.get("proxy") || (Array.isArray(proxyHeader) ? proxyHeader[0] : proxyHeader); + const launchOptionsHeader = request2.headers["x-playwright-launch-options"] || ""; + const launchOptionsHeaderValue = Array.isArray(launchOptionsHeader) ? launchOptionsHeader[0] : launchOptionsHeader; + const launchOptionsParam = url2.searchParams.get("launch-options"); + let launchOptions = { timeout: DEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT }; + try { + launchOptions = JSON.parse(launchOptionsParam || launchOptionsHeaderValue); + if (!launchOptions.timeout) + launchOptions.timeout = DEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT; + } catch (e) { + } + const isExtension = this._options.mode === "extension"; + launchOptions = filterLaunchOptions(launchOptions, isExtension || !!this._options.unsafe); + if (this._options.artifactsDir) + launchOptions.artifactsDir = this._options.artifactsDir; + if (isExtension) { + const connectFilter = url2.searchParams.get("connect"); + if (connectFilter) { + if (connectFilter !== "first") + throw new Error(`Unknown connect filter: ${connectFilter}`); + return new PlaywrightConnection( + browserSemaphore, + new WebSocketServerTransport(ws4), + false, + this._playwright, + () => this._initConnectMode(id, connectFilter, browserName, launchOptions), + id + ); + } + if (url2.searchParams.has("debug-controller")) { + return new PlaywrightConnection( + controllerSemaphore, + new WebSocketServerTransport(ws4), + true, + this._playwright, + async () => { + throw new Error("shouldnt be used"); + }, + id + ); + } + return new PlaywrightConnection( + reuseBrowserSemaphore, + new WebSocketServerTransport(ws4), + false, + this._playwright, + () => this._initReuseBrowsersMode(browserName, launchOptions, id), + id + ); + } + if (this._options.mode === "launchServer" || this._options.mode === "launchServerShared") { + if (this._options.preLaunchedBrowser) { + return new PlaywrightConnection( + browserSemaphore, + new WebSocketServerTransport(ws4), + false, + this._playwright, + () => this._initPreLaunchedBrowserMode(id), + id + ); + } + return new PlaywrightConnection( + browserSemaphore, + new WebSocketServerTransport(ws4), + false, + this._playwright, + () => this._initPreLaunchedAndroidMode(id), + id + ); + } + return new PlaywrightConnection( + browserSemaphore, + new WebSocketServerTransport(ws4), + false, + this._playwright, + () => this._initLaunchBrowserMode(browserName, proxyValue, launchOptions, id), + id + ); + } + }); + } + async _initReuseBrowsersMode(browserName, launchOptions, id) { + debugLogger.log("server", `[${id}] engaged reuse browsers mode for ${browserName}`); + const requestedOptions = launchOptionsHash(launchOptions); + let browser = this._playwright.allBrowsers().find((b) => { + if (b.options.name !== browserName) + return false; + if (this._dontReuseBrowsers.has(b)) + return false; + const existingOptions = launchOptionsHash({ ...b.options.originalLaunchOptions, timeout: DEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT }); + return existingOptions === requestedOptions; + }); + for (const b of this._playwright.allBrowsers()) { + if (b === browser) + continue; + if (this._dontReuseBrowsers.has(b)) + continue; + if (b.options.name === browserName && b.options.channel === launchOptions.channel) + await b.close(nullProgress, { reason: "Connection terminated" }); + } + if (!browser) { + const browserType = this._playwright[browserName || "chromium"]; + const controller = new ProgressController(); + browser = await controller.run((progress2) => browserType.launch(progress2, { + ...launchOptions, + headless: !!process.env.PW_DEBUG_CONTROLLER_HEADLESS + }), launchOptions.timeout); + } + return { + preLaunchedBrowser: browser, + denyLaunch: true, + dispose: async () => { + for (const context2 of browser.contexts()) { + if (!context2.pages().length) + await context2.close(nullProgress, { reason: "Connection terminated" }); + } + } + }; + } + async _initConnectMode(id, filter, browserName, launchOptions) { + browserName ??= "chromium"; + debugLogger.log("server", `[${id}] engaged connect mode`); + let browser = this._playwright.allBrowsers().find((b) => b.options.name === browserName); + if (!browser) { + const browserType = this._playwright[browserName]; + const controller = new ProgressController(); + browser = await controller.run((progress2) => browserType.launch(progress2, launchOptions), launchOptions.timeout); + this._dontReuse(browser); + } + return { + preLaunchedBrowser: browser, + denyLaunch: true, + sharedBrowser: true + }; + } + async _initPreLaunchedBrowserMode(id) { + debugLogger.log("server", `[${id}] engaged pre-launched (browser) mode`); + const browser = this._options.preLaunchedBrowser; + for (const b of this._playwright.allBrowsers()) { + if (b !== browser) + await b.close(nullProgress, { reason: "Connection terminated" }); + } + return { + preLaunchedBrowser: browser, + socksProxy: this._options.preLaunchedSocksProxy, + sharedBrowser: this._options.mode === "launchServerShared", + denyLaunch: true + }; + } + async _initPreLaunchedAndroidMode(id) { + debugLogger.log("server", `[${id}] engaged pre-launched (Android) mode`); + const androidDevice = this._options.preLaunchedAndroidDevice; + return { + preLaunchedAndroidDevice: androidDevice, + denyLaunch: true + }; + } + async _initLaunchBrowserMode(browserName, proxyValue, launchOptions, id) { + debugLogger.log("server", `[${id}] engaged launch mode for "${browserName}"`); + let socksProxy; + if (proxyValue) { + socksProxy = new SocksProxy(); + socksProxy.setPattern(proxyValue); + launchOptions.socksProxyPort = await socksProxy.listen(0); + debugLogger.log("server", `[${id}] started socks proxy on port ${launchOptions.socksProxyPort}`); + } else { + launchOptions.socksProxyPort = void 0; + } + const browserType = this._playwright[browserName]; + const controller = new ProgressController(); + const browser = await controller.run((progress2) => browserType.launch(progress2, launchOptions), launchOptions.timeout); + this._dontReuseBrowsers.add(browser); + return { + preLaunchedBrowser: browser, + socksProxy, + denyLaunch: true, + dispose: async () => { + await browser.close(nullProgress, { reason: "Connection terminated" }); + socksProxy?.close(); + } + }; + } + _dontReuse(browser) { + this._dontReuseBrowsers.add(browser); + browser.on(Browser.Events.Disconnected, () => { + this._dontReuseBrowsers.delete(browser); + }); + } + async listen(port = 0, hostname) { + return this._wsServer.listen(port, hostname, this._options.path); + } + async close() { + await this._wsServer.close(); + for (const browser of this._playwright.allBrowsers()) + await browser.close(nullProgress, { reason: "Server closed" }); + } + }; + defaultLaunchOptions = { + ignoreAllDefaultArgs: false, + handleSIGINT: false, + handleSIGTERM: false, + handleSIGHUP: false, + headless: true + }; + optionsThatAllowBrowserReuse = [ + "headless", + "timeout", + "tracesDir", + "artifactsDir" + ]; + } +}); + +// packages/playwright-core/src/androidServerImpl.ts +var import_events16, AndroidServerLauncherImpl; +var init_androidServerImpl = __esm({ + "packages/playwright-core/src/androidServerImpl.ts"() { + "use strict"; + import_events16 = __toESM(require("events")); + init_crypto(); + init_playwrightServer(); + init_playwright(); + init_progress(); + AndroidServerLauncherImpl = class { + async launchServer(options = {}) { + const playwright2 = createPlaywright({ sdkLanguage: "javascript", isServer: true }); + const controller = new ProgressController(); + let devices = await controller.run((progress2) => playwright2.android.devices(progress2, { + host: options.adbHost, + port: options.adbPort, + omitDriverInstall: options.omitDriverInstall + })); + if (devices.length === 0) + throw new Error("No devices found"); + if (options.deviceSerialNumber) { + devices = devices.filter((d) => d.serial === options.deviceSerialNumber); + if (devices.length === 0) + throw new Error(`No device with serial number '${options.deviceSerialNumber}' was found`); + } + if (devices.length > 1) + throw new Error(`More than one device found. Please specify deviceSerialNumber`); + const device = devices[0]; + const path59 = options.wsPath ? options.wsPath.startsWith("/") ? options.wsPath : `/${options.wsPath}` : `/${createGuid()}`; + const server = new PlaywrightServer({ mode: "launchServer", path: path59, maxConnections: 1, preLaunchedAndroidDevice: device }); + const wsEndpoint = await server.listen(options.port, options.host); + const browserServer = new import_events16.default(); + browserServer.wsEndpoint = () => wsEndpoint; + browserServer.close = () => device.close(nullProgress); + browserServer.kill = () => device.close(nullProgress); + device.on("close", () => { + server.close(); + browserServer.emit("close"); + }); + return browserServer; + } + }; + } +}); + +// packages/playwright-core/src/browserServerImpl.ts +function toProtocolLogger(logger) { + return logger ? (direction, message) => { + if (logger.isEnabled("protocol", "verbose")) + logger.log("protocol", "verbose", (direction === "send" ? "SEND \u25BA " : "\u25C0 RECV ") + JSON.stringify(message), [], {}); + } : void 0; +} +function envObjectToArray(env) { + const result2 = []; + for (const name in env) { + if (!Object.is(env[name], void 0)) + result2.push({ name, value: String(env[name]) }); + } + return result2; +} +var import_events17, BrowserServerLauncherImpl; +var init_browserServerImpl = __esm({ + "packages/playwright-core/src/browserServerImpl.ts"() { + "use strict"; + import_events17 = __toESM(require("events")); + init_crypto(); + init_debug(); + init_stackTrace(); + init_time(); + init_playwrightServer(); + init_helper(); + init_playwright(); + init_validatorPrimitives(); + init_progress(); + BrowserServerLauncherImpl = class { + constructor(browserName) { + this._browserName = browserName; + } + async launchServer(options = {}) { + const playwright2 = createPlaywright({ sdkLanguage: "javascript", isServer: true }); + const metadata = { id: "", startTime: 0, endTime: 0, type: "Internal", method: "", params: {}, log: [], internal: true }; + const validatorContext = { + tChannelImpl: (names, arg, path60) => { + throw new ValidationError(`${path60}: channels are not expected in launchServer`); + }, + binary: "buffer", + isUnderTest + }; + let launchOptions = { + ...options, + ignoreDefaultArgs: Array.isArray(options.ignoreDefaultArgs) ? options.ignoreDefaultArgs : void 0, + ignoreAllDefaultArgs: !!options.ignoreDefaultArgs && !Array.isArray(options.ignoreDefaultArgs), + env: options.env ? envObjectToArray(options.env) : void 0, + timeout: options.timeout ?? DEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT + }; + let browser; + try { + const controller = new ProgressController(metadata); + browser = await controller.run(async (progress2) => { + if (options._userDataDir !== void 0) { + const validator = scheme["BrowserTypeLaunchPersistentContextParams"]; + launchOptions = validator({ ...launchOptions, userDataDir: options._userDataDir }, "", validatorContext); + const context2 = await playwright2[this._browserName].launchPersistentContext(progress2, options._userDataDir, launchOptions); + return context2._browser; + } else { + const validator = scheme["BrowserTypeLaunchParams"]; + launchOptions = validator(launchOptions, "", validatorContext); + return await playwright2[this._browserName].launch(progress2, launchOptions, toProtocolLogger(options.logger)); + } + }); + } catch (e) { + const log2 = helper.formatBrowserLogs(metadata.log); + rewriteErrorMessage(e, `${e.message} Failed to launch browser.${log2}`); + throw e; + } + const path59 = options.wsPath ? options.wsPath.startsWith("/") ? options.wsPath : `/${options.wsPath}` : `/${createGuid()}`; + const server = new PlaywrightServer({ mode: options._sharedBrowser ? "launchServerShared" : "launchServer", path: path59, maxConnections: Infinity, preLaunchedBrowser: browser }); + const wsEndpoint = await server.listen(options.port, options.host); + const browserServer = new import_events17.default(); + browserServer.process = () => browser.options.browserProcess.process; + browserServer.wsEndpoint = () => wsEndpoint; + browserServer.close = () => browser.options.browserProcess.close(); + browserServer[Symbol.asyncDispose] = browserServer.close; + browserServer.kill = () => browser.options.browserProcess.kill(); + browserServer._disconnectForTest = () => server.close(); + browserServer._userDataDirForTest = browser._userDataDirForTest; + browser.options.browserProcess.onclose = (exitCode, signal) => { + server.close(); + browserServer.emit("close", exitCode, signal); + }; + return browserServer; + } + }; + } +}); + +// packages/playwright-core/src/client/clientStackTrace.ts +function captureLibraryStackTrace(platform) { + const stack = captureRawStack(); + let parsedFrames = stack.map((line) => { + const frame = parseStackFrame(line, platform.pathSeparator, platform.showInternalStackFrames()); + if (!frame || !frame.file) + return null; + const isPlaywrightLibrary = !!platform.coreDir && frame.file.startsWith(platform.coreDir); + const parsed = { + frame, + frameText: line, + isPlaywrightLibrary + }; + return parsed; + }).filter(Boolean); + let apiName = ""; + for (let i = 0; i < parsedFrames.length - 1; i++) { + const parsedFrame = parsedFrames[i]; + if (parsedFrame.isPlaywrightLibrary && !parsedFrames[i + 1].isPlaywrightLibrary) { + apiName = apiName || normalizeAPIName(parsedFrame.frame.function); + break; + } + } + function normalizeAPIName(name) { + if (!name) + return ""; + const match = name.match(/(API|JS|CDP|[A-Z])([^\d]+)\d?\.(.*)/); + if (!match) + return name; + return match[1].toLowerCase() + match[2] + "." + match[3]; + } + const filterPrefixes = platform.boxedStackPrefixes(); + parsedFrames = parsedFrames.filter((f) => { + if (filterPrefixes.some((prefix) => f.frame.file.startsWith(prefix))) + return false; + return true; + }); + return { + frames: parsedFrames.map((p) => p.frame), + apiName + }; +} +var init_clientStackTrace = __esm({ + "packages/playwright-core/src/client/clientStackTrace.ts"() { + "use strict"; + init_stackTrace(); + } +}); + +// packages/playwright-core/src/client/channelOwner.ts +function logApiCall(platform, logger, message) { + if (logger && logger.isEnabled("api", "info")) + logger.log("api", "info", message, [], { color: "cyan" }); + platform.log("api", message); +} +function tChannelImplToWire(names, arg, path59, context2) { + if (arg._object instanceof ChannelOwner && (names === "*" || names.includes(arg._object._type))) + return { guid: arg._object._guid }; + throw new ValidationError(`${path59}: expected channel ${names.toString()}`); +} +var ChannelOwner; +var init_channelOwner = __esm({ + "packages/playwright-core/src/client/channelOwner.ts"() { + "use strict"; + init_protocolMetainfo(); + init_stackTrace(); + init_eventEmitter(); + init_validator(); + init_clientStackTrace(); + ChannelOwner = class _ChannelOwner extends EventEmitter3 { + constructor(parent, type3, guid, initializer) { + const connection = parent instanceof _ChannelOwner ? parent._connection : parent; + super(connection._platform); + this._objects = /* @__PURE__ */ new Map(); + this._eventToSubscriptionMapping = /* @__PURE__ */ new Map(); + this._wasCollected = false; + this.setMaxListeners(0); + this._connection = connection; + this._type = type3; + this._guid = guid; + this._parent = parent instanceof _ChannelOwner ? parent : void 0; + this._instrumentation = this._connection._instrumentation; + this._connection._objects.set(guid, this); + if (this._parent) { + this._parent._objects.set(guid, this); + this._logger = this._parent._logger; + } + this._channel = this._createChannel(new EventEmitter3(connection._platform)); + this._initializer = initializer; + } + _setEventToSubscriptionMapping(mapping) { + this._eventToSubscriptionMapping = mapping; + } + _updateSubscription(event, enabled) { + const protocolEvent = this._eventToSubscriptionMapping.get(String(event)); + if (protocolEvent) + this._channel.updateSubscription({ event: protocolEvent, enabled }).catch(() => { + }); + } + on(event, listener) { + if (!this.listenerCount(event)) + this._updateSubscription(event, true); + super.on(event, listener); + return this; + } + addListener(event, listener) { + if (!this.listenerCount(event)) + this._updateSubscription(event, true); + super.addListener(event, listener); + return this; + } + prependListener(event, listener) { + if (!this.listenerCount(event)) + this._updateSubscription(event, true); + super.prependListener(event, listener); + return this; + } + off(event, listener) { + super.off(event, listener); + if (!this.listenerCount(event)) + this._updateSubscription(event, false); + return this; + } + removeListener(event, listener) { + super.removeListener(event, listener); + if (!this.listenerCount(event)) + this._updateSubscription(event, false); + return this; + } + _adopt(child) { + child._parent._objects.delete(child._guid); + this._objects.set(child._guid, child); + child._parent = this; + } + _dispose(reason) { + if (this._parent) + this._parent._objects.delete(this._guid); + this._connection._objects.delete(this._guid); + this._wasCollected = reason === "gc"; + for (const object of [...this._objects.values()]) + object._dispose(reason); + this._objects.clear(); + } + _debugScopeState() { + return { + _guid: this._guid, + objects: Array.from(this._objects.values()).map((o) => o._debugScopeState()) + }; + } + _validatorToWireContext() { + return { + tChannelImpl: tChannelImplToWire, + binary: this._connection.rawBuffers() ? "buffer" : "toBase64", + isUnderTest: () => this._platform.isUnderTest() + }; + } + _createChannel(base) { + const channel = new Proxy(base, { + get: (obj, prop) => { + if (typeof prop === "string") { + const validator = maybeFindValidator(this._type, prop, "Params"); + const { internal } = getMetainfo({ type: this._type, method: prop }) || {}; + if (validator) { + return async (params2) => { + return await this._wrapApiCall(async (apiZone) => { + const validatedParams = validator(params2, "", this._validatorToWireContext()); + if (!apiZone.internal && !apiZone.reported) { + apiZone.reported = true; + this._instrumentation.onApiCallBegin(apiZone, { type: this._type, method: prop, params: params2 }); + logApiCall(this._platform, this._logger, `=> ${apiZone.apiName} started`); + return await this._connection.sendMessageToServer(this, prop, validatedParams, apiZone); + } + return await this._connection.sendMessageToServer(this, prop, validatedParams, { internal: true }); + }, { internal }); + }; + } + } + return obj[prop]; + } + }); + channel._object = this; + return channel; + } + async _wrapApiCall(func, options) { + const logger = this._logger; + const existingApiZone = this._platform.zones.current().data(); + if (existingApiZone) + return await func(existingApiZone); + const stackTrace = captureLibraryStackTrace(this._platform); + const apiZone = { title: options?.title, apiName: stackTrace.apiName, frames: stackTrace.frames, internal: options?.internal ?? false, reported: false, userData: void 0, stepId: void 0 }; + try { + const result2 = await this._platform.zones.current().push(apiZone).run(async () => await func(apiZone)); + if (!options?.internal) { + logApiCall(this._platform, logger, `<= ${apiZone.apiName} succeeded`); + this._instrumentation.onApiCallEnd(apiZone); + } + return result2; + } catch (e) { + const innerError = (this._platform.showInternalStackFrames() || this._platform.isUnderTest()) && e.stack ? "\n\n" + e.stack : ""; + if (apiZone.apiName && !apiZone.apiName.includes("")) + e.message = apiZone.apiName + ": " + e.message; + const stackFrames = "\n" + stringifyStackFrames(stackTrace.frames).join("\n") + innerError; + if (stackFrames.trim()) + e.stack = e.message + stackFrames; + else + e.stack = ""; + if (!options?.internal) { + apiZone.error = e; + logApiCall(this._platform, logger, `<= ${apiZone.apiName} failed`); + this._instrumentation.onApiCallEnd(apiZone); + } + throw e; + } + } + toJSON() { + return { + _type: this._type, + _guid: this._guid + }; + } + }; + } +}); + +// packages/playwright-core/src/client/cdpSession.ts +var CDPSession2; +var init_cdpSession = __esm({ + "packages/playwright-core/src/client/cdpSession.ts"() { + "use strict"; + init_channelOwner(); + CDPSession2 = class extends ChannelOwner { + static from(cdpSession) { + return cdpSession._object; + } + constructor(parent, type3, guid, initializer) { + super(parent, type3, guid, initializer); + this._channel.on("event", (event) => { + this.emit(event.method, event.params); + this.emit("event", event); + }); + this._channel.on("close", () => { + this.emit("close", this); + }); + this.on = super.on; + this.addListener = super.addListener; + this.off = super.removeListener; + this.removeListener = super.removeListener; + this.once = super.once; + } + async send(method, params2) { + const result2 = await this._channel.send({ method, params: params2 }); + return result2.result; + } + async detach() { + return await this._channel.detach(); + } + }; + } +}); + +// packages/playwright-core/src/client/clientHelper.ts +function envObjectToArray2(env) { + const result2 = []; + for (const name in env) { + if (!Object.is(env[name], void 0)) + result2.push({ name, value: String(env[name]) }); + } + return result2; +} +async function evaluationScript(platform, fun, arg, addSourceUrl = true) { + if (typeof fun === "function") { + const source11 = fun.toString(); + const argString = Object.is(arg, void 0) ? "undefined" : JSON.stringify(arg); + return `(${source11})(${argString})`; + } + if (arg !== void 0) + throw new Error("Cannot evaluate a string with arguments"); + if (isString(fun)) + return fun; + if (fun.content !== void 0) + return fun.content; + if (fun.path !== void 0) { + let source11 = await platform.fs().promises.readFile(fun.path, "utf8"); + if (addSourceUrl) + source11 = addSourceUrlToScript(source11, fun.path); + return source11; + } + throw new Error("Either path or content property must be present"); +} +function addSourceUrlToScript(source11, path59) { + return `${source11} +//# sourceURL=${path59.replace(/\n/g, "")}`; +} +var init_clientHelper = __esm({ + "packages/playwright-core/src/client/clientHelper.ts"() { + "use strict"; + init_rtti(); + } +}); + +// packages/playwright-core/src/client/clock.ts +function parseTime2(time) { + if (typeof time === "number") + return { timeNumber: time }; + if (typeof time === "string") + return { timeString: time }; + if (!isFinite(time.getTime())) + throw new Error(`Invalid date: ${time}`); + return { timeNumber: time.getTime() }; +} +function parseTicks2(ticks) { + return { + ticksNumber: typeof ticks === "number" ? ticks : void 0, + ticksString: typeof ticks === "string" ? ticks : void 0 + }; +} +var Clock2; +var init_clock2 = __esm({ + "packages/playwright-core/src/client/clock.ts"() { + "use strict"; + Clock2 = class { + constructor(browserContext) { + this._browserContext = browserContext; + } + async install(options = {}) { + await this._browserContext._channel.clockInstall(options.time !== void 0 ? parseTime2(options.time) : {}); + } + async fastForward(ticks) { + await this._browserContext._channel.clockFastForward(parseTicks2(ticks)); + } + async pauseAt(time) { + await this._browserContext._channel.clockPauseAt(parseTime2(time)); + } + async resume() { + await this._browserContext._channel.clockResume({}); + } + async runFor(ticks) { + await this._browserContext._channel.clockRunFor(parseTicks2(ticks)); + } + async setFixedTime(time) { + await this._browserContext._channel.clockSetFixedTime(parseTime2(time)); + } + async setSystemTime(time) { + await this._browserContext._channel.clockSetSystemTime(parseTime2(time)); + } + }; + } +}); + +// packages/playwright-core/src/client/errors.ts +function isTargetClosedError2(error) { + return error instanceof TargetClosedError2; +} +function serializeError2(e) { + if (isError(e)) + return { error: { message: e.message, stack: e.stack, name: e.name } }; + return { value: serializeValue(e, (value2) => ({ fallThrough: value2 })) }; +} +function parseError2(error) { + if (!error.error) { + if (error.value === void 0) + throw new Error("Serialized error must have either an error or a value"); + return parseSerializedValue(error.value, void 0); + } + let e; + if (error.error.name === "TimeoutError") + e = new TimeoutError2(error.error.message); + else if (error.error.name === "TargetClosedError") + e = new TargetClosedError2(error.error.message); + else + e = Object.assign(new PlaywrightError(error.error.message), { name: error.error.name }); + e.stack = error.error.stack || ""; + return e; +} +var PlaywrightError, TimeoutError2, TargetClosedError2; +var init_errors2 = __esm({ + "packages/playwright-core/src/client/errors.ts"() { + "use strict"; + init_rtti(); + init_serializers(); + PlaywrightError = class extends Error { + constructor() { + super(...arguments); + this.log = []; + } + // As declared in the protocol. + }; + TimeoutError2 = class extends PlaywrightError { + constructor(message) { + super(message); + this.name = "TimeoutError"; + } + }; + TargetClosedError2 = class extends PlaywrightError { + constructor(cause) { + super(cause || "Target page, context or browser has been closed"); + } + }; + } +}); + +// packages/playwright-core/src/client/jsHandle.ts +function serializeArgument(arg) { + const handles = []; + const pushHandle = (channel) => { + handles.push(channel); + return handles.length - 1; + }; + const value2 = serializeValue(arg, (value3) => { + if (value3 instanceof JSHandle2) + return { h: pushHandle(value3._channel) }; + return { fallThrough: value3 }; + }); + return { value: value2, handles }; +} +function parseResult(value2) { + return parseSerializedValue(value2, void 0); +} +function assertMaxArguments(count, max) { + if (count > max) + throw new Error("Too many arguments. If you need to pass more than 1 argument to the function wrap them in an object."); +} +var JSHandle2; +var init_jsHandle = __esm({ + "packages/playwright-core/src/client/jsHandle.ts"() { + "use strict"; + init_channelOwner(); + init_errors2(); + init_serializers(); + JSHandle2 = class _JSHandle extends ChannelOwner { + static from(handle) { + return handle._object; + } + constructor(parent, type3, guid, initializer) { + super(parent, type3, guid, initializer); + this._preview = this._initializer.preview; + this._channel.on("previewUpdated", ({ preview }) => this._preview = preview); + } + async evaluate(pageFunction, arg) { + const result2 = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: serializeArgument(arg) }); + return parseResult(result2.value); + } + async evaluateHandle(pageFunction, arg) { + const result2 = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: serializeArgument(arg) }); + return _JSHandle.from(result2.handle); + } + async getProperty(propertyName) { + const result2 = await this._channel.getProperty({ name: propertyName }); + return _JSHandle.from(result2.handle); + } + async getProperties() { + const map = /* @__PURE__ */ new Map(); + for (const { name, value: value2 } of (await this._channel.getPropertyList()).properties) + map.set(name, _JSHandle.from(value2)); + return map; + } + async jsonValue() { + return parseResult((await this._channel.jsonValue()).value); + } + asElement() { + return null; + } + async [Symbol.asyncDispose]() { + await this.dispose(); + } + async dispose() { + try { + await this._channel.dispose(); + } catch (e) { + if (isTargetClosedError2(e)) + return; + throw e; + } + } + toString() { + return this._preview; + } + }; + } +}); + +// packages/playwright-core/src/client/consoleMessage.ts +var ConsoleMessage2; +var init_consoleMessage = __esm({ + "packages/playwright-core/src/client/consoleMessage.ts"() { + "use strict"; + init_jsHandle(); + ConsoleMessage2 = class { + constructor(platform, event, page, worker) { + this._page = page; + this._worker = worker; + this._event = event; + if (platform.inspectCustom) + this[platform.inspectCustom] = () => this._inspect(); + } + worker() { + return this._worker; + } + page() { + return this._page; + } + type() { + return this._event.type; + } + text() { + return this._event.text; + } + args() { + return this._event.args.map(JSHandle2.from); + } + location() { + const { url: url2, lineNumber, columnNumber } = this._event.location; + return { url: url2, line: lineNumber, column: columnNumber, lineNumber, columnNumber }; + } + timestamp() { + return this._event.timestamp; + } + _inspect() { + return this.text(); + } + }; + } +}); + +// packages/playwright-core/src/client/credentials.ts +var Credentials2; +var init_credentials2 = __esm({ + "packages/playwright-core/src/client/credentials.ts"() { + "use strict"; + Credentials2 = class { + constructor(browserContext) { + this._browserContext = browserContext; + } + async install() { + await this._browserContext._channel.credentialsInstall({}); + } + async create(rpId, options = {}) { + const { credential } = await this._browserContext._channel.credentialsCreate({ ...options, rpId }); + return credential; + } + async get(options = {}) { + const { credentials } = await this._browserContext._channel.credentialsGet(options); + return credentials; + } + async delete(id) { + await this._browserContext._channel.credentialsDelete({ id }); + } + }; + } +}); + +// packages/playwright-core/src/client/events.ts +var Events; +var init_events = __esm({ + "packages/playwright-core/src/client/events.ts"() { + "use strict"; + Events = { + AndroidDevice: { + WebView: "webview", + Close: "close" + }, + AndroidSocket: { + Data: "data", + Close: "close" + }, + AndroidWebView: { + Close: "close" + }, + Browser: { + Context: "context", + Disconnected: "disconnected" + }, + Debugger: { + PausedStateChanged: "pausedstatechanged" + }, + BrowserContext: { + Console: "console", + Close: "close", + Dialog: "dialog", + Download: "download", + FrameAttached: "frameattached", + FrameDetached: "framedetached", + FrameNavigated: "framenavigated", + Page: "page", + PageClose: "pageclose", + PageLoad: "pageload", + // Can't use just 'error' due to node.js special treatment of error events. + // @see https://nodejs.org/api/events.html#events_error_events + WebError: "weberror", + BackgroundPage: "backgroundpage", + // Deprecated in v1.56, never emitted anymore. + ServiceWorker: "serviceworker", + Request: "request", + Response: "response", + RequestFailed: "requestfailed", + RequestFinished: "requestfinished" + }, + BrowserServer: { + Close: "close" + }, + Page: { + Close: "close", + Crash: "crash", + Console: "console", + Dialog: "dialog", + Download: "download", + FileChooser: "filechooser", + DOMContentLoaded: "domcontentloaded", + // Can't use just 'error' due to node.js special treatment of error events. + // @see https://nodejs.org/api/events.html#events_error_events + PageError: "pageerror", + Request: "request", + Response: "response", + RequestFailed: "requestfailed", + RequestFinished: "requestfinished", + FrameAttached: "frameattached", + FrameDetached: "framedetached", + FrameNavigated: "framenavigated", + Load: "load", + Popup: "popup", + WebSocket: "websocket", + Worker: "worker" + }, + WebSocket: { + Close: "close", + Error: "socketerror", + FrameReceived: "framereceived", + FrameSent: "framesent" + }, + Worker: { + Close: "close", + Console: "console" + }, + ElectronApplication: { + Close: "close", + Console: "console", + Window: "window" + } + }; + } +}); + +// packages/playwright-core/src/client/debugger.ts +var Debugger2; +var init_debugger2 = __esm({ + "packages/playwright-core/src/client/debugger.ts"() { + "use strict"; + init_channelOwner(); + init_events(); + Debugger2 = class extends ChannelOwner { + constructor(parent, type3, guid, initializer) { + super(parent, type3, guid, initializer); + this._pausedDetails = null; + this._channel.on("pausedStateChanged", ({ pausedDetails }) => { + this._pausedDetails = pausedDetails ?? null; + this.emit(Events.Debugger.PausedStateChanged); + }); + } + static from(channel) { + return channel._object; + } + async requestPause() { + await this._channel.requestPause(); + } + async resume() { + await this._channel.resume(); + } + async next() { + await this._channel.next(); + } + async runTo(location2) { + await this._channel.runTo({ location: location2 }); + } + pausedDetails() { + return this._pausedDetails; + } + }; + } +}); + +// packages/playwright-core/src/client/stream.ts +var Stream; +var init_stream = __esm({ + "packages/playwright-core/src/client/stream.ts"() { + "use strict"; + init_channelOwner(); + Stream = class extends ChannelOwner { + static from(Stream2) { + return Stream2._object; + } + constructor(parent, type3, guid, initializer) { + super(parent, type3, guid, initializer); + } + stream() { + return this._platform.streamReadable(this._channel); + } + }; + } +}); + +// packages/playwright-core/src/client/fileUtils.ts +async function mkdirIfNeeded2(platform, filePath) { + await platform.fs().promises.mkdir(platform.path().dirname(filePath), { recursive: true }).catch(() => { + }); +} +var fileUploadSizeLimit2; +var init_fileUtils2 = __esm({ + "packages/playwright-core/src/client/fileUtils.ts"() { + "use strict"; + fileUploadSizeLimit2 = 50 * 1024 * 1024; + } +}); + +// packages/playwright-core/src/client/artifact.ts +var Artifact2; +var init_artifact2 = __esm({ + "packages/playwright-core/src/client/artifact.ts"() { + "use strict"; + init_channelOwner(); + init_stream(); + init_fileUtils2(); + Artifact2 = class extends ChannelOwner { + static from(channel) { + return channel._object; + } + async pathAfterFinished() { + if (this._connection.isRemote()) + throw new Error(`Path is not available when connecting remotely. Use saveAs() to save a local copy.`); + return (await this._channel.pathAfterFinished()).value; + } + async saveAs(path59) { + if (!this._connection.isRemote()) { + await this._channel.saveAs({ path: path59 }); + return; + } + const result2 = await this._channel.saveAsStream(); + const stream3 = Stream.from(result2.stream); + await mkdirIfNeeded2(this._platform, path59); + await new Promise((resolve, reject) => { + stream3.stream().pipe(this._platform.fs().createWriteStream(path59)).on("finish", resolve).on("error", reject); + }); + } + async failure() { + return (await this._channel.failure()).error || null; + } + async createReadStream() { + const result2 = await this._channel.stream(); + const stream3 = Stream.from(result2.stream); + return stream3.stream(); + } + async readIntoBuffer() { + const stream3 = await this.createReadStream(); + return await new Promise((resolve, reject) => { + const chunks = []; + stream3.on("data", (chunk) => { + chunks.push(chunk); + }); + stream3.on("end", () => { + resolve(Buffer.concat(chunks)); + }); + stream3.on("error", reject); + }); + } + async cancel() { + return await this._channel.cancel(); + } + async delete() { + return await this._channel.delete(); + } + }; + } +}); + +// packages/playwright-core/src/client/coverage.ts +var Coverage; +var init_coverage = __esm({ + "packages/playwright-core/src/client/coverage.ts"() { + "use strict"; + Coverage = class { + constructor(channel) { + this._channel = channel; + } + async startJSCoverage(options = {}) { + await this._channel.startJSCoverage(options); + } + async stopJSCoverage() { + return (await this._channel.stopJSCoverage()).entries; + } + async startCSSCoverage(options = {}) { + await this._channel.startCSSCoverage(options); + } + async stopCSSCoverage() { + return (await this._channel.stopCSSCoverage()).entries; + } + }; + } +}); + +// packages/playwright-core/src/client/disposable.ts +var DisposableObject2, DisposableStub; +var init_disposable3 = __esm({ + "packages/playwright-core/src/client/disposable.ts"() { + "use strict"; + init_channelOwner(); + init_errors2(); + DisposableObject2 = class extends ChannelOwner { + static from(channel) { + return channel._object; + } + async [Symbol.asyncDispose]() { + await this.dispose(); + } + async dispose() { + try { + await this._channel.dispose(); + } catch (e) { + if (isTargetClosedError2(e)) + return; + throw e; + } + } + }; + DisposableStub = class { + constructor(dispose) { + this._dispose = dispose; + } + async [Symbol.asyncDispose]() { + await this.dispose(); + } + async dispose() { + if (!this._dispose) + return; + try { + const dispose = this._dispose; + this._dispose = void 0; + await dispose(); + } catch (e) { + if (isTargetClosedError2(e)) + return; + throw e; + } + } + }; + } +}); + +// packages/playwright-core/src/client/download.ts +var Download2; +var init_download2 = __esm({ + "packages/playwright-core/src/client/download.ts"() { + "use strict"; + Download2 = class { + constructor(page, url2, suggestedFilename, artifact) { + this._page = page; + this._url = url2; + this._suggestedFilename = suggestedFilename; + this._artifact = artifact; + } + page() { + return this._page; + } + url() { + return this._url; + } + suggestedFilename() { + return this._suggestedFilename; + } + async path() { + return await this._artifact.pathAfterFinished(); + } + async saveAs(path59) { + return await this._artifact.saveAs(path59); + } + async failure() { + return await this._artifact.failure(); + } + async createReadStream() { + return await this._artifact.createReadStream(); + } + async cancel() { + return await this._artifact.cancel(); + } + async delete() { + return await this._artifact.delete(); + } + }; + } +}); + +// packages/playwright-core/src/client/locator.ts +function testIdAttributeName() { + return _testIdAttributeName; +} +function setTestIdAttribute(attributeName) { + _testIdAttributeName = attributeName; +} +function cssObjectToString(style) { + return Object.entries(style).map(([key, value2]) => { + const property = key.startsWith("--") ? key : key.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase()); + return `${property}: ${value2}`; + }).join("; "); +} +var Locator, FrameLocator, _testIdAttributeName; +var init_locator = __esm({ + "packages/playwright-core/src/client/locator.ts"() { + "use strict"; + init_locatorGenerators(); + init_locatorUtils(); + init_stringUtils(); + init_rtti(); + init_time(); + init_elementHandle(); + init_disposable3(); + Locator = class _Locator { + constructor(frame, selector, options) { + this._apiName = "Locator"; + this._frame = frame; + this._selector = selector; + if (options?.hasText) + this._selector += ` >> internal:has-text=${escapeForTextSelector(options.hasText, false)}`; + if (options?.hasNotText) + this._selector += ` >> internal:has-not-text=${escapeForTextSelector(options.hasNotText, false)}`; + if (options?.has) { + const locator2 = options.has; + if (locator2._frame !== frame) + throw new Error(`Inner "has" locator must belong to the same frame.`); + this._selector += ` >> internal:has=` + JSON.stringify(locator2._selector); + } + if (options?.hasNot) { + const locator2 = options.hasNot; + if (locator2._frame !== frame) + throw new Error(`Inner "hasNot" locator must belong to the same frame.`); + this._selector += ` >> internal:has-not=` + JSON.stringify(locator2._selector); + } + if (options?.visible !== void 0) + this._selector += ` >> visible=${options.visible ? "true" : "false"}`; + if (this._frame._platform.inspectCustom) + this[this._frame._platform.inspectCustom] = () => this._inspect(); + } + async _withElement(task, options) { + const timeout = this._frame._timeout({ timeout: options.timeout }); + const deadline = timeout ? monotonicTime() + timeout : 0; + return await this._frame._wrapApiCall(async () => { + const result2 = await this._frame._channel.waitForSelector({ selector: this._selector, strict: true, state: "attached", timeout }); + const handle = ElementHandle2.fromNullable(result2.element); + if (!handle) + throw new Error(`Could not resolve ${this._selector} to DOM Element`); + try { + return await task(handle, deadline ? deadline - monotonicTime() : 0); + } finally { + await handle.dispose(); + } + }, { title: options.title, internal: options.internal }); + } + _equals(locator2) { + return this._frame === locator2._frame && this._selector === locator2._selector; + } + page() { + return this._frame.page(); + } + async boundingBox(options) { + return await this._withElement((h) => h.boundingBox(), { title: "Bounding box", timeout: options?.timeout }); + } + async check(options = {}) { + return await this._frame.check(this._selector, { strict: true, ...options }); + } + async click(options = {}) { + return await this._frame.click(this._selector, { strict: true, ...options }); + } + async dblclick(options = {}) { + await this._frame.dblclick(this._selector, { strict: true, ...options }); + } + async dispatchEvent(type3, eventInit = {}, options) { + return await this._frame.dispatchEvent(this._selector, type3, eventInit, { strict: true, ...options }); + } + async dragTo(target, options = {}) { + return await this._frame.dragAndDrop(this._selector, target._selector, { + strict: true, + ...options + }); + } + async drop(payload, options = {}) { + await this._frame._drop(this._selector, payload, { strict: true, ...options }); + } + async evaluate(pageFunction, arg, options) { + return await this._withElement((h) => h.evaluate(pageFunction, arg), { title: "Evaluate", timeout: options?.timeout }); + } + async evaluateAll(pageFunction, arg) { + return await this._frame.$$eval(this._selector, pageFunction, arg); + } + async evaluateHandle(pageFunction, arg, options) { + return await this._withElement((h) => h.evaluateHandle(pageFunction, arg), { title: "Evaluate", timeout: options?.timeout }); + } + async fill(value2, options = {}) { + return await this._frame.fill(this._selector, value2, { strict: true, ...options }); + } + async clear(options = {}) { + await this._frame._wrapApiCall(() => this.fill("", options), { title: "Clear" }); + } + async _highlight() { + return await this._frame._highlight(this._selector); + } + async highlight(options = {}) { + const style = typeof options.style === "object" ? cssObjectToString(options.style) : options.style; + await this._frame._highlight(this._selector, style); + return new DisposableStub(() => this.hideHighlight()); + } + async hideHighlight() { + await this._frame._hideHighlight(this._selector); + } + locator(selectorOrLocator, options) { + if (isString(selectorOrLocator)) + return new _Locator(this._frame, this._selector + " >> " + selectorOrLocator, options); + if (selectorOrLocator._frame !== this._frame) + throw new Error(`Locators must belong to the same frame.`); + return new _Locator(this._frame, this._selector + " >> internal:chain=" + JSON.stringify(selectorOrLocator._selector), options); + } + getByTestId(testId) { + return this.locator(getByTestIdSelector(testIdAttributeName(), testId)); + } + getByAltText(text2, options) { + return this.locator(getByAltTextSelector(text2, options)); + } + getByLabel(text2, options) { + return this.locator(getByLabelSelector(text2, options)); + } + getByPlaceholder(text2, options) { + return this.locator(getByPlaceholderSelector(text2, options)); + } + getByText(text2, options) { + return this.locator(getByTextSelector(text2, options)); + } + getByTitle(text2, options) { + return this.locator(getByTitleSelector(text2, options)); + } + getByRole(role, options = {}) { + return this.locator(getByRoleSelector(role, options)); + } + frameLocator(selector) { + return new FrameLocator(this._frame, this._selector + " >> " + selector); + } + filter(options) { + return new _Locator(this._frame, this._selector, options); + } + async elementHandle(options) { + return await this._frame.waitForSelector(this._selector, { strict: true, state: "attached", ...options }); + } + async elementHandles() { + return await this._frame.$$(this._selector); + } + contentFrame() { + return new FrameLocator(this._frame, this._selector); + } + describe(description) { + return new _Locator(this._frame, this._selector + " >> internal:describe=" + JSON.stringify(description)); + } + description() { + return locatorCustomDescription(this._selector) || null; + } + first() { + return new _Locator(this._frame, this._selector + " >> nth=0"); + } + last() { + return new _Locator(this._frame, this._selector + ` >> nth=-1`); + } + nth(index) { + return new _Locator(this._frame, this._selector + ` >> nth=${index}`); + } + and(locator2) { + if (locator2._frame !== this._frame) + throw new Error(`Locators must belong to the same frame.`); + return new _Locator(this._frame, this._selector + ` >> internal:and=` + JSON.stringify(locator2._selector)); + } + or(locator2) { + if (locator2._frame !== this._frame) + throw new Error(`Locators must belong to the same frame.`); + return new _Locator(this._frame, this._selector + ` >> internal:or=` + JSON.stringify(locator2._selector)); + } + async focus(options) { + return await this._frame.focus(this._selector, { strict: true, ...options }); + } + async blur(options) { + await this._frame._channel.blur({ selector: this._selector, strict: true, ...options, timeout: this._frame._timeout(options) }); + } + // options are only here for testing + async count(_options) { + return await this._frame._queryCount(this._selector, _options); + } + async normalize() { + const { resolvedSelector } = await this._frame._channel.resolveSelector({ selector: this._selector }); + return new _Locator(this._frame, resolvedSelector); + } + async getAttribute(name, options) { + return await this._frame.getAttribute(this._selector, name, { strict: true, ...options }); + } + async hover(options = {}) { + return await this._frame.hover(this._selector, { strict: true, ...options }); + } + async innerHTML(options) { + return await this._frame.innerHTML(this._selector, { strict: true, ...options }); + } + async innerText(options) { + return await this._frame.innerText(this._selector, { strict: true, ...options }); + } + async inputValue(options) { + return await this._frame.inputValue(this._selector, { strict: true, ...options }); + } + async isChecked(options) { + return await this._frame.isChecked(this._selector, { strict: true, ...options }); + } + async isDisabled(options) { + return await this._frame.isDisabled(this._selector, { strict: true, ...options }); + } + async isEditable(options) { + return await this._frame.isEditable(this._selector, { strict: true, ...options }); + } + async isEnabled(options) { + return await this._frame.isEnabled(this._selector, { strict: true, ...options }); + } + async isHidden(options) { + return await this._frame.isHidden(this._selector, { strict: true, ...options }); + } + async isVisible(options) { + return await this._frame.isVisible(this._selector, { strict: true, ...options }); + } + async press(key, options = {}) { + return await this._frame.press(this._selector, key, { strict: true, ...options }); + } + async screenshot(options = {}) { + const mask = options.mask; + return await this._withElement((h, timeout) => h.screenshot({ ...options, mask, timeout }), { title: "Screenshot", timeout: options.timeout }); + } + async ariaSnapshot(options = {}) { + const result2 = await this._frame._channel.ariaSnapshot({ timeout: this._frame._timeout(options), mode: options.mode, selector: this._selector, depth: options.depth, boxes: options.boxes }); + return result2.snapshot; + } + async scrollIntoViewIfNeeded(options = {}) { + return await this._withElement((h, timeout) => h.scrollIntoViewIfNeeded({ ...options, timeout }), { title: "Scroll into view", timeout: options.timeout }); + } + async selectOption(values, options = {}) { + return await this._frame.selectOption(this._selector, values, { strict: true, ...options }); + } + async selectText(options = {}) { + return await this._withElement((h, timeout) => h.selectText({ ...options, timeout }), { title: "Select text", timeout: options.timeout }); + } + async setChecked(checked, options) { + if (checked) + await this.check(options); + else + await this.uncheck(options); + } + async setInputFiles(files, options = {}) { + return await this._frame.setInputFiles(this._selector, files, { strict: true, ...options }); + } + async tap(options = {}) { + return await this._frame.tap(this._selector, { strict: true, ...options }); + } + async textContent(options) { + return await this._frame.textContent(this._selector, { strict: true, ...options }); + } + async type(text2, options = {}) { + return await this._frame.type(this._selector, text2, { strict: true, ...options }); + } + async pressSequentially(text2, options = {}) { + return await this.type(text2, options); + } + async uncheck(options = {}) { + return await this._frame.uncheck(this._selector, { strict: true, ...options }); + } + async all() { + return new Array(await this.count()).fill(0).map((e, i) => this.nth(i)); + } + async allInnerTexts() { + return await this._frame.$$eval(this._selector, (ee) => ee.map((e) => e.innerText)); + } + async allTextContents() { + return await this._frame.$$eval(this._selector, (ee) => ee.map((e) => e.textContent || "")); + } + async waitFor(options) { + await this._frame._channel.waitForSelector({ selector: this._selector, strict: true, omitReturnValue: true, ...options, timeout: this._frame._timeout(options) }); + } + async _expect(expression2, options) { + return this._frame._expect(expression2, { + ...options, + selector: this._selector + }); + } + _inspect() { + return this.toString(); + } + toString() { + return asLocatorDescription("javascript", this._selector); + } + }; + FrameLocator = class _FrameLocator { + constructor(frame, selector) { + this._frame = frame; + this._frameSelector = selector; + } + locator(selectorOrLocator, options) { + if (isString(selectorOrLocator)) + return new Locator(this._frame, this._frameSelector + " >> internal:control=enter-frame >> " + selectorOrLocator, options); + if (selectorOrLocator._frame !== this._frame) + throw new Error(`Locators must belong to the same frame.`); + return new Locator(this._frame, this._frameSelector + " >> internal:control=enter-frame >> " + selectorOrLocator._selector, options); + } + getByTestId(testId) { + return this.locator(getByTestIdSelector(testIdAttributeName(), testId)); + } + getByAltText(text2, options) { + return this.locator(getByAltTextSelector(text2, options)); + } + getByLabel(text2, options) { + return this.locator(getByLabelSelector(text2, options)); + } + getByPlaceholder(text2, options) { + return this.locator(getByPlaceholderSelector(text2, options)); + } + getByText(text2, options) { + return this.locator(getByTextSelector(text2, options)); + } + getByTitle(text2, options) { + return this.locator(getByTitleSelector(text2, options)); + } + getByRole(role, options = {}) { + return this.locator(getByRoleSelector(role, options)); + } + owner() { + return new Locator(this._frame, this._frameSelector); + } + frameLocator(selector) { + return new _FrameLocator(this._frame, this._frameSelector + " >> internal:control=enter-frame >> " + selector); + } + first() { + return new _FrameLocator(this._frame, this._frameSelector + " >> nth=0"); + } + last() { + return new _FrameLocator(this._frame, this._frameSelector + ` >> nth=-1`); + } + nth(index) { + return new _FrameLocator(this._frame, this._frameSelector + ` >> nth=${index}`); + } + }; + _testIdAttributeName = "data-testid"; + } +}); + +// packages/playwright-core/src/client/timeoutSettings.ts +var TimeoutSettings; +var init_timeoutSettings = __esm({ + "packages/playwright-core/src/client/timeoutSettings.ts"() { + "use strict"; + init_time(); + TimeoutSettings = class { + constructor(platform, parent) { + this._parent = parent; + this._platform = platform; + } + setDefaultTimeout(timeout) { + this._defaultTimeout = timeout; + } + setDefaultNavigationTimeout(timeout) { + this._defaultNavigationTimeout = timeout; + } + defaultNavigationTimeout() { + return this._defaultNavigationTimeout; + } + defaultTimeout() { + return this._defaultTimeout; + } + navigationTimeout(options) { + if (typeof options.timeout === "number") + return options.timeout; + if (this._defaultNavigationTimeout !== void 0) + return this._defaultNavigationTimeout; + if (this._platform.isDebugMode()) + return 0; + if (this._defaultTimeout !== void 0) + return this._defaultTimeout; + if (this._parent) + return this._parent.navigationTimeout(options); + return DEFAULT_PLAYWRIGHT_TIMEOUT; + } + timeout(options) { + if (typeof options.timeout === "number") + return options.timeout; + if (this._platform.isDebugMode()) + return 0; + if (this._defaultTimeout !== void 0) + return this._defaultTimeout; + if (this._parent) + return this._parent.timeout(options); + return DEFAULT_PLAYWRIGHT_TIMEOUT; + } + launchTimeout(options) { + if (typeof options.timeout === "number") + return options.timeout; + if (this._platform.isDebugMode()) + return 0; + if (this._parent) + return this._parent.launchTimeout(options); + return DEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT; + } + }; + } +}); + +// packages/playwright-core/src/client/waiter.ts +function waitForEvent(emitter, event, savedZone, predicate) { + let listener; + const promise = new Promise((resolve, reject) => { + listener = async (eventArg) => { + await savedZone.run(async () => { + try { + if (predicate && !await predicate(eventArg)) + return; + emitter.removeListener(event, listener); + resolve(eventArg); + } catch (e) { + emitter.removeListener(event, listener); + reject(e); + } + }); + }; + emitter.addListener(event, listener); + }); + const dispose = () => emitter.removeListener(event, listener); + return { promise, dispose }; +} +function waitForTimeout(timeout) { + let timeoutId; + const promise = new Promise((resolve) => timeoutId = setTimeout(resolve, timeout)); + const dispose = () => clearTimeout(timeoutId); + return { promise, dispose }; +} +function formatLogRecording(log2) { + if (!log2.length) + return ""; + const header = ` logs `; + const headerLength = 60; + const leftLength = (headerLength - header.length) / 2; + const rightLength = headerLength - header.length - leftLength; + return ` +${"=".repeat(leftLength)}${header}${"=".repeat(rightLength)} +${log2.join("\n")} +${"=".repeat(headerLength)}`; +} +var Waiter; +var init_waiter = __esm({ + "packages/playwright-core/src/client/waiter.ts"() { + "use strict"; + init_stackTrace(); + init_errors2(); + Waiter = class _Waiter { + constructor(channelOwner, event) { + this._failures = []; + this._logs = []; + this._waitId = channelOwner._platform.createGuid(); + this._channelOwner = channelOwner; + this._savedZone = channelOwner._platform.zones.current().pop(); + const title = `Wait for event "${event}"`; + this._sendWaitInfo({ waitId: this._waitId, phase: "before", event }, { title }); + this._dispose = [ + () => this._sendWaitInfo({ waitId: this._waitId, phase: "after", error: this._error }, { internal: true }) + ]; + } + static createForEvent(channelOwner, event) { + return new _Waiter(channelOwner, event); + } + _sendWaitInfo(info, options) { + const owner = this._channelOwner; + owner._wrapApiCall(async (apiZone) => { + if (apiZone.internal || apiZone.reported) { + void owner._connection.sendMessageToServer(owner, "__waitInfo__", info, { internal: true }); + return; + } + apiZone.reported = true; + if (!apiZone.title) + apiZone.title = options.title; + owner._instrumentation.onApiCallBegin(apiZone, { type: owner._type, method: "__waitInfo__", params: info }); + void owner._connection.sendMessageToServer(owner, "__waitInfo__", info, apiZone); + }, options).catch(() => { + }); + } + async waitForEvent(emitter, event, predicate) { + const { promise, dispose } = waitForEvent(emitter, event, this._savedZone, predicate); + return await this.waitForPromise(promise, dispose); + } + rejectOnEvent(emitter, event, error, predicate) { + const { promise, dispose } = waitForEvent(emitter, event, this._savedZone, predicate); + this._rejectOn(promise.then(() => { + throw typeof error === "function" ? error() : error; + }), dispose); + } + rejectOnTimeout(timeout, message) { + if (!timeout) + return; + const { promise, dispose } = waitForTimeout(timeout); + this._rejectOn(promise.then(() => { + throw new TimeoutError2(message); + }), dispose); + } + rejectImmediately(error) { + this._immediateError = error; + } + dispose() { + for (const dispose of this._dispose) + dispose(); + } + async waitForPromise(promise, dispose) { + try { + if (this._immediateError) + throw this._immediateError; + const result2 = await Promise.race([promise, ...this._failures]); + if (dispose) + dispose(); + return result2; + } catch (e) { + if (dispose) + dispose(); + this._error = e.message; + this.dispose(); + rewriteErrorMessage(e, e.message + formatLogRecording(this._logs)); + throw e; + } + } + log(s) { + this._logs.push(s); + this._sendWaitInfo({ waitId: this._waitId, phase: "log", message: s }, { internal: true }); + } + _rejectOn(promise, dispose) { + this._failures.push(promise); + if (dispose) + this._dispose.push(dispose); + } + }; + } +}); + +// packages/playwright-core/src/client/worker.ts +var Worker2; +var init_worker = __esm({ + "packages/playwright-core/src/client/worker.ts"() { + "use strict"; + init_manualPromise(); + init_channelOwner(); + init_consoleMessage(); + init_errors2(); + init_events(); + init_jsHandle(); + init_timeoutSettings(); + init_waiter(); + Worker2 = class _Worker extends ChannelOwner { + constructor(parent, type3, guid, initializer) { + super(parent, type3, guid, initializer); + // Set for service workers. + this._closedScope = new LongStandingScope(); + this._setEventToSubscriptionMapping(/* @__PURE__ */ new Map([ + [Events.Worker.Console, "console"] + ])); + this._channel.on("console", (event) => { + this.emit(Events.Worker.Console, new ConsoleMessage2(this._platform, event, null, this)); + }); + this._channel.on("close", () => { + if (this._page) + this._page._workers.delete(this); + if (this._context) + this._context._serviceWorkers.delete(this); + this.emit(Events.Worker.Close, this); + }); + this.once(Events.Worker.Close, () => this._closedScope.close(this._closeErrorWithReason())); + } + static fromNullable(worker) { + return worker ? _Worker.from(worker) : null; + } + static from(worker) { + return worker._object; + } + _closeErrorWithReason() { + if (this._closeReason) + return new TargetClosedError2(this._closeReason); + return this._page?._closeErrorWithReason() || new TargetClosedError2(); + } + url() { + return this._initializer.url; + } + async evaluate(pageFunction, arg) { + assertMaxArguments(arguments.length, 2); + const result2 = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: serializeArgument(arg) }); + return parseResult(result2.value); + } + async evaluateHandle(pageFunction, arg) { + assertMaxArguments(arguments.length, 2); + const result2 = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: serializeArgument(arg) }); + return JSHandle2.from(result2.handle); + } + async waitForEvent(event, optionsOrPredicate = {}) { + return await this._wrapApiCall(async () => { + const timeoutSettings = this._page?._timeoutSettings ?? this._context?._timeoutSettings ?? new TimeoutSettings(this._platform); + const timeout = timeoutSettings.timeout(typeof optionsOrPredicate === "function" ? {} : optionsOrPredicate); + const predicate = typeof optionsOrPredicate === "function" ? optionsOrPredicate : optionsOrPredicate.predicate; + const waiter = Waiter.createForEvent(this, event); + waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`); + if (event !== Events.Worker.Close) + waiter.rejectOnEvent(this, Events.Worker.Close, () => this._closeErrorWithReason()); + const result2 = await waiter.waitForEvent(this, event, predicate); + waiter.dispose(); + return result2; + }); + } + async _disconnect(options = {}) { + this._closeReason = options.reason; + try { + await this._channel.disconnect(options); + } catch (e) { + if (isTargetClosedError2(e)) + return; + throw e; + } + } + }; + } +}); + +// packages/playwright-core/src/client/tracing.ts +var Tracing2; +var init_tracing2 = __esm({ + "packages/playwright-core/src/client/tracing.ts"() { + "use strict"; + init_rtti(); + init_artifact2(); + init_channelOwner(); + init_disposable3(); + Tracing2 = class extends ChannelOwner { + constructor(parent, type3, guid, initializer) { + super(parent, type3, guid, initializer); + this._includeSources = false; + this._additionalSources = /* @__PURE__ */ new Set(); + this._isLive = false; + this._isTracing = false; + this._harRecorders = /* @__PURE__ */ new Map(); + } + static from(channel) { + return channel._object; + } + async start(options = {}) { + await this._wrapApiCall(async () => { + this._includeSources = !!options.sources; + this._isLive = !!options.live; + await this._channel.tracingStart({ + name: options.name, + snapshots: options.snapshots, + screenshots: options.screenshots, + live: options.live + }); + const { traceName } = await this._channel.tracingStartChunk({ name: options.name, title: options.title }); + await this._startCollectingStacks(traceName, this._isLive); + }); + } + async startChunk(options = {}) { + await this._wrapApiCall(async () => { + const { traceName } = await this._channel.tracingStartChunk(options); + await this._startCollectingStacks(traceName, this._isLive); + }); + } + async group(name, options = {}) { + if (options.location) + this._additionalSources.add(options.location.file); + await this._channel.tracingGroup({ name, location: options.location }); + return new DisposableStub(() => this.groupEnd()); + } + async groupEnd() { + await this._channel.tracingGroupEnd(); + } + async _startCollectingStacks(traceName, live) { + if (!this._isTracing) { + this._isTracing = true; + this._connection.setIsTracing(true); + } + const result2 = await this._connection.localUtils()?.tracingStarted({ tracesDir: this._tracesDir, traceName, live }); + this._stacksId = result2?.stacksId; + } + async stopChunk(options = {}) { + await this._wrapApiCall(async () => { + await this._doStopChunk(options.path); + }); + } + async stop(options = {}) { + await this._wrapApiCall(async () => { + await this._doStopChunk(options.path); + await this._channel.tracingStop(); + }); + } + async startHar(path59, options = {}) { + await this._wrapApiCall(async () => { + if (this._harId) + throw new Error("HAR recording has already been started"); + if (options.resourcesDir && path59.endsWith(".zip")) + throw new Error("resourcesDir option is not compatible with a .zip har file"); + const defaultContent = path59.endsWith(".zip") ? "attach" : "embed"; + this._harId = await this._recordIntoHAR(path59, null, { + url: options.urlFilter, + updateContent: options.content ?? defaultContent, + updateMode: options.mode ?? "full", + resourcesDir: options.resourcesDir + }); + }); + return new DisposableStub(() => this.stopHar()); + } + async stopHar() { + await this._wrapApiCall(async () => { + const harId = this._harId; + if (!harId) + throw new Error("HAR recording has not been started"); + this._harId = void 0; + await this._exportHAR(harId); + }); + } + async _recordIntoHAR(har, page, options = {}) { + const isZip = har.endsWith(".zip"); + const { harId } = await this._channel.harStart({ + page: page?._channel, + options: { + content: options.updateContent ?? "attach", + urlGlob: isString(options.url) ? options.url : void 0, + urlRegexSource: isRegExp(options.url) ? options.url.source : void 0, + urlRegexFlags: isRegExp(options.url) ? options.url.flags : void 0, + mode: options.updateMode ?? "minimal", + harPath: isZip ? void 0 : har, + resourcesDir: options.resourcesDir + } + }); + this._harRecorders.set(harId, { path: har, resourcesDir: options.resourcesDir }); + return harId; + } + async _exportHAR(harId) { + const harParams = this._harRecorders.get(harId); + if (!harParams) + return; + this._harRecorders.delete(harId); + const isLocal = !this._connection.isRemote(); + const isZip = harParams.path.endsWith(".zip"); + if (isLocal) { + const { entries } = await this._channel.harExport({ harId, mode: "entries" }); + if (!isZip) { + return; + } + const localUtils2 = this._connection.localUtils(); + if (!localUtils2) + throw new Error("Cannot save zipped HAR in thin clients"); + await localUtils2.zip({ zipFile: harParams.path, entries, mode: "write", includeSources: false, additionalSources: [] }); + return; + } + const { artifact: artifactChannel } = await this._channel.harExport({ harId, mode: "archive" }); + const artifact = Artifact2.from(artifactChannel); + if (isZip) { + await artifact.saveAs(harParams.path); + await artifact.delete(); + return; + } + const localUtils = this._connection.localUtils(); + if (!localUtils) + throw new Error("Uncompressed har is not supported in thin clients"); + await artifact.saveAs(harParams.path + ".tmp"); + await localUtils.harUnzip({ zipFile: harParams.path + ".tmp", harFile: harParams.path, resourcesDir: harParams.resourcesDir }); + await artifact.delete(); + } + async _exportAllHars() { + await this._wrapApiCall(async () => { + await Promise.all([...this._harRecorders.keys()].map((harId) => this._exportHAR(harId))); + }, { internal: true }); + } + async _doStopChunk(filePath) { + this._resetStackCounter(); + const additionalSources = [...this._additionalSources]; + this._additionalSources.clear(); + if (!filePath) { + await this._channel.tracingStopChunk({ mode: "discard" }); + if (this._stacksId) + await this._connection.localUtils().traceDiscarded({ stacksId: this._stacksId }); + return; + } + const localUtils = this._connection.localUtils(); + if (!localUtils) + throw new Error("Cannot save trace in thin clients"); + const isLocal = !this._connection.isRemote(); + if (isLocal) { + const result3 = await this._channel.tracingStopChunk({ mode: "entries" }); + await localUtils.zip({ zipFile: filePath, entries: result3.entries, mode: "write", stacksId: this._stacksId, includeSources: this._includeSources, additionalSources }); + return; + } + const result2 = await this._channel.tracingStopChunk({ mode: "archive" }); + if (!result2.artifact) { + if (this._stacksId) + await localUtils.traceDiscarded({ stacksId: this._stacksId }); + return; + } + const artifact = Artifact2.from(result2.artifact); + await artifact.saveAs(filePath); + await artifact.delete(); + await localUtils.zip({ zipFile: filePath, entries: [], mode: "append", stacksId: this._stacksId, includeSources: this._includeSources, additionalSources }); + } + _resetStackCounter() { + if (this._isTracing) { + this._isTracing = false; + this._connection.setIsTracing(false); + } + } + }; + } +}); + +// packages/playwright-core/src/client/fetch.ts +async function toFormField(platform, name, value2) { + const typeOfValue = typeof value2; + if (isFilePayload(value2)) { + const payload = value2; + if (!Buffer.isBuffer(payload.buffer)) + throw new Error(`Unexpected buffer type of 'data.${name}'`); + return { name, file: filePayloadToJson(payload) }; + } else if (typeOfValue === "string" || typeOfValue === "number" || typeOfValue === "boolean") { + return { name, value: String(value2) }; + } else { + return { name, file: await readStreamToJson(platform, value2) }; + } +} +function isJsonParsable(value2) { + if (typeof value2 !== "string") + return false; + try { + JSON.parse(value2); + return true; + } catch (e) { + if (e instanceof SyntaxError) + return false; + else + throw e; + } +} +function filePayloadToJson(payload) { + return { + name: payload.name, + mimeType: payload.mimeType, + buffer: payload.buffer + }; +} +async function readStreamToJson(platform, stream3) { + const buffer = await new Promise((resolve, reject) => { + const chunks = []; + stream3.on("data", (chunk) => chunks.push(chunk)); + stream3.on("end", () => resolve(Buffer.concat(chunks))); + stream3.on("error", (err) => reject(err)); + }); + const streamPath = Buffer.isBuffer(stream3.path) ? stream3.path.toString("utf8") : stream3.path; + return { + name: platform.path().basename(streamPath), + buffer + }; +} +function isJsonContentType(headers) { + if (!headers) + return false; + for (const { name, value: value2 } of headers) { + if (name.toLocaleLowerCase() === "content-type") + return value2 === "application/json"; + } + return false; +} +function objectToArray(map) { + if (!map) + return void 0; + const result2 = []; + for (const [name, value2] of Object.entries(map)) { + if (value2 !== void 0) + result2.push({ name, value: String(value2) }); + } + return result2; +} +function isFilePayload(value2) { + return typeof value2 === "object" && value2["name"] && value2["mimeType"] && value2["buffer"]; +} +var APIRequest, APIRequestContext2, APIResponse; +var init_fetch2 = __esm({ + "packages/playwright-core/src/client/fetch.ts"() { + "use strict"; + init_assert(); + init_headers(); + init_rtti(); + init_browserContext2(); + init_channelOwner(); + init_errors2(); + init_network3(); + init_tracing2(); + init_fileUtils2(); + init_timeoutSettings(); + APIRequest = class { + constructor(playwright2) { + this._contexts = /* @__PURE__ */ new Set(); + this._playwright = playwright2; + } + async newContext(options = {}) { + options = { ...options }; + await this._playwright._instrumentation.runBeforeCreateRequestContext(options); + const storageState2 = typeof options.storageState === "string" ? JSON.parse(await this._playwright._platform.fs().promises.readFile(options.storageState, "utf8")) : options.storageState; + const context2 = APIRequestContext2.from((await this._playwright._channel.newRequest({ + ...options, + extraHTTPHeaders: options.extraHTTPHeaders ? headersObjectToArray(options.extraHTTPHeaders) : void 0, + storageState: storageState2, + tracesDir: this._playwright._defaultLaunchOptions?.tracesDir, + // We do not expose tracesDir in the API, so do not allow options to accidentally override it. + clientCertificates: await toClientCertificatesProtocol(this._playwright._platform, options.clientCertificates) + })).request); + this._contexts.add(context2); + context2._request = this; + context2._timeoutSettings.setDefaultTimeout(options.timeout ?? this._playwright._defaultContextTimeout); + context2.tracing._tracesDir = this._playwright._defaultLaunchOptions?.tracesDir; + await context2._instrumentation.runAfterCreateRequestContext(context2); + return context2; + } + }; + APIRequestContext2 = class extends ChannelOwner { + static from(channel) { + return channel._object; + } + constructor(parent, type3, guid, initializer) { + super(parent, type3, guid, initializer); + this.tracing = Tracing2.from(initializer.tracing); + this._timeoutSettings = new TimeoutSettings(this._platform); + } + async [Symbol.asyncDispose]() { + await this.dispose(); + } + async dispose(options = {}) { + this._closeReason = options.reason; + await this._instrumentation.runBeforeCloseRequestContext(this); + await this.tracing._exportAllHars(); + try { + await this._channel.dispose(options); + } catch (e) { + if (isTargetClosedError2(e)) + return; + throw e; + } + this.tracing._resetStackCounter(); + this._request?._contexts.delete(this); + } + async delete(url2, options) { + return await this.fetch(url2, { + ...options, + method: "DELETE" + }); + } + async head(url2, options) { + return await this.fetch(url2, { + ...options, + method: "HEAD" + }); + } + async get(url2, options) { + return await this.fetch(url2, { + ...options, + method: "GET" + }); + } + async patch(url2, options) { + return await this.fetch(url2, { + ...options, + method: "PATCH" + }); + } + async post(url2, options) { + return await this.fetch(url2, { + ...options, + method: "POST" + }); + } + async put(url2, options) { + return await this.fetch(url2, { + ...options, + method: "PUT" + }); + } + async fetch(urlOrRequest, options = {}) { + const url2 = isString(urlOrRequest) ? urlOrRequest : void 0; + const request2 = isString(urlOrRequest) ? void 0 : urlOrRequest; + return await this._innerFetch({ url: url2, request: request2, ...options }); + } + async _innerFetch(options = {}) { + return await this._wrapApiCall(async () => { + if (this._closeReason) + throw new TargetClosedError2(this._closeReason); + assert(options.request || typeof options.url === "string", "First argument must be either URL string or Request"); + assert((options.data === void 0 ? 0 : 1) + (options.form === void 0 ? 0 : 1) + (options.multipart === void 0 ? 0 : 1) <= 1, `Only one of 'data', 'form' or 'multipart' can be specified`); + assert(options.maxRedirects === void 0 || options.maxRedirects >= 0, `'maxRedirects' must be greater than or equal to '0'`); + assert(options.maxRetries === void 0 || options.maxRetries >= 0, `'maxRetries' must be greater than or equal to '0'`); + const url2 = options.url !== void 0 ? options.url : options.request.url(); + const method = options.method || options.request?.method(); + let encodedParams = void 0; + if (typeof options.params === "string") + encodedParams = options.params; + else if (options.params instanceof URLSearchParams) + encodedParams = options.params.toString(); + const headersObj = options.headers || options.request?.headers(); + const headers = headersObj ? headersObjectToArray(headersObj) : void 0; + let jsonData; + let formData; + let multipartData; + let postDataBuffer; + if (options.data !== void 0) { + if (isString(options.data)) { + if (isJsonContentType(headers)) + jsonData = isJsonParsable(options.data) ? options.data : JSON.stringify(options.data); + else + postDataBuffer = Buffer.from(options.data, "utf8"); + } else if (Buffer.isBuffer(options.data)) { + postDataBuffer = options.data; + } else if (typeof options.data === "object" || typeof options.data === "number" || typeof options.data === "boolean") { + jsonData = JSON.stringify(options.data); + } else { + throw new Error(`Unexpected 'data' type`); + } + } else if (options.form) { + if (globalThis.FormData && options.form instanceof FormData) { + formData = []; + for (const [name, value2] of options.form.entries()) { + if (typeof value2 !== "string") + throw new Error(`Expected string for options.form["${name}"], found File. Please use options.multipart instead.`); + formData.push({ name, value: value2 }); + } + } else { + formData = objectToArray(options.form); + } + } else if (options.multipart) { + multipartData = []; + if (globalThis.FormData && options.multipart instanceof FormData) { + const form = options.multipart; + for (const [name, value2] of form.entries()) { + if (isString(value2)) { + multipartData.push({ name, value: value2 }); + } else { + const file = { + name: value2.name, + mimeType: value2.type, + buffer: Buffer.from(await value2.arrayBuffer()) + }; + multipartData.push({ name, file }); + } + } + } else { + for (const [name, value2] of Object.entries(options.multipart)) + multipartData.push(await toFormField(this._platform, name, value2)); + } + } + if (postDataBuffer === void 0 && jsonData === void 0 && formData === void 0 && multipartData === void 0) + postDataBuffer = options.request?.postDataBuffer() || void 0; + const fixtures = { + __testHookLookup: options.__testHookLookup + }; + const result2 = await this._channel.fetch({ + url: url2, + params: typeof options.params === "object" ? objectToArray(options.params) : void 0, + encodedParams, + method, + headers, + postData: postDataBuffer, + jsonData, + formData, + multipartData, + timeout: this._timeoutSettings.timeout(options), + failOnStatusCode: options.failOnStatusCode, + ignoreHTTPSErrors: options.ignoreHTTPSErrors, + maxRedirects: options.maxRedirects, + maxRetries: options.maxRetries, + ...fixtures + }); + return new APIResponse(this, result2.response); + }); + } + async storageState(options = {}) { + const state = await this._channel.storageState({ indexedDB: options.indexedDB }); + if (options.path) { + await mkdirIfNeeded2(this._platform, options.path); + await this._platform.fs().promises.writeFile(options.path, JSON.stringify(state, void 0, 2), "utf8"); + } + return state; + } + }; + APIResponse = class { + constructor(context2, initializer) { + this._apiName = "APIResponse"; + this._request = context2; + this._initializer = initializer; + this._headers = new RawHeaders(this._initializer.headers); + if (context2._platform.inspectCustom) + this[context2._platform.inspectCustom] = () => this._inspect(); + } + ok() { + return this._initializer.status >= 200 && this._initializer.status <= 299; + } + url() { + return this._initializer.url; + } + status() { + return this._initializer.status; + } + statusText() { + return this._initializer.statusText; + } + headers() { + return this._headers.headers(); + } + headersArray() { + return this._headers.headersArray(); + } + async securityDetails() { + return this._initializer.securityDetails ?? null; + } + async serverAddr() { + return this._initializer.serverAddr ?? null; + } + async body() { + return await this._request._wrapApiCall(async () => { + try { + const result2 = await this._request._channel.fetchResponseBody({ fetchUid: this._fetchUid() }); + if (result2.binary === void 0) + throw new Error("Response has been disposed"); + return result2.binary; + } catch (e) { + if (isTargetClosedError2(e)) + throw new Error("Response has been disposed"); + throw e; + } + }, { internal: true }); + } + async text() { + const content = await this.body(); + return content.toString("utf8"); + } + async json() { + const content = await this.text(); + return JSON.parse(content); + } + async [Symbol.asyncDispose]() { + await this.dispose(); + } + async dispose() { + await this._request._channel.disposeAPIResponse({ fetchUid: this._fetchUid() }); + } + _inspect() { + const headers = this.headersArray().map(({ name, value: value2 }) => ` ${name}: ${value2}`); + return `APIResponse: ${this.status()} ${this.statusText()} +${headers.join("\n")}`; + } + _fetchUid() { + return this._initializer.fetchUid; + } + async _fetchLog() { + const { log: log2 } = await this._request._channel.fetchLog({ fetchUid: this._fetchUid() }); + return log2; + } + }; + } +}); + +// packages/playwright-core/src/client/network.ts +function validateHeaders(headers) { + for (const key of Object.keys(headers)) { + const value2 = headers[key]; + if (!Object.is(value2, void 0) && !isString(value2)) + throw new Error(`Expected value of header "${key}" to be String, but "${typeof value2}" is found.`); + } +} +var Request2, Route2, WebSocketRoute, WebSocketRouteHandler, Response3, WebSocket2, RouteHandler, RawHeaders; +var init_network3 = __esm({ + "packages/playwright-core/src/client/network.ts"() { + "use strict"; + init_assert(); + init_headers(); + init_urlMatch(); + init_manualPromise(); + init_multimap(); + init_rtti(); + init_stackTrace(); + init_mimeType(); + init_worker(); + init_waiter(); + init_frame(); + init_fetch2(); + init_events(); + init_errors2(); + init_channelOwner(); + Request2 = class _Request extends ChannelOwner { + constructor(parent, type3, guid, initializer) { + super(parent, type3, guid, initializer); + this._redirectedFrom = null; + this._redirectedTo = null; + this._failureText = null; + this._response = null; + this._fallbackOverrides = {}; + this._redirectedFrom = _Request.fromNullable(initializer.redirectedFrom); + if (this._redirectedFrom) + this._redirectedFrom._redirectedTo = this; + this._provisionalHeaders = new RawHeaders(initializer.headers); + this._timing = { + startTime: 0, + domainLookupStart: -1, + domainLookupEnd: -1, + connectStart: -1, + secureConnectionStart: -1, + connectEnd: -1, + requestStart: -1, + responseStart: -1, + responseEnd: -1 + }; + } + static from(request2) { + return request2._object; + } + static fromNullable(request2) { + return request2 ? _Request.from(request2) : null; + } + url() { + return this._fallbackOverrides.url || this._initializer.url; + } + resourceType() { + return this._initializer.resourceType; + } + method() { + return this._fallbackOverrides.method || this._initializer.method; + } + postData() { + return (this._fallbackOverrides.postDataBuffer || this._initializer.postData)?.toString("utf-8") || null; + } + postDataBuffer() { + return this._fallbackOverrides.postDataBuffer || this._initializer.postData || null; + } + postDataJSON() { + const postData = this.postData(); + if (!postData) + return null; + const contentType = this.headers()["content-type"]; + if (contentType?.includes("application/x-www-form-urlencoded")) { + const entries = {}; + const parsed = new URLSearchParams(postData); + for (const [k, v] of parsed.entries()) + entries[k] = v; + return entries; + } + try { + return JSON.parse(postData); + } catch (e) { + throw new Error("POST data is not a valid JSON object: " + postData); + } + } + /** + * @deprecated + */ + headers() { + if (this._fallbackOverrides.headers) + return RawHeaders._fromHeadersObjectLossy(this._fallbackOverrides.headers).headers(); + return this._provisionalHeaders.headers(); + } + async _actualHeaders() { + if (this._fallbackOverrides.headers) + return RawHeaders._fromHeadersObjectLossy(this._fallbackOverrides.headers); + if (!this._actualHeadersPromise) { + this._actualHeadersPromise = this._wrapApiCall(async () => { + return new RawHeaders((await this._channel.rawRequestHeaders()).headers); + }, { internal: true }); + } + return await this._actualHeadersPromise; + } + async allHeaders() { + return (await this._actualHeaders()).headers(); + } + async headersArray() { + return (await this._actualHeaders()).headersArray(); + } + async headerValue(name) { + return (await this._actualHeaders()).get(name); + } + async response() { + return Response3.fromNullable((await this._channel.response()).response); + } + async _internalResponse() { + return Response3.fromNullable((await this._channel.response()).response); + } + existingResponse() { + return this._response; + } + frame() { + if (!this._initializer.frame) { + assert(this.serviceWorker()); + throw new Error("Service Worker requests do not have an associated frame."); + } + const frame = Frame2.from(this._initializer.frame); + if (!frame._page) { + throw new Error([ + "Frame for this navigation request is not available, because the request", + "was issued before the frame is created. You can check whether the request", + "is a navigation request by calling isNavigationRequest() method." + ].join("\n")); + } + return frame; + } + _safePage() { + return Frame2.fromNullable(this._initializer.frame)?._page || null; + } + serviceWorker() { + return this._initializer.serviceWorker ? Worker2.from(this._initializer.serviceWorker) : null; + } + isNavigationRequest() { + return this._initializer.isNavigationRequest; + } + redirectedFrom() { + return this._redirectedFrom; + } + redirectedTo() { + return this._redirectedTo; + } + failure() { + if (this._failureText === null) + return null; + return { + errorText: this._failureText + }; + } + timing() { + return this._timing; + } + async sizes() { + const response2 = await this.response(); + if (!response2) + throw new Error("Unable to fetch sizes for failed request"); + return (await response2._channel.sizes()).sizes; + } + _setResponseEndTiming(responseEndTiming) { + this._timing.responseEnd = responseEndTiming; + if (this._timing.responseStart === -1) + this._timing.responseStart = responseEndTiming; + } + _finalRequest() { + return this._redirectedTo ? this._redirectedTo._finalRequest() : this; + } + _applyFallbackOverrides(overrides) { + if (overrides.url) + this._fallbackOverrides.url = overrides.url; + if (overrides.method) + this._fallbackOverrides.method = overrides.method; + if (overrides.headers) + this._fallbackOverrides.headers = overrides.headers; + if (isString(overrides.postData)) + this._fallbackOverrides.postDataBuffer = Buffer.from(overrides.postData, "utf-8"); + else if (overrides.postData instanceof Buffer) + this._fallbackOverrides.postDataBuffer = overrides.postData; + else if (overrides.postData) + this._fallbackOverrides.postDataBuffer = Buffer.from(JSON.stringify(overrides.postData), "utf-8"); + } + _fallbackOverridesForContinue() { + return this._fallbackOverrides; + } + _targetClosedScope() { + return this.serviceWorker()?._closedScope || this._safePage()?._closedOrCrashedScope || new LongStandingScope(); + } + }; + Route2 = class extends ChannelOwner { + constructor(parent, type3, guid, initializer) { + super(parent, type3, guid, initializer); + this._handlingPromise = null; + this._didThrow = false; + } + static from(route2) { + return route2._object; + } + request() { + return Request2.from(this._initializer.request); + } + async _raceWithTargetClose(promise) { + return await this.request()._targetClosedScope().safeRace(promise); + } + async _startHandling() { + this._handlingPromise = new ManualPromise(); + return await this._handlingPromise; + } + async fallback(options = {}) { + this._checkNotHandled(); + this.request()._applyFallbackOverrides(options); + this._reportHandled(false); + } + async abort(errorCode) { + await this._handleRoute(async () => { + await this._raceWithTargetClose(this._channel.abort({ errorCode })); + }); + } + async _redirectNavigationRequest(url2) { + await this._handleRoute(async () => { + await this._raceWithTargetClose(this._channel.redirectNavigationRequest({ url: url2 })); + }); + } + async fetch(options = {}) { + return await this._wrapApiCall(async () => { + return await this._context.request._innerFetch({ request: this.request(), data: options.postData, ...options }); + }); + } + async fulfill(options = {}) { + await this._handleRoute(async () => { + await this._innerFulfill(options); + }); + } + async _handleRoute(callback) { + this._checkNotHandled(); + try { + await callback(); + this._reportHandled(true); + } catch (e) { + this._didThrow = true; + throw e; + } + } + async _innerFulfill(options = {}) { + let fetchResponseUid; + let { status: statusOption, headers: headersOption, body } = options; + if (options.json !== void 0) { + assert(options.body === void 0, "Can specify either body or json parameters"); + body = JSON.stringify(options.json); + } + if (options.response instanceof APIResponse) { + statusOption ??= options.response.status(); + headersOption ??= options.response.headers(); + if (body === void 0 && options.path === void 0) { + if (options.response._request._connection === this._connection) + fetchResponseUid = options.response._fetchUid(); + else + body = await options.response.body(); + } + } + let isBase64 = false; + let length = 0; + if (options.path) { + const buffer = await this._platform.fs().promises.readFile(options.path); + body = buffer.toString("base64"); + isBase64 = true; + length = buffer.length; + } else if (isString(body)) { + isBase64 = false; + length = Buffer.byteLength(body); + } else if (body) { + length = body.length; + body = body.toString("base64"); + isBase64 = true; + } + const headers = {}; + for (const header of Object.keys(headersOption || {})) + headers[header.toLowerCase()] = String(headersOption[header]); + if (options.contentType) + headers["content-type"] = String(options.contentType); + else if (options.json) + headers["content-type"] = "application/json"; + else if (options.path) + headers["content-type"] = getMimeTypeForPath(options.path) || "application/octet-stream"; + if (length && !("content-length" in headers)) + headers["content-length"] = String(length); + await this._raceWithTargetClose(this._channel.fulfill({ + status: statusOption || 200, + headers: headersObjectToArray(headers), + body, + isBase64, + fetchResponseUid + })); + } + async continue(options = {}) { + await this._handleRoute(async () => { + this.request()._applyFallbackOverrides(options); + await this._innerContinue( + false + /* isFallback */ + ); + }); + } + _checkNotHandled() { + if (!this._handlingPromise) + throw new Error("Route is already handled!"); + } + _reportHandled(done) { + const chain = this._handlingPromise; + this._handlingPromise = null; + chain.resolve(done); + } + async _innerContinue(isFallback) { + const options = this.request()._fallbackOverridesForContinue(); + return await this._raceWithTargetClose(this._channel.continue({ + url: options.url, + method: options.method, + headers: options.headers ? headersObjectToArray(options.headers) : void 0, + postData: options.postDataBuffer, + isFallback + })); + } + }; + WebSocketRoute = class extends ChannelOwner { + constructor(parent, type3, guid, initializer) { + super(parent, type3, guid, initializer); + this._connected = false; + this._server = { + onMessage: (handler) => { + this._onServerMessage = handler; + }, + onClose: (handler) => { + this._onServerClose = handler; + }, + connectToServer: () => { + throw new Error(`connectToServer must be called on the page-side WebSocketRoute`); + }, + url: () => { + return this._initializer.url; + }, + protocols: () => { + return [...this._initializer.protocols]; + }, + close: async (options = {}) => { + await this._channel.closeServer({ ...options, wasClean: true }).catch(() => { + }); + }, + send: (message) => { + if (isString(message)) + this._channel.sendToServer({ message, isBase64: false }).catch(() => { + }); + else + this._channel.sendToServer({ message: message.toString("base64"), isBase64: true }).catch(() => { + }); + }, + async [Symbol.asyncDispose]() { + await this.close(); + } + }; + this._channel.on("messageFromPage", ({ message, isBase64 }) => { + if (this._onPageMessage) + this._onPageMessage(isBase64 ? Buffer.from(message, "base64") : message); + else if (this._connected) + this._channel.sendToServer({ message, isBase64 }).catch(() => { + }); + }); + this._channel.on("messageFromServer", ({ message, isBase64 }) => { + if (this._onServerMessage) + this._onServerMessage(isBase64 ? Buffer.from(message, "base64") : message); + else + this._channel.sendToPage({ message, isBase64 }).catch(() => { + }); + }); + this._channel.on("closePage", ({ code, reason, wasClean }) => { + if (this._onPageClose) + this._onPageClose(code, reason); + else + this._channel.closeServer({ code, reason, wasClean }).catch(() => { + }); + }); + this._channel.on("closeServer", ({ code, reason, wasClean }) => { + if (this._onServerClose) + this._onServerClose(code, reason); + else + this._channel.closePage({ code, reason, wasClean }).catch(() => { + }); + }); + } + static from(route2) { + return route2._object; + } + url() { + return this._initializer.url; + } + protocols() { + return [...this._initializer.protocols]; + } + async close(options = {}) { + await this._channel.closePage({ ...options, wasClean: true }).catch(() => { + }); + } + connectToServer() { + if (this._connected) + throw new Error("Already connected to the server"); + this._connected = true; + this._channel.connect().catch(() => { + }); + return this._server; + } + send(message) { + if (isString(message)) + this._channel.sendToPage({ message, isBase64: false }).catch(() => { + }); + else + this._channel.sendToPage({ message: message.toString("base64"), isBase64: true }).catch(() => { + }); + } + onMessage(handler) { + this._onPageMessage = handler; + } + onClose(handler) { + this._onPageClose = handler; + } + async [Symbol.asyncDispose]() { + await this.close(); + } + async _afterHandle() { + if (this._connected) + return; + await this._channel.ensureOpened().catch(() => { + }); + } + }; + WebSocketRouteHandler = class { + constructor(baseURL, url2, handler) { + this._baseURL = baseURL; + this.url = url2; + this.handler = handler; + if (typeof url2 === "string") + resolveGlobToRegexPattern(baseURL, url2, true); + } + static prepareInterceptionPatterns(handlers) { + const patterns = []; + let all = false; + for (const handler of handlers) { + const serialized = serializeURLMatch(handler.url); + if (serialized) + patterns.push(serialized); + else + all = true; + } + if (all) + return [{ glob: "**/*" }]; + return patterns; + } + matches(wsURL) { + return urlMatches(this._baseURL, wsURL, this.url, true); + } + async handle(webSocketRoute) { + const handler = this.handler; + await handler(webSocketRoute); + await webSocketRoute._afterHandle(); + } + }; + Response3 = class _Response extends ChannelOwner { + constructor(parent, type3, guid, initializer) { + super(parent, type3, guid, initializer); + this._finishedPromise = new ManualPromise(); + this._provisionalHeaders = new RawHeaders(initializer.headers); + this._request = Request2.from(this._initializer.request); + this._request._response = this; + Object.assign(this._request._timing, this._initializer.timing); + } + static from(response2) { + return response2._object; + } + static fromNullable(response2) { + return response2 ? _Response.from(response2) : null; + } + url() { + return this._initializer.url; + } + ok() { + return this._initializer.status === 0 || this._initializer.status >= 200 && this._initializer.status <= 299; + } + status() { + return this._initializer.status; + } + statusText() { + return this._initializer.statusText; + } + fromServiceWorker() { + return this._initializer.fromServiceWorker; + } + /** + * @deprecated + */ + headers() { + return this._provisionalHeaders.headers(); + } + async _actualHeaders() { + if (!this._actualHeadersPromise) { + this._actualHeadersPromise = (async () => { + return new RawHeaders((await this._channel.rawResponseHeaders()).headers); + })(); + } + return await this._actualHeadersPromise; + } + async allHeaders() { + return (await this._actualHeaders()).headers(); + } + async headersArray() { + return (await this._actualHeaders()).headersArray().slice(); + } + async headerValue(name) { + return (await this._actualHeaders()).get(name); + } + async headerValues(name) { + return (await this._actualHeaders()).getAll(name); + } + async finished() { + return await this.request()._targetClosedScope().race(this._finishedPromise); + } + async body() { + return (await this._channel.body()).binary; + } + async text() { + const content = await this.body(); + return content.toString("utf8"); + } + async json() { + const content = await this.text(); + return JSON.parse(content); + } + request() { + return this._request; + } + frame() { + return this._request.frame(); + } + async serverAddr() { + return (await this._channel.serverAddr()).value || null; + } + async securityDetails() { + return (await this._channel.securityDetails()).value || null; + } + async httpVersion() { + return (await this._channel.httpVersion()).value; + } + }; + WebSocket2 = class extends ChannelOwner { + static from(webSocket) { + return webSocket._object; + } + constructor(parent, type3, guid, initializer) { + super(parent, type3, guid, initializer); + this._isClosed = false; + this._page = parent; + this._channel.on("frameSent", (event) => { + if (event.opcode === 1) + this.emit(Events.WebSocket.FrameSent, { payload: event.data }); + else if (event.opcode === 2) + this.emit(Events.WebSocket.FrameSent, { payload: Buffer.from(event.data, "base64") }); + }); + this._channel.on("frameReceived", (event) => { + if (event.opcode === 1) + this.emit(Events.WebSocket.FrameReceived, { payload: event.data }); + else if (event.opcode === 2) + this.emit(Events.WebSocket.FrameReceived, { payload: Buffer.from(event.data, "base64") }); + }); + this._channel.on("socketError", ({ error }) => this.emit(Events.WebSocket.Error, error)); + this._channel.on("close", () => { + this._isClosed = true; + this.emit(Events.WebSocket.Close, this); + }); + } + url() { + return this._initializer.url; + } + isClosed() { + return this._isClosed; + } + async waitForEvent(event, optionsOrPredicate = {}) { + return await this._wrapApiCall(async () => { + const timeout = this._page._timeoutSettings.timeout(typeof optionsOrPredicate === "function" ? {} : optionsOrPredicate); + const predicate = typeof optionsOrPredicate === "function" ? optionsOrPredicate : optionsOrPredicate.predicate; + const waiter = Waiter.createForEvent(this, event); + waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`); + if (event !== Events.WebSocket.Error) + waiter.rejectOnEvent(this, Events.WebSocket.Error, new Error("Socket error")); + if (event !== Events.WebSocket.Close) + waiter.rejectOnEvent(this, Events.WebSocket.Close, new Error("Socket closed")); + waiter.rejectOnEvent(this._page, Events.Page.Close, () => this._page._closeErrorWithReason()); + const result2 = await waiter.waitForEvent(this, event, predicate); + waiter.dispose(); + return result2; + }); + } + }; + RouteHandler = class { + constructor(platform, baseURL, url2, handler, times = Number.MAX_SAFE_INTEGER) { + this.handledCount = 0; + this._ignoreException = false; + this._activeInvocations = /* @__PURE__ */ new Set(); + this._baseURL = baseURL; + this._times = times; + this.url = url2; + this.handler = handler; + this._savedZone = platform.zones.current().pop(); + if (typeof url2 === "string") + resolveGlobToRegexPattern(baseURL, url2); + } + static prepareInterceptionPatterns(handlers) { + const patterns = []; + let all = false; + for (const handler of handlers) { + const serialized = serializeURLMatch(handler.url); + if (serialized) + patterns.push(serialized); + else + all = true; + } + if (all) + return [{ glob: "**/*" }]; + return patterns; + } + matches(requestURL) { + return urlMatches(this._baseURL, requestURL, this.url); + } + async handle(route2) { + return await this._savedZone.run(async () => this._handleImpl(route2)); + } + async _handleImpl(route2) { + const handlerInvocation = { complete: new ManualPromise(), route: route2 }; + this._activeInvocations.add(handlerInvocation); + try { + return await this._handleInternal(route2); + } catch (e) { + if (this._ignoreException) + return false; + if (isTargetClosedError2(e)) { + rewriteErrorMessage(e, `"${e.message}" while running route callback. +Consider awaiting \`await page.unrouteAll({ behavior: 'ignoreErrors' })\` +before the end of the test to ignore remaining routes in flight.`); + } + throw e; + } finally { + handlerInvocation.complete.resolve(); + this._activeInvocations.delete(handlerInvocation); + } + } + async stop(behavior) { + if (behavior === "ignoreErrors") { + this._ignoreException = true; + } else { + const promises = []; + for (const activation of this._activeInvocations) { + if (!activation.route._didThrow) + promises.push(activation.complete); + } + await Promise.all(promises); + } + } + async _handleInternal(route2) { + ++this.handledCount; + const handledPromise = route2._startHandling(); + const handler = this.handler; + const [handled] = await Promise.all([ + handledPromise, + handler(route2, route2.request()) + ]); + return handled; + } + willExpire() { + return this.handledCount + 1 >= this._times; + } + }; + RawHeaders = class _RawHeaders { + constructor(headers) { + this._headersMap = new MultiMap(); + this._headersArray = headers; + for (const header of headers) + this._headersMap.set(header.name.toLowerCase(), header.value); + } + static _fromHeadersObjectLossy(headers) { + const headersArray = Object.entries(headers).map(([name, value2]) => ({ + name, + value: value2 + })).filter((header) => header.value !== void 0); + return new _RawHeaders(headersArray); + } + get(name) { + const values = this.getAll(name); + if (!values || !values.length) + return null; + return values.join(name.toLowerCase() === "set-cookie" ? "\n" : ", "); + } + getAll(name) { + return [...this._headersMap.get(name.toLowerCase())]; + } + headers() { + const result2 = {}; + for (const name of this._headersMap.keys()) + result2[name] = this.get(name); + return result2; + } + headersArray() { + return this._headersArray; + } + }; + } +}); + +// packages/playwright-core/src/client/types.ts +var kLifecycleEvents2; +var init_types2 = __esm({ + "packages/playwright-core/src/client/types.ts"() { + "use strict"; + kLifecycleEvents2 = /* @__PURE__ */ new Set(["load", "domcontentloaded", "networkidle", "commit"]); + } +}); + +// packages/playwright-core/src/client/frame.ts +function verifyLoadState(name, waitUntil) { + if (waitUntil === "networkidle0") + waitUntil = "networkidle"; + if (!kLifecycleEvents2.has(waitUntil)) + throw new Error(`${name}: expected one of (load|domcontentloaded|networkidle|commit)`); + return waitUntil; +} +var Frame2; +var init_frame = __esm({ + "packages/playwright-core/src/client/frame.ts"() { + "use strict"; + init_assert(); + init_locatorUtils(); + init_urlMatch(); + init_eventEmitter(); + init_channelOwner(); + init_clientHelper(); + init_elementHandle(); + init_errors2(); + init_events(); + init_jsHandle(); + init_locator(); + init_network3(); + init_types2(); + init_waiter(); + init_timeoutSettings(); + Frame2 = class _Frame extends ChannelOwner { + constructor(parent, type3, guid, initializer) { + super(parent, type3, guid, initializer); + this._parentFrame = null; + this._url = ""; + this._name = ""; + this._detached = false; + this._childFrames = /* @__PURE__ */ new Set(); + this._eventEmitter = new EventEmitter3(parent._platform); + this._eventEmitter.setMaxListeners(0); + this._parentFrame = _Frame.fromNullable(initializer.parentFrame); + if (this._parentFrame) + this._parentFrame._childFrames.add(this); + this._name = initializer.name; + this._url = initializer.url; + this._loadStates = new Set(initializer.loadStates); + this._channel.on("loadstate", (event) => { + if (event.add) { + this._loadStates.add(event.add); + this._eventEmitter.emit("loadstate", event.add); + } + if (event.remove) + this._loadStates.delete(event.remove); + if (!this._parentFrame && event.add === "load" && this._page) { + this._page.emit(Events.Page.Load, this._page); + this._page.context().emit(Events.BrowserContext.PageLoad, this._page); + } + if (!this._parentFrame && event.add === "domcontentloaded" && this._page) + this._page.emit(Events.Page.DOMContentLoaded, this._page); + }); + this._channel.on("navigated", (event) => { + this._url = event.url; + this._name = event.name; + this._eventEmitter.emit("navigated", event); + if (!event.error && this._page) { + this._page.emit(Events.Page.FrameNavigated, this); + this._page.context().emit(Events.BrowserContext.FrameNavigated, this); + } + }); + } + static from(frame) { + return frame._object; + } + static fromNullable(frame) { + return frame ? _Frame.from(frame) : null; + } + page() { + return this._page; + } + _timeout(options) { + const timeoutSettings = this._page?._timeoutSettings || new TimeoutSettings(this._platform); + return timeoutSettings.timeout(options || {}); + } + _navigationTimeout(options) { + const timeoutSettings = this._page?._timeoutSettings || new TimeoutSettings(this._platform); + return timeoutSettings.navigationTimeout(options || {}); + } + async goto(url2, options = {}) { + const waitUntil = verifyLoadState("waitUntil", options.waitUntil === void 0 ? "load" : options.waitUntil); + return Response3.fromNullable((await this._channel.goto({ url: url2, ...options, waitUntil, timeout: this._navigationTimeout(options) })).response); + } + _setupNavigationWaiter(options) { + const waiter = new Waiter(this._page, ""); + if (this._page.isClosed()) + waiter.rejectImmediately(this._page._closeErrorWithReason()); + waiter.rejectOnEvent(this._page, Events.Page.Close, () => this._page._closeErrorWithReason()); + waiter.rejectOnEvent(this._page, Events.Page.Crash, new Error("Navigation failed because page crashed!")); + waiter.rejectOnEvent(this._page, Events.Page.FrameDetached, new Error("Navigating frame was detached!"), (frame) => frame === this); + const timeout = this._page._timeoutSettings.navigationTimeout(options); + waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded.`); + return waiter; + } + async waitForNavigation(options = {}) { + return await this._page._wrapApiCall(async () => { + const waitUntil = verifyLoadState("waitUntil", options.waitUntil === void 0 ? "load" : options.waitUntil); + const waiter = this._setupNavigationWaiter(options); + const toUrl = typeof options.url === "string" ? ` to "${options.url}"` : ""; + waiter.log(`waiting for navigation${toUrl} until "${waitUntil}"`); + const navigatedEvent = await waiter.waitForEvent(this._eventEmitter, "navigated", (event) => { + if (event.error) + return true; + waiter.log(` navigated to "${event.url}"`); + return urlMatches(this._page?.context()._options.baseURL, event.url, options.url); + }); + if (navigatedEvent.error) { + const e = new Error(navigatedEvent.error); + e.stack = ""; + await waiter.waitForPromise(Promise.reject(e)); + } + if (!this._loadStates.has(waitUntil)) { + await waiter.waitForEvent(this._eventEmitter, "loadstate", (s) => { + waiter.log(` "${s}" event fired`); + return s === waitUntil; + }); + } + const request2 = navigatedEvent.newDocument ? Request2.fromNullable(navigatedEvent.newDocument.request) : null; + const response2 = request2 ? await waiter.waitForPromise(request2._finalRequest()._internalResponse()) : null; + waiter.dispose(); + return response2; + }, { title: "Wait for navigation" }); + } + async waitForLoadState(state = "load", options = {}) { + state = verifyLoadState("state", state); + return await this._page._wrapApiCall(async () => { + const waiter = this._setupNavigationWaiter(options); + if (this._loadStates.has(state)) { + waiter.log(` not waiting, "${state}" event already fired`); + } else { + await waiter.waitForEvent(this._eventEmitter, "loadstate", (s) => { + waiter.log(` "${s}" event fired`); + return s === state; + }); + } + waiter.dispose(); + }, { title: `Wait for load state "${state}"` }); + } + async waitForURL(url2, options = {}) { + if (urlMatches(this._page?.context()._options.baseURL, this.url(), url2)) + return await this.waitForLoadState(options.waitUntil, options); + await this.waitForNavigation({ url: url2, ...options }); + } + async frameElement() { + return ElementHandle2.from((await this._channel.frameElement()).element); + } + async evaluateHandle(pageFunction, arg) { + assertMaxArguments(arguments.length, 2); + const result2 = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: serializeArgument(arg) }); + return JSHandle2.from(result2.handle); + } + async evaluate(pageFunction, arg) { + assertMaxArguments(arguments.length, 2); + const result2 = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: serializeArgument(arg) }); + return parseResult(result2.value); + } + async _evaluateExposeUtilityScript(pageFunction, arg) { + assertMaxArguments(arguments.length, 2); + const result2 = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: serializeArgument(arg) }); + return parseResult(result2.value); + } + async $(selector, options) { + const result2 = await this._channel.querySelector({ selector, ...options }); + return ElementHandle2.fromNullable(result2.element); + } + async waitForSelector(selector, options = {}) { + if (options.visibility) + throw new Error("options.visibility is not supported, did you mean options.state?"); + if (options.waitFor && options.waitFor !== "visible") + throw new Error("options.waitFor is not supported, did you mean options.state?"); + const result2 = await this._channel.waitForSelector({ selector, ...options, timeout: this._timeout(options) }); + return ElementHandle2.fromNullable(result2.element); + } + async dispatchEvent(selector, type3, eventInit, options = {}) { + await this._channel.dispatchEvent({ selector, type: type3, eventInit: serializeArgument(eventInit), ...options, timeout: this._timeout(options) }); + } + async $eval(selector, pageFunction, arg) { + assertMaxArguments(arguments.length, 3); + const result2 = await this._channel.evalOnSelector({ selector, expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: serializeArgument(arg) }); + return parseResult(result2.value); + } + async $$eval(selector, pageFunction, arg) { + assertMaxArguments(arguments.length, 3); + const result2 = await this._channel.evalOnSelectorAll({ selector, expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: serializeArgument(arg) }); + return parseResult(result2.value); + } + async $$(selector) { + const result2 = await this._channel.querySelectorAll({ selector }); + return result2.elements.map((e) => ElementHandle2.from(e)); + } + async _queryCount(selector, options) { + return (await this._channel.queryCount({ selector, ...options })).value; + } + async content() { + return (await this._channel.content()).value; + } + async setContent(html, options = {}) { + const waitUntil = verifyLoadState("waitUntil", options.waitUntil === void 0 ? "load" : options.waitUntil); + await this._channel.setContent({ html, ...options, waitUntil, timeout: this._navigationTimeout(options) }); + } + name() { + return this._name || ""; + } + url() { + return this._url; + } + parentFrame() { + return this._parentFrame; + } + childFrames() { + return Array.from(this._childFrames); + } + isDetached() { + return this._detached; + } + async addScriptTag(options = {}) { + const copy = { ...options }; + if (copy.path) { + copy.content = (await this._platform.fs().promises.readFile(copy.path)).toString(); + copy.content = addSourceUrlToScript(copy.content, copy.path); + } + return ElementHandle2.from((await this._channel.addScriptTag({ ...copy })).element); + } + async addStyleTag(options = {}) { + const copy = { ...options }; + if (copy.path) { + copy.content = (await this._platform.fs().promises.readFile(copy.path)).toString(); + copy.content += "/*# sourceURL=" + copy.path.replace(/\n/g, "") + "*/"; + } + return ElementHandle2.from((await this._channel.addStyleTag({ ...copy })).element); + } + async click(selector, options = {}) { + return await this._channel.click({ selector, ...options, timeout: this._timeout(options) }); + } + async dblclick(selector, options = {}) { + return await this._channel.dblclick({ selector, ...options, timeout: this._timeout(options) }); + } + async dragAndDrop(source11, target, options = {}) { + return await this._channel.dragAndDrop({ source: source11, target, ...options, timeout: this._timeout(options) }); + } + async _drop(selector, payload, options = {}) { + let fileParams = {}; + if (payload.files !== void 0) { + const converted = await convertInputFiles(this._platform, payload.files, this.page().context()); + if (converted.localDirectory || converted.directoryStream) + throw new Error("Dropping a directory is not supported \u2014 pass individual files."); + fileParams = { payloads: converted.payloads, localPaths: converted.localPaths, streams: converted.streams }; + } + const dataArray = payload.data ? Object.entries(payload.data).map(([mimeType, value2]) => ({ mimeType, value: value2 })) : void 0; + await this._channel.drop({ + selector, + ...fileParams, + data: dataArray, + ...options, + timeout: this._timeout(options) + }); + } + async tap(selector, options = {}) { + return await this._channel.tap({ selector, ...options, timeout: this._timeout(options) }); + } + async fill(selector, value2, options = {}) { + return await this._channel.fill({ selector, value: value2, ...options, timeout: this._timeout(options) }); + } + async _highlight(selector, style) { + return await this._channel.highlight({ selector, style }); + } + async _hideHighlight(selector) { + return await this._channel.hideHighlight({ selector }); + } + locator(selector, options) { + return new Locator(this, selector, options); + } + getByTestId(testId) { + return this.locator(getByTestIdSelector(testIdAttributeName(), testId)); + } + getByAltText(text2, options) { + return this.locator(getByAltTextSelector(text2, options)); + } + getByLabel(text2, options) { + return this.locator(getByLabelSelector(text2, options)); + } + getByPlaceholder(text2, options) { + return this.locator(getByPlaceholderSelector(text2, options)); + } + getByText(text2, options) { + return this.locator(getByTextSelector(text2, options)); + } + getByTitle(text2, options) { + return this.locator(getByTitleSelector(text2, options)); + } + getByRole(role, options = {}) { + return this.locator(getByRoleSelector(role, options)); + } + frameLocator(selector) { + return new FrameLocator(this, selector); + } + async focus(selector, options = {}) { + await this._channel.focus({ selector, ...options, timeout: this._timeout(options) }); + } + async textContent(selector, options = {}) { + const value2 = (await this._channel.textContent({ selector, ...options, timeout: this._timeout(options) })).value; + return value2 === void 0 ? null : value2; + } + async innerText(selector, options = {}) { + return (await this._channel.innerText({ selector, ...options, timeout: this._timeout(options) })).value; + } + async innerHTML(selector, options = {}) { + return (await this._channel.innerHTML({ selector, ...options, timeout: this._timeout(options) })).value; + } + async getAttribute(selector, name, options = {}) { + const value2 = (await this._channel.getAttribute({ selector, name, ...options, timeout: this._timeout(options) })).value; + return value2 === void 0 ? null : value2; + } + async inputValue(selector, options = {}) { + return (await this._channel.inputValue({ selector, ...options, timeout: this._timeout(options) })).value; + } + async isChecked(selector, options = {}) { + return (await this._channel.isChecked({ selector, ...options, timeout: this._timeout(options) })).value; + } + async isDisabled(selector, options = {}) { + return (await this._channel.isDisabled({ selector, ...options, timeout: this._timeout(options) })).value; + } + async isEditable(selector, options = {}) { + return (await this._channel.isEditable({ selector, ...options, timeout: this._timeout(options) })).value; + } + async isEnabled(selector, options = {}) { + return (await this._channel.isEnabled({ selector, ...options, timeout: this._timeout(options) })).value; + } + async isHidden(selector, options = {}) { + return (await this._channel.isHidden({ selector, ...options })).value; + } + async isVisible(selector, options = {}) { + return (await this._channel.isVisible({ selector, ...options })).value; + } + async hover(selector, options = {}) { + await this._channel.hover({ selector, ...options, timeout: this._timeout(options) }); + } + async selectOption(selector, values, options = {}) { + return (await this._channel.selectOption({ selector, ...convertSelectOptionValues(values), ...options, timeout: this._timeout(options) })).values; + } + async setInputFiles(selector, files, options = {}) { + const converted = await convertInputFiles(this._platform, files, this.page().context()); + await this._channel.setInputFiles({ selector, ...converted, ...options, timeout: this._timeout(options) }); + } + async type(selector, text2, options = {}) { + await this._channel.type({ selector, text: text2, ...options, timeout: this._timeout(options) }); + } + async press(selector, key, options = {}) { + await this._channel.press({ selector, key, ...options, timeout: this._timeout(options) }); + } + async check(selector, options = {}) { + await this._channel.check({ selector, ...options, timeout: this._timeout(options) }); + } + async uncheck(selector, options = {}) { + await this._channel.uncheck({ selector, ...options, timeout: this._timeout(options) }); + } + async setChecked(selector, checked, options) { + if (checked) + await this.check(selector, options); + else + await this.uncheck(selector, options); + } + async waitForTimeout(timeout) { + await this._channel.waitForTimeout({ waitTimeout: timeout }); + } + async waitForFunction(pageFunction, arg, options = {}) { + if (typeof options.polling === "string") + assert(options.polling === "raf", "Unknown polling option: " + options.polling); + const result2 = await this._channel.waitForFunction({ + ...options, + pollingInterval: options.polling === "raf" ? void 0 : options.polling, + expression: String(pageFunction), + isFunction: typeof pageFunction === "function", + arg: serializeArgument(arg), + timeout: this._timeout(options) + }); + return JSHandle2.from(result2.handle); + } + async title() { + return (await this._channel.title()).value; + } + async _expect(expression2, options) { + const params2 = { expression: expression2, ...options, isNot: !!options.isNot }; + params2.expectedValue = serializeArgument(options.expectedValue); + try { + await this._channel.expect(params2); + return { matches: !params2.isNot }; + } catch (e) { + if (!(e instanceof PlaywrightError)) + throw e; + const details = e.details; + const received = details.received ? { + value: details.received.value !== void 0 ? parseResult(details.received.value) : void 0, + ariaSnapshot: details.received.ariaSnapshot + } : void 0; + return { + matches: !!params2.isNot, + received, + log: e.log, + timedOut: details.timedOut, + errorMessage: details.customErrorMessage ? "Error: " + details.customErrorMessage : void 0 + }; + } + } + }; + } +}); + +// packages/playwright-core/src/client/writableStream.ts +var WritableStream; +var init_writableStream = __esm({ + "packages/playwright-core/src/client/writableStream.ts"() { + "use strict"; + init_channelOwner(); + WritableStream = class extends ChannelOwner { + static from(Stream2) { + return Stream2._object; + } + constructor(parent, type3, guid, initializer) { + super(parent, type3, guid, initializer); + } + stream() { + return this._platform.streamWritable(this._channel); + } + }; + } +}); + +// packages/playwright-core/src/client/elementHandle.ts +function convertSelectOptionValues(values) { + if (values === null) + return {}; + if (!Array.isArray(values)) + values = [values]; + if (!values.length) + return {}; + for (let i = 0; i < values.length; i++) + assert(values[i] !== null, `options[${i}]: expected object, got null`); + if (values[0] instanceof ElementHandle2) + return { elements: values.map((v) => v._elementChannel) }; + if (isString(values[0])) + return { options: values.map((valueOrLabel) => ({ valueOrLabel })) }; + return { options: values }; +} +function filePayloadExceedsSizeLimit(payloads) { + return payloads.reduce((size, item) => size + (item.buffer ? item.buffer.byteLength : 0), 0) >= fileUploadSizeLimit2; +} +async function resolvePathsAndDirectoryForInputFiles(platform, items) { + let localPaths; + let localDirectory; + for (const item of items) { + const stat = await platform.fs().promises.stat(item); + if (stat.isDirectory()) { + if (localDirectory) + throw new Error("Multiple directories are not supported"); + localDirectory = platform.path().resolve(item); + } else { + localPaths ??= []; + localPaths.push(platform.path().resolve(item)); + } + } + if (localPaths?.length && localDirectory) + throw new Error("File paths must be all files or a single directory"); + return [localPaths, localDirectory]; +} +async function convertInputFiles(platform, files, context2) { + const items = Array.isArray(files) ? files.slice() : [files]; + if (items.some((item) => typeof item === "string")) { + if (!items.every((item) => typeof item === "string")) + throw new Error("File paths cannot be mixed with buffers"); + const [localPaths, localDirectory] = await resolvePathsAndDirectoryForInputFiles(platform, items); + if (context2._connection.isRemote()) { + const files2 = localDirectory ? (await platform.fs().promises.readdir(localDirectory, { withFileTypes: true, recursive: true })).filter((f) => f.isFile()).map((f) => platform.path().join(f.parentPath, f.name)) : localPaths; + const { writableStreams, rootDir } = await context2._wrapApiCall(async () => context2._channel.createTempFiles({ + rootDirName: localDirectory ? platform.path().basename(localDirectory) : void 0, + items: await Promise.all(files2.map(async (file) => { + const lastModifiedMs = (await platform.fs().promises.stat(file)).mtimeMs; + return { + name: localDirectory ? platform.path().relative(localDirectory, file) : platform.path().basename(file), + lastModifiedMs + }; + })) + }), { internal: true }); + for (let i = 0; i < files2.length; i++) { + const writable = WritableStream.from(writableStreams[i]); + await platform.streamFile(files2[i], writable.stream()); + } + return { + directoryStream: rootDir, + streams: localDirectory ? void 0 : writableStreams + }; + } + return { + localPaths, + localDirectory + }; + } + const payloads = items; + if (filePayloadExceedsSizeLimit(payloads)) + throw new Error("Cannot set buffer larger than 50Mb, please write it to a file and pass its path instead."); + return { payloads }; +} +function determineScreenshotType(options) { + if (options.path) { + const mimeType = getMimeTypeForPath(options.path); + if (mimeType === "image/png") + return "png"; + else if (mimeType === "image/jpeg") + return "jpeg"; + throw new Error(`path: unsupported mime type "${mimeType}"`); + } + return options.type; +} +var ElementHandle2; +var init_elementHandle = __esm({ + "packages/playwright-core/src/client/elementHandle.ts"() { + "use strict"; + init_assert(); + init_rtti(); + init_mimeType(); + init_frame(); + init_jsHandle(); + init_fileUtils2(); + init_writableStream(); + ElementHandle2 = class _ElementHandle extends JSHandle2 { + static from(handle) { + return handle._object; + } + static fromNullable(handle) { + return handle ? _ElementHandle.from(handle) : null; + } + constructor(parent, type3, guid, initializer) { + super(parent, type3, guid, initializer); + this._frame = parent; + this._elementChannel = this._channel; + } + asElement() { + return this; + } + async ownerFrame() { + return Frame2.fromNullable((await this._elementChannel.ownerFrame()).frame); + } + async contentFrame() { + return Frame2.fromNullable((await this._elementChannel.contentFrame()).frame); + } + async getAttribute(name) { + const value2 = (await this._elementChannel.getAttribute({ name })).value; + return value2 === void 0 ? null : value2; + } + async inputValue() { + return (await this._elementChannel.inputValue()).value; + } + async textContent() { + const value2 = (await this._elementChannel.textContent()).value; + return value2 === void 0 ? null : value2; + } + async innerText() { + return (await this._elementChannel.innerText()).value; + } + async innerHTML() { + return (await this._elementChannel.innerHTML()).value; + } + async isChecked() { + return (await this._elementChannel.isChecked()).value; + } + async isDisabled() { + return (await this._elementChannel.isDisabled()).value; + } + async isEditable() { + return (await this._elementChannel.isEditable()).value; + } + async isEnabled() { + return (await this._elementChannel.isEnabled()).value; + } + async isHidden() { + return (await this._elementChannel.isHidden()).value; + } + async isVisible() { + return (await this._elementChannel.isVisible()).value; + } + async dispatchEvent(type3, eventInit = {}) { + await this._elementChannel.dispatchEvent({ type: type3, eventInit: serializeArgument(eventInit) }); + } + async scrollIntoViewIfNeeded(options = {}) { + await this._elementChannel.scrollIntoViewIfNeeded({ ...options, timeout: this._frame._timeout(options) }); + } + async hover(options = {}) { + await this._elementChannel.hover({ ...options, timeout: this._frame._timeout(options) }); + } + async click(options = {}) { + return await this._elementChannel.click({ ...options, timeout: this._frame._timeout(options) }); + } + async dblclick(options = {}) { + return await this._elementChannel.dblclick({ ...options, timeout: this._frame._timeout(options) }); + } + async tap(options = {}) { + return await this._elementChannel.tap({ ...options, timeout: this._frame._timeout(options) }); + } + async selectOption(values, options = {}) { + const result2 = await this._elementChannel.selectOption({ ...convertSelectOptionValues(values), ...options, timeout: this._frame._timeout(options) }); + return result2.values; + } + async fill(value2, options = {}) { + return await this._elementChannel.fill({ value: value2, ...options, timeout: this._frame._timeout(options) }); + } + async selectText(options = {}) { + await this._elementChannel.selectText({ ...options, timeout: this._frame._timeout(options) }); + } + async setInputFiles(files, options = {}) { + const frame = await this.ownerFrame(); + if (!frame) + throw new Error("Cannot set input files to detached element"); + const converted = await convertInputFiles(this._platform, files, frame.page().context()); + await this._elementChannel.setInputFiles({ ...converted, ...options, timeout: this._frame._timeout(options) }); + } + async focus() { + await this._elementChannel.focus(); + } + async type(text2, options = {}) { + await this._elementChannel.type({ text: text2, ...options, timeout: this._frame._timeout(options) }); + } + async press(key, options = {}) { + await this._elementChannel.press({ key, ...options, timeout: this._frame._timeout(options) }); + } + async check(options = {}) { + return await this._elementChannel.check({ ...options, timeout: this._frame._timeout(options) }); + } + async uncheck(options = {}) { + return await this._elementChannel.uncheck({ ...options, timeout: this._frame._timeout(options) }); + } + async setChecked(checked, options) { + if (checked) + await this.check(options); + else + await this.uncheck(options); + } + async boundingBox() { + const value2 = (await this._elementChannel.boundingBox()).value; + return value2 === void 0 ? null : value2; + } + async screenshot(options = {}) { + const mask = options.mask; + const copy = { ...options, mask: void 0, timeout: this._frame._timeout(options) }; + if (!copy.type) + copy.type = determineScreenshotType(options); + if (mask) { + copy.mask = mask.map((locator2) => ({ + frame: locator2._frame._channel, + selector: locator2._selector + })); + } + const result2 = await this._elementChannel.screenshot(copy); + if (options.path) { + await mkdirIfNeeded2(this._platform, options.path); + await this._platform.fs().promises.writeFile(options.path, result2.binary); + } + return result2.binary; + } + async $(selector) { + return _ElementHandle.fromNullable((await this._elementChannel.querySelector({ selector })).element); + } + async $$(selector) { + const result2 = await this._elementChannel.querySelectorAll({ selector }); + return result2.elements.map((h) => _ElementHandle.from(h)); + } + async $eval(selector, pageFunction, arg) { + const result2 = await this._elementChannel.evalOnSelector({ selector, expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: serializeArgument(arg) }); + return parseResult(result2.value); + } + async $$eval(selector, pageFunction, arg) { + const result2 = await this._elementChannel.evalOnSelectorAll({ selector, expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: serializeArgument(arg) }); + return parseResult(result2.value); + } + async waitForElementState(state, options = {}) { + return await this._elementChannel.waitForElementState({ state, ...options, timeout: this._frame._timeout(options) }); + } + async waitForSelector(selector, options = {}) { + const result2 = await this._elementChannel.waitForSelector({ selector, ...options, timeout: this._frame._timeout(options) }); + return _ElementHandle.fromNullable(result2.element); + } + }; + } +}); + +// packages/playwright-core/src/client/fileChooser.ts +var FileChooser2; +var init_fileChooser2 = __esm({ + "packages/playwright-core/src/client/fileChooser.ts"() { + "use strict"; + FileChooser2 = class { + constructor(page, elementHandle, isMultiple) { + this._page = page; + this._elementHandle = elementHandle; + this._isMultiple = isMultiple; + } + element() { + return this._elementHandle; + } + isMultiple() { + return this._isMultiple; + } + page() { + return this._page; + } + async setFiles(files, options) { + return await this._elementHandle.setInputFiles(files, options); + } + }; + } +}); + +// packages/playwright-core/src/client/harRouter.ts +var HarRouter; +var init_harRouter = __esm({ + "packages/playwright-core/src/client/harRouter.ts"() { + "use strict"; + HarRouter = class _HarRouter { + static async create(localUtils, file, notFoundAction, options) { + const { harId, error } = await localUtils.harOpen({ file }); + if (error) + throw new Error(error); + return new _HarRouter(localUtils, harId, notFoundAction, options); + } + constructor(localUtils, harId, notFoundAction, options) { + this._localUtils = localUtils; + this._harId = harId; + this._options = options; + this._notFoundAction = notFoundAction; + } + async _handle(route2) { + const request2 = route2.request(); + const response2 = await this._localUtils.harLookup({ + harId: this._harId, + url: request2.url(), + method: request2.method(), + headers: await request2.headersArray(), + postData: request2.postDataBuffer() || void 0, + isNavigationRequest: request2.isNavigationRequest() + }); + if (response2.action === "redirect") { + route2._platform.log("api", `HAR: ${route2.request().url()} redirected to ${response2.redirectURL}`); + await route2._redirectNavigationRequest(response2.redirectURL); + return; + } + if (response2.action === "fulfill") { + if (response2.status === -1) + return; + const transformedHeaders = response2.headers.reduce((headersMap, { name, value: value2 }) => { + if (name.toLowerCase() !== "set-cookie") { + headersMap[name] = value2; + } else { + if (!headersMap["set-cookie"]) + headersMap["set-cookie"] = value2; + else + headersMap["set-cookie"] += ` +${value2}`; + } + return headersMap; + }, {}); + await route2.fulfill({ + status: response2.status, + headers: transformedHeaders, + body: response2.body + }); + return; + } + if (response2.action === "error") + route2._platform.log("api", "HAR: " + response2.message); + if (this._notFoundAction === "abort") { + await route2.abort(); + return; + } + await route2.fallback(); + } + async addContextRoute(context2) { + await context2.route(this._options.urlMatch || "**/*", (route2) => this._handle(route2)); + } + async addPageRoute(page) { + await page.route(this._options.urlMatch || "**/*", (route2) => this._handle(route2)); + } + async [Symbol.asyncDispose]() { + await this.dispose(); + } + dispose() { + this._localUtils.harClose({ harId: this._harId }).catch(() => { + }); + } + }; + } +}); + +// packages/playwright-core/src/client/input.ts +var Keyboard2, Mouse2, Touchscreen2; +var init_input2 = __esm({ + "packages/playwright-core/src/client/input.ts"() { + "use strict"; + Keyboard2 = class { + constructor(page) { + this._page = page; + } + async down(key) { + await this._page._channel.keyboardDown({ key }); + } + async up(key) { + await this._page._channel.keyboardUp({ key }); + } + async insertText(text2) { + await this._page._channel.keyboardInsertText({ text: text2 }); + } + async type(text2, options = {}) { + await this._page._channel.keyboardType({ text: text2, ...options }); + } + async press(key, options = {}) { + await this._page._channel.keyboardPress({ key, ...options }); + } + }; + Mouse2 = class { + constructor(page) { + this._page = page; + } + async move(x, y, options = {}) { + await this._page._channel.mouseMove({ x, y, ...options }); + } + async down(options = {}) { + await this._page._channel.mouseDown({ ...options }); + } + async up(options = {}) { + await this._page._channel.mouseUp(options); + } + async click(x, y, options = {}) { + await this._page._channel.mouseClick({ x, y, ...options }); + } + async dblclick(x, y, options = {}) { + await this._page._wrapApiCall(async () => { + await this.click(x, y, { ...options, clickCount: 2 }); + }, { title: "Double click" }); + } + async wheel(deltaX, deltaY) { + await this._page._channel.mouseWheel({ deltaX, deltaY }); + } + }; + Touchscreen2 = class { + constructor(page) { + this._page = page; + } + async tap(x, y) { + await this._page._channel.touchscreenTap({ x, y }); + } + }; + } +}); + +// packages/playwright-core/src/client/webStorage.ts +var WebStorage; +var init_webStorage = __esm({ + "packages/playwright-core/src/client/webStorage.ts"() { + "use strict"; + WebStorage = class { + constructor(page, kind) { + this._page = page; + this._kind = kind; + } + async items() { + const { items } = await this._page._channel.webStorageItems({ kind: this._kind }); + return items; + } + async getItem(name) { + const { value: value2 } = await this._page._channel.webStorageGetItem({ kind: this._kind, name }); + return value2 ?? null; + } + async setItem(name, value2) { + await this._page._channel.webStorageSetItem({ kind: this._kind, name, value: value2 }); + } + async removeItem(name) { + await this._page._channel.webStorageRemoveItem({ kind: this._kind, name }); + } + async clear() { + await this._page._channel.webStorageClear({ kind: this._kind }); + } + }; + } +}); + +// packages/playwright-core/src/client/video.ts +var Video; +var init_video = __esm({ + "packages/playwright-core/src/client/video.ts"() { + "use strict"; + init_eventEmitter(); + Video = class extends EventEmitter3 { + constructor(page, connection, artifact) { + super(page._platform); + this._isRemote = false; + this._isRemote = connection.isRemote(); + this._artifact = artifact; + } + async path() { + if (this._isRemote) + throw new Error(`Path is not available when connecting remotely. Use saveAs() to save a local copy.`); + if (!this._artifact) + throw new Error("Video recording has not been started."); + return this._artifact._initializer.absolutePath; + } + async saveAs(path59) { + if (!this._artifact) + throw new Error("Video recording has not been started."); + return await this._artifact.saveAs(path59); + } + async delete() { + if (this._artifact) + await this._artifact.delete(); + } + }; + } +}); + +// packages/playwright-core/src/client/screencast.ts +var Screencast2; +var init_screencast2 = __esm({ + "packages/playwright-core/src/client/screencast.ts"() { + "use strict"; + init_artifact2(); + init_disposable3(); + Screencast2 = class { + constructor(page) { + this._started = false; + this._onFrame = null; + this._page = page; + this._page._channel.on("screencastFrame", ({ data, timestamp, viewportWidth, viewportHeight }) => { + void this._onFrame?.({ data, timestamp, viewportWidth, viewportHeight }); + }); + } + async start(options = {}) { + if (this._started) + throw new Error("Screencast is already started"); + this._started = true; + if (options.onFrame) + this._onFrame = options.onFrame; + const result2 = await this._page._channel.screencastStart({ + size: options.size, + quality: options.quality, + sendFrames: !!options.onFrame, + record: !!options.path + }); + if (result2.artifact) { + this._artifact = Artifact2.from(result2.artifact); + this._savePath = options.path; + } + return new DisposableStub(() => this.stop()); + } + async stop() { + await this._page._wrapApiCall(async () => { + this._started = false; + this._onFrame = null; + await this._page._channel.screencastStop(); + if (this._savePath) + await this._artifact?.saveAs(this._savePath); + this._artifact = void 0; + this._savePath = void 0; + }); + } + async showActions(options) { + await this._page._channel.screencastShowActions({ duration: options?.duration, position: options?.position, fontSize: options?.fontSize, cursor: options?.cursor }); + return new DisposableStub(() => this._page._channel.screencastHideActions()); + } + async hideActions() { + await this._page._channel.screencastHideActions(); + } + async showOverlay(html, options) { + const { id } = await this._page._channel.screencastShowOverlay({ html, duration: options?.duration }); + return new DisposableStub(() => this._page._channel.screencastRemoveOverlay({ id })); + } + async showChapter(title, options) { + await this._page._channel.screencastChapter({ title, ...options }); + } + async showOverlays() { + await this._page._channel.screencastSetOverlayVisible({ visible: true }); + } + async hideOverlays() { + await this._page._channel.screencastSetOverlayVisible({ visible: false }); + } + }; + } +}); + +// packages/playwright-core/src/client/page.ts +function trimUrl(param) { + if (isRegExp(param)) + return `/${trimStringWithEllipsis(param.source, 50)}/${param.flags}`; + if (isString(param)) + return `"${trimStringWithEllipsis(param, 50)}"`; +} +var Page2, BindingCall; +var init_page2 = __esm({ + "packages/playwright-core/src/client/page.ts"() { + "use strict"; + init_assert(); + init_headers(); + init_stringUtils(); + init_urlMatch(); + init_manualPromise(); + init_rtti(); + init_artifact2(); + init_channelOwner(); + init_clientHelper(); + init_coverage(); + init_disposable3(); + init_download2(); + init_elementHandle(); + init_errors2(); + init_events(); + init_fileChooser2(); + init_frame(); + init_harRouter(); + init_input2(); + init_webStorage(); + init_jsHandle(); + init_network3(); + init_video(); + init_screencast2(); + init_waiter(); + init_worker(); + init_timeoutSettings(); + init_fileUtils2(); + init_consoleMessage(); + Page2 = class _Page extends ChannelOwner { + constructor(parent, type3, guid, initializer) { + super(parent, type3, guid, initializer); + this._apiName = "Page"; + this._frames = /* @__PURE__ */ new Set(); + this._workers = /* @__PURE__ */ new Set(); + this._closed = false; + this._closedOrCrashedScope = new LongStandingScope(); + this._routes = []; + this._webSocketRoutes = []; + this._bindings = /* @__PURE__ */ new Map(); + this._closeWasCalled = false; + this._harRouters = []; + this._locatorHandlers = /* @__PURE__ */ new Map(); + this._instrumentation.onPage(this); + this._browserContext = parent; + this._timeoutSettings = new TimeoutSettings(this._platform, this._browserContext._timeoutSettings); + this.keyboard = new Keyboard2(this); + this.mouse = new Mouse2(this); + this.request = this._browserContext.request; + this.touchscreen = new Touchscreen2(this); + this.clock = this._browserContext.clock; + this.localStorage = new WebStorage(this, "local"); + this.sessionStorage = new WebStorage(this, "session"); + this._mainFrame = Frame2.from(initializer.mainFrame); + this._mainFrame._page = this; + this._frames.add(this._mainFrame); + this._viewportSize = initializer.viewportSize; + this._closed = initializer.isClosed; + this._opener = _Page.fromNullable(initializer.opener); + this._video = new Video(this, this._connection, initializer.video ? Artifact2.from(initializer.video) : void 0); + this.screencast = new Screencast2(this); + this._channel.on("bindingCall", ({ binding }) => this._onBinding(BindingCall.from(binding))); + this._channel.on("close", () => this._onClose()); + this._channel.on("crash", () => this._onCrash()); + this._channel.on("download", ({ url: url2, suggestedFilename, artifact }) => { + const artifactObject = Artifact2.from(artifact); + const download = new Download2(this, url2, suggestedFilename, artifactObject); + this.emit(Events.Page.Download, download); + this._browserContext.emit(Events.BrowserContext.Download, download); + }); + this._channel.on("fileChooser", ({ element: element2, isMultiple }) => this.emit(Events.Page.FileChooser, new FileChooser2(this, ElementHandle2.from(element2), isMultiple))); + this._channel.on("frameAttached", ({ frame }) => this._onFrameAttached(Frame2.from(frame))); + this._channel.on("frameDetached", ({ frame }) => this._onFrameDetached(Frame2.from(frame))); + this._channel.on("locatorHandlerTriggered", ({ uid }) => this._onLocatorHandlerTriggered(uid)); + this._channel.on("route", ({ route: route2 }) => this._onRoute(Route2.from(route2))); + this._channel.on("webSocketRoute", ({ webSocketRoute }) => this._onWebSocketRoute(WebSocketRoute.from(webSocketRoute))); + this._channel.on("viewportSizeChanged", ({ viewportSize }) => this._viewportSize = viewportSize); + this._channel.on("webSocket", ({ webSocket }) => this.emit(Events.Page.WebSocket, WebSocket2.from(webSocket))); + this._channel.on("worker", ({ worker }) => this._onWorker(Worker2.from(worker))); + this.coverage = new Coverage(this._channel); + this.once(Events.Page.Close, () => this._closedOrCrashedScope.close(this._closeErrorWithReason())); + this.once(Events.Page.Crash, () => this._closedOrCrashedScope.close(new TargetClosedError2())); + this._setEventToSubscriptionMapping(/* @__PURE__ */ new Map([ + [Events.Page.Console, "console"], + [Events.Page.Dialog, "dialog"], + [Events.Page.Request, "request"], + [Events.Page.Response, "response"], + [Events.Page.RequestFinished, "requestFinished"], + [Events.Page.RequestFailed, "requestFailed"], + [Events.Page.FileChooser, "fileChooser"] + ])); + } + static from(page) { + return page._object; + } + static fromNullable(page) { + return page ? _Page.from(page) : null; + } + _onFrameAttached(frame) { + frame._page = this; + this._frames.add(frame); + if (frame._parentFrame) + frame._parentFrame._childFrames.add(frame); + this.emit(Events.Page.FrameAttached, frame); + this._browserContext.emit(Events.BrowserContext.FrameAttached, frame); + } + _onFrameDetached(frame) { + this._frames.delete(frame); + frame._detached = true; + if (frame._parentFrame) + frame._parentFrame._childFrames.delete(frame); + this.emit(Events.Page.FrameDetached, frame); + this._browserContext.emit(Events.BrowserContext.FrameDetached, frame); + } + async _onRoute(route2) { + route2._context = this.context(); + const routeHandlers = this._routes.slice(); + for (const routeHandler of routeHandlers) { + if (this._closeWasCalled || this._browserContext.isClosed()) + return; + if (!routeHandler.matches(route2.request().url())) + continue; + const index = this._routes.indexOf(routeHandler); + if (index === -1) + continue; + if (routeHandler.willExpire()) + this._routes.splice(index, 1); + const handled = await routeHandler.handle(route2); + if (!this._routes.length) + this._updateInterceptionPatterns({ internal: true }).catch(() => { + }); + if (handled) + return; + } + await this._browserContext._onRoute(route2); + } + async _onWebSocketRoute(webSocketRoute) { + const routeHandler = this._webSocketRoutes.find((route2) => route2.matches(webSocketRoute.url())); + if (routeHandler) + await routeHandler.handle(webSocketRoute); + else + await this._browserContext._onWebSocketRoute(webSocketRoute); + } + async _onBinding(bindingCall) { + const func = this._bindings.get(bindingCall._initializer.name); + if (func) { + await bindingCall.call(func); + return; + } + await this._browserContext._onBinding(bindingCall); + } + _onWorker(worker) { + this._workers.add(worker); + worker._page = this; + this.emit(Events.Page.Worker, worker); + } + _onClose() { + this._closed = true; + this._browserContext._pages.delete(this); + this._disposeHarRouters(); + this.emit(Events.Page.Close, this); + this._browserContext.emit(Events.BrowserContext.PageClose, this); + } + _onCrash() { + this.emit(Events.Page.Crash, this); + } + context() { + return this._browserContext; + } + async opener() { + if (!this._opener || this._opener.isClosed()) + return null; + return this._opener; + } + mainFrame() { + return this._mainFrame; + } + frame(frameSelector) { + const name = isString(frameSelector) ? frameSelector : frameSelector.name; + const url2 = isObject(frameSelector) ? frameSelector.url : void 0; + assert(name || url2, "Either name or url matcher should be specified"); + return this.frames().find((f) => { + if (name) + return f.name() === name; + return urlMatches(this._browserContext._options.baseURL, f.url(), url2); + }) || null; + } + frames() { + return [...this._frames]; + } + setDefaultNavigationTimeout(timeout) { + this._timeoutSettings.setDefaultNavigationTimeout(timeout); + } + setDefaultTimeout(timeout) { + this._timeoutSettings.setDefaultTimeout(timeout); + } + video() { + if (!this._browserContext._options.recordVideo) + return null; + return this._video; + } + async pickLocator() { + const { selector } = await this._channel.pickLocator({}); + return this.locator(selector); + } + async cancelPickLocator() { + await this._channel.cancelPickLocator({}); + } + async hideHighlight() { + await this._channel.hideHighlight({}); + } + async $(selector, options) { + return await this._mainFrame.$(selector, options); + } + async waitForSelector(selector, options) { + return await this._mainFrame.waitForSelector(selector, options); + } + async dispatchEvent(selector, type3, eventInit, options) { + return await this._mainFrame.dispatchEvent(selector, type3, eventInit, options); + } + async evaluateHandle(pageFunction, arg) { + assertMaxArguments(arguments.length, 2); + return await this._mainFrame.evaluateHandle(pageFunction, arg); + } + async $eval(selector, pageFunction, arg) { + assertMaxArguments(arguments.length, 3); + return await this._mainFrame.$eval(selector, pageFunction, arg); + } + async $$eval(selector, pageFunction, arg) { + assertMaxArguments(arguments.length, 3); + return await this._mainFrame.$$eval(selector, pageFunction, arg); + } + async $$(selector) { + return await this._mainFrame.$$(selector); + } + async addScriptTag(options = {}) { + return await this._mainFrame.addScriptTag(options); + } + async addStyleTag(options = {}) { + return await this._mainFrame.addStyleTag(options); + } + async exposeFunction(name, callback) { + const result2 = await this._channel.exposeBinding({ name }); + const binding = (source11, ...args) => callback(...args); + this._bindings.set(name, binding); + return DisposableObject2.from(result2.disposable); + } + async exposeBinding(name, callback) { + const result2 = await this._channel.exposeBinding({ name }); + this._bindings.set(name, callback); + return DisposableObject2.from(result2.disposable); + } + async setExtraHTTPHeaders(headers) { + validateHeaders(headers); + await this._channel.setExtraHTTPHeaders({ headers: headersObjectToArray(headers) }); + } + url() { + return this._mainFrame.url(); + } + async content() { + return await this._mainFrame.content(); + } + async setContent(html, options) { + return await this._mainFrame.setContent(html, options); + } + async goto(url2, options) { + return await this._mainFrame.goto(url2, options); + } + async reload(options = {}) { + const waitUntil = verifyLoadState("waitUntil", options.waitUntil === void 0 ? "load" : options.waitUntil); + return Response3.fromNullable((await this._channel.reload({ ...options, waitUntil, timeout: this._timeoutSettings.navigationTimeout(options) })).response); + } + async addLocatorHandler(locator2, handler, options = {}) { + if (locator2._frame !== this._mainFrame) + throw new Error(`Locator must belong to the main frame of this page`); + if (options.times === 0) + return; + const { uid } = await this._channel.registerLocatorHandler({ selector: locator2._selector, noWaitAfter: options.noWaitAfter }); + this._locatorHandlers.set(uid, { locator: locator2, handler, times: options.times }); + } + async _onLocatorHandlerTriggered(uid) { + let remove = false; + try { + const handler = this._locatorHandlers.get(uid); + if (handler && handler.times !== 0) { + if (handler.times !== void 0) + handler.times--; + await handler.handler(handler.locator); + } + remove = handler?.times === 0; + } finally { + if (remove) + this._locatorHandlers.delete(uid); + this._channel.resolveLocatorHandlerNoReply({ uid, remove }).catch(() => { + }); + } + } + async removeLocatorHandler(locator2) { + for (const [uid, data] of this._locatorHandlers) { + if (data.locator._equals(locator2)) { + this._locatorHandlers.delete(uid); + await this._channel.unregisterLocatorHandler({ uid }).catch(() => { + }); + } + } + } + async waitForLoadState(state, options) { + return await this._mainFrame.waitForLoadState(state, options); + } + async waitForNavigation(options) { + return await this._mainFrame.waitForNavigation(options); + } + async waitForURL(url2, options) { + return await this._mainFrame.waitForURL(url2, options); + } + async waitForRequest(urlOrPredicate, options = {}) { + const predicate = async (request2) => { + if (isString(urlOrPredicate) || isRegExp(urlOrPredicate)) + return urlMatches(this._browserContext._options.baseURL, request2.url(), urlOrPredicate); + return await urlOrPredicate(request2); + }; + const trimmedUrl = trimUrl(urlOrPredicate); + const logLine = trimmedUrl ? `waiting for request ${trimmedUrl}` : void 0; + return await this._waitForEvent(Events.Page.Request, { predicate, timeout: options.timeout }, logLine); + } + async waitForResponse(urlOrPredicate, options = {}) { + const predicate = async (response2) => { + if (isString(urlOrPredicate) || isRegExp(urlOrPredicate)) + return urlMatches(this._browserContext._options.baseURL, response2.url(), urlOrPredicate); + return await urlOrPredicate(response2); + }; + const trimmedUrl = trimUrl(urlOrPredicate); + const logLine = trimmedUrl ? `waiting for response ${trimmedUrl}` : void 0; + return await this._waitForEvent(Events.Page.Response, { predicate, timeout: options.timeout }, logLine); + } + async waitForEvent(event, optionsOrPredicate = {}) { + return await this._waitForEvent(event, optionsOrPredicate, `waiting for event "${event}"`); + } + _closeErrorWithReason() { + return new TargetClosedError2(this._closeReason || this._browserContext._effectiveCloseReason()); + } + async _waitForEvent(event, optionsOrPredicate, logLine) { + return await this._wrapApiCall(async () => { + const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === "function" ? {} : optionsOrPredicate); + const predicate = typeof optionsOrPredicate === "function" ? optionsOrPredicate : optionsOrPredicate.predicate; + const waiter = Waiter.createForEvent(this, event); + if (logLine) + waiter.log(logLine); + waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`); + if (event !== Events.Page.Crash) + waiter.rejectOnEvent(this, Events.Page.Crash, new Error("Page crashed")); + if (event !== Events.Page.Close) + waiter.rejectOnEvent(this, Events.Page.Close, () => this._closeErrorWithReason()); + const result2 = await waiter.waitForEvent(this, event, predicate); + waiter.dispose(); + return result2; + }); + } + async goBack(options = {}) { + const waitUntil = verifyLoadState("waitUntil", options.waitUntil === void 0 ? "load" : options.waitUntil); + return Response3.fromNullable((await this._channel.goBack({ ...options, waitUntil, timeout: this._timeoutSettings.navigationTimeout(options) })).response); + } + async goForward(options = {}) { + const waitUntil = verifyLoadState("waitUntil", options.waitUntil === void 0 ? "load" : options.waitUntil); + return Response3.fromNullable((await this._channel.goForward({ ...options, waitUntil, timeout: this._timeoutSettings.navigationTimeout(options) })).response); + } + async requestGC() { + await this._channel.requestGC(); + } + async emulateMedia(options = {}) { + await this._channel.emulateMedia({ + media: options.media === null ? "no-override" : options.media, + colorScheme: options.colorScheme === null ? "no-override" : options.colorScheme, + reducedMotion: options.reducedMotion === null ? "no-override" : options.reducedMotion, + forcedColors: options.forcedColors === null ? "no-override" : options.forcedColors, + contrast: options.contrast === null ? "no-override" : options.contrast + }); + } + async setViewportSize(viewportSize) { + this._viewportSize = viewportSize; + await this._channel.setViewportSize({ viewportSize }); + } + viewportSize() { + return this._viewportSize || null; + } + async evaluate(pageFunction, arg) { + assertMaxArguments(arguments.length, 2); + return await this._mainFrame.evaluate(pageFunction, arg); + } + async addInitScript(script, arg) { + const source11 = await evaluationScript(this._platform, script, arg); + return DisposableObject2.from((await this._channel.addInitScript({ source: source11 })).disposable); + } + async route(url2, handler, options = {}) { + this._routes.unshift(new RouteHandler(this._platform, this._browserContext._options.baseURL, url2, handler, options.times)); + await this._updateInterceptionPatterns({ title: "Route requests" }); + return new DisposableStub(() => this.unroute(url2, handler)); + } + async routeFromHAR(har, options = {}) { + const localUtils = this._connection.localUtils(); + if (!localUtils) + throw new Error("Route from har is not supported in thin clients"); + if (options.update) { + await this._browserContext.tracing._recordIntoHAR(har, this, options); + return; + } + const harRouter = await HarRouter.create(localUtils, har, options.notFound || "abort", { urlMatch: options.url }); + this._harRouters.push(harRouter); + await harRouter.addPageRoute(this); + } + async routeWebSocket(url2, handler) { + this._webSocketRoutes.unshift(new WebSocketRouteHandler(this._browserContext._options.baseURL, url2, handler)); + await this._updateWebSocketInterceptionPatterns({ title: "Route WebSockets" }); + } + _disposeHarRouters() { + this._harRouters.forEach((router) => router.dispose()); + this._harRouters = []; + } + async unrouteAll(options) { + await this._unrouteInternal(this._routes, [], options?.behavior); + this._disposeHarRouters(); + } + async unroute(url2, handler) { + const removed = []; + const remaining = []; + for (const route2 of this._routes) { + if (urlMatchesEqual(route2.url, url2) && (!handler || route2.handler === handler)) + removed.push(route2); + else + remaining.push(route2); + } + await this._unrouteInternal(removed, remaining, "default"); + } + async _unrouteInternal(removed, remaining, behavior) { + this._routes = remaining; + if (behavior && behavior !== "default") { + const promises = removed.map((routeHandler) => routeHandler.stop(behavior)); + await Promise.all(promises); + } + await this._updateInterceptionPatterns({ title: "Unroute requests" }); + } + async _updateInterceptionPatterns(options) { + const patterns = RouteHandler.prepareInterceptionPatterns(this._routes); + await this._wrapApiCall(() => this._channel.setNetworkInterceptionPatterns({ patterns }), options); + } + async _updateWebSocketInterceptionPatterns(options) { + const patterns = WebSocketRouteHandler.prepareInterceptionPatterns(this._webSocketRoutes); + await this._wrapApiCall(() => this._channel.setWebSocketInterceptionPatterns({ patterns }), options); + } + async screenshot(options = {}) { + const mask = options.mask; + const copy = { ...options, mask: void 0, timeout: this._timeoutSettings.timeout(options) }; + if (!copy.type) + copy.type = determineScreenshotType(options); + if (mask) { + copy.mask = mask.map((locator2) => ({ + frame: locator2._frame._channel, + selector: locator2._selector + })); + } + const result2 = await this._channel.screenshot(copy); + if (options.path) { + await mkdirIfNeeded2(this._platform, options.path); + await this._platform.fs().promises.writeFile(options.path, result2.binary); + } + return result2.binary; + } + async _expectScreenshot(options) { + const mask = options?.mask ? options?.mask.map((locator3) => ({ + frame: locator3._frame._channel, + selector: locator3._selector + })) : void 0; + const locator2 = options.locator ? { + frame: options.locator._frame._channel, + selector: options.locator._selector + } : void 0; + try { + const result2 = await this._channel.expectScreenshot({ + ...options, + isNot: !!options.isNot, + locator: locator2, + mask + }); + return { actual: result2.actual }; + } catch (e) { + if (!(e instanceof PlaywrightError)) + throw e; + const details = e.details; + return { ...details, errorMessage: details.customErrorMessage }; + } + } + async title() { + return await this._mainFrame.title(); + } + async bringToFront() { + await this._channel.bringToFront(); + } + async [Symbol.asyncDispose]() { + await this.close(); + } + async close(options = {}) { + this._closeReason = options.reason; + if (!options.runBeforeUnload) + this._closeWasCalled = true; + try { + if (this._ownedContext) + await this._ownedContext.close(); + else if (options.runBeforeUnload) + await this._channel.runBeforeUnload(); + else + await this._channel.close({ reason: options.reason }); + } catch (e) { + if (isTargetClosedError2(e) && !options.runBeforeUnload) + return; + throw e; + } + } + isClosed() { + return this._closed; + } + async click(selector, options) { + return await this._mainFrame.click(selector, options); + } + async dragAndDrop(source11, target, options) { + return await this._mainFrame.dragAndDrop(source11, target, options); + } + async dblclick(selector, options) { + await this._mainFrame.dblclick(selector, options); + } + async tap(selector, options) { + return await this._mainFrame.tap(selector, options); + } + async fill(selector, value2, options) { + return await this._mainFrame.fill(selector, value2, options); + } + async clearConsoleMessages() { + await this._channel.clearConsoleMessages(); + } + async consoleMessages(options) { + const { messages } = await this._channel.consoleMessages({ filter: options?.filter }); + return messages.map((message) => new ConsoleMessage2(this._platform, message, this, null)); + } + async clearPageErrors() { + await this._channel.clearPageErrors(); + } + async pageErrors(options) { + const { errors } = await this._channel.pageErrors({ filter: options?.filter }); + return errors.map((error) => parseError2(error)); + } + locator(selector, options) { + return this.mainFrame().locator(selector, options); + } + getByTestId(testId) { + return this.mainFrame().getByTestId(testId); + } + getByAltText(text2, options) { + return this.mainFrame().getByAltText(text2, options); + } + getByLabel(text2, options) { + return this.mainFrame().getByLabel(text2, options); + } + getByPlaceholder(text2, options) { + return this.mainFrame().getByPlaceholder(text2, options); + } + getByText(text2, options) { + return this.mainFrame().getByText(text2, options); + } + getByTitle(text2, options) { + return this.mainFrame().getByTitle(text2, options); + } + getByRole(role, options = {}) { + return this.mainFrame().getByRole(role, options); + } + frameLocator(selector) { + return this.mainFrame().frameLocator(selector); + } + async focus(selector, options) { + return await this._mainFrame.focus(selector, options); + } + async textContent(selector, options) { + return await this._mainFrame.textContent(selector, options); + } + async innerText(selector, options) { + return await this._mainFrame.innerText(selector, options); + } + async innerHTML(selector, options) { + return await this._mainFrame.innerHTML(selector, options); + } + async getAttribute(selector, name, options) { + return await this._mainFrame.getAttribute(selector, name, options); + } + async inputValue(selector, options) { + return await this._mainFrame.inputValue(selector, options); + } + async isChecked(selector, options) { + return await this._mainFrame.isChecked(selector, options); + } + async isDisabled(selector, options) { + return await this._mainFrame.isDisabled(selector, options); + } + async isEditable(selector, options) { + return await this._mainFrame.isEditable(selector, options); + } + async isEnabled(selector, options) { + return await this._mainFrame.isEnabled(selector, options); + } + async isHidden(selector, options) { + return await this._mainFrame.isHidden(selector, options); + } + async isVisible(selector, options) { + return await this._mainFrame.isVisible(selector, options); + } + async hover(selector, options) { + return await this._mainFrame.hover(selector, options); + } + async selectOption(selector, values, options) { + return await this._mainFrame.selectOption(selector, values, options); + } + async setInputFiles(selector, files, options) { + return await this._mainFrame.setInputFiles(selector, files, options); + } + async type(selector, text2, options) { + return await this._mainFrame.type(selector, text2, options); + } + async press(selector, key, options) { + return await this._mainFrame.press(selector, key, options); + } + async check(selector, options) { + return await this._mainFrame.check(selector, options); + } + async uncheck(selector, options) { + return await this._mainFrame.uncheck(selector, options); + } + async setChecked(selector, checked, options) { + return await this._mainFrame.setChecked(selector, checked, options); + } + async waitForTimeout(timeout) { + return await this._mainFrame.waitForTimeout(timeout); + } + async waitForFunction(pageFunction, arg, options) { + return await this._mainFrame.waitForFunction(pageFunction, arg, options); + } + async requests() { + const { requests: requests2 } = await this._channel.requests(); + return requests2.map((request2) => Request2.from(request2)); + } + workers() { + return [...this._workers]; + } + async pause(_options) { + if (this._platform.isJSDebuggerAttached()) + return; + const defaultNavigationTimeout = this._browserContext._timeoutSettings.defaultNavigationTimeout(); + const defaultTimeout = this._browserContext._timeoutSettings.defaultTimeout(); + this._browserContext.setDefaultNavigationTimeout(0); + this._browserContext.setDefaultTimeout(0); + this._instrumentation?.onWillPause({ keepTestTimeout: !!_options?.__testHookKeepTestTimeout }); + await this._closedOrCrashedScope.safeRace(this.context()._channel.pause()); + this._browserContext.setDefaultNavigationTimeout(defaultNavigationTimeout); + this._browserContext.setDefaultTimeout(defaultTimeout); + } + async pdf(options = {}) { + const transportOptions = { ...options }; + if (transportOptions.margin) + transportOptions.margin = { ...transportOptions.margin }; + if (typeof options.width === "number") + transportOptions.width = options.width + "px"; + if (typeof options.height === "number") + transportOptions.height = options.height + "px"; + for (const margin of ["top", "right", "bottom", "left"]) { + const index = margin; + if (options.margin && typeof options.margin[index] === "number") + transportOptions.margin[index] = transportOptions.margin[index] + "px"; + } + const result2 = await this._channel.pdf(transportOptions); + if (options.path) { + const platform = this._platform; + await platform.fs().promises.mkdir(platform.path().dirname(options.path), { recursive: true }); + await platform.fs().promises.writeFile(options.path, result2.pdf); + } + return result2.pdf; + } + async ariaSnapshot(options = {}) { + const result2 = await this.mainFrame()._channel.ariaSnapshot({ timeout: this._timeoutSettings.timeout(options), track: options._track, mode: options.mode, depth: options.depth, boxes: options.boxes }); + return result2.snapshot; + } + async _setDockTile(image) { + await this._channel.setDockTile({ image }); + } + }; + BindingCall = class extends ChannelOwner { + static from(channel) { + return channel._object; + } + constructor(parent, type3, guid, initializer) { + super(parent, type3, guid, initializer); + } + async call(func) { + try { + const frame = Frame2.from(this._initializer.frame); + const source11 = { + context: frame._page.context(), + page: frame._page, + frame + }; + const result2 = await func(source11, ...this._initializer.args.map(parseResult)); + this._channel.resolve({ result: serializeArgument(result2) }).catch(() => { + }); + } catch (e) { + this._channel.reject({ error: serializeError2(e) }).catch(() => { + }); + } + } + }; + } +}); + +// packages/playwright-core/src/client/dialog.ts +var Dialog2; +var init_dialog2 = __esm({ + "packages/playwright-core/src/client/dialog.ts"() { + "use strict"; + init_channelOwner(); + init_page2(); + init_errors2(); + Dialog2 = class extends ChannelOwner { + static from(dialog) { + return dialog._object; + } + constructor(parent, type3, guid, initializer) { + super(parent, type3, guid, initializer); + this._page = Page2.fromNullable(initializer.page); + } + page() { + return this._page; + } + type() { + return this._initializer.type; + } + message() { + return this._initializer.message; + } + defaultValue() { + return this._initializer.defaultValue; + } + async accept(promptText) { + await this._channel.accept({ promptText }); + } + async dismiss() { + try { + await this._channel.dismiss(); + } catch (e) { + if (isTargetClosedError2(e)) + return; + throw e; + } + } + }; + } +}); + +// packages/playwright-core/src/client/webError.ts +var WebError; +var init_webError = __esm({ + "packages/playwright-core/src/client/webError.ts"() { + "use strict"; + WebError = class { + constructor(page, error, location2) { + this._page = page; + this._error = error; + this._location = location2; + } + page() { + return this._page; + } + error() { + return this._error; + } + location() { + return this._location; + } + }; + } +}); + +// packages/playwright-core/src/client/browserContext.ts +async function prepareStorageState(platform, storageState2) { + if (typeof storageState2 !== "string") + return storageState2; + try { + return JSON.parse(await platform.fs().promises.readFile(storageState2, "utf8")); + } catch (e) { + rewriteErrorMessage(e, `Error reading storage state from ${storageState2}: +` + e.message); + throw e; + } +} +async function prepareBrowserContextParams(platform, options) { + if (options.extraHTTPHeaders) + validateHeaders(options.extraHTTPHeaders); + const contextParams = { + ...options, + viewport: options.viewport === null ? void 0 : options.viewport, + noDefaultViewport: options.viewport === null, + extraHTTPHeaders: options.extraHTTPHeaders ? headersObjectToArray(options.extraHTTPHeaders) : void 0, + storageState: options.storageState ? await prepareStorageState(platform, options.storageState) : void 0, + serviceWorkers: options.serviceWorkers, + colorScheme: options.colorScheme === null ? "no-override" : options.colorScheme, + reducedMotion: options.reducedMotion === null ? "no-override" : options.reducedMotion, + forcedColors: options.forcedColors === null ? "no-override" : options.forcedColors, + contrast: options.contrast === null ? "no-override" : options.contrast, + acceptDownloads: toAcceptDownloadsProtocol(options.acceptDownloads), + clientCertificates: await toClientCertificatesProtocol(platform, options.clientCertificates) + }; + if (contextParams.recordVideo && contextParams.recordVideo.dir) + contextParams.recordVideo.dir = platform.path().resolve(contextParams.recordVideo.dir); + return contextParams; +} +function toAcceptDownloadsProtocol(acceptDownloads) { + if (acceptDownloads === void 0) + return void 0; + if (acceptDownloads) + return "accept"; + return "deny"; +} +async function toClientCertificatesProtocol(platform, certs) { + if (!certs) + return void 0; + const bufferizeContent = async (value2, path59) => { + if (value2) + return value2; + if (path59) + return await platform.fs().promises.readFile(path59); + }; + return await Promise.all(certs.map(async (cert) => ({ + origin: cert.origin, + cert: await bufferizeContent(cert.cert, cert.certPath), + key: await bufferizeContent(cert.key, cert.keyPath), + pfx: await bufferizeContent(cert.pfx, cert.pfxPath), + passphrase: cert.passphrase + }))); +} +var BrowserContext2; +var init_browserContext2 = __esm({ + "packages/playwright-core/src/client/browserContext.ts"() { + "use strict"; + init_headers(); + init_urlMatch(); + init_rtti(); + init_stackTrace(); + init_cdpSession(); + init_channelOwner(); + init_clientHelper(); + init_clock2(); + init_consoleMessage(); + init_credentials2(); + init_debugger2(); + init_dialog2(); + init_disposable3(); + init_errors2(); + init_events(); + init_fetch2(); + init_frame(); + init_harRouter(); + init_network3(); + init_page2(); + init_tracing2(); + init_waiter(); + init_webError(); + init_worker(); + init_timeoutSettings(); + init_fileUtils2(); + BrowserContext2 = class _BrowserContext extends ChannelOwner { + constructor(parent, type3, guid, initializer) { + super(parent, type3, guid, initializer); + this._pages = /* @__PURE__ */ new Set(); + this._routes = []; + this._webSocketRoutes = []; + // Browser is null for browser contexts created outside of normal browser, e.g. android or electron. + this._browser = null; + this._bindings = /* @__PURE__ */ new Map(); + this._forReuse = false; + this._serviceWorkers = /* @__PURE__ */ new Set(); + this._closingStatus = "none"; + this._harRouters = []; + this._options = initializer.options; + this._timeoutSettings = new TimeoutSettings(this._platform); + this.debugger = Debugger2.from(initializer.debugger); + this.tracing = Tracing2.from(initializer.tracing); + this.request = APIRequestContext2.from(initializer.requestContext); + this.request._timeoutSettings = this._timeoutSettings; + this.clock = new Clock2(this); + this.credentials = new Credentials2(this); + this._channel.on("bindingCall", ({ binding }) => this._onBinding(BindingCall.from(binding))); + this._channel.on("close", () => this._onClose()); + this._channel.on("page", ({ page }) => this._onPage(Page2.from(page))); + this._channel.on("route", ({ route: route2 }) => this._onRoute(Route2.from(route2))); + this._channel.on("webSocketRoute", ({ webSocketRoute }) => this._onWebSocketRoute(WebSocketRoute.from(webSocketRoute))); + this._channel.on("serviceWorker", ({ worker }) => { + const serviceWorker = Worker2.from(worker); + serviceWorker._context = this; + this._serviceWorkers.add(serviceWorker); + this.emit(Events.BrowserContext.ServiceWorker, serviceWorker); + }); + this._channel.on("console", (event) => { + const worker = Worker2.fromNullable(event.worker); + const page = Page2.fromNullable(event.page); + const consoleMessage = new ConsoleMessage2(this._platform, event, page, worker); + worker?.emit(Events.Worker.Console, consoleMessage); + page?.emit(Events.Page.Console, consoleMessage); + if (worker && this._serviceWorkers.has(worker)) { + const scope = this._serviceWorkerScope(worker); + for (const page2 of this._pages) { + if (scope && page2.url().startsWith(scope)) + page2.emit(Events.Page.Console, consoleMessage); + } + } + this.emit(Events.BrowserContext.Console, consoleMessage); + }); + this._channel.on("pageError", ({ error, page, location: location2 }) => { + const pageObject = Page2.from(page); + const parsedError = parseError2(error); + this.emit(Events.BrowserContext.WebError, new WebError(pageObject, parsedError, location2)); + if (pageObject) + pageObject.emit(Events.Page.PageError, parsedError); + }); + this._channel.on("dialog", ({ dialog }) => { + const dialogObject = Dialog2.from(dialog); + let hasListeners = this.emit(Events.BrowserContext.Dialog, dialogObject); + const page = dialogObject.page(); + if (page) + hasListeners = page.emit(Events.Page.Dialog, dialogObject) || hasListeners; + if (!hasListeners) { + if (dialogObject.type() === "beforeunload") + dialog.accept({}).catch(() => { + }); + else + dialog.dismiss().catch(() => { + }); + } + }); + this._channel.on("request", ({ request: request2, page }) => this._onRequest(Request2.from(request2), Page2.fromNullable(page))); + this._channel.on("requestFailed", ({ request: request2, failureText, responseEndTiming, page }) => this._onRequestFailed(Request2.from(request2), responseEndTiming, failureText, Page2.fromNullable(page))); + this._channel.on("requestFinished", (params2) => this._onRequestFinished(params2)); + this._channel.on("response", ({ response: response2, page }) => this._onResponse(Response3.from(response2), Page2.fromNullable(page))); + this._channel.on("recorderEvent", ({ event, data, page, code }) => { + if (event === "actionAdded") + this._onRecorderEventSink?.actionAdded?.(Page2.from(page), data, code); + else if (event === "actionUpdated") + this._onRecorderEventSink?.actionUpdated?.(Page2.from(page), data, code); + else if (event === "signalAdded") + this._onRecorderEventSink?.signalAdded?.(Page2.from(page), data); + }); + this._closedPromise = new Promise((f) => this.once(Events.BrowserContext.Close, f)); + this._setEventToSubscriptionMapping(/* @__PURE__ */ new Map([ + [Events.BrowserContext.Console, "console"], + [Events.BrowserContext.Dialog, "dialog"], + [Events.BrowserContext.Request, "request"], + [Events.BrowserContext.Response, "response"], + [Events.BrowserContext.RequestFinished, "requestFinished"], + [Events.BrowserContext.RequestFailed, "requestFailed"] + ])); + } + static from(context2) { + return context2._object; + } + static fromNullable(context2) { + return context2 ? _BrowserContext.from(context2) : null; + } + async _initializeHarFromOptions(recordHar) { + if (!recordHar) + return; + const defaultContent = recordHar.path.endsWith(".zip") ? "attach" : "embed"; + await this.tracing._recordIntoHAR(recordHar.path, null, { + url: recordHar.urlFilter, + updateContent: recordHar.content ?? (recordHar.omitContent ? "omit" : defaultContent), + updateMode: recordHar.mode ?? "full" + }); + } + _onPage(page) { + this._pages.add(page); + this.emit(Events.BrowserContext.Page, page); + if (page._opener && !page._opener.isClosed()) + page._opener.emit(Events.Page.Popup, page); + } + _onRequest(request2, page) { + this.emit(Events.BrowserContext.Request, request2); + if (page) + page.emit(Events.Page.Request, request2); + } + _onResponse(response2, page) { + this.emit(Events.BrowserContext.Response, response2); + if (page) + page.emit(Events.Page.Response, response2); + } + _onRequestFailed(request2, responseEndTiming, failureText, page) { + request2._failureText = failureText || null; + request2._setResponseEndTiming(responseEndTiming); + this.emit(Events.BrowserContext.RequestFailed, request2); + if (page) + page.emit(Events.Page.RequestFailed, request2); + } + _onRequestFinished(params2) { + const { responseEndTiming } = params2; + const request2 = Request2.from(params2.request); + const response2 = Response3.fromNullable(params2.response); + const page = Page2.fromNullable(params2.page); + request2._setResponseEndTiming(responseEndTiming); + this.emit(Events.BrowserContext.RequestFinished, request2); + if (page) + page.emit(Events.Page.RequestFinished, request2); + if (response2) + response2._finishedPromise.resolve(null); + } + async _onRoute(route2) { + route2._context = this; + const page = route2.request()._safePage(); + const routeHandlers = this._routes.slice(); + for (const routeHandler of routeHandlers) { + if (page?._closeWasCalled || this.isClosed()) + return; + if (!routeHandler.matches(route2.request().url())) + continue; + const index = this._routes.indexOf(routeHandler); + if (index === -1) + continue; + if (routeHandler.willExpire()) + this._routes.splice(index, 1); + const handled = await routeHandler.handle(route2); + if (!this._routes.length) + this._updateInterceptionPatterns({ internal: true }).catch(() => { + }); + if (handled) + return; + } + await route2._innerContinue( + true + /* isFallback */ + ).catch(() => { + }); + } + async _onWebSocketRoute(webSocketRoute) { + const routeHandler = this._webSocketRoutes.find((route2) => route2.matches(webSocketRoute.url())); + if (routeHandler) + await routeHandler.handle(webSocketRoute); + else + webSocketRoute.connectToServer(); + } + async _onBinding(bindingCall) { + const func = this._bindings.get(bindingCall._initializer.name); + if (!func) + return; + await bindingCall.call(func); + } + _serviceWorkerScope(serviceWorker) { + try { + let url2 = new URL(".", serviceWorker.url()).href; + if (!url2.endsWith("/")) + url2 += "/"; + return url2; + } catch { + return null; + } + } + setDefaultNavigationTimeout(timeout) { + this._timeoutSettings.setDefaultNavigationTimeout(timeout); + } + setDefaultTimeout(timeout) { + this._timeoutSettings.setDefaultTimeout(timeout); + } + browser() { + return this._browser; + } + pages() { + return [...this._pages]; + } + isClosed() { + return this._closingStatus !== "none"; + } + async newPage() { + if (this._ownerPage) + throw new Error("Please use browser.newContext()"); + return Page2.from((await this._channel.newPage()).page); + } + async cookies(urls) { + if (!urls) + urls = []; + if (urls && typeof urls === "string") + urls = [urls]; + return (await this._channel.cookies({ urls })).cookies; + } + async addCookies(cookies) { + await this._channel.addCookies({ cookies }); + } + async clearCookies(options = {}) { + await this._channel.clearCookies({ + name: isString(options.name) ? options.name : void 0, + nameRegexSource: isRegExp(options.name) ? options.name.source : void 0, + nameRegexFlags: isRegExp(options.name) ? options.name.flags : void 0, + domain: isString(options.domain) ? options.domain : void 0, + domainRegexSource: isRegExp(options.domain) ? options.domain.source : void 0, + domainRegexFlags: isRegExp(options.domain) ? options.domain.flags : void 0, + path: isString(options.path) ? options.path : void 0, + pathRegexSource: isRegExp(options.path) ? options.path.source : void 0, + pathRegexFlags: isRegExp(options.path) ? options.path.flags : void 0 + }); + } + async grantPermissions(permissions, options) { + await this._channel.grantPermissions({ permissions, ...options }); + } + async clearPermissions() { + await this._channel.clearPermissions(); + } + async setGeolocation(geolocation) { + await this._channel.setGeolocation({ geolocation: geolocation || void 0 }); + } + async setExtraHTTPHeaders(headers) { + validateHeaders(headers); + await this._channel.setExtraHTTPHeaders({ headers: headersObjectToArray(headers) }); + } + async setOffline(offline) { + await this._channel.setOffline({ offline }); + } + async setHTTPCredentials(httpCredentials) { + await this._channel.setHTTPCredentials({ httpCredentials: httpCredentials || void 0 }); + } + async addInitScript(script, arg) { + const source11 = await evaluationScript(this._platform, script, arg); + return DisposableObject2.from((await this._channel.addInitScript({ source: source11 })).disposable); + } + async exposeBinding(name, callback) { + const result2 = await this._channel.exposeBinding({ name }); + this._bindings.set(name, callback); + return DisposableObject2.from(result2.disposable); + } + async exposeFunction(name, callback) { + const result2 = await this._channel.exposeBinding({ name }); + const binding = (source11, ...args) => callback(...args); + this._bindings.set(name, binding); + return DisposableObject2.from(result2.disposable); + } + async route(url2, handler, options = {}) { + this._routes.unshift(new RouteHandler(this._platform, this._options.baseURL, url2, handler, options.times)); + await this._updateInterceptionPatterns({ title: "Route requests" }); + return new DisposableStub(() => this.unroute(url2, handler)); + } + async routeWebSocket(url2, handler) { + this._webSocketRoutes.unshift(new WebSocketRouteHandler(this._options.baseURL, url2, handler)); + await this._updateWebSocketInterceptionPatterns({ title: "Route WebSockets" }); + } + async routeFromHAR(har, options = {}) { + const localUtils = this._connection.localUtils(); + if (!localUtils) + throw new Error("Route from har is not supported in thin clients"); + if (options.update) { + await this.tracing._recordIntoHAR(har, null, options); + return; + } + const harRouter = await HarRouter.create(localUtils, har, options.notFound || "abort", { urlMatch: options.url }); + this._harRouters.push(harRouter); + await harRouter.addContextRoute(this); + } + _disposeHarRouters() { + this._harRouters.forEach((router) => router.dispose()); + this._harRouters = []; + } + async unrouteAll(options) { + await this._unrouteInternal(this._routes, [], options?.behavior); + this._disposeHarRouters(); + } + async unroute(url2, handler) { + const removed = []; + const remaining = []; + for (const route2 of this._routes) { + if (urlMatchesEqual(route2.url, url2) && (!handler || route2.handler === handler)) + removed.push(route2); + else + remaining.push(route2); + } + await this._unrouteInternal(removed, remaining, "default"); + } + async _unrouteInternal(removed, remaining, behavior) { + this._routes = remaining; + if (behavior && behavior !== "default") { + const promises = removed.map((routeHandler) => routeHandler.stop(behavior)); + await Promise.all(promises); + } + await this._updateInterceptionPatterns({ title: "Unroute requests" }); + } + async _updateInterceptionPatterns(options) { + const patterns = RouteHandler.prepareInterceptionPatterns(this._routes); + await this._wrapApiCall(() => this._channel.setNetworkInterceptionPatterns({ patterns }), options); + } + async _updateWebSocketInterceptionPatterns(options) { + const patterns = WebSocketRouteHandler.prepareInterceptionPatterns(this._webSocketRoutes); + await this._wrapApiCall(() => this._channel.setWebSocketInterceptionPatterns({ patterns }), options); + } + _effectiveCloseReason() { + return this._closeReason || this._browser?._closeReason; + } + async waitForEvent(event, optionsOrPredicate = {}) { + return await this._wrapApiCall(async () => { + const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === "function" ? {} : optionsOrPredicate); + const predicate = typeof optionsOrPredicate === "function" ? optionsOrPredicate : optionsOrPredicate.predicate; + const waiter = Waiter.createForEvent(this, event); + waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`); + if (event !== Events.BrowserContext.Close) + waiter.rejectOnEvent(this, Events.BrowserContext.Close, () => new TargetClosedError2(this._effectiveCloseReason())); + const result2 = await waiter.waitForEvent(this, event, predicate); + waiter.dispose(); + return result2; + }); + } + async storageState(options = {}) { + const state = await this._channel.storageState({ indexedDB: options.indexedDB }); + if (options.path) { + await mkdirIfNeeded2(this._platform, options.path); + await this._platform.fs().promises.writeFile(options.path, JSON.stringify(state, void 0, 2), "utf8"); + } + return state; + } + async setStorageState(storageState2) { + const state = await prepareStorageState(this._platform, storageState2); + await this._channel.setStorageState({ storageState: state }); + } + backgroundPages() { + return []; + } + serviceWorkers() { + return [...this._serviceWorkers]; + } + async newCDPSession(page) { + if (!(page instanceof Page2) && !(page instanceof Frame2)) + throw new Error("page: expected Page or Frame"); + const result2 = await this._channel.newCDPSession(page instanceof Page2 ? { page: page._channel } : { frame: page._channel }); + return CDPSession2.from(result2.session); + } + _onClose() { + this._closingStatus = "closed"; + this._browser?._contexts.delete(this); + this._browser?._browserType._contexts.delete(this); + this._browser?._browserType._playwright.selectors._contextsForSelectors.delete(this); + this._disposeHarRouters(); + this.tracing._resetStackCounter(); + this.emit(Events.BrowserContext.Close, this); + } + async [Symbol.asyncDispose]() { + await this.close(); + } + async close(options = {}) { + if (this.isClosed()) + return; + this._closeReason = options.reason; + this._closingStatus = "closing"; + await this.request.dispose(options); + await this._instrumentation.runBeforeCloseBrowserContext(this); + await this.tracing._exportAllHars(); + await this._channel.close(options); + await this._closedPromise; + } + async _enableRecorder(params2, eventSink) { + if (eventSink) + this._onRecorderEventSink = eventSink; + await this._channel.enableRecorder(params2); + } + async _disableRecorder() { + this._onRecorderEventSink = void 0; + await this._channel.disableRecorder(); + } + async _exposeConsoleApi() { + await this._channel.exposeConsoleApi(); + } + }; + } +}); + +// packages/playwright-core/src/client/browser.ts +var Browser2; +var init_browser2 = __esm({ + "packages/playwright-core/src/client/browser.ts"() { + "use strict"; + init_artifact2(); + init_browserContext2(); + init_cdpSession(); + init_channelOwner(); + init_errors2(); + init_events(); + init_fileUtils2(); + Browser2 = class extends ChannelOwner { + constructor(parent, type3, guid, initializer) { + super(parent, type3, guid, initializer); + this._contexts = /* @__PURE__ */ new Set(); + this._isConnected = true; + this._shouldCloseConnectionOnClose = false; + this._options = {}; + this._name = initializer.name; + this._browserName = initializer.browserName; + this._channel.on("context", ({ context: context2 }) => this._didCreateContext(BrowserContext2.from(context2))); + this._channel.on("close", () => this._didClose()); + this._closedPromise = new Promise((f) => this.once(Events.Browser.Disconnected, f)); + } + static from(browser) { + return browser._object; + } + browserType() { + return this._browserType; + } + async newContext(options = {}) { + return await this._innerNewContext(options, false); + } + async _newContextForReuse(options = {}) { + return await this._innerNewContext(options, true); + } + async _disconnectFromReusedContext(reason) { + const context2 = [...this._contexts].find((context3) => context3._forReuse); + if (!context2) + return; + await this._instrumentation.runBeforeCloseBrowserContext(context2); + for (const page of context2.pages()) + page._onClose(); + context2._onClose(); + await this._channel.disconnectFromReusedContext({ reason }); + } + async _innerNewContext(userOptions = {}, forReuse) { + const options = this._browserType._playwright.selectors._withSelectorOptions(userOptions); + await this._instrumentation.runBeforeCreateBrowserContext(options); + const contextOptions = await prepareBrowserContextParams(this._platform, options); + const response2 = forReuse ? await this._channel.newContextForReuse(contextOptions) : await this._channel.newContext(contextOptions); + const context2 = BrowserContext2.from(response2.context); + if (forReuse) + context2._forReuse = true; + if (options.logger) + context2._logger = options.logger; + await context2._initializeHarFromOptions(options.recordHar); + await this._instrumentation.runAfterCreateBrowserContext(context2); + return context2; + } + _connectToBrowserType(browserType, browserOptions, logger) { + this._browserType = browserType; + this._options = browserOptions; + this._logger = logger; + for (const context2 of this._contexts) + this._setupBrowserContext(context2); + } + _didCreateContext(context2) { + context2._browser = this; + this._contexts.add(context2); + if (this._browserType) + this._setupBrowserContext(context2); + this.emit(Events.Browser.Context, context2); + } + _setupBrowserContext(context2) { + context2._logger = this._logger; + context2.tracing._tracesDir = this._options.tracesDir; + this._browserType._contexts.add(context2); + this._browserType._playwright.selectors._contextsForSelectors.add(context2); + context2.setDefaultTimeout(this._browserType._playwright._defaultContextTimeout); + context2.setDefaultNavigationTimeout(this._browserType._playwright._defaultContextNavigationTimeout); + } + contexts() { + return [...this._contexts]; + } + version() { + return this._initializer.version; + } + async bind(title, options = {}) { + const { endpoint } = await this._channel.startServer({ title, ...options }); + return { endpoint }; + } + async unbind() { + await this._channel.stopServer(); + } + async newPage(options = {}) { + return await this._wrapApiCall(async () => { + const context2 = await this.newContext(options); + const page = await context2.newPage(); + page._ownedContext = context2; + context2._ownerPage = page; + return page; + }, { title: "Create page" }); + } + isConnected() { + return this._isConnected; + } + async newBrowserCDPSession() { + return CDPSession2.from((await this._channel.newBrowserCDPSession()).session); + } + async startTracing(page, options = {}) { + this._path = options.path; + await this._channel.startTracing({ ...options, page: page ? page._channel : void 0 }); + } + async stopTracing() { + const artifact = Artifact2.from((await this._channel.stopTracing()).artifact); + const buffer = await artifact.readIntoBuffer(); + await artifact.delete(); + if (this._path) { + await mkdirIfNeeded2(this._platform, this._path); + await this._platform.fs().promises.writeFile(this._path, buffer); + this._path = void 0; + } + return buffer; + } + async [Symbol.asyncDispose]() { + await this.close(); + } + async close(options = {}) { + this._closeReason = options.reason; + try { + if (this._shouldCloseConnectionOnClose) + this._connection.close(); + else + await this._channel.close(options); + await this._closedPromise; + } catch (e) { + if (isTargetClosedError2(e)) + return; + throw e; + } + } + _didClose() { + this._isConnected = false; + this.emit(Events.Browser.Disconnected, this); + } + }; + } +}); + +// packages/playwright-core/src/client/connect.ts +async function connectToBrowser(playwright2, params2) { + const deadline = params2.timeout ? monotonicTime() + params2.timeout : 0; + const nameParam = params2.browserName ? { "x-playwright-browser": params2.browserName } : {}; + const headers = { ...nameParam, ...params2.headers }; + const connectParams = { + endpoint: params2.endpoint, + headers, + exposeNetwork: params2.exposeNetwork, + slowMo: params2.slowMo, + timeout: params2.timeout || 0 + }; + if (params2.__testHookRedirectPortForwarding) + connectParams.socksProxyRedirectPortForTest = params2.__testHookRedirectPortForwarding; + const connection = await connectToEndpoint(playwright2._connection, connectParams); + let browser; + connection.on("close", () => { + for (const context2 of browser?.contexts() || []) { + for (const page of context2.pages()) + page._onClose(); + context2._onClose(); + } + setTimeout(() => browser?._didClose(), 0); + }); + const result2 = await raceAgainstDeadline(async () => { + if (params2.__testHookBeforeCreateBrowser) + await params2.__testHookBeforeCreateBrowser(); + const playwright3 = await connection.initializePlaywright(); + if (!playwright3._initializer.preLaunchedBrowser) { + connection.close(); + throw new Error("Malformed endpoint. Did you use BrowserType.launchServer method?"); + } + playwright3.selectors = playwright3.selectors; + browser = Browser2.from(playwright3._initializer.preLaunchedBrowser); + browser._shouldCloseConnectionOnClose = true; + browser.on(Events.Browser.Disconnected, () => connection.close()); + return browser; + }, deadline); + if (!result2.timedOut) { + return result2.result; + } else { + connection.close(); + throw new Error(`Timeout ${params2.timeout}ms exceeded`); + } +} +async function connectToEndpoint(parentConnection, params2) { + const localUtils = parentConnection.localUtils(); + const transport = localUtils ? new JsonPipeTransport(localUtils) : new WebSocketTransport2(); + const connectHeaders = await transport.connect(params2); + const connection = new Connection(parentConnection._platform, localUtils, parentConnection._instrumentation, connectHeaders); + connection.markAsRemote(); + connection.on("close", () => transport.close()); + let closeError; + const onTransportClosed = (reason) => { + connection.close(reason || closeError); + }; + transport.onClose((reason) => onTransportClosed(reason)); + connection.onmessage = (message) => transport.send(message).catch(() => onTransportClosed()); + transport.onMessage((message) => { + try { + connection.dispatch(message); + } catch (e) { + closeError = String(e); + transport.close().catch(() => { + }); + } + }); + return connection; +} +var JsonPipeTransport, WebSocketTransport2; +var init_connect = __esm({ + "packages/playwright-core/src/client/connect.ts"() { + "use strict"; + init_time(); + init_timeoutRunner(); + init_browser2(); + init_connection(); + init_events(); + JsonPipeTransport = class { + constructor(owner) { + this._owner = owner; + } + async connect(params2) { + const { pipe, headers: connectHeaders } = await this._owner._channel.connect(params2); + this._pipe = pipe; + return connectHeaders; + } + async send(message) { + await this._pipe.send({ message }); + } + onMessage(callback) { + this._pipe.on("message", ({ message }) => callback(message)); + } + onClose(callback) { + this._pipe.on("closed", ({ reason }) => callback(reason)); + } + async close() { + await this._pipe.close().catch(() => { + }); + } + }; + WebSocketTransport2 = class { + async connect(params2) { + this._ws = new window.WebSocket(params2.endpoint); + return []; + } + async send(message) { + this._ws.send(JSON.stringify(message)); + } + onMessage(callback) { + this._ws.addEventListener("message", (event) => callback(JSON.parse(event.data))); + } + onClose(callback) { + this._ws.addEventListener("close", () => callback()); + } + async close() { + this._ws.close(); + } + }; + } +}); + +// packages/playwright-core/src/client/android.ts +async function loadFile(platform, file) { + if (isString(file)) + return await platform.fs().promises.readFile(file); + return file; +} +function toSelectorChannel(selector) { + const { + checkable, + checked, + clazz, + clickable, + depth, + desc, + enabled, + focusable, + focused, + hasChild, + hasDescendant, + longClickable, + pkg, + res, + scrollable, + selected, + text: text2 + } = selector; + const toRegex = (value2) => { + if (value2 === void 0) + return void 0; + if (isRegExp(value2)) + return value2.source; + return "^" + value2.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d") + "$"; + }; + return { + checkable, + checked, + clazz: toRegex(clazz), + pkg: toRegex(pkg), + desc: toRegex(desc), + res: toRegex(res), + text: toRegex(text2), + clickable, + depth, + enabled, + focusable, + focused, + hasChild: hasChild ? { androidSelector: toSelectorChannel(hasChild.selector) } : void 0, + hasDescendant: hasDescendant ? { androidSelector: toSelectorChannel(hasDescendant.selector), maxDepth: hasDescendant.maxDepth } : void 0, + longClickable, + scrollable, + selected + }; +} +var Android2, AndroidDevice2, AndroidSocket, AndroidInput, AndroidWebView; +var init_android2 = __esm({ + "packages/playwright-core/src/client/android.ts"() { + "use strict"; + init_rtti(); + init_time(); + init_timeoutRunner(); + init_eventEmitter(); + init_browserContext2(); + init_channelOwner(); + init_errors2(); + init_events(); + init_waiter(); + init_timeoutSettings(); + init_connect(); + Android2 = class extends ChannelOwner { + static from(android) { + return android._object; + } + constructor(parent, type3, guid, initializer) { + super(parent, type3, guid, initializer); + this._timeoutSettings = new TimeoutSettings(this._platform); + } + setDefaultTimeout(timeout) { + this._timeoutSettings.setDefaultTimeout(timeout); + } + async devices(options = {}) { + const { devices } = await this._channel.devices(options); + return devices.map((d) => AndroidDevice2.from(d)); + } + async launchServer(options = {}) { + if (!this._serverLauncher) + throw new Error("Launching server is not supported"); + return await this._serverLauncher.launchServer(options); + } + async connect(endpoint, options = {}) { + return await this._wrapApiCall(async () => { + const deadline = options.timeout ? monotonicTime() + options.timeout : 0; + const headers = { "x-playwright-browser": "android", ...options.headers }; + const connectParams = { endpoint, headers, slowMo: options.slowMo, timeout: options.timeout || 0 }; + const connection = await connectToEndpoint(this._connection, connectParams); + let device; + connection.on("close", () => { + device?._didClose(); + }); + const result2 = await raceAgainstDeadline(async () => { + const playwright2 = await connection.initializePlaywright(); + if (!playwright2._initializer.preConnectedAndroidDevice) { + connection.close(); + throw new Error("Malformed endpoint. Did you use Android.launchServer method?"); + } + device = AndroidDevice2.from(playwright2._initializer.preConnectedAndroidDevice); + device._shouldCloseConnectionOnClose = true; + device.on(Events.AndroidDevice.Close, () => connection.close()); + return device; + }, deadline); + if (!result2.timedOut) { + return result2.result; + } else { + connection.close(); + throw new Error(`Timeout ${options.timeout}ms exceeded`); + } + }); + } + }; + AndroidDevice2 = class extends ChannelOwner { + constructor(parent, type3, guid, initializer) { + super(parent, type3, guid, initializer); + this._webViews = /* @__PURE__ */ new Map(); + this._shouldCloseConnectionOnClose = false; + this._android = parent; + this.input = new AndroidInput(this); + this._timeoutSettings = new TimeoutSettings(this._platform, parent._timeoutSettings); + this._channel.on("webViewAdded", ({ webView }) => this._onWebViewAdded(webView)); + this._channel.on("webViewRemoved", ({ socketName }) => this._onWebViewRemoved(socketName)); + this._channel.on("close", () => this._didClose()); + } + static from(androidDevice) { + return androidDevice._object; + } + _onWebViewAdded(webView) { + const view = new AndroidWebView(this, webView); + this._webViews.set(webView.socketName, view); + this.emit(Events.AndroidDevice.WebView, view); + } + _onWebViewRemoved(socketName) { + const view = this._webViews.get(socketName); + this._webViews.delete(socketName); + if (view) + view.emit(Events.AndroidWebView.Close); + } + setDefaultTimeout(timeout) { + this._timeoutSettings.setDefaultTimeout(timeout); + } + serial() { + return this._initializer.serial; + } + model() { + return this._initializer.model; + } + webViews() { + return [...this._webViews.values()]; + } + async webView(selector, options) { + const predicate = (v) => { + if (selector.pkg) + return v.pkg() === selector.pkg; + if (selector.socketName) + return v._socketName() === selector.socketName; + return false; + }; + const webView = [...this._webViews.values()].find(predicate); + if (webView) + return webView; + return await this.waitForEvent("webview", { ...options, predicate }); + } + async wait(selector, options = {}) { + await this._channel.wait({ androidSelector: toSelectorChannel(selector), ...options, timeout: this._timeoutSettings.timeout(options) }); + } + async fill(selector, text2, options = {}) { + await this._channel.fill({ androidSelector: toSelectorChannel(selector), text: text2, ...options, timeout: this._timeoutSettings.timeout(options) }); + } + async press(selector, key, options = {}) { + await this.tap(selector, options); + await this.input.press(key); + } + async tap(selector, options = {}) { + await this._channel.tap({ androidSelector: toSelectorChannel(selector), ...options, timeout: this._timeoutSettings.timeout(options) }); + } + async drag(selector, dest, options = {}) { + await this._channel.drag({ androidSelector: toSelectorChannel(selector), dest, ...options, timeout: this._timeoutSettings.timeout(options) }); + } + async fling(selector, direction, options = {}) { + await this._channel.fling({ androidSelector: toSelectorChannel(selector), direction, ...options, timeout: this._timeoutSettings.timeout(options) }); + } + async longTap(selector, options = {}) { + await this._channel.longTap({ androidSelector: toSelectorChannel(selector), ...options, timeout: this._timeoutSettings.timeout(options) }); + } + async pinchClose(selector, percent, options = {}) { + await this._channel.pinchClose({ androidSelector: toSelectorChannel(selector), percent, ...options, timeout: this._timeoutSettings.timeout(options) }); + } + async pinchOpen(selector, percent, options = {}) { + await this._channel.pinchOpen({ androidSelector: toSelectorChannel(selector), percent, ...options, timeout: this._timeoutSettings.timeout(options) }); + } + async scroll(selector, direction, percent, options = {}) { + await this._channel.scroll({ androidSelector: toSelectorChannel(selector), direction, percent, ...options, timeout: this._timeoutSettings.timeout(options) }); + } + async swipe(selector, direction, percent, options = {}) { + await this._channel.swipe({ androidSelector: toSelectorChannel(selector), direction, percent, ...options, timeout: this._timeoutSettings.timeout(options) }); + } + async info(selector) { + return (await this._channel.info({ androidSelector: toSelectorChannel(selector) })).info; + } + async screenshot(options = {}) { + const { binary } = await this._channel.screenshot(); + if (options.path) + await this._platform.fs().promises.writeFile(options.path, binary); + return binary; + } + async [Symbol.asyncDispose]() { + await this.close(); + } + async close() { + try { + if (this._shouldCloseConnectionOnClose) + this._connection.close(); + else + await this._channel.close(); + } catch (e) { + if (isTargetClosedError2(e)) + return; + throw e; + } + } + _didClose() { + this.emit(Events.AndroidDevice.Close, this); + } + async shell(command) { + const { result: result2 } = await this._channel.shell({ command }); + return result2; + } + async open(command) { + return AndroidSocket.from((await this._channel.open({ command })).socket); + } + async installApk(file, options) { + await this._channel.installApk({ file: await loadFile(this._platform, file), args: options && options.args }); + } + async push(file, path59, options) { + await this._channel.push({ file: await loadFile(this._platform, file), path: path59, mode: options ? options.mode : void 0 }); + } + async launchBrowser(options = {}) { + const contextOptions = await prepareBrowserContextParams(this._platform, options); + const result2 = await this._channel.launchBrowser(contextOptions); + const context2 = BrowserContext2.from(result2.context); + const selectors = this._android._playwright.selectors; + selectors._contextsForSelectors.add(context2); + context2.once(Events.BrowserContext.Close, () => selectors._contextsForSelectors.delete(context2)); + await context2._initializeHarFromOptions(options.recordHar); + return context2; + } + async waitForEvent(event, optionsOrPredicate = {}) { + return await this._wrapApiCall(async () => { + const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === "function" ? {} : optionsOrPredicate); + const predicate = typeof optionsOrPredicate === "function" ? optionsOrPredicate : optionsOrPredicate.predicate; + const waiter = Waiter.createForEvent(this, event); + waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`); + if (event !== Events.AndroidDevice.Close) + waiter.rejectOnEvent(this, Events.AndroidDevice.Close, () => new TargetClosedError2()); + const result2 = await waiter.waitForEvent(this, event, predicate); + waiter.dispose(); + return result2; + }); + } + }; + AndroidSocket = class extends ChannelOwner { + static from(androidDevice) { + return androidDevice._object; + } + constructor(parent, type3, guid, initializer) { + super(parent, type3, guid, initializer); + this._channel.on("data", ({ data }) => this.emit(Events.AndroidSocket.Data, data)); + this._channel.on("close", () => this.emit(Events.AndroidSocket.Close)); + } + async write(data) { + await this._channel.write({ data }); + } + async close() { + await this._channel.close(); + } + async [Symbol.asyncDispose]() { + await this.close(); + } + }; + AndroidInput = class { + constructor(device) { + this._device = device; + } + async type(text2) { + await this._device._channel.inputType({ text: text2 }); + } + async press(key) { + await this._device._channel.inputPress({ key }); + } + async tap(point) { + await this._device._channel.inputTap({ point }); + } + async swipe(from, segments, steps) { + await this._device._channel.inputSwipe({ segments, steps }); + } + async drag(from, to, steps) { + await this._device._channel.inputDrag({ from, to, steps }); + } + }; + AndroidWebView = class extends EventEmitter3 { + constructor(device, data) { + super(device._platform); + this._device = device; + this._data = data; + } + pid() { + return this._data.pid; + } + pkg() { + return this._data.pkg; + } + _socketName() { + return this._data.socketName; + } + async page() { + if (!this._pagePromise) + this._pagePromise = this._fetchPage(); + return await this._pagePromise; + } + async _fetchPage() { + const { context: context2 } = await this._device._channel.connectToWebView({ socketName: this._data.socketName }); + return BrowserContext2.from(context2).pages()[0]; + } + }; + } +}); + +// packages/playwright-core/src/client/browserType.ts +function isConnectionTransport(value2) { + return !!value2 && typeof value2 === "object" && typeof value2.send === "function" && typeof value2.close === "function"; +} +var BrowserType2; +var init_browserType2 = __esm({ + "packages/playwright-core/src/client/browserType.ts"() { + "use strict"; + init_assert(); + init_headers(); + init_browser2(); + init_browserContext2(); + init_channelOwner(); + init_clientHelper(); + init_connect(); + init_timeoutSettings(); + init_worker(); + BrowserType2 = class extends ChannelOwner { + constructor() { + super(...arguments); + this._contexts = /* @__PURE__ */ new Set(); + } + static from(browserType) { + return browserType._object; + } + executablePath() { + if (!this._initializer.executablePath) + throw new Error("Browser is not supported on current platform"); + return this._initializer.executablePath; + } + name() { + return this._initializer.name; + } + async launch(options = {}) { + assert(!options.userDataDir, "userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead"); + assert(!options.port, "Cannot specify a port without launching as a server."); + const logger = options.logger || this._playwright._defaultLaunchOptions?.logger; + options = { ...this._playwright._defaultLaunchOptions, ...options }; + const launchOptions = { + ...options, + ignoreDefaultArgs: Array.isArray(options.ignoreDefaultArgs) ? options.ignoreDefaultArgs : void 0, + ignoreAllDefaultArgs: !!options.ignoreDefaultArgs && !Array.isArray(options.ignoreDefaultArgs), + env: options.env ? envObjectToArray2(options.env) : void 0, + timeout: new TimeoutSettings(this._platform).launchTimeout(options) + }; + return await this._wrapApiCall(async () => { + const browser = Browser2.from((await this._channel.launch(launchOptions)).browser); + browser._connectToBrowserType(this, options, logger); + return browser; + }); + } + async launchServer(options = {}) { + if (!this._serverLauncher) + throw new Error("Launching server is not supported"); + options = { ...this._playwright._defaultLaunchOptions, ...options }; + return await this._serverLauncher.launchServer(options); + } + async launchPersistentContext(userDataDir, options = {}) { + assert(!options.port, "Cannot specify a port without launching as a server."); + options = this._playwright.selectors._withSelectorOptions({ + ...this._playwright._defaultLaunchOptions, + ...options + }); + await this._instrumentation.runBeforeCreateBrowserContext(options); + const logger = options.logger || this._playwright._defaultLaunchOptions?.logger; + const contextParams = await prepareBrowserContextParams(this._platform, options); + const persistentParams = { + ...contextParams, + ignoreDefaultArgs: Array.isArray(options.ignoreDefaultArgs) ? options.ignoreDefaultArgs : void 0, + ignoreAllDefaultArgs: !!options.ignoreDefaultArgs && !Array.isArray(options.ignoreDefaultArgs), + env: options.env ? envObjectToArray2(options.env) : void 0, + channel: options.channel, + userDataDir: this._platform.path().isAbsolute(userDataDir) || !userDataDir ? userDataDir : this._platform.path().resolve(userDataDir), + timeout: new TimeoutSettings(this._platform).launchTimeout(options) + }; + const context2 = await this._wrapApiCall(async () => { + const result2 = await this._channel.launchPersistentContext(persistentParams); + const browser = Browser2.from(result2.browser); + browser._connectToBrowserType(this, options, logger); + const context3 = BrowserContext2.from(result2.context); + await context3._initializeHarFromOptions(options.recordHar); + return context3; + }); + await this._instrumentation.runAfterCreateBrowserContext(context2); + return context2; + } + async connect(optionsOrEndpoint, options) { + if (typeof optionsOrEndpoint === "string") + return await this._connect({ ...options, endpoint: optionsOrEndpoint }); + assert(optionsOrEndpoint.wsEndpoint, "options.wsEndpoint is required"); + return await this._connect({ ...options, endpoint: optionsOrEndpoint.wsEndpoint }); + } + async _connect(params2) { + return await this._wrapApiCall(async () => { + const browser = await connectToBrowser(this._playwright, { browserName: this.name(), ...params2 }); + browser._connectToBrowserType(this, {}, void 0); + return browser; + }); + } + async connectOverCDP(overloaded, options) { + let endpointURL; + let transport; + let params2; + if (typeof overloaded === "string") { + endpointURL = overloaded; + params2 = options ?? {}; + } else if (isConnectionTransport(overloaded)) { + if (this.name() !== "chromium" && this.name() !== "webkit") + throw new Error("Connecting over CDP is only supported in Chromium and WebKit."); + if (this._connection.isRemote()) + throw new Error("Passing a ConnectionTransport to connectOverCDP is not supported when connecting remotely."); + transport = overloaded; + params2 = options ?? {}; + } else { + endpointURL = "endpointURL" in overloaded ? overloaded.endpointURL : overloaded.wsEndpoint; + assert(endpointURL, "Cannot connect over CDP without wsEndpoint."); + params2 = overloaded; + } + if (endpointURL && this.name() !== "chromium" && this.name() !== "webkit") + throw new Error("Connecting over CDP is only supported in Chromium and WebKit."); + const result2 = await this._channel.connectOverCDP({ + endpointURL, + transport, + headers: params2.headers ? headersObjectToArray(params2.headers) : void 0, + slowMo: params2.slowMo, + timeout: new TimeoutSettings(this._platform).timeout(params2), + isLocal: params2.isLocal, + noDefaults: params2.noDefaults, + artifactsDir: params2.artifactsDir + }); + return await this._browserFromConnectResult(result2); + } + async _browserFromConnectResult(result2) { + const browser = Browser2.from(result2.browser); + browser._connectToBrowserType(this, {}, void 0); + if (result2.defaultContext) + await this._instrumentation.runAfterCreateBrowserContext(BrowserContext2.from(result2.defaultContext)); + return browser; + } + async _connectToWorker(endpoint, options = {}) { + if (this.name() !== "chromium") + throw new Error("Connecting to workers is only supported in Chromium."); + const result2 = await this._channel.connectToWorker({ + endpoint, + timeout: new TimeoutSettings(this._platform).timeout(options) + }); + return Worker2.from(result2.worker); + } + }; + } +}); + +// packages/playwright-core/src/client/clientInstrumentation.ts +function createInstrumentation2() { + const listeners = []; + return new Proxy({}, { + get: (obj, prop) => { + if (typeof prop !== "string") + return obj[prop]; + if (prop === "addListener") + return (listener) => listeners.push(listener); + if (prop === "removeListener") + return (listener) => listeners.splice(listeners.indexOf(listener), 1); + if (prop === "removeAllListeners") + return () => listeners.splice(0, listeners.length); + if (prop.startsWith("run")) { + return async (...params2) => { + for (const listener of listeners) + await listener[prop]?.(...params2); + }; + } + if (prop.startsWith("on")) { + return (...params2) => { + for (const listener of listeners) + listener[prop]?.(...params2); + }; + } + return obj[prop]; + } + }); +} +var init_clientInstrumentation = __esm({ + "packages/playwright-core/src/client/clientInstrumentation.ts"() { + "use strict"; + } +}); + +// packages/playwright-core/src/client/electron.ts +var Electron2, ElectronApplication2; +var init_electron2 = __esm({ + "packages/playwright-core/src/client/electron.ts"() { + "use strict"; + init_browserContext2(); + init_channelOwner(); + init_clientHelper(); + init_consoleMessage(); + init_errors2(); + init_events(); + init_jsHandle(); + init_waiter(); + init_timeoutSettings(); + Electron2 = class extends ChannelOwner { + static from(electron) { + return electron._object; + } + constructor(parent, type3, guid, initializer) { + super(parent, type3, guid, initializer); + } + async launch(options = {}) { + options = this._playwright.selectors._withSelectorOptions(options); + const params2 = { + ...await prepareBrowserContextParams(this._platform, options), + env: envObjectToArray2(options.env ? options.env : this._platform.env), + tracesDir: options.tracesDir, + artifactsDir: options.artifactsDir, + timeout: new TimeoutSettings(this._platform).launchTimeout(options) + }; + const app = ElectronApplication2.from((await this._channel.launch(params2)).electronApplication); + this._playwright.selectors._contextsForSelectors.add(app._context); + app.once(Events.ElectronApplication.Close, () => this._playwright.selectors._contextsForSelectors.delete(app._context)); + await app._context._initializeHarFromOptions(options.recordHar); + app._context.tracing._tracesDir = options.tracesDir; + return app; + } + }; + ElectronApplication2 = class extends ChannelOwner { + constructor(parent, type3, guid, initializer) { + super(parent, type3, guid, initializer); + this._windows = /* @__PURE__ */ new Set(); + this._timeoutSettings = new TimeoutSettings(this._platform); + this._context = BrowserContext2.from(initializer.context); + for (const page of this._context._pages) + this._onPage(page); + this._context.on(Events.BrowserContext.Page, (page) => this._onPage(page)); + this._channel.on("close", () => { + this.emit(Events.ElectronApplication.Close); + }); + this._channel.on("console", (event) => this.emit(Events.ElectronApplication.Console, new ConsoleMessage2(this._platform, event, null, null))); + this._setEventToSubscriptionMapping(/* @__PURE__ */ new Map([ + [Events.ElectronApplication.Console, "console"] + ])); + } + static from(electronApplication) { + return electronApplication._object; + } + process() { + return this._connection.toImpl?.(this)?.process(); + } + _onPage(page) { + this._windows.add(page); + this.emit(Events.ElectronApplication.Window, page); + page.once(Events.Page.Close, () => this._windows.delete(page)); + } + windows() { + return [...this._windows]; + } + async firstWindow(options) { + if (this._windows.size) + return this._windows.values().next().value; + return await this.waitForEvent("window", options); + } + context() { + return this._context; + } + async [Symbol.asyncDispose]() { + await this.close(); + } + async close() { + try { + await this._context.close(); + } catch (e) { + if (isTargetClosedError2(e)) + return; + throw e; + } + } + async waitForEvent(event, optionsOrPredicate = {}) { + return await this._wrapApiCall(async () => { + const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === "function" ? {} : optionsOrPredicate); + const predicate = typeof optionsOrPredicate === "function" ? optionsOrPredicate : optionsOrPredicate.predicate; + const waiter = Waiter.createForEvent(this, event); + waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`); + if (event !== Events.ElectronApplication.Close) + waiter.rejectOnEvent(this, Events.ElectronApplication.Close, () => new TargetClosedError2()); + const result2 = await waiter.waitForEvent(this, event, predicate); + waiter.dispose(); + return result2; + }); + } + async browserWindow(page) { + const result2 = await this._channel.browserWindow({ page: page._channel }); + return JSHandle2.from(result2.handle); + } + async evaluate(pageFunction, arg) { + const result2 = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: serializeArgument(arg) }); + return parseResult(result2.value); + } + async evaluateHandle(pageFunction, arg) { + const result2 = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: serializeArgument(arg) }); + return JSHandle2.from(result2.handle); + } + }; + } +}); + +// packages/playwright-core/src/client/jsonPipe.ts +var JsonPipe; +var init_jsonPipe = __esm({ + "packages/playwright-core/src/client/jsonPipe.ts"() { + "use strict"; + init_channelOwner(); + JsonPipe = class extends ChannelOwner { + static from(jsonPipe) { + return jsonPipe._object; + } + constructor(parent, type3, guid, initializer) { + super(parent, type3, guid, initializer); + } + channel() { + return this._channel; + } + }; + } +}); + +// packages/playwright-core/src/client/localUtils.ts +var LocalUtils; +var init_localUtils2 = __esm({ + "packages/playwright-core/src/client/localUtils.ts"() { + "use strict"; + init_channelOwner(); + LocalUtils = class extends ChannelOwner { + constructor(parent, type3, guid, initializer) { + super(parent, type3, guid, initializer); + this.devices = {}; + for (const { name, descriptor } of initializer.deviceDescriptors) + this.devices[name] = descriptor; + } + async zip(params2) { + return await this._channel.zip(params2); + } + async harOpen(params2) { + return await this._channel.harOpen(params2); + } + async harLookup(params2) { + return await this._channel.harLookup(params2); + } + async harClose(params2) { + return await this._channel.harClose(params2); + } + async harUnzip(params2) { + return await this._channel.harUnzip(params2); + } + async tracingStarted(params2) { + return await this._channel.tracingStarted(params2); + } + async traceDiscarded(params2) { + return await this._channel.traceDiscarded(params2); + } + async addStackToTracingNoReply(params2) { + return await this._channel.addStackToTracingNoReply(params2); + } + }; + } +}); + +// packages/playwright-core/src/client/selectors.ts +var Selectors2; +var init_selectors2 = __esm({ + "packages/playwright-core/src/client/selectors.ts"() { + "use strict"; + init_clientHelper(); + init_locator(); + Selectors2 = class { + constructor(platform) { + this._selectorEngines = []; + this._contextsForSelectors = /* @__PURE__ */ new Set(); + this._platform = platform; + } + async register(name, script, options = {}) { + if (this._selectorEngines.some((engine) => engine.name === name)) + throw new Error(`selectors.register: "${name}" selector engine has been already registered`); + const source11 = await evaluationScript(this._platform, script, void 0, false); + const selectorEngine = { ...options, name, source: source11 }; + for (const context2 of this._contextsForSelectors) + await context2._channel.registerSelectorEngine({ selectorEngine }); + this._selectorEngines.push(selectorEngine); + } + setTestIdAttribute(attributeName) { + this._testIdAttributeName = attributeName; + setTestIdAttribute(attributeName); + for (const context2 of this._contextsForSelectors) { + context2._options.testIdAttributeName = attributeName; + context2._channel.setTestIdAttributeName({ testIdAttributeName: attributeName }).catch(() => { + }); + } + } + _withSelectorOptions(options) { + return { ...options, selectorEngines: this._selectorEngines, testIdAttributeName: this._testIdAttributeName }; + } + }; + } +}); + +// packages/playwright-core/src/client/playwright.ts +var Playwright2; +var init_playwright2 = __esm({ + "packages/playwright-core/src/client/playwright.ts"() { + "use strict"; + init_android2(); + init_browser2(); + init_browserType2(); + init_channelOwner(); + init_electron2(); + init_errors2(); + init_fetch2(); + init_selectors2(); + Playwright2 = class extends ChannelOwner { + constructor(parent, type3, guid, initializer) { + super(parent, type3, guid, initializer); + this.request = new APIRequest(this); + this.chromium = BrowserType2.from(initializer.chromium); + this.chromium._playwright = this; + this.firefox = BrowserType2.from(initializer.firefox); + this.firefox._playwright = this; + this.webkit = BrowserType2.from(initializer.webkit); + this.webkit._playwright = this; + this._android = Android2.from(initializer.android); + this._android._playwright = this; + this._electron = Electron2.from(initializer.electron); + this._electron._playwright = this; + this.devices = this._connection.localUtils()?.devices ?? {}; + this.selectors = new Selectors2(this._connection._platform); + this.errors = { TimeoutError: TimeoutError2 }; + } + static from(channel) { + return channel._object; + } + _browserTypes() { + return [this.chromium, this.firefox, this.webkit]; + } + _preLaunchedBrowser() { + const browser = Browser2.from(this._initializer.preLaunchedBrowser); + browser._connectToBrowserType(this[browser._name], {}, void 0); + return browser; + } + _allContexts() { + return this._browserTypes().flatMap((type3) => [...type3._contexts]); + } + _allPages() { + return this._allContexts().flatMap((context2) => context2.pages()); + } + }; + } +}); + +// packages/playwright-core/src/client/connection.ts +function formatCallLog(platform, log2) { + if (!log2 || !log2.some((l) => !!l)) + return ""; + return ` +Call log: +${platform.colors.dim(log2.join("\n"))} +`; +} +var Root, DummyChannelOwner, Connection; +var init_connection = __esm({ + "packages/playwright-core/src/client/connection.ts"() { + "use strict"; + init_stackTrace(); + init_eventEmitter(); + init_android2(); + init_artifact2(); + init_browser2(); + init_browserContext2(); + init_browserType2(); + init_cdpSession(); + init_channelOwner(); + init_clientInstrumentation(); + init_debugger2(); + init_dialog2(); + init_disposable3(); + init_electron2(); + init_elementHandle(); + init_errors2(); + init_fetch2(); + init_frame(); + init_jsHandle(); + init_jsonPipe(); + init_localUtils2(); + init_network3(); + init_page2(); + init_playwright2(); + init_stream(); + init_tracing2(); + init_worker(); + init_writableStream(); + init_validator(); + Root = class extends ChannelOwner { + constructor(connection) { + super(connection, "Root", "", {}); + } + async initialize() { + return Playwright2.from((await this._channel.initialize({ + sdkLanguage: "javascript" + })).playwright); + } + }; + DummyChannelOwner = class extends ChannelOwner { + }; + Connection = class extends EventEmitter3 { + constructor(platform, localUtils, instrumentation, headers = []) { + super(platform); + this._objects = /* @__PURE__ */ new Map(); + this.onmessage = (message) => { + }; + this._lastId = 0; + this._callbacks = /* @__PURE__ */ new Map(); + this._isRemote = false; + this._rawBuffers = false; + this._tracingCount = 0; + this._objectFactories = /* @__PURE__ */ new Map(); + this._instrumentation = instrumentation || createInstrumentation2(); + this._localUtils = localUtils; + this._rootObject = new Root(this); + this.headers = headers; + this.registerObjectFactories({ + Android: (parent, type3, guid, init) => new Android2(parent, type3, guid, init), + AndroidDevice: (parent, type3, guid, init) => new AndroidDevice2(parent, type3, guid, init), + AndroidSocket: (parent, type3, guid, init) => new AndroidSocket(parent, type3, guid, init), + APIRequestContext: (parent, type3, guid, init) => new APIRequestContext2(parent, type3, guid, init), + Artifact: (parent, type3, guid, init) => new Artifact2(parent, type3, guid, init), + BindingCall: (parent, type3, guid, init) => new BindingCall(parent, type3, guid, init), + Browser: (parent, type3, guid, init) => new Browser2(parent, type3, guid, init), + BrowserContext: (parent, type3, guid, init) => new BrowserContext2(parent, type3, guid, init), + BrowserType: (parent, type3, guid, init) => new BrowserType2(parent, type3, guid, init), + CDPSession: (parent, type3, guid, init) => new CDPSession2(parent, type3, guid, init), + Debugger: (parent, type3, guid, init) => new Debugger2(parent, type3, guid, init), + Dialog: (parent, type3, guid, init) => new Dialog2(parent, type3, guid, init), + Disposable: (parent, type3, guid, init) => new DisposableObject2(parent, type3, guid, init), + Electron: (parent, type3, guid, init) => new Electron2(parent, type3, guid, init), + ElectronApplication: (parent, type3, guid, init) => new ElectronApplication2(parent, type3, guid, init), + ElementHandle: (parent, type3, guid, init) => new ElementHandle2(parent, type3, guid, init), + Frame: (parent, type3, guid, init) => new Frame2(parent, type3, guid, init), + JSHandle: (parent, type3, guid, init) => new JSHandle2(parent, type3, guid, init), + JsonPipe: (parent, type3, guid, init) => new JsonPipe(parent, type3, guid, init), + LocalUtils: (parent, type3, guid, init) => { + const result2 = new LocalUtils(parent, type3, guid, init); + if (!this._localUtils) + this._localUtils = result2; + return result2; + }, + Page: (parent, type3, guid, init) => new Page2(parent, type3, guid, init), + Playwright: (parent, type3, guid, init) => new Playwright2(parent, type3, guid, init), + Request: (parent, type3, guid, init) => new Request2(parent, type3, guid, init), + Response: (parent, type3, guid, init) => new Response3(parent, type3, guid, init), + Route: (parent, type3, guid, init) => new Route2(parent, type3, guid, init), + Stream: (parent, type3, guid, init) => new Stream(parent, type3, guid, init), + SocksSupport: (parent, type3, guid, init) => new DummyChannelOwner(parent, type3, guid, init), + Tracing: (parent, type3, guid, init) => new Tracing2(parent, type3, guid, init), + WebSocket: (parent, type3, guid, init) => new WebSocket2(parent, type3, guid, init), + WebSocketRoute: (parent, type3, guid, init) => new WebSocketRoute(parent, type3, guid, init), + Worker: (parent, type3, guid, init) => new Worker2(parent, type3, guid, init), + WritableStream: (parent, type3, guid, init) => new WritableStream(parent, type3, guid, init) + }); + } + registerObjectFactories(factories) { + for (const [type3, factory] of Object.entries(factories)) + this._objectFactories.set(type3, factory); + } + markAsRemote() { + this._isRemote = true; + } + isRemote() { + return this._isRemote; + } + useRawBuffers() { + this._rawBuffers = true; + } + rawBuffers() { + return this._rawBuffers; + } + localUtils() { + return this._localUtils; + } + async initializePlaywright() { + return await this._rootObject.initialize(); + } + getObjectWithKnownName(guid) { + return this._objects.get(guid); + } + setIsTracing(isTracing) { + if (isTracing) + this._tracingCount++; + else + this._tracingCount--; + } + async sendMessageToServer(object, method, params2, options) { + if (method === "__waitInfo__" && (this._closedError || object._wasCollected)) + return; + if (this._closedError) + throw this._closedError; + if (object._wasCollected) + throw new Error("The object has been collected to prevent unbounded heap growth."); + const guid = object._guid; + const type3 = object._type; + const id = ++this._lastId; + const message = { id, guid, method, params: params2 }; + if (this._platform.isLogEnabled("channel")) { + this._platform.log("channel", "SEND> " + JSON.stringify(message)); + } + const location2 = options.frames?.[0] ? { file: options.frames[0].file, line: options.frames[0].line, column: options.frames[0].column } : void 0; + const metadata = { title: options.title, location: location2, internal: options.internal, stepId: options.stepId }; + if (this._tracingCount && options.frames && type3 !== "LocalUtils") + this._localUtils?.addStackToTracingNoReply({ callData: { stack: options.frames ?? [], id } }).catch(() => { + }); + this._platform.zones.empty.run(() => this.onmessage({ ...message, metadata })); + if (method === "__waitInfo__") + return; + return await new Promise((resolve, reject) => this._callbacks.set(id, { resolve, reject, title: options.title, type: type3, method })); + } + _validatorFromWireContext() { + return { + tChannelImpl: this._tChannelImplFromWire.bind(this), + binary: this._rawBuffers ? "buffer" : "fromBase64", + isUnderTest: () => this._platform.isUnderTest() + }; + } + dispatch(message) { + if (this._closedError) + return; + const { id, guid, method, params: params2, result: result2, error, errorDetails, log: log2 } = message; + if (id) { + if (this._platform.isLogEnabled("channel")) + this._platform.log("channel", " createInProcessPlaywright, + playwright: () => playwright +}); +function createInProcessPlaywright() { + const playwright2 = createPlaywright({ sdkLanguage: process.env.PW_LANG_NAME || "javascript", isClientCollocatedWithServer: true }); + const clientConnection = new Connection(nodePlatform(packageRoot)); + clientConnection.useRawBuffers(); + const dispatcherConnection = new DispatcherConnection( + true + /* in process */ + ); + dispatcherConnection.onmessage = (message) => clientConnection.dispatch(message); + clientConnection.onmessage = (message) => dispatcherConnection.dispatch(message); + const rootScope = new RootDispatcher(dispatcherConnection); + new PlaywrightDispatcher(rootScope, playwright2); + const playwrightAPI = clientConnection.getObjectWithKnownName("Playwright"); + playwrightAPI.chromium._serverLauncher = new BrowserServerLauncherImpl("chromium"); + playwrightAPI.firefox._serverLauncher = new BrowserServerLauncherImpl("firefox"); + playwrightAPI.webkit._serverLauncher = new BrowserServerLauncherImpl("webkit"); + playwrightAPI._android._serverLauncher = new AndroidServerLauncherImpl(); + dispatcherConnection.onmessage = (message) => setImmediate(() => clientConnection.dispatch(message)); + clientConnection.onmessage = (message) => setImmediate(() => dispatcherConnection.dispatch(message)); + clientConnection.toImpl = (x) => { + if (x instanceof Connection) + return x === clientConnection ? dispatcherConnection : void 0; + if (!x) + return dispatcherConnection._dispatcherByGuid.get(""); + return dispatcherConnection._dispatcherByGuid.get(x._guid)._object; + }; + return playwrightAPI; +} +var playwright; +var init_inprocess = __esm({ + "packages/playwright-core/src/inprocess.ts"() { + "use strict"; + init_nodePlatform(); + init_androidServerImpl(); + init_browserServerImpl(); + init_server(); + init_connection(); + init_package(); + playwright = createInProcessPlaywright(); + } +}); + +// packages/playwright-core/src/tools/backend/utils.ts +async function waitForCompletion(tab2, callback) { + const requests2 = []; + const requestListener = (request2) => requests2.push(request2); + const disposeListeners = () => { + tab2.page.off("request", requestListener); + }; + tab2.page.on("request", requestListener); + let result2; + try { + result2 = await callback(); + await tab2.waitForTimeout(500); + } finally { + disposeListeners(); + } + const requestedNavigation = requests2.some((request2) => request2.isNavigationRequest()); + if (requestedNavigation) { + await tab2.page.mainFrame().waitForLoadState("load", { timeout: 1e4 }).catch(() => { + }); + return result2; + } + const promises = []; + for (const request2 of requests2) { + if (["document", "stylesheet", "script", "xhr", "fetch"].includes(request2.resourceType())) + promises.push(request2.response().then((r) => r?.finished()).catch(() => { + })); + else + promises.push(request2.response().catch(() => { + })); + } + const timeout = new Promise((resolve) => setTimeout(resolve, 5e3)); + await Promise.race([Promise.all(promises), timeout]); + if (requests2.length) + await tab2.waitForTimeout(500); + return result2; +} +function eventWaiter(page, event, timeout) { + const disposables = []; + const eventPromise = new Promise((resolve, reject) => { + page.on(event, resolve); + disposables.push(() => page.off(event, resolve)); + }); + let abort; + const abortPromise = new Promise((resolve, reject) => { + abort = () => resolve(void 0); + }); + const timeoutPromise = new Promise((f) => { + const timeoutId = setTimeout(() => f(void 0), timeout); + disposables.push(() => clearTimeout(timeoutId)); + }); + return { + promise: Promise.race([eventPromise, abortPromise, timeoutPromise]).finally(() => disposables.forEach((dispose) => dispose())), + abort + }; +} +var init_utils3 = __esm({ + "packages/playwright-core/src/tools/backend/utils.ts"() { + "use strict"; + } +}); + +// packages/playwright-core/src/tools/backend/logFile.ts +var import_fs41, import_path38, debug5, LogFile; +var init_logFile = __esm({ + "packages/playwright-core/src/tools/backend/logFile.ts"() { + "use strict"; + import_fs41 = __toESM(require("fs")); + import_path38 = __toESM(require("path")); + debug5 = require("./utilsBundle").debug; + LogFile = class { + constructor(context2, startTime, filePrefix, title) { + this._stopped = false; + this._line = 0; + this._entries = 0; + this._lastLine = 0; + this._lastEntries = 0; + this._writeChain = Promise.resolve(); + this._context = context2; + this._startTime = startTime; + this._filePrefix = filePrefix; + this._title = title; + } + appendLine(wallTime, text2) { + this._writeChain = this._writeChain.then(() => this._write(wallTime, text2)).catch((e) => debug5("pw:tools:error")(e)); + } + stop() { + this._stopped = true; + } + async take(relativeTo) { + const logChunk = await this._take(); + if (!logChunk) + return void 0; + const logFilePath = relativeTo ? import_path38.default.relative(relativeTo, logChunk.file) : logChunk.file; + const lineRange = logChunk.fromLine === logChunk.toLine ? `#L${logChunk.fromLine}` : `#L${logChunk.fromLine}-L${logChunk.toLine}`; + return `${logFilePath}${lineRange}`; + } + async _take() { + await this._writeChain; + if (!this._file || this._entries === this._lastEntries) + return void 0; + const chunk = { + type: this._title.toLowerCase(), + file: this._file, + fromLine: this._lastLine + 1, + toLine: this._line, + entryCount: this._entries - this._lastEntries + }; + this._lastLine = this._line; + this._lastEntries = this._entries; + return chunk; + } + async _write(wallTime, text2) { + if (this._stopped) + return; + this._file ??= await this._context.outputFile({ prefix: this._filePrefix, ext: "log", date: new Date(this._startTime) }, { origin: "code" }); + const relativeTime = Math.round(wallTime - this._startTime); + const logLine = `[${String(relativeTime).padStart(8, " ")}ms] ${text2} +`; + await import_fs41.default.promises.appendFile(this._file, logLine); + const lineCount = logLine.split("\n").length - 1; + this._line += lineCount; + this._entries++; + } + }; + } +}); + +// packages/playwright-core/src/tools/backend/tool.ts +function defineTool(tool) { + return tool; +} +function defineTabTool(tool) { + return { + ...tool, + handle: async (context2, params2, response2, signal) => { + const tab2 = await context2.ensureTab(); + const modalStates = tab2.modalStates().map((state) => state.type); + if (tool.clearsModalState && !modalStates.includes(tool.clearsModalState)) + response2.addError(`Error: The tool "${tool.schema.name}" can only be used when there is related modal state present.`); + else if (!tool.clearsModalState && modalStates.length) + response2.addError(`Error: Tool "${tool.schema.name}" does not handle the modal state.`); + else + return tool.handle(tab2, params2, response2, signal); + } + }; +} +var init_tool = __esm({ + "packages/playwright-core/src/tools/backend/tool.ts"() { + "use strict"; + } +}); + +// packages/playwright-core/src/tools/backend/dialogs.ts +var z, handleDialog, dialogs_default; +var init_dialogs = __esm({ + "packages/playwright-core/src/tools/backend/dialogs.ts"() { + "use strict"; + init_tool(); + z = require("./utilsBundle").z; + handleDialog = defineTabTool({ + capability: "core", + schema: { + name: "browser_handle_dialog", + title: "Handle a dialog", + description: "Handle a dialog", + inputSchema: z.object({ + accept: z.boolean().describe("Whether to accept the dialog."), + promptText: z.string().optional().describe("The text of the prompt in case of a prompt dialog.") + }), + type: "action" + }, + handle: async (tab2, params2, response2) => { + const dialogState = tab2.modalStates().find((state) => state.type === "dialog"); + if (!dialogState) + throw new Error("No dialog visible"); + tab2.clearModalState(dialogState); + await tab2.waitForCompletion(async () => { + if (params2.accept) + await dialogState.dialog.accept(params2.promptText); + else + await dialogState.dialog.dismiss(); + }); + }, + clearsModalState: "dialog" + }); + dialogs_default = [ + handleDialog + ]; + } +}); + +// packages/playwright-core/src/tools/backend/snapshot.ts +var z2, elementTargetDescription, optionalElementSchema, elementSchema, snapshot, clickSchema, click, drag, hover, selectOptionSchema, selectOption, generateLocator, check, uncheck, snapshot_default; +var init_snapshot = __esm({ + "packages/playwright-core/src/tools/backend/snapshot.ts"() { + "use strict"; + init_stringUtils(); + init_tool(); + z2 = require("./utilsBundle").z; + elementTargetDescription = "Exact target element reference from the page snapshot, or a unique element selector"; + optionalElementSchema = z2.object({ + element: z2.string().optional().describe("Human-readable element description used to obtain permission to interact with the element"), + target: z2.string().optional().describe(elementTargetDescription) + }); + elementSchema = z2.object({ + element: z2.string().optional().describe("Human-readable element description used to obtain permission to interact with the element"), + target: z2.string().describe(elementTargetDescription) + }); + snapshot = defineTabTool({ + capability: "core", + schema: { + name: "browser_snapshot", + title: "Page snapshot", + description: "Capture accessibility snapshot of the current page, this is better than screenshot", + inputSchema: z2.object({ + target: z2.string().optional().describe(elementTargetDescription), + filename: z2.string().optional().describe("Save snapshot to markdown file instead of returning it in the response."), + depth: z2.number().optional().describe("Limit the depth of the snapshot tree"), + boxes: z2.boolean().optional().describe("Include each element's bounding box as [box=x,y,width,height] in the snapshot. Coordinates are viewport-relative, in CSS pixels (Element.getBoundingClientRect)") + }), + type: "readOnly" + }, + handle: async (tab2, params2, response2) => { + let resolved = { locator: void 0, resolved: "" }; + if (params2.target) + resolved = await tab2.targetLocator({ target: params2.target }); + response2.setIncludeFullSnapshot(params2.filename, resolved.locator, params2.depth, params2.boxes); + } + }); + clickSchema = elementSchema.extend({ + doubleClick: z2.boolean().optional().describe("Whether to perform a double click instead of a single click"), + button: z2.enum(["left", "right", "middle"]).optional().describe("Button to click, defaults to left"), + modifiers: z2.array(z2.enum(["Alt", "Control", "ControlOrMeta", "Meta", "Shift"])).optional().describe("Modifier keys to press") + }); + click = defineTabTool({ + capability: "core", + schema: { + name: "browser_click", + title: "Click", + description: "Perform click on a web page", + inputSchema: clickSchema, + type: "input" + }, + handle: async (tab2, params2, response2) => { + response2.setIncludeSnapshot(); + const { locator: locator2, resolved } = await tab2.targetLocator(params2); + const options = { + button: params2.button, + modifiers: params2.modifiers, + ...tab2.actionTimeoutOptions + }; + const optionsArg = formatObjectOrVoid(options); + if (params2.doubleClick) + response2.addCode(`await page.${resolved}.dblclick(${optionsArg});`); + else + response2.addCode(`await page.${resolved}.click(${optionsArg});`); + await tab2.waitForCompletion(async () => { + if (params2.doubleClick) + await locator2.dblclick(options); + else + await locator2.click(options); + }); + } + }); + drag = defineTabTool({ + capability: "core", + schema: { + name: "browser_drag", + title: "Drag mouse", + description: "Perform drag and drop between two elements", + inputSchema: z2.object({ + startElement: z2.string().optional().describe("Human-readable source element description used to obtain the permission to interact with the element"), + startTarget: z2.string().describe(elementTargetDescription), + endElement: z2.string().optional().describe("Human-readable target element description used to obtain the permission to interact with the element"), + endTarget: z2.string().describe(elementTargetDescription) + }), + type: "input" + }, + handle: async (tab2, params2, response2) => { + response2.setIncludeSnapshot(); + const [start3, end] = await tab2.targetLocators([ + { target: params2.startTarget, element: params2.startElement }, + { target: params2.endTarget, element: params2.endElement } + ]); + await tab2.waitForCompletion(async () => { + await start3.locator.dragTo(end.locator, tab2.actionTimeoutOptions); + }); + response2.addCode(`await page.${start3.resolved}.dragTo(page.${end.resolved});`); + } + }); + hover = defineTabTool({ + capability: "core", + schema: { + name: "browser_hover", + title: "Hover mouse", + description: "Hover over element on page", + inputSchema: elementSchema, + type: "input" + }, + handle: async (tab2, params2, response2) => { + response2.setIncludeSnapshot(); + const { locator: locator2, resolved } = await tab2.targetLocator(params2); + response2.addCode(`await page.${resolved}.hover();`); + await locator2.hover(tab2.actionTimeoutOptions); + } + }); + selectOptionSchema = elementSchema.extend({ + values: z2.array(z2.string()).describe("Array of values to select in the dropdown. This can be a single value or multiple values.") + }); + selectOption = defineTabTool({ + capability: "core", + schema: { + name: "browser_select_option", + title: "Select option", + description: "Select an option in a dropdown", + inputSchema: selectOptionSchema, + type: "input" + }, + handle: async (tab2, params2, response2) => { + response2.setIncludeSnapshot(); + const { locator: locator2, resolved } = await tab2.targetLocator(params2); + response2.addCode(`await page.${resolved}.selectOption(${formatObject(params2.values)});`); + await locator2.selectOption(params2.values, tab2.actionTimeoutOptions); + } + }); + generateLocator = defineTabTool({ + capability: "testing", + schema: { + name: "browser_generate_locator", + title: "Create locator for element", + description: "Generate locator for the given element to use in tests", + inputSchema: elementSchema, + type: "readOnly" + }, + handle: async (tab2, params2, response2) => { + const { resolved } = await tab2.targetLocator(params2); + response2.addTextResult(resolved); + } + }); + check = defineTabTool({ + capability: "core-input", + skillOnly: true, + schema: { + name: "browser_check", + title: "Check", + description: "Check a checkbox or radio button", + inputSchema: elementSchema, + type: "input" + }, + handle: async (tab2, params2, response2) => { + const { locator: locator2, resolved } = await tab2.targetLocator(params2); + response2.addCode(`await page.${resolved}.check();`); + await locator2.check(tab2.actionTimeoutOptions); + } + }); + uncheck = defineTabTool({ + capability: "core-input", + skillOnly: true, + schema: { + name: "browser_uncheck", + title: "Uncheck", + description: "Uncheck a checkbox or radio button", + inputSchema: elementSchema, + type: "input" + }, + handle: async (tab2, params2, response2) => { + const { locator: locator2, resolved } = await tab2.targetLocator(params2); + response2.addCode(`await page.${resolved}.uncheck();`); + await locator2.uncheck(tab2.actionTimeoutOptions); + } + }); + snapshot_default = [ + snapshot, + click, + drag, + hover, + selectOption, + generateLocator, + check, + uncheck + ]; + } +}); + +// packages/playwright-core/src/tools/backend/files.ts +var z3, uploadFile, drop, files_default; +var init_files = __esm({ + "packages/playwright-core/src/tools/backend/files.ts"() { + "use strict"; + init_stringUtils(); + init_tool(); + init_snapshot(); + z3 = require("./utilsBundle").z; + uploadFile = defineTabTool({ + capability: "core", + schema: { + name: "browser_file_upload", + title: "Upload files", + description: "Upload one or multiple files", + inputSchema: z3.object({ + paths: z3.array(z3.string()).optional().describe("The absolute paths to the files to upload. Can be single file or multiple files. If omitted, file chooser is cancelled.") + }), + type: "action" + }, + handle: async (tab2, params2, response2) => { + response2.setIncludeSnapshot(); + const modalState = tab2.modalStates().find((state) => state.type === "fileChooser"); + if (!modalState) + throw new Error("No file chooser visible"); + if (params2.paths) + await Promise.all(params2.paths.map((filePath) => response2.resolveClientFilename(filePath))); + response2.addCode(`await fileChooser.setFiles(${JSON.stringify(params2.paths)})`); + tab2.clearModalState(modalState); + await tab2.waitForCompletion(async () => { + if (params2.paths) + await modalState.fileChooser.setFiles(params2.paths); + }); + }, + clearsModalState: "fileChooser" + }); + drop = defineTabTool({ + capability: "core", + schema: { + name: "browser_drop", + title: "Drop files or data onto an element", + description: 'Drop files or MIME-typed data onto an element, as if dragged from outside the page. At least one of "paths" or "data" must be provided.', + inputSchema: elementSchema.extend({ + paths: z3.array(z3.string()).optional().describe("Absolute paths to files to drop onto the element."), + data: z3.record(z3.string(), z3.string()).optional().describe('Data to drop, as a map of MIME type to string value (e.g. {"text/plain": "hello", "text/uri-list": "https://example.com"}).') + }), + type: "action" + }, + handle: async (tab2, params2, response2) => { + if (!params2.paths?.length && !params2.data) + throw new Error('At least one of "paths" or "data" must be provided.'); + response2.setIncludeSnapshot(); + const { locator: locator2, resolved } = await tab2.targetLocator(params2); + if (params2.paths) + await Promise.all(params2.paths.map((p) => response2.resolveClientFilename(p))); + const payload = {}; + if (params2.paths?.length) + payload.files = params2.paths.length === 1 ? params2.paths[0] : params2.paths; + if (params2.data) + payload.data = params2.data; + await tab2.waitForCompletion(async () => { + await locator2.drop(payload, tab2.actionTimeoutOptions); + }); + response2.addCode(`await page.${resolved}.drop(${formatObject(payload)});`); + } + }); + files_default = [ + uploadFile, + drop + ]; + } +}); + +// packages/playwright-core/src/tools/backend/tab.ts +function messageToConsoleMessage(message) { + return { + type: message.type(), + timestamp: message.timestamp(), + text: message.text(), + toString: () => `[${message.type().toUpperCase()}] ${message.text()} @ ${message.location().url}:${message.location().lineNumber}` + }; +} +function pageErrorToConsoleMessage(errorOrValue) { + if (errorOrValue instanceof Error) { + return { + type: "error", + timestamp: Date.now(), + text: errorOrValue.message, + toString: () => errorOrValue.stack || errorOrValue.message + }; + } + return { + type: "error", + timestamp: Date.now(), + text: String(errorOrValue), + toString: () => String(errorOrValue) + }; +} +function renderModalStates(config, modalStates) { + const result2 = []; + if (modalStates.length === 0) + result2.push("- There is no modal state present"); + for (const state of modalStates) + result2.push(`- [${state.description}]: can be handled by ${config.skillMode ? state.clearedBy.skill : state.clearedBy.tool}`); + return result2; +} +function shouldIncludeMessage(thresholdLevel, type3) { + const messageLevel = consoleLevelForMessageType(type3); + return consoleMessageLevels.indexOf(messageLevel) <= consoleMessageLevels.indexOf(thresholdLevel || "info"); +} +function consoleLevelForMessageType(type3) { + switch (type3) { + case "assert": + case "error": + return "error"; + case "warning": + return "warning"; + case "count": + case "dir": + case "dirxml": + case "info": + case "log": + case "table": + case "time": + case "timeEnd": + return "info"; + case "clear": + case "debug": + case "endGroup": + case "profile": + case "profileEnd": + case "startGroup": + case "startGroupCollapsed": + case "trace": + return "debug"; + default: + return "info"; + } +} +function sanitizeForFilePath2(s) { + const sanitize = (s2) => s2.replace(/[\x00-\x2C\x2E-\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]+/g, "-"); + const separator = s.lastIndexOf("."); + if (separator === -1) + return sanitize(s); + return sanitize(s.substring(0, separator)) + "." + sanitize(s.substring(separator + 1)); +} +function tabHeaderEquals(a, b) { + return a.title === b.title && a.url === b.url && a.current === b.current && a.crashed === b.crashed && a.console.errors === b.console.errors && a.console.warnings === b.console.warnings && a.console.total === b.console.total; +} +var import_events28, debug6, TabEvents, Tab, consoleMessageLevels, tabSymbol; +var init_tab = __esm({ + "packages/playwright-core/src/tools/backend/tab.ts"() { + "use strict"; + import_events28 = require("events"); + init_locatorGenerators(); + init_locatorParser(); + init_manualPromise(); + init_eventsHelper(); + init_disposable(); + init_utils3(); + init_logFile(); + init_dialogs(); + init_files(); + debug6 = require("./utilsBundle").debug; + TabEvents = { + modalState: "modalState" + }; + Tab = class _Tab extends import_events28.EventEmitter { + constructor(context2, page, onPageClose) { + super(); + this._lastHeader = { title: "about:blank", url: "about:blank", current: false, crashed: false, console: { total: 0, warnings: 0, errors: 0 } }; + this._downloads = []; + this._requests = []; + this.crashed = false; + this._modalStates = []; + this._recentEventEntries = []; + this.context = context2; + this.page = page; + this._onPageClose = onPageClose; + const p = page; + this._disposables = [ + eventsHelper.addEventListener(p, "console", (event) => this._handleConsoleMessage(messageToConsoleMessage(event))), + eventsHelper.addEventListener(p, "pageerror", (error) => this._handleConsoleMessage(pageErrorToConsoleMessage(error))), + eventsHelper.addEventListener(p, "request", (request2) => this._handleRequest(request2)), + eventsHelper.addEventListener(p, "response", (response2) => this._handleResponse(response2)), + eventsHelper.addEventListener(p, "requestfailed", (request2) => this._handleRequestFailed(request2)), + eventsHelper.addEventListener(p, "close", () => this._onClose()), + eventsHelper.addEventListener(p, "crash", () => { + this.crashed = true; + }), + eventsHelper.addEventListener(p, "filechooser", (chooser) => { + this.setModalState({ + type: "fileChooser", + description: "File chooser", + fileChooser: chooser, + clearedBy: { tool: uploadFile.schema.name, skill: "upload" } + }); + }), + eventsHelper.addEventListener(p, "dialog", (dialog) => this._dialogShown(dialog)), + eventsHelper.addEventListener(p, "download", (download) => { + void this._downloadStarted(download); + }) + ]; + page[tabSymbol] = this; + const wallTime = Date.now(); + this._consoleLog = new LogFile(this.context, wallTime, "console", "Console"); + this._initializedPromise = this._initialize(); + this.actionTimeoutOptions = { timeout: context2.config.timeouts?.action }; + this.navigationTimeoutOptions = { timeout: context2.config.timeouts?.navigation }; + this.expectTimeoutOptions = { timeout: context2.config.timeouts?.expect }; + } + async dispose() { + await disposeAll(this._disposables); + this._consoleLog.stop(); + } + static forPage(page) { + return page[tabSymbol]; + } + static async collectConsoleMessages(page) { + const result2 = []; + const messages = await page.consoleMessages().catch(() => []); + for (const message of messages) + result2.push(messageToConsoleMessage(message)); + const errors = await page.pageErrors().catch(() => []); + for (const error of errors) + result2.push(pageErrorToConsoleMessage(error)); + return result2; + } + async _initialize() { + for (const message of await _Tab.collectConsoleMessages(this.page)) + this._handleConsoleMessage(message); + const requests2 = await this.page.requests().catch(() => []); + for (const request2 of requests2.filter((r) => r.existingResponse() || r.failure())) + this._requests.push(request2); + for (const initPage of this.context.config.browser?.initPage || []) { + try { + const { default: func } = require(initPage); + await func({ page: this.page }); + } catch (e) { + const reason = e instanceof Error ? e.message : String(e); + throw new Error(`Failed to load init page "${initPage}": ${reason}`, { cause: e }); + } + } + } + modalStates() { + return this._modalStates; + } + setModalState(modalState) { + this._modalStates.push(modalState); + this.emit(TabEvents.modalState, modalState); + } + clearModalState(modalState) { + this._modalStates = this._modalStates.filter((state) => state !== modalState); + } + _dialogShown(dialog) { + this.setModalState({ + type: "dialog", + description: `"${dialog.type()}" dialog with message "${dialog.message()}"`, + dialog, + clearedBy: { tool: handleDialog.schema.name, skill: "dialog-accept or dialog-dismiss" } + }); + } + async _downloadStarted(download) { + const outputFile2 = await this.context.outputFile({ suggestedFilename: sanitizeForFilePath2(download.suggestedFilename()), prefix: "download", ext: "bin" }, { origin: "code" }); + const entry = { + download, + finished: false, + outputFile: outputFile2 + }; + this._downloads.push(entry); + this._addLogEntry({ type: "download-start", wallTime: Date.now(), download: entry }); + await download.saveAs(entry.outputFile); + entry.finished = true; + this._addLogEntry({ type: "download-finish", wallTime: Date.now(), download: entry }); + } + _clearCollectedArtifacts() { + this._downloads.length = 0; + this._requests.length = 0; + this._recentEventEntries.length = 0; + this._resetLogs(); + } + _resetLogs() { + const wallTime = Date.now(); + this._consoleLog.stop(); + this._consoleLog = new LogFile(this.context, wallTime, "console", "Console"); + } + _handleRequest(request2) { + this._requests.push(request2); + const wallTime = request2.timing().startTime || Date.now(); + this._addLogEntry({ type: "request", wallTime, request: request2 }); + } + _handleResponse(response2) { + const timing = response2.request().timing(); + const wallTime = timing.responseStart + timing.startTime; + this._addLogEntry({ type: "request", wallTime, request: response2.request() }); + } + _handleRequestFailed(request2) { + this._requests.push(request2); + const timing = request2.timing(); + const wallTime = timing.responseEnd + timing.startTime; + this._addLogEntry({ type: "request", wallTime, request: request2 }); + } + _handleConsoleMessage(message) { + const wallTime = message.timestamp; + this._addLogEntry({ type: "console", wallTime, message }); + if (shouldIncludeMessage(this.context.config.console?.level, message.type)) + this._consoleLog.appendLine(wallTime, message.toString()); + } + logErrorMessage(text2) { + this._handleConsoleMessage(pageErrorToConsoleMessage(new Error(text2))); + } + _addLogEntry(entry) { + this._recentEventEntries.push(entry); + } + _onClose() { + this._clearCollectedArtifacts(); + this._onPageClose(this); + } + async headerSnapshot() { + let title; + let consoleCounts = { total: 0, errors: 0, warnings: 0 }; + if (!this.crashed) { + await this._raceAgainstModalStates(async () => { + title = await this.page.title(); + }); + consoleCounts = await this.consoleMessageCount(); + } + const newHeader = { + title: title ?? "", + url: this.page.url(), + current: this.isCurrentTab(), + crashed: this.crashed, + console: consoleCounts + }; + if (!tabHeaderEquals(this._lastHeader, newHeader)) { + this._lastHeader = newHeader; + return { ...this._lastHeader, changed: true }; + } + return { ...this._lastHeader, changed: false }; + } + isCurrentTab() { + return this === this.context.currentTab(); + } + async waitForLoadState(state, options) { + await this._initializedPromise; + await this.page.waitForLoadState(state, options).catch((e) => debug6("pw:tools:error")(e)); + } + async checkUrlAndNavigate(url2) { + try { + new URL(url2); + } catch (e) { + if (url2.startsWith("localhost")) + url2 = "http://" + url2; + else + url2 = "https://" + url2; + } + this.context.checkUrlAllowed(url2); + await this.navigate(url2); + return url2; + } + async navigate(url2) { + await this._initializedPromise; + this._clearCollectedArtifacts(); + const { promise: downloadEvent, abort: abortDownloadEvent } = eventWaiter(this.page, "download", 3e3); + try { + await this.page.goto(url2, { waitUntil: "domcontentloaded", ...this.navigationTimeoutOptions }); + abortDownloadEvent(); + } catch (_e) { + const e = _e; + const mightBeDownload = e.message.includes("net::ERR_ABORTED") || e.message.includes("Download is starting"); + if (!mightBeDownload) + throw e; + const download = await downloadEvent; + if (!download) + throw e; + await new Promise((resolve) => setTimeout(resolve, 500)); + return; + } + await this.waitForLoadState("load", { timeout: 5e3 }); + } + async consoleMessageCount() { + await this._initializedPromise; + const messages = await this.page.consoleMessages({ filter: "since-navigation" }); + const pageErrors = await this.page.pageErrors({ filter: "since-navigation" }); + let errors = pageErrors.length; + let warnings = 0; + for (const message of messages) { + if (message.type() === "error") + errors++; + else if (message.type() === "warning") + warnings++; + } + return { total: messages.length + pageErrors.length, errors, warnings }; + } + async consoleMessages(level, all) { + await this._initializedPromise; + const result2 = []; + const messages = await this.page.consoleMessages({ filter: all ? "all" : "since-navigation" }); + for (const message of messages) { + const cm = messageToConsoleMessage(message); + if (shouldIncludeMessage(level, cm.type)) + result2.push(cm); + } + if (shouldIncludeMessage(level, "error")) { + const errors = await this.page.pageErrors({ filter: all ? "all" : "since-navigation" }); + for (const error of errors) + result2.push(pageErrorToConsoleMessage(error)); + } + return result2; + } + async clearConsoleMessages() { + await this._initializedPromise; + await Promise.all([ + this.page.clearConsoleMessages(), + this.page.clearPageErrors() + ]); + } + async requests() { + await this._initializedPromise; + return this._requests; + } + async clearRequests() { + await this._initializedPromise; + this._requests.length = 0; + } + async captureSnapshot(root, depth, boxes, relativeTo) { + await this._initializedPromise; + let tabSnapshot; + const modalStates = await this._raceAgainstModalStates(async () => { + const ariaSnapshot = root ? await root.ariaSnapshot({ mode: "ai", depth, boxes }) : await this.page.ariaSnapshot({ mode: "ai", depth, boxes }); + tabSnapshot = { + ariaSnapshot, + modalStates: [], + events: [] + }; + }); + if (tabSnapshot) { + tabSnapshot.consoleLink = await this._consoleLog.take(relativeTo); + tabSnapshot.events = this._recentEventEntries; + this._recentEventEntries = []; + } + return tabSnapshot ?? { + ariaSnapshot: "", + modalStates, + events: [] + }; + } + _javaScriptBlocked() { + return this._modalStates.some((state) => state.type === "dialog"); + } + async _raceAgainstModalStates(action) { + if (this.modalStates().length) + return this.modalStates(); + const promise = new ManualPromise(); + const listener = (modalState) => promise.resolve([modalState]); + this.once(TabEvents.modalState, listener); + return await Promise.race([ + action().then(() => { + this.off(TabEvents.modalState, listener); + return []; + }), + promise + ]); + } + async waitForCompletion(callback) { + await this._initializedPromise; + await this._raceAgainstModalStates(() => waitForCompletion(this, callback)); + } + async targetLocator(params2) { + await this._initializedPromise; + return (await this.targetLocators([params2]))[0]; + } + async targetLocators(params2) { + await this._initializedPromise; + return Promise.all(params2.map(async (param) => { + if (!param.target.match(/^(f\d+)?e\d+$/)) { + const selector = locatorOrSelectorAsSelector("javascript", param.target, this.context.config.testIdAttribute || "data-testid"); + const handle = await this.page.$(selector); + if (!handle) + throw new Error(`"${param.target}" does not match any elements.`); + handle.dispose().catch(() => { + }); + return { locator: this.page.locator(selector), resolved: asLocator("javascript", selector) }; + } else { + try { + let locator2 = this.page.locator(`aria-ref=${param.target}`); + if (param.element) + locator2 = locator2.describe(param.element); + const resolved = await locator2.normalize(); + return { locator: locator2, resolved: resolved.toString() }; + } catch (e) { + throw new Error(`Ref ${param.target} not found in the current page snapshot. Try capturing new snapshot.`); + } + } + })); + } + async waitForTimeout(time) { + if (this._javaScriptBlocked()) { + await new Promise((f) => setTimeout(f, time)); + return; + } + await this.page.evaluate((ms) => new Promise((f) => setTimeout(f, ms)), time).catch(() => { + }); + } + }; + consoleMessageLevels = ["error", "warning", "info", "debug"]; + tabSymbol = Symbol("tabSymbol"); + } +}); + +// packages/playwright-core/src/tools/backend/context.ts +function originOrHostGlob(originOrHost) { + const wildcardPortMatch = originOrHost.match(/^(https?:\/\/[^/:]+):\*$/); + if (wildcardPortMatch) + return `${wildcardPortMatch[1]}:*/**`; + try { + const url2 = new URL(originOrHost); + if (url2.origin !== "null") + return `${url2.origin}/**`; + } catch { + } + return `*://${originOrHost}/**`; +} +async function workspaceFile(options, fileName, perCallWorkspaceDir) { + const workspace = perCallWorkspaceDir ?? options.cwd; + const resolvedName = import_path39.default.resolve(workspace, fileName); + await checkFile(options, resolvedName, { origin: "llm" }); + return resolvedName; +} +function outputDir(options) { + if (options.config.outputDir) + return import_path39.default.resolve(options.config.outputDir); + const baseName = options.config.skillMode ? ".playwright-cli" : ".playwright-mcp"; + if (isSystemDirectory(options.cwd) || !isWritable(options.cwd)) + return import_path39.default.join(import_os19.default.tmpdir(), baseName); + return import_path39.default.join(options.cwd, baseName); +} +async function outputFile(options, fileName, flags) { + const resolvedFile = import_path39.default.resolve(outputDir(options), fileName); + await checkFile(options, resolvedFile, flags); + await import_fs42.default.promises.mkdir(import_path39.default.dirname(resolvedFile), { recursive: true }); + debug7("pw:mcp:file")(resolvedFile); + return resolvedFile; +} +async function checkFile(options, resolvedFilename, flags) { + if (flags.origin === "code" || options.config.allowUnrestrictedFileAccess || options.config.skillMode) + return; + const output = outputDir(options); + const workspace = options.cwd; + if (!isPathInside(output, resolvedFilename) && !isPathInside(workspace, resolvedFilename)) + throw new Error(`File access denied: ${resolvedFilename} is outside allowed roots. Allowed roots: ${output}, ${workspace}`); +} +var import_fs42, import_os19, import_path39, debug7, testDebug, Context; +var init_context = __esm({ + "packages/playwright-core/src/tools/backend/context.ts"() { + "use strict"; + import_fs42 = __toESM(require("fs")); + import_os19 = __toESM(require("os")); + import_path39 = __toESM(require("path")); + init_stringUtils(); + init_disposable(); + init_eventsHelper(); + init_fileUtils(); + init_inprocess(); + init_tab(); + debug7 = require("./utilsBundle").debug; + testDebug = debug7("pw:mcp:test"); + Context = class { + constructor(browserContext, options) { + this._tabs = []; + this._routes = []; + this._disposables = []; + this._pendingUnhandledRejections = []; + this._unhandledRejectionListeners = /* @__PURE__ */ new Set(); + this._onUnhandledRejection = (reason) => { + this._pendingUnhandledRejections.push(reason); + for (const listener of this._unhandledRejectionListeners) + listener(reason); + }; + this.config = options.config; + this.sessionLog = options.sessionLog; + this.options = options; + this._rawBrowserContext = browserContext; + testDebug("create context"); + process.on("unhandledRejection", this._onUnhandledRejection); + } + async dispose() { + process.off("unhandledRejection", this._onUnhandledRejection); + await disposeAll(this._disposables); + for (const tab2 of this._tabs) + await tab2.dispose(); + this._tabs.length = 0; + this._currentTab = void 0; + await this.stopVideoRecording(); + } + drainPendingUnhandledRejections() { + const reasons = this._pendingUnhandledRejections.slice(); + this._pendingUnhandledRejections.length = 0; + return reasons; + } + onUnhandledRejection(listener) { + this._unhandledRejectionListeners.add(listener); + return () => this._unhandledRejectionListeners.delete(listener); + } + debugger() { + return this._rawBrowserContext.debugger; + } + tabs() { + return this._tabs; + } + currentTab() { + return this._currentTab; + } + currentTabOrDie() { + if (!this._currentTab) + throw new Error("No open pages available."); + return this._currentTab; + } + async newTab() { + const browserContext = await this.ensureBrowserContext(); + const page = await browserContext.newPage(); + this._currentTab = this._tabs.find((t) => t.page === page); + return this._currentTab; + } + async selectTab(index) { + const tab2 = this._tabs[index]; + if (!tab2) + throw new Error(`Tab ${index} not found`); + await tab2.page.bringToFront(); + this._currentTab = tab2; + return tab2; + } + async ensureTab() { + await this.ensureBrowserContext(); + const crashed = this._currentTab?.crashed; + if (crashed) { + await this._currentTab.page.close().catch(() => { + }); + this._currentTab = void 0; + } + if (!this._currentTab) + await this.newTab(); + if (crashed) + this._currentTab.logErrorMessage("Page crashed and was reset to about:blank."); + return this._currentTab; + } + async closeTab(index) { + const tab2 = index === void 0 ? this._currentTab : this._tabs[index]; + if (!tab2) + throw new Error(`Tab ${index} not found`); + const url2 = tab2.page.url(); + await tab2.page.close(); + return url2; + } + async workspaceFile(fileName, perCallWorkspaceDir) { + return await workspaceFile(this.options, fileName, perCallWorkspaceDir); + } + async outputFile(template, options) { + const baseName = template.suggestedFilename || `${template.prefix}-${(template.date ?? /* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}${template.ext ? "." + template.ext : ""}`; + return await outputFile(this.options, baseName, options); + } + async startVideoRecording(fileName, params2) { + if (this._video) + throw new Error("Video recording has already been started."); + this._video = { params: params2, fileName, fileNames: [] }; + const browserContext = await this.ensureBrowserContext(); + for (const page of browserContext.pages()) + await this._startPageVideo(page); + } + async stopVideoRecording() { + if (!this._video) + return []; + const video = this._video; + for (const page of this._rawBrowserContext.pages()) + await page.screencast.stop(); + this._video = void 0; + return [...video.fileNames]; + } + async _startPageVideo(page) { + if (!this._video) + return; + const suffix = this._video.fileNames.length ? `-${this._video.fileNames.length}` : ""; + let fileName = this._video.fileName; + if (fileName && suffix) { + const ext = import_path39.default.extname(fileName); + fileName = import_path39.default.basename(fileName, ext) + suffix + ext; + } + this._video.fileNames.push(fileName); + await page.screencast.start({ path: fileName, ...this._video.params }); + } + _onPageCreated(page) { + const tab2 = new Tab(this, page, (tab3) => this._onPageClosed(tab3)); + this._tabs.push(tab2); + if (!this._currentTab) + this._currentTab = tab2; + this._startPageVideo(page).catch(() => { + }); + } + _onPageClosed(tab2) { + const index = this._tabs.indexOf(tab2); + if (index === -1) + return; + this._tabs.splice(index, 1); + if (this._currentTab === tab2) + this._currentTab = this._tabs[Math.min(index, this._tabs.length - 1)]; + } + routes() { + return this._routes; + } + async addRoute(entry) { + const browserContext = await this.ensureBrowserContext(); + await browserContext.route(entry.pattern, entry.handler); + this._routes.push(entry); + } + async removeRoute(pattern) { + let removed = 0; + const browserContext = await this.ensureBrowserContext(); + if (pattern) { + const toRemove = this._routes.filter((r) => r.pattern === pattern); + for (const route2 of toRemove) + await browserContext.unroute(route2.pattern, route2.handler); + this._routes = this._routes.filter((r) => r.pattern !== pattern); + removed = toRemove.length; + } else { + for (const route2 of this._routes) + await browserContext.unroute(route2.pattern, route2.handler); + removed = this._routes.length; + this._routes = []; + } + return removed; + } + isRunningTool() { + return this._runningToolName !== void 0; + } + setRunningTool(name) { + this._runningToolName = name; + } + async _setupRequestInterception(context2) { + if (this.config.network?.allowedOrigins?.length) { + this._disposables.push(await context2.route("**", (route2) => route2.abort("blockedbyclient"))); + for (const origin of this.config.network.allowedOrigins) { + const glob = originOrHostGlob(origin); + this._disposables.push(await context2.route(glob, (route2) => route2.continue())); + } + } + if (this.config.network?.blockedOrigins?.length) { + for (const origin of this.config.network.blockedOrigins) + this._disposables.push(await context2.route(originOrHostGlob(origin), (route2) => route2.abort("blockedbyclient"))); + } + } + async ensureBrowserContext() { + if (this._browserContextPromise) + return this._browserContextPromise; + this._browserContextPromise = this._initializeBrowserContext(); + return this._browserContextPromise; + } + async _initializeBrowserContext() { + if (this.config.testIdAttribute) + playwright.selectors.setTestIdAttribute(this.config.testIdAttribute); + const browserContext = this._rawBrowserContext; + await this._setupRequestInterception(browserContext); + for (const initScript of this.config.browser?.initScript || []) + this._disposables.push(await browserContext.addInitScript({ path: import_path39.default.resolve(this.options.cwd, initScript) })); + for (const page of browserContext.pages()) + this._onPageCreated(page); + this._disposables.push(eventsHelper.addEventListener(browserContext, "page", (page) => this._onPageCreated(page))); + return browserContext; + } + checkUrlAllowed(url2) { + if (this.config.allowUnrestrictedFileAccess) + return; + if (!URL.canParse(url2)) + return; + if (new URL(url2).protocol === "file:") + throw new Error(`Access to "file:" protocol is blocked. Attempted URL: "${url2}"`); + } + lookupSecret(secretName) { + if (!this.config.secrets?.[secretName]) + return { value: secretName, code: escapeWithQuotes(secretName, "'") }; + return { + value: this.config.secrets[secretName], + code: `process.env['${secretName}']` + }; + } + }; + } +}); + +// packages/playwright-core/src/tools/backend/screenshot.ts +function scaleImageToFitMessage(buffer, imageType) { + const image = imageType === "png" ? PNG4.sync.read(buffer) : jpegjs5.decode(buffer, { maxMemoryUsageInMB: 512 }); + const pixels = image.width * image.height; + const shrink = Math.min(1568 / image.width, 1568 / image.height, Math.sqrt(1.15 * 1024 * 1024 / pixels)); + if (shrink > 1) + return buffer; + const width = image.width * shrink | 0; + const height = image.height * shrink | 0; + const scaledImage = scaleImageToSize(image, { width, height }); + return imageType === "png" ? PNG4.sync.write(scaledImage) : jpegjs5.encode(scaledImage, 80).data; +} +var jpegjs5, PNG4, z4, screenshotSchema, screenshot, screenshot_default; +var init_screenshot = __esm({ + "packages/playwright-core/src/tools/backend/screenshot.ts"() { + "use strict"; + init_stringUtils(); + init_imageUtils(); + init_tool(); + init_snapshot(); + jpegjs5 = require("./utilsBundle").jpegjs; + ({ PNG: PNG4 } = require("./utilsBundle")); + z4 = require("./utilsBundle").z; + screenshotSchema = optionalElementSchema.extend({ + type: z4.enum(["png", "jpeg"]).default("png").describe("Image format for the screenshot. Default is png."), + filename: z4.string().optional().describe("File name to save the screenshot to. Defaults to `page-{timestamp}.{png|jpeg}` if not specified. Prefer relative file names to stay within the output directory."), + fullPage: z4.boolean().optional().describe("When true, takes a screenshot of the full scrollable page, instead of the currently visible viewport. Cannot be used with element screenshots.") + }); + screenshot = defineTabTool({ + capability: "core", + schema: { + name: "browser_take_screenshot", + title: "Take a screenshot", + description: `Take a screenshot of the current page. You can't perform actions based on the screenshot, use browser_snapshot for actions.`, + inputSchema: screenshotSchema, + type: "readOnly" + }, + handle: async (tab2, params2, response2) => { + if (params2.fullPage && params2.target) + throw new Error("fullPage cannot be used with element screenshots."); + const fileType = params2.type || "png"; + const options = { + type: fileType, + quality: fileType === "png" ? void 0 : 90, + scale: "css", + ...tab2.actionTimeoutOptions, + ...params2.fullPage !== void 0 && { fullPage: params2.fullPage } + }; + const screenshotTargetLabel = params2.target ? params2.element || "element" : params2.fullPage ? "full page" : "viewport"; + const target = params2.target ? await tab2.targetLocator({ element: params2.element, target: params2.target }) : null; + const data = target ? await target.locator.screenshot(options) : await tab2.page.screenshot(options); + const resolvedFile = await response2.resolveClientFile({ prefix: target ? "element" : "page", ext: fileType, suggestedFilename: params2.filename }, `Screenshot of ${screenshotTargetLabel}`); + response2.addCode(`// Screenshot ${screenshotTargetLabel} and save it as ${resolvedFile.relativeName}`); + if (target) + response2.addCode(`await page.${target.resolved}.screenshot(${formatObject({ ...options, path: resolvedFile.relativeName })});`); + else + response2.addCode(`await page.screenshot(${formatObject({ ...options, path: resolvedFile.relativeName })});`); + await response2.addFileResult(resolvedFile, data); + if (!params2.filename) + await response2.registerImageResult(data, fileType); + } + }); + screenshot_default = [ + screenshot + ]; + } +}); + +// packages/playwright-core/src/tools/backend/response.ts +function renderTabMarkdown(tab2) { + const lines = [`- Page URL: ${tab2.url}`]; + if (tab2.title) + lines.push(`- Page Title: ${tab2.title}`); + if (tab2.crashed) + lines.push(`- Page status: crashed`); + if (tab2.console.errors || tab2.console.warnings) + lines.push(`- Console: ${tab2.console.errors} errors, ${tab2.console.warnings} warnings`); + return lines; +} +function renderTabsMarkdown(tabs) { + if (!tabs.length) + return ["No open tabs. Navigate to a URL to create one."]; + const lines = []; + for (let i = 0; i < tabs.length; i++) { + const tab2 = tabs[i]; + const current = tab2.current ? " (current)" : ""; + const crashed = tab2.crashed ? " [crashed]" : ""; + lines.push(`- ${i}:${current} [${tab2.title}](${tab2.url})${crashed}`); + } + return lines; +} +function sanitizeUnicode(text2) { + return text2.toWellFormed?.() ?? text2; +} +async function listFilesRecursive(dir) { + const entries = await import_fs43.default.promises.readdir(dir, { recursive: true, withFileTypes: true }); + const files = entries.filter((e) => e.isFile()); + return Promise.all(files.map(async (e) => { + const full = import_path40.default.join(e.parentPath, e.name); + const { size, mtimeMs } = await import_fs43.default.promises.stat(full); + return { path: full, size, mtimeMs }; + })); +} +function parseSections(text2) { + const sections = /* @__PURE__ */ new Map(); + const sectionHeaders = text2.split(/^### /m).slice(1); + for (const section of sectionHeaders) { + const firstNewlineIndex = section.indexOf("\n"); + if (firstNewlineIndex === -1) + continue; + const sectionName = section.substring(0, firstNewlineIndex); + const sectionContent = section.substring(firstNewlineIndex + 1).trim(); + sections.set(sectionName, sectionContent); + } + return sections; +} +function parseResponse(response2, cwd) { + if (response2.content?.[0].type !== "text") + return void 0; + const text2 = response2.content[0].text; + const sections = parseSections(text2); + const error = sections.get("Error"); + const result2 = sections.get("Result"); + const code = sections.get("Ran Playwright code"); + const tabs = sections.get("Open tabs"); + const page = sections.get("Page"); + const snapshotSection = sections.get("Snapshot"); + const events = sections.get("Events"); + const modalState = sections.get("Modal state"); + const paused = sections.get("Paused"); + const codeNoFrame = code?.replace(/^```js\n/, "").replace(/\n```$/, ""); + const isError4 = response2.isError; + const attachments = response2.content.length > 1 ? response2.content.slice(1) : void 0; + let snapshot3; + let inlineSnapshot; + if (snapshotSection) { + const match = snapshotSection.match(/\[Snapshot\]\(([^)]+)\)/); + if (match) { + if (cwd) { + try { + snapshot3 = import_fs43.default.readFileSync(import_path40.default.resolve(cwd, match[1]), "utf-8"); + } catch { + } + } + } else { + inlineSnapshot = snapshotSection.replace(/^```yaml\n?/, "").replace(/\n?```$/, ""); + } + } + return { + result: result2, + error, + code: codeNoFrame, + tabs, + page, + snapshot: snapshot3, + inlineSnapshot, + events, + modalState, + paused, + isError: isError4, + attachments, + text: text2 + }; +} +var import_fs43, import_path40, debug8, requestDebug, Response4; +var init_response = __esm({ + "packages/playwright-core/src/tools/backend/response.ts"() { + "use strict"; + import_fs43 = __toESM(require("fs")); + import_path40 = __toESM(require("path")); + init_tab(); + init_screenshot(); + init_context(); + debug8 = require("./utilsBundle").debug; + requestDebug = debug8("pw:mcp:request"); + Response4 = class { + constructor(context2, toolName, toolArgs, options) { + this._results = []; + this._errors = []; + this._code = []; + this._includeSnapshot = "none"; + this._isClose = false; + this._imageResults = []; + this._writtenFiles = /* @__PURE__ */ new Set(); + this._context = context2; + this.toolName = toolName; + this.toolArgs = toolArgs; + this._clientWorkspace = options?.relativeTo ?? context2.options.cwd; + this._json = options?.json ?? false; + this._raw = this._json || (options?.raw ?? false); + } + _computeRelativeTo(fileName) { + const rel = import_path40.default.relative(this._clientWorkspace, fileName); + if (import_path40.default.dirname(rel) === "." && !rel.startsWith(".")) + return "./" + rel; + return rel; + } + async resolveClientFile(template, title) { + let fileName; + if (template.suggestedFilename) + fileName = await this.resolveClientFilename(template.suggestedFilename); + else + fileName = await this._context.outputFile(template, { origin: "llm" }); + const relativeName = this._computeRelativeTo(fileName); + const printableLink = `- [${title}](${relativeName})`; + return { fileName, relativeName, printableLink }; + } + async resolveClientFilename(filename) { + return await this._context.workspaceFile(filename, this._clientWorkspace); + } + addTextResult(text2) { + this._results.push(text2); + } + async addResult(title, data, file) { + if (file.suggestedFilename || typeof data !== "string") { + const resolvedFile = await this.resolveClientFile(file, title); + await this.addFileResult(resolvedFile, data); + } else { + this.addTextResult(data); + } + } + async _writeFile(resolvedFile, data) { + if (typeof data === "string") + await import_fs43.default.promises.writeFile(resolvedFile.fileName, this._redactSecrets(data), "utf-8"); + else if (data) + await import_fs43.default.promises.writeFile(resolvedFile.fileName, data); + this._writtenFiles.add(import_path40.default.resolve(resolvedFile.fileName)); + } + async addFileResult(resolvedFile, data) { + await this._writeFile(resolvedFile, data); + this.addTextResult(resolvedFile.printableLink); + } + addFileLink(title, fileName) { + const relativeName = this._computeRelativeTo(fileName); + this.addTextResult(`- [${title}](${relativeName})`); + } + async registerImageResult(data, imageType) { + this._imageResults.push({ data, imageType }); + } + setClose() { + this._isClose = true; + } + addError(error) { + this._errors.push(error); + } + addCode(code) { + this._code.push(code); + } + setIncludeSnapshot() { + this._includeSnapshot = this._context.config.snapshot?.mode ?? "full"; + } + setIncludeFullSnapshot(includeSnapshotFileName, root, depth, boxes) { + this._includeSnapshot = "explicit"; + this._includeSnapshotFileName = includeSnapshotFileName; + this._includeSnapshotDepth = depth; + this._includeSnapshotBoxes = boxes; + this._includeSnapshotRoot = root; + } + _redactSecrets(text2) { + for (const [secretName, secretValue] of Object.entries(this._context.config.secrets ?? {})) { + if (!secretValue) + continue; + text2 = text2.replaceAll(secretValue, `${secretName}`); + } + return text2; + } + async serialize() { + const allSections = await this._build(); + await this._enforceOutputBudget(); + const rawSections = ["Error", "Result", "Snapshot"]; + const sections = this._raw ? allSections.filter((section) => rawSections.includes(section.title)) : allSections; + let serializedText; + if (this._json) { + const payload = {}; + const isError4 = sections.some((section) => section.isError); + if (isError4) + payload.isError = true; + for (const section of sections) { + if (!section.content.length) + continue; + const key = section.title.toLowerCase(); + if (key === "snapshot") { + const match = section.content[0]?.match(/^- \[Snapshot\]\(([^)]+)\)$/); + payload.snapshot = match ? { file: match[1] } : section.content.join("\n"); + } else { + payload[key] = section.content.join("\n"); + } + } + serializedText = JSON.stringify(payload, null, 2); + } else { + const text2 = []; + for (const section of sections) { + if (!section.content.length) + continue; + if (!this._raw) { + text2.push(`### ${section.title}`); + if (section.codeframe) + text2.push(`\`\`\`${section.codeframe}`); + text2.push(...section.content); + if (section.codeframe) + text2.push("```"); + } else { + text2.push(...section.content); + } + } + serializedText = text2.join("\n"); + } + const content = [ + { + type: "text", + text: sanitizeUnicode(this._redactSecrets(serializedText)) + } + ]; + if (this._context.config.imageResponses !== "omit") { + for (const imageResult of this._imageResults) { + const scaledData = scaleImageToFitMessage(imageResult.data, imageResult.imageType); + content.push({ type: "image", data: scaledData.toString("base64"), mimeType: imageResult.imageType === "png" ? "image/png" : "image/jpeg" }); + } + } + return { + content, + ...this._isClose ? { isClose: true } : {}, + ...sections.some((section) => section.isError) ? { isError: true } : {} + }; + } + async _enforceOutputBudget() { + const maxSize = this._context.config.outputMaxSize; + if (!maxSize) + return; + const dir = outputDir(this._context.options); + let entries; + try { + entries = await listFilesRecursive(dir); + } catch { + return; + } + let total = 0; + for (const e of entries) + total += e.size; + if (total <= maxSize) + return; + entries.sort((a, b) => a.mtimeMs - b.mtimeMs); + for (const entry of entries) { + if (total <= maxSize) + break; + if (this._writtenFiles.has(entry.path)) + continue; + try { + await import_fs43.default.promises.unlink(entry.path); + total -= entry.size; + } catch (error) { + requestDebug("output-budget unlink failed %s: %s", entry.path, error); + } + } + } + async _build() { + const sections = []; + const addSection = (title, content, codeframe) => { + const section = { title, content, isError: title === "Error", codeframe }; + sections.push(section); + return content; + }; + if (this._errors.length) + addSection("Error", this._errors); + if (this._results.length) + addSection("Result", this._results); + if (this._context.config.codegen !== "none" && this._code.length) + addSection("Ran Playwright code", this._code, "js"); + const tabSnapshot = this._context.currentTab() ? await this._context.currentTabOrDie().captureSnapshot(this._includeSnapshotRoot, this._includeSnapshotDepth, this._includeSnapshotBoxes, this._clientWorkspace) : void 0; + const tabHeaders = await Promise.all(this._context.tabs().map((tab2) => tab2.headerSnapshot())); + if (this._includeSnapshot !== "none" || tabHeaders.some((header) => header.changed)) { + if (tabHeaders.length !== 1) + addSection("Open tabs", renderTabsMarkdown(tabHeaders)); + addSection("Page", renderTabMarkdown(tabHeaders.find((h) => h.current) ?? tabHeaders[0])); + } + if (tabSnapshot?.modalStates.length) + addSection("Modal state", renderModalStates(this._context.config, tabSnapshot.modalStates)); + if (tabSnapshot && this._includeSnapshot !== "none") { + if (this._includeSnapshot !== "explicit" || this._includeSnapshotFileName) { + const suggestedFilename = this._includeSnapshotFileName === "" ? void 0 : this._includeSnapshotFileName; + const resolvedFile = await this.resolveClientFile({ prefix: "page", ext: "yml", suggestedFilename }, "Snapshot"); + await this._writeFile(resolvedFile, tabSnapshot.ariaSnapshot); + addSection("Snapshot", [resolvedFile.printableLink]); + } else { + addSection("Snapshot", [tabSnapshot.ariaSnapshot], "yaml"); + } + } + const text2 = []; + if (tabSnapshot?.consoleLink) + text2.push(`- New console entries: ${tabSnapshot.consoleLink}`); + if (tabSnapshot?.events.filter((event) => event.type !== "request").length) { + for (const event of tabSnapshot.events) { + if (event.type === "download-start") + text2.push(`- Downloading file ${event.download.download.suggestedFilename()} ...`); + else if (event.type === "download-finish") + text2.push(`- Downloaded file ${event.download.download.suggestedFilename()} to "${this._computeRelativeTo(event.download.outputFile)}"`); + } + } + if (text2.length) + addSection("Events", text2); + const pausedDetails = this._context.debugger().pausedDetails(); + if (pausedDetails) { + addSection("Paused", [ + `- ${pausedDetails.title} at ${this._computeRelativeTo(pausedDetails.location.file)}${pausedDetails.location.line ? ":" + pausedDetails.location.line : ""}`, + "- Use any tools to explore and interact, resume by calling resume/step-over/pause-at" + ]); + } + return sections; + } + }; + } +}); + +// packages/playwright-core/src/tools/backend/sessionLog.ts +var import_fs44, import_path41, debug9, SessionLog; +var init_sessionLog = __esm({ + "packages/playwright-core/src/tools/backend/sessionLog.ts"() { + "use strict"; + import_fs44 = __toESM(require("fs")); + import_path41 = __toESM(require("path")); + init_context(); + init_response(); + debug9 = require("./utilsBundle").debug; + SessionLog = class _SessionLog { + constructor(sessionFolder, cwd) { + this._sessionFileQueue = Promise.resolve(); + this._folder = sessionFolder; + this._file = import_path41.default.join(this._folder, "session.md"); + this._cwd = cwd; + } + static async create(config, cwd) { + const sessionFolder = await outputFile({ config, cwd }, `session-${Date.now()}`, { origin: "code" }); + await import_fs44.default.promises.mkdir(sessionFolder, { recursive: true }); + console.error(`Session: ${sessionFolder}`); + return new _SessionLog(sessionFolder, cwd); + } + logResponse(toolName, toolArgs, responseObject) { + const parsed = { ...parseResponse(responseObject, this._cwd), text: void 0 }; + const lines = [""]; + lines.push( + `### Tool call: ${toolName}`, + `- Args`, + "```json", + JSON.stringify(toolArgs, null, 2), + "```" + ); + if (parsed) { + lines.push(`- Result`); + lines.push("```json"); + lines.push(JSON.stringify(parsed, null, 2)); + lines.push("```"); + } + lines.push(""); + this._sessionFileQueue = this._sessionFileQueue.then(() => import_fs44.default.promises.appendFile(this._file, lines.join("\n"))).catch((e) => debug9("pw:tools:error")(e)); + } + }; + } +}); + +// packages/playwright-core/src/tools/backend/browserBackend.ts +function formatRejectionReason(reason) { + if (reason instanceof Error) + return reason.stack ?? reason.message; + return String(reason); +} +var z5, debug10, BrowserBackend; +var init_browserBackend = __esm({ + "packages/playwright-core/src/tools/backend/browserBackend.ts"() { + "use strict"; + init_context(); + init_response(); + init_sessionLog(); + z5 = require("./utilsBundle").z; + debug10 = require("./utilsBundle").debug; + BrowserBackend = class { + constructor(config, browserContext, tools) { + this._disconnected = false; + this._config = config; + this._tools = tools; + this.browserContext = browserContext; + const markDisconnected = () => { + this._disconnected = true; + }; + this.browserContext.once("close", markDisconnected); + this.browserContext.browser()?.once("disconnected", markDisconnected); + } + async initialize(clientInfo) { + this._sessionLog = this._config.saveSession ? await SessionLog.create(this._config, clientInfo.cwd) : void 0; + this._context = new Context(this.browserContext, { + config: this._config, + sessionLog: this._sessionLog, + cwd: clientInfo.cwd + }); + } + async dispose() { + await this._context?.dispose().catch((e) => debug10("pw:tools:error")(e)); + } + async callTool(name, rawArguments = {}, signal) { + const json = !!rawArguments._meta?.json; + const formatError = (message) => ({ + content: [{ type: "text", text: json ? JSON.stringify({ isError: true, error: message }, null, 2) : `### Error +${message}` }], + isError: true + }); + const tool = this._tools.find((tool2) => tool2.schema.name === name); + if (!tool) + return formatError(`Tool "${name}" not found`); + let parsedArguments; + try { + parsedArguments = tool.schema.inputSchema.parse(rawArguments); + } catch (error) { + if (error instanceof z5.ZodError) + return formatError(`Invalid arguments for tool "${name}": +${z5.prettifyError(error)}`); + throw error; + } + const cwd = rawArguments._meta?.cwd; + const raw = !!rawArguments._meta?.raw; + const context2 = this._context; + const response2 = new Response4(context2, name, parsedArguments, { relativeTo: cwd, raw, json }); + context2.setRunningTool(name); + let responseObject; + try { + await tool.handle(context2, parsedArguments, response2, signal); + for (const reason of context2.drainPendingUnhandledRejections()) + response2.addError(formatRejectionReason(reason)); + responseObject = await response2.serialize(); + this._sessionLog?.logResponse(name, parsedArguments, responseObject); + } catch (error) { + const messages = [String(error), ...context2.drainPendingUnhandledRejections().map(formatRejectionReason)]; + responseObject = formatError(messages.join("\n\n")); + } finally { + context2.setRunningTool(void 0); + } + if (this._disconnected) + responseObject.isClose = true; + return responseObject; + } + }; + } +}); + +// packages/playwright-core/src/tools/backend/common.ts +var z6, close, resize, common_default; +var init_common = __esm({ + "packages/playwright-core/src/tools/backend/common.ts"() { + "use strict"; + init_tool(); + init_response(); + z6 = require("./utilsBundle").z; + close = defineTool({ + capability: "core", + schema: { + name: "browser_close", + title: "Close browser", + description: "Close the page", + inputSchema: z6.object({}), + type: "action" + }, + handle: async (context2, params2, response2) => { + const result2 = renderTabsMarkdown([]); + response2.addTextResult(result2.join("\n")); + response2.addCode(`await page.close()`); + response2.setClose(); + } + }); + resize = defineTabTool({ + capability: "core", + schema: { + name: "browser_resize", + title: "Resize browser window", + description: "Resize the browser window", + inputSchema: z6.object({ + width: z6.number().describe("Width of the browser window"), + height: z6.number().describe("Height of the browser window") + }), + type: "action" + }, + handle: async (tab2, params2, response2) => { + response2.addCode(`await page.setViewportSize({ width: ${params2.width}, height: ${params2.height} });`); + await tab2.page.setViewportSize({ width: params2.width, height: params2.height }); + } + }); + common_default = [ + close, + resize + ]; + } +}); + +// packages/playwright-core/src/tools/backend/config.ts +var z7, configShow, config_default; +var init_config = __esm({ + "packages/playwright-core/src/tools/backend/config.ts"() { + "use strict"; + init_tool(); + z7 = require("./utilsBundle").z; + configShow = defineTool({ + capability: "config", + schema: { + name: "browser_get_config", + title: "Get config", + description: "Get the final resolved config after merging CLI options, environment variables and config file.", + inputSchema: z7.object({}), + type: "readOnly" + }, + handle: async (context2, params2, response2) => { + response2.addTextResult(JSON.stringify(context2.config, null, 2)); + } + }); + config_default = [ + configShow + ]; + } +}); + +// packages/playwright-core/src/tools/backend/console.ts +var z8, console2, consoleClear, console_default; +var init_console2 = __esm({ + "packages/playwright-core/src/tools/backend/console.ts"() { + "use strict"; + init_tool(); + z8 = require("./utilsBundle").z; + console2 = defineTabTool({ + capability: "core", + schema: { + name: "browser_console_messages", + title: "Get console messages", + description: "Returns all console messages", + inputSchema: z8.object({ + level: z8.enum(["error", "warning", "info", "debug"]).default("info").describe('Level of the console messages to return. Each level includes the messages of more severe levels. Defaults to "info".'), + all: z8.boolean().optional().describe("Return all console messages since the beginning of the session, not just since the last navigation. Defaults to false."), + filename: z8.string().optional().describe("Filename to save the console messages to. If not provided, messages are returned as text.") + }), + type: "readOnly" + }, + handle: async (tab2, params2, response2) => { + const count = await tab2.consoleMessageCount(); + const header = [`Total messages: ${count.total} (Errors: ${count.errors}, Warnings: ${count.warnings})`]; + const messages = await tab2.consoleMessages(params2.level, params2.all); + if (messages.length !== count.total) + header.push(`Returning ${messages.length} messages for level "${params2.level}"`); + const text2 = [...header, "", ...messages.map((message) => message.toString())].join("\n"); + await response2.addResult("Console", text2, { prefix: "console", ext: "log", suggestedFilename: params2.filename }); + } + }); + consoleClear = defineTabTool({ + capability: "core", + skillOnly: true, + schema: { + name: "browser_console_clear", + title: "Clear console messages", + description: "Clear all console messages", + inputSchema: z8.object({}), + type: "readOnly" + }, + handle: async (tab2) => { + await tab2.clearConsoleMessages(); + } + }); + console_default = [ + console2, + consoleClear + ]; + } +}); + +// packages/playwright-core/src/tools/backend/cookies.ts +var z9, cookieList, cookieGet, cookieSet, cookieDelete, cookieClear, cookies_default; +var init_cookies = __esm({ + "packages/playwright-core/src/tools/backend/cookies.ts"() { + "use strict"; + init_tool(); + z9 = require("./utilsBundle").z; + cookieList = defineTool({ + capability: "storage", + schema: { + name: "browser_cookie_list", + title: "List cookies", + description: "List all cookies (optionally filtered by domain/path)", + inputSchema: z9.object({ + domain: z9.string().optional().describe("Filter cookies by domain"), + path: z9.string().optional().describe("Filter cookies by path") + }), + type: "readOnly" + }, + handle: async (context2, params2, response2) => { + const browserContext = await context2.ensureBrowserContext(); + let cookies = await browserContext.cookies(); + if (params2.domain) + cookies = cookies.filter((c) => c.domain.includes(params2.domain)); + if (params2.path) + cookies = cookies.filter((c) => c.path.startsWith(params2.path)); + if (cookies.length === 0) + response2.addTextResult("No cookies found"); + else + response2.addTextResult(cookies.map((c) => `${c.name}=${c.value} (domain: ${c.domain}, path: ${c.path})`).join("\n")); + response2.addCode(`await page.context().cookies();`); + } + }); + cookieGet = defineTool({ + capability: "storage", + schema: { + name: "browser_cookie_get", + title: "Get cookie", + description: "Get a specific cookie by name", + inputSchema: z9.object({ + name: z9.string().describe("Cookie name to get") + }), + type: "readOnly" + }, + handle: async (context2, params2, response2) => { + const browserContext = await context2.ensureBrowserContext(); + const cookies = await browserContext.cookies(); + const cookie = cookies.find((c) => c.name === params2.name); + if (!cookie) + response2.addTextResult(`Cookie '${params2.name}' not found`); + else + response2.addTextResult(`${cookie.name}=${cookie.value} (domain: ${cookie.domain}, path: ${cookie.path}, httpOnly: ${cookie.httpOnly}, secure: ${cookie.secure}, sameSite: ${cookie.sameSite})`); + response2.addCode(`await page.context().cookies();`); + } + }); + cookieSet = defineTool({ + capability: "storage", + schema: { + name: "browser_cookie_set", + title: "Set cookie", + description: "Set a cookie with optional flags (domain, path, expires, httpOnly, secure, sameSite)", + inputSchema: z9.object({ + name: z9.string().describe("Cookie name"), + value: z9.string().describe("Cookie value"), + domain: z9.string().optional().describe("Cookie domain"), + path: z9.string().optional().describe("Cookie path"), + expires: z9.number().optional().describe("Cookie expiration as Unix timestamp"), + httpOnly: z9.boolean().optional().describe("Whether the cookie is HTTP only"), + secure: z9.boolean().optional().describe("Whether the cookie is secure"), + sameSite: z9.enum(["Strict", "Lax", "None"]).optional().describe("Cookie SameSite attribute") + }), + type: "action" + }, + handle: async (context2, params2, response2) => { + const browserContext = await context2.ensureBrowserContext(); + const tab2 = await context2.ensureTab(); + const url2 = new URL(tab2.page.url()); + const cookie = { + name: params2.name, + value: params2.value, + domain: params2.domain || url2.hostname, + path: params2.path || "/" + }; + if (params2.expires !== void 0) + cookie.expires = params2.expires; + if (params2.httpOnly !== void 0) + cookie.httpOnly = params2.httpOnly; + if (params2.secure !== void 0) + cookie.secure = params2.secure; + if (params2.sameSite !== void 0) + cookie.sameSite = params2.sameSite; + await browserContext.addCookies([cookie]); + response2.addCode(`await page.context().addCookies([${JSON.stringify(cookie)}]);`); + } + }); + cookieDelete = defineTool({ + capability: "storage", + schema: { + name: "browser_cookie_delete", + title: "Delete cookie", + description: "Delete a specific cookie", + inputSchema: z9.object({ + name: z9.string().describe("Cookie name to delete") + }), + type: "action" + }, + handle: async (context2, params2, response2) => { + const browserContext = await context2.ensureBrowserContext(); + await browserContext.clearCookies({ name: params2.name }); + response2.addCode(`await page.context().clearCookies({ name: '${params2.name}' });`); + } + }); + cookieClear = defineTool({ + capability: "storage", + schema: { + name: "browser_cookie_clear", + title: "Clear cookies", + description: "Clear all cookies", + inputSchema: z9.object({}), + type: "action" + }, + handle: async (context2, params2, response2) => { + const browserContext = await context2.ensureBrowserContext(); + await browserContext.clearCookies(); + response2.addCode(`await page.context().clearCookies();`); + } + }); + cookies_default = [ + cookieList, + cookieGet, + cookieSet, + cookieDelete, + cookieClear + ]; + } +}); + +// packages/playwright-core/src/tools/backend/devtools.ts +var import_child_process3, z10, resume, highlight, hideHighlight, annotate, devtools_default; +var init_devtools = __esm({ + "packages/playwright-core/src/tools/backend/devtools.ts"() { + "use strict"; + import_child_process3 = require("child_process"); + init_package(); + init_tool(); + init_snapshot(); + z10 = require("./utilsBundle").z; + resume = defineTool({ + capability: "devtools", + schema: { + name: "browser_resume", + title: "Resume paused script execution", + description: "Resume script execution after it was paused. When called with step set to true, execution will pause again before the next action.", + inputSchema: z10.object({ + step: z10.boolean().optional().describe("When true, execution will pause again before the next action, allowing step-by-step debugging."), + location: z10.string().optional().describe('Pause execution at a specific :, e.g. "example.spec.ts:42".') + }), + type: "action" + }, + handle: async (context2, params2, response2) => { + const browserContext = await context2.ensureBrowserContext(); + const pausedPromise = new Promise((resolve) => { + const listener = () => { + if (browserContext.debugger.pausedDetails()) { + browserContext.debugger.off("pausedstatechanged", listener); + resolve(); + } + }; + browserContext.debugger.on("pausedstatechanged", listener); + }); + if (params2.location) { + const [file, lineStr] = params2.location.split(":"); + let location2; + if (lineStr) { + const line = Number(lineStr); + if (isNaN(line)) + throw new Error(`Invalid location "${params2.location}", expected format is :, e.g. "example.spec.ts:42"`); + location2 = { file, line }; + } else { + location2 = { file: params2.location }; + } + await browserContext.debugger.runTo(location2); + } else if (params2.step) { + await browserContext.debugger.next(); + } else { + await browserContext.debugger.resume(); + } + await pausedPromise; + } + }); + highlight = defineTabTool({ + capability: "devtools", + schema: { + name: "browser_highlight", + title: "Highlight element", + description: "Show a persistent highlight overlay around the element on the page.", + inputSchema: elementSchema.extend({ + style: z10.string().optional().describe('Additional inline CSS applied to the highlight overlay, e.g. "outline: 2px dashed red".') + }), + type: "readOnly" + }, + handle: async (tab2, params2, response2) => { + const { locator: locator2 } = await tab2.targetLocator(params2); + await locator2.highlight({ style: params2.style }); + response2.addTextResult(`Highlighted ${locator2}`); + } + }); + hideHighlight = defineTabTool({ + capability: "devtools", + schema: { + name: "browser_hide_highlight", + title: "Hide element highlight", + description: "Remove a highlight overlay previously added for the element.", + inputSchema: optionalElementSchema.extend({ + element: z10.string().optional().describe("Human-readable element description used when adding the highlight; must match the value passed to browser_highlight.") + }), + type: "readOnly" + }, + handle: async (tab2, params2, response2) => { + if (params2.target) { + const { locator: locator2 } = await tab2.targetLocator({ target: params2.target, element: params2.element }); + await locator2.hideHighlight(); + response2.addTextResult(`Hid highlight for ${locator2}`); + } else { + await tab2.page.hideHighlight(); + response2.addTextResult(`Hid page highlight`); + } + } + }); + annotate = defineTabTool({ + capability: "devtools", + schema: { + name: "browser_annotate", + title: "Annotate the current page", + description: "Open the Playwright Dashboard in annotation mode for the current page and wait for the user to draw annotations. Returns the annotated screenshot, ARIA snapshot, and the list of annotations.", + inputSchema: z10.object({}), + type: "readOnly" + }, + handle: async (tab2, params2, response2, signal) => { + const pageId4 = tab2.page._guid; + const daemonScript = libPath("entry", "dashboardApp.js"); + const daemonArgs = [daemonScript, `--pageId=${pageId4}`]; + const daemon = (0, import_child_process3.spawn)(process.execPath, daemonArgs, { detached: true, stdio: "ignore" }); + daemon.unref(); + const client = (0, import_child_process3.spawn)(process.execPath, [...daemonArgs, "--annotate"], { + stdio: ["pipe", "pipe", "inherit"] + }); + const onAbort = () => client.kill(); + signal?.addEventListener("abort", onAbort); + const stdoutChunks = []; + client.stdout.on("data", (chunk) => stdoutChunks.push(chunk)); + const exitCode = await new Promise((resolve) => client.on("exit", (code) => resolve(code))); + signal?.removeEventListener("abort", onAbort); + if (signal?.aborted) { + response2.addTextResult("Annotation cancelled."); + return; + } + if (exitCode !== 0) { + response2.addError(`Annotation client exited with code ${exitCode}`); + return; + } + const text2 = Buffer.concat(stdoutChunks).toString("utf8").trim(); + if (!text2) { + response2.addTextResult("No annotations were submitted."); + return; + } + const result2 = JSON.parse(text2); + if (result2.type !== "submitted" || result2.frames.length === 0) { + response2.addTextResult("No annotations were submitted."); + return; + } + const { frames, feedback } = result2; + const date = /* @__PURE__ */ new Date(); + if (feedback) + response2.addTextResult(feedback); + const multi = frames.length > 1; + for (let i = 0; i < frames.length; i++) { + const frame = frames[i]; + const idx = i + 1; + const session2 = frame.sessionTitle || "session"; + const tab3 = frame.title || "tab"; + response2.addTextResult(`${multi ? `## Screenshot ${idx} +` : ""}${session2} / ${tab3} @ ${frame.url} (${frame.viewportWidth}x${frame.viewportHeight})`); + for (const a of frame.annotations) + response2.addTextResult(` { x: ${a.x}, y: ${a.y}, width: ${a.width}, height: ${a.height} }: ${a.text}`); + if (frame.data) + await response2.addResult(`Annotation image${multi ? " " + idx : ""}`, Buffer.from(frame.data, "base64"), { prefix: `annotations${multi ? "-" + idx : ""}`, ext: "png", date }); + if (frame.ariaSnapshot) + await response2.addResult(`Annotation snapshot${multi ? " " + idx : ""}`, Buffer.from(frame.ariaSnapshot, "utf8"), { prefix: `annotations${multi ? "-" + idx : ""}`, ext: "yaml", date }); + } + } + }); + devtools_default = [resume, highlight, hideHighlight, annotate]; + } +}); + +// packages/playwright-core/src/tools/backend/evaluate.ts +var z11, evaluateSchema, evaluate2, evaluate_default; +var init_evaluate = __esm({ + "packages/playwright-core/src/tools/backend/evaluate.ts"() { + "use strict"; + init_stringUtils(); + init_tool(); + init_snapshot(); + z11 = require("./utilsBundle").z; + evaluateSchema = optionalElementSchema.extend({ + function: z11.string().describe("() => { /* code */ } or (element) => { /* code */ } when element is provided"), + filename: z11.string().optional().describe("Filename to save the result to. If not provided, result is returned as text.") + }); + evaluate2 = defineTabTool({ + capability: "core", + schema: { + name: "browser_evaluate", + title: "Evaluate JavaScript", + description: "Evaluate JavaScript expression on page or element", + inputSchema: evaluateSchema, + type: "action" + }, + handle: async (tab, params, response) => { + let locator; + const expression = params.function; + if (params.target) + locator = await tab.targetLocator({ target: params.target, element: params.element || "element" }); + await tab.waitForCompletion(async () => { + let evalResult; + if (locator?.locator) { + evalResult = await locator.locator.evaluate(async (element, expr) => { + const value = eval(`(${expr})`); + const isFunction = typeof value === "function"; + const result = await (isFunction ? value(element) : value); + return { result, isFunction }; + }, expression); + } else { + evalResult = await tab.page.evaluate(async (expr) => { + const value = eval(`(${expr})`); + const isFunction = typeof value === "function"; + const result = await (isFunction ? value() : value); + return { result, isFunction }; + }, expression); + } + const codeExpression = evalResult.isFunction ? expression : `() => (${expression})`; + if (locator) + response.addCode(`await page.${locator.resolved}.evaluate(${escapeWithQuotes(codeExpression)});`); + else + response.addCode(`await page.evaluate(${escapeWithQuotes(codeExpression)});`); + const text = JSON.stringify(evalResult.result, null, 2) ?? "undefined"; + await response.addResult("Evaluation result", text, { prefix: "result", ext: "json", suggestedFilename: params.filename }); + }).catch((e) => { + response.addError(e instanceof Error ? e.message : String(e)); + }); + } + }); + evaluate_default = [ + evaluate2 + ]; + } +}); + +// packages/playwright-core/src/tools/backend/form.ts +var z12, fillForm, form_default; +var init_form = __esm({ + "packages/playwright-core/src/tools/backend/form.ts"() { + "use strict"; + init_stringUtils(); + init_tool(); + init_snapshot(); + z12 = require("./utilsBundle").z; + fillForm = defineTabTool({ + capability: "core", + schema: { + name: "browser_fill_form", + title: "Fill form", + description: "Fill multiple form fields", + inputSchema: z12.object({ + fields: z12.array(elementSchema.extend({ + name: z12.string().describe("Human-readable field name"), + type: z12.enum(["textbox", "checkbox", "radio", "combobox", "slider"]).describe("Type of the field"), + value: z12.string().describe("Value to fill in the field. If the field is a checkbox, the value should be `true` or `false`. If the field is a combobox, the value should be the text of the option.") + })).describe("Fields to fill in") + }), + type: "input" + }, + handle: async (tab2, params2, response2) => { + for (const field of params2.fields) { + const { locator: locator2, resolved } = await tab2.targetLocator({ element: field.name, target: field.target }); + const locatorSource = `await page.${resolved}`; + if (field.type === "textbox" || field.type === "slider") { + const secret = tab2.context.lookupSecret(field.value); + await locator2.fill(secret.value, tab2.actionTimeoutOptions); + response2.addCode(`${locatorSource}.fill(${secret.code});`); + } else if (field.type === "checkbox" || field.type === "radio") { + await locator2.setChecked(field.value === "true", tab2.actionTimeoutOptions); + response2.addCode(`${locatorSource}.setChecked(${field.value});`); + } else if (field.type === "combobox") { + await locator2.selectOption({ label: field.value }, tab2.actionTimeoutOptions); + response2.addCode(`${locatorSource}.selectOption(${escapeWithQuotes(field.value)});`); + } + } + } + }); + form_default = [ + fillForm + ]; + } +}); + +// packages/playwright-core/src/tools/backend/keyboard.ts +var z13, press, pressSequentially, typeSchema, type, keydown, keyup, keyboard_default; +var init_keyboard = __esm({ + "packages/playwright-core/src/tools/backend/keyboard.ts"() { + "use strict"; + init_tool(); + init_snapshot(); + z13 = require("./utilsBundle").z; + press = defineTabTool({ + capability: "core-input", + schema: { + name: "browser_press_key", + title: "Press a key", + description: "Press a key on the keyboard", + inputSchema: z13.object({ + key: z13.string().describe("Name of the key to press or a character to generate, such as `ArrowLeft` or `a`") + }), + type: "input" + }, + handle: async (tab2, params2, response2) => { + response2.addCode(`// Press ${params2.key}`); + response2.addCode(`await page.keyboard.press('${params2.key}');`); + if (params2.key === "Enter") { + response2.setIncludeSnapshot(); + await tab2.waitForCompletion(async () => { + await tab2.page.keyboard.press("Enter"); + }); + } else { + await tab2.page.keyboard.press(params2.key); + } + } + }); + pressSequentially = defineTabTool({ + capability: "core-input", + skillOnly: true, + schema: { + name: "browser_press_sequentially", + title: "Type text key by key", + description: "Type text key by key on the keyboard", + inputSchema: z13.object({ + text: z13.string().describe("Text to type"), + submit: z13.boolean().optional().describe("Whether to submit entered text (press Enter after)") + }), + type: "input" + }, + handle: async (tab2, params2, response2) => { + response2.addCode(`// Press ${params2.text}`); + response2.addCode(`await page.keyboard.type('${params2.text}');`); + await tab2.page.keyboard.type(params2.text); + if (params2.submit) { + response2.addCode(`await page.keyboard.press('Enter');`); + response2.setIncludeSnapshot(); + await tab2.waitForCompletion(async () => { + await tab2.page.keyboard.press("Enter"); + }); + } + } + }); + typeSchema = elementSchema.extend({ + text: z13.string().describe("Text to type into the element"), + submit: z13.boolean().optional().describe("Whether to submit entered text (press Enter after)"), + slowly: z13.boolean().optional().describe("Whether to type one character at a time. Useful for triggering key handlers in the page. By default entire text is filled in at once.") + }); + type = defineTabTool({ + capability: "core-input", + schema: { + name: "browser_type", + title: "Type text", + description: "Type text into editable element", + inputSchema: typeSchema, + type: "input" + }, + handle: async (tab2, params2, response2) => { + const { locator: locator2, resolved } = await tab2.targetLocator(params2); + const secret = tab2.context.lookupSecret(params2.text); + const action = async () => { + if (params2.slowly) { + response2.setIncludeSnapshot(); + response2.addCode(`await page.${resolved}.pressSequentially(${secret.code});`); + await locator2.pressSequentially(secret.value, tab2.actionTimeoutOptions); + } else { + response2.addCode(`await page.${resolved}.fill(${secret.code});`); + await locator2.fill(secret.value, tab2.actionTimeoutOptions); + } + if (params2.submit) { + response2.setIncludeSnapshot(); + response2.addCode(`await page.${resolved}.press('Enter');`); + await locator2.press("Enter", tab2.actionTimeoutOptions); + } + }; + if (params2.submit || params2.slowly) + await tab2.waitForCompletion(action); + else + await action(); + } + }); + keydown = defineTabTool({ + capability: "core-input", + skillOnly: true, + schema: { + name: "browser_keydown", + title: "Press a key down", + description: "Press a key down on the keyboard", + inputSchema: z13.object({ + key: z13.string().describe("Name of the key to press or a character to generate, such as `ArrowLeft` or `a`") + }), + type: "input" + }, + handle: async (tab2, params2, response2) => { + response2.addCode(`await page.keyboard.down('${params2.key}');`); + await tab2.page.keyboard.down(params2.key); + } + }); + keyup = defineTabTool({ + capability: "core-input", + skillOnly: true, + schema: { + name: "browser_keyup", + title: "Press a key up", + description: "Press a key up on the keyboard", + inputSchema: z13.object({ + key: z13.string().describe("Name of the key to press or a character to generate, such as `ArrowLeft` or `a`") + }), + type: "input" + }, + handle: async (tab2, params2, response2) => { + response2.addCode(`await page.keyboard.up('${params2.key}');`); + await tab2.page.keyboard.up(params2.key); + } + }); + keyboard_default = [ + press, + type, + pressSequentially, + keydown, + keyup + ]; + } +}); + +// packages/playwright-core/src/tools/backend/mouse.ts +var z14, mouseMove, mouseDown, mouseUp, mouseWheel, mouseClick, mouseDrag, mouse_default; +var init_mouse = __esm({ + "packages/playwright-core/src/tools/backend/mouse.ts"() { + "use strict"; + init_stringUtils(); + init_tool(); + z14 = require("./utilsBundle").z; + mouseMove = defineTabTool({ + capability: "vision", + schema: { + name: "browser_mouse_move_xy", + title: "Move mouse", + description: "Move mouse to a given position", + inputSchema: z14.object({ + x: z14.number().describe("X coordinate"), + y: z14.number().describe("Y coordinate") + }), + type: "input" + }, + handle: async (tab2, params2, response2) => { + response2.addCode(`// Move mouse to (${params2.x}, ${params2.y})`); + response2.addCode(`await page.mouse.move(${params2.x}, ${params2.y});`); + await tab2.page.mouse.move(params2.x, params2.y); + } + }); + mouseDown = defineTabTool({ + capability: "vision", + schema: { + name: "browser_mouse_down", + title: "Press mouse down", + description: "Press mouse down", + inputSchema: z14.object({ + button: z14.enum(["left", "right", "middle"]).optional().describe("Button to press, defaults to left") + }), + type: "input" + }, + handle: async (tab2, params2, response2) => { + const options = { button: params2.button }; + const optionsArg = formatObjectOrVoid(options); + response2.addCode(`// Press mouse down`); + response2.addCode(`await page.mouse.down(${optionsArg});`); + await tab2.page.mouse.down(options); + } + }); + mouseUp = defineTabTool({ + capability: "vision", + schema: { + name: "browser_mouse_up", + title: "Press mouse up", + description: "Press mouse up", + inputSchema: z14.object({ + button: z14.enum(["left", "right", "middle"]).optional().describe("Button to press, defaults to left") + }), + type: "input" + }, + handle: async (tab2, params2, response2) => { + const options = { button: params2.button }; + const optionsArg = formatObjectOrVoid(options); + response2.addCode(`// Press mouse up`); + response2.addCode(`await page.mouse.up(${optionsArg});`); + await tab2.page.mouse.up(options); + } + }); + mouseWheel = defineTabTool({ + capability: "vision", + schema: { + name: "browser_mouse_wheel", + title: "Scroll mouse wheel", + description: "Scroll mouse wheel", + inputSchema: z14.object({ + deltaX: z14.number().default(0).describe("X delta"), + deltaY: z14.number().default(0).describe("Y delta") + }), + type: "input" + }, + handle: async (tab2, params2, response2) => { + response2.addCode(`// Scroll mouse wheel`); + response2.addCode(`await page.mouse.wheel(${params2.deltaX}, ${params2.deltaY});`); + await tab2.page.mouse.wheel(params2.deltaX, params2.deltaY); + } + }); + mouseClick = defineTabTool({ + capability: "vision", + schema: { + name: "browser_mouse_click_xy", + title: "Click", + description: "Click mouse button at a given position", + inputSchema: z14.object({ + x: z14.number().describe("X coordinate"), + y: z14.number().describe("Y coordinate"), + button: z14.enum(["left", "right", "middle"]).optional().describe("Button to click, defaults to left"), + clickCount: z14.number().optional().describe("Number of clicks, defaults to 1"), + delay: z14.number().optional().describe("Time to wait between mouse down and mouse up in milliseconds, defaults to 0") + }), + type: "input" + }, + handle: async (tab2, params2, response2) => { + response2.setIncludeSnapshot(); + const options = { + button: params2.button, + clickCount: params2.clickCount, + delay: params2.delay + }; + const formatted = formatObjectOrVoid(options); + const optionsArg = formatted ? `, ${formatted}` : ""; + response2.addCode(`// Click mouse at coordinates (${params2.x}, ${params2.y})`); + response2.addCode(`await page.mouse.click(${params2.x}, ${params2.y}${optionsArg});`); + await tab2.waitForCompletion(async () => { + await tab2.page.mouse.click(params2.x, params2.y, options); + }); + } + }); + mouseDrag = defineTabTool({ + capability: "vision", + schema: { + name: "browser_mouse_drag_xy", + title: "Drag mouse", + description: "Drag left mouse button to a given position", + inputSchema: z14.object({ + startX: z14.number().describe("Start X coordinate"), + startY: z14.number().describe("Start Y coordinate"), + endX: z14.number().describe("End X coordinate"), + endY: z14.number().describe("End Y coordinate") + }), + type: "input" + }, + handle: async (tab2, params2, response2) => { + response2.setIncludeSnapshot(); + response2.addCode(`// Drag mouse from (${params2.startX}, ${params2.startY}) to (${params2.endX}, ${params2.endY})`); + response2.addCode(`await page.mouse.move(${params2.startX}, ${params2.startY});`); + response2.addCode(`await page.mouse.down();`); + response2.addCode(`await page.mouse.move(${params2.endX}, ${params2.endY});`); + response2.addCode(`await page.mouse.up();`); + await tab2.waitForCompletion(async () => { + await tab2.page.mouse.move(params2.startX, params2.startY); + await tab2.page.mouse.down(); + await tab2.page.mouse.move(params2.endX, params2.endY); + await tab2.page.mouse.up(); + }); + } + }); + mouse_default = [ + mouseMove, + mouseClick, + mouseDrag, + mouseDown, + mouseUp, + mouseWheel + ]; + } +}); + +// packages/playwright-core/src/tools/backend/navigate.ts +var z15, navigate, goBack, goForward, reload, navigate_default; +var init_navigate = __esm({ + "packages/playwright-core/src/tools/backend/navigate.ts"() { + "use strict"; + init_tool(); + z15 = require("./utilsBundle").z; + navigate = defineTool({ + capability: "core-navigation", + schema: { + name: "browser_navigate", + title: "Navigate to a URL", + description: "Navigate to a URL", + inputSchema: z15.object({ + url: z15.string().describe("The URL to navigate to") + }), + type: "action" + }, + handle: async (context2, params2, response2) => { + const tab2 = await context2.ensureTab(); + const url2 = await tab2.checkUrlAndNavigate(params2.url); + response2.setIncludeSnapshot(); + response2.addCode(`await page.goto('${url2}');`); + } + }); + goBack = defineTabTool({ + capability: "core-navigation", + schema: { + name: "browser_navigate_back", + title: "Go back", + description: "Go back to the previous page in the history", + inputSchema: z15.object({}), + type: "action" + }, + handle: async (tab2, params2, response2) => { + await tab2.page.goBack({ waitUntil: "commit", ...tab2.navigationTimeoutOptions }); + response2.setIncludeSnapshot(); + response2.addCode(`await page.goBack();`); + } + }); + goForward = defineTabTool({ + capability: "core-navigation", + skillOnly: true, + schema: { + name: "browser_navigate_forward", + title: "Go forward", + description: "Go forward to the next page in the history", + inputSchema: z15.object({}), + type: "action" + }, + handle: async (tab2, params2, response2) => { + await tab2.page.goForward({ waitUntil: "commit", ...tab2.navigationTimeoutOptions }); + response2.setIncludeSnapshot(); + response2.addCode(`await page.goForward();`); + } + }); + reload = defineTabTool({ + capability: "core-navigation", + skillOnly: true, + schema: { + name: "browser_reload", + title: "Reload the page", + description: "Reload the current page", + inputSchema: z15.object({}), + type: "action" + }, + handle: async (tab2, params2, response2) => { + await tab2.page.reload(tab2.navigationTimeoutOptions); + response2.setIncludeSnapshot(); + response2.addCode(`await page.reload();`); + } + }); + navigate_default = [ + navigate, + goBack, + goForward, + reload + ]; + } +}); + +// packages/playwright-core/src/tools/backend/network.ts +function isSuccessfulResponse(request2) { + if (request2.failure()) + return false; + const response2 = request2.existingResponse(); + return !!response2 && response2.status() < 400; +} +function isFetch(request2) { + return ["fetch", "xhr"].includes(request2.resourceType()); +} +function renderRequestLine(request2) { + const response2 = request2.existingResponse(); + let line = `[${request2.method().toUpperCase()}] ${request2.url()}`; + if (response2) + line += ` => [${response2.status()}] ${response2.statusText()}`; + else if (request2.failure()) + line += ` => [FAILED] ${request2.failure()?.errorText ?? "Unknown error"}`; + return line; +} +function renderRequestDetails(index, request2, skillMode) { + const httpResponse = request2.existingResponse(); + const responseHeaders = httpResponse?.headers(); + const lines = []; + lines.push(`#${index} [${request2.method().toUpperCase()}] ${request2.url()}`); + lines.push(""); + lines.push(" General"); + if (httpResponse) + lines.push(` status: [${httpResponse.status()}] ${httpResponse.statusText()}`); + else if (request2.failure()) + lines.push(` status: [FAILED] ${request2.failure()?.errorText ?? "Unknown error"}`); + const duration = computeDurationMs(request2); + if (duration !== void 0) + lines.push(` duration: ${duration}ms`); + lines.push(` type: ${request2.resourceType()}`); + const contentType = responseHeaders?.["content-type"]; + if (contentType) + lines.push(` mimeType: ${contentType.split(";")[0].trim()}`); + appendHeaderSection(lines, "Request headers", request2.headers()); + if (responseHeaders) + appendHeaderSection(lines, "Response headers", responseHeaders); + const hints = []; + if (request2.postData()) + hints.push(partHint(skillMode, "request-body", index)); + if (canHaveResponseBody(httpResponse)) + hints.push(partHint(skillMode, "response-body", index)); + if (hints.length) + lines.push("", ...hints); + return lines.join("\n"); +} +function partHint(skillMode, part, index) { + const subject = part === "request-body" ? "request body" : "response body"; + return skillMode ? `Run \`${part} ${index}\` to read the ${subject}.` : `Call browser_network_request with part="${part}" to read the ${subject}.`; +} +function canHaveResponseBody(httpResponse) { + if (!httpResponse) + return false; + const status = httpResponse.status(); + return status !== 204 && status !== 304 && !(status >= 100 && status < 200); +} +function appendHeaderSection(lines, title, headers) { + const entries = Object.entries(headers); + if (!entries.length) + return; + lines.push(""); + lines.push(` ${title}`); + for (const [k, v] of entries) + lines.push(` ${k}: ${v}`); +} +function computeDurationMs(request2) { + const timing = request2.timing(); + if (!timing || timing.responseEnd < 0) + return void 0; + return Math.round(timing.responseEnd); +} +async function renderRequestPart(request2, part, response2, suggestedFilename) { + if (part === "request-headers") { + await response2.addResult("Request headers", renderHeaders(request2.headers()), { prefix: "request", ext: "txt", suggestedFilename }); + return; + } + if (part === "request-body") { + const data = request2.postData(); + if (data !== null) + await response2.addResult("Request body", data, { prefix: "request", ext: "txt", suggestedFilename }); + return; + } + const httpResponse = request2.existingResponse(); + if (!httpResponse) + return; + if (part === "response-headers") { + await response2.addResult("Response headers", renderHeaders(httpResponse.headers()), { prefix: "response", ext: "txt", suggestedFilename }); + return; + } + const contentType = httpResponse.headers()["content-type"]; + if (isTextualMimeType(contentType ?? "")) { + let text2; + try { + text2 = await httpResponse.text(); + } catch { + return; + } + await response2.addResult("Response body", text2, { prefix: "response", ext: "txt", suggestedFilename }); + return; + } + const path59 = await saveResponseBody(request2, response2, suggestedFilename); + if (path59 !== void 0) + response2.addTextResult(path59); +} +function renderHeaders(headers) { + return Object.entries(headers).map(([k, v]) => `${k}: ${v}`).join("\n"); +} +async function saveResponseBody(request2, response2, suggestedFilename) { + const httpResponse = request2.existingResponse(); + if (!canHaveResponseBody(httpResponse)) + return void 0; + let body; + try { + body = await httpResponse.body(); + } catch { + return void 0; + } + if (!body.length) + return void 0; + const ext = getExtensionForMimeType(httpResponse.headers()["content-type"]); + const resolved = await response2.resolveClientFile({ prefix: "response", ext, suggestedFilename }, "Response body"); + await import_fs45.default.promises.writeFile(resolved.fileName, body); + return resolved.relativeName; +} +var import_fs45, z16, requests, REQUEST_PARTS, request, networkClear, networkStateSet, network_default; +var init_network4 = __esm({ + "packages/playwright-core/src/tools/backend/network.ts"() { + "use strict"; + import_fs45 = __toESM(require("fs")); + init_mimeType(); + init_rtti(); + init_tool(); + z16 = require("./utilsBundle").z; + requests = defineTabTool({ + capability: "core", + schema: { + name: "browser_network_requests", + title: "List network requests", + description: "Returns a numbered list of network requests since loading the page. Use browser_network_request with the number to get full details.", + inputSchema: z16.object({ + static: z16.boolean().default(false).describe("Whether to include successful static resources like images, fonts, scripts, etc. Defaults to false."), + filter: z16.string().optional().refine((v) => !v || isRegexString(v), { message: "Invalid regular expression" }).describe('Only return requests whose URL matches this regexp (e.g. "/api/.*user").'), + filename: z16.string().optional().describe("Filename to save the network requests to. If not provided, requests are returned as text.") + }), + type: "readOnly" + }, + handle: async (tab2, params2, response2) => { + const allRequests = await tab2.requests(); + const filter = params2.filter ? new RegExp(params2.filter) : void 0; + const lines = []; + let hiddenStaticCount = 0; + for (let i = 0; i < allRequests.length; i++) { + const request2 = allRequests[i]; + if (!params2.static && !isFetch(request2) && isSuccessfulResponse(request2)) { + hiddenStaticCount++; + continue; + } + if (filter) { + filter.lastIndex = 0; + if (!filter.test(request2.url())) + continue; + } + lines.push(`${i + 1}. ${renderRequestLine(request2)}`); + } + if (hiddenStaticCount > 0) { + const optionName = tab2.context.config.skillMode ? "--static" : '"static"'; + lines.push(` +Note: ${hiddenStaticCount} static request${hiddenStaticCount === 1 ? "" : "s"} not shown, run with ${optionName} option to see ${hiddenStaticCount === 1 ? "it" : "them"}.`); + } + await response2.addResult("Network", lines.join("\n"), { prefix: "network", ext: "log", suggestedFilename: params2.filename }); + } + }); + REQUEST_PARTS = ["request-headers", "request-body", "response-headers", "response-body"]; + request = defineTabTool({ + capability: "core", + schema: { + name: "browser_network_request", + title: "Show network request details", + description: "Returns full details (headers and body) of a single network request, or a single part if `part` is set. Use the number from browser_network_requests.", + inputSchema: z16.object({ + index: z16.number().int().min(1).describe("1-based index of the request, as printed by browser_network_requests."), + part: z16.enum(REQUEST_PARTS).optional().describe("Return only this part of the request. Omit to return full details."), + filename: z16.string().optional().describe("Filename to save the result to. If not provided, output is returned as text.") + }), + type: "readOnly" + }, + handle: async (tab2, params2, response2) => { + const allRequests = await tab2.requests(); + const request2 = allRequests[params2.index - 1]; + if (!request2) { + response2.addError(`Request #${params2.index} not found. Use browser_network_requests to see available indexes.`); + return; + } + if (params2.part) { + await renderRequestPart(request2, params2.part, response2, params2.filename); + return; + } + await response2.addResult("Request", renderRequestDetails(params2.index, request2, !!tab2.context.config.skillMode), { prefix: "request", ext: "log", suggestedFilename: params2.filename }); + } + }); + networkClear = defineTabTool({ + capability: "core", + skillOnly: true, + schema: { + name: "browser_network_clear", + title: "Clear network requests", + description: "Clear all network requests", + inputSchema: z16.object({}), + type: "readOnly" + }, + handle: async (tab2, params2, response2) => { + await tab2.clearRequests(); + } + }); + networkStateSet = defineTool({ + capability: "network", + schema: { + name: "browser_network_state_set", + title: "Set network state", + description: "Sets the browser network state to online or offline. When offline, all network requests will fail.", + inputSchema: z16.object({ + state: z16.enum(["online", "offline"]).describe('Set to "offline" to simulate offline mode, "online" to restore network connectivity') + }), + type: "action" + }, + handle: async (context2, params2, response2) => { + const browserContext = await context2.ensureBrowserContext(); + const offline = params2.state === "offline"; + await browserContext.setOffline(offline); + response2.addTextResult(`Network is now ${params2.state}`); + response2.addCode(`await page.context().setOffline(${offline});`); + } + }); + network_default = [ + requests, + request, + networkClear, + networkStateSet + ]; + } +}); + +// packages/playwright-core/src/tools/backend/pdf.ts +var z17, pdfSchema, pdf, pdf_default; +var init_pdf = __esm({ + "packages/playwright-core/src/tools/backend/pdf.ts"() { + "use strict"; + init_stringUtils(); + init_tool(); + z17 = require("./utilsBundle").z; + pdfSchema = z17.object({ + filename: z17.string().optional().describe("File name to save the pdf to. Defaults to `page-{timestamp}.pdf` if not specified. Prefer relative file names to stay within the output directory.") + }); + pdf = defineTabTool({ + capability: "pdf", + schema: { + name: "browser_pdf_save", + title: "Save as PDF", + description: "Save page as PDF", + inputSchema: pdfSchema, + type: "readOnly" + }, + handle: async (tab2, params2, response2) => { + const data = await tab2.page.pdf(); + const result2 = await response2.resolveClientFile({ prefix: "page", ext: "pdf", suggestedFilename: params2.filename }, "Page as pdf"); + await response2.addFileResult(result2, data); + response2.addCode(`await page.pdf(${formatObject({ path: result2.relativeName })});`); + } + }); + pdf_default = [ + pdf + ]; + } +}); + +// packages/playwright-core/src/tools/backend/route.ts +var z18, route, routeList, unroute, route_default; +var init_route = __esm({ + "packages/playwright-core/src/tools/backend/route.ts"() { + "use strict"; + init_tool(); + z18 = require("./utilsBundle").z; + route = defineTool({ + capability: "network", + schema: { + name: "browser_route", + title: "Mock network requests", + description: "Set up a route to mock network requests matching a URL pattern", + inputSchema: z18.object({ + pattern: z18.string().describe('URL pattern to match (e.g., "**/api/users", "**/*.{png,jpg}")'), + status: z18.number().optional().describe("HTTP status code to return (default: 200)"), + body: z18.string().optional().describe("Response body (text or JSON string)"), + contentType: z18.string().optional().describe('Content-Type header (e.g., "application/json", "text/html")'), + headers: z18.array(z18.string()).optional().describe('Headers to add in "Name: Value" format'), + removeHeaders: z18.string().optional().describe("Comma-separated list of header names to remove from request") + }), + type: "action" + }, + handle: async (context2, params2, response2) => { + const addHeaders = params2.headers ? Object.fromEntries(params2.headers.map((h) => { + const colonIndex = h.indexOf(":"); + return [h.substring(0, colonIndex).trim(), h.substring(colonIndex + 1).trim()]; + })) : void 0; + const removeHeaders = params2.removeHeaders ? params2.removeHeaders.split(",").map((h) => h.trim()) : void 0; + const handler = async (route2) => { + if (params2.body !== void 0 || params2.status !== void 0) { + await route2.fulfill({ + status: params2.status ?? 200, + contentType: params2.contentType, + body: params2.body + }); + return; + } + const headers = { ...route2.request().headers() }; + if (addHeaders) { + for (const [key, value2] of Object.entries(addHeaders)) + headers[key] = value2; + } + if (removeHeaders) { + for (const header of removeHeaders) + delete headers[header.toLowerCase()]; + } + await route2.continue({ headers }); + }; + const entry = { + pattern: params2.pattern, + status: params2.status, + body: params2.body, + contentType: params2.contentType, + addHeaders, + removeHeaders, + handler + }; + await context2.addRoute(entry); + response2.addTextResult(`Route added for pattern: ${params2.pattern}`); + response2.addCode(`await page.context().route('${params2.pattern}', async route => { /* route handler */ });`); + } + }); + routeList = defineTool({ + capability: "network", + schema: { + name: "browser_route_list", + title: "List network routes", + description: "List all active network routes", + inputSchema: z18.object({}), + type: "readOnly" + }, + handle: async (context2, params2, response2) => { + const routes = context2.routes(); + if (routes.length === 0) { + response2.addTextResult("No active routes"); + return; + } + const lines = []; + for (let i = 0; i < routes.length; i++) { + const route2 = routes[i]; + const details = []; + if (route2.status !== void 0) + details.push(`status=${route2.status}`); + if (route2.body !== void 0) + details.push(`body=${route2.body.length > 50 ? route2.body.substring(0, 50) + "..." : route2.body}`); + if (route2.contentType) + details.push(`contentType=${route2.contentType}`); + if (route2.addHeaders) + details.push(`addHeaders=${JSON.stringify(route2.addHeaders)}`); + if (route2.removeHeaders) + details.push(`removeHeaders=${route2.removeHeaders.join(",")}`); + const detailsStr = details.length ? ` (${details.join(", ")})` : ""; + lines.push(`${i + 1}. ${route2.pattern}${detailsStr}`); + } + response2.addTextResult(lines.join("\n")); + } + }); + unroute = defineTool({ + capability: "network", + schema: { + name: "browser_unroute", + title: "Remove network routes", + description: "Remove network routes matching a pattern (or all routes if no pattern specified)", + inputSchema: z18.object({ + pattern: z18.string().optional().describe("URL pattern to unroute (omit to remove all routes)") + }), + type: "action" + }, + handle: async (context2, params2, response2) => { + const removed = await context2.removeRoute(params2.pattern); + if (params2.pattern) + response2.addTextResult(`Removed ${removed} route(s) for pattern: ${params2.pattern}`); + else + response2.addTextResult(`Removed all ${removed} route(s)`); + } + }); + route_default = [ + route, + routeList, + unroute + ]; + } +}); + +// packages/playwright-core/src/tools/backend/runCode.ts +var import_fs46, import_vm, z19, codeSchema, runCode, runCode_default; +var init_runCode = __esm({ + "packages/playwright-core/src/tools/backend/runCode.ts"() { + "use strict"; + import_fs46 = __toESM(require("fs")); + import_vm = __toESM(require("vm")); + init_manualPromise(); + init_tool(); + z19 = require("./utilsBundle").z; + codeSchema = z19.object({ + code: z19.string().optional().describe(`A JavaScript function containing Playwright code to execute. It will be invoked with a single argument, page, which you can use for any page interaction. For example: \`async (page) => { await page.getByRole('button', { name: 'Submit' }).click(); return await page.title(); }\``), + filename: z19.string().optional().describe("Load code from the specified file. If both code and filename are provided, code will be ignored.") + }); + runCode = defineTabTool({ + capability: "core", + schema: { + name: "browser_run_code_unsafe", + title: "Run Playwright code (unsafe)", + description: "Run a Playwright code snippet. Unsafe: executes arbitrary JavaScript in the Playwright server process and is RCE-equivalent.", + inputSchema: codeSchema, + type: "action" + }, + handle: async (tab2, params2, response2) => { + let code = params2.code; + if (params2.filename) { + const resolvedPath = await response2.resolveClientFilename(params2.filename); + code = await import_fs46.default.promises.readFile(resolvedPath, "utf-8"); + } + response2.addCode(`await (${code})(page);`); + const __end__ = new ManualPromise(); + const context2 = { + page: tab2.page, + __end__ + }; + import_vm.default.createContext(context2); + const unsubscribe = tab2.context.onUnhandledRejection((reason) => { + if (!__end__.isDone()) + __end__.reject(reason instanceof Error ? reason : new Error(String(reason))); + }); + try { + await tab2.waitForCompletion(async () => { + context2.__fn__ = import_vm.default.runInContext("(" + code + ")", context2); + const snippet = "(async () => {\n try {\n const result = await __fn__(page);\n __end__.resolve(JSON.stringify(result));\n } catch (e) {\n __end__.reject(e);\n }\n})()"; + const iifePromise = import_vm.default.runInContext(snippet, context2); + await Promise.race([iifePromise, __end__]); + const result2 = await __end__; + if (typeof result2 === "string") + response2.addTextResult(result2); + }); + } finally { + unsubscribe(); + } + } + }); + runCode_default = [ + runCode + ]; + } +}); + +// packages/playwright-core/src/tools/backend/storage.ts +var z20, storageState, setStorageState, storage_default; +var init_storage = __esm({ + "packages/playwright-core/src/tools/backend/storage.ts"() { + "use strict"; + init_tool(); + z20 = require("./utilsBundle").z; + storageState = defineTool({ + capability: "storage", + schema: { + name: "browser_storage_state", + title: "Save storage state", + description: "Save storage state (cookies, local storage) to a file for later reuse", + inputSchema: z20.object({ + filename: z20.string().optional().describe("File name to save the storage state to. Defaults to `storage-state-{timestamp}.json` if not specified.") + }), + type: "readOnly" + }, + handle: async (context2, params2, response2) => { + const browserContext = await context2.ensureBrowserContext(); + const state = await browserContext.storageState(); + const serializedState = JSON.stringify(state, null, 2); + const resolvedFile = await response2.resolveClientFile({ prefix: "storage-state", ext: "json", suggestedFilename: params2.filename }, "Storage state"); + response2.addCode(`await page.context().storageState({ path: '${resolvedFile.relativeName}' });`); + await response2.addFileResult(resolvedFile, serializedState); + } + }); + setStorageState = defineTool({ + capability: "storage", + schema: { + name: "browser_set_storage_state", + title: "Restore storage state", + description: "Restore storage state (cookies, local storage) from a file. This clears existing cookies and local storage before restoring.", + inputSchema: z20.object({ + filename: z20.string().describe("Path to the storage state file to restore from") + }), + type: "action" + }, + handle: async (context2, params2, response2) => { + const browserContext = await context2.ensureBrowserContext(); + const resolvedFilename = await response2.resolveClientFilename(params2.filename); + await browserContext.setStorageState(resolvedFilename); + response2.addTextResult(`Storage state restored from ${params2.filename}`); + response2.addCode(`await page.context().setStorageState('${params2.filename}');`); + } + }); + storage_default = [ + storageState, + setStorageState + ]; + } +}); + +// packages/playwright-core/src/tools/backend/tabs.ts +var z21, browserTabs, tabs_default; +var init_tabs = __esm({ + "packages/playwright-core/src/tools/backend/tabs.ts"() { + "use strict"; + init_tool(); + init_response(); + z21 = require("./utilsBundle").z; + browserTabs = defineTool({ + capability: "core-tabs", + schema: { + name: "browser_tabs", + title: "Manage tabs", + description: "List, create, close, or select a browser tab.", + inputSchema: z21.object({ + action: z21.enum(["list", "new", "close", "select"]).describe("Operation to perform"), + index: z21.number().optional().describe("Tab index, used for close/select. If omitted for close, current tab is closed."), + url: z21.string().optional().describe("URL to navigate to in the new tab, used for new.") + }), + type: "action" + }, + handle: async (context2, params2, response2) => { + switch (params2.action) { + case "list": { + await context2.ensureTab(); + break; + } + case "new": { + const tab2 = await context2.newTab(); + if (params2.url) { + const url2 = await tab2.checkUrlAndNavigate(params2.url); + response2.setIncludeSnapshot(); + response2.addCode(`await page.goto('${url2}');`); + } + break; + } + case "close": { + await context2.closeTab(params2.index); + break; + } + case "select": { + if (params2.index === void 0) + throw new Error("Tab index is required"); + await context2.selectTab(params2.index); + break; + } + } + const tabHeaders = await Promise.all(context2.tabs().map((tab2) => tab2.headerSnapshot())); + const result2 = renderTabsMarkdown(tabHeaders); + response2.addTextResult(result2.join("\n")); + } + }); + tabs_default = [ + browserTabs + ]; + } +}); + +// packages/playwright-core/src/tools/backend/tracing.ts +var z22, tracingStart, tracingStop, tracing_default, traceLegendSymbol; +var init_tracing3 = __esm({ + "packages/playwright-core/src/tools/backend/tracing.ts"() { + "use strict"; + init_tool(); + z22 = require("./utilsBundle").z; + tracingStart = defineTool({ + capability: "devtools", + schema: { + name: "browser_start_tracing", + title: "Start tracing", + description: "Start trace recording", + inputSchema: z22.object({}), + type: "readOnly" + }, + handle: async (context2, params2, response2) => { + const browserContext = await context2.ensureBrowserContext(); + const tracesDir = await context2.outputFile({ prefix: "", suggestedFilename: `traces`, ext: "" }, { origin: "code" }); + const name = "trace-" + Date.now(); + await browserContext.tracing.start({ + name, + screenshots: true, + snapshots: true, + live: true + }); + response2.addTextResult(`Trace recording started`); + response2.addFileLink("Action log", `${tracesDir}/${name}.trace`); + response2.addFileLink("Network log", `${tracesDir}/${name}.network`); + response2.addFileLink("Resources", `${tracesDir}/resources`); + browserContext.tracing[traceLegendSymbol] = { tracesDir, name }; + } + }); + tracingStop = defineTool({ + capability: "devtools", + schema: { + name: "browser_stop_tracing", + title: "Stop tracing", + description: "Stop trace recording", + inputSchema: z22.object({}), + type: "readOnly" + }, + handle: async (context2, params2, response2) => { + const browserContext = await context2.ensureBrowserContext(); + await browserContext.tracing.stop(); + const traceLegend = browserContext.tracing[traceLegendSymbol]; + if (!traceLegend) + throw new Error("Tracing is not started"); + delete browserContext.tracing[traceLegendSymbol]; + response2.addTextResult(`Trace recording stopped.`); + response2.addFileLink("Trace", `${traceLegend.tracesDir}/${traceLegend.name}.trace`); + response2.addFileLink("Network log", `${traceLegend.tracesDir}/${traceLegend.name}.network`); + response2.addFileLink("Resources", `${traceLegend.tracesDir}/resources`); + } + }); + tracing_default = [ + tracingStart, + tracingStop + ]; + traceLegendSymbol = Symbol("tracesDir"); + } +}); + +// packages/playwright-core/src/tools/backend/verify.ts +var z23, verifyElement, verifyText, verifyList, verifyValue, verify_default; +var init_verify = __esm({ + "packages/playwright-core/src/tools/backend/verify.ts"() { + "use strict"; + init_stringUtils(); + init_tool(); + z23 = require("./utilsBundle").z; + verifyElement = defineTabTool({ + capability: "testing", + schema: { + name: "browser_verify_element_visible", + title: "Verify element visible", + description: "Verify element is visible on the page", + inputSchema: z23.object({ + role: z23.string().describe('ROLE of the element. Can be found in the snapshot like this: `- {ROLE} "Accessible Name":`'), + accessibleName: z23.string().describe('ACCESSIBLE_NAME of the element. Can be found in the snapshot like this: `- role "{ACCESSIBLE_NAME}"`') + }), + type: "assertion" + }, + handle: async (tab2, params2, response2) => { + for (const frame of tab2.page.frames()) { + const locator2 = frame.getByRole(params2.role, { name: params2.accessibleName }); + if (await locator2.count() > 0) { + const resolved = await locator2.normalize(); + response2.addCode(`await expect(page.${resolved}).toBeVisible();`); + response2.addTextResult("Done"); + return; + } + } + response2.addError(`Element with role "${params2.role}" and accessible name "${params2.accessibleName}" not found`); + } + }); + verifyText = defineTabTool({ + capability: "testing", + schema: { + name: "browser_verify_text_visible", + title: "Verify text visible", + description: `Verify text is visible on the page. Prefer ${verifyElement.schema.name} if possible.`, + inputSchema: z23.object({ + text: z23.string().describe('TEXT to verify. Can be found in the snapshot like this: `- role "Accessible Name": {TEXT}` or like this: `- text: {TEXT}`') + }), + type: "assertion" + }, + handle: async (tab2, params2, response2) => { + for (const frame of tab2.page.frames()) { + const locator2 = frame.getByText(params2.text).filter({ visible: true }); + if (await locator2.count() > 0) { + const resolved = await locator2.normalize(); + response2.addCode(`await expect(page.${resolved}).toBeVisible();`); + response2.addTextResult("Done"); + return; + } + } + response2.addError("Text not found"); + } + }); + verifyList = defineTabTool({ + capability: "testing", + schema: { + name: "browser_verify_list_visible", + title: "Verify list visible", + description: "Verify list is visible on the page", + inputSchema: z23.object({ + element: z23.string().describe("Human-readable list description"), + target: z23.string().describe("Exact target element reference that points to the list"), + items: z23.array(z23.string()).describe("Items to verify") + }), + type: "assertion" + }, + handle: async (tab2, params2, response2) => { + const { locator: locator2 } = await tab2.targetLocator(params2); + const itemTexts = []; + for (const item of params2.items) { + const itemLocator = locator2.getByText(item); + if (await itemLocator.count() === 0) { + response2.addError(`Item "${item}" not found`); + return; + } + itemTexts.push(await itemLocator.textContent(tab2.expectTimeoutOptions)); + } + const ariaSnapshot = `\` +- list: +${itemTexts.map((t) => ` - listitem: ${escapeWithQuotes(t, '"')}`).join("\n")} +\``; + response2.addCode(`await expect(page.locator('body')).toMatchAriaSnapshot(${ariaSnapshot});`); + response2.addTextResult("Done"); + } + }); + verifyValue = defineTabTool({ + capability: "testing", + schema: { + name: "browser_verify_value", + title: "Verify value", + description: "Verify element value", + inputSchema: z23.object({ + type: z23.enum(["textbox", "checkbox", "radio", "combobox", "slider"]).describe("Type of the element"), + element: z23.string().describe("Human-readable element description"), + target: z23.string().describe("Exact target element reference from the page snapshot"), + value: z23.string().describe('Value to verify. For checkbox, use "true" or "false".') + }), + type: "assertion" + }, + handle: async (tab2, params2, response2) => { + const { locator: locator2, resolved } = await tab2.targetLocator(params2); + const locatorSource = `page.${resolved}`; + if (params2.type === "textbox" || params2.type === "slider" || params2.type === "combobox") { + const value2 = await locator2.inputValue(tab2.expectTimeoutOptions); + if (value2 !== params2.value) { + response2.addError(`Expected value "${params2.value}", but got "${value2}"`); + return; + } + response2.addCode(`await expect(${locatorSource}).toHaveValue(${escapeWithQuotes(params2.value)});`); + } else if (params2.type === "checkbox" || params2.type === "radio") { + const value2 = await locator2.isChecked(tab2.expectTimeoutOptions); + if (value2 !== (params2.value === "true")) { + response2.addError(`Expected value "${params2.value}", but got "${value2}"`); + return; + } + const matcher = value2 ? "toBeChecked" : "not.toBeChecked"; + response2.addCode(`await expect(${locatorSource}).${matcher}();`); + } + response2.addTextResult("Done"); + } + }); + verify_default = [ + verifyElement, + verifyText, + verifyList, + verifyValue + ]; + } +}); + +// packages/playwright-core/src/tools/backend/video.ts +var z24, videoStart, videoStop, videoChapter, actionPosition, actionCursor, videoShowActions, videoHideActions, video_default; +var init_video2 = __esm({ + "packages/playwright-core/src/tools/backend/video.ts"() { + "use strict"; + init_tool(); + z24 = require("./utilsBundle").z; + videoStart = defineTool({ + capability: "devtools", + schema: { + name: "browser_start_video", + title: "Start video", + description: "Start video recording", + inputSchema: z24.object({ + filename: z24.string().optional().describe("Filename to save the video."), + size: z24.object({ + width: z24.number().describe("Video width"), + height: z24.number().describe("Video height") + }).optional().describe("Video size") + }), + type: "readOnly" + }, + handle: async (context2, params2, response2) => { + const resolvedFile = await response2.resolveClientFile({ prefix: "video", ext: "webm", suggestedFilename: params2.filename }, "Video"); + await context2.startVideoRecording(resolvedFile.fileName, { size: params2.size }); + response2.addTextResult("Video recording started."); + } + }); + videoStop = defineTool({ + capability: "devtools", + schema: { + name: "browser_stop_video", + title: "Stop video", + description: "Stop video recording", + inputSchema: z24.object({}), + type: "readOnly" + }, + handle: async (context2, params2, response2) => { + const fileNames = await context2.stopVideoRecording(); + if (!fileNames.length) { + response2.addTextResult("No videos were recorded."); + return; + } + for (const fileName of fileNames) { + const resolvedFile = await response2.resolveClientFile({ + prefix: "video", + ext: "webm", + suggestedFilename: fileName + }, "Video"); + await response2.addFileResult(resolvedFile, null); + } + } + }); + videoChapter = defineTool({ + capability: "devtools", + schema: { + name: "browser_video_chapter", + title: "Video chapter", + description: "Add a chapter marker to the video recording. Shows a full-screen chapter card with blurred backdrop.", + inputSchema: z24.object({ + title: z24.string().describe("Chapter title"), + description: z24.string().optional().describe("Chapter description"), + duration: z24.number().optional().describe("Duration in milliseconds to show the chapter card") + }), + type: "readOnly" + }, + handle: async (context2, params2, response2) => { + const tab2 = context2.currentTabOrDie(); + await tab2.page.screencast.showChapter(params2.title, { + description: params2.description, + duration: params2.duration + }); + response2.addTextResult(`Chapter '${params2.title}' added.`); + } + }); + actionPosition = z24.enum(["top-left", "top", "top-right", "bottom-left", "bottom", "bottom-right"]); + actionCursor = z24.enum(["none", "pointer"]); + videoShowActions = defineTool({ + capability: "devtools", + schema: { + name: "browser_video_show_actions", + title: "Show action overlays", + description: "Annotate subsequent actions performed on the page with a callout that names the action and highlights the target element. Useful while video recording or screencasting.", + inputSchema: z24.object({ + duration: z24.number().optional().describe("How long each action annotation stays on screen, in milliseconds. Defaults to 500."), + position: actionPosition.optional().describe("Where to place the action title relative to the page. Defaults to top-right."), + cursor: actionCursor.optional().describe('Cursor decoration for pointer actions. "pointer" (default) animates a mouse pointer from the previous action point to the next one; "none" disables the cursor decoration.') + }), + type: "readOnly" + }, + handle: async (context2, params2, response2) => { + const tab2 = context2.currentTabOrDie(); + await tab2.page.screencast.showActions({ + duration: params2.duration, + position: params2.position, + cursor: params2.cursor + }); + response2.addTextResult("Action annotations enabled."); + } + }); + videoHideActions = defineTool({ + capability: "devtools", + schema: { + name: "browser_video_hide_actions", + title: "Hide action overlays", + description: "Stop annotating actions performed on the page.", + inputSchema: z24.object({}), + type: "readOnly" + }, + handle: async (context2, params2, response2) => { + const tab2 = context2.currentTabOrDie(); + await tab2.page.screencast.hideActions(); + response2.addTextResult("Action annotations disabled."); + } + }); + video_default = [ + videoStart, + videoStop, + videoChapter, + videoShowActions, + videoHideActions + ]; + } +}); + +// packages/playwright-core/src/tools/backend/wait.ts +var z25, wait, wait_default; +var init_wait = __esm({ + "packages/playwright-core/src/tools/backend/wait.ts"() { + "use strict"; + init_tool(); + z25 = require("./utilsBundle").z; + wait = defineTool({ + capability: "core", + schema: { + name: "browser_wait_for", + title: "Wait for", + description: "Wait for text to appear or disappear or a specified time to pass", + inputSchema: z25.object({ + time: z25.number().optional().describe("The time to wait in seconds"), + text: z25.string().optional().describe("The text to wait for"), + textGone: z25.string().optional().describe("The text to wait for to disappear") + }), + type: "assertion" + }, + handle: async (context2, params2, response2) => { + if (!params2.text && !params2.textGone && !params2.time) + throw new Error("Either time, text or textGone must be provided"); + if (params2.time) { + response2.addCode(`await new Promise(f => setTimeout(f, ${params2.time} * 1000));`); + await new Promise((f) => setTimeout(f, Math.min(3e4, params2.time * 1e3))); + } + const tab2 = context2.currentTabOrDie(); + const locator2 = params2.text ? tab2.page.getByText(params2.text).first() : void 0; + const goneLocator = params2.textGone ? tab2.page.getByText(params2.textGone).first() : void 0; + if (goneLocator) { + response2.addCode(`await page.getByText(${JSON.stringify(params2.textGone)}).first().waitFor({ state: 'hidden' });`); + await goneLocator.waitFor({ state: "hidden" }); + } + if (locator2) { + response2.addCode(`await page.getByText(${JSON.stringify(params2.text)}).first().waitFor({ state: 'visible' });`); + await locator2.waitFor({ state: "visible" }); + } + response2.addTextResult(`Waited for ${params2.text || params2.textGone || params2.time}`); + response2.setIncludeSnapshot(); + } + }); + wait_default = [ + wait + ]; + } +}); + +// packages/playwright-core/src/tools/backend/webstorage.ts +var z26, localStorageList, localStorageGet, localStorageSet, localStorageDelete, localStorageClear, sessionStorageList, sessionStorageGet, sessionStorageSet, sessionStorageDelete, sessionStorageClear, webstorage_default; +var init_webstorage = __esm({ + "packages/playwright-core/src/tools/backend/webstorage.ts"() { + "use strict"; + init_tool(); + z26 = require("./utilsBundle").z; + localStorageList = defineTabTool({ + capability: "storage", + schema: { + name: "browser_localstorage_list", + title: "List localStorage", + description: "List all localStorage key-value pairs", + inputSchema: z26.object({}), + type: "readOnly" + }, + handle: async (tab2, params2, response2) => { + const items = await tab2.page.localStorage.items(); + if (items.length === 0) + response2.addTextResult("No localStorage items found"); + else + response2.addTextResult(items.map((item) => `${item.name}=${item.value}`).join("\n")); + response2.addCode(`await page.localStorage.items();`); + } + }); + localStorageGet = defineTabTool({ + capability: "storage", + schema: { + name: "browser_localstorage_get", + title: "Get localStorage item", + description: "Get a localStorage item by key", + inputSchema: z26.object({ + key: z26.string().describe("Key to get") + }), + type: "readOnly" + }, + handle: async (tab2, params2, response2) => { + const value2 = await tab2.page.localStorage.getItem(params2.key); + if (value2 === null) + response2.addTextResult(`localStorage key '${params2.key}' not found`); + else + response2.addTextResult(`${params2.key}=${value2}`); + response2.addCode(`await page.localStorage.getItem('${params2.key}');`); + } + }); + localStorageSet = defineTabTool({ + capability: "storage", + schema: { + name: "browser_localstorage_set", + title: "Set localStorage item", + description: "Set a localStorage item", + inputSchema: z26.object({ + key: z26.string().describe("Key to set"), + value: z26.string().describe("Value to set") + }), + type: "action" + }, + handle: async (tab2, params2, response2) => { + await tab2.page.localStorage.setItem(params2.key, params2.value); + response2.addCode(`await page.localStorage.setItem('${params2.key}', '${params2.value}');`); + } + }); + localStorageDelete = defineTabTool({ + capability: "storage", + schema: { + name: "browser_localstorage_delete", + title: "Delete localStorage item", + description: "Delete a localStorage item", + inputSchema: z26.object({ + key: z26.string().describe("Key to delete") + }), + type: "action" + }, + handle: async (tab2, params2, response2) => { + await tab2.page.localStorage.removeItem(params2.key); + response2.addCode(`await page.localStorage.removeItem('${params2.key}');`); + } + }); + localStorageClear = defineTabTool({ + capability: "storage", + schema: { + name: "browser_localstorage_clear", + title: "Clear localStorage", + description: "Clear all localStorage", + inputSchema: z26.object({}), + type: "action" + }, + handle: async (tab2, params2, response2) => { + await tab2.page.localStorage.clear(); + response2.addCode(`await page.localStorage.clear();`); + } + }); + sessionStorageList = defineTabTool({ + capability: "storage", + schema: { + name: "browser_sessionstorage_list", + title: "List sessionStorage", + description: "List all sessionStorage key-value pairs", + inputSchema: z26.object({}), + type: "readOnly" + }, + handle: async (tab2, params2, response2) => { + const items = await tab2.page.sessionStorage.items(); + if (items.length === 0) + response2.addTextResult("No sessionStorage items found"); + else + response2.addTextResult(items.map((item) => `${item.name}=${item.value}`).join("\n")); + response2.addCode(`await page.sessionStorage.items();`); + } + }); + sessionStorageGet = defineTabTool({ + capability: "storage", + schema: { + name: "browser_sessionstorage_get", + title: "Get sessionStorage item", + description: "Get a sessionStorage item by key", + inputSchema: z26.object({ + key: z26.string().describe("Key to get") + }), + type: "readOnly" + }, + handle: async (tab2, params2, response2) => { + const value2 = await tab2.page.sessionStorage.getItem(params2.key); + if (value2 === null) + response2.addTextResult(`sessionStorage key '${params2.key}' not found`); + else + response2.addTextResult(`${params2.key}=${value2}`); + response2.addCode(`await page.sessionStorage.getItem('${params2.key}');`); + } + }); + sessionStorageSet = defineTabTool({ + capability: "storage", + schema: { + name: "browser_sessionstorage_set", + title: "Set sessionStorage item", + description: "Set a sessionStorage item", + inputSchema: z26.object({ + key: z26.string().describe("Key to set"), + value: z26.string().describe("Value to set") + }), + type: "action" + }, + handle: async (tab2, params2, response2) => { + await tab2.page.sessionStorage.setItem(params2.key, params2.value); + response2.addCode(`await page.sessionStorage.setItem('${params2.key}', '${params2.value}');`); + } + }); + sessionStorageDelete = defineTabTool({ + capability: "storage", + schema: { + name: "browser_sessionstorage_delete", + title: "Delete sessionStorage item", + description: "Delete a sessionStorage item", + inputSchema: z26.object({ + key: z26.string().describe("Key to delete") + }), + type: "action" + }, + handle: async (tab2, params2, response2) => { + await tab2.page.sessionStorage.removeItem(params2.key); + response2.addCode(`await page.sessionStorage.removeItem('${params2.key}');`); + } + }); + sessionStorageClear = defineTabTool({ + capability: "storage", + schema: { + name: "browser_sessionstorage_clear", + title: "Clear sessionStorage", + description: "Clear all sessionStorage", + inputSchema: z26.object({}), + type: "action" + }, + handle: async (tab2, params2, response2) => { + await tab2.page.sessionStorage.clear(); + response2.addCode(`await page.sessionStorage.clear();`); + } + }); + webstorage_default = [ + localStorageList, + localStorageGet, + localStorageSet, + localStorageDelete, + localStorageClear, + sessionStorageList, + sessionStorageGet, + sessionStorageSet, + sessionStorageDelete, + sessionStorageClear + ]; + } +}); + +// packages/playwright-core/src/tools/backend/tools.ts +function filteredTools(config) { + return browserTools.filter((tool) => tool.capability.startsWith("core") || config.capabilities?.includes(tool.capability)).filter((tool) => !tool.skillOnly).map((tool) => ({ + ...tool, + schema: { + ...tool.schema, + // Note: we first ensure that "selector" property is present, so that we can omit() it without an error. + inputSchema: tool.schema.inputSchema.extend({ selector: z27.string(), startSelector: z27.string(), endSelector: z27.string() }).omit({ selector: true, startSelector: true, endSelector: true }) + } + })); +} +var z27, browserTools; +var init_tools = __esm({ + "packages/playwright-core/src/tools/backend/tools.ts"() { + "use strict"; + init_common(); + init_config(); + init_console2(); + init_cookies(); + init_devtools(); + init_dialogs(); + init_evaluate(); + init_files(); + init_form(); + init_keyboard(); + init_mouse(); + init_navigate(); + init_network4(); + init_pdf(); + init_route(); + init_runCode(); + init_snapshot(); + init_screenshot(); + init_storage(); + init_tabs(); + init_tracing3(); + init_verify(); + init_video2(); + init_wait(); + init_webstorage(); + z27 = require("./utilsBundle").z; + browserTools = [ + ...common_default, + ...config_default, + ...console_default, + ...cookies_default, + ...devtools_default, + ...dialogs_default, + ...evaluate_default, + ...files_default, + ...form_default, + ...keyboard_default, + ...mouse_default, + ...navigate_default, + ...network_default, + ...pdf_default, + ...route_default, + ...runCode_default, + ...screenshot_default, + ...snapshot_default, + ...storage_default, + ...tabs_default, + ...tracing_default, + ...verify_default, + ...video_default, + ...wait_default, + ...webstorage_default + ]; + } +}); + +// packages/playwright-core/src/tools/cli-daemon/command.ts +function declareCommand(command) { + return command; +} +function parseCommand(command, args) { + const optionsObject = { ...args }; + delete optionsObject["_"]; + const optionsSchema = (command.options ?? kEmptyOptions).strict(); + const options = zodParse(optionsSchema, optionsObject, "option"); + const argsSchema = (command.args ?? kEmptyArgs).strict(); + const argNames = [...Object.keys(argsSchema.shape)]; + const argv = args["_"].slice(1); + if (argv.length > argNames.length) + throw new Error(`error: too many arguments: expected ${argNames.length}, received ${argv.length}`); + const argsObject = {}; + argNames.forEach((name, index) => argsObject[name] = argv[index]); + const parsedArgsObject = zodParse(argsSchema, argsObject, "argument"); + const toolName = typeof command.toolName === "function" ? command.toolName({ ...parsedArgsObject, ...options }) : command.toolName; + const toolParams = command.toolParams({ ...parsedArgsObject, ...options }); + return { toolName, toolParams }; +} +function zodParse(schema, data, type3) { + try { + return schema.parse(data); + } catch (e) { + throw new Error(e.issues.map((issue) => { + const keys = issue.code === "unrecognized_keys" ? issue.keys : [""]; + const props = keys.map((key) => [...issue.path, key].filter(Boolean).join(".")); + return props.map((prop) => { + const label = type3 === "option" ? `'--${prop}' option` : `'${prop}' argument`; + switch (issue.code) { + case "invalid_type": + return "error: " + label + ": " + issue.message.replace(/Invalid input:/, "").trim(); + case "unrecognized_keys": + return "error: unknown " + label; + default: + return "error: " + label + ": " + issue.message; + } + }); + }).flat().join("\n")); + } +} +var z28, kEmptyOptions, kEmptyArgs; +var init_command = __esm({ + "packages/playwright-core/src/tools/cli-daemon/command.ts"() { + "use strict"; + z28 = require("./utilsBundle").z; + kEmptyOptions = z28.object({}); + kEmptyArgs = z28.object({}); + } +}); + +// packages/playwright-core/src/tools/cli-client/minimist.ts +function minimist(args, opts) { + if (!opts) + opts = {}; + const bools = {}; + const strings = {}; + for (const key of toArray(opts.boolean)) + bools[key] = true; + for (const key of toArray(opts.string)) + strings[key] = true; + const argv = { _: [] }; + function setArg(key, val) { + if (argv[key] === void 0 || bools[key] || typeof argv[key] === "boolean") + argv[key] = val; + else if (Array.isArray(argv[key])) + argv[key].push(val); + else + argv[key] = [argv[key], val]; + } + let notFlags = []; + const doubleDashIndex = args.indexOf("--"); + if (doubleDashIndex !== -1) { + notFlags = args.slice(doubleDashIndex + 1); + args = args.slice(0, doubleDashIndex); + } + for (let i = 0; i < args.length; i++) { + const arg = args[i]; + let key; + let next; + if (/^--.+=/.test(arg)) { + const m = arg.match(/^--([^=]+)=([\s\S]*)$/); + key = m[1]; + if (bools[key]) + throw new Error(`boolean option '--${key}' should not be passed with '=value', use '--${key}' or '--no-${key}' instead`); + setArg(key, m[2]); + } else if (/^--no-.+/.test(arg)) { + key = arg.match(/^--no-(.+)/)[1]; + setArg(key, false); + } else if (/^--.+/.test(arg)) { + key = arg.match(/^--(.+)/)[1]; + next = args[i + 1]; + if (next !== void 0 && !/^(-|--)[^-]/.test(next) && !bools[key]) { + setArg(key, next); + i += 1; + } else if (/^(true|false)$/.test(next)) { + setArg(key, next === "true"); + i += 1; + } else { + setArg(key, strings[key] ? "" : true); + } + } else if (/^-[^-]+/.test(arg)) { + const letters = arg.slice(1, -1).split(""); + let broken = false; + for (let j = 0; j < letters.length; j++) { + next = arg.slice(j + 2); + if (next === "-") { + setArg(letters[j], next); + continue; + } + if (/[A-Za-z]/.test(letters[j]) && next[0] === "=") { + setArg(letters[j], next.slice(1)); + broken = true; + break; + } + if (/[A-Za-z]/.test(letters[j]) && /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) { + setArg(letters[j], next); + broken = true; + break; + } + if (letters[j + 1] && letters[j + 1].match(/\W/)) { + setArg(letters[j], arg.slice(j + 2)); + broken = true; + break; + } else { + setArg(letters[j], strings[letters[j]] ? "" : true); + } + } + key = arg.slice(-1)[0]; + if (!broken && key !== "-") { + if (args[i + 1] && !/^(-|--)[^-]/.test(args[i + 1]) && !bools[key]) { + setArg(key, args[i + 1]); + i += 1; + } else if (args[i + 1] && /^(true|false)$/.test(args[i + 1])) { + setArg(key, args[i + 1] === "true"); + i += 1; + } else { + setArg(key, strings[key] ? "" : true); + } + } + } else { + argv._.push(arg); + } + } + for (const k of notFlags) + argv._.push(k); + return argv; +} +function toArray(value2) { + if (!value2) + return []; + return Array.isArray(value2) ? value2 : [value2]; +} +var init_minimist = __esm({ + "packages/playwright-core/src/tools/cli-client/minimist.ts"() { + "use strict"; + } +}); + +// packages/playwright-core/src/tools/cli-daemon/commands.ts +var z29, elementTargetDescription2, numberArg, stringArrayArg, open3, attach, close2, detach, goto, goBack2, goForward2, reload2, pressKey, type2, keydown2, keyup2, mouseMove2, mouseDown2, mouseUp2, mouseWheel2, click2, doubleClick, drag2, drop2, fill, hover2, select, fileUpload, check2, uncheck2, snapshot2, generateLocator2, highlight2, evaluate3, dialogAccept, dialogDismiss, resize2, runCode2, tabList, tabNew, tabClose, tabSelect, stateLoad, stateSave, cookieList2, cookieGet2, cookieSet2, cookieDelete2, cookieClear2, localStorageList2, localStorageGet2, localStorageSet2, localStorageDelete2, localStorageClear2, sessionStorageList2, sessionStorageGet2, sessionStorageSet2, sessionStorageDelete2, sessionStorageClear2, routeMock, routeList2, unroute2, networkStateSet2, screenshot2, pdfSave, consoleList, networkRequests, filenameOption, networkRequest, networkRequestHeaders, networkRequestBody, networkResponseHeaders, networkResponseBody, tracingStart2, tracingStop2, videoStart2, videoStop2, videoChapter2, actionPositionArg, actionCursorArg, videoShowActions2, videoHideActions2, dashboardShow, resume2, stepOver, pauseAt, sessionList, sessionCloseAll, killAll, deleteData, configPrint, install, installBrowser, tray, commandsArray, commands; +var init_commands = __esm({ + "packages/playwright-core/src/tools/cli-daemon/commands.ts"() { + "use strict"; + init_command(); + z29 = require("./utilsBundle").z; + elementTargetDescription2 = "Exact target element reference from the page snapshot, or a unique element selector"; + numberArg = z29.preprocess((val, ctx) => { + const number = Number(val); + if (Number.isNaN(number)) { + ctx.issues.push({ + code: "custom", + message: `expected number, received '${val}'`, + input: val + }); + } + return number; + }, z29.number()); + stringArrayArg = z29.union([z29.string(), z29.array(z29.string())]).transform((v) => Array.isArray(v) ? v : [v]); + open3 = declareCommand({ + name: "open", + description: "Open the browser", + category: "core", + args: z29.object({ + url: z29.string().optional().describe("The URL to navigate to") + }), + options: z29.object({ + browser: z29.string().optional().describe("Browser or chrome channel to use, possible values: chrome, firefox, webkit, msedge."), + config: z29.string().optional().describe("Path to the configuration file, defaults to .playwright/cli.config.json"), + headed: z29.boolean().optional().describe("Run browser in headed mode"), + persistent: z29.boolean().optional().describe("Use persistent browser profile"), + profile: z29.string().optional().describe("Path to a persistent user data directory.") + }), + toolName: "", + toolParams: () => ({}) + }); + attach = declareCommand({ + name: "attach", + description: "Attach to a running Playwright browser", + category: "core", + args: z29.object({ + name: z29.string().optional().describe("Bound browser name to attach to") + }), + options: z29.object({ + cdp: z29.string().optional().describe("Connect to an existing browser via CDP endpoint URL."), + endpoint: z29.string().optional().describe("Playwright browser server endpoint to attach to."), + extension: z29.union([z29.boolean(), z29.string()]).optional().describe("Connect to browser extension, optionally specify browser name (e.g. --extension=chrome)"), + config: z29.string().optional().describe("Path to the configuration file, defaults to .playwright/cli.config.json"), + session: z29.string().optional().describe('Session name (defaults to bound browser name or "default")') + }), + toolName: "browser_snapshot", + toolParams: () => ({ filename: "" }) + }); + close2 = declareCommand({ + name: "close", + description: "Close the browser", + category: "core", + args: z29.object({}), + toolName: "", + toolParams: () => ({}) + }); + detach = declareCommand({ + name: "detach", + description: "Detach from an attached browser", + category: "core", + args: z29.object({}), + toolName: "", + toolParams: () => ({}) + }); + goto = declareCommand({ + name: "goto", + description: "Navigate to a URL", + category: "core", + args: z29.object({ + url: z29.string().describe("The URL to navigate to") + }), + toolName: "browser_navigate", + toolParams: ({ url: url2 }) => ({ url: url2 }) + }); + goBack2 = declareCommand({ + name: "go-back", + description: "Go back to the previous page", + category: "navigation", + args: z29.object({}), + toolName: "browser_navigate_back", + toolParams: () => ({}) + }); + goForward2 = declareCommand({ + name: "go-forward", + description: "Go forward to the next page", + category: "navigation", + args: z29.object({}), + toolName: "browser_navigate_forward", + toolParams: () => ({}) + }); + reload2 = declareCommand({ + name: "reload", + description: "Reload the current page", + category: "navigation", + args: z29.object({}), + toolName: "browser_reload", + toolParams: () => ({}) + }); + pressKey = declareCommand({ + name: "press", + description: "Press a key on the keyboard, `a`, `ArrowLeft`", + category: "keyboard", + args: z29.object({ + key: z29.string().describe("Name of the key to press or a character to generate, such as `ArrowLeft` or `a`") + }), + toolName: "browser_press_key", + toolParams: ({ key }) => ({ key }) + }); + type2 = declareCommand({ + name: "type", + description: "Type text into editable element", + category: "core", + args: z29.object({ + text: z29.string().describe("Text to type into the element") + }), + options: z29.object({ + submit: z29.boolean().optional().describe("Whether to submit entered text (press Enter after)") + }), + toolName: "browser_press_sequentially", + toolParams: ({ text: text2, submit }) => ({ text: text2, submit }) + }); + keydown2 = declareCommand({ + name: "keydown", + description: "Press a key down on the keyboard", + category: "keyboard", + args: z29.object({ + key: z29.string().describe("Name of the key to press or a character to generate, such as `ArrowLeft` or `a`") + }), + toolName: "browser_keydown", + toolParams: ({ key }) => ({ key }) + }); + keyup2 = declareCommand({ + name: "keyup", + description: "Press a key up on the keyboard", + category: "keyboard", + args: z29.object({ + key: z29.string().describe("Name of the key to press or a character to generate, such as `ArrowLeft` or `a`") + }), + toolName: "browser_keyup", + toolParams: ({ key }) => ({ key }) + }); + mouseMove2 = declareCommand({ + name: "mousemove", + description: "Move mouse to a given position", + category: "mouse", + args: z29.object({ + x: numberArg.describe("X coordinate"), + y: numberArg.describe("Y coordinate") + }), + toolName: "browser_mouse_move_xy", + toolParams: ({ x, y }) => ({ x, y }) + }); + mouseDown2 = declareCommand({ + name: "mousedown", + description: "Press mouse down", + category: "mouse", + args: z29.object({ + button: z29.string().optional().describe("Button to press, defaults to left") + }), + toolName: "browser_mouse_down", + toolParams: ({ button }) => ({ button }) + }); + mouseUp2 = declareCommand({ + name: "mouseup", + description: "Press mouse up", + category: "mouse", + args: z29.object({ + button: z29.string().optional().describe("Button to press, defaults to left") + }), + toolName: "browser_mouse_up", + toolParams: ({ button }) => ({ button }) + }); + mouseWheel2 = declareCommand({ + name: "mousewheel", + description: "Scroll mouse wheel", + category: "mouse", + args: z29.object({ + dx: numberArg.describe("X delta"), + dy: numberArg.describe("Y delta") + }), + toolName: "browser_mouse_wheel", + toolParams: ({ dx: deltaX, dy: deltaY }) => ({ deltaX, deltaY }) + }); + click2 = declareCommand({ + name: "click", + description: "Perform click on a web page", + category: "core", + args: z29.object({ + target: z29.string().describe(elementTargetDescription2), + button: z29.string().optional().describe("Button to click, defaults to left") + }), + options: z29.object({ + modifiers: stringArrayArg.optional().describe("Modifier key to press (repeatable)") + }), + toolName: "browser_click", + toolParams: ({ target, button, modifiers }) => ({ target, button, modifiers }) + }); + doubleClick = declareCommand({ + name: "dblclick", + description: "Perform double click on a web page", + category: "core", + args: z29.object({ + target: z29.string().describe(elementTargetDescription2), + button: z29.string().optional().describe("Button to click, defaults to left") + }), + options: z29.object({ + modifiers: stringArrayArg.optional().describe("Modifier key to press (repeatable)") + }), + toolName: "browser_click", + toolParams: ({ target, button, modifiers }) => ({ target, button, modifiers, doubleClick: true }) + }); + drag2 = declareCommand({ + name: "drag", + description: "Perform drag and drop between two elements", + category: "core", + args: z29.object({ + startTarget: z29.string().describe("Exact source element reference from the page snapshot, or a unique element selector"), + endTarget: z29.string().describe(elementTargetDescription2) + }), + toolName: "browser_drag", + toolParams: ({ startTarget, endTarget }) => ({ startTarget, endTarget }) + }); + drop2 = declareCommand({ + name: "drop", + description: "Drop files or data onto an element", + category: "core", + args: z29.object({ + target: z29.string().describe(elementTargetDescription2) + }), + options: z29.object({ + path: stringArrayArg.optional().describe("Absolute path to a file to drop onto the element (repeatable)"), + data: stringArrayArg.optional().describe('Data to drop in "mime/type=value" format, e.g. --data "text/plain=hello" (repeatable)') + }), + toolName: "browser_drop", + toolParams: ({ target, path: path59, data }) => { + let dataMap; + if (data) { + dataMap = {}; + for (const entry of data) { + const idx = entry.indexOf("="); + if (idx === -1) + throw new Error(`--data must be in "mime/type=value" format, got: ${entry}`); + dataMap[entry.slice(0, idx)] = entry.slice(idx + 1); + } + } + return { target, paths: path59, data: dataMap }; + } + }); + fill = declareCommand({ + name: "fill", + description: "Fill text into editable element", + category: "core", + args: z29.object({ + target: z29.string().describe(elementTargetDescription2), + text: z29.string().describe("Text to fill into the element") + }), + options: z29.object({ + submit: z29.boolean().optional().describe("Whether to submit entered text (press Enter after)") + }), + toolName: "browser_type", + toolParams: ({ target, text: text2, submit }) => ({ target, text: text2, submit }) + }); + hover2 = declareCommand({ + name: "hover", + description: "Hover over element on page", + category: "core", + args: z29.object({ + target: z29.string().describe(elementTargetDescription2) + }), + toolName: "browser_hover", + toolParams: ({ target }) => ({ target }) + }); + select = declareCommand({ + name: "select", + description: "Select an option in a dropdown", + category: "core", + args: z29.object({ + target: z29.string().describe(elementTargetDescription2), + val: z29.string().describe("Value to select in the dropdown") + }), + toolName: "browser_select_option", + toolParams: ({ target, val: value2 }) => ({ target, values: [value2] }) + }); + fileUpload = declareCommand({ + name: "upload", + description: "Upload one or multiple files", + category: "core", + args: z29.object({ + file: z29.string().describe("The absolute paths to the files to upload") + }), + toolName: "browser_file_upload", + toolParams: ({ file }) => ({ paths: [file] }) + }); + check2 = declareCommand({ + name: "check", + description: "Check a checkbox or radio button", + category: "core", + args: z29.object({ + target: z29.string().describe(elementTargetDescription2) + }), + toolName: "browser_check", + toolParams: ({ target }) => ({ target }) + }); + uncheck2 = declareCommand({ + name: "uncheck", + description: "Uncheck a checkbox or radio button", + category: "core", + args: z29.object({ + target: z29.string().describe(elementTargetDescription2) + }), + toolName: "browser_uncheck", + toolParams: ({ target }) => ({ target }) + }); + snapshot2 = declareCommand({ + name: "snapshot", + description: "Capture page snapshot to obtain element ref", + category: "core", + args: z29.object({ + target: z29.string().optional().describe("Element reference from the previous page snapshot, or a unique element selector for the root element to capture a partial snapshot instead of the whole page") + }), + options: z29.object({ + filename: z29.string().optional().describe("Save snapshot to markdown file instead of returning it in the response."), + depth: numberArg.optional().describe("Limit snapshot depth, unlimited by default."), + boxes: z29.boolean().optional().describe("Include each element's bounding box as [box=x,y,width,height] in the snapshot. Coordinates are viewport-relative, in CSS pixels (Element.getBoundingClientRect).") + }), + toolName: "browser_snapshot", + toolParams: ({ filename, target, depth, boxes }) => ({ filename, target, depth, boxes }) + }); + generateLocator2 = declareCommand({ + name: "generate-locator", + description: "Generate a Playwright locator for the given element", + category: "devtools", + args: z29.object({ + target: z29.string().describe(elementTargetDescription2) + }), + toolName: "browser_generate_locator", + toolParams: ({ target }) => ({ target }) + }); + highlight2 = declareCommand({ + name: "highlight", + description: "Show (or with --hide, remove) a highlight overlay for an element; `--hide` without a target hides all page highlights.", + category: "devtools", + args: z29.object({ + target: z29.string().optional().describe(elementTargetDescription2) + }), + options: z29.object({ + hide: z29.boolean().optional().describe("Hide a previously added highlight for this element, or all page highlights when no element is given"), + style: z29.string().optional().describe('Additional inline CSS applied to the highlight overlay, e.g. "outline: 2px dashed red"') + }), + toolName: ({ hide }) => hide ? "browser_hide_highlight" : "browser_highlight", + toolParams: ({ target, style }) => ({ target, style }) + }); + evaluate3 = declareCommand({ + name: "eval", + description: "Evaluate JavaScript expression on page or element", + category: "core", + args: z29.object({ + func: z29.string().describe("() => { /* code */ } or (element) => { /* code */ } when element is provided"), + target: z29.string().optional().describe(elementTargetDescription2) + }), + options: z29.object({ + filename: z29.string().optional().describe("Save evaluation result to a file instead of returning it in the response.") + }), + toolName: "browser_evaluate", + toolParams: ({ func, target, filename }) => ({ function: func, filename, target }) + }); + dialogAccept = declareCommand({ + name: "dialog-accept", + description: "Accept a dialog", + category: "core", + args: z29.object({ + prompt: z29.string().optional().describe("The text of the prompt in case of a prompt dialog.") + }), + toolName: "browser_handle_dialog", + toolParams: ({ prompt: promptText }) => ({ accept: true, promptText }) + }); + dialogDismiss = declareCommand({ + name: "dialog-dismiss", + description: "Dismiss a dialog", + category: "core", + args: z29.object({}), + toolName: "browser_handle_dialog", + toolParams: () => ({ accept: false }) + }); + resize2 = declareCommand({ + name: "resize", + description: "Resize the browser window", + category: "core", + args: z29.object({ + w: numberArg.describe("Width of the browser window"), + h: numberArg.describe("Height of the browser window") + }), + toolName: "browser_resize", + toolParams: ({ w: width, h: height }) => ({ width, height }) + }); + runCode2 = declareCommand({ + name: "run-code", + description: "Run Playwright code snippet", + category: "devtools", + args: z29.object({ + code: z29.string().optional().describe("A JavaScript function containing Playwright code to execute. It will be invoked with a single argument, page, which you can use for any page interaction.") + }), + options: z29.object({ + filename: z29.string().optional().describe("Load code from the specified file.") + }), + toolName: "browser_run_code_unsafe", + toolParams: ({ code, filename }) => ({ code, filename }) + }); + tabList = declareCommand({ + name: "tab-list", + description: "List all tabs", + category: "tabs", + args: z29.object({}), + toolName: "browser_tabs", + toolParams: () => ({ action: "list" }) + }); + tabNew = declareCommand({ + name: "tab-new", + description: "Create a new tab", + category: "tabs", + args: z29.object({ + url: z29.string().optional().describe("The URL to navigate to in the new tab. If omitted, the new tab will be blank.") + }), + toolName: "browser_tabs", + toolParams: ({ url: url2 }) => ({ action: "new", url: url2 }) + }); + tabClose = declareCommand({ + name: "tab-close", + description: "Close a browser tab", + category: "tabs", + args: z29.object({ + index: numberArg.optional().describe("Tab index. If omitted, current tab is closed.") + }), + toolName: "browser_tabs", + toolParams: ({ index }) => ({ action: "close", index }) + }); + tabSelect = declareCommand({ + name: "tab-select", + description: "Select a browser tab", + category: "tabs", + args: z29.object({ + index: numberArg.describe("Tab index") + }), + toolName: "browser_tabs", + toolParams: ({ index }) => ({ action: "select", index }) + }); + stateLoad = declareCommand({ + name: "state-load", + description: "Loads browser storage (authentication) state from a file", + category: "storage", + args: z29.object({ + filename: z29.string().describe("File name to load the storage state from.") + }), + toolName: "browser_set_storage_state", + toolParams: ({ filename }) => ({ filename }) + }); + stateSave = declareCommand({ + name: "state-save", + description: "Saves the current storage (authentication) state to a file", + category: "storage", + args: z29.object({ + filename: z29.string().optional().describe("File name to save the storage state to.") + }), + toolName: "browser_storage_state", + toolParams: ({ filename }) => ({ filename }) + }); + cookieList2 = declareCommand({ + name: "cookie-list", + description: "List all cookies (optionally filtered by domain/path)", + category: "storage", + raw: true, + args: z29.object({}), + options: z29.object({ + domain: z29.string().optional().describe("Filter cookies by domain"), + path: z29.string().optional().describe("Filter cookies by path") + }), + toolName: "browser_cookie_list", + toolParams: ({ domain, path: path59 }) => ({ domain, path: path59 }) + }); + cookieGet2 = declareCommand({ + name: "cookie-get", + description: "Get a specific cookie by name", + category: "storage", + raw: true, + args: z29.object({ + name: z29.string().describe("Cookie name") + }), + toolName: "browser_cookie_get", + toolParams: ({ name }) => ({ name }) + }); + cookieSet2 = declareCommand({ + name: "cookie-set", + description: "Set a cookie with optional flags", + category: "storage", + args: z29.object({ + name: z29.string().describe("Cookie name"), + value: z29.string().describe("Cookie value") + }), + options: z29.object({ + domain: z29.string().optional().describe("Cookie domain"), + path: z29.string().optional().describe("Cookie path"), + expires: numberArg.optional().describe("Cookie expiration as Unix timestamp"), + httpOnly: z29.boolean().optional().describe("Whether the cookie is HTTP only"), + secure: z29.boolean().optional().describe("Whether the cookie is secure"), + sameSite: z29.enum(["Strict", "Lax", "None"]).optional().describe("Cookie SameSite attribute") + }), + toolName: "browser_cookie_set", + toolParams: ({ name, value: value2, domain, path: path59, expires, httpOnly, secure, sameSite }) => ({ name, value: value2, domain, path: path59, expires, httpOnly, secure, sameSite }) + }); + cookieDelete2 = declareCommand({ + name: "cookie-delete", + description: "Delete a specific cookie", + category: "storage", + args: z29.object({ + name: z29.string().describe("Cookie name") + }), + toolName: "browser_cookie_delete", + toolParams: ({ name }) => ({ name }) + }); + cookieClear2 = declareCommand({ + name: "cookie-clear", + description: "Clear all cookies", + category: "storage", + args: z29.object({}), + toolName: "browser_cookie_clear", + toolParams: () => ({}) + }); + localStorageList2 = declareCommand({ + name: "localstorage-list", + description: "List all localStorage key-value pairs", + category: "storage", + raw: true, + args: z29.object({}), + toolName: "browser_localstorage_list", + toolParams: () => ({}) + }); + localStorageGet2 = declareCommand({ + name: "localstorage-get", + description: "Get a localStorage item by key", + category: "storage", + raw: true, + args: z29.object({ + key: z29.string().describe("Key to get") + }), + toolName: "browser_localstorage_get", + toolParams: ({ key }) => ({ key }) + }); + localStorageSet2 = declareCommand({ + name: "localstorage-set", + description: "Set a localStorage item", + category: "storage", + args: z29.object({ + key: z29.string().describe("Key to set"), + value: z29.string().describe("Value to set") + }), + toolName: "browser_localstorage_set", + toolParams: ({ key, value: value2 }) => ({ key, value: value2 }) + }); + localStorageDelete2 = declareCommand({ + name: "localstorage-delete", + description: "Delete a localStorage item", + category: "storage", + args: z29.object({ + key: z29.string().describe("Key to delete") + }), + toolName: "browser_localstorage_delete", + toolParams: ({ key }) => ({ key }) + }); + localStorageClear2 = declareCommand({ + name: "localstorage-clear", + description: "Clear all localStorage", + category: "storage", + args: z29.object({}), + toolName: "browser_localstorage_clear", + toolParams: () => ({}) + }); + sessionStorageList2 = declareCommand({ + name: "sessionstorage-list", + description: "List all sessionStorage key-value pairs", + category: "storage", + raw: true, + args: z29.object({}), + toolName: "browser_sessionstorage_list", + toolParams: () => ({}) + }); + sessionStorageGet2 = declareCommand({ + name: "sessionstorage-get", + description: "Get a sessionStorage item by key", + category: "storage", + raw: true, + args: z29.object({ + key: z29.string().describe("Key to get") + }), + toolName: "browser_sessionstorage_get", + toolParams: ({ key }) => ({ key }) + }); + sessionStorageSet2 = declareCommand({ + name: "sessionstorage-set", + description: "Set a sessionStorage item", + category: "storage", + args: z29.object({ + key: z29.string().describe("Key to set"), + value: z29.string().describe("Value to set") + }), + toolName: "browser_sessionstorage_set", + toolParams: ({ key, value: value2 }) => ({ key, value: value2 }) + }); + sessionStorageDelete2 = declareCommand({ + name: "sessionstorage-delete", + description: "Delete a sessionStorage item", + category: "storage", + args: z29.object({ + key: z29.string().describe("Key to delete") + }), + toolName: "browser_sessionstorage_delete", + toolParams: ({ key }) => ({ key }) + }); + sessionStorageClear2 = declareCommand({ + name: "sessionstorage-clear", + description: "Clear all sessionStorage", + category: "storage", + args: z29.object({}), + toolName: "browser_sessionstorage_clear", + toolParams: () => ({}) + }); + routeMock = declareCommand({ + name: "route", + description: "Mock network requests matching a URL pattern", + category: "network", + args: z29.object({ + pattern: z29.string().describe('URL pattern to match (e.g., "**/api/users")') + }), + options: z29.object({ + status: numberArg.optional().describe("HTTP status code (default: 200)"), + body: z29.string().optional().describe("Response body (text or JSON string)"), + ["content-type"]: z29.string().optional().describe("Content-Type header"), + header: stringArrayArg.optional().describe('Header to add in "Name: Value" format (repeatable)'), + ["remove-header"]: z29.string().optional().describe("Comma-separated header names to remove") + }), + toolName: "browser_route", + toolParams: ({ pattern, status, body, ["content-type"]: contentType, header: headers, ["remove-header"]: removeHeaders }) => ({ + pattern, + status, + body, + contentType, + headers, + removeHeaders + }) + }); + routeList2 = declareCommand({ + name: "route-list", + description: "List all active network routes", + category: "network", + raw: true, + args: z29.object({}), + toolName: "browser_route_list", + toolParams: () => ({}) + }); + unroute2 = declareCommand({ + name: "unroute", + description: "Remove routes matching a pattern (or all routes)", + category: "network", + args: z29.object({ + pattern: z29.string().optional().describe("URL pattern to unroute (omit to remove all)") + }), + toolName: "browser_unroute", + toolParams: ({ pattern }) => ({ pattern }) + }); + networkStateSet2 = declareCommand({ + name: "network-state-set", + description: "Set the browser network state to online or offline", + category: "network", + args: z29.object({ + state: z29.enum(["online", "offline"]).describe('Set to "offline" to simulate offline mode, "online" to restore network connectivity') + }), + toolName: "browser_network_state_set", + toolParams: ({ state }) => ({ state }) + }); + screenshot2 = declareCommand({ + name: "screenshot", + description: "screenshot of the current page or element", + category: "export", + args: z29.object({ + target: z29.string().optional().describe(elementTargetDescription2) + }), + options: z29.object({ + filename: z29.string().optional().describe("File name to save the screenshot to. Defaults to `page-{timestamp}.{png|jpeg}` if not specified."), + ["full-page"]: z29.boolean().optional().describe("When true, takes a screenshot of the full scrollable page, instead of the currently visible viewport.") + }), + toolName: "browser_take_screenshot", + toolParams: ({ target, filename, ["full-page"]: fullPage }) => ({ filename, target, fullPage }) + }); + pdfSave = declareCommand({ + name: "pdf", + description: "Save page as PDF", + category: "export", + args: z29.object({}), + options: z29.object({ + filename: z29.string().optional().describe("File name to save the pdf to. Defaults to `page-{timestamp}.pdf` if not specified.") + }), + toolName: "browser_pdf_save", + toolParams: ({ filename }) => ({ filename }) + }); + consoleList = declareCommand({ + name: "console", + description: "List console messages", + category: "devtools", + args: z29.object({ + ["min-level"]: z29.string().optional().describe('Level of the console messages to return. Each level includes the messages of more severe levels. Defaults to "info".') + }), + options: z29.object({ + clear: z29.boolean().optional().describe("Whether to clear the console list") + }), + toolName: ({ clear }) => clear ? "browser_console_clear" : "browser_console_messages", + toolParams: ({ ["min-level"]: level, clear }) => clear ? {} : { level } + }); + networkRequests = declareCommand({ + name: "requests", + description: "List all network requests since loading the page. Each request is numbered for use with the `request` command.", + category: "network", + args: z29.object({}), + options: z29.object({ + static: z29.boolean().optional().describe("Whether to include successful static resources like images, fonts, scripts, etc. Defaults to false."), + filter: z29.string().optional().describe('Only return requests whose URL matches this regexp (e.g. "/api/.*user").'), + clear: z29.boolean().optional().describe("Whether to clear the network list") + }), + toolName: ({ clear }) => clear ? "browser_network_clear" : "browser_network_requests", + toolParams: ({ static: s, filter, clear }) => clear ? {} : { static: s, filter } + }); + filenameOption = z29.string().optional().describe("Filename to save the result to. If not provided, output is returned as text."); + networkRequest = declareCommand({ + name: "request", + description: "Show full details (headers, body, response) of a single network request by its number from the `requests` command.", + category: "network", + args: z29.object({ + index: numberArg.describe("1-based number of the request as listed by `requests`") + }), + options: z29.object({ + filename: filenameOption + }), + toolName: "browser_network_request", + toolParams: ({ index, filename }) => ({ index, filename }) + }); + networkRequestHeaders = declareCommand({ + name: "request-headers", + description: "Print only the request headers for a single network request by its number from the `requests` command.", + category: "network", + raw: true, + args: z29.object({ + index: numberArg.describe("1-based number of the request as listed by `requests`") + }), + options: z29.object({ + filename: filenameOption + }), + toolName: "browser_network_request", + toolParams: ({ index, filename }) => ({ index, part: "request-headers", filename }) + }); + networkRequestBody = declareCommand({ + name: "request-body", + description: "Print only the request body for a single network request by its number from the `requests` command.", + category: "network", + raw: true, + args: z29.object({ + index: numberArg.describe("1-based number of the request as listed by `requests`") + }), + options: z29.object({ + filename: filenameOption + }), + toolName: "browser_network_request", + toolParams: ({ index, filename }) => ({ index, part: "request-body", filename }) + }); + networkResponseHeaders = declareCommand({ + name: "response-headers", + description: "Print only the response headers for a single network request by its number from the `requests` command.", + category: "network", + raw: true, + args: z29.object({ + index: numberArg.describe("1-based number of the request as listed by `requests`") + }), + options: z29.object({ + filename: filenameOption + }), + toolName: "browser_network_request", + toolParams: ({ index, filename }) => ({ index, part: "response-headers", filename }) + }); + networkResponseBody = declareCommand({ + name: "response-body", + description: "Print the response body for a single network request by its number from the `requests` command. Textual bodies are inlined; binary bodies are saved to a file and the path is printed.", + category: "network", + raw: true, + args: z29.object({ + index: numberArg.describe("1-based number of the request as listed by `requests`") + }), + options: z29.object({ + filename: filenameOption + }), + toolName: "browser_network_request", + toolParams: ({ index, filename }) => ({ index, part: "response-body", filename }) + }); + tracingStart2 = declareCommand({ + name: "tracing-start", + description: "Start trace recording", + category: "devtools", + args: z29.object({}), + toolName: "browser_start_tracing", + toolParams: () => ({}) + }); + tracingStop2 = declareCommand({ + name: "tracing-stop", + description: "Stop trace recording", + category: "devtools", + args: z29.object({}), + toolName: "browser_stop_tracing", + toolParams: () => ({}) + }); + videoStart2 = declareCommand({ + name: "video-start", + description: "Start video recording", + category: "devtools", + args: z29.object({ + filename: z29.string().optional().describe("Filename to save the video.") + }), + options: z29.object({ + size: z29.string().optional().describe('Video frame size, e.g. "800x600". If not specified, the size of the recorded video will fit 800x800.') + }), + toolName: "browser_start_video", + toolParams: ({ filename, size }) => { + const parsedSize = size ? size.split("x").map(Number) : void 0; + return { filename, size: parsedSize ? { width: parsedSize[0], height: parsedSize[1] } : void 0 }; + } + }); + videoStop2 = declareCommand({ + name: "video-stop", + description: "Stop video recording", + category: "devtools", + toolName: "browser_stop_video", + toolParams: () => ({}) + }); + videoChapter2 = declareCommand({ + name: "video-chapter", + description: "Add a chapter marker to the video recording", + category: "devtools", + args: z29.object({ + title: z29.string().describe("Chapter title.") + }), + options: z29.object({ + description: z29.string().optional().describe("Chapter description."), + duration: numberArg.optional().describe("Duration in milliseconds to show the chapter card.") + }), + toolName: "browser_video_chapter", + toolParams: ({ title, description, duration }) => ({ title, description, duration }) + }); + actionPositionArg = z29.enum(["top-left", "top", "top-right", "bottom-left", "bottom", "bottom-right"]); + actionCursorArg = z29.enum(["none", "pointer"]); + videoShowActions2 = declareCommand({ + name: "video-show-actions", + description: "Annotate subsequent CLI/MCP actions on the page with a callout that names the action and highlights the target element", + category: "devtools", + args: z29.object({}), + options: z29.object({ + duration: numberArg.optional().describe("How long each action annotation stays on screen, in milliseconds. Defaults to 500."), + position: actionPositionArg.optional().describe("Where to place the action title: top-left, top, top-right, bottom-left, bottom, bottom-right. Defaults to top-right."), + cursor: actionCursorArg.optional().describe('Cursor decoration: "pointer" (default) animates a mouse pointer between action points; "none" disables it.') + }), + toolName: "browser_video_show_actions", + toolParams: ({ duration, position, cursor }) => ({ duration, position, cursor }) + }); + videoHideActions2 = declareCommand({ + name: "video-hide-actions", + description: "Stop annotating actions performed on the page", + category: "devtools", + toolName: "browser_video_hide_actions", + toolParams: () => ({}) + }); + dashboardShow = declareCommand({ + name: "show", + description: "Show Playwright Dashboard", + category: "devtools", + raw: true, + args: z29.object({}), + options: z29.object({ + port: numberArg.optional().describe("Start as a blocking HTTP server on this port (use 0 for a random port)"), + host: z29.string().optional().describe("Host to bind to when using --port (defaults to localhost)"), + annotate: z29.boolean().optional().describe("Switch the dashboard into annotation mode."), + kill: z29.boolean().optional().describe("Kill the dashboard daemon.") + }), + toolName: ({ annotate: annotate2 }) => annotate2 ? "browser_annotate" : "", + toolParams: () => ({}) + }); + resume2 = declareCommand({ + name: "resume", + description: "Resume the test execution", + category: "devtools", + args: z29.object({}), + toolName: "browser_resume", + toolParams: ({ step }) => ({ step }) + }); + stepOver = declareCommand({ + name: "step-over", + description: "Step over the next call in the test", + category: "devtools", + args: z29.object({}), + toolName: "browser_resume", + toolParams: ({}) => ({ step: true }) + }); + pauseAt = declareCommand({ + name: "pause-at", + description: "Run the test up to a specific location and pause there", + category: "devtools", + args: z29.object({ + location: z29.string().describe('Location to pause at. Format is :, e.g. "example.spec.ts:42".') + }), + toolName: "browser_resume", + toolParams: ({ location: location2 }) => ({ location: location2 }) + }); + sessionList = declareCommand({ + name: "list", + description: "List browser sessions", + category: "browsers", + args: z29.object({}), + options: z29.object({ + all: z29.boolean().optional().describe("List all browser sessions across all workspaces") + }), + toolName: "", + toolParams: () => ({}) + }); + sessionCloseAll = declareCommand({ + name: "close-all", + description: "Close all browser sessions", + category: "browsers", + toolName: "", + toolParams: () => ({}) + }); + killAll = declareCommand({ + name: "kill-all", + description: "Forcefully kill all browser sessions (for stale/zombie processes)", + category: "browsers", + toolName: "", + toolParams: () => ({}) + }); + deleteData = declareCommand({ + name: "delete-data", + description: "Delete session data", + category: "core", + toolName: "", + toolParams: () => ({}) + }); + configPrint = declareCommand({ + name: "config-print", + description: "Print the final resolved config after merging CLI options, environment variables and config file.", + category: "config", + hidden: true, + toolName: "browser_get_config", + toolParams: () => ({}) + }); + install = declareCommand({ + name: "install", + description: "Initialize workspace", + category: "install", + args: z29.object({}), + options: z29.object({ + skills: z29.string().optional().describe("Install skills, possible values: claude (default), agents.") + }), + toolName: "", + toolParams: () => ({}) + }); + installBrowser = declareCommand({ + name: "install-browser", + description: "Install browser", + category: "install", + args: z29.object({ + browser: z29.string().optional().describe("Browser to install") + }), + options: z29.object({ + ["with-deps"]: z29.boolean().optional().describe("Install system dependencies for browsers"), + ["dry-run"]: z29.boolean().optional().describe("Do not execute installation, only print information"), + list: z29.boolean().optional().describe("Prints list of browsers from all Playwright installations"), + force: z29.boolean().optional().describe("Force reinstall of already installed browsers"), + ["only-shell"]: z29.boolean().optional().describe("Only install headless shell when installing Chromium"), + ["no-shell"]: z29.boolean().optional().describe("Do not install Chromium headless shell") + }), + toolName: "", + toolParams: () => ({}) + }); + tray = declareCommand({ + name: "tray", + description: "Run tray", + category: "config", + hidden: true, + toolName: "", + toolParams: () => ({}) + }); + commandsArray = [ + // core category + open3, + attach, + close2, + detach, + goto, + type2, + click2, + doubleClick, + fill, + drag2, + drop2, + hover2, + select, + fileUpload, + check2, + uncheck2, + snapshot2, + evaluate3, + consoleList, + dialogAccept, + dialogDismiss, + resize2, + runCode2, + deleteData, + // navigation category + goBack2, + goForward2, + reload2, + // keyboard category + pressKey, + keydown2, + keyup2, + // mouse category + mouseMove2, + mouseDown2, + mouseUp2, + mouseWheel2, + // export category + screenshot2, + pdfSave, + // tabs category + tabList, + tabNew, + tabClose, + tabSelect, + // storage category + stateLoad, + stateSave, + cookieList2, + cookieGet2, + cookieSet2, + cookieDelete2, + cookieClear2, + localStorageList2, + localStorageGet2, + localStorageSet2, + localStorageDelete2, + localStorageClear2, + sessionStorageList2, + sessionStorageGet2, + sessionStorageSet2, + sessionStorageDelete2, + sessionStorageClear2, + // network category + networkRequests, + networkRequest, + networkRequestHeaders, + networkRequestBody, + networkResponseHeaders, + networkResponseBody, + routeMock, + routeList2, + unroute2, + networkStateSet2, + // config category + configPrint, + // install category + install, + installBrowser, + // devtools category + tracingStart2, + tracingStop2, + videoStart2, + videoStop2, + videoChapter2, + videoShowActions2, + videoHideActions2, + dashboardShow, + pauseAt, + resume2, + stepOver, + generateLocator2, + highlight2, + // session category + sessionList, + sessionCloseAll, + killAll, + // Hidden commands + tray + ]; + commands = Object.fromEntries(commandsArray.map((cmd) => [cmd.name, cmd])); + } +}); + +// packages/playwright-core/src/tools/trace/traceSnapshot.ts +async function traceSnapshot(actionId, options) { + const trace = await loadTrace(); + const action = trace.resolveActionId(actionId); + if (!action) { + console.error(`Action '${actionId}' not found.`); + process.exitCode = 1; + return; + } + const pageId4 = action.pageId; + if (!pageId4) { + console.error(`Action '${actionId}' has no associated page.`); + process.exitCode = 1; + return; + } + const callId = action.callId; + const storage = trace.loader.storage(); + let snapshotName; + let renderer; + if (options.name) { + snapshotName = options.name; + renderer = storage.snapshotByName(pageId4, `${snapshotName}@${callId}`); + } else { + for (const candidate of ["input", "before", "after"]) { + renderer = storage.snapshotByName(pageId4, `${candidate}@${callId}`); + if (renderer) { + snapshotName = candidate; + break; + } + } + } + if (!renderer || !snapshotName) { + console.error(`No snapshot found for action '${actionId}'.`); + process.exitCode = 1; + return; + } + const snapshotKey = `${snapshotName}@${callId}`; + const server = await serveTraceSnapshot(storage, trace.loader, pageId4, snapshotKey); + if (options.serve) { + console.log(`Serving snapshot at ${server.url}`); + await new Promise(() => { + }); + return; + } + await runCommandOnSnapshot(server, options.browserArgs || []); +} +async function serveTraceSnapshot(storage, loader, pageId4, snapshotKey) { + const snapshotServer = new SnapshotServer(storage, (sha1) => loader.resourceForSha1(sha1)); + const httpServer = new HttpServer(); + httpServer.routePrefix("/snapshot/", (request2, response2) => { + const url2 = new URL("http://localhost" + request2.url); + const pageOrFrameId = url2.pathname.substring("/snapshot/".length); + const searchParams = url2.searchParams; + searchParams.set("name", snapshotKey); + const snapshotResponse = snapshotServer.serveSnapshot(pageOrFrameId, searchParams, url2.href); + response2.statusCode = snapshotResponse.status; + snapshotResponse.headers.forEach((value2, key) => response2.setHeader(key, value2)); + snapshotResponse.text().then((text2) => response2.end(text2)); + return true; + }); + httpServer.routePath("/__pwsnapshot/sw.js", (_request, response2) => { + response2.statusCode = 200; + response2.setHeader("Content-Type", "application/javascript"); + response2.setHeader("Service-Worker-Allowed", "/"); + response2.end(`(${snapshotServiceWorker.toString()})();`); + return true; + }); + httpServer.routePath("/__pwsnapshot/resource", (request2, response2) => { + const url2 = new URL("http://localhost" + request2.url); + const frame = new URL(url2.searchParams.get("frame")); + const snapshotUrl2 = "http://localhost" + frame.pathname + frame.search; + const requestUrl = url2.searchParams.get("url"); + const method = url2.searchParams.get("method"); + snapshotServer.serveResource([requestUrl], method, snapshotUrl2).then(async (resp) => { + response2.statusCode = resp.status; + resp.headers.forEach((value2, key) => response2.appendHeader(key, value2)); + response2.end(Buffer.from(await resp.arrayBuffer())); + }).catch(() => { + response2.statusCode = 500; + response2.end(); + }); + return true; + }); + const startTime = Date.now(); + const snapshotUrl = `/snapshot/${pageId4}?name=${encodeURIComponent(snapshotKey)}`; + httpServer.routePrefix("/", (_request, response2) => { + response2.statusCode = 200; + response2.setHeader("Content-Type", "text/html; charset=utf-8"); + response2.end(``); + return true; + }); + await httpServer.start({ preferredPort: 0 }); + return { url: httpServer.urlPrefix("human-readable"), stop: () => httpServer.stop() }; +} +function snapshotServiceWorker() { + const sw = self; + sw.addEventListener("install", () => sw.skipWaiting()); + sw.addEventListener("activate", (event) => event.waitUntil(sw.clients.claim())); + sw.addEventListener("fetch", (event) => { + const requestUrl = new URL(event.request.url); + if (requestUrl.origin === sw.location.origin) + return; + event.respondWith((async () => { + const client = event.clientId ? await sw.clients.get(event.clientId) : null; + if (!client) + return new Response("No client", { status: 500 }); + const params2 = new URLSearchParams({ + frame: client.url, + url: event.request.url, + method: event.request.method + }); + return fetch("/__pwsnapshot/resource?" + params2.toString()); + })()); + }); +} +async function bootstrapSnapshotPage(swUrl, snapshotUrl) { + await navigator.serviceWorker.register(swUrl, { scope: "/" }); + await navigator.serviceWorker.ready; + if (!navigator.serviceWorker.controller) + await new Promise((resolve) => navigator.serviceWorker.addEventListener("controllerchange", resolve, { once: true })); + location.replace(snapshotUrl); +} +async function runCommandOnSnapshot(server, browserArgs) { + const browser = await playwright.chromium.launch({ headless: true }); + const context2 = await browser.newContext(); + const page = await context2.newPage(); + await page.goto(server.url); + await page.waitForURL((url2) => new URL(url2).pathname.startsWith("/snapshot/")); + const backend = new BrowserBackend({ + snapshot: { mode: "full" }, + outputMode: "file", + skillMode: true + }, context2, browserTools); + await backend.initialize({ cwd: process.cwd(), clientName: "playwright-cli" }); + try { + if (!browserArgs.length) + browserArgs = ["snapshot"]; + const args = minimist(browserArgs, { string: ["_"] }); + const command = commands[args._[0]]; + if (!command) + throw new Error(`Unknown command: ${args._[0]}`); + const { toolName, toolParams } = parseCommand(command, args); + const result2 = await backend.callTool(toolName, toolParams); + const text2 = result2.content[0]?.type === "text" ? result2.content[0].text : void 0; + if (text2) + console.log(text2); + if (result2.isError) { + console.error("Command failed."); + process.exitCode = 1; + } + } catch (e) { + console.error(e.message); + process.exitCode = 1; + } finally { + await server.stop().catch((e) => console.error(e)); + await gracefullyCloseAll(); + } +} +var init_traceSnapshot = __esm({ + "packages/playwright-core/src/tools/trace/traceSnapshot.ts"() { + "use strict"; + init_processLauncher(); + init_httpServer(); + init_snapshotServer(); + init_browserBackend(); + init_tools(); + init_inprocess(); + init_command(); + init_minimist(); + init_commands(); + init_traceUtils2(); + } +}); + +// packages/playwright-core/src/tools/trace/traceScreenshot.ts +async function traceScreenshot(actionId, options) { + const trace = await loadTrace(); + const action = trace.resolveActionId(actionId); + if (!action) { + console.error(`Action '${actionId}' not found.`); + process.exitCode = 1; + return; + } + const pageId4 = action.pageId; + if (!pageId4) { + console.error(`Action '${actionId}' has no associated page.`); + process.exitCode = 1; + return; + } + const callId = action.callId; + const storage = trace.loader.storage(); + const snapshotNames = ["input", "before", "after"]; + let sha1; + for (const name of snapshotNames) { + const renderer = storage.snapshotByName(pageId4, `${name}@${callId}`); + sha1 = renderer?.closestScreenshot(); + if (sha1) + break; + } + if (!sha1) { + console.error(`No screenshot found for action '${actionId}'.`); + process.exitCode = 1; + return; + } + const blob = await trace.loader.resourceForSha1(sha1); + if (!blob) { + console.error(`Screenshot resource not found.`); + process.exitCode = 1; + return; + } + const defaultName = `screenshot-${actionId}.png`; + const buffer = Buffer.from(await blob.arrayBuffer()); + const outFile = await saveOutputFile(defaultName, buffer, options.output); + console.log(` Screenshot saved to ${outFile}`); +} +var init_traceScreenshot = __esm({ + "packages/playwright-core/src/tools/trace/traceScreenshot.ts"() { + "use strict"; + init_traceUtils2(); + } +}); + +// packages/playwright-core/src/tools/trace/traceAttachments.ts +async function traceAttachments() { + const trace = await loadTrace(); + if (!trace.model.attachments.length) { + console.log(" No attachments"); + return; + } + console.log(` ${"#".padStart(4)} ${"Name".padEnd(40)} ${"Content-Type".padEnd(30)} ${"Action".padEnd(8)}`); + console.log(` ${"\u2500".repeat(4)} ${"\u2500".repeat(40)} ${"\u2500".repeat(30)} ${"\u2500".repeat(8)}`); + for (let i = 0; i < trace.model.attachments.length; i++) { + const a = trace.model.attachments[i]; + const actionOrdinal = trace.callIdToOrdinal.get(a.callId); + console.log(` ${(i + 1 + ".").padStart(4)} ${a.name.padEnd(40)} ${a.contentType.padEnd(30)} ${(actionOrdinal !== void 0 ? String(actionOrdinal) : a.callId).padEnd(8)}`); + } +} +async function traceAttachment(attachmentId, options) { + const trace = await loadTrace(); + const ordinal = parseInt(attachmentId, 10); + const attachment = !isNaN(ordinal) && ordinal >= 1 && ordinal <= trace.model.attachments.length ? trace.model.attachments[ordinal - 1] : void 0; + if (!attachment) { + console.error(`Attachment '${attachmentId}' not found. Use 'trace attachments' to see available attachments.`); + process.exitCode = 1; + return; + } + let content; + if (attachment.sha1) { + const blob = await trace.loader.resourceForSha1(attachment.sha1); + if (blob) + content = Buffer.from(await blob.arrayBuffer()); + } else if (attachment.base64) { + content = Buffer.from(attachment.base64, "base64"); + } + if (!content) { + console.error(`Could not extract attachment content.`); + process.exitCode = 1; + return; + } + const outFile = await saveOutputFile(attachment.name, content, options.output); + console.log(` Attachment saved to ${outFile}`); +} +var init_traceAttachments = __esm({ + "packages/playwright-core/src/tools/trace/traceAttachments.ts"() { + "use strict"; + init_traceUtils2(); + } +}); + +// packages/playwright-core/src/tools/trace/installSkill.ts +async function installSkill() { + const cwd = process.cwd(); + const skillSource = libPath("tools", "trace", "SKILL.md"); + const destDir = import_path42.default.join(cwd, ".claude", "skills", "playwright-trace"); + await import_fs47.default.promises.mkdir(destDir, { recursive: true }); + const destFile = import_path42.default.join(destDir, "SKILL.md"); + await import_fs47.default.promises.copyFile(skillSource, destFile); + console.log(`\u2705 Skill installed to \`${import_path42.default.relative(cwd, destFile)}\`.`); +} +var import_fs47, import_path42; +var init_installSkill = __esm({ + "packages/playwright-core/src/tools/trace/installSkill.ts"() { + "use strict"; + import_fs47 = __toESM(require("fs")); + import_path42 = __toESM(require("path")); + init_package(); + } +}); + +// packages/playwright-core/src/tools/trace/traceCli.ts +function addTraceCommands(program4, logErrorAndExit2) { + const traceCommand = program4.command("trace").description("inspect trace files from the command line"); + traceCommand.command("open ").description("extract trace file for inspection").action(async (trace) => { + traceOpen(trace).catch(logErrorAndExit2); + }); + traceCommand.command("close").description("remove extracted trace data").action(async () => { + closeTrace().catch(logErrorAndExit2); + }); + traceCommand.command("actions").description("list actions in the trace").option("--grep ", "filter actions by title pattern").option("--errors-only", "only show failed actions").action(async (options) => { + traceActions(options).catch(logErrorAndExit2); + }); + traceCommand.command("action ").description("show details of a specific action").action(async (actionId) => { + traceAction(actionId).catch(logErrorAndExit2); + }); + traceCommand.command("requests").description("show network requests").option("--grep ", "filter by URL pattern").option("--method ", "filter by HTTP method").option("--status ", "filter by status code").option("--failed", "only show failed requests (status >= 400)").action(async (options) => { + traceRequests(options).catch(logErrorAndExit2); + }); + traceCommand.command("request ").description("show details of a specific network request").action(async (requestId) => { + traceRequest(requestId).catch(logErrorAndExit2); + }); + traceCommand.command("console").description("show console messages").option("--errors-only", "only show errors").option("--warnings", "show errors and warnings").option("--browser", "only browser console messages").option("--stdio", "only stdout/stderr").action(async (options) => { + traceConsole(options).catch(logErrorAndExit2); + }); + traceCommand.command("errors").description("show errors with stack traces").action(async () => { + traceErrors().catch(logErrorAndExit2); + }); + traceCommand.command("snapshot ").description("run a playwright-cli command against a DOM snapshot").option("--name ", "snapshot phase: before, input, or after").option("--serve", "serve snapshot on localhost and keep running").allowUnknownOption(true).allowExcessArguments(true).action(async (actionId, options, cmd) => { + try { + const browserArgs = cmd.args.slice(1); + await traceSnapshot(actionId, { ...options, browserArgs }); + } catch (e) { + logErrorAndExit2(e); + } + }); + traceCommand.command("screenshot ").description("save screencast screenshot for an action").option("-o, --output ", "output file path").action(async (actionId, options) => { + traceScreenshot(actionId, options).catch(logErrorAndExit2); + }); + traceCommand.command("attachments").description("list trace attachments").action(async () => { + traceAttachments().catch(logErrorAndExit2); + }); + traceCommand.command("attachment ").description("extract a trace attachment by its number").option("-o, --output ", "output file path").action(async (attachmentId, options) => { + traceAttachment(attachmentId, options).catch(logErrorAndExit2); + }); + traceCommand.command("install-skill").description("install SKILL.md for LLM integration").action(async () => { + installSkill().catch(logErrorAndExit2); + }); +} +var init_traceCli = __esm({ + "packages/playwright-core/src/tools/trace/traceCli.ts"() { + "use strict"; + init_traceOpen(); + init_traceUtils2(); + init_traceActions(); + init_traceActions(); + init_traceRequests(); + init_traceRequests(); + init_traceConsole(); + init_traceErrors(); + init_traceSnapshot(); + init_traceScreenshot(); + init_traceAttachments(); + init_traceAttachments(); + init_installSkill(); + } +}); + +// packages/playwright-core/src/cli/driver.ts +function printApiJson() { + console.log(JSON.stringify(require(import_path43.default.join(packageRoot, "api.json")))); +} +function runDriver() { + const dispatcherConnection = new DispatcherConnection(); + new RootDispatcher(dispatcherConnection, async (rootScope, { sdkLanguage }) => { + const playwright2 = createPlaywright({ sdkLanguage, isClientCollocatedWithServer: true }); + return new PlaywrightDispatcher(rootScope, playwright2); + }); + const transport = new PipeTransport(process.stdout, process.stdin); + transport.onmessage = (message) => dispatcherConnection.dispatch(JSON.parse(message)); + const isJavaScriptLanguageBinding = !process.env.PW_LANG_NAME || process.env.PW_LANG_NAME === "javascript"; + const replacer = !isJavaScriptLanguageBinding && String.prototype.toWellFormed ? (key, value2) => { + if (typeof value2 === "string") + return value2.toWellFormed(); + return value2; + } : void 0; + dispatcherConnection.onmessage = (message) => transport.send(JSON.stringify(message, replacer)); + transport.onclose = () => { + dispatcherConnection.onmessage = () => { + }; + gracefullyProcessExitDoNotHang(0); + }; + process.on("SIGINT", () => { + }); +} +async function runServer(options) { + const { + port, + host, + path: path59 = "/", + maxConnections = Infinity, + extension, + artifactsDir, + unsafe + } = options; + const server = new PlaywrightServer({ mode: extension ? "extension" : "default", path: path59, maxConnections, artifactsDir, unsafe }); + const wsEndpoint = await server.listen(port, host); + process.on("exit", () => server.close().catch(console.error)); + console.log("Listening on " + wsEndpoint); + process.stdin.on("close", () => gracefullyProcessExitDoNotHang(0)); +} +async function launchBrowserServer(browserName, configFile) { + let options = {}; + if (configFile) + options = JSON.parse(import_fs48.default.readFileSync(configFile).toString()); + const browserType = playwright[browserName]; + const server = await browserType.launchServer(options); + console.log(server.wsEndpoint()); +} +var import_fs48, import_path43; +var init_driver = __esm({ + "packages/playwright-core/src/cli/driver.ts"() { + "use strict"; + import_fs48 = __toESM(require("fs")); + import_path43 = __toESM(require("path")); + init_pipeTransport(); + init_processLauncher(); + init_package(); + init_inprocess(); + init_playwrightServer(); + init_server(); + } +}); + +// packages/playwright-core/src/cli/installActions.ts +function printInstalledBrowsers(browsers) { + const browserPaths = /* @__PURE__ */ new Set(); + for (const browser of browsers) + browserPaths.add(browser.browserPath); + console.log(` Browsers:`); + for (const browserPath of [...browserPaths].sort()) + console.log(` ${browserPath}`); + console.log(` References:`); + const references = /* @__PURE__ */ new Set(); + for (const browser of browsers) + references.add(browser.referenceDir); + for (const reference of [...references].sort()) + console.log(` ${reference}`); +} +function printGroupedByPlaywrightVersion(browsers) { + const dirToVersion = /* @__PURE__ */ new Map(); + for (const browser of browsers) { + if (dirToVersion.has(browser.referenceDir)) + continue; + const packageJSON2 = require(import_path44.default.join(browser.referenceDir, "package.json")); + const version3 = packageJSON2.version; + dirToVersion.set(browser.referenceDir, version3); + } + const groupedByPlaywrightMinorVersion = /* @__PURE__ */ new Map(); + for (const browser of browsers) { + const version3 = dirToVersion.get(browser.referenceDir); + let entries = groupedByPlaywrightMinorVersion.get(version3); + if (!entries) { + entries = []; + groupedByPlaywrightMinorVersion.set(version3, entries); + } + entries.push(browser); + } + const sortedVersions = [...groupedByPlaywrightMinorVersion.keys()].sort((a, b) => { + const aComponents = a.split("."); + const bComponents = b.split("."); + const aMajor = parseInt(aComponents[0], 10); + const bMajor = parseInt(bComponents[0], 10); + if (aMajor !== bMajor) + return aMajor - bMajor; + const aMinor = parseInt(aComponents[1], 10); + const bMinor = parseInt(bComponents[1], 10); + if (aMinor !== bMinor) + return aMinor - bMinor; + return aComponents.slice(2).join(".").localeCompare(bComponents.slice(2).join(".")); + }); + for (const version3 of sortedVersions) { + console.log(` +Playwright version: ${version3}`); + printInstalledBrowsers(groupedByPlaywrightMinorVersion.get(version3)); + } +} +async function markDockerImage(dockerImageNameTemplate) { + assert(dockerImageNameTemplate, "dockerImageNameTemplate is required"); + await writeDockerVersion(dockerImageNameTemplate); +} +async function installBrowsers(args, options) { + if (options.progress === false) + process.env.PLAYWRIGHT_DOWNLOAD_NO_PROGRESS = "1"; + if (isLikelyNpxGlobal()) { + console.error(wrapInASCIIBox([ + `WARNING: It looks like you are running 'npx playwright install' without first`, + `installing your project's dependencies.`, + ``, + `To avoid unexpected behavior, please install your dependencies first, and`, + `then run Playwright's install command:`, + ``, + ` npm install`, + ` npx playwright install`, + ``, + `If your project does not yet depend on Playwright, first install the`, + `applicable npm package (most commonly @playwright/test), and`, + `then run Playwright's install command to download the browsers:`, + ``, + ` npm install @playwright/test`, + ` npx playwright install`, + `` + ].join("\n"), 1)); + } + if (options.shell === false && options.onlyShell) + throw new Error(`Only one of --no-shell and --only-shell can be specified`); + const shell = options.shell === false ? "no" : options.onlyShell ? "only" : void 0; + const executables = registry.resolveBrowsers(args, { shell }); + if (options.withDeps) + await registry.installDeps(executables, !!options.dryRun); + if (options.dryRun && options.list) + throw new Error(`Only one of --dry-run and --list can be specified`); + if (options.dryRun) { + for (const executable of executables) { + console.log(registry.calculateDownloadTitle(executable)); + console.log(` Install location: ${executable.directory ?? ""}`); + if (executable.downloadURLs?.length) { + const [url2, ...fallbacks] = executable.downloadURLs; + console.log(` Download url: ${url2}`); + for (let i = 0; i < fallbacks.length; ++i) + console.log(` Download fallback ${i + 1}: ${fallbacks[i]}`); + } + console.log(``); + } + } else if (options.list) { + const browsers = await registry.listInstalledBrowsers(); + printGroupedByPlaywrightVersion(browsers); + } else { + await registry.install(executables, { force: options.force }); + await registry.validateHostRequirementsForExecutablesIfNeeded(executables, process.env.PW_LANG_NAME || "javascript").catch((e) => { + e.name = "Playwright Host validation warning"; + console.error(e); + }); + } +} +async function uninstallBrowsers(options) { + delete process.env.PLAYWRIGHT_SKIP_BROWSER_GC; + await registry.uninstall(!!options.all).then(({ numberOfBrowsersLeft }) => { + if (!options.all && numberOfBrowsersLeft > 0) { + console.log("Successfully uninstalled Playwright browsers for the current Playwright installation."); + console.log(`There are still ${numberOfBrowsersLeft} browsers left, used by other Playwright installations. +To uninstall Playwright browsers for all installations, re-run with --all flag.`); + } + }); +} +async function installDeps(args, options) { + await registry.installDeps(registry.resolveBrowsers(args, {}), !!options.dryRun); +} +var import_path44; +var init_installActions = __esm({ + "packages/playwright-core/src/cli/installActions.ts"() { + "use strict"; + import_path44 = __toESM(require("path")); + init_ascii(); + init_env(); + init_assert(); + init_registry(); + } +}); + +// packages/playwright-core/src/cli/browserActions.ts +async function launchContext(options, extraOptions) { + validateOptions(options); + const browserType = lookupBrowserType(options); + const launchOptions = extraOptions; + if (options.channel) + launchOptions.channel = options.channel; + launchOptions.handleSIGINT = false; + const contextOptions = ( + // Copy the device descriptor since we have to compare and modify the options. + options.device ? { ...playwright.devices[options.device] } : {} + ); + if (!extraOptions.headless) + contextOptions.deviceScaleFactor = import_os20.default.platform() === "darwin" ? 2 : 1; + if (browserType.name() === "webkit" && process.platform === "linux") { + delete contextOptions.hasTouch; + delete contextOptions.isMobile; + } + if (options.blockServiceWorkers) + contextOptions.serviceWorkers = "block"; + if (options.proxyServer) { + launchOptions.proxy = { + server: options.proxyServer + }; + if (options.proxyBypass) + launchOptions.proxy.bypass = options.proxyBypass; + } + if (options.viewportSize) { + try { + const [width, height] = options.viewportSize.split(",").map((n) => +n); + if (isNaN(width) || isNaN(height)) + throw new Error("bad values"); + contextOptions.viewport = { width, height }; + } catch (e) { + throw new Error('Invalid viewport size format: use "width,height", for example --viewport-size="800,600"'); + } + } + if (options.geolocation) { + try { + const [latitude, longitude] = options.geolocation.split(",").map((n) => parseFloat(n.trim())); + contextOptions.geolocation = { + latitude, + longitude + }; + } catch (e) { + throw new Error('Invalid geolocation format, should be "lat,long". For example --geolocation="37.819722,-122.478611"'); + } + contextOptions.permissions = ["geolocation"]; + } + if (options.userAgent) + contextOptions.userAgent = options.userAgent; + if (options.lang) + contextOptions.locale = options.lang; + if (options.colorScheme) + contextOptions.colorScheme = options.colorScheme; + if (options.timezone) + contextOptions.timezoneId = options.timezone; + if (options.loadStorage) + contextOptions.storageState = options.loadStorage; + if (options.ignoreHttpsErrors) + contextOptions.ignoreHTTPSErrors = true; + if (options.saveHar) { + contextOptions.recordHar = { path: import_path45.default.resolve(process.cwd(), options.saveHar), mode: "minimal" }; + if (options.saveHarGlob) + contextOptions.recordHar.urlFilter = options.saveHarGlob; + contextOptions.serviceWorkers = "block"; + } + let browser; + let context2; + if (options.userDataDir) { + context2 = await browserType.launchPersistentContext(options.userDataDir, { ...launchOptions, ...contextOptions }); + browser = context2.browser(); + } else { + browser = await browserType.launch(launchOptions); + context2 = await browser.newContext(contextOptions); + } + let closingBrowser = false; + async function closeBrowser() { + if (closingBrowser) + return; + closingBrowser = true; + if (options.saveStorage) + await context2.storageState({ path: options.saveStorage }).catch((e) => null); + if (options.saveHar) + await context2.close(); + await browser.close(); + } + context2.on("page", (page) => { + page.on("dialog", () => { + }); + page.on("close", () => { + const hasPage = browser.contexts().some((context3) => context3.pages().length > 0); + if (hasPage) + return; + closeBrowser().catch(() => { + }); + }); + }); + process.on("SIGINT", async () => { + await closeBrowser(); + gracefullyProcessExitDoNotHang(130); + }); + const timeout = options.timeout ? parseInt(options.timeout, 10) : 0; + context2.setDefaultTimeout(timeout); + context2.setDefaultNavigationTimeout(timeout); + delete launchOptions.headless; + delete launchOptions.executablePath; + delete launchOptions.handleSIGINT; + delete contextOptions.deviceScaleFactor; + return { browser, browserName: browserType.name(), context: context2, contextOptions, launchOptions, closeBrowser }; +} +async function openPage(context2, url2) { + let page = context2.pages()[0]; + if (!page) + page = await context2.newPage(); + if (url2) { + if (import_fs49.default.existsSync(url2)) + url2 = "file://" + import_path45.default.resolve(url2); + else if (!url2.startsWith("http") && !url2.startsWith("file://") && !url2.startsWith("about:") && !url2.startsWith("data:")) + url2 = "http://" + url2; + await page.goto(url2); + } + return page; +} +async function open4(options, url2) { + const { context: context2 } = await launchContext(options, { headless: !!process.env.PWTEST_CLI_HEADLESS, executablePath: process.env.PWTEST_CLI_EXECUTABLE_PATH }); + await context2._exposeConsoleApi(); + await openPage(context2, url2); +} +async function codegen(options, url2) { + const { target: language, output: outputFile2, testIdAttribute: testIdAttributeName2 } = options; + const tracesDir = import_path45.default.join(import_os20.default.tmpdir(), `playwright-recorder-trace-${Date.now()}`); + const { context: context2, browser, launchOptions, contextOptions, closeBrowser } = await launchContext(options, { + headless: !!process.env.PWTEST_CLI_HEADLESS, + executablePath: process.env.PWTEST_CLI_EXECUTABLE_PATH, + tracesDir + }); + const donePromise = new ManualPromise(); + maybeSetupTestHooks(browser, closeBrowser, donePromise); + dotenv.config({ path: "playwright.env" }); + await context2._enableRecorder({ + language, + launchOptions, + contextOptions, + device: options.device, + saveStorage: options.saveStorage, + mode: "recording", + testIdAttributeName: testIdAttributeName2, + outputFile: outputFile2 ? import_path45.default.resolve(outputFile2) : void 0, + handleSIGINT: false + }); + await openPage(context2, url2); + donePromise.resolve(); +} +async function maybeSetupTestHooks(browser, closeBrowser, donePromise) { + if (!process.env.PWTEST_CLI_IS_UNDER_TEST) + return; + const logs = []; + debug11.log = (...args) => { + const line = require("util").format(...args) + "\n"; + logs.push(line); + process.stderr.write(line); + }; + browser.on("disconnected", () => { + const hasCrashLine = logs.some((line) => line.includes("process did exit:") && !line.includes("process did exit: exitCode=0, signal=null")); + if (hasCrashLine) { + process.stderr.write("Detected browser crash.\n"); + gracefullyProcessExitDoNotHang(1); + } + }); + const close3 = async () => { + await donePromise; + await closeBrowser(); + }; + if (process.env.PWTEST_CLI_EXIT_AFTER_TIMEOUT) { + setTimeout(close3, +process.env.PWTEST_CLI_EXIT_AFTER_TIMEOUT); + return; + } + let stdin = ""; + process.stdin.on("data", (data) => { + stdin += data.toString(); + if (stdin.startsWith("exit")) { + process.stdin.destroy(); + close3(); + } + }); +} +async function waitForPage(page, captureOptions) { + if (captureOptions.waitForSelector) { + console.log(`Waiting for selector ${captureOptions.waitForSelector}...`); + await page.waitForSelector(captureOptions.waitForSelector); + } + if (captureOptions.waitForTimeout) { + console.log(`Waiting for timeout ${captureOptions.waitForTimeout}...`); + await page.waitForTimeout(parseInt(captureOptions.waitForTimeout, 10)); + } +} +async function screenshot3(options, captureOptions, url2, path59) { + const { context: context2 } = await launchContext(options, { headless: true }); + console.log("Navigating to " + url2); + const page = await openPage(context2, url2); + await waitForPage(page, captureOptions); + console.log("Capturing screenshot into " + path59); + await page.screenshot({ path: path59, fullPage: !!captureOptions.fullPage }); + await page.close(); +} +async function pdf2(options, captureOptions, url2, path59) { + if (options.browser !== "chromium") + throw new Error("PDF creation is only working with Chromium"); + const { context: context2 } = await launchContext({ ...options, browser: "chromium" }, { headless: true }); + console.log("Navigating to " + url2); + const page = await openPage(context2, url2); + await waitForPage(page, captureOptions); + console.log("Saving as pdf into " + path59); + await page.pdf({ path: path59, format: captureOptions.paperFormat }); + await page.close(); +} +function lookupBrowserType(options) { + let name = options.browser; + if (options.device) { + const device = playwright.devices[options.device]; + name = device.defaultBrowserType; + } + let browserType; + switch (name) { + case "chromium": + browserType = playwright.chromium; + break; + case "webkit": + browserType = playwright.webkit; + break; + case "firefox": + browserType = playwright.firefox; + break; + case "cr": + browserType = playwright.chromium; + break; + case "wk": + browserType = playwright.webkit; + break; + case "ff": + browserType = playwright.firefox; + break; + } + if (browserType) + return browserType; + program.help(); +} +function validateOptions(options) { + if (options.device && !(options.device in playwright.devices)) { + const lines = [`Device descriptor not found: '${options.device}', available devices are:`]; + for (const name in playwright.devices) + lines.push(` "${name}"`); + throw new Error(lines.join("\n")); + } + if (options.colorScheme && !["light", "dark"].includes(options.colorScheme)) + throw new Error('Invalid color scheme, should be one of "light", "dark"'); +} +var import_fs49, import_os20, import_path45, debug11, dotenv, program; +var init_browserActions = __esm({ + "packages/playwright-core/src/cli/browserActions.ts"() { + "use strict"; + import_fs49 = __toESM(require("fs")); + import_os20 = __toESM(require("os")); + import_path45 = __toESM(require("path")); + init_processLauncher(); + init_manualPromise(); + init_inprocess(); + debug11 = require("./utilsBundle").debug; + dotenv = require("./utilsBundle").dotenv; + ({ program } = require("./utilsBundle")); + } +}); + +// packages/playwright-core/src/tools/utils/extension.ts +async function isPlaywrightExtensionInstalled(userDataDir) { + let entries; + try { + entries = await import_fs50.default.promises.readdir(userDataDir); + } catch { + return false; + } + for (const entry of entries) { + if (entry !== "Default" && !entry.startsWith("Profile ")) + continue; + if (await isExtensionInstalledInProfile(import_path46.default.join(userDataDir, entry))) + return true; + } + return false; +} +async function isExtensionInstalledInProfile(profileDir2) { + if (await pathExists(import_path46.default.join(profileDir2, "Extensions", playwrightExtensionId))) + return true; + try { + const prefs = await import_fs50.default.promises.readFile(import_path46.default.join(profileDir2, "Preferences"), "utf-8"); + return prefs.includes(`"${playwrightExtensionId}"`); + } catch { + return false; + } +} +async function pathExists(p) { + try { + await import_fs50.default.promises.access(p); + return true; + } catch { + return false; + } +} +var import_fs50, import_path46, playwrightExtensionId, playwrightExtensionInstallUrl; +var init_extension = __esm({ + "packages/playwright-core/src/tools/utils/extension.ts"() { + "use strict"; + import_fs50 = __toESM(require("fs")); + import_path46 = __toESM(require("path")); + playwrightExtensionId = "mmlmfjhmonkocbjadbfplnigmagldckm"; + playwrightExtensionInstallUrl = `https://chromewebstore.google.com/detail/playwright-extension/${playwrightExtensionId}`; + } +}); + +// packages/playwright-core/src/tools/cli-client/channelSessions.ts +function isKnownChannel(name) { + return channelToUserDataDir.has(name); +} +async function listChannelSessions() { + if (process.env.PWTEST_CLI_CHANNEL_SCAN_DISABLED_FOR_TEST) + return []; + const result2 = []; + for (const [channel, dirs] of channelToUserDataDir) { + const userDataDir = dirs[process.platform]; + if (!userDataDir) + continue; + if (!await pathExists2(userDataDir)) + continue; + const [endpoint, extensionInstalled] = await Promise.all([ + readEndpoint(userDataDir), + isPlaywrightExtensionInstalled(userDataDir) + ]); + result2.push({ channel, userDataDir, endpoint, extensionInstalled }); + } + return result2; +} +async function pathExists2(p) { + try { + await import_fs51.default.promises.access(p); + return true; + } catch { + return false; + } +} +async function readEndpoint(userDataDir) { + let contents; + try { + contents = await import_fs51.default.promises.readFile(import_path47.default.join(userDataDir, "DevToolsActivePort"), "utf-8"); + } catch { + return void 0; + } + const port = parseInt(contents.trim().split("\n")[0], 10); + if (!Number.isFinite(port)) + return void 0; + if (!await isPortOpen(port)) + return void 0; + return `http://localhost:${port}`; +} +async function isPortOpen(port) { + return new Promise((resolve) => { + const socket = import_net8.default.createConnection(port, "127.0.0.1"); + const done = (value2) => { + socket.destroy(); + resolve(value2); + }; + socket.once("connect", () => done(true)); + socket.once("error", () => done(false)); + socket.setTimeout(250, () => done(false)); + }); +} +var import_fs51, import_net8, import_os21, import_path47, channelToUserDataDir; +var init_channelSessions = __esm({ + "packages/playwright-core/src/tools/cli-client/channelSessions.ts"() { + "use strict"; + import_fs51 = __toESM(require("fs")); + import_net8 = __toESM(require("net")); + import_os21 = __toESM(require("os")); + import_path47 = __toESM(require("path")); + init_extension(); + channelToUserDataDir = /* @__PURE__ */ new Map([ + ["chrome", { + "linux": import_path47.default.join(import_os21.default.homedir(), ".config", "google-chrome"), + "darwin": import_path47.default.join(import_os21.default.homedir(), "Library", "Application Support", "Google", "Chrome"), + "win32": import_path47.default.join(process.env.LOCALAPPDATA || import_path47.default.join(import_os21.default.homedir(), "AppData", "Local"), "Google", "Chrome", "User Data") + }], + ["chrome-beta", { + "linux": import_path47.default.join(import_os21.default.homedir(), ".config", "google-chrome-beta"), + "darwin": import_path47.default.join(import_os21.default.homedir(), "Library", "Application Support", "Google", "Chrome Beta"), + "win32": import_path47.default.join(process.env.LOCALAPPDATA || import_path47.default.join(import_os21.default.homedir(), "AppData", "Local"), "Google", "Chrome Beta", "User Data") + }], + ["chrome-dev", { + "linux": import_path47.default.join(import_os21.default.homedir(), ".config", "google-chrome-unstable"), + "darwin": import_path47.default.join(import_os21.default.homedir(), "Library", "Application Support", "Google", "Chrome Dev"), + "win32": import_path47.default.join(process.env.LOCALAPPDATA || import_path47.default.join(import_os21.default.homedir(), "AppData", "Local"), "Google", "Chrome Dev", "User Data") + }], + ["chrome-canary", { + "linux": import_path47.default.join(import_os21.default.homedir(), ".config", "google-chrome-canary"), + "darwin": import_path47.default.join(import_os21.default.homedir(), "Library", "Application Support", "Google", "Chrome Canary"), + "win32": import_path47.default.join(process.env.LOCALAPPDATA || import_path47.default.join(import_os21.default.homedir(), "AppData", "Local"), "Google", "Chrome SxS", "User Data") + }], + ["msedge", { + "linux": import_path47.default.join(import_os21.default.homedir(), ".config", "microsoft-edge"), + "darwin": import_path47.default.join(import_os21.default.homedir(), "Library", "Application Support", "Microsoft Edge"), + "win32": import_path47.default.join(process.env.LOCALAPPDATA || import_path47.default.join(import_os21.default.homedir(), "AppData", "Local"), "Microsoft", "Edge", "User Data") + }], + ["msedge-beta", { + "linux": import_path47.default.join(import_os21.default.homedir(), ".config", "microsoft-edge-beta"), + "darwin": import_path47.default.join(import_os21.default.homedir(), "Library", "Application Support", "Microsoft Edge Beta"), + "win32": import_path47.default.join(process.env.LOCALAPPDATA || import_path47.default.join(import_os21.default.homedir(), "AppData", "Local"), "Microsoft", "Edge Beta", "User Data") + }], + ["msedge-dev", { + "linux": import_path47.default.join(import_os21.default.homedir(), ".config", "microsoft-edge-dev"), + "darwin": import_path47.default.join(import_os21.default.homedir(), "Library", "Application Support", "Microsoft Edge Dev"), + "win32": import_path47.default.join(process.env.LOCALAPPDATA || import_path47.default.join(import_os21.default.homedir(), "AppData", "Local"), "Microsoft", "Edge Dev", "User Data") + }], + ["msedge-canary", { + "linux": import_path47.default.join(import_os21.default.homedir(), ".config", "microsoft-edge-canary"), + "darwin": import_path47.default.join(import_os21.default.homedir(), "Library", "Application Support", "Microsoft Edge Canary"), + "win32": import_path47.default.join(process.env.LOCALAPPDATA || import_path47.default.join(import_os21.default.homedir(), "AppData", "Local"), "Microsoft", "Edge SxS", "User Data") + }] + ]); + } +}); + +// packages/playwright-core/src/tools/cli-client/output.ts +function parseJsonText(text2) { + try { + return JSON.parse(text2); + } catch { + return text2; + } +} +function renderBrowser(browser) { + const lines = [`- ${browser.name}:`]; + lines.push(` - status: ${browser.status}`); + if (browser.status === "open" && !browser.compatible) + lines.push(` - version: v${browser.version} [incompatible please re-open]`); + if (browser.browserType) + lines.push(` - browser-type: ${browser.browserType}${browser.attached ? " (attached)" : ""}`); + if (!browser.attached) { + if (browser.userDataDir === null) + lines.push(` - user-data-dir: `); + else + lines.push(` - user-data-dir: ${browser.userDataDir}`); + if (browser.headed !== void 0) + lines.push(` - headed: ${browser.headed}`); + } + return lines.join("\n"); +} +function renderServer(server) { + const lines = [`- browser "${server.title}":`]; + lines.push(` - browser: ${server.browser.browserName}`); + lines.push(` - version: v${server.playwrightVersion}`); + if (server.browser.userDataDir) + lines.push(` - data-dir: ${server.browser.userDataDir}`); + else + lines.push(` - data-dir: `); + lines.push(` - run \`playwright-cli attach "${server.title}"\` to attach`); + return lines.join("\n"); +} +function renderChannelSession(session2) { + const lines = [`- ${session2.channel}:`]; + lines.push(` - data-dir: ${session2.userDataDir}`); + if (session2.extensionInstalled) + lines.push(` - attach (extension): \`playwright-cli attach --extension=${session2.channel}\``); + else + lines.push(` - attach (extension): install at ${playwrightExtensionInstallUrl}`); + if (session2.endpoint) { + lines.push(` - attach (remote debugging): \`playwright-cli attach --cdp=${session2.channel}\``); + } else { + const inspectScheme = session2.channel.startsWith("msedge") ? "edge" : "chrome"; + lines.push(` - attach (remote debugging): enable at ${inspectScheme}://inspect/#remote-debugging`); + } + return lines.join("\n"); +} +var import_path48, TextOutput, JsonOutput; +var init_output = __esm({ + "packages/playwright-core/src/tools/cli-client/output.ts"() { + "use strict"; + import_path48 = __toESM(require("path")); + init_extension(); + TextOutput = class { + constructor() { + this.json = false; + } + version(v) { + console.log(v); + } + help(text2) { + console.log(text2); + } + errorUnknownCommand(name, globalHelp) { + console.error(`Unknown command: ${name} +`); + console.log(globalHelp); + return process.exit(1); + } + errorUnknownOption(opts, commandHelp) { + console.error(`Unknown option${opts.length > 1 ? "s" : ""}: ${opts.map((f) => `--${f}`).join(", ")}`); + console.log(""); + console.log(commandHelp); + return process.exit(1); + } + errorTooManyArguments(expected, received, commandHelp) { + console.error(`error: too many arguments: expected ${expected}, received ${received}`); + console.log(""); + console.log(commandHelp); + return process.exit(1); + } + errorAttachConflict() { + console.error(`Error: only one of [name], --cdp, --endpoint, or --extension can be specified`); + return process.exit(1); + } + errorDetachNotAttached(session2) { + console.error(`Error: session '${session2}' was not attached; use \`playwright-cli${session2 !== "default" ? ` -s=${session2}` : ""} close\` to stop it.`); + return process.exit(1); + } + errorBrowserNotOpenForTool(session2) { + console.log(`The browser '${session2}' is not open, please run open first`); + console.log(""); + console.log(` playwright-cli${session2 !== "default" ? ` -s=${session2}` : ""} open [params]`); + return process.exit(1); + } + errorAttachNoTarget() { + console.error(`Error: no target specified for attach command; use one of [name], --cdp, --endpoint, or --extension to specify the target to attach to.`); + return process.exit(1); + } + list({ all, browsers, servers, channelSessions }) { + const byWorkspace = /* @__PURE__ */ new Map(); + for (const browser of browsers) { + let list = byWorkspace.get(browser.workspace); + if (!list) { + list = []; + byWorkspace.set(browser.workspace, list); + } + list.push(browser); + } + let count = 0; + for (const [workspaceKey, list] of byWorkspace) { + if (count === 0) + console.log("### Browsers"); + if (all) + console.log(`${import_path48.default.relative(process.cwd(), workspaceKey) || "/"}:`); + for (const browser of list) + console.log(renderBrowser(browser)); + count += list.length; + } + if (!all) { + if (!count) + console.log(" (no browsers)"); + return; + } + if (servers?.length) { + if (count) + console.log(""); + console.log("### Browser servers available for attach"); + const serversByWorkspace = /* @__PURE__ */ new Map(); + for (const server of servers) { + let list = serversByWorkspace.get(server.workspaceDir ?? ""); + if (!list) { + list = []; + serversByWorkspace.set(server.workspaceDir ?? "", list); + } + list.push(server); + } + for (const [workspaceKey, list] of serversByWorkspace) { + if (workspaceKey) + console.log(`${import_path48.default.relative(process.cwd(), workspaceKey) || "/"}:`); + for (const server of list) + console.log(renderServer(server)); + } + count += servers.length; + } + if (!count) + console.log(" (no browsers)"); + if (channelSessions?.length) { + console.log(""); + console.log("### Browsers available to attach via CDP"); + for (const session2 of channelSessions) + console.log(renderChannelSession(session2)); + } + } + closeAll(_sessions) { + } + deleteData(session2, result2) { + if (!result2.existed) { + console.log(`No user data found for browser '${session2}'.`); + return; + } + if (result2.deletedUserDataDir) + console.log(`Deleted user data for browser '${session2}'.`); + } + killAll(pids) { + for (const pid of pids) + console.log(`Killed daemon process ${pid}`); + if (pids.length === 0) + console.log("No daemon processes found."); + else + console.log(`Killed ${pids.length} daemon process${pids.length === 1 ? "" : "es"}.`); + } + open(session2, pid, toolResult) { + console.log(`### Browser \`${session2}\` opened with pid ${pid}.`); + if (toolResult) + console.log(toolResult); + } + attach(session2, pid, endpoint, toolResult) { + if (endpoint) { + console.log(`### Session \`${session2}\` created, attached to \`${endpoint}\`.`); + console.log(`Run commands with: playwright-cli --s=${session2} `); + console.log(""); + } else { + console.log(`### Browser \`${session2}\` opened with pid ${pid}.`); + } + if (toolResult) + console.log(toolResult); + } + close(session2, wasOpen) { + if (!wasOpen) { + console.log(`Browser '${session2}' is not open.`); + return; + } + console.log(`Browser '${session2}' closed +`); + } + detach(session2, wasAttached) { + if (!wasAttached) { + console.log(`Browser '${session2}' is not attached.`); + return; + } + console.log(`Browser '${session2}' detached +`); + } + installed() { + } + show(_session, pid) { + if (process.env.PWTEST_PRINT_DASHBOARD_PID_FOR_TEST) + console.log(`### Dashboard opened with pid ${pid}.`); + } + toolResult(text2) { + console.log(text2); + } + installStdio() { + return "inherit"; + } + }; + JsonOutput = class { + constructor() { + this.json = true; + } + version(v) { + this._emit({ version: v }); + } + help(text2) { + this._emit({ help: text2 }); + } + errorUnknownCommand(name, _globalHelp) { + this._emit({ isError: true, error: `Unknown command: ${name}` }); + return process.exit(1); + } + errorUnknownOption(opts, _commandHelp) { + this._emit({ isError: true, error: `Unknown option${opts.length > 1 ? "s" : ""}: ${opts.map((f) => `--${f}`).join(", ")}` }); + return process.exit(1); + } + errorTooManyArguments(expected, received, _commandHelp) { + this._emit({ isError: true, error: `error: too many arguments: expected ${expected}, received ${received}` }); + return process.exit(1); + } + errorAttachConflict() { + this._emit({ isError: true, error: `only one of [name], --cdp, --endpoint, or --extension can be specified` }); + return process.exit(1); + } + errorDetachNotAttached(session2) { + this._emit({ isError: true, error: `session '${session2}' was not attached; use close to stop it.` }); + return process.exit(1); + } + errorBrowserNotOpenForTool(session2) { + this._emit({ isError: true, error: `The browser '${session2}' is not open, please run open first` }); + return process.exit(1); + } + errorAttachNoTarget() { + this._emit({ isError: true, error: `no target specified for attach command; use one of [name], --cdp, --endpoint, or --extension to specify the target to attach to.` }); + return process.exit(1); + } + list({ all, browsers, servers, channelSessions }) { + const payload = { browsers }; + if (all) { + payload.servers = servers ?? []; + payload.channelSessions = channelSessions ?? []; + } + this._emit(payload); + } + closeAll(sessions) { + this._emit({ closed: sessions }); + } + deleteData(session2, result2) { + this._emit({ session: session2, deleted: result2.existed }); + } + killAll(pids) { + this._emit({ killed: pids.length, pids }); + } + open(session2, pid, toolResult) { + this._emit({ session: session2, pid, result: parseJsonText(toolResult) }); + } + attach(session2, pid, endpoint, toolResult) { + this._emit({ + session: session2, + pid, + ...endpoint ? { endpoint } : {}, + result: parseJsonText(toolResult) + }); + } + close(session2, wasOpen) { + this._emit({ session: session2, status: wasOpen ? "closed" : "not-open" }); + } + detach(session2, wasAttached) { + this._emit({ session: session2, status: wasAttached ? "detached" : "not-attached" }); + } + installed() { + this._emit({ installed: true }); + } + show(session2, pid) { + this._emit({ session: session2, pid }); + } + toolResult(text2) { + console.log(text2); + } + installStdio() { + return "ignore"; + } + _emit(value2) { + console.log(JSON.stringify(value2, null, 2)); + } + }; + } +}); + +// packages/playwright-core/src/tools/cli-client/registry.ts +function clientKey(clientInfo) { + return clientInfo.workspaceDir || clientInfo.workspaceDirHash; +} +function createClientInfo() { + const workspaceDir = findWorkspaceDir(process.cwd()); + const version3 = process.env.PLAYWRIGHT_CLI_VERSION_FOR_TEST || packageJSON.version; + const hash = import_crypto26.default.createHash("sha1"); + hash.update(workspaceDir || packageRoot); + const workspaceDirHash = hash.digest("hex").substring(0, 16); + return { + version: version3, + workspaceDir, + workspaceDirHash, + daemonProfilesDir: daemonProfilesDir(workspaceDirHash), + homeDir: import_os22.default.homedir() + }; +} +function findWorkspaceDir(startDir) { + let dir = startDir; + for (let i = 0; i < 10; i++) { + if (import_fs52.default.existsSync(import_path49.default.join(dir, ".playwright"))) + return dir; + const parentDir = import_path49.default.dirname(dir); + if (parentDir === dir) + break; + dir = parentDir; + } + return void 0; +} +function explicitSessionName(sessionName) { + return sessionName || process.env.PLAYWRIGHT_CLI_SESSION; +} +function resolveSessionName(sessionName) { + return explicitSessionName(sessionName) || "default"; +} +var import_crypto26, import_fs52, import_os22, import_path49, Registry2, baseDaemonDir, daemonProfilesDir; +var init_registry2 = __esm({ + "packages/playwright-core/src/tools/cli-client/registry.ts"() { + "use strict"; + import_crypto26 = __toESM(require("crypto")); + import_fs52 = __toESM(require("fs")); + import_os22 = __toESM(require("os")); + import_path49 = __toESM(require("path")); + init_package(); + Registry2 = class _Registry { + constructor(files) { + this._files = files; + } + entry(clientInfo, sessionName) { + const key = clientKey(clientInfo); + const entries = this._files.get(key) || []; + return entries.find((entry) => entry.config.name === sessionName); + } + entries(clientInfo) { + return this._files.get(clientKey(clientInfo)) || []; + } + entryMap() { + return this._files; + } + async loadEntry(clientInfo, sessionName) { + const entry = await _Registry._loadSessionEntry(clientInfo.daemonProfilesDir, sessionName + ".session"); + if (!entry) + throw new Error(`Could not start the session "${sessionName}"`); + const key = clientKey(clientInfo); + let list = this._files.get(key); + if (!list) { + list = []; + this._files.set(key, list); + } + const oldIndex = list.findIndex((e) => e.config.name === sessionName); + if (oldIndex !== -1) + list.splice(oldIndex, 1); + list.push(entry); + return entry; + } + static async _loadSessionEntry(daemonDir, file) { + try { + const fileName = import_path49.default.join(daemonDir, file); + const data = await import_fs52.default.promises.readFile(fileName, "utf-8"); + const config = JSON.parse(data); + if (!config.name) + config.name = import_path49.default.basename(file, ".session"); + if (!config.timestamp) + config.timestamp = 0; + return { file: fileName, config, daemonDir }; + } catch { + return void 0; + } + } + static async load() { + const sessions = /* @__PURE__ */ new Map(); + const hashDirs = await import_fs52.default.promises.readdir(baseDaemonDir).catch(() => []); + for (const workspaceDirHash of hashDirs) { + const daemonDir = import_path49.default.join(baseDaemonDir, workspaceDirHash); + const stat = await import_fs52.default.promises.stat(daemonDir); + if (!stat.isDirectory()) + continue; + const files = await import_fs52.default.promises.readdir(daemonDir).catch(() => []); + for (const file of files) { + if (!file.endsWith(".session")) + continue; + const entry = await _Registry._loadSessionEntry(daemonDir, file); + if (!entry) + continue; + const key = entry.config.workspaceDir || workspaceDirHash; + let list = sessions.get(key); + if (!list) { + list = []; + sessions.set(key, list); + } + list.push(entry); + } + } + return new _Registry(sessions); + } + }; + baseDaemonDir = (() => { + if (process.env.PWTEST_DAEMON_SESSION_DIR) + return process.env.PWTEST_DAEMON_SESSION_DIR; + let localCacheDir; + if (process.platform === "linux") + localCacheDir = process.env.XDG_CACHE_HOME || import_path49.default.join(import_os22.default.homedir(), ".cache"); + if (process.platform === "darwin") + localCacheDir = import_path49.default.join(import_os22.default.homedir(), "Library", "Caches"); + if (process.platform === "win32") + localCacheDir = process.env.LOCALAPPDATA || import_path49.default.join(import_os22.default.homedir(), "AppData", "Local"); + if (!localCacheDir) + throw new Error("Unsupported platform: " + process.platform); + return import_path49.default.join(localCacheDir, "ms-playwright", "daemon"); + })(); + daemonProfilesDir = (workspaceDirHash) => { + return import_path49.default.join(baseDaemonDir, workspaceDirHash); + }; + } +}); + +// packages/playwright-core/src/tools/utils/socketConnection.ts +function compareSemver(a, b) { + const aBase = a.replace(/-.*$/, ""); + const bBase = b.replace(/-.*$/, ""); + const aParts = aBase.split(".").map(Number); + const bParts = bBase.split(".").map(Number); + for (let i = 0; i < 3; i++) { + if (aParts[i] > bParts[i]) + return 1; + if (aParts[i] < bParts[i]) + return -1; + } + const aTimestamp = parseSuffixTimestamp(a); + const bTimestamp = parseSuffixTimestamp(b); + if (aTimestamp > bTimestamp) + return 1; + if (aTimestamp < bTimestamp) + return -1; + return 0; +} +function parseSuffixTimestamp(version3) { + const match = version3.match(/^\d+\.\d+\.\d+-(?:alpha|beta)-(.+)$/); + if (!match) + return Infinity; + const suffix = match[1]; + if (/^\d{4}-\d{2}-\d{2}$/.test(suffix)) + return new Date(suffix).getTime(); + return Number(suffix); +} +var SocketConnection; +var init_socketConnection = __esm({ + "packages/playwright-core/src/tools/utils/socketConnection.ts"() { + "use strict"; + SocketConnection = class { + constructor(socket) { + this._pendingBuffers = []; + this._socket = socket; + socket.on("data", (buffer) => this._onData(buffer)); + socket.on("close", () => { + this.onclose?.(); + }); + socket.on("error", (e) => console.error(`error: ${e.message}`)); + } + async send(message) { + await new Promise((resolve, reject) => { + this._socket.write(`${JSON.stringify(message)} +`, (error) => { + if (error) + reject(error); + else + resolve(void 0); + }); + }); + } + close() { + this._socket.destroy(); + } + _onData(buffer) { + let end = buffer.indexOf("\n"); + if (end === -1) { + this._pendingBuffers.push(buffer); + return; + } + this._pendingBuffers.push(buffer.slice(0, end)); + const message = Buffer.concat(this._pendingBuffers).toString(); + this._dispatchMessage(message); + let start3 = end + 1; + end = buffer.indexOf("\n", start3); + while (end !== -1) { + const message2 = buffer.toString(void 0, start3, end); + this._dispatchMessage(message2); + start3 = end + 1; + end = buffer.indexOf("\n", start3); + } + this._pendingBuffers = [buffer.slice(start3)]; + } + _dispatchMessage(message) { + try { + this.onmessage?.(JSON.parse(message)); + } catch (e) { + console.error("failed to dispatch message", e); + } + } + }; + } +}); + +// packages/playwright-core/src/tools/cli-client/session.ts +var import_child_process4, import_fs53, import_net9, import_os23, import_path50, Session2, SocketConnectionClient; +var init_session = __esm({ + "packages/playwright-core/src/tools/cli-client/session.ts"() { + "use strict"; + import_child_process4 = require("child_process"); + import_fs53 = __toESM(require("fs")); + import_net9 = __toESM(require("net")); + import_os23 = __toESM(require("os")); + import_path50 = __toESM(require("path")); + init_package(); + init_socketConnection(); + init_registry2(); + Session2 = class { + constructor(sessionFile) { + this.config = sessionFile.config; + this.name = this.config.name; + this._sessionFile = sessionFile; + } + isCompatible(clientInfo) { + return compareSemver(clientInfo.version, this.config.version) >= 0; + } + async run(clientInfo, args, options) { + if (!this.isCompatible(clientInfo)) + throw new Error(`Client is v${clientInfo.version}, session '${this.name}' is v${this.config.version}. Run + + playwright-cli${this.name !== "default" ? ` -s=${this.name}` : ""} open + +to restart the browser session.`); + const { socket } = await this._connect(); + if (!socket) + throw new Error(`Browser '${this.name}' is not open. Run + + playwright-cli${this.name !== "default" ? ` -s=${this.name}` : ""} open + +to start the browser session.`); + return await SocketConnectionClient.sendAndClose(socket, "run", { args, cwd: process.cwd(), raw: options?.raw, json: options?.json }); + } + async stop() { + if (!await this.canConnect()) + return { wasOpen: false }; + await this._stopDaemon(); + return { wasOpen: true }; + } + async deleteData() { + await this.stop(); + const dataDirs = await import_fs53.default.promises.readdir(this._sessionFile.daemonDir).catch(() => []); + const matchingEntries = dataDirs.filter((file) => file === `${this.name}.session` || file.startsWith(`ud-${this.name}-`)); + if (matchingEntries.length === 0) + return { existed: false, deletedUserDataDir: false }; + let deletedUserDataDir = false; + for (const entry of matchingEntries) { + const userDataDir = import_path50.default.resolve(this._sessionFile.daemonDir, entry); + for (let i = 0; i < 5; i++) { + try { + await import_fs53.default.promises.rm(userDataDir, { recursive: true }); + if (entry.startsWith("ud-")) + deletedUserDataDir = true; + break; + } catch (e) { + if (e.code === "ENOENT") + break; + await new Promise((resolve) => setTimeout(resolve, 1e3)); + if (i === 4) + throw e; + } + } + } + return { existed: true, deletedUserDataDir }; + } + async _connect() { + return await new Promise((resolve) => { + const socket = import_net9.default.createConnection(this.config.socketPath, () => { + resolve({ socket }); + }); + socket.on("error", (error) => { + if (import_os23.default.platform() !== "win32") + void import_fs53.default.promises.unlink(this.config.socketPath).catch(() => { + }).then(() => resolve({ error })); + else + resolve({ error }); + }); + }); + } + async canConnect() { + const { socket } = await this._connect(); + if (socket) { + socket.destroy(); + return true; + } + return false; + } + static async startDaemon(clientInfo, cliArgs, mode) { + await import_fs53.default.promises.mkdir(clientInfo.daemonProfilesDir, { recursive: true }); + const cliPath = libPath("entry", "cliDaemon.js"); + const sessionName = resolveSessionName(cliArgs.session); + const errLog = import_path50.default.join(clientInfo.daemonProfilesDir, sessionName + ".err"); + const err = import_fs53.default.openSync(errLog, "w"); + const args = [ + cliPath, + sessionName + ]; + if (cliArgs.headed) + args.push("--headed"); + if (cliArgs.browser) + args.push(`--browser=${cliArgs.browser}`); + if (cliArgs.persistent) + args.push("--persistent"); + if (cliArgs.profile) + args.push(`--profile=${cliArgs.profile}`); + if (cliArgs.config) + args.push(`--config=${cliArgs.config}`); + if (cliArgs.extension) + args.push("--extension"); + else if (cliArgs.cdp) + args.push(`--cdp=${cliArgs.cdp}`); + else if (cliArgs.endpoint) + args.push(`--endpoint=${cliArgs.endpoint}`); + const child = (0, import_child_process4.spawn)(process.execPath, args, { + detached: true, + stdio: ["ignore", "pipe", err], + cwd: process.cwd() + // Will be used as root. + }); + let signalled = false; + const sigintHandler2 = () => { + signalled = true; + child.kill("SIGINT"); + }; + const sigtermHandler2 = () => { + signalled = true; + child.kill("SIGTERM"); + }; + process.on("SIGINT", sigintHandler2); + process.on("SIGTERM", sigtermHandler2); + let outLog = ""; + const rejectWithPid = (reject, message) => reject(Object.assign(new Error(`Daemon pid=${child.pid}: ${message}`), { daemonPid: child.pid })); + await new Promise((resolve, reject) => { + child.stdout.on("data", (data) => { + outLog += data.toString(); + if (outLog.includes("Daemon listening on")) + resolve(); + }); + child.on("close", (code) => { + if (!signalled) { + const errLogContent = import_fs53.default.readFileSync(errLog, "utf-8"); + rejectWithPid(reject, `Daemon process exited with code ${code}` + (outLog ? "\n" + outLog : "") + (errLogContent ? "\n" + errLogContent : "")); + } + }); + }); + process.off("SIGINT", sigintHandler2); + process.off("SIGTERM", sigtermHandler2); + child.stdout.destroy(); + child.unref(); + return { pid: child.pid, sessionName, endpoint: cliArgs.endpoint }; + } + async _stopDaemon() { + const { socket } = await this._connect(); + if (!socket) + return; + let error; + await SocketConnectionClient.sendAndClose(socket, "stop", {}).catch((e) => error = e); + if (error && !error?.message?.includes("Session closed")) + throw error; + } + async deleteSessionConfig() { + await import_fs53.default.promises.rm(this._sessionFile.file).catch(() => { + }); + } + }; + SocketConnectionClient = class _SocketConnectionClient { + constructor(socket) { + this._nextMessageId = 1; + this._callbacks = /* @__PURE__ */ new Map(); + this._connection = new SocketConnection(socket); + this._connection.onmessage = (message) => this._onMessage(message); + this._connection.onclose = () => this._rejectCallbacks(); + } + async send(method, params2 = {}) { + const messageId = this._nextMessageId++; + const message = { + id: messageId, + method, + params: params2 + }; + const responsePromise = new Promise((resolve, reject) => { + this._callbacks.set(messageId, { resolve, reject, method, params: params2 }); + }); + const [result2] = await Promise.all([responsePromise, this._connection.send(message)]); + return result2; + } + static async sendAndClose(socket, method, params2 = {}) { + const connection = new _SocketConnectionClient(socket); + try { + return await connection.send(method, params2); + } finally { + connection.close(); + } + } + close() { + this._connection.close(); + } + _onMessage(object) { + if (object.id && this._callbacks.has(object.id)) { + const callback = this._callbacks.get(object.id); + this._callbacks.delete(object.id); + if (object.error) + callback.reject(new Error(object.error)); + else + callback.resolve(object.result); + } else if (object.id) { + throw new Error(`Unexpected message id: ${object.id}`); + } else { + throw new Error(`Unexpected message without id: ${JSON.stringify(object)}`); + } + } + _rejectCallbacks() { + for (const callback of this._callbacks.values()) + callback.reject(new Error("Session closed")); + this._callbacks.clear(); + } + }; + } +}); + +// packages/playwright-core/src/tools/cli-client/program.ts +async function program2(options) { + const clientInfo = createClientInfo(); + const help = require(libPath("tools", "cli-client", "help.json")); + const argv = process.argv.slice(2); + const boolean = [...help.booleanOptions, ...booleanOptions]; + const args = minimist(argv, { boolean, string: ["_"] }); + if (args.s) { + args.session = args.s; + delete args.s; + } + const output = args.json ? new JsonOutput() : new TextOutput(); + const commandName = args._?.[0]; + if (args.version || args.v) { + output.version(options?.embedderVersion ?? clientInfo.version); + process.exit(0); + } + const command = commandName && help.commands[commandName]; + if (args.help || args.h || !commandName) { + if (command) { + output.help(command.help); + } else { + const lines = ["playwright-cli - run playwright mcp commands from terminal"]; + if (process.env.CLAUDECODE || process.env.COPILOT_CLI) + lines.push(`Agent skill: ${import_path51.default.relative(process.cwd(), libPath("tools", "cli-client", "skill", "SKILL.md"))}`); + lines.push(help.global); + output.help(lines.join("\n\n")); + } + process.exit(0); + } + if (!command) + output.errorUnknownCommand(commandName, help.global); + validateFlags(args, command, output); + validateArgs(args, command, output); + const registry2 = await Registry2.load(); + const sessionName = resolveSessionName(args.session); + switch (commandName) { + case "list": { + const data = await collectList(registry2, clientInfo, !!args.all); + output.list(data); + return; + } + case "close-all": { + const entries = registry2.entries(clientInfo); + const closed = []; + for (const entry of entries) { + await new Session2(entry).stop(); + closed.push(entry.config.name); + } + output.closeAll(closed); + return; + } + case "delete-data": { + const entry = registry2.entry(clientInfo, sessionName); + if (!entry) { + output.deleteData(sessionName, { existed: false, deletedUserDataDir: false }); + return; + } + const result2 = await new Session2(entry).deleteData(); + output.deleteData(sessionName, result2); + return; + } + case "kill-all": { + const pids = await killAllDaemons(); + output.killAll(pids); + return; + } + case "open": { + const { pid } = await startSession(sessionName, registry2, clientInfo, args, "open"); + const newEntry = await registry2.loadEntry(clientInfo, sessionName); + const params2 = args._.slice(1); + const toolText = await runInSessionOrStop(newEntry, clientInfo, { _: ["goto", ...params2.length ? params2 : ["about:blank"]] }, output); + output.open(sessionName, pid, toolText); + return; + } + case "attach": { + const attachTarget = args._[1]; + const targetCount = (attachTarget ? 1 : 0) + (args.cdp ? 1 : 0) + (args.endpoint ? 1 : 0) + (args.extension ? 1 : 0); + if (targetCount > 1) + output.errorAttachConflict(); + if (attachTarget) + args.endpoint = attachTarget; + const extensionChannel = typeof args.extension === "string" ? args.extension : void 0; + if (extensionChannel) { + args.browser = extensionChannel; + args.extension = true; + } + const cdpChannel = typeof args.cdp === "string" && isKnownChannel(args.cdp) ? args.cdp : void 0; + const targetName = attachTarget ?? cdpChannel ?? extensionChannel ?? args.endpoint ?? args.cdp; + if (!targetName) + output.errorAttachNoTarget(); + const attachSessionName = explicitSessionName(args.session) ?? attachTarget ?? cdpChannel ?? extensionChannel ?? sessionName; + args.session = attachSessionName; + const { pid } = await startSession(attachSessionName, registry2, clientInfo, args, "attach"); + const newEntry = await registry2.loadEntry(clientInfo, attachSessionName); + const toolText = await runInSessionOrStop(newEntry, clientInfo, { _: ["snapshot"], filename: "" }, output); + output.attach(attachSessionName, pid, targetName, toolText); + return; + } + case "close": { + const closeEntry = registry2.entry(clientInfo, sessionName); + const { wasOpen } = closeEntry ? await new Session2(closeEntry).stop() : { wasOpen: false }; + output.close(sessionName, wasOpen); + return; + } + case "detach": { + const detachEntry = registry2.entry(clientInfo, sessionName); + if (detachEntry && !detachEntry.config.attached) + output.errorDetachNotAttached(sessionName); + const { wasOpen } = detachEntry ? await new Session2(detachEntry).stop() : { wasOpen: false }; + output.detach(sessionName, wasOpen); + return; + } + case "install": + await runInitWorkspace(args, output); + output.installed(); + return; + case "install-browser": + await installBrowser2(); + output.installed(); + return; + case "show": { + const daemonScript = libPath("entry", "dashboardApp.js"); + const daemonArgs = [ + daemonScript, + `--workspaceDir=${clientInfo.workspaceDir ?? ""}` + ]; + const explicit = explicitSessionName(args.session); + if (explicit) + daemonArgs.push(`--sessionName=${explicit}`); + if (args.port !== void 0) + daemonArgs.push(`--port=${args.port}`); + if (args.host !== void 0) + daemonArgs.push(`--host=${args.host}`); + if (args.kill) { + daemonArgs.push(`--kill`); + const child2 = (0, import_child_process5.spawn)(process.execPath, daemonArgs, { stdio: "ignore" }); + await new Promise((resolve) => child2.on("exit", () => resolve())); + return; + } + if (args.annotate) { + const entry = registry2.entry(clientInfo, sessionName); + if (!entry) + output.errorBrowserNotOpenForTool(sessionName); + args.raw = true; + const text2 = await runInSession(entry, clientInfo, args, output); + output.toolResult(text2); + return; + } + const foreground = args.port !== void 0; + const child = (0, import_child_process5.spawn)(process.execPath, daemonArgs, { + detached: !foreground, + stdio: foreground ? "inherit" : ["pipe", "pipe", "ignore"] + }); + if (foreground) { + await new Promise((resolve) => child.on("exit", () => resolve())); + return; + } + const timer = setTimeout(() => child.stdin.destroy(), 6e4); + child.unref(); + let daemonPid; + try { + await new Promise((resolve, reject) => { + let outLog = ""; + child.stdout.on("data", (data) => { + outLog += data.toString(); + const match = outLog.match(/Dashboard is running pid=(\d+)/); + if (match) { + daemonPid = Number(match[1]); + resolve(); + } + }); + child.once("exit", (code, signal) => reject(new Error(`Dashboard daemon exited (code=${code}, signal=${signal}) before signaling READY${outLog ? "\n" + outLog : ""}`))); + }); + } finally { + clearTimeout(timer); + child.removeAllListeners("exit"); + child.stdin.destroy(); + child.stdout.destroy(); + } + output.show(sessionName, daemonPid); + return; + } + default: { + const entry = registry2.entry(clientInfo, sessionName); + if (!entry) + output.errorBrowserNotOpenForTool(sessionName); + if (command.raw) + args.raw = true; + const text2 = await runInSession(entry, clientInfo, args, output); + output.toolResult(text2); + } + } +} +async function startSession(sessionName, registry2, clientInfo, args, mode) { + const entry = registry2.entry(clientInfo, sessionName); + if (entry) + await new Session2(entry).stop(); + return await Session2.startDaemon(clientInfo, args, mode); +} +async function runInSession(entry, clientInfo, args, output) { + const raw = !!args.raw; + for (const globalOption of globalOptions) + delete args[globalOption]; + const session2 = new Session2(entry); + const result2 = await session2.run(clientInfo, args, { raw, json: output.json }); + return result2.text; +} +async function runInSessionOrStop(entry, clientInfo, args, output) { + try { + return await runInSession(entry, clientInfo, args, output); + } catch (e) { + await new Session2(entry).stop().catch(() => { + }); + throw e; + } +} +async function runInitWorkspace(args, output) { + const cliPath = libPath("entry", "cliDaemon.js"); + const daemonArgs = [cliPath, "--init-workspace", ...args.skills ? ["--init-skills", String(args.skills)] : []]; + await new Promise((resolve, reject) => { + const child = (0, import_child_process5.spawn)(process.execPath, daemonArgs, { + stdio: output.installStdio(), + cwd: process.cwd() + }); + child.on("close", (code) => { + if (code === 0) + resolve(); + else + reject(new Error(`Workspace initialization failed with exit code ${code}`)); + }); + }); +} +async function installBrowser2() { + const argv = process.argv.map((arg) => arg === "install-browser" ? "install" : arg); + const { libCli } = (init_coreBundle(), __toCommonJS(coreBundle_exports)); + const { program: program4 } = require("./utilsBundle"); + if (!program4.version()) + libCli.decorateProgram(program4); + program4.parse(argv); +} +async function killAllDaemons() { + const platform = import_os24.default.platform(); + const pidFilterEnv = process.env.PWTEST_KILL_ALL_PID_FILTER_FOR_TEST; + const pidFilter = pidFilterEnv ? new Set(pidFilterEnv.split(",").map((p) => parseInt(p, 10)).filter((n) => !isNaN(n))) : void 0; + const killed = []; + try { + if (platform === "win32") { + const clauses = [`(${daemonProcessPatterns.map((p) => `$_.CommandLine -like '*${p}*'`).join(" -or ")})`]; + if (pidFilter) + clauses.push(`(${[...pidFilter].map((p) => `$_.ProcessId -eq ${p}`).join(" -or ")})`); + const whereClause = clauses.join(" -and "); + const result2 = (0, import_child_process5.execSync)( + `powershell -NoProfile -NonInteractive -Command "Get-CimInstance Win32_Process | Where-Object { ${whereClause} } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue; $_.ProcessId }"`, + { encoding: "utf-8" } + ); + const pids = result2.split("\n").map((line) => line.trim()).filter((line) => /^\d+$/.test(line)); + for (const pid of pids) + killed.push(parseInt(pid, 10)); + } else { + const result2 = (0, import_child_process5.execSync)("ps auxww", { encoding: "utf-8" }); + const lines = result2.split("\n"); + for (const line of lines) { + if (daemonProcessPatterns.some((p) => line.includes(p))) { + const parts = line.trim().split(/\s+/); + const pid = parts[1]; + if (pid && /^\d+$/.test(pid)) { + const numericPid = parseInt(pid, 10); + if (pidFilter && !pidFilter.has(numericPid)) + continue; + try { + process.kill(numericPid, "SIGKILL"); + killed.push(numericPid); + } catch { + } + } + } + } + } + } catch (e) { + } + return killed; +} +async function collectList(registry2, clientInfo, all) { + const browsers = []; + const entries = registry2.entryMap(); + const serverEntries = await serverRegistry.list(); + const key = clientKey(clientInfo); + for (const [workspaceKey, list] of entries) { + if (!all && workspaceKey !== key) + continue; + for (const entry of list) { + const session2 = new Session2(entry); + const canConnect = await session2.canConnect(); + if (!canConnect) { + await session2.deleteSessionConfig(); + continue; + } + const config = session2.config; + const channel = config.browser?.launchOptions.channel ?? config.browser?.browserName; + browsers.push({ + name: session2.name, + workspace: workspaceKey, + status: canConnect ? "open" : "closed", + browserType: channel, + userDataDir: config.browser?.userDataDir ?? null, + headed: config.browser ? !config.browser.launchOptions.headless : void 0, + persistent: !!config.cli.persistent, + attached: !!config.attached, + compatible: session2.isCompatible(clientInfo), + version: config.version + }); + } + } + if (!all) + return { all, browsers }; + const servers = [...serverEntries.values()].flat(); + return { all, browsers, servers, channelSessions: await listChannelSessions() }; +} +function validateFlags(args, command, output) { + const unknownFlags = []; + for (const key of Object.keys(args)) { + if (key === "_") + continue; + if (globalOptions.includes(key)) + continue; + if (!(key in command.flags)) + unknownFlags.push(key); + } + if (unknownFlags.length) + output.errorUnknownOption(unknownFlags, command.help); +} +function validateArgs(args, command, output) { + const positional = args._.slice(1); + if (positional.length > command.args.length) + output.errorTooManyArguments(command.args.length, positional.length, command.help); +} +var import_child_process5, import_os24, import_path51, globalOptions, booleanOptions, daemonProcessPatterns; +var init_program = __esm({ + "packages/playwright-core/src/tools/cli-client/program.ts"() { + "use strict"; + import_child_process5 = require("child_process"); + import_os24 = __toESM(require("os")); + import_path51 = __toESM(require("path")); + init_channelSessions(); + init_output(); + init_registry2(); + init_session(); + init_package(); + init_serverRegistry(); + init_minimist(); + globalOptions = [ + "json", + "raw", + "session" + ]; + booleanOptions = [ + "all", + "help", + "json", + "raw", + "version" + ]; + daemonProcessPatterns = ["run-mcp-server", "run-cli-server", "cli-daemon", "cliDaemon.js", "dashboardApp.js"]; + } +}); + +// packages/playwright-core/src/cli/program.ts +var program_exports = {}; +__export(program_exports, { + decorateProgram: () => decorateProgram +}); +function decorateProgram(program4) { + program4.version("Version " + (process.env.PW_CLI_DISPLAY_VERSION || packageJSON.version)).name(buildBasePlaywrightCLICommand(process.env.PW_LANG_NAME)); + program4.command("mark-docker-image [dockerImageNameTemplate]", { hidden: true }).description("mark docker image").allowUnknownOption(true).action(async function(dockerImageNameTemplate) { + markDockerImage(dockerImageNameTemplate).catch(logErrorAndExit); + }); + commandWithOpenOptions("open [url]", "open page in browser specified via -b, --browser", []).action(async function(url2, options) { + open4(options, url2).catch(logErrorAndExit); + }).addHelpText("afterAll", ` + Examples: + + $ open + $ open -b webkit https://example.com`); + commandWithOpenOptions( + "codegen [url]", + "open page and generate code for user actions", + [ + ["-o, --output ", "saves the generated script to a file"], + ["--target ", `language to generate, one of javascript, playwright-test, python, python-async, python-pytest, csharp, csharp-mstest, csharp-nunit, csharp-xunit, java, java-junit`, codegenId()], + ["--test-id-attribute ", "use the specified attribute to generate data test ID selectors"] + ] + ).action(async function(url2, options) { + await codegen(options, url2); + }).addHelpText("afterAll", ` + Examples: + + $ codegen + $ codegen --target=python + $ codegen -b webkit https://example.com`); + program4.command("install [browser...]").description("ensure browsers necessary for this version of Playwright are installed").option("--with-deps", "install system dependencies for browsers").option("--dry-run", "do not execute installation, only print information").option("--list", "prints list of browsers from all playwright installations").option("--force", "force reinstall of already installed browsers").option("--only-shell", "only install headless shell when installing chromium").option("--no-shell", "do not install chromium headless shell").option("--no-progress", "do not show download progress bars").action(async function(args, options) { + try { + await installBrowsers(args, options); + } catch (e) { + console.log(`Failed to install browsers +${e}`); + gracefullyProcessExitDoNotHang(1); + } + }).addHelpText("afterAll", ` + + Examples: + - $ install + Install default browsers. + + - $ install chrome firefox + Install custom browsers, supports chromium, firefox, webkit, chromium-headless-shell.`); + program4.command("uninstall").description("Removes browsers used by this installation of Playwright from the system (chromium, firefox, webkit, ffmpeg). This does not include branded channels.").option("--all", "Removes all browsers used by any Playwright installation from the system.").action(async (options) => { + uninstallBrowsers(options).catch(logErrorAndExit); + }); + program4.command("install-deps [browser...]").description("install dependencies necessary to run browsers (will ask for sudo permissions)").option("--dry-run", "Do not modify the system. On Linux, simulate the install via apt-get and exit with a non-zero code if any required packages are missing; on Windows, print the install command.").action(async function(args, options) { + try { + await installDeps(args, options); + } catch (e) { + console.log(`Failed to install browser dependencies +${e}`); + gracefullyProcessExitDoNotHang(1); + } + }).addHelpText("afterAll", ` + Examples: + - $ install-deps + Install dependencies for default browsers. + + - $ install-deps chrome firefox + Install dependencies for specific browsers, supports chromium, firefox, webkit, chromium-headless-shell. + + - $ install-deps --dry-run + Report which required system dependencies are missing without modifying the system. Exits non-zero if any are missing. Useful for non-interactive verification scripts.`); + const browsers = [ + { alias: "cr", name: "Chromium", type: "chromium" }, + { alias: "ff", name: "Firefox", type: "firefox" }, + { alias: "wk", name: "WebKit", type: "webkit" } + ]; + for (const { alias, name, type: type3 } of browsers) { + commandWithOpenOptions(`${alias} [url]`, `open page in ${name}`, []).action(async function(url2, options) { + open4({ ...options, browser: type3 }, url2).catch(logErrorAndExit); + }).addHelpText("afterAll", ` + Examples: + + $ ${alias} https://example.com`); + } + commandWithOpenOptions( + "screenshot ", + "capture a page screenshot", + [ + ["--wait-for-selector ", "wait for selector before taking a screenshot"], + ["--wait-for-timeout ", "wait for timeout in milliseconds before taking a screenshot"], + ["--full-page", "whether to take a full page screenshot (entire scrollable area)"] + ] + ).action(async function(url2, filename, command) { + screenshot3(command, command, url2, filename).catch(logErrorAndExit); + }).addHelpText("afterAll", ` + Examples: + + $ screenshot -b webkit https://example.com example.png`); + commandWithOpenOptions( + "pdf ", + "save page as pdf", + [ + ["--paper-format ", "paper format: Letter, Legal, Tabloid, Ledger, A0, A1, A2, A3, A4, A5, A6"], + ["--wait-for-selector ", "wait for given selector before saving as pdf"], + ["--wait-for-timeout ", "wait for given timeout in milliseconds before saving as pdf"] + ] + ).action(async function(url2, filename, options) { + pdf2(options, options, url2, filename).catch(logErrorAndExit); + }).addHelpText("afterAll", ` + Examples: + + $ pdf https://example.com example.pdf`); + program4.command("run-driver", { hidden: true }).action(async function(options) { + runDriver(); + }); + program4.command("run-server", { hidden: true }).option("--port ", "Server port").option("--host ", "Server host").option("--path ", "Endpoint Path", "/").option("--max-clients ", "Maximum clients").option("--mode ", 'Server mode, either "default" or "extension"').option("--artifacts-dir ", "Artifacts directory").option("--unsafe", "Allow clients to set unsafe launch options (args, executablePath, ignoreAllDefaultArgs, etc)").action(async function(options) { + runServer({ + port: options.port ? +options.port : void 0, + host: options.host, + path: options.path, + maxConnections: options.maxClients ? +options.maxClients : Infinity, + extension: options.mode === "extension" || !!process.env.PW_EXTENSION_MODE, + artifactsDir: options.artifactsDir, + unsafe: !!options.unsafe + }).catch(logErrorAndExit); + }); + program4.command("print-api-json", { hidden: true }).action(async function(options) { + printApiJson(); + }); + program4.command("launch-server", { hidden: true }).requiredOption("--browser ", 'Browser name, one of "chromium", "firefox" or "webkit"').option("--config ", "JSON file with launchServer options").action(async function(options) { + launchBrowserServer(options.browser, options.config); + }); + program4.command("show-trace [trace]").option("-b, --browser ", "browser to use, one of cr, chromium, ff, firefox, wk, webkit", "chromium").option("-h, --host ", "Host to serve trace on; specifying this option opens trace in a browser tab").option("-p, --port ", "Port to serve trace on, 0 for any free port; specifying this option opens trace in a browser tab").option("--stdin", "Accept trace URLs over stdin to update the viewer").description("show trace viewer").action(async function(trace, options) { + if (options.browser === "cr") + options.browser = "chromium"; + if (options.browser === "ff") + options.browser = "firefox"; + if (options.browser === "wk") + options.browser = "webkit"; + const openOptions = { + host: options.host, + port: +options.port, + isServer: !!options.stdin + }; + if (options.port !== void 0 || options.host !== void 0) + runTraceInBrowser(trace, openOptions).catch(logErrorAndExit); + else + runTraceViewerApp(trace, options.browser, openOptions).catch(logErrorAndExit); + }).addHelpText("afterAll", ` + Examples: + + $ show-trace + $ show-trace https://example.com/trace.zip`); + addTraceCommands(program4, logErrorAndExit); + program4.command("cli", { hidden: true }).allowExcessArguments(true).allowUnknownOption(true).helpOption(false).action(async (options) => { + process.argv.splice(process.argv.indexOf("cli"), 1); + program2().catch(logErrorAndExit); + }); +} +function logErrorAndExit(e) { + if (process.env.PWDEBUGIMPL) + console.error(e); + else + console.error(e.name + ": " + e.message); + gracefullyProcessExitDoNotHang(1); +} +function codegenId() { + return process.env.PW_LANG_NAME || "playwright-test"; +} +function commandWithOpenOptions(command, description, options) { + let result2 = program3.command(command).description(description); + for (const option of options) + result2 = result2.option(option[0], ...option.slice(1)); + return result2.option("-b, --browser ", "browser to use, one of cr, chromium, ff, firefox, wk, webkit", "chromium").option("--block-service-workers", "block service workers").option("--channel ", 'Chromium distribution channel, "chrome", "chrome-beta", "msedge-dev", etc').option("--color-scheme ", 'emulate preferred color scheme, "light" or "dark"').option("--device ", 'emulate device, for example "iPhone 11"').option("--geolocation ", 'specify geolocation coordinates, for example "37.819722,-122.478611"').option("--ignore-https-errors", "ignore https errors").option("--load-storage ", "load context storage state from the file, previously saved with --save-storage").option("--lang ", 'specify language / locale, for example "en-GB"').option("--proxy-server ", 'specify proxy server, for example "http://myproxy:3128" or "socks5://myproxy:8080"').option("--proxy-bypass ", 'comma-separated domains to bypass proxy, for example ".com,chromium.org,.domain.com"').option("--save-har ", "save HAR file with all network activity at the end").option("--save-har-glob ", "filter entries in the HAR by matching url against this glob pattern").option("--save-storage ", "save context storage state at the end, for later use with --load-storage").option("--timezone ", 'time zone to emulate, for example "Europe/Rome"').option("--timeout ", "timeout for Playwright actions in milliseconds, no timeout by default").option("--user-agent ", "specify user agent string").option("--user-data-dir ", "use the specified user data directory instead of a new context").option("--viewport-size ", 'specify browser viewport size in pixels, for example "1280, 720"'); +} +function buildBasePlaywrightCLICommand(cliTargetLang) { + switch (cliTargetLang) { + case "python": + return `playwright`; + case "java": + return `mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="...options.."`; + case "csharp": + return `pwsh bin/Debug/netX/playwright.ps1`; + default: { + const packageManagerCommand = getPackageManagerExecCommand(); + return `${packageManagerCommand} playwright`; + } + } +} +var program3; +var init_program2 = __esm({ + "packages/playwright-core/src/cli/program.ts"() { + "use strict"; + init_bootstrap(); + init_processLauncher(); + init_env(); + init_package(); + init_traceCli(); + init_driver(); + init_installActions(); + init_browserActions(); + init_installActions(); + init_traceViewer(); + init_browserActions(); + init_program(); + ({ program: program3 } = require("./utilsBundle")); + } +}); + +// packages/playwright-core/src/cli/programWithTestStub.ts +var programWithTestStub_exports = {}; +__export(programWithTestStub_exports, { + decorateProgram: () => decorateProgram2 +}); +function printPlaywrightTestError(command) { + const packages = []; + for (const pkg of ["playwright", "playwright-chromium", "playwright-firefox", "playwright-webkit"]) { + try { + require.resolve(pkg); + packages.push(pkg); + } catch (e) { + } + } + if (!packages.length) + packages.push("playwright"); + const packageManager = getPackageManager(); + if (packageManager === "yarn") { + console.error(`Please install @playwright/test package before running "yarn playwright ${command}"`); + console.error(` yarn remove ${packages.join(" ")}`); + console.error(" yarn add -D @playwright/test"); + } else if (packageManager === "pnpm") { + console.error(`Please install @playwright/test package before running "pnpm exec playwright ${command}"`); + console.error(` pnpm remove ${packages.join(" ")}`); + console.error(" pnpm add -D @playwright/test"); + } else { + console.error(`Please install @playwright/test package before running "npx playwright ${command}"`); + console.error(` npm uninstall ${packages.join(" ")}`); + console.error(" npm install -D @playwright/test"); + } +} +function addExternalPlaywrightTestCommands(program4) { + for (const [command, description] of kExternalPlaywrightTestCommands) { + const playwrightTest = program4.command(command).allowUnknownOption(true).allowExcessArguments(true); + playwrightTest.description(`${description} Available in @playwright/test package.`); + playwrightTest.action(async () => { + printPlaywrightTestError(command); + gracefullyProcessExitDoNotHang(1); + }); + } +} +function decorateProgram2(program4) { + if (!process.env.PW_LANG_NAME) + addExternalPlaywrightTestCommands(program4); +} +var kExternalPlaywrightTestCommands; +var init_programWithTestStub = __esm({ + "packages/playwright-core/src/cli/programWithTestStub.ts"() { + "use strict"; + init_processLauncher(); + init_env(); + kExternalPlaywrightTestCommands = [ + ["test", "Run tests with Playwright Test."], + ["show-report", "Show Playwright Test HTML report."], + ["merge-reports", "Merge Playwright Test Blob reports"] + ]; + } +}); + +// packages/playwright-core/src/outofprocess.ts +var outofprocess_exports = {}; +__export(outofprocess_exports, { + start: () => start +}); +async function start(env = {}) { + const client = new PlaywrightClient(env); + const playwright2 = await client._playwright; + playwright2.driverProcess = client._driverProcess; + return { playwright: playwright2, stop: () => client.stop() }; +} +var childProcess4, import_path52, PlaywrightClient; +var init_outofprocess = __esm({ + "packages/playwright-core/src/outofprocess.ts"() { + "use strict"; + childProcess4 = __toESM(require("child_process")); + import_path52 = __toESM(require("path")); + init_pipeTransport(); + init_nodePlatform(); + init_manualPromise(); + init_connection(); + init_package(); + PlaywrightClient = class { + constructor(env) { + this._closePromise = new ManualPromise(); + this._driverProcess = childProcess4.fork(import_path52.default.join(packageRoot, "cli.js"), ["run-driver"], { + stdio: "pipe", + detached: true, + env: { + ...process.env, + ...env + } + }); + this._driverProcess.unref(); + this._driverProcess.stderr.on("data", (data) => process.stderr.write(data)); + const connection = new Connection(nodePlatform(packageRoot)); + const transport = new PipeTransport(this._driverProcess.stdin, this._driverProcess.stdout); + connection.onmessage = (message) => transport.send(JSON.stringify(message)); + transport.onmessage = (message) => connection.dispatch(JSON.parse(message)); + transport.onclose = () => this._closePromise.resolve(); + this._playwright = connection.initializePlaywright(); + } + async stop() { + this._driverProcess.stdin.destroy(); + this._driverProcess.stdout.destroy(); + this._driverProcess.stderr.destroy(); + await this._closePromise; + } + }; + } +}); + +// packages/playwright-core/src/tools/mcp/log.ts +function logUnhandledError(error) { + errorDebug(error); +} +var debug12, errorDebug, testDebug2; +var init_log = __esm({ + "packages/playwright-core/src/tools/mcp/log.ts"() { + "use strict"; + debug12 = require("./utilsBundle").debug; + errorDebug = debug12("pw:mcp:error"); + testDebug2 = debug12("pw:mcp:test"); + } +}); + +// packages/playwright-core/src/tools/mcp/watchdog.ts +function setupExitWatchdog() { + let isExiting = false; + const handleExit = async (signal) => { + if (isExiting) + return; + isExiting = true; + setTimeout(() => process.exit(0), 15e3); + testDebug2("gracefully closing " + gracefullyCloseSet.size); + await gracefullyCloseAll(); + process.exit(0); + }; + process.stdin.on("close", () => handleExit("close")); + process.on("SIGINT", () => handleExit("SIGINT")); + process.on("SIGTERM", () => handleExit("SIGTERM")); +} +var init_watchdog = __esm({ + "packages/playwright-core/src/tools/mcp/watchdog.ts"() { + "use strict"; + init_processLauncher(); + init_log(); + } +}); + +// packages/playwright-core/src/tools/utils/mcp/http.ts +async function startMcpHttpServer(config, serverBackendFactory, allowedHosts) { + const httpServer = createHttpServer(); + await startHttpServer(httpServer, config); + return await installHttpTransport(httpServer, serverBackendFactory, allowedHosts); +} +function addressToString(address, options) { + (0, import_assert54.default)(address, "Could not bind server socket"); + if (typeof address === "string") + throw new Error("Unexpected address type: " + address); + let host = urlHostFromAddress(address); + if (options.normalizeLoopback && (host === "0.0.0.0" || host === "[::]" || host === "[::1]" || host === "127.0.0.1")) + host = "localhost"; + return `${options.protocol}://${host}:${address.port}`; +} +async function installHttpTransport(httpServer, serverBackendFactory, allowedHosts) { + const url2 = addressToString(httpServer.address(), { protocol: "http", normalizeLoopback: true }); + const host = new URL(url2).host; + allowedHosts = (allowedHosts || [host]).map((h) => h.toLowerCase()); + const allowAnyHost = allowedHosts.includes("*"); + const sseSessions = /* @__PURE__ */ new Map(); + const streamableSessions = /* @__PURE__ */ new Map(); + httpServer.on("request", async (req, res) => { + if (!allowAnyHost) { + const host2 = req.headers.host?.toLowerCase(); + if (!host2) { + res.statusCode = 400; + return res.end("Missing host"); + } + if (!allowedHosts.includes(host2)) { + res.statusCode = 403; + return res.end("Access is only allowed at " + allowedHosts.join(", ")); + } + } + const url3 = new URL(`http://localhost${req.url}`); + if (url3.pathname === "/killkillkill") { + if (req.method !== "POST" || req.headers["x-pw-mcp-kill"] !== "1") { + res.statusCode = 405; + return res.end(); + } + res.statusCode = 200; + res.end("Killing process"); + process.emit("SIGINT"); + return; + } + if (url3.pathname.startsWith("/sse")) + await handleSSE(serverBackendFactory, req, res, url3, sseSessions); + else + await handleStreamable(serverBackendFactory, req, res, streamableSessions); + }); + return url2; +} +async function handleSSE(serverBackendFactory, req, res, url2, sessions) { + if (req.method === "POST") { + const sessionId = url2.searchParams.get("sessionId"); + if (!sessionId) { + res.statusCode = 400; + return res.end("Missing sessionId"); + } + const transport = sessions.get(sessionId); + if (!transport) { + res.statusCode = 404; + return res.end("Session not found"); + } + return await transport.handlePostMessage(req, res); + } else if (req.method === "GET") { + const transport = new SSEServerTransport("/sse", res); + sessions.set(transport.sessionId, transport); + testDebug3(`create SSE session`); + await connect(serverBackendFactory, transport, false); + res.on("close", () => { + testDebug3(`delete SSE session`); + sessions.delete(transport.sessionId); + }); + return; + } + res.statusCode = 405; + res.end("Method not allowed"); +} +async function handleStreamable(serverBackendFactory, req, res, sessions) { + const sessionId = req.headers["mcp-session-id"]; + if (sessionId) { + const transport = sessions.get(sessionId); + if (!transport) { + res.statusCode = 404; + res.end("Session not found"); + return; + } + return await transport.handleRequest(req, res); + } + if (req.method === "POST") { + const transport = new StreamableHTTPServerTransport({ + sessionIdGenerator: () => import_crypto27.default.randomUUID(), + onsessioninitialized: async (sessionId2) => { + testDebug3(`create http session`); + await connect(serverBackendFactory, transport, true); + sessions.set(sessionId2, transport); + } + }); + transport.onclose = () => { + if (!transport.sessionId) + return; + sessions.delete(transport.sessionId); + testDebug3(`delete http session`); + }; + await transport.handleRequest(req, res); + return; + } + res.statusCode = 400; + res.end("Invalid request"); +} +var import_assert54, import_crypto27, debug13, SSEServerTransport, StreamableHTTPServerTransport, testDebug3; +var init_http = __esm({ + "packages/playwright-core/src/tools/utils/mcp/http.ts"() { + "use strict"; + import_assert54 = __toESM(require("assert")); + import_crypto27 = __toESM(require("crypto")); + init_httpServer(); + init_network(); + init_server2(); + debug13 = require("./utilsBundle").debug; + ({ SSEServerTransport } = require("./utilsBundle")); + ({ StreamableHTTPServerTransport } = require("./utilsBundle")); + testDebug3 = debug13("pw:mcp:test"); + } +}); + +// packages/playwright-core/src/tools/utils/mcp/tool.ts +function toMcpTool(tool) { + const readOnly = tool.type === "readOnly" || tool.type === "assertion"; + return { + name: tool.name, + description: tool.description, + inputSchema: zod.toJSONSchema(tool.inputSchema), + annotations: { + title: tool.title, + readOnlyHint: readOnly, + destructiveHint: !readOnly, + openWorldHint: true + } + }; +} +var zod; +var init_tool2 = __esm({ + "packages/playwright-core/src/tools/utils/mcp/tool.ts"() { + "use strict"; + zod = require("./utilsBundle").z; + } +}); + +// packages/playwright-core/src/tools/utils/mcp/server.ts +async function connect(factory, transport, runHeartbeat) { + const server = createServer(factory.name, factory.version, factory, runHeartbeat); + await server.connect(transport); +} +function createServer(name, version3, factory, runHeartbeat) { + const server = new Server({ name, version: version3 }, { + capabilities: { + tools: {} + } + }); + server.setRequestHandler(ListToolsRequestSchema, async () => { + serverDebug("listTools"); + return { tools: factory.toolSchemas.map((s) => toMcpTool(s)) }; + }); + let backendPromise; + const onClose = () => backendPromise?.then((b) => backendManager.disposeBackend(b)).catch(serverDebug); + addServerListener(server, "close", onClose); + server.setRequestHandler(CallToolRequestSchema, async (request2, extra) => { + serverDebug("callTool", request2); + try { + if (!backendPromise) { + backendPromise = initializeServer(server, factory, runHeartbeat).catch((e) => { + backendPromise = void 0; + throw e; + }); + } + const backend = await backendPromise; + const toolResult = await backend.callTool(request2.params.name, request2.params.arguments || {}, extra.signal); + if (toolResult.isClose) { + await backendManager.disposeBackend(backend).catch(serverDebug); + backendPromise = void 0; + delete toolResult.isClose; + } + const mergedResult = mergeTextParts(toolResult); + serverDebugResponse("callResult", mergedResult); + return mergedResult; + } catch (error) { + return { + content: [{ type: "text", text: "### Error\n" + String(error) }], + isError: true + }; + } + }); + return server; +} +function addServerListener(server, event, listener) { + const oldListener = server[`on${event}`]; + server[`on${event}`] = () => { + oldListener?.(); + listener(); + }; +} +async function start2(serverBackendFactory, options = {}) { + if (options.port === void 0) { + const transport = new StdioServerTransport(); + process.stdin.on("end", () => void transport.close()); + await connect(serverBackendFactory, transport, false); + return; + } + const url2 = await startMcpHttpServer(options, serverBackendFactory, options.allowedHosts); + const mcpConfig = { mcpServers: {} }; + mcpConfig.mcpServers[serverBackendFactory.nameInConfig] = { + url: `${url2}/mcp` + }; + const message = [ + `Listening on ${url2}`, + "Put this in your client config:", + JSON.stringify(mcpConfig, void 0, 2), + "For legacy SSE transport support, you can use the /sse endpoint instead." + ].join("\n"); + console.error(message); +} +function firstRootPath(roots) { + return allRootPaths(roots)[0]; +} +function allRootPaths(roots) { + const paths = []; + for (const root of roots) { + const url2 = new URL(root.uri); + let rootPath; + try { + rootPath = (0, import_url2.fileURLToPath)(url2); + } catch (e) { + if (e.code === "ERR_INVALID_FILE_URL_PATH" && process.platform === "win32") + rootPath = decodeURIComponent(url2.pathname); + } + if (!rootPath) + continue; + paths.push(rootPath); + } + if (paths.length === 0) + paths.push(process.cwd()); + return paths; +} +function mergeTextParts(result2) { + const content = []; + const testParts = []; + for (const part of result2.content) { + if (part.type === "text") { + testParts.push(part.text); + continue; + } + if (testParts.length > 0) { + content.push({ type: "text", text: testParts.join("\n") }); + testParts.length = 0; + } + content.push(part); + } + if (testParts.length > 0) + content.push({ type: "text", text: testParts.join("\n") }); + return { + ...result2, + content + }; +} +var import_url2, CallToolRequestSchema, ListToolsRequestSchema, debug14, Server, StdioServerTransport, serverDebug, serverDebugResponse, BackendManager, backendManager, initializeServer, startHeartbeat; +var init_server2 = __esm({ + "packages/playwright-core/src/tools/utils/mcp/server.ts"() { + "use strict"; + import_url2 = require("url"); + init_http(); + init_tool2(); + ({ CallToolRequestSchema, ListToolsRequestSchema } = require("./utilsBundle")); + debug14 = require("./utilsBundle").debug; + ({ Server } = require("./utilsBundle")); + ({ StdioServerTransport } = require("./utilsBundle")); + serverDebug = debug14("pw:mcp:server"); + serverDebugResponse = debug14("pw:mcp:server:response"); + BackendManager = class { + constructor() { + this._backends = /* @__PURE__ */ new Map(); + } + async createBackend(factory, clientInfo) { + const backend = await factory.create(clientInfo); + await backend.initialize?.(clientInfo); + this._backends.set(backend, factory); + return backend; + } + async disposeBackend(backend) { + const factory = this._backends.get(backend); + if (!factory) + return; + await backend.dispose?.(); + await factory.disposed(backend).catch(serverDebug); + this._backends.delete(backend); + } + }; + backendManager = new BackendManager(); + initializeServer = async (server, factory, runHeartbeat) => { + const capabilities = server.getClientCapabilities(); + let clientRoots = []; + if (capabilities?.roots) { + const { roots } = await server.listRoots().catch((e) => { + serverDebug(e); + return { roots: [] }; + }); + clientRoots = roots; + } + const clientInfo = { + cwd: firstRootPath(clientRoots), + clientName: server.getClientVersion()?.name ?? "Playwright MCP" + }; + const backend = await backendManager.createBackend(factory, clientInfo); + if (runHeartbeat) + startHeartbeat(server); + return backend; + }; + startHeartbeat = (server) => { + const beat = () => { + Promise.race([ + server.ping(), + new Promise((_, reject) => setTimeout(() => reject(new Error("ping timeout")), 5e3)) + ]).then(() => { + setTimeout(beat, 3e3); + }).catch(() => { + void server.close(); + }); + }; + beat(); + }; + } +}); + +// packages/playwright-core/src/tools/mcp/configIni.ts +function configFromIniFile(filePath) { + const content = import_fs54.default.readFileSync(filePath, "utf8"); + const parsed = ini.parse(content); + return iniEntriesToConfig(parsed); +} +function iniEntriesToConfig(entries) { + const config = {}; + for (const [targetPath, rawValue] of Object.entries(entries)) { + const type3 = longhandTypes[targetPath]; + const value2 = type3 ? coerceToType(rawValue, type3) : coerceIniValue(rawValue); + setNestedValue(config, targetPath, value2); + } + return config; +} +function coerceToType(value2, type3) { + switch (type3) { + case "string": + return String(value2); + case "number": + return Number(value2); + case "boolean": + if (typeof value2 === "boolean") + return value2; + return value2 === "true" || value2 === "1"; + case "string[]": + if (Array.isArray(value2)) + return value2.map(String); + return [String(value2)]; + case "size": { + if (typeof value2 === "string" && value2.includes("x")) { + const [w, h] = value2.split("x").map(Number); + if (!isNaN(w) && !isNaN(h) && w > 0 && h > 0) + return { width: w, height: h }; + } + return void 0; + } + } +} +function coerceIniValue(value2) { + if (typeof value2 !== "string") + return value2; + const trimmed = value2.trim(); + if (trimmed === "") + return trimmed; + const num = Number(trimmed); + if (!isNaN(num)) + return num; + return value2; +} +function setNestedValue(obj, dotPath, value2) { + const parts = dotPath.split("."); + let current = obj; + for (let i = 0; i < parts.length - 1; i++) { + const part = parts[i]; + if (!(part in current) || typeof current[part] !== "object" || current[part] === null) + current[part] = {}; + current = current[part]; + } + current[parts[parts.length - 1]] = value2; +} +var import_fs54, ini, longhandTypes; +var init_configIni = __esm({ + "packages/playwright-core/src/tools/mcp/configIni.ts"() { + "use strict"; + import_fs54 = __toESM(require("fs")); + ini = require("./utilsBundle").ini; + longhandTypes = { + // browser direct + "browser.browserName": "string", + "browser.isolated": "boolean", + "browser.userDataDir": "string", + "browser.cdpEndpoint": "string", + "browser.cdpTimeout": "number", + "browser.remoteEndpoint": "string", + "browser.initPage": "string[]", + "browser.initScript": "string[]", + // browser.launchOptions + "browser.launchOptions.channel": "string", + "browser.launchOptions.headless": "boolean", + "browser.launchOptions.executablePath": "string", + "browser.launchOptions.chromiumSandbox": "boolean", + "browser.launchOptions.args": "string[]", + "browser.launchOptions.downloadsPath": "string", + "browser.launchOptions.handleSIGHUP": "boolean", + "browser.launchOptions.handleSIGINT": "boolean", + "browser.launchOptions.handleSIGTERM": "boolean", + "browser.launchOptions.slowMo": "number", + "browser.launchOptions.timeout": "number", + "browser.launchOptions.tracesDir": "string", + "browser.launchOptions.proxy.server": "string", + "browser.launchOptions.proxy.bypass": "string", + "browser.launchOptions.proxy.username": "string", + "browser.launchOptions.proxy.password": "string", + // browser.contextOptions + "browser.contextOptions.acceptDownloads": "boolean", + "browser.contextOptions.baseURL": "string", + "browser.contextOptions.bypassCSP": "boolean", + "browser.contextOptions.colorScheme": "string", + "browser.contextOptions.contrast": "string", + "browser.contextOptions.deviceScaleFactor": "number", + "browser.contextOptions.forcedColors": "string", + "browser.contextOptions.hasTouch": "boolean", + "browser.contextOptions.ignoreHTTPSErrors": "boolean", + "browser.contextOptions.isMobile": "boolean", + "browser.contextOptions.javaScriptEnabled": "boolean", + "browser.contextOptions.locale": "string", + "browser.contextOptions.offline": "boolean", + "browser.contextOptions.permissions": "string[]", + "browser.contextOptions.reducedMotion": "string", + "browser.contextOptions.screen": "size", + "browser.contextOptions.serviceWorkers": "string", + "browser.contextOptions.storageState": "string", + "browser.contextOptions.strictSelectors": "boolean", + "browser.contextOptions.timezoneId": "string", + "browser.contextOptions.userAgent": "string", + "browser.contextOptions.viewport": "size", + // top-level + "extension": "boolean", + "capabilities": "string[]", + "saveSession": "boolean", + "saveVideo": "size", + "sharedBrowserContext": "boolean", + "outputDir": "string", + "outputMaxSize": "number", + "imageResponses": "string", + "allowUnrestrictedFileAccess": "boolean", + "codegen": "string", + "testIdAttribute": "string", + // server + "server.port": "number", + "server.host": "string", + "server.allowedHosts": "string[]", + // console + "console.level": "string", + // network + "network.allowedOrigins": "string[]", + "network.blockedOrigins": "string[]", + // timeouts + "timeouts.action": "number", + "timeouts.navigation": "number", + // snapshot + "snapshot.mode": "string" + }; + } +}); + +// packages/playwright-core/src/tools/mcp/config.ts +async function fileExistsAsync(resolved) { + try { + return (await import_fs55.default.promises.stat(resolved)).isFile(); + } catch { + return false; + } +} +async function resolveConfig(config) { + const merged = mergeConfig(defaultConfig, config); + const browser = await validateBrowserConfig(merged.browser); + return { ...merged, browser }; +} +async function resolveCLIConfigForMCP(cliOptions, env) { + const envOverrides = configFromEnv(env); + const cliOverrides = configFromCLIOptions(cliOptions); + const configFile = cliOverrides.configFile ?? envOverrides.configFile; + const configInFile = await loadConfig(configFile); + const configDir = configFile ? import_path53.default.dirname(import_path53.default.resolve(configFile)) : process.cwd(); + let result2 = defaultConfig; + result2 = mergeConfig(result2, resolveConfigPaths(configInFile, configDir)); + result2 = mergeConfig(result2, resolveConfigPaths(envOverrides, process.cwd())); + result2 = mergeConfig(result2, resolveConfigPaths(cliOverrides, process.cwd())); + const browser = await validateBrowserConfig(result2.browser); + if (browser.launchOptions.headless === void 0) + browser.launchOptions.headless = import_os25.default.platform() === "linux" && !process.env.DISPLAY; + validateOutputDir(result2.outputDir); + return { ...result2, browser, configFile }; +} +function validateOutputDir(outputDir2) { + if (!outputDir2) + return; + if (isSystemDirectory(outputDir2)) + throw new Error(`--output-dir cannot point to a system directory: ${import_path53.default.resolve(outputDir2)}.`); +} +async function resolveCLIConfigForCLI(daemonProfilesDir2, sessionName, options, env) { + const config = options.config ? import_path53.default.resolve(options.config) : void 0; + try { + const defaultConfigFile2 = import_path53.default.resolve(".playwright", "cli.config.json"); + if (!config && import_fs55.default.existsSync(defaultConfigFile2)) + options.config = defaultConfigFile2; + } catch { + } + const daemonOverrides = configFromCLIOptions({ + endpoint: options.endpoint, + cdpEndpoint: options.cdp, + config: options.config, + browser: options.browser, + headless: options.headed ? false : void 0, + extension: options.extension, + userDataDir: options.profile, + snapshotMode: "full" + }); + const envOverrides = configFromEnv(env); + const configFile = daemonOverrides.configFile ?? envOverrides.configFile; + const configInFile = await loadConfig(configFile); + const configDir = configFile ? import_path53.default.dirname(import_path53.default.resolve(configFile)) : process.cwd(); + const globalConfigPath = import_path53.default.join((env ?? process.env)["PWTEST_CLI_GLOBAL_CONFIG"] ?? import_os25.default.homedir(), ".playwright", "cli.config.json"); + const globalConfigExists = import_fs55.default.existsSync(globalConfigPath); + const globalConfigInFile = await loadConfig(globalConfigExists ? globalConfigPath : void 0); + const globalConfigDir = globalConfigExists ? import_path53.default.dirname(globalConfigPath) : process.cwd(); + let result2 = defaultConfig; + result2 = mergeConfig(result2, resolveConfigPaths(globalConfigInFile, globalConfigDir)); + result2 = mergeConfig(result2, resolveConfigPaths(configInFile, configDir)); + result2 = mergeConfig(result2, resolveConfigPaths(envOverrides, process.cwd())); + result2 = mergeConfig(result2, resolveConfigPaths(daemonOverrides, process.cwd())); + if (result2.browser.isolated === void 0) + result2.browser.isolated = !options.profile && !options.persistent && !result2.browser.userDataDir && !result2.browser.remoteEndpoint && !result2.browser.cdpEndpoint && !result2.extension; + if (result2.browser.launchOptions.headless === void 0) + result2.browser.launchOptions.headless = true; + const browser = await validateBrowserConfig(result2.browser); + validateOutputDir(result2.outputDir); + if (!result2.extension && !browser.isolated && !browser.userDataDir && !browser.remoteEndpoint && !browser.cdpEndpoint) { + const browserToken = browser.launchOptions?.channel ?? browser?.browserName; + const userDataDir = import_path53.default.resolve(daemonProfilesDir2, `ud-${sessionName}-${browserToken}`); + browser.userDataDir = userDataDir; + } + return { ...result2, browser, configFile, skillMode: true }; +} +function resolveExtensionOptions(cliOptions) { + const browser = cliOptions.browser ?? envToString(process.env.PLAYWRIGHT_MCP_BROWSER); + const { channel } = resolveBrowserParam(browser); + const executablePath = cliOptions.executablePath ?? envToString(process.env.PLAYWRIGHT_MCP_EXECUTABLE_PATH); + return { channel: channel ?? "chrome", executablePath }; +} +async function validateBrowserConfig(browser) { + let browserName = browser.browserName; + if (!browserName) { + browserName = "chromium"; + if (browser.launchOptions.channel === void 0) + browser.launchOptions.channel = "chrome"; + } + if (browser.browserName === "chromium" && browser.launchOptions.chromiumSandbox === void 0) { + if (process.platform === "linux") + browser.launchOptions.chromiumSandbox = browser.launchOptions.channel !== "chromium" && browser.launchOptions.channel !== "chrome-for-testing"; + else + browser.launchOptions.chromiumSandbox = true; + } + if (browser.isolated && browser.userDataDir) + throw new Error("Browser userDataDir is not supported in isolated mode."); + if (browser.initScript) { + for (const script of browser.initScript) { + if (!await fileExistsAsync(script)) + throw new Error(`Init script file does not exist: ${script}`); + } + } + if (browser.initPage) { + for (const page of browser.initPage) { + if (!await fileExistsAsync(page)) + throw new Error(`Init page file does not exist: ${page}`); + } + } + if (browser.contextOptions.viewport === void 0) { + if (browser.launchOptions.headless) + browser.contextOptions.viewport = { width: 1280, height: 720 }; + else + browser.contextOptions.viewport = null; + } + if (browserName === "chromium") { + browser.launchOptions.args = browser.launchOptions.args ?? []; + if (!browser.launchOptions.args.some((a) => a.includes("--disable-blink-features"))) + browser.launchOptions.args.push(`--disable-blink-features=AutomationControlled`); + } + return { ...browser, browserName }; +} +function resolveBrowserParam(browserOption) { + switch (browserOption) { + case "chrome": + case "chrome-beta": + case "chrome-canary": + case "chrome-dev": + case "msedge": + case "msedge-beta": + case "msedge-canary": + case "msedge-dev": + return { browserName: "chromium", channel: browserOption }; + case "chromium": + return { browserName: "chromium", channel: "chrome-for-testing" }; + case "firefox": + return { browserName: "firefox" }; + case "moz-firefox": + case "moz-firefox-beta": + case "moz-firefox-nightly": + return { browserName: "firefox", channel: browserOption }; + case "webkit": + return { browserName: "webkit" }; + default: + return {}; + } +} +function configFromCLIOptions(cliOptions) { + const { browserName, channel } = resolveBrowserParam(cliOptions.browser); + const launchOptions = { + channel, + executablePath: cliOptions.executablePath, + headless: cliOptions.headless + }; + if (cliOptions.sandbox !== void 0) + launchOptions.chromiumSandbox = cliOptions.sandbox; + if (cliOptions.device && cliOptions.cdpEndpoint) + throw new Error("Device emulation is not supported with cdpEndpoint."); + const contextOptions = cliOptions.device ? playwright.devices[cliOptions.device] : {}; + if (cliOptions.proxyServer) { + const proxy = { server: cliOptions.proxyServer }; + if (cliOptions.proxyBypass) + proxy.bypass = cliOptions.proxyBypass; + launchOptions.proxy = proxy; + contextOptions.proxy = proxy; + } + if (cliOptions.storageState) + contextOptions.storageState = cliOptions.storageState; + if (cliOptions.userAgent) + contextOptions.userAgent = cliOptions.userAgent; + if (cliOptions.viewportSize) + contextOptions.viewport = cliOptions.viewportSize; + if (cliOptions.ignoreHttpsErrors) + contextOptions.ignoreHTTPSErrors = true; + if (cliOptions.blockServiceWorkers) + contextOptions.serviceWorkers = "block"; + if (cliOptions.grantPermissions) + contextOptions.permissions = cliOptions.grantPermissions; + const config = { + browser: { + browserName, + isolated: cliOptions.isolated, + userDataDir: cliOptions.userDataDir, + launchOptions, + contextOptions, + cdpEndpoint: cliOptions.cdpEndpoint, + cdpHeaders: cliOptions.cdpHeader, + cdpTimeout: cliOptions.cdpTimeout, + initPage: cliOptions.initPage, + initScript: cliOptions.initScript, + remoteEndpoint: cliOptions.endpoint + }, + extension: cliOptions.extension, + server: { + port: cliOptions.port, + host: cliOptions.host, + allowedHosts: cliOptions.allowedHosts + }, + capabilities: cliOptions.caps, + console: { + level: cliOptions.consoleLevel + }, + network: { + allowedOrigins: cliOptions.allowedOrigins, + blockedOrigins: cliOptions.blockedOrigins + }, + allowUnrestrictedFileAccess: cliOptions.allowUnrestrictedFileAccess, + codegen: cliOptions.codegen, + saveSession: cliOptions.saveSession, + secrets: cliOptions.secrets, + sharedBrowserContext: cliOptions.sharedBrowserContext, + snapshot: cliOptions.snapshotMode ? { mode: cliOptions.snapshotMode } : void 0, + outputDir: cliOptions.outputDir, + outputMaxSize: cliOptions.outputMaxSize, + imageResponses: cliOptions.imageResponses, + testIdAttribute: cliOptions.testIdAttribute, + timeouts: { + action: cliOptions.timeoutAction, + navigation: cliOptions.timeoutNavigation + } + }; + if (cliOptions.remoteHeader) + config.browser.remoteHeaders = cliOptions.remoteHeader; + return { ...config, configFile: cliOptions.config }; +} +function configFromEnv(env) { + const e = env ?? process.env; + const options = {}; + options.allowedHosts = commaSeparatedList(e.PLAYWRIGHT_MCP_ALLOWED_HOSTS); + options.allowedOrigins = semicolonSeparatedList(e.PLAYWRIGHT_MCP_ALLOWED_ORIGINS); + options.allowUnrestrictedFileAccess = envToBoolean(e.PLAYWRIGHT_MCP_ALLOW_UNRESTRICTED_FILE_ACCESS); + options.blockedOrigins = semicolonSeparatedList(e.PLAYWRIGHT_MCP_BLOCKED_ORIGINS); + options.blockServiceWorkers = envToBoolean(e.PLAYWRIGHT_MCP_BLOCK_SERVICE_WORKERS); + options.browser = envToString(e.PLAYWRIGHT_MCP_BROWSER); + options.caps = commaSeparatedList(e.PLAYWRIGHT_MCP_CAPS); + options.cdpEndpoint = envToString(e.PLAYWRIGHT_MCP_CDP_ENDPOINT); + options.cdpHeader = headerParser(envToString(e.PLAYWRIGHT_MCP_CDP_HEADERS)); + options.cdpTimeout = numberParser(e.PLAYWRIGHT_MCP_CDP_TIMEOUT); + options.config = envToString(e.PLAYWRIGHT_MCP_CONFIG); + if (e.PLAYWRIGHT_MCP_CONSOLE_LEVEL) + options.consoleLevel = enumParser("--console-level", ["error", "warning", "info", "debug"], e.PLAYWRIGHT_MCP_CONSOLE_LEVEL); + options.device = envToString(e.PLAYWRIGHT_MCP_DEVICE); + options.executablePath = envToString(e.PLAYWRIGHT_MCP_EXECUTABLE_PATH); + options.extension = envToBoolean(e.PLAYWRIGHT_MCP_EXTENSION); + options.grantPermissions = commaSeparatedList(e.PLAYWRIGHT_MCP_GRANT_PERMISSIONS); + options.headless = envToBoolean(e.PLAYWRIGHT_MCP_HEADLESS); + options.host = envToString(e.PLAYWRIGHT_MCP_HOST); + options.ignoreHttpsErrors = envToBoolean(e.PLAYWRIGHT_MCP_IGNORE_HTTPS_ERRORS); + const initPage = envToString(e.PLAYWRIGHT_MCP_INIT_PAGE); + if (initPage) + options.initPage = [initPage]; + const initScript = envToString(e.PLAYWRIGHT_MCP_INIT_SCRIPT); + if (initScript) + options.initScript = [initScript]; + options.isolated = envToBoolean(e.PLAYWRIGHT_MCP_ISOLATED); + if (e.PLAYWRIGHT_MCP_IMAGE_RESPONSES) + options.imageResponses = enumParser("--image-responses", ["allow", "omit"], e.PLAYWRIGHT_MCP_IMAGE_RESPONSES); + options.sandbox = envToBoolean(e.PLAYWRIGHT_MCP_SANDBOX); + options.outputDir = envToString(e.PLAYWRIGHT_MCP_OUTPUT_DIR); + options.outputMaxSize = numberParser(e.PLAYWRIGHT_MCP_OUTPUT_MAX_SIZE); + options.port = numberParser(e.PLAYWRIGHT_MCP_PORT); + options.proxyBypass = envToString(e.PLAYWRIGHT_MCP_PROXY_BYPASS); + options.proxyServer = envToString(e.PLAYWRIGHT_MCP_PROXY_SERVER); + options.remoteHeader = headerParser(envToString(e.PLAYWRIGHT_MCP_REMOTE_HEADERS)); + options.secrets = dotenvFileLoader(e.PLAYWRIGHT_MCP_SECRETS_FILE); + options.storageState = envToString(e.PLAYWRIGHT_MCP_STORAGE_STATE); + options.testIdAttribute = envToString(e.PLAYWRIGHT_MCP_TEST_ID_ATTRIBUTE); + options.timeoutAction = numberParser(e.PLAYWRIGHT_MCP_TIMEOUT_ACTION); + options.timeoutNavigation = numberParser(e.PLAYWRIGHT_MCP_TIMEOUT_NAVIGATION); + options.userAgent = envToString(e.PLAYWRIGHT_MCP_USER_AGENT); + options.userDataDir = envToString(e.PLAYWRIGHT_MCP_USER_DATA_DIR); + options.viewportSize = resolutionParser("--viewport-size", e.PLAYWRIGHT_MCP_VIEWPORT_SIZE); + return configFromCLIOptions(options); +} +async function loadConfig(configFile) { + if (!configFile) + return {}; + if (configFile.endsWith(".ini")) + return configFromIniFile(configFile); + try { + const data = await import_fs55.default.promises.readFile(configFile, "utf8"); + return JSON.parse(data.charCodeAt(0) === 65279 ? data.slice(1) : data); + } catch { + return configFromIniFile(configFile); + } +} +function resolveConfigPaths(config, baseDir) { + if (config.browser?.initPage) + config.browser.initPage = config.browser.initPage.map((p) => import_path53.default.resolve(baseDir, p)); + if (config.browser?.initScript) + config.browser.initScript = config.browser.initScript.map((p) => import_path53.default.resolve(baseDir, p)); + return config; +} +function pickDefined(obj) { + return Object.fromEntries( + Object.entries(obj ?? {}).filter(([_, v]) => v !== void 0) + ); +} +function mergeConfig(base, overrides) { + const browser = { + ...pickDefined(base.browser), + ...pickDefined(overrides.browser), + browserName: overrides.browser?.browserName ?? base.browser?.browserName, + isolated: overrides.browser?.isolated ?? base.browser?.isolated, + launchOptions: { + ...pickDefined(base.browser?.launchOptions), + ...pickDefined(overrides.browser?.launchOptions) + }, + contextOptions: { + ...pickDefined(base.browser?.contextOptions), + ...pickDefined(overrides.browser?.contextOptions) + } + }; + if (browser.browserName !== "chromium" && browser.launchOptions && !browser.launchOptions.channel?.startsWith("moz-")) + delete browser.launchOptions.channel; + return { + ...pickDefined(base), + ...pickDefined(overrides), + browser, + console: { + ...pickDefined(base.console), + ...pickDefined(overrides.console) + }, + network: { + ...pickDefined(base.network), + ...pickDefined(overrides.network) + }, + server: { + ...pickDefined(base.server), + ...pickDefined(overrides.server) + }, + snapshot: { + ...pickDefined(base.snapshot), + ...pickDefined(overrides.snapshot) + }, + timeouts: { + ...pickDefined(base.timeouts), + ...pickDefined(overrides.timeouts) + } + }; +} +function semicolonSeparatedList(value2) { + if (!value2) + return void 0; + return value2.split(";").map((v) => v.trim()); +} +function commaSeparatedList(value2) { + if (!value2) + return void 0; + return value2.split(",").map((v) => v.trim()); +} +function dotenvFileLoader(value2) { + if (!value2) + return void 0; + return dotenv2.parse(import_fs55.default.readFileSync(value2, "utf8")); +} +function numberParser(value2) { + if (!value2) + return void 0; + return +value2; +} +function resolutionParser(name, value2) { + if (!value2) + return void 0; + if (value2.includes("x")) { + const [width, height] = value2.split("x").map((v) => +v); + if (isNaN(width) || isNaN(height) || width <= 0 || height <= 0) + throw new Error(`Invalid resolution format: use ${name}="800x600"`); + return { width, height }; + } + if (value2.includes(",")) { + const [width, height] = value2.split(",").map((v) => +v); + if (isNaN(width) || isNaN(height) || width <= 0 || height <= 0) + throw new Error(`Invalid resolution format: use ${name}="800x600"`); + return { width, height }; + } + throw new Error(`Invalid resolution format: use ${name}="800x600"`); +} +function headerParser(arg, previous) { + if (!arg) + return previous; + const result2 = { ...previous ?? {} }; + const colonIndex = arg.indexOf(":"); + const name = colonIndex === -1 ? arg.trim() : arg.substring(0, colonIndex).trim(); + const value2 = colonIndex === -1 ? "" : arg.substring(colonIndex + 1).trim(); + result2[name] = value2; + return result2; +} +function enumParser(name, options, value2) { + if (!options.includes(value2)) + throw new Error(`Invalid ${name}: ${value2}. Valid values are: ${options.join(", ")}`); + return value2; +} +function envToBoolean(value2) { + if (value2 === "true" || value2 === "1") + return true; + if (value2 === "false" || value2 === "0") + return false; + return void 0; +} +function envToString(value2) { + return value2 ? value2.trim() : void 0; +} +var import_fs55, import_path53, import_os25, dotenv2, defaultConfig; +var init_config2 = __esm({ + "packages/playwright-core/src/tools/mcp/config.ts"() { + "use strict"; + import_fs55 = __toESM(require("fs")); + import_path53 = __toESM(require("path")); + import_os25 = __toESM(require("os")); + init_fileUtils(); + init_inprocess(); + init_configIni(); + dotenv2 = require("./utilsBundle").dotenv; + defaultConfig = { + browser: { + launchOptions: {}, + contextOptions: {} + }, + timeouts: { + action: 5e3, + navigation: 6e4, + expect: 5e3 + } + }; + } +}); + +// packages/playwright-core/src/tools/mcp/cdpRelayV1.ts +var ExtensionProtocolV1; +var init_cdpRelayV1 = __esm({ + "packages/playwright-core/src/tools/mcp/cdpRelayV1.ts"() { + "use strict"; + ExtensionProtocolV1 = class { + constructor(sendCommand) { + this._sendToCDPClient = null; + this._nextSessionId = 1; + this._sendCommand = sendCommand; + } + connectOverCDP(sendToCDPClient) { + this._sendToCDPClient = sendToCDPClient; + } + handleExtensionEvent(method, params2) { + switch (method) { + case "forwardCDPEvent": { + const p = params2; + const sessionId = p.sessionId || this._connectedTabInfo?.sessionId; + this._sendToCDPClient?.({ + sessionId, + method: p.method, + params: p.params + }); + break; + } + } + } + async handleCDPCommand(method, params2, sessionId) { + switch (method) { + case "Target.setAutoAttach": { + if (sessionId) + return void 0; + const { targetInfo } = await this._sendCommand("attachToTab", {}); + this._connectedTabInfo = { + targetInfo, + sessionId: `pw-tab-${this._nextSessionId++}` + }; + this._sendToCDPClient?.({ + method: "Target.attachedToTarget", + params: { + sessionId: this._connectedTabInfo.sessionId, + targetInfo: { + ...this._connectedTabInfo.targetInfo, + attached: true + }, + waitingForDebugger: false + } + }); + return { result: {} }; + } + case "Target.getTargetInfo": { + return { result: this._connectedTabInfo?.targetInfo }; + } + case "Target.createTarget": { + throw new Error("Tab creation is not supported yet."); + } + } + return void 0; + } + async forwardToExtension(method, params2, sessionId) { + if (this._connectedTabInfo?.sessionId === sessionId) + sessionId = void 0; + return await this._sendCommand("forwardCDPCommand", { sessionId, method, params: params2 }); + } + ready() { + return Promise.resolve(); + } + onExtensionDisconnect(_reason) { + } + }; + } +}); + +// packages/playwright-core/src/tools/mcp/browserModel.ts +var BrowserModel; +var init_browserModel = __esm({ + "packages/playwright-core/src/tools/mcp/browserModel.ts"() { + "use strict"; + init_log(); + BrowserModel = class { + constructor(sendToExtension) { + // Set only while a Playwright CDP connection is attached (see + // `connectOverCDP`). Before that, any attempt to emit to Playwright is a + // no-op โ the model is observation-only during the extension handshake. + this._sendToCDPClient = null; + // Tabs observed via chrome.tabs.onCreated, whether or not the debugger is attached yet. + this._knownTabs = /* @__PURE__ */ new Map(); + // Subset of _knownTabs we've attached the debugger to and assigned a sessionId. + this._tabSessions = /* @__PURE__ */ new Map(); + this._autoAttach = false; + this._nextSessionId = 1; + this._sendToExtension = sendToExtension; + } + // Wires the model's CDP output sink. Called by the handler once the + // extension handshake is done and a Playwright CDP client is ready to + // receive events. Before this call the model is observation-only โ + // `Target.attachedToTarget` etc. would be answered into a black hole. + connectOverCDP(sendToCDPClient) { + this._sendToCDPClient = sendToCDPClient; + } + _emit(message) { + this._sendToCDPClient?.(message); + } + // โโโ Extension โ model inputs โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ + onTabCreated(tab2) { + if (tab2.id === void 0) + return; + this._knownTabs.set(tab2.id, tab2); + if (this._autoAttach) + void this._attachTab(tab2.id).catch(logUnhandledError); + } + onTabRemoved(tabId) { + this._knownTabs.delete(tabId); + this._detachTab(tabId); + } + onDebuggerEvent(source11, method, params2) { + if (source11.tabId === void 0) + return; + const tabSession = this._tabSessions.get(source11.tabId); + if (!tabSession) + return; + const childSessionId = params2?.sessionId; + if (method === "Target.attachedToTarget" && childSessionId) + tabSession.childSessions.add(childSessionId); + else if (method === "Target.detachedFromTarget" && childSessionId) + tabSession.childSessions.delete(childSessionId); + const sessionId = source11.sessionId || tabSession.sessionId; + this._emit({ sessionId, method, params: params2 }); + } + onDebuggerDetach(source11) { + if (source11.tabId !== void 0) + this._detachTab(source11.tabId); + } + // โโโ Playwright โ model commands โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ + // Turn auto-attach on and attach to every tab we already know about. + // Called in response to Playwright's `Target.setAutoAttach` on the root session. + async enableAutoAttach() { + this._autoAttach = true; + const tabIds = [...this._knownTabs.keys()]; + await Promise.all(tabIds.map((tabId) => this._attachTab(tabId).catch(logUnhandledError))); + } + async createTarget(url2) { + const tab2 = await this._sendToExtension("chrome.tabs.create", [{ url: url2 }]); + if (tab2?.id === void 0) + throw new Error("Failed to create tab"); + this._knownTabs.set(tab2.id, tab2); + const tabSession = await this._attachTab(tab2.id); + return { targetId: tabSession.targetInfo?.targetId }; + } + async closeTarget(targetId) { + const tabSession = targetId ? this._findTabSession((s) => s.targetInfo?.targetId === targetId) : void 0; + if (!tabSession) + return { success: false }; + await this._sendToExtension("chrome.tabs.remove", [tabSession.tabId]); + return { success: true }; + } + getTargetInfo(sessionId) { + if (!sessionId) + return void 0; + return this._findTabSession((s) => s.sessionId === sessionId)?.targetInfo; + } + // Forward a browser-level CDP command (Storage.*, Browser.*, etc.) that is + // not associated with a specific tab. chrome.debugger.sendCommand requires a + // target, so we route through any attached tab; the command itself is + // browser-scoped and returns the same result regardless of which tab is used. + async sendBrowserCommand(method, params2) { + const tabSession = this._tabSessions.values().next().value; + if (!tabSession) + throw new Error(`No attached tab to forward browser-level command: ${method}`); + return await this._sendToExtension("chrome.debugger.sendCommand", [ + { tabId: tabSession.tabId }, + method, + params2 + ]); + } + // Forward a CDP command from Playwright to the tab its sessionId resolves to. + async sendCommand(sessionId, method, params2) { + let tabSession = this._findTabSession((s) => s.sessionId === sessionId); + let cdpSessionId; + if (!tabSession) { + tabSession = this._findTabSession((s) => s.childSessions.has(sessionId)); + cdpSessionId = sessionId; + } + if (!tabSession) + throw new Error(`No tab found for sessionId: ${sessionId}`); + return await this._sendToExtension("chrome.debugger.sendCommand", [ + { tabId: tabSession.tabId, sessionId: cdpSessionId }, + method, + params2 + ]); + } + // โโโ Internals โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ + async _attachTab(tabId) { + const existing = this._tabSessions.get(tabId); + if (existing) + return existing; + await this._sendToExtension("chrome.debugger.attach", [{ tabId }, "1.3"]); + const result2 = await this._sendToExtension("chrome.debugger.sendCommand", [ + { tabId }, + "Target.getTargetInfo" + ]); + const targetInfo = result2?.targetInfo; + const sessionId = `pw-tab-${this._nextSessionId++}`; + const tabSession = { tabId, sessionId, targetInfo, childSessions: /* @__PURE__ */ new Set() }; + this._tabSessions.set(tabId, tabSession); + this._emit({ + method: "Target.attachedToTarget", + params: { + sessionId, + targetInfo: { ...targetInfo, attached: true }, + waitingForDebugger: false + } + }); + return tabSession; + } + _detachTab(tabId) { + const tabSession = this._tabSessions.get(tabId); + if (!tabSession) + return; + this._tabSessions.delete(tabId); + this._emit({ + method: "Target.detachedFromTarget", + params: { + sessionId: tabSession.sessionId, + targetId: tabSession.targetInfo?.targetId + } + }); + } + _findTabSession(predicate) { + for (const session2 of this._tabSessions.values()) { + if (predicate(session2)) + return session2; + } + return void 0; + } + }; + } +}); + +// packages/playwright-core/src/tools/mcp/cdpRelayV2.ts +var ExtensionProtocolV2; +var init_cdpRelayV2 = __esm({ + "packages/playwright-core/src/tools/mcp/cdpRelayV2.ts"() { + "use strict"; + init_manualPromise(); + init_log(); + init_browserModel(); + ExtensionProtocolV2 = class { + constructor(sendCommand) { + // Resolved by `extension.initialized`. Purely a handshake signal for the + // relay โ the model itself is oblivious to this phase. + this._ready = new ManualPromise(); + this._model = new BrowserModel(sendCommand); + void this._ready.catch(logUnhandledError); + } + ready() { + return this._ready; + } + connectOverCDP(sendToCDPClient) { + this._model.connectOverCDP(sendToCDPClient); + } + onExtensionDisconnect(reason) { + if (!this._ready.isDone()) + this._ready.reject(new Error(`Extension disconnected before initialization: ${reason}`)); + } + handleExtensionEvent(method, params2) { + switch (method) { + case "chrome.debugger.onEvent": { + const [source11, cdpMethod, cdpParams] = params2; + this._model.onDebuggerEvent(source11, cdpMethod, cdpParams); + break; + } + case "chrome.debugger.onDetach": { + const [source11] = params2; + this._model.onDebuggerDetach(source11); + break; + } + case "chrome.tabs.onCreated": { + const [tab2] = params2; + this._model.onTabCreated(tab2); + break; + } + case "chrome.tabs.onRemoved": { + const [tabId] = params2; + this._model.onTabRemoved(tabId); + break; + } + case "extension.initialized": { + this._ready.resolve(); + break; + } + } + } + async handleCDPCommand(method, params2, sessionId) { + switch (method) { + case "Target.setAutoAttach": { + if (sessionId) + return void 0; + await this._model.enableAutoAttach(); + return { result: {} }; + } + case "Target.createTarget": + return { result: await this._model.createTarget(params2?.url) }; + case "Target.closeTarget": + return { result: await this._model.closeTarget(params2?.targetId) }; + case "Target.getTargetInfo": + return { result: this._model.getTargetInfo(sessionId) }; + } + return void 0; + } + async forwardToExtension(method, params2, sessionId) { + if (!sessionId) + return await this._model.sendBrowserCommand(method, params2); + return await this._model.sendCommand(sessionId, method, params2); + } + }; + } +}); + +// packages/playwright-core/src/tools/mcp/protocol.ts +var DEFAULT_VERSION; +var init_protocol = __esm({ + "packages/playwright-core/src/tools/mcp/protocol.ts"() { + "use strict"; + DEFAULT_VERSION = 2; + } +}); + +// packages/playwright-core/src/tools/mcp/cdpRelay.ts +var import_child_process6, import_os26, debug15, ws3, wsServer3, debugLogger2, CDPRelayServer, ExtensionConnection; +var init_cdpRelay = __esm({ + "packages/playwright-core/src/tools/mcp/cdpRelay.ts"() { + "use strict"; + import_child_process6 = require("child_process"); + import_os26 = __toESM(require("os")); + init_manualPromise(); + init_registry(); + init_extension(); + init_http(); + init_log(); + init_cdpRelayV1(); + init_cdpRelayV2(); + init_protocol(); + debug15 = require("./utilsBundle").debug; + ws3 = require("./utilsBundle").ws; + ({ wsServer: wsServer3 } = require("./utilsBundle")); + debugLogger2 = debug15("pw:mcp:relay"); + CDPRelayServer = class { + constructor(server, browserChannel, executablePath) { + this._cdpConnection = null; + this._extensionConnection = null; + this._extensionConnectionPromise = new ManualPromise(); + this._wsHost = addressToString(server.address(), { protocol: "ws" }); + this._browserChannel = browserChannel; + this._executablePath = executablePath; + this._protocolVersion = parseInt(process.env.PLAYWRIGHT_EXTENSION_PROTOCOL ?? DEFAULT_VERSION.toString(), 10); + const sendCommand = (method, params2) => { + if (!this._extensionConnection) + throw new Error("Extension not connected"); + return this._extensionConnection.send(method, params2); + }; + if (this._protocolVersion >= 2) + this._handler = new ExtensionProtocolV2(sendCommand); + else + this._handler = new ExtensionProtocolV1(sendCommand); + const uuid = crypto.randomUUID(); + this._cdpPath = `/cdp/${uuid}`; + this._extensionPath = `/extension/${uuid}`; + void this._extensionConnectionPromise.catch(logUnhandledError); + this._wss = new wsServer3({ server }); + this._wss.on("connection", this._onConnection.bind(this)); + } + cdpEndpoint() { + return `${this._wsHost}${this._cdpPath}`; + } + extensionEndpoint() { + return `${this._wsHost}${this._extensionPath}`; + } + async establishExtensionConnection(clientName) { + debugLogger2("Establishing extension connection"); + this._openConnectPageInBrowser(clientName); + debugLogger2("Waiting for incoming extension connection"); + await this._extensionConnectionPromise; + await this._handler.ready(); + debugLogger2("Extension connection established"); + } + _openConnectPageInBrowser(clientName) { + const mcpRelayEndpoint = `${this._wsHost}${this._extensionPath}`; + const url2 = new URL(`chrome-extension://${playwrightExtensionId}/connect.html`); + url2.searchParams.set("mcpRelayUrl", mcpRelayEndpoint); + const client = { + name: clientName, + // Not used anymore. + version: void 0 + }; + url2.searchParams.set("client", JSON.stringify(client)); + url2.searchParams.set("protocolVersion", this._protocolVersion.toString()); + const token = process.env.PLAYWRIGHT_MCP_EXTENSION_TOKEN; + if (token) + url2.searchParams.set("token", token); + const href = url2.toString(); + const channel = registry.isChromiumAlias(this._browserChannel) ? "chromium" : this._browserChannel; + let executablePath = this._executablePath; + if (!executablePath) { + const executableInfo = registry.findExecutable(channel); + if (!executableInfo) + throw new Error(`Unsupported channel: "${this._browserChannel}"`); + executablePath = executableInfo.executablePath(); + if (!executablePath) + throw new Error(`"${this._browserChannel}" executable not found. Make sure it is installed at a standard location.`); + } + const args = []; + const userDataDir = process.env.PWTEST_EXTENSION_USER_DATA_DIR; + if (userDataDir) + args.push(`--user-data-dir=${userDataDir}`); + if (import_os26.default.platform() === "linux" && channel === "chromium") + args.push("--no-sandbox"); + args.push(href); + (0, import_child_process6.spawn)(executablePath, args, { + windowsHide: true, + detached: true, + shell: false, + stdio: "ignore" + }); + } + stop() { + this._closeConnections("Server stopped"); + this._wss.close(); + } + _closeConnections(reason) { + this._closeCDPConnection(reason); + this._closeExtensionConnection(reason); + } + _onConnection(ws4, request2) { + const url2 = new URL(`http://localhost${request2.url}`); + debugLogger2(`New connection to ${url2.pathname}`); + if (url2.pathname === this._cdpPath) { + this._handlePlaywrightConnection(ws4); + } else if (url2.pathname === this._extensionPath) { + this._handleExtensionConnection(ws4); + } else { + debugLogger2(`Invalid path: ${url2.pathname}`); + ws4.close(4004, "Invalid path"); + } + } + _handlePlaywrightConnection(ws4) { + if (!this._extensionConnection) { + debugLogger2("Rejecting Playwright connection: extension not connected"); + ws4.close(1e3, "Extension not connected"); + return; + } + if (this._cdpConnection) { + debugLogger2("Rejecting second Playwright connection"); + ws4.close(1e3, "Another CDP client already connected"); + return; + } + this._cdpConnection = ws4; + this._handler.connectOverCDP((msg) => this._sendToCDPClient(msg)); + ws4.on("message", async (data) => { + try { + await this._handlePlaywrightMessage(JSON.parse(data.toString())); + } catch (error) { + debugLogger2(`Error while handling Playwright message +${data.toString()} +`, error); + } + }); + ws4.on("close", () => { + this._closeExtensionConnection("Playwright client disconnected"); + debugLogger2("Playwright WebSocket closed"); + }); + ws4.on("error", (error) => { + debugLogger2("Playwright WebSocket error:", error); + }); + debugLogger2("Playwright MCP connected"); + } + _closeExtensionConnection(reason) { + this._extensionConnection?.close(reason); + if (!this._extensionConnectionPromise.isDone()) + this._extensionConnectionPromise.reject(new Error(reason)); + } + _closeCDPConnection(reason) { + if (this._cdpConnection?.readyState === ws3.OPEN) + this._cdpConnection.close(1e3, reason); + } + _handleExtensionConnection(ws4) { + if (this._extensionConnection) { + ws4.close(1e3, "Another extension connection already established"); + return; + } + this._extensionConnection = new ExtensionConnection(ws4); + this._extensionConnection.onclose = (reason) => { + debugLogger2("Extension WebSocket closed:", reason); + this._handler.onExtensionDisconnect(reason); + this._closeCDPConnection(`Extension disconnected: ${reason}`); + }; + this._extensionConnection.onmessage = (method, params2) => this._handler.handleExtensionEvent(method, params2); + this._extensionConnectionPromise.resolve(); + } + async _handlePlaywrightMessage(message) { + debugLogger2("\u2190 Playwright:", `${message.method} (id=${message.id})`); + const { id, sessionId, method, params: params2 } = message; + try { + const result2 = await this._handleCDPCommand(method, params2, sessionId); + this._sendToCDPClient({ id, sessionId, result: result2 }); + } catch (e) { + debugLogger2("Error in the extension:", e); + this._sendToCDPClient({ + id, + sessionId, + error: { message: e.message } + }); + } + } + async _handleCDPCommand(method, params2, sessionId) { + switch (method) { + case "Browser.getVersion": { + return { + protocolVersion: "1.3", + product: "Chrome/Extension-Bridge", + userAgent: "CDP-Bridge-Server/1.0.0" + }; + } + case "Browser.setDownloadBehavior": { + return {}; + } + } + const handled = await this._handler.handleCDPCommand(method, params2, sessionId); + if (handled) + return handled.result; + return await this._handler.forwardToExtension(method, params2, sessionId); + } + _sendToCDPClient(message) { + debugLogger2("\u2192 Playwright:", `${message.method ?? `response(id=${message.id})`}`); + this._cdpConnection?.send(JSON.stringify(message)); + } + }; + ExtensionConnection = class { + constructor(ws4) { + this._callbacks = /* @__PURE__ */ new Map(); + this._lastId = 0; + this._ws = ws4; + this._ws.on("message", this._onMessage.bind(this)); + this._ws.on("close", this._onClose.bind(this)); + this._ws.on("error", this._onError.bind(this)); + } + async send(method, params2) { + if (this._ws.readyState !== ws3.OPEN) + throw new Error(`Unexpected WebSocket state: ${this._ws.readyState}`); + const id = ++this._lastId; + this._ws.send(JSON.stringify({ id, method, params: params2 })); + const error = new Error(`Protocol error: ${method}`); + return new Promise((resolve, reject) => { + this._callbacks.set(id, { resolve, reject, error }); + }); + } + close(message) { + debugLogger2("closing extension connection:", message); + if (this._ws.readyState === ws3.OPEN) + this._ws.close(1e3, message); + } + _onMessage(event) { + const eventData = event.toString(); + let parsedJson; + try { + parsedJson = JSON.parse(eventData); + } catch (e) { + debugLogger2(` Closing websocket due to malformed JSON. eventData=${eventData} e=${e?.message}`); + this._ws.close(); + return; + } + try { + this._handleParsedMessage(parsedJson); + } catch (e) { + debugLogger2(` Closing websocket due to failed onmessage callback. eventData=${eventData} e=${e?.message}`); + this._ws.close(); + } + } + _handleParsedMessage(object) { + if (object.id && this._callbacks.has(object.id)) { + const callback = this._callbacks.get(object.id); + this._callbacks.delete(object.id); + if (object.error) { + const error = callback.error; + error.message = object.error; + callback.reject(error); + } else { + callback.resolve(object.result); + } + } else if (object.id) { + debugLogger2("\u2190 Extension: unexpected response", object); + } else { + this.onmessage?.(object.method, object.params); + } + } + _onClose(event) { + debugLogger2(` code=${event.code} reason=${event.reason}`); + this._dispose(); + this.onclose?.(event.reason); + } + _onError(event) { + debugLogger2(` message=${event.message} type=${event.type} target=${event.target}`); + this._dispose(); + } + _dispose() { + for (const callback of this._callbacks.values()) + callback.reject(new Error("WebSocket closed")); + this._callbacks.clear(); + } + }; + } +}); + +// packages/playwright-core/src/tools/mcp/extensionContextFactory.ts +async function createExtensionBrowser(channel, executablePath, clientName) { + if (!executablePath) { + const userDataDir = process.env.PWTEST_EXTENSION_USER_DATA_DIR ?? defaultUserDataDirForChannel(channel); + if (userDataDir && !await isPlaywrightExtensionInstalled(userDataDir)) + throw new Error(`Playwright Extension not found in "${userDataDir}". Install it from ${playwrightExtensionInstallUrl}`); + } + const httpServer = createHttpServer(); + await startHttpServer(httpServer, {}); + const relay = new CDPRelayServer(httpServer, channel, executablePath); + debugLogger3(`CDP relay server started, extension endpoint: ${relay.extensionEndpoint()}.`); + try { + await relay.establishExtensionConnection(clientName); + return await playwright.chromium.connectOverCDP(relay.cdpEndpoint(), { isLocal: true, timeout: 0 }); + } catch (error) { + relay.stop(); + httpServer.close(); + throw error; + } +} +var debug16, debugLogger3; +var init_extensionContextFactory = __esm({ + "packages/playwright-core/src/tools/mcp/extensionContextFactory.ts"() { + "use strict"; + init_network(); + init_chromiumChannels(); + init_inprocess(); + init_extension(); + init_cdpRelay(); + debug16 = require("./utilsBundle").debug; + debugLogger3 = debug16("pw:mcp:relay"); + } +}); + +// packages/playwright-core/src/tools/utils/connect.ts +async function connectToBrowserAcrossVersions(descriptor) { + const pw = require(descriptor.playwrightLib); + const browserType = pw[descriptor.browser.browserName]; + return await browserType.connect(descriptor.endpoint ?? descriptor.pipeName); +} +var init_connect2 = __esm({ + "packages/playwright-core/src/tools/utils/connect.ts"() { + "use strict"; + } +}); + +// packages/playwright-core/src/tools/mcp/browserFactory.ts +async function createBrowserWithInfo(config, clientInfo, cliOptions) { + if (config.browser.remoteEndpoint) + return await createRemoteBrowser(config); + let browser; + let canBind = false; + let ownership = "own"; + if (config.browser.cdpEndpoint) { + browser = await createCDPBrowser(config, clientInfo); + canBind = true; + ownership = "attached"; + } else if (config.browser.isolated) { + browser = await createIsolatedBrowser(config, clientInfo); + canBind = true; + ownership = "own"; + } else if (config.extension) { + const { channel, executablePath } = resolveExtensionOptions(cliOptions); + browser = await createExtensionBrowser(channel, executablePath, clientInfo.clientName); + ownership = "attached"; + } else { + browser = await createPersistentBrowser(config, clientInfo); + canBind = true; + ownership = "own"; + } + return { browser, browserInfo: browserInfo(browser, config), canBind, ownership }; +} +function browserInfo(browser, config) { + return { + // eslint-disable-next-line no-restricted-syntax + guid: browser._guid, + browserName: config.browser.browserName, + launchOptions: config.browser.launchOptions, + userDataDir: config.browser.userDataDir + }; +} +async function createIsolatedBrowser(config, clientInfo) { + testDebug2("create browser (isolated)"); + const browserType = playwright[config.browser.browserName]; + const tracesDir = await computeTracesDir(config, clientInfo); + const browser = await browserType.launch({ + tracesDir, + ...config.browser.launchOptions, + handleSIGINT: false, + handleSIGTERM: false + }).catch((error) => { + throwIfExecutableMissing(error, config); + throw error; + }); + return browser; +} +async function createCDPBrowser(config, clientInfo) { + testDebug2("create browser (cdp)"); + const artifactsDir = await computeTracesDir(config, clientInfo); + const browser = await playwright.chromium.connectOverCDP(config.browser.cdpEndpoint, { + headers: config.browser.cdpHeaders, + timeout: config.browser.cdpTimeout, + artifactsDir + }); + return browser; +} +async function createRemoteBrowser(config) { + testDebug2("create browser (remote)"); + const remote = config.browser.remoteEndpoint; + const remoteHeaders = config.browser.remoteHeaders; + const remoteOptions = typeof remote === "string" ? { endpoint: remote, headers: remoteHeaders } : { ...remote, headers: { ...remoteHeaders, ...remote.headers } }; + const descriptor = await serverRegistry.find(remoteOptions.endpoint); + if (descriptor) { + const browser2 = await connectToBrowserAcrossVersions(descriptor); + return { + browser: browser2, + browserInfo: { + guid: descriptor.browser.guid, + browserName: descriptor.browser.browserName, + launchOptions: descriptor.browser.launchOptions, + userDataDir: descriptor.browser.userDataDir + }, + canBind: false, + ownership: "attached" + }; + } + const playwrightObject = playwright; + const browser = await connectToBrowser(playwrightObject, remoteOptions); + browser._connectToBrowserType(playwrightObject[browser._browserName], {}, void 0); + if (!browser.contexts().length) + await browser.newContext(config.browser.contextOptions); + return { browser, browserInfo: browserInfo(browser, config), canBind: false, ownership: "attached" }; +} +async function createPersistentBrowser(config, clientInfo) { + testDebug2("create browser (persistent)"); + const userDataDir = config.browser.userDataDir ?? await createUserDataDir(config, clientInfo); + const tracesDir = await computeTracesDir(config, clientInfo); + if (await isProfileLocked5Times(userDataDir)) + throw new Error(`Browser is already in use for ${userDataDir}, use --isolated to run multiple instances of the same browser`); + const browserType = playwright[config.browser.browserName]; + const configIgnoreDefaultArgs = config.browser.launchOptions?.ignoreDefaultArgs; + const launchOptions = { + tracesDir, + ...config.browser.launchOptions, + ...config.browser.contextOptions, + handleSIGINT: false, + handleSIGTERM: false, + ignoreDefaultArgs: configIgnoreDefaultArgs === true ? true : [ + "--disable-extensions", + ...Array.isArray(configIgnoreDefaultArgs) ? configIgnoreDefaultArgs : [] + ] + }; + try { + const browserContext = await browserType.launchPersistentContext(userDataDir, launchOptions); + const browser = browserContext.browser(); + return browser; + } catch (error) { + throwIfExecutableMissing(error, config); + if (error.message.includes("cannot open shared object file: No such file or directory")) { + const browserName = launchOptions.channel ?? config.browser.browserName; + throw new Error(`Missing system dependencies required to run browser ${browserName}. Install them with: sudo npx playwright install-deps ${browserName}`); + } + if (error.message.includes("ProcessSingleton") || error.message.includes("exitCode=21")) + throw new Error(`Browser is already in use for ${userDataDir}, use --isolated to run multiple instances of the same browser`); + throw error; + } +} +async function createUserDataDir(config, clientInfo) { + const dir = process.env.PWMCP_PROFILES_DIR_FOR_TEST ?? import_path54.default.join(defaultCacheDirectory, "ms-playwright-mcp"); + const browserToken = config.browser.launchOptions?.channel ?? config.browser?.browserName; + const rootPathToken = createHash(clientInfo.cwd); + const result2 = import_path54.default.join(dir, `mcp-${browserToken}-${rootPathToken}`); + await import_fs56.default.promises.mkdir(result2, { recursive: true }); + return result2; +} +function createHash(data) { + return import_crypto28.default.createHash("sha256").update(data).digest("hex").slice(0, 7); +} +async function computeTracesDir(config, clientInfo) { + return import_path54.default.resolve(outputDir({ config, cwd: clientInfo.cwd }), "traces"); +} +async function isProfileLocked5Times(userDataDir) { + for (let i = 0; i < 5; i++) { + if (!isProfileLocked(userDataDir)) + return false; + await new Promise((f) => setTimeout(f, 1e3)); + } + return true; +} +function isProfileLocked(userDataDir) { + const lockFile = process.platform === "win32" ? "lockfile" : "SingletonLock"; + const lockPath = import_path54.default.join(userDataDir, lockFile); + if (process.platform === "win32") { + try { + const fd = import_fs56.default.openSync(lockPath, "r+"); + import_fs56.default.closeSync(fd); + return false; + } catch (e) { + return e.code !== "ENOENT"; + } + } + try { + const target = import_fs56.default.readlinkSync(lockPath); + const pid = parseInt(target.split("-").pop() || "", 10); + if (isNaN(pid)) + return false; + process.kill(pid, 0); + return true; + } catch { + return false; + } +} +function throwIfExecutableMissing(error, config) { + if (!error.message.includes(`Executable doesn't exist`)) + return; + const target = error.message.includes("ffmpeg") ? "ffmpeg" : config.browser.launchOptions?.channel ?? config.browser.browserName; + const label = target === "ffmpeg" ? "FFmpeg" : `Browser "${target}"`; + if (config.skillMode) + throw new Error(`${label} is not installed. Run \`playwright-cli install-browser ${target}\` to install`); + throw new Error(`${label} is not installed. Run \`npx @playwright/mcp install-browser ${target}\` to install`); +} +var import_crypto28, import_fs56, import_path54; +var init_browserFactory = __esm({ + "packages/playwright-core/src/tools/mcp/browserFactory.ts"() { + "use strict"; + import_crypto28 = __toESM(require("crypto")); + import_fs56 = __toESM(require("fs")); + import_path54 = __toESM(require("path")); + init_inprocess(); + init_registry(); + init_log(); + init_context(); + init_extensionContextFactory(); + init_connect2(); + init_serverRegistry(); + init_config2(); + init_connect(); + } +}); + +// packages/playwright-core/src/tools/mcp/index.ts +async function createConnection(userConfig = {}, contextGetter) { + const config = await resolveConfig(userConfig); + const tools = filteredTools(config); + const backendFactory = { + name: "api", + nameInConfig: "api", + version: packageJSON.version, + toolSchemas: tools.map((tool) => tool.schema), + create: async (clientInfo) => { + const browser = contextGetter ? new SimpleBrowser(await contextGetter()) : (await createBrowserWithInfo(config, clientInfo, {})).browser; + const context2 = config.browser.isolated ? await browser.newContext(config.browser.contextOptions) : browser.contexts()[0]; + return new BrowserBackend(config, context2, tools); + }, + disposed: async () => { + } + }; + return createServer("api", packageJSON.version, backendFactory, false); +} +var SimpleBrowser; +var init_mcp = __esm({ + "packages/playwright-core/src/tools/mcp/index.ts"() { + "use strict"; + init_config2(); + init_tools(); + init_browserFactory(); + init_browserBackend(); + init_server2(); + init_package(); + SimpleBrowser = class { + constructor(context2) { + this._context = context2; + } + contexts() { + return [this._context]; + } + async newContext() { + throw new Error("Creating a new context is not supported in SimpleBrowserContextFactory."); + } + }; + } +}); + +// packages/playwright-core/src/tools/mcp/program.ts +function decorateMCPCommand(command) { + command.option("--allowed-hosts ", "comma-separated list of hosts this server is allowed to serve from. Defaults to the host the server is bound to. Pass '*' to disable the host check.", commaSeparatedList).option("--allowed-origins ", "semicolon-separated list of TRUSTED origins to allow the browser to request. Default is to allow all.\nImportant: *does not* serve as a security boundary and *does not* affect redirects. ", semicolonSeparatedList).option("--allow-unrestricted-file-access", "allow access to files outside of the workspace roots. Also allows unrestricted access to file:// URLs. By default access to file system is restricted to workspace root directories (or cwd if no roots are configured) only, and navigation to file:// URLs is blocked.").option("--blocked-origins ", "semicolon-separated list of origins to block the browser from requesting. Blocklist is evaluated before allowlist. If used without the allowlist, requests not matching the blocklist are still allowed.\nImportant: *does not* serve as a security boundary and *does not* affect redirects.", semicolonSeparatedList).option("--block-service-workers", "block service workers").option("--browser ", "browser or chrome channel to use, possible values: chrome, firefox, webkit, msedge.").option("--caps ", "comma-separated list of additional capabilities to enable, possible values: vision, pdf, devtools.", commaSeparatedList).option("--cdp-endpoint ", "CDP endpoint to connect to.").option("--cdp-header ", "CDP headers to send with the connect request, multiple can be specified.", headerParser).option("--cdp-timeout ", "timeout in milliseconds for connecting to CDP endpoint, defaults to 30000ms", numberParser).option("--codegen ", 'specify the language to use for code generation, possible values: "typescript", "none". Default is "typescript".', enumParser.bind(null, "--codegen", ["none", "typescript"])).option("--config ", "path to the configuration file.").option("--console-level ", 'level of console messages to return: "error", "warning", "info", "debug". Each level includes the messages of more severe levels.', enumParser.bind(null, "--console-level", ["error", "warning", "info", "debug"])).option("--device ", 'device to emulate, for example: "iPhone 15"').option("--executable-path ", "path to the browser executable.").option("--extension", 'Connect to a running browser instance (Edge/Chrome only). Requires the "Playwright Extension" to be installed.').option("--endpoint ", "Bound browser endpoint to connect to.").option("--grant-permissions ", 'List of permissions to grant to the browser context, for example "geolocation", "clipboard-read", "clipboard-write".', commaSeparatedList).option("--headless", "run browser in headless mode, headed by default").option("--host ", "host to bind server to. Default is localhost. Use 0.0.0.0 to bind to all interfaces.").option("--ignore-https-errors", "ignore https errors").option("--init-page ", "path to TypeScript file to evaluate on Playwright page object").option("--init-script ", "path to JavaScript file to add as an initialization script. The script will be evaluated in every page before any of the page's scripts. Can be specified multiple times.").option("--isolated", "keep the browser profile in memory, do not save it to disk.").option("--image-responses ", 'whether to send image responses to the client. Can be "allow" or "omit", Defaults to "allow".', enumParser.bind(null, "--image-responses", ["allow", "omit"])).option("--no-sandbox", "disable the sandbox for all process types that are normally sandboxed.").option("--output-dir ", "path to the directory for output files.").option("--output-max-size ", "Threshold for evicting old output files, in bytes.", numberParser).option("--output-mode ", 'whether to save snapshots, console messages, network logs to a file or to the standard output. Can be "file" or "stdout". Default is "stdout".', enumParser.bind(null, "--output-mode", ["file", "stdout"])).option("--port ", "port to listen on for SSE transport.").option("--proxy-bypass ", 'comma-separated domains to bypass proxy, for example ".com,chromium.org,.domain.com"').option("--proxy-server ", 'specify proxy server, for example "http://myproxy:3128" or "socks5://myproxy:8080"').addOption(new ProgramOption("--remote-header ", "headers to send with the remote endpoint connect request, multiple can be specified.").argParser(headerParser).hideHelp()).option("--sandbox", "enable the sandbox for all process types that are normally not sandboxed.").option("--save-session", "Whether to save the Playwright MCP session into the output directory.").option("--secrets ", "path to a file containing secrets in the dotenv format", dotenvFileLoader).option("--shared-browser-context", "reuse the same browser context between all connected HTTP clients.").option("--snapshot-mode ", 'when taking snapshots for responses, specifies the mode to use. Can be "full" or "none". Default is "full".').option("--storage-state ", "path to the storage state file for isolated sessions.").option("--test-id-attribute ", 'specify the attribute to use for test ids, defaults to "data-testid"').option("--timeout-action ", "specify action timeout in milliseconds, defaults to 5000ms", numberParser).option("--timeout-navigation ", "specify navigation timeout in milliseconds, defaults to 60000ms", numberParser).option("--user-agent ", "specify user agent string").option("--user-data-dir ", "path to the user data directory. If not specified, a temporary directory will be created.").option("--viewport-size ", 'specify browser viewport size in pixels, for example "1280x720"', resolutionParser.bind(null, "--viewport-size")).addOption(new ProgramOption("--vision", "Legacy option, use --caps=vision instead").hideHelp()).action(async (options) => { + options.sandbox = options.sandbox === true ? void 0 : false; + setupExitWatchdog(); + if (options.vision) { + console.error("The --vision option is deprecated, use --caps=vision instead"); + options.caps ??= []; + options.caps.push("vision"); + } + if (options.caps?.includes("tracing")) + options.caps.push("devtools"); + const config = await resolveCLIConfigForMCP(options); + const tools = filteredTools(config); + const useSharedBrowser = config.sharedBrowserContext || config.browser.isolated; + let sharedBrowserPromise; + let clientCount = 0; + const clientNameCounters = /* @__PURE__ */ new Map(); + const factory = { + name: "Playwright", + nameInConfig: "playwright", + version: version2, + toolSchemas: tools.map((tool) => tool.schema), + create: async (clientInfo) => { + if (useSharedBrowser && !sharedBrowserPromise) { + sharedBrowserPromise = (async () => { + const { browser: browser2, canBind: canBind2 } = await createBrowserWithInfo(config, clientInfo, options); + if (canBind2) + await browser2.bind(clientInfo.clientName, { workspaceDir: clientInfo.cwd }); + return browser2; + })().catch((error) => { + sharedBrowserPromise = void 0; + throw error; + }); + } + clientCount++; + const { browser, canBind } = sharedBrowserPromise ? { browser: await sharedBrowserPromise, canBind: false } : await createBrowserWithInfo(config, clientInfo, options); + if (canBind) { + const count = (clientNameCounters.get(clientInfo.clientName) ?? 0) + 1; + clientNameCounters.set(clientInfo.clientName, count); + const sessionName = count > 1 ? `${clientInfo.clientName} (${count})` : clientInfo.clientName; + await browser.bind(sessionName, { workspaceDir: clientInfo.cwd }); + } + const browserContext = config.browser.isolated ? await browser.newContext(config.browser.contextOptions) : browser.contexts()[0]; + return new BrowserBackend(config, browserContext, tools); + }, + disposed: async (backend) => { + clientCount--; + if (sharedBrowserPromise && clientCount > 0) + return; + testDebug2("close browser"); + sharedBrowserPromise = void 0; + const browserContext = backend.browserContext; + await browserContext.close().catch(() => { + }); + await browserContext.browser()?.close().catch(() => { + }); + } + }; + await start2(factory, config.server); + }); +} +var ProgramOption, version2; +var init_program3 = __esm({ + "packages/playwright-core/src/tools/mcp/program.ts"() { + "use strict"; + init_server2(); + init_config2(); + init_watchdog(); + init_browserFactory(); + init_browserBackend(); + init_tools(); + init_log(); + init_package(); + ({ ProgramOption } = require("./utilsBundle")); + version2 = packageJSON.version; + } +}); + +// packages/playwright-core/src/tools/cli-daemon/helpGenerator.ts +function commandArgs(command) { + const args = []; + const shape = command.args ? command.args.shape : {}; + for (const [name, schema] of Object.entries(shape)) { + const zodSchema = schema; + const description = zodSchema.description ?? ""; + args.push({ name, description, optional: zodSchema.safeParse(void 0).success }); + } + return args; +} +function commandArgsText(args) { + return args.map((a) => a.optional ? `[${a.name}]` : `<${a.name}>`).join(" "); +} +function generateCommandHelp(command) { + const args = commandArgs(command); + const lines = [ + `playwright-cli ${command.name} ${commandArgsText(args)}`, + "", + command.description, + "" + ]; + if (args.length) { + lines.push("Arguments:"); + lines.push(...args.map((a) => formatWithGap(` ${a.optional ? `[${a.name}]` : `<${a.name}>`}`, a.description.toLowerCase()))); + } + if (command.options) { + lines.push("Options:"); + const optionsShape = command.options.shape; + for (const [name, schema] of Object.entries(optionsShape)) { + const zodSchema = schema; + const description = (zodSchema.description ?? "").toLowerCase(); + lines.push(formatWithGap(` --${name}`, description)); + } + } + return lines.join("\n"); +} +function generateHelp() { + const lines = []; + lines.push("Usage: playwright-cli [args] [options]"); + lines.push("Usage: playwright-cli -s= [args] [options]"); + const commandsByCategory = /* @__PURE__ */ new Map(); + for (const c of categories) + commandsByCategory.set(c.name, []); + for (const command of Object.values(commands)) { + if (command.hidden) + continue; + commandsByCategory.get(command.category).push(command); + } + for (const c of categories) { + const cc = commandsByCategory.get(c.name); + if (!cc.length) + continue; + lines.push(` +${c.title}:`); + for (const command of cc) + lines.push(generateHelpEntry(command)); + } + lines.push("\nGlobal options:"); + lines.push(formatWithGap(" --help [command]", "print help")); + lines.push(formatWithGap(" --json", "output response as JSON")); + lines.push(formatWithGap(" --raw", "output only the result value, without status and code")); + lines.push(formatWithGap(" --version", "print version")); + return lines.join("\n"); +} +function generateHelpEntry(command) { + const args = commandArgs(command); + const prefix = ` ${command.name} ${commandArgsText(args)}`; + const suffix = command.description.toLowerCase(); + return formatWithGap(prefix, suffix); +} +function unwrapZodType(schema) { + if ("unwrap" in schema && typeof schema.unwrap === "function") + return unwrapZodType(schema.unwrap()); + return schema; +} +function isBooleanSchema(schema) { + return unwrapZodType(schema) instanceof z30.ZodBoolean; +} +function generateHelpJSON() { + const booleanOptions2 = /* @__PURE__ */ new Set(); + const commandEntries = {}; + for (const [name, command] of Object.entries(commands)) { + const flags = {}; + if (command.options) { + const optionsShape = command.options.shape; + for (const [flagName, schema] of Object.entries(optionsShape)) { + const isBoolean = isBooleanSchema(schema); + flags[flagName] = isBoolean ? "boolean" : "string"; + if (isBoolean) + booleanOptions2.add(flagName); + } + } + const args = command.args ? Object.keys(command.args.shape) : []; + commandEntries[name] = { help: generateCommandHelp(command), flags, args }; + if (command.raw) + commandEntries[name].raw = true; + } + return { + global: generateHelp(), + commands: commandEntries, + booleanOptions: [...booleanOptions2] + }; +} +function formatWithGap(prefix, text2, threshold = 30) { + const indent = Math.max(1, threshold - prefix.length); + return prefix + " ".repeat(indent) + text2; +} +var z30, categories; +var init_helpGenerator = __esm({ + "packages/playwright-core/src/tools/cli-daemon/helpGenerator.ts"() { + "use strict"; + init_commands(); + z30 = require("./utilsBundle").z; + categories = [ + { name: "core", title: "Core" }, + { name: "navigation", title: "Navigation" }, + { name: "keyboard", title: "Keyboard" }, + { name: "mouse", title: "Mouse" }, + { name: "export", title: "Save as" }, + { name: "tabs", title: "Tabs" }, + { name: "storage", title: "Storage" }, + { name: "network", title: "Network" }, + { name: "devtools", title: "DevTools" }, + { name: "install", title: "Install" }, + { name: "config", title: "Configuration" }, + { name: "browsers", title: "Browser sessions" } + ]; + } +}); + +// packages/playwright-core/src/tools/cli-daemon/daemon.ts +async function socketExists(socketPath) { + try { + const stat = await import_fs57.default.promises.stat(socketPath); + if (stat?.isSocket()) + return true; + } catch (e) { + } + return false; +} +async function startCliDaemonServer(sessionName, browserContext, browserInfo2, contextConfig, clientInfo, mcpClientInfo, options) { + const sessionConfig = createSessionConfig(clientInfo, sessionName, browserInfo2, options); + const { socketPath } = sessionConfig; + if (process.platform !== "win32" && await socketExists(socketPath)) { + try { + await import_fs57.default.promises.unlink(socketPath); + } catch (error) { + throw error; + } + } + const backend = new BrowserBackend(contextConfig, browserContext, browserTools); + await backend.initialize(mcpClientInfo); + if (browserContext.isClosed()) + throw new Error("Browser context was closed before the daemon could start"); + const server = import_net10.default.createServer((socket) => { + const connection = new SocketConnection(socket); + const abortController = new AbortController(); + connection.onclose = () => abortController.abort(); + connection.onmessage = async (message) => { + const { id, method, params: params2 } = message; + try { + if (method === "stop") { + await deleteSessionFile(clientInfo, sessionConfig); + const sendAck = async () => connection.send({ id, result: "ok" }).catch(() => { + }); + if (options?.exitOnClose) + gracefullyProcessExitDoNotHang(0, () => sendAck()); + else + await sendAck(); + } else if (method === "run") { + const { toolName, toolParams } = parseCliCommand(params2.args); + toolParams._meta = { cwd: params2.cwd, raw: params2.raw || params2.json, json: !!params2.json }; + const response2 = await backend.callTool(toolName, toolParams, abortController.signal); + await connection.send({ id, result: formatResult(response2) }); + } else { + throw new Error(`Unknown method: ${method}`); + } + } catch (e) { + const error = process.env.PWDEBUGIMPL ? e.stack || e.message : e.message; + connection.send({ id, error }).catch(() => { + }); + } + }; + }); + decorateServer(server); + browserContext.on("close", () => Promise.resolve().then(async () => { + await deleteSessionFile(clientInfo, sessionConfig); + if (options?.exitOnClose) + gracefullyProcessExitDoNotHang(0); + })); + await new Promise((resolve, reject) => { + server.on("error", reject); + server.listen(socketPath, () => resolve()); + }); + await saveSessionFile(clientInfo, sessionConfig); + return socketPath; +} +async function saveSessionFile(clientInfo, sessionConfig) { + await import_fs57.default.promises.mkdir(clientInfo.daemonProfilesDir, { recursive: true }); + const sessionFile = import_path55.default.join(clientInfo.daemonProfilesDir, `${sessionConfig.name}.session`); + await import_fs57.default.promises.writeFile(sessionFile, JSON.stringify(sessionConfig, null, 2)); +} +async function deleteSessionFile(clientInfo, sessionConfig) { + await import_fs57.default.promises.unlink(sessionConfig.socketPath).catch(() => { + }); + if (!sessionConfig.cli.persistent) { + const sessionFile = import_path55.default.join(clientInfo.daemonProfilesDir, `${sessionConfig.name}.session`); + await import_fs57.default.promises.rm(sessionFile).catch(() => { + }); + } +} +function formatResult(result2) { + const isError4 = result2.isError; + const text2 = result2.content[0].type === "text" ? result2.content[0].text : void 0; + return { isError: isError4, text: text2 }; +} +function parseCliCommand(args) { + const command = commands[args._[0]]; + if (!command) + throw new Error("Command is required"); + return parseCommand(command, args); +} +function daemonSocketPath(clientInfo, sessionName) { + return makeSocketPath("cli", `${clientInfo.workspaceDirHash}-${sessionName}`); +} +function createSessionConfig(clientInfo, sessionName, browserInfo2, options = {}) { + return { + name: sessionName, + version: clientInfo.version, + timestamp: Date.now(), + socketPath: daemonSocketPath(clientInfo, sessionName), + workspaceDir: clientInfo.workspaceDir, + attached: options.ownership === "attached" ? true : void 0, + cli: { persistent: options.persistent }, + browser: { + browserName: browserInfo2.browserName, + launchOptions: browserInfo2.launchOptions, + userDataDir: browserInfo2.userDataDir + } + }; +} +var import_fs57, import_net10, import_path55; +var init_daemon = __esm({ + "packages/playwright-core/src/tools/cli-daemon/daemon.ts"() { + "use strict"; + import_fs57 = __toESM(require("fs")); + import_net10 = __toESM(require("net")); + import_path55 = __toESM(require("path")); + init_network(); + init_fileUtils(); + init_processLauncher(); + init_browserBackend(); + init_tools(); + init_command(); + init_commands(); + init_socketConnection(); + } +}); + +// packages/playwright-core/src/tools/cli-daemon/program.ts +function decorateProgram3(program4) { + program4.argument("[session-name]", "name of the session to create or connect to", "default").option("--headed", "run in headed mode (non-headless)").option("--extension", "run with the extension").option("--browser ", "browser to use (chromium, chrome, firefox, webkit)").option("--persistent", "use a persistent browser context").option("--profile ", "path to the user data dir").option("--config ", "path to the config file; by default uses .playwright/cli.config.json in the project directory and ~/.playwright/cli.config.json as global config").option("--cdp ", "connect to an existing browser via CDP endpoint URL").option("--endpoint ", "attach to a running Playwright browser endpoint").option("--init-workspace", "initialize workspace").option("--init-skills ", 'install skills for the given agent type ("claude" or "agents")').action(async (sessionName, options) => { + if (options.initWorkspace) { + await initWorkspace(options.initSkills); + return; + } + setupExitWatchdog(); + const clientInfo = createClientInfo(); + const mcpConfig = await resolveCLIConfigForCLI(clientInfo.daemonProfilesDir, sessionName, options); + const mcpClientInfo = { + cwd: process.cwd(), + clientName: guessClientName() + }; + try { + const { browser, browserInfo: browserInfo2, canBind, ownership } = await createBrowserWithInfo(mcpConfig, mcpClientInfo, options); + if (canBind) + await browser.bind(sessionName, { workspaceDir: clientInfo.workspaceDir }); + const browserContext = mcpConfig.browser.isolated ? await browser.newContext(mcpConfig.browser.contextOptions) : browser.contexts()[0]; + if (!browserContext) + throw new Error("Error: unable to connect to a browser that does not have any contexts"); + const persistent = options.persistent || options.profile || mcpConfig.browser.userDataDir ? true : void 0; + const socketPath = await startCliDaemonServer(sessionName, browserContext, browserInfo2, mcpConfig, clientInfo, mcpClientInfo, { persistent, exitOnClose: true, ownership }); + console.log(`Daemon listening on ${socketPath} +`); + } catch (error) { + console.log(error); + gracefullyProcessExitDoNotHang(1); + } + }); +} +function defaultConfigFile() { + return import_path56.default.resolve(".playwright", "cli.config.json"); +} +function globalConfigFile() { + return import_path56.default.join(process.env["PWTEST_CLI_GLOBAL_CONFIG"] ?? import_os27.default.homedir(), ".playwright", "cli.config.json"); +} +async function initWorkspace(initSkills) { + const cwd = process.cwd(); + const playwrightDir = import_path56.default.join(cwd, ".playwright"); + await import_fs58.default.promises.mkdir(playwrightDir, { recursive: true }); + console.log(`\u2705 Workspace initialized at \`${cwd}\`.`); + if (initSkills) { + const skillSourceDir = libPath("tools", "cli-client", "skill"); + const target = initSkills === "agents" ? "agents" : "claude"; + const skillDestDir = import_path56.default.join(cwd, `.${target}`, "skills", "playwright-cli"); + if (!import_fs58.default.existsSync(skillSourceDir)) { + console.error("\u274C Skills source directory not found:", skillSourceDir); + process.exit(1); + } + await import_fs58.default.promises.cp(skillSourceDir, skillDestDir, { recursive: true }); + console.log(`\u2705 Skills installed to \`${import_path56.default.relative(cwd, skillDestDir)}\`.`); + } + await ensureConfiguredBrowserInstalled(); +} +async function ensureConfiguredBrowserInstalled() { + if (getAsBooleanFromENV("PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD")) + return; + if (import_fs58.default.existsSync(defaultConfigFile()) || import_fs58.default.existsSync(globalConfigFile())) { + const clientInfo = createClientInfo(); + const config = await resolveCLIConfigForCLI(clientInfo.daemonProfilesDir, "default", {}); + const browserName = config.browser.browserName; + const channel = config.browser.launchOptions.channel; + if (!channel || channel.startsWith("chromium")) + await resolveAndInstall(channel ?? browserName); + } else { + const channel = await findOrInstallDefaultBrowser(); + if (channel !== "chrome") + await createDefaultConfig(channel); + } +} +async function findOrInstallDefaultBrowser() { + const channels = ["chrome", "msedge"]; + for (const channel of channels) { + const executable = registry.findExecutable(channel); + if (!executable?.executablePath()) + continue; + await resolveAndInstall("ffmpeg"); + console.log(`\u2705 Found ${channel}, will use it as the default browser.`); + return channel; + } + await resolveAndInstall("chromium"); + return "chromium"; +} +async function resolveAndInstall(nameOrChannel) { + const executables = registry.resolveBrowsers([nameOrChannel], { shell: "no" }); + await registry.install(executables); +} +async function createDefaultConfig(channel) { + const config = { + browser: { + browserName: "chromium", + launchOptions: { channel } + } + }; + await import_fs58.default.promises.writeFile(defaultConfigFile(), JSON.stringify(config, null, 2)); + console.log(`\u2705 Created default config for ${channel} at ${import_path56.default.relative(process.cwd(), defaultConfigFile())}.`); +} +var import_fs58, import_os27, import_path56; +var init_program4 = __esm({ + "packages/playwright-core/src/tools/cli-daemon/program.ts"() { + "use strict"; + import_fs58 = __toESM(require("fs")); + import_os27 = __toESM(require("os")); + import_path56 = __toESM(require("path")); + init_env(); + init_processLauncher(); + init_package(); + init_daemon(); + init_watchdog(); + init_browserFactory(); + init_config2(); + init_registry2(); + init_registry(); + } +}); + +// packages/playwright-core/src/tools/dashboard/sessionProvider.ts +var SessionProviderEvent; +var init_sessionProvider = __esm({ + "packages/playwright-core/src/tools/dashboard/sessionProvider.ts"() { + "use strict"; + SessionProviderEvent = { + SessionsChanged: "sessionsChanged", + TabsChanged: "tabsChanged", + ContextClosed: "contextClosed", + AttachRequested: "attachRequested" + }; + } +}); + +// packages/playwright-core/src/tools/dashboard/dashboardController.ts +function browserId(browser) { + return browser._guid; +} +function pageId(p) { + return p._guid; +} +function contextId(c) { + return c._guid; +} +async function faviconUrl(page) { + const url2 = page.evaluate(async () => { + const response2 = await fetch(document.querySelector('link[rel~="icon"]')?.href ?? "/favicon.ico"); + if (!response2.ok) + return void 0; + const blob = await response2.blob(); + if (!blob.type.startsWith("image/")) + return void 0; + return await new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onloadend = () => resolve(reader.result); + reader.onerror = reject; + reader.readAsDataURL(blob); + }); + }).catch(() => void 0); + const timeout = new Promise((resolve) => setTimeout(() => resolve(void 0), 3e3)); + return await Promise.race([url2, timeout]); +} +var import_path57, import_os28, import_fs59, import_crypto29, import_child_process7, DashboardConnection, AttachedPage; +var init_dashboardController = __esm({ + "packages/playwright-core/src/tools/dashboard/dashboardController.ts"() { + "use strict"; + import_path57 = __toESM(require("path")); + import_os28 = __toESM(require("os")); + import_fs59 = __toESM(require("fs")); + import_crypto29 = __toESM(require("crypto")); + import_child_process7 = require("child_process"); + init_manualPromise(); + init_eventsHelper(); + init_registry2(); + init_sessionProvider(); + DashboardConnection = class { + constructor(provider, onclose, onconnected) { + this._pushTabsScheduled = false; + this._visible = true; + this._streams = /* @__PURE__ */ new Map(); + this._provider = provider; + this._onclose = onclose; + this._onconnected = onconnected; + this._recordingDir = import_fs59.default.mkdtempSync(import_path57.default.join(import_os28.default.tmpdir(), "playwright-recordings-")); + } + onconnect() { + this._provider.on(SessionProviderEvent.SessionsChanged, () => { + this._pushSessions(); + void this._tryRevealPending(); + }); + this._provider.on(SessionProviderEvent.TabsChanged, () => { + this._pushTabs(); + void this._tryRevealPending(); + }); + this._provider.on(SessionProviderEvent.ContextClosed, (context2) => { + if (this._attachedPage?.page.context() === context2) { + this._attachedPage.dispose(); + this._attachedPage = void 0; + } + }); + this._provider.on(SessionProviderEvent.AttachRequested, (page) => { + void this._switchAttachedTo(page); + }); + this._provider.start(); + this._onconnected?.(); + } + onclose() { + this._provider.dispose(); + this._attachedPage?.dispose(); + this._attachedPage = void 0; + this._pendingReveal?.done.reject(new Error("Dashboard connection closed")); + this._pendingReveal = void 0; + this._resolvePendingAnnotate({ type: "cancelled" }); + for (const stream3 of this._streams.values()) { + void stream3.handle.close().catch(() => { + }).then(() => import_fs59.default.promises.unlink(stream3.path)).catch(() => { + }); + } + this._streams.clear(); + void import_fs59.default.promises.rm(this._recordingDir, { recursive: true, force: true }).catch(() => { + }); + this._onclose(); + } + async dispatch(method, params2) { + const handler = this[method]; + if (typeof handler === "function") + return handler.call(this, params2); + const attached = this._attachedPage; + if (!attached) + return; + const onAtt = attached[method]; + if (typeof onAtt === "function") + return onAtt.call(attached, params2); + } + async selectTab(params2) { + const page = this._provider.findPage(params2); + if (page) + await this._switchAttachedTo(page); + this._pushTabs(); + } + async newTab(params2) { + const context2 = this._provider.findContext(params2); + if (!context2) + return; + const page = await context2.newPage(); + await this._switchAttachedTo(page); + this._pushTabs(); + } + async closeTab(params2) { + const page = this._provider.findPage(params2); + await page?.close({ reason: "Closed in Dashboard" }); + } + async closeSession(params2) { + await this._provider.closeSession(params2.browser); + } + async setVisible(params2) { + if (this._visible === params2.visible) + return; + this._visible = params2.visible; + await this._attachedPage?.setScreencastActive(params2.visible); + } + revealSession(sessionName, workspaceDir) { + const existing = this._pendingReveal; + if (existing && existing.pageId === void 0 && existing.sessionName === sessionName && existing.workspaceDir === workspaceDir) + return existing.done; + existing?.done.reject(new Error("Reveal superseded")); + const done = new ManualPromise(); + this._pendingReveal = { sessionName, workspaceDir, done }; + void this._tryRevealPending(); + return done; + } + revealPage(pageId4) { + const existing = this._pendingReveal; + if (existing && existing.pageId === pageId4) + return existing.done; + existing?.done.reject(new Error("Reveal superseded")); + const done = new ManualPromise(); + this._pendingReveal = { pageId: pageId4, done }; + void this._tryRevealPending(); + return done; + } + async _tryRevealPending() { + const pending = this._pendingReveal; + if (!pending) + return; + const allPages = this._provider.contextEntries().flatMap((e) => e.context.pages().map((page2) => ({ entry: e, page: page2 }))); + let page; + if (pending.pageId !== void 0) { + page = allPages.find(({ page: p }) => pageId(p) === pending.pageId)?.page; + } else if (pending.sessionName !== void 0) { + page = allPages.find(({ entry }) => entry.descriptor.title === pending.sessionName && (pending.workspaceDir === void 0 || entry.descriptor.workspaceDir === pending.workspaceDir))?.page; + } + if (!page) + return; + this._pendingReveal = void 0; + try { + await this._switchAttachedTo(page); + this._pushTabs(); + pending.done.resolve(); + } catch (e) { + pending.done.reject(e instanceof Error ? e : new Error(String(e))); + throw e; + } + } + async submitAnnotation(params2) { + this._resolvePendingAnnotate({ type: "submitted", frames: params2.frames, feedback: params2.feedback }); + } + async cancelAnnotation() { + this._resolvePendingAnnotate({ type: "cancelled" }); + } + async reveal(params2) { + switch (import_os28.default.platform()) { + case "darwin": + (0, import_child_process7.execFile)("open", ["-R", params2.path]); + break; + case "win32": + (0, import_child_process7.execFile)("explorer", ["/select,", params2.path]); + break; + case "linux": + (0, import_child_process7.execFile)("xdg-open", [import_path57.default.dirname(params2.path)]); + break; + } + } + async readStream(params2) { + const stream3 = this._streams.get(params2.streamId); + if (!stream3) + throw new Error(`Unknown stream: ${params2.streamId}`); + const buffer = Buffer.alloc(256 * 1024); + const { bytesRead } = await stream3.handle.read(buffer, 0, buffer.length); + if (bytesRead === 0) { + this._streams.delete(params2.streamId); + await stream3.handle.close().catch(() => { + }); + await import_fs59.default.promises.unlink(stream3.path).catch(() => { + }); + return { data: "", eof: true }; + } + return { data: buffer.subarray(0, bytesRead).toString("base64"), eof: false }; + } + visible() { + return this._visible; + } + emitSessions(sessions) { + this.sendEvent?.("sessions", { sessions, clientInfo: createClientInfo() }); + } + emitTabs(tabs) { + this.sendEvent?.("tabs", { tabs }); + } + emitFrame(data, viewportWidth, viewportHeight) { + this.sendEvent?.("frame", { data, viewportWidth, viewportHeight }); + } + emitAnnotate({ signal }) { + return new Promise((resolve) => { + if (signal.aborted) { + resolve({ type: "cancelled" }); + return; + } + this._resolvePendingAnnotate({ type: "cancelled" }); + const onAbort = () => { + if (this._pendingAnnotate !== pending) + return; + this._pendingAnnotate = void 0; + pending.dispose(); + this.sendEvent?.("cancelAnnotate", {}); + resolve({ type: "cancelled" }); + }; + const pending = { + resolve, + dispose: () => signal.removeEventListener("abort", onAbort) + }; + this._pendingAnnotate = pending; + signal.addEventListener("abort", onAbort); + this._tryFireAnnotate(); + }); + } + _tryFireAnnotate() { + if (!this._pendingAnnotate || !this._attachedPage) + return; + this.sendEvent?.("annotate", {}); + } + _resolvePendingAnnotate(result2) { + this._pendingAnnotate?.resolve(result2); + this._pendingAnnotate?.dispose(); + this._pendingAnnotate = void 0; + } + artifactsDirFor(context2) { + for (const entry of this._provider.contextEntries()) { + if (entry.context === context2) + return entry.descriptor.browser.launchOptions.artifactsDir ?? this._recordingDir; + } + return this._recordingDir; + } + _pushTabs() { + if (this._pushTabsScheduled) + return; + this._pushTabsScheduled = true; + queueMicrotask(async () => { + this._pushTabsScheduled = false; + try { + const tabs = await this._aggregateTabs(); + this.emitTabs(tabs); + } catch { + } + }); + } + _pushSessions() { + void (async () => { + try { + const sessions = await this._provider.sessions(); + this.emitSessions(sessions); + } catch { + } + })(); + } + async _aggregateTabs() { + const attachedPage = this._attachedPage?.page; + const tasks = []; + for (const { browser, context: context2 } of this._provider.contextEntries()) { + for (const page of context2.pages()) { + tasks.push((async () => ({ + browser: browserId(browser), + context: contextId(context2), + page: pageId(page), + title: await page.title().catch(() => ""), + url: page.url(), + selected: page === attachedPage, + faviconUrl: await faviconUrl(page) + }))()); + } + } + return await Promise.all(tasks); + } + async _switchAttachedTo(page) { + if (this._attachedPage?.page === page) + return; + this._attachedPage?.dispose(); + const attached = new AttachedPage(this, page); + this._attachedPage = attached; + try { + await attached.init(); + } catch (e) { + if (this._attachedPage === attached) + this._attachedPage = void 0; + attached.dispose(); + throw e; + } + if (this._attachedPage === attached) + this._tryFireAnnotate(); + } + _handleAttachedPageClose(context2) { + this._attachedPage?.dispose(); + this._attachedPage = void 0; + const next = context2.pages()[0]; + if (next) + void this._switchAttachedTo(next); + this._pushTabs(); + } + }; + AttachedPage = class { + constructor(owner, page) { + this._listeners = []; + this._screencastRunning = false; + this._recordingPath = null; + this._disposed = false; + this._owner = owner; + this._page = page; + } + get page() { + return this._page; + } + async init() { + this._listeners.push( + eventsHelper.addEventListener(this._page, "close", () => { + this._owner._handleAttachedPageClose(this._page.context()); + }), + eventsHelper.addEventListener(this._page, "framenavigated", (frame) => { + if (frame === this._page.mainFrame()) + this._owner._pushTabs(); + }) + ); + this._owner._pushTabs(); + if (this._owner.visible()) { + this._screencastRunning = true; + await this._startScreencast(this._page); + } + } + dispose() { + this._disposed = true; + this._listeners.forEach((d) => d.dispose()); + this._listeners = []; + if (this._screencastRunning) + this._page.screencast.stop().catch(() => { + }); + this._screencastRunning = false; + this._recordingPath = null; + } + async setScreencastActive(active) { + if (active && !this._screencastRunning) { + this._screencastRunning = true; + await this._startScreencast(this._page); + } else if (!active && this._screencastRunning) { + this._screencastRunning = false; + await this._page.screencast.stop().catch(() => { + }); + } + } + async navigate(params2) { + if (!params2.url) + return; + await this._page.goto(params2.url); + } + async back() { + await this._page.goBack(); + } + async forward() { + await this._page.goForward(); + } + async reload() { + await this._page.reload(); + } + async mousemove(params2) { + await this._page.mouse.move(params2.x, params2.y); + } + async mousedown(params2) { + await this._page.mouse.move(params2.x, params2.y); + await this._page.mouse.down({ button: params2.button || "left" }); + } + async mouseup(params2) { + await this._page.mouse.move(params2.x, params2.y); + await this._page.mouse.up({ button: params2.button || "left" }); + } + async wheel(params2) { + await this._page.mouse.wheel(params2.deltaX, params2.deltaY); + } + async keydown(params2) { + await this._page.keyboard.down(params2.key); + } + async keyup(params2) { + await this._page.keyboard.up(params2.key); + } + async startRecording() { + const artifactsDir = this._owner.artifactsDirFor(this._page.context()); + this._recordingPath = import_path57.default.join(artifactsDir, `recording-${Date.now()}.webm`); + if (this._screencastRunning) + await this._restartScreencast(this._page); + } + async stopRecording() { + const p = this._recordingPath; + if (!p) + throw new Error("No recording in progress"); + this._recordingPath = null; + if (this._screencastRunning) + await this._restartScreencast(this._page); + const handle = await import_fs59.default.promises.open(p, "r"); + const streamId = import_crypto29.default.randomUUID(); + this._owner._streams.set(streamId, { handle, path: p }); + return { streamId }; + } + async screenshot() { + const buffer = await this._page.screenshot({ type: "png" }); + const ariaSnapshot = await this._page.ariaSnapshot({ boxes: true, mode: "ai" }); + const vp = await this._viewportSize(); + return { + data: buffer.toString("base64"), + viewportWidth: vp.width, + viewportHeight: vp.height, + ariaSnapshot + }; + } + async _viewportSize() { + const vp = this._page.viewportSize(); + if (vp) + return vp; + return await this._page.evaluate(() => ({ width: window.innerWidth, height: window.innerHeight })); + } + async _startScreencast(page) { + await page.screencast.start({ + onFrame: ({ data, viewportWidth, viewportHeight }) => { + if (this._disposed) + return; + this._owner.emitFrame(data.toString("base64"), viewportWidth, viewportHeight); + }, + size: { width: 1280, height: 800 }, + ...this._recordingPath ? { path: this._recordingPath } : {} + }); + } + async _restartScreencast(page) { + await page.screencast.stop().catch(() => { + }); + await this._startScreencast(page); + } + }; + } +}); + +// packages/playwright-core/src/tools/dashboard/registrySessionProvider.ts +function pageId2(p) { + return p._guid; +} +function contextId2(c) { + return c._guid; +} +var import_events29, BrowserTracker, RegistrySessionProvider; +var init_registrySessionProvider = __esm({ + "packages/playwright-core/src/tools/dashboard/registrySessionProvider.ts"() { + "use strict"; + import_events29 = require("events"); + init_eventsHelper(); + init_connect2(); + init_serverRegistry(); + init_sessionProvider(); + BrowserTracker = class _BrowserTracker { + constructor(descriptor, browser, callbacks) { + this._contextListeners = /* @__PURE__ */ new Map(); + this._browserListeners = []; + this.descriptor = descriptor; + this.browser = browser; + this._callbacks = callbacks; + } + static async create(descriptor, callbacks) { + try { + const browser = await connectToBrowserAcrossVersions(descriptor); + const slot = new _BrowserTracker(descriptor, browser, callbacks); + for (const context2 of browser.contexts()) + slot._wireContext(context2); + slot._browserListeners.push(eventsHelper.addEventListener(browser, "context", (context2) => { + slot._wireContext(context2); + })); + return slot; + } catch { + return void 0; + } + } + contexts() { + return this.browser.contexts(); + } + dispose() { + this._browserListeners.forEach((d) => d.dispose()); + this._browserListeners = []; + for (const listeners of this._contextListeners.values()) + listeners.forEach((d) => d.dispose()); + this._contextListeners.clear(); + void this.browser.close().catch(() => { + }); + } + _wireContext(context2) { + if (this._contextListeners.has(context2)) + return; + const onTabsChanged = () => this._callbacks.onTabsChanged(); + const listeners = [ + eventsHelper.addEventListener(context2, "page", onTabsChanged), + eventsHelper.addEventListener(context2, "pageload", onTabsChanged), + eventsHelper.addEventListener(context2, "pageclose", onTabsChanged), + eventsHelper.addEventListener(context2, "framenavigated", (frame) => { + if (frame === frame.page().mainFrame()) + this._callbacks.onTabsChanged(); + }), + eventsHelper.addEventListener(context2, "close", () => { + const ls = this._contextListeners.get(context2); + if (ls) { + ls.forEach((d) => d.dispose()); + this._contextListeners.delete(context2); + } + this._callbacks.onContextClosed(context2); + this._callbacks.onTabsChanged(); + }) + ]; + this._contextListeners.set(context2, listeners); + this._callbacks.onTabsChanged(); + } + }; + RegistrySessionProvider = class extends import_events29.EventEmitter { + constructor() { + super(...arguments); + this._trackers = /* @__PURE__ */ new Map(); + this._pushSessionsScheduled = false; + this._scheduleSessions = () => { + if (this._pushSessionsScheduled) + return; + this._pushSessionsScheduled = true; + queueMicrotask(async () => { + this._pushSessionsScheduled = false; + try { + const sessions = await this.sessions(); + await this._reconcile(sessions); + this.emit(SessionProviderEvent.SessionsChanged); + this.emit(SessionProviderEvent.TabsChanged); + } catch { + } + }); + }; + } + start() { + this._serverRegistryDispose = serverRegistry.watch(); + serverRegistry.on("added", this._scheduleSessions); + serverRegistry.on("removed", this._scheduleSessions); + serverRegistry.on("changed", this._scheduleSessions); + this._scheduleSessions(); + } + dispose() { + serverRegistry.off("added", this._scheduleSessions); + serverRegistry.off("removed", this._scheduleSessions); + serverRegistry.off("changed", this._scheduleSessions); + this._serverRegistryDispose?.(); + this._serverRegistryDispose = void 0; + for (const tracker of this._trackers.values()) + tracker.dispose(); + this._trackers.clear(); + this.removeAllListeners(); + } + async sessions() { + const byWs = await serverRegistry.list(); + const sessions = []; + for (const list of byWs.values()) { + for (const status of list) { + if (status.title.startsWith("--playwright-internal")) + continue; + sessions.push(status); + } + } + return sessions; + } + contextEntries() { + const entries = []; + for (const tracker of this._trackers.values()) { + for (const context2 of tracker.contexts()) + entries.push({ browser: tracker.browser, context: context2, descriptor: tracker.descriptor }); + } + return entries; + } + findContext(params2) { + const tracker = this._trackers.get(params2.browser); + if (!tracker) + return void 0; + return tracker.contexts().find((c) => contextId2(c) === params2.context); + } + findPage(params2) { + const context2 = this.findContext(params2); + return context2?.pages().find((p) => pageId2(p) === params2.page); + } + async closeSession(browserId2) { + const descriptor = serverRegistry.readDescriptor(browserId2); + const browser = await connectToBrowserAcrossVersions(descriptor); + try { + await Promise.all(browser.contexts().map((context2) => context2.close())); + await browser.close(); + } catch { + } + } + async _reconcile(sessions) { + const connectable = /* @__PURE__ */ new Map(); + for (const status of sessions) + connectable.set(status.browser.guid, status); + for (const [guid, tracker] of this._trackers) { + if (connectable.has(guid)) + continue; + tracker.dispose(); + this._trackers.delete(guid); + } + for (const [guid, status] of connectable) { + if (this._trackers.has(guid)) + continue; + const tracker = await BrowserTracker.create(status, { + onTabsChanged: () => this.emit(SessionProviderEvent.TabsChanged), + onContextClosed: (context2) => this.emit(SessionProviderEvent.ContextClosed, context2) + }); + if (!tracker) + continue; + if (this._trackers.has(guid)) { + tracker.dispose(); + continue; + } + this._trackers.set(guid, tracker); + } + } + }; + } +}); + +// packages/playwright-core/src/tools/dashboard/identitySessionProvider.ts +function synthesiseDescriptor(browser) { + const browserName = browser.browserType().name(); + const browserInfo2 = { + // eslint-disable-next-line no-restricted-syntax -- _guid is very conservative. + guid: browser._guid, + browserName, + launchOptions: {} + }; + return { + title: "Playwright", + playwrightVersion: packageJSON.version, + playwrightLib: packageRoot, + browser: browserInfo2 + }; +} +function pageId3(p) { + return p._guid; +} +function contextId3(c) { + return c._guid; +} +var import_events30, IdentitySessionProvider; +var init_identitySessionProvider = __esm({ + "packages/playwright-core/src/tools/dashboard/identitySessionProvider.ts"() { + "use strict"; + import_events30 = require("events"); + init_eventsHelper(); + init_package(); + init_sessionProvider(); + IdentitySessionProvider = class extends import_events30.EventEmitter { + constructor(context2) { + super(); + this._listeners = []; + this._closed = false; + const browser = context2.browser(); + if (!browser) + throw new Error("SingleContextDashboardProvider requires a context with an attached browser"); + this._context = context2; + this._browser = browser; + this._descriptor = synthesiseDescriptor(browser); + } + start() { + const emitTabsChanged = () => this.emit(SessionProviderEvent.TabsChanged); + this._listeners.push( + eventsHelper.addEventListener(this._context, "page", emitTabsChanged), + eventsHelper.addEventListener(this._context, "pageload", emitTabsChanged), + eventsHelper.addEventListener(this._context, "pageclose", emitTabsChanged), + eventsHelper.addEventListener(this._context, "framenavigated", (frame) => { + if (frame === frame.page().mainFrame()) + this.emit(SessionProviderEvent.TabsChanged); + }), + eventsHelper.addEventListener(this._context, "close", () => { + this._closed = true; + this.emit(SessionProviderEvent.ContextClosed, this._context); + this.emit(SessionProviderEvent.TabsChanged); + }) + ); + this.emit(SessionProviderEvent.SessionsChanged); + this.emit(SessionProviderEvent.TabsChanged); + const firstPage = this._context.pages()[0]; + if (firstPage) + this.emit(SessionProviderEvent.AttachRequested, firstPage); + } + dispose() { + this._listeners.forEach((d) => d.dispose()); + this._listeners = []; + this.removeAllListeners(); + } + async sessions() { + return this._closed ? [] : [this._descriptor]; + } + contextEntries() { + if (this._closed) + return []; + return [{ browser: this._browser, context: this._context, descriptor: this._descriptor }]; + } + findContext(params2) { + if (this._closed) + return void 0; + if (params2.browser !== this._descriptor.browser.guid) + return void 0; + if (contextId3(this._context) !== params2.context) + return void 0; + return this._context; + } + findPage(params2) { + const context2 = this.findContext(params2); + return context2?.pages().find((p) => pageId3(p) === params2.page); + } + async closeSession() { + } + }; + } +}); + +// packages/playwright-core/src/tools/dashboard/dashboardApp.ts +async function startDashboardServer(provider, options) { + const dashboardDir = libPath("vite", "dashboard"); + const httpServer = new HttpServer(dashboardDir); + const connections = /* @__PURE__ */ new Set(); + let connectionLanded = new ManualPromise(); + httpServer.createWebSocket(() => { + let connection; + connection = new DashboardConnection(provider, () => { + connections.delete(connection); + if (connections.size === 0) + connectionLanded = new ManualPromise(); + }, () => { + connectionLanded.resolve(); + }); + connections.add(connection); + return connection; + }); + const wsGuid = httpServer.wsGuid(); + httpServer.routePath("/", (_, response2) => { + response2.statusCode = 302; + response2.setHeader("Location", `/index.html?ws=${wsGuid}`); + response2.end(); + return true; + }); + if (false) + await attachDashboardDevServer(httpServer); + else + attachDashboardStaticServer(httpServer, dashboardDir); + await httpServer.start({ port: options.port, host: options.host }); + const reveal = async (next) => { + await connectionLanded; + await Promise.all([...connections].map(async (c) => { + if (next.pageId) + await c.revealPage(next.pageId); + else if (next.sessionName) + await c.revealSession(next.sessionName, next.workspaceDir); + })); + }; + const triggerAnnotate = async (cancellation) => { + await connectionLanded; + if (cancellation.aborted || connections.size === 0) + return { type: "cancelled" }; + return await Promise.race([...connections].map((c) => c.emitAnnotate({ signal: cancellation }))); + }; + const close3 = async () => { + for (const c of connections) + c.close?.(); + await httpServer.stop(); + }; + return { url: httpServer.urlPrefix("human-readable"), reveal, triggerAnnotate, close: close3 }; +} +function attachDashboardStaticServer(httpServer, dashboardDir) { + httpServer.routePrefix("/", (request2, response2) => { + const pathname = new URL(request2.url, `http://${request2.headers.host}`).pathname; + const filePath = pathname === "/" ? "index.html" : pathname.substring(1); + const resolved = import_path58.default.join(dashboardDir, filePath); + return httpServer.serveFile(request2, response2, resolved); + }); +} +async function innerOpenDashboardApp(options) { + const server = await startDashboardServer(new RegistrySessionProvider(), options); + void server.reveal(options).catch(() => { + }); + const { page } = await launchApp2("dashboard", { onClose: () => gracefullyProcessExitDoNotHang(0) }); + await page.goto(server.url); + return { page, server }; +} +async function launchApp2(appName, options) { + const channel = findChromiumChannelBestEffort("javascript"); + const context2 = await playwright.chromium.launchPersistentContext("", { + ignoreDefaultArgs: ["--enable-automation"], + channel, + headless: !!process.env.PWTEST_DASHBOARD_APP_BIND_TITLE, + args: [ + "--app=data:text/html,", + "--test-type=", + `--window-size=1280,800`, + `--window-position=100,100` + ], + viewport: null + }); + if (process.env.PWTEST_DASHBOARD_APP_BIND_TITLE) + await context2.browser()?.bind(process.env.PWTEST_DASHBOARD_APP_BIND_TITLE, { workspaceDir: process.cwd() }); + const [page] = context2.pages(); + if (process.platform === "darwin") { + context2.on("page", async (newPage) => { + if (newPage.mainFrame().url() === "chrome://new-tab-page/") { + await page.bringToFront(); + await newPage.close(); + } + }); + } + page.on("close", () => options?.onClose?.()); + const image = await import_fs60.default.promises.readFile(libPath("tools", "dashboard", "appIcon.png")); + await page._setDockTile?.(image); + await syncLocalStorageWithSettings2(page, appName); + return { context: context2, page }; +} +async function syncLocalStorageWithSettings2(page, appName) { + const settingsFile = process.env.PWTEST_DASHBOARD_SETTINGS_FILE ?? import_path58.default.join(registryDirectory, ".settings", `${appName}.json`); + await page.exposeBinding("_saveSerializedSettings", (_, settings2) => { + import_fs60.default.mkdirSync(import_path58.default.dirname(settingsFile), { recursive: true }); + import_fs60.default.writeFileSync(settingsFile, settings2); + }); + const settings = await import_fs60.default.promises.readFile(settingsFile, "utf-8").catch(() => "{}"); + await page.addInitScript( + `(${String((settings2) => { + if (location && location.protocol === "data:") + return; + if (window.top !== window) + return; + Object.entries(settings2).map(([k, v]) => localStorage[k] = v); + window.saveSettings = () => { + window._saveSerializedSettings(JSON.stringify({ ...localStorage })); + }; + })})(${settings}); + ` + ); +} +function dashboardSocketPath() { + return makeSocketPath("dashboard", "app"); +} +function parseOpenArgs() { + const args = minimist(process.argv.slice(2), { string: ["sessionName", "workspaceDir", "host", "pageId"], boolean: ["annotate", "kill"] }); + const portStr = args.port; + return { + sessionName: args.sessionName, + workspaceDir: args.workspaceDir, + pageId: args.pageId, + port: portStr !== void 0 ? Number(portStr) : void 0, + host: args.host, + annotate: !!args.annotate, + kill: !!args.kill + }; +} +async function acquireSingleton(options) { + const socketPath = dashboardSocketPath(); + if (process.platform !== "win32") + await import_fs60.default.promises.mkdir(import_path58.default.dirname(socketPath), { recursive: true }); + return await new Promise((resolve, reject) => { + const server = import_net11.default.createServer(); + server.listen(socketPath, () => resolve({ role: "winner", server })); + server.on("error", (err) => { + if (err.code !== "EADDRINUSE" && err.code !== "EEXIST") + return reject(err); + let ackBuffer = ""; + const client = import_net11.default.connect(socketPath, () => { + client.write(JSON.stringify(options) + "\n"); + }); + client.on("data", (chunk) => { + ackBuffer += chunk.toString(); + }); + client.on("end", () => { + try { + const { pid } = JSON.parse(ackBuffer.trim()); + resolve({ role: "loser", daemonPid: pid }); + } catch (e) { + reject(e); + } + }); + client.on("error", () => { + if (process.platform !== "win32") + import_fs60.default.unlinkSync(socketPath); + server.listen(socketPath, () => resolve({ role: "winner", server })); + }); + }); + }); +} +async function openDashboardApp() { + const options = parseOpenArgs(); + if (options.kill) { + await runKillClient(); + return; + } + if (options.annotate) { + await runAnnotateClient(options); + return; + } + process.on("unhandledRejection", (error) => { + console.error("Unhandled promise rejection:", error); + }); + if (options.port !== void 0) { + const server2 = await startDashboardServer(new RegistrySessionProvider(), options); + void server2.reveal(options).catch(() => { + }); + console.log(`Listening on ${server2.url}`); + await new Promise((f) => process.stdout.write("", f)); + selfDestructOnParentGone(); + return; + } + const stopSelfDestruct = selfDestructOnParentGone(); + const acquired = await acquireSingleton(options); + if (acquired.role === "loser") { + stopSelfDestruct(); + console.log(`Dashboard is running pid=${acquired.daemonPid}`); + await new Promise((f) => process.stdout.write("", f)); + return; + } + const { server } = acquired; + process.on("exit", () => server.close()); + try { + await startApp(server, options); + stopSelfDestruct(); + console.log(`Dashboard is running pid=${process.pid}`); + } catch (error) { + console.log(error); + gracefullyProcessExitDoNotHang(1); + } +} +async function startApp(server, options) { + const statePromise = innerOpenDashboardApp(options); + server.on("connection", (socket) => { + let buffer = ""; + socket.on("data", async (data) => { + buffer += data.toString(); + const newlineIndex = buffer.indexOf("\n"); + if (newlineIndex === -1) + return; + const line = buffer.slice(0, newlineIndex); + buffer = buffer.slice(newlineIndex + 1); + let parsed; + try { + parsed = JSON.parse(line); + } catch { + } + if (!parsed) { + socket.end(); + return; + } + const { page, server: dashboard } = await statePromise; + if (parsed.annotate) { + const cancellation = new AbortController(); + socket.on("close", () => cancellation.abort()); + socket.on("error", () => cancellation.abort()); + try { + await page?.bringToFront(); + await dashboard.reveal(parsed); + const result2 = await dashboard.triggerAnnotate(cancellation.signal); + socket.end(JSON.stringify(result2)); + } catch (e) { + socket.end(e); + } + } else if (parsed.kill) { + await dashboard.close().catch(() => { + }); + gracefullyProcessExitDoNotHang(0, () => new Promise((r) => socket.end(r))); + } else { + try { + await page?.bringToFront(); + await dashboard.reveal(parsed); + socket.end(JSON.stringify({ pid: process.pid }) + "\n"); + } catch (e) { + socket.end(e); + } + } + }); + }); + await statePromise; +} +async function openDashboardForContext(context2) { + const server = await startDashboardServer(new IdentitySessionProvider(context2), {}); + let closed = false; + const close3 = async () => { + if (closed) + return; + closed = true; + await server.close(); + }; + const { page } = await launchApp2("dashboard", { onClose: () => { + void close3(); + } }); + context2.on("close", () => { + void close3(); + }); + await page.goto(server.url); +} +async function runKillClient() { + const socketPath = dashboardSocketPath(); + await new Promise((resolve) => { + const client = import_net11.default.connect(socketPath); + client.once("connect", () => { + client.write(JSON.stringify({ kill: true }) + "\n"); + }); + client.once("end", () => resolve()); + client.once("error", () => resolve()); + }); +} +async function runAnnotateClient(options) { + selfDestructOnParentGone(); + const socketPath = dashboardSocketPath(); + const tryConnect = () => new Promise((resolve) => { + const s = import_net11.default.connect(socketPath); + const onError = () => { + s.destroy(); + resolve(void 0); + }; + s.once("connect", () => { + s.off("error", onError); + resolve(s); + }); + s.once("error", onError); + }); + const deadline = Date.now() + 15e3; + let socket; + while (Date.now() < deadline) { + socket = await tryConnect(); + if (socket) + break; + await new Promise((r) => setTimeout(r, 200)); + } + if (!socket) { + console.error("Dashboard did not start in time."); + gracefullyProcessExitDoNotHang(1); + return; + } + socket.write(JSON.stringify(options) + "\n"); + const chunks = []; + await new Promise((resolve, reject) => { + socket.on("data", (chunk) => chunks.push(chunk)); + socket.on("end", () => resolve()); + socket.on("error", reject); + }); + socket.destroy(); + const text2 = Buffer.concat(chunks).toString(); + if (!text2) + return; + console.log(text2); +} +function selfDestructOnParentGone() { + const onClose = () => gracefullyProcessExitDoNotHang(0); + process.stdin.on("close", onClose); + return () => process.stdin.off("close", onClose); +} +var import_fs60, import_path58, import_net11; +var init_dashboardApp = __esm({ + "packages/playwright-core/src/tools/dashboard/dashboardApp.ts"() { + "use strict"; + import_fs60 = __toESM(require("fs")); + import_path58 = __toESM(require("path")); + import_net11 = __toESM(require("net")); + init_httpServer(); + init_fileUtils(); + init_processLauncher(); + init_manualPromise(); + init_package(); + init_inprocess(); + init_registry(); + init_minimist(); + init_dashboardController(); + init_registrySessionProvider(); + init_identitySessionProvider(); + } +}); + +// packages/playwright-core/src/tools/index.ts +var tools_exports = {}; +__export(tools_exports, { + BrowserBackend: () => BrowserBackend, + DirTraceLoaderBackend: () => DirTraceLoaderBackend, + Tab: () => Tab, + browserTools: () => browserTools, + cliProgram: () => program2, + compareSemver: () => compareSemver, + createConnection: () => createConnection, + decorateCliDaemonProgram: () => decorateProgram3, + decorateMCPCommand: () => decorateMCPCommand, + extractTrace: () => extractTrace, + filteredTools: () => filteredTools, + generateHelp: () => generateHelp, + generateHelpJSON: () => generateHelpJSON, + isProfileLocked: () => isProfileLocked, + isSystemDirectory: () => isSystemDirectory, + openDashboardApp: () => openDashboardApp, + openDashboardForContext: () => openDashboardForContext, + outputDir: () => outputDir, + parseResponse: () => parseResponse, + resolveCLIConfigForCLI: () => resolveCLIConfigForCLI, + resolveCLIConfigForMCP: () => resolveCLIConfigForMCP, + setupExitWatchdog: () => setupExitWatchdog, + start: () => start2 +}); +var init_tools2 = __esm({ + "packages/playwright-core/src/tools/index.ts"() { + "use strict"; + init_watchdog(); + init_browserBackend(); + init_response(); + init_tab(); + init_tools(); + init_server2(); + init_mcp(); + init_config2(); + init_context(); + init_fileUtils(); + init_browserFactory(); + init_socketConnection(); + init_traceParser(); + init_program3(); + init_program(); + init_helpGenerator(); + init_program4(); + init_dashboardApp(); + } +}); + +// packages/playwright-core/src/coreBundle.ts +var coreBundle_exports = {}; +__export(coreBundle_exports, { + clientEventEmitter: () => EventEmitter3, + getPlaywrightVersion: () => getPlaywrightVersion, + getUserAgent: () => getUserAgent, + inprocess: () => inprocess_exports, + iso: () => isomorphic_exports, + libCli: () => program_exports, + libCliTestStub: () => programWithTestStub_exports, + oop: () => outofprocess_exports, + registry: () => registry_exports, + remote: () => playwrightServer_exports, + server: () => server_exports, + tools: () => tools_exports, + utils: () => utils_exports +}); +module.exports = __toCommonJS(coreBundle_exports); +var init_coreBundle = __esm({ + "packages/playwright-core/src/coreBundle.ts"() { + init_isomorphic(); + init_utils(); + init_eventEmitter(); + init_program2(); + init_programWithTestStub(); + init_inprocess(); + init_outofprocess(); + init_playwrightServer(); + init_registry(); + init_server(); + init_tools2(); + init_userAgent(); + } +}); +init_coreBundle(); +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + clientEventEmitter, + getPlaywrightVersion, + getUserAgent, + inprocess, + iso, + libCli, + libCliTestStub, + oop, + registry, + remote, + server, + tools, + utils +}); +/** + * @license + * Copyright 2024 Google Inc. + * Modifications copyright (c) Microsoft Corporation. + * SPDX-License-Identifier: Apache-2.0 + */ +/** + * @license + * Copyright 2023 Google Inc. + * SPDX-License-Identifier: Apache-2.0 + */ diff --git a/simulator/e2e/node_modules/playwright-core/lib/entry/cliDaemon.js b/simulator/e2e/node_modules/playwright-core/lib/entry/cliDaemon.js new file mode 100644 index 0000000..480f3b9 --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/lib/entry/cliDaemon.js @@ -0,0 +1,5 @@ +"use strict"; +var import_coreBundle = require("../coreBundle"); +const { program } = require("../utilsBundle"); +import_coreBundle.tools.decorateCliDaemonProgram(program); +void program.parseAsync(); diff --git a/simulator/e2e/node_modules/playwright-core/lib/entry/dashboardApp.js b/simulator/e2e/node_modules/playwright-core/lib/entry/dashboardApp.js new file mode 100644 index 0000000..5b7e30a --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/lib/entry/dashboardApp.js @@ -0,0 +1,3 @@ +"use strict"; +var import_coreBundle = require("../coreBundle"); +import_coreBundle.tools.openDashboardApp(); diff --git a/simulator/e2e/node_modules/playwright-core/lib/entry/mcp.js b/simulator/e2e/node_modules/playwright-core/lib/entry/mcp.js new file mode 100644 index 0000000..2b57633 --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/lib/entry/mcp.js @@ -0,0 +1,10 @@ +"use strict"; +var import_coreBundle = require("../coreBundle"); +var import_package = require("../package"); +const { program } = require("../utilsBundle"); +const p = program.version("Version " + import_package.packageJSON.version).name("Playwright MCP"); +import_coreBundle.tools.decorateMCPCommand(p); +program.parseAsync(process.argv).catch((e) => { + console.error(e.message); + import_coreBundle.utils.gracefullyProcessExitDoNotHang(1); +}); diff --git a/simulator/e2e/node_modules/playwright-core/lib/entry/oopBrowserDownload.js b/simulator/e2e/node_modules/playwright-core/lib/entry/oopBrowserDownload.js new file mode 100644 index 0000000..62dab85 --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/lib/entry/oopBrowserDownload.js @@ -0,0 +1,3 @@ +"use strict"; +var import_coreBundle = require("../coreBundle"); +import_coreBundle.registry.runOopDownloadBrowserMain(); diff --git a/simulator/e2e/node_modules/playwright-core/lib/package.js b/simulator/e2e/node_modules/playwright-core/lib/package.js new file mode 100644 index 0000000..607f39e --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/lib/package.js @@ -0,0 +1,50 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var package_exports = {}; +__export(package_exports, { + binPath: () => binPath, + libPath: () => libPath, + packageJSON: () => packageJSON, + packageRoot: () => packageRoot +}); +module.exports = __toCommonJS(package_exports); +var import_path = __toESM(require("path")); +const packageRoot = import_path.default.join(__dirname, ".."); +const packageJSON = require(import_path.default.join(packageRoot, "package.json")); +const binPath = import_path.default.join(packageRoot, "bin"); +function libPath(...parts) { + return import_path.default.join(packageRoot, "lib", ...parts); +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + binPath, + libPath, + packageJSON, + packageRoot +}); diff --git a/simulator/e2e/node_modules/playwright-core/lib/server/chromium/appIcon.png b/simulator/e2e/node_modules/playwright-core/lib/server/chromium/appIcon.png new file mode 100644 index 0000000..1c89846 Binary files /dev/null and b/simulator/e2e/node_modules/playwright-core/lib/server/chromium/appIcon.png differ diff --git a/simulator/e2e/node_modules/playwright-core/lib/server/deviceDescriptorsSource.json b/simulator/e2e/node_modules/playwright-core/lib/server/deviceDescriptorsSource.json new file mode 100644 index 0000000..9b93a48 --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/lib/server/deviceDescriptorsSource.json @@ -0,0 +1,2739 @@ +{ + "Blackberry PlayBook": { + "userAgent": "Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML like Gecko) Version/26.5 Safari/536.2+", + "viewport": { + "width": 600, + "height": 1024 + }, + "deviceScaleFactor": 1, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "Blackberry PlayBook landscape": { + "userAgent": "Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML like Gecko) Version/26.5 Safari/536.2+", + "viewport": { + "width": 1024, + "height": 600 + }, + "deviceScaleFactor": 1, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "BlackBerry Z30": { + "userAgent": "Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) Version/26.5 Mobile Safari/537.10+", + "viewport": { + "width": 360, + "height": 640 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "BlackBerry Z30 landscape": { + "userAgent": "Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) Version/26.5 Mobile Safari/537.10+", + "viewport": { + "width": 640, + "height": 360 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "Galaxy Note 3": { + "userAgent": "Mozilla/5.0 (Linux; U; Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/26.5 Mobile Safari/534.30", + "viewport": { + "width": 360, + "height": 640 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "Galaxy Note 3 landscape": { + "userAgent": "Mozilla/5.0 (Linux; U; Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/26.5 Mobile Safari/534.30", + "viewport": { + "width": 640, + "height": 360 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "Galaxy Note II": { + "userAgent": "Mozilla/5.0 (Linux; U; Android 4.1; en-us; GT-N7100 Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/26.5 Mobile Safari/534.30", + "viewport": { + "width": 360, + "height": 640 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "Galaxy Note II landscape": { + "userAgent": "Mozilla/5.0 (Linux; U; Android 4.1; en-us; GT-N7100 Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/26.5 Mobile Safari/534.30", + "viewport": { + "width": 640, + "height": 360 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "Galaxy S III": { + "userAgent": "Mozilla/5.0 (Linux; U; Android 4.0; en-us; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/26.5 Mobile Safari/534.30", + "viewport": { + "width": 360, + "height": 640 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "Galaxy S III landscape": { + "userAgent": "Mozilla/5.0 (Linux; U; Android 4.0; en-us; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/26.5 Mobile Safari/534.30", + "viewport": { + "width": 640, + "height": 360 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "Galaxy S5": { + "userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 360, + "height": 640 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy S5 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 640, + "height": 360 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy S8": { + "userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 360, + "height": 740 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy S8 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 740, + "height": 360 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy S9+": { + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 320, + "height": 658 + }, + "deviceScaleFactor": 4.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy S9+ landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 658, + "height": 320 + }, + "deviceScaleFactor": 4.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy S24": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; SM-S921U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 360, + "height": 780 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy S24 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; SM-S921U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 780, + "height": 360 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy A55": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; SM-A556B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 480, + "height": 1040 + }, + "deviceScaleFactor": 2.25, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy A55 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; SM-A556B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 1040, + "height": 480 + }, + "deviceScaleFactor": 2.25, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Tab S4": { + "userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36", + "viewport": { + "width": 712, + "height": 1138 + }, + "deviceScaleFactor": 2.25, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Tab S4 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36", + "viewport": { + "width": 1138, + "height": 712 + }, + "deviceScaleFactor": 2.25, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Tab S9": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; SM-X710) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36", + "viewport": { + "width": 640, + "height": 1024 + }, + "deviceScaleFactor": 2.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Tab S9 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; SM-X710) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36", + "viewport": { + "width": 1024, + "height": 640 + }, + "deviceScaleFactor": 2.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Z Fold 6": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-F956U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 928, + "height": 1080 + }, + "viewport": { + "width": 928, + "height": 1004 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Z Fold 6 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-F956U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 1080, + "height": 928 + }, + "viewport": { + "width": 1028, + "height": 876 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Z Fold 6 Cover": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-F956U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 484, + "height": 1188 + }, + "viewport": { + "width": 484, + "height": 1112 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Z Fold 6 Cover landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-F956U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 1188, + "height": 484 + }, + "viewport": { + "width": 1136, + "height": 432 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Z Fold 7": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-F966U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 984, + "height": 1092 + }, + "viewport": { + "width": 984, + "height": 1016 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Z Fold 7 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-F966U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 1092, + "height": 984 + }, + "viewport": { + "width": 1040, + "height": 932 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Z Fold 7 Cover": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-F966U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 360, + "height": 840 + }, + "viewport": { + "width": 360, + "height": 764 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Z Fold 7 Cover landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-F966U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 840, + "height": 360 + }, + "viewport": { + "width": 788, + "height": 308 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Z Flip 6": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-F741U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 360, + "height": 880 + }, + "viewport": { + "width": 360, + "height": 804 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Z Flip 6 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-F741U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 880, + "height": 360 + }, + "viewport": { + "width": 828, + "height": 308 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Z Flip 6 Cover": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-F741U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 360, + "height": 374 + }, + "viewport": { + "width": 360, + "height": 298 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Z Flip 6 Cover landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-F741U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 374, + "height": 360 + }, + "viewport": { + "width": 322, + "height": 308 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Z Flip 7": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-F761U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 360, + "height": 840 + }, + "viewport": { + "width": 360, + "height": 764 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Z Flip 7 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-F761U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 840, + "height": 360 + }, + "viewport": { + "width": 788, + "height": 308 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Z Flip 7 Cover": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-F761U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 474, + "height": 524 + }, + "viewport": { + "width": 474, + "height": 448 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Z Flip 7 Cover landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-F761U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 524, + "height": 474 + }, + "viewport": { + "width": 472, + "height": 422 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "iPad (gen 5)": { + "userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "viewport": { + "width": 768, + "height": 1024 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPad (gen 5) landscape": { + "userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "viewport": { + "width": 1024, + "height": 768 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPad (gen 6)": { + "userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "viewport": { + "width": 768, + "height": 1024 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPad (gen 6) landscape": { + "userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "viewport": { + "width": 1024, + "height": 768 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPad (gen 7)": { + "userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "viewport": { + "width": 810, + "height": 1080 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPad (gen 7) landscape": { + "userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "viewport": { + "width": 1080, + "height": 810 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPad (gen 11)": { + "userAgent": "Mozilla/5.0 (iPad; CPU OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/19E241 Safari/604.1", + "viewport": { + "width": 656, + "height": 944 + }, + "deviceScaleFactor": 2.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPad (gen 11) landscape": { + "userAgent": "Mozilla/5.0 (iPad; CPU OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/19E241 Safari/604.1", + "viewport": { + "width": 944, + "height": 656 + }, + "deviceScaleFactor": 2.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPad Mini": { + "userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "viewport": { + "width": 768, + "height": 1024 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPad Mini landscape": { + "userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "viewport": { + "width": 1024, + "height": 768 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPad Pro 11": { + "userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "viewport": { + "width": 834, + "height": 1194 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPad Pro 11 landscape": { + "userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "viewport": { + "width": 1194, + "height": 834 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 6": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + "viewport": { + "width": 375, + "height": 667 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 6 landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + "viewport": { + "width": 667, + "height": 375 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 6 Plus": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + "viewport": { + "width": 414, + "height": 736 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 6 Plus landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + "viewport": { + "width": 736, + "height": 414 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 7": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + "viewport": { + "width": 375, + "height": 667 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 7 landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + "viewport": { + "width": 667, + "height": 375 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 7 Plus": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + "viewport": { + "width": 414, + "height": 736 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 7 Plus landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + "viewport": { + "width": 736, + "height": 414 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 8": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + "viewport": { + "width": 375, + "height": 667 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 8 landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + "viewport": { + "width": 667, + "height": 375 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 8 Plus": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + "viewport": { + "width": 414, + "height": 736 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 8 Plus landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + "viewport": { + "width": 736, + "height": 414 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone SE": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/26.5 Mobile/14E304 Safari/602.1", + "viewport": { + "width": 320, + "height": 568 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone SE landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/26.5 Mobile/14E304 Safari/602.1", + "viewport": { + "width": 568, + "height": 320 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone SE (3rd gen)": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/26.5 Mobile/19E241 Safari/602.1", + "viewport": { + "width": 375, + "height": 667 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone SE (3rd gen) landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/26.5 Mobile/19E241 Safari/602.1", + "viewport": { + "width": 667, + "height": 375 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone X": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + "viewport": { + "width": 375, + "height": 812 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone X landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + "viewport": { + "width": 812, + "height": 375 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone XR": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "viewport": { + "width": 414, + "height": 896 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone XR landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "viewport": { + "width": 896, + "height": 414 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 11": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 414, + "height": 896 + }, + "viewport": { + "width": 414, + "height": 715 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 11 landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 414, + "height": 896 + }, + "viewport": { + "width": 800, + "height": 364 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 11 Pro": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 375, + "height": 812 + }, + "viewport": { + "width": 375, + "height": 635 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 11 Pro landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 375, + "height": 812 + }, + "viewport": { + "width": 724, + "height": 325 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 11 Pro Max": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 414, + "height": 896 + }, + "viewport": { + "width": 414, + "height": 715 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 11 Pro Max landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 414, + "height": 896 + }, + "viewport": { + "width": 808, + "height": 364 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 12": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 390, + "height": 844 + }, + "viewport": { + "width": 390, + "height": 664 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 12 landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 390, + "height": 844 + }, + "viewport": { + "width": 750, + "height": 340 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 12 Pro": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 390, + "height": 844 + }, + "viewport": { + "width": 390, + "height": 664 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 12 Pro landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 390, + "height": 844 + }, + "viewport": { + "width": 750, + "height": 340 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 12 Pro Max": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 428, + "height": 926 + }, + "viewport": { + "width": 428, + "height": 746 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 12 Pro Max landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 428, + "height": 926 + }, + "viewport": { + "width": 832, + "height": 378 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 12 Mini": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 375, + "height": 812 + }, + "viewport": { + "width": 375, + "height": 629 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 12 Mini landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 375, + "height": 812 + }, + "viewport": { + "width": 712, + "height": 325 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 13": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 390, + "height": 844 + }, + "viewport": { + "width": 390, + "height": 664 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 13 landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 390, + "height": 844 + }, + "viewport": { + "width": 750, + "height": 342 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 13 Pro": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 390, + "height": 844 + }, + "viewport": { + "width": 390, + "height": 664 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 13 Pro landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 390, + "height": 844 + }, + "viewport": { + "width": 750, + "height": 342 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 13 Pro Max": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 428, + "height": 926 + }, + "viewport": { + "width": 428, + "height": 746 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 13 Pro Max landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 428, + "height": 926 + }, + "viewport": { + "width": 832, + "height": 380 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 13 Mini": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 375, + "height": 812 + }, + "viewport": { + "width": 375, + "height": 629 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 13 Mini landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 375, + "height": 812 + }, + "viewport": { + "width": 712, + "height": 327 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 14": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 390, + "height": 844 + }, + "viewport": { + "width": 390, + "height": 664 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 14 landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 390, + "height": 844 + }, + "viewport": { + "width": 750, + "height": 340 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 14 Plus": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 428, + "height": 926 + }, + "viewport": { + "width": 428, + "height": 746 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 14 Plus landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 428, + "height": 926 + }, + "viewport": { + "width": 832, + "height": 378 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 14 Pro": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 393, + "height": 852 + }, + "viewport": { + "width": 393, + "height": 660 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 14 Pro landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 393, + "height": 852 + }, + "viewport": { + "width": 734, + "height": 343 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 14 Pro Max": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 430, + "height": 932 + }, + "viewport": { + "width": 430, + "height": 740 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 14 Pro Max landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 430, + "height": 932 + }, + "viewport": { + "width": 814, + "height": 380 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 15": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 393, + "height": 852 + }, + "viewport": { + "width": 393, + "height": 659 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 15 landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 393, + "height": 852 + }, + "viewport": { + "width": 734, + "height": 343 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 15 Plus": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 430, + "height": 932 + }, + "viewport": { + "width": 430, + "height": 739 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 15 Plus landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 430, + "height": 932 + }, + "viewport": { + "width": 814, + "height": 380 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 15 Pro": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 393, + "height": 852 + }, + "viewport": { + "width": 393, + "height": 659 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 15 Pro landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 393, + "height": 852 + }, + "viewport": { + "width": 734, + "height": 343 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 15 Pro Max": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 430, + "height": 932 + }, + "viewport": { + "width": 430, + "height": 739 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 15 Pro Max landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 430, + "height": 932 + }, + "viewport": { + "width": 814, + "height": 380 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 16": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 393, + "height": 852 + }, + "viewport": { + "width": 393, + "height": 659 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 16 landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 393, + "height": 852 + }, + "viewport": { + "width": 734, + "height": 343 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 16 Plus": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 430, + "height": 932 + }, + "viewport": { + "width": 430, + "height": 739 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 16 Plus landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 430, + "height": 932 + }, + "viewport": { + "width": 814, + "height": 380 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 16 Pro": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 402, + "height": 874 + }, + "viewport": { + "width": 402, + "height": 681 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 16 Pro landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 402, + "height": 874 + }, + "viewport": { + "width": 756, + "height": 352 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 16 Pro Max": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 440, + "height": 956 + }, + "viewport": { + "width": 440, + "height": 763 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 16 Pro Max landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 440, + "height": 956 + }, + "viewport": { + "width": 838, + "height": 390 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 16e": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 390, + "height": 844 + }, + "viewport": { + "width": 390, + "height": 651 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 16e landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 390, + "height": 844 + }, + "viewport": { + "width": 726, + "height": 340 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 17": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 402, + "height": 874 + }, + "viewport": { + "width": 402, + "height": 681 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 17 landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 402, + "height": 874 + }, + "viewport": { + "width": 756, + "height": 352 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone Air": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 420, + "height": 912 + }, + "viewport": { + "width": 420, + "height": 719 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone Air landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 420, + "height": 912 + }, + "viewport": { + "width": 794, + "height": 370 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 17 Pro": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 402, + "height": 874 + }, + "viewport": { + "width": 402, + "height": 681 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 17 Pro landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 402, + "height": 874 + }, + "viewport": { + "width": 756, + "height": 352 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 17 Pro Max": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 440, + "height": 956 + }, + "viewport": { + "width": 440, + "height": 763 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 17 Pro Max landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 440, + "height": 956 + }, + "viewport": { + "width": 838, + "height": 390 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 17e": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 390, + "height": 844 + }, + "viewport": { + "width": 390, + "height": 651 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 17e landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 390, + "height": 844 + }, + "viewport": { + "width": 726, + "height": 340 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "Kindle Fire HDX": { + "userAgent": "Mozilla/5.0 (Linux; U; en-us; KFAPWI Build/JDQ39) AppleWebKit/535.19 (KHTML, like Gecko) Silk/3.13 Safari/535.19 Silk-Accelerated=true", + "viewport": { + "width": 800, + "height": 1280 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "Kindle Fire HDX landscape": { + "userAgent": "Mozilla/5.0 (Linux; U; en-us; KFAPWI Build/JDQ39) AppleWebKit/535.19 (KHTML, like Gecko) Silk/3.13 Safari/535.19 Silk-Accelerated=true", + "viewport": { + "width": 1280, + "height": 800 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "LG Optimus L70": { + "userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 384, + "height": 640 + }, + "deviceScaleFactor": 1.25, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "LG Optimus L70 landscape": { + "userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 640, + "height": 384 + }, + "deviceScaleFactor": 1.25, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Microsoft Lumia 550": { + "userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36 Edge/14.14263", + "viewport": { + "width": 360, + "height": 640 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Microsoft Lumia 550 landscape": { + "userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36 Edge/14.14263", + "viewport": { + "width": 640, + "height": 360 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Microsoft Lumia 950": { + "userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36 Edge/14.14263", + "viewport": { + "width": 360, + "height": 640 + }, + "deviceScaleFactor": 4, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Microsoft Lumia 950 landscape": { + "userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36 Edge/14.14263", + "viewport": { + "width": 640, + "height": 360 + }, + "deviceScaleFactor": 4, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nexus 10": { + "userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36", + "viewport": { + "width": 800, + "height": 1280 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nexus 10 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36", + "viewport": { + "width": 1280, + "height": 800 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nexus 4": { + "userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 384, + "height": 640 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nexus 4 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 640, + "height": 384 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nexus 5": { + "userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 360, + "height": 640 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nexus 5 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 640, + "height": 360 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nexus 5X": { + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 412, + "height": 732 + }, + "deviceScaleFactor": 2.625, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nexus 5X landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 732, + "height": 412 + }, + "deviceScaleFactor": 2.625, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nexus 6": { + "userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 412, + "height": 732 + }, + "deviceScaleFactor": 3.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nexus 6 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 732, + "height": 412 + }, + "deviceScaleFactor": 3.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nexus 6P": { + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 412, + "height": 732 + }, + "deviceScaleFactor": 3.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nexus 6P landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 732, + "height": 412 + }, + "deviceScaleFactor": 3.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nexus 7": { + "userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36", + "viewport": { + "width": 600, + "height": 960 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nexus 7 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36", + "viewport": { + "width": 960, + "height": 600 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nokia Lumia 520": { + "userAgent": "Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 520)", + "viewport": { + "width": 320, + "height": 533 + }, + "deviceScaleFactor": 1.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nokia Lumia 520 landscape": { + "userAgent": "Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 520)", + "viewport": { + "width": 533, + "height": 320 + }, + "deviceScaleFactor": 1.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nokia N9": { + "userAgent": "Mozilla/5.0 (MeeGo; NokiaN9) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13", + "viewport": { + "width": 480, + "height": 854 + }, + "deviceScaleFactor": 1, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "Nokia N9 landscape": { + "userAgent": "Mozilla/5.0 (MeeGo; NokiaN9) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13", + "viewport": { + "width": 854, + "height": 480 + }, + "deviceScaleFactor": 1, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "Pixel 2": { + "userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 411, + "height": 731 + }, + "deviceScaleFactor": 2.625, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 2 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 731, + "height": 411 + }, + "deviceScaleFactor": 2.625, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 2 XL": { + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 411, + "height": 823 + }, + "deviceScaleFactor": 3.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 2 XL landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 823, + "height": 411 + }, + "deviceScaleFactor": 3.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 3": { + "userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 393, + "height": 786 + }, + "deviceScaleFactor": 2.75, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 3 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 786, + "height": 393 + }, + "deviceScaleFactor": 2.75, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 4": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 353, + "height": 745 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 4 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 745, + "height": 353 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 4a (5G)": { + "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 412, + "height": 892 + }, + "viewport": { + "width": 412, + "height": 765 + }, + "deviceScaleFactor": 2.63, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 4a (5G) landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "height": 892, + "width": 412 + }, + "viewport": { + "width": 840, + "height": 312 + }, + "deviceScaleFactor": 2.63, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 5": { + "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 393, + "height": 851 + }, + "viewport": { + "width": 393, + "height": 727 + }, + "deviceScaleFactor": 2.75, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 5 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 851, + "height": 393 + }, + "viewport": { + "width": 802, + "height": 293 + }, + "deviceScaleFactor": 2.75, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 6": { + "userAgent": "Mozilla/5.0 (Linux; Android 12; Pixel 6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 412, + "height": 915 + }, + "viewport": { + "width": 412, + "height": 839 + }, + "deviceScaleFactor": 2.625, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 6 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 12; Pixel 6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 915, + "height": 412 + }, + "viewport": { + "width": 863, + "height": 360 + }, + "deviceScaleFactor": 2.625, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 6 Pro": { + "userAgent": "Mozilla/5.0 (Linux; Android 12; Pixel 6 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 412, + "height": 892 + }, + "viewport": { + "width": 412, + "height": 816 + }, + "deviceScaleFactor": 3.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 6 Pro landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 12; Pixel 6 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 892, + "height": 412 + }, + "viewport": { + "width": 840, + "height": 360 + }, + "deviceScaleFactor": 3.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 6a": { + "userAgent": "Mozilla/5.0 (Linux; Android 12; Pixel 6a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 412, + "height": 915 + }, + "viewport": { + "width": 412, + "height": 839 + }, + "deviceScaleFactor": 2.625, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 6a landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 12; Pixel 6a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 915, + "height": 412 + }, + "viewport": { + "width": 863, + "height": 360 + }, + "deviceScaleFactor": 2.625, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 7": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 412, + "height": 915 + }, + "viewport": { + "width": 412, + "height": 839 + }, + "deviceScaleFactor": 2.625, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 7 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 915, + "height": 412 + }, + "viewport": { + "width": 863, + "height": 360 + }, + "deviceScaleFactor": 2.625, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 7 Pro": { + "userAgent": "Mozilla/5.0 (Linux; Android 13; Pixel 7 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 412, + "height": 892 + }, + "viewport": { + "width": 412, + "height": 816 + }, + "deviceScaleFactor": 3.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 7 Pro landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 13; Pixel 7 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 892, + "height": 412 + }, + "viewport": { + "width": 840, + "height": 360 + }, + "deviceScaleFactor": 3.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 7a": { + "userAgent": "Mozilla/5.0 (Linux; Android 13; Pixel 7a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 412, + "height": 915 + }, + "viewport": { + "width": 412, + "height": 839 + }, + "deviceScaleFactor": 2.625, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 7a landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 13; Pixel 7a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 915, + "height": 412 + }, + "viewport": { + "width": 863, + "height": 360 + }, + "deviceScaleFactor": 2.625, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 8": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 412, + "height": 915 + }, + "viewport": { + "width": 412, + "height": 839 + }, + "deviceScaleFactor": 2.625, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 8 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 915, + "height": 412 + }, + "viewport": { + "width": 863, + "height": 360 + }, + "deviceScaleFactor": 2.625, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 8 Pro": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 8 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 448, + "height": 997 + }, + "viewport": { + "width": 448, + "height": 921 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 8 Pro landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 8 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 997, + "height": 448 + }, + "viewport": { + "width": 945, + "height": 396 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 8a": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 8a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 412, + "height": 915 + }, + "viewport": { + "width": 412, + "height": 839 + }, + "deviceScaleFactor": 2.625, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 8a landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 8a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 915, + "height": 412 + }, + "viewport": { + "width": 863, + "height": 360 + }, + "deviceScaleFactor": 2.625, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 9": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 9) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 360, + "height": 808 + }, + "viewport": { + "width": 360, + "height": 732 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 9 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 9) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 808, + "height": 360 + }, + "viewport": { + "width": 756, + "height": 308 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 9 Pro": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 9 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 427, + "height": 952 + }, + "viewport": { + "width": 427, + "height": 876 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 9 Pro landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 9 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 952, + "height": 427 + }, + "viewport": { + "width": 900, + "height": 375 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 9 Pro XL": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 9 Pro XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 448, + "height": 997 + }, + "viewport": { + "width": 448, + "height": 921 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 9 Pro XL landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 9 Pro XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 997, + "height": 448 + }, + "viewport": { + "width": 945, + "height": 396 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 10": { + "userAgent": "Mozilla/5.0 (Linux; Android 16; Pixel 10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 360, + "height": 808 + }, + "viewport": { + "width": 360, + "height": 732 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 10 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 16; Pixel 10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 808, + "height": 360 + }, + "viewport": { + "width": 756, + "height": 308 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 10 Pro": { + "userAgent": "Mozilla/5.0 (Linux; Android 16; Pixel 10 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 427, + "height": 952 + }, + "viewport": { + "width": 427, + "height": 876 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 10 Pro landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 16; Pixel 10 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 952, + "height": 427 + }, + "viewport": { + "width": 900, + "height": 375 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 10 Pro XL": { + "userAgent": "Mozilla/5.0 (Linux; Android 16; Pixel 10 Pro XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 448, + "height": 997 + }, + "viewport": { + "width": 448, + "height": 921 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 10 Pro XL landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 16; Pixel 10 Pro XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 997, + "height": 448 + }, + "viewport": { + "width": 945, + "height": 396 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Moto G4": { + "userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 360, + "height": 640 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Moto G4 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 640, + "height": 360 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Desktop Chrome HiDPI": { + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36", + "screen": { + "width": 1792, + "height": 1120 + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "deviceScaleFactor": 2, + "isMobile": false, + "hasTouch": false, + "defaultBrowserType": "chromium" + }, + "Desktop Edge HiDPI": { + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36 Edg/149.0.7827.55", + "screen": { + "width": 1792, + "height": 1120 + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "deviceScaleFactor": 2, + "isMobile": false, + "hasTouch": false, + "defaultBrowserType": "chromium" + }, + "Desktop Firefox HiDPI": { + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:151.0) Gecko/20100101 Firefox/151.0", + "screen": { + "width": 1792, + "height": 1120 + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "deviceScaleFactor": 2, + "isMobile": false, + "hasTouch": false, + "defaultBrowserType": "firefox" + }, + "Desktop Safari": { + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Safari/605.1.15", + "screen": { + "width": 1792, + "height": 1120 + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "deviceScaleFactor": 2, + "isMobile": false, + "hasTouch": false, + "defaultBrowserType": "webkit" + }, + "Desktop Chrome": { + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36", + "screen": { + "width": 1920, + "height": 1080 + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "deviceScaleFactor": 1, + "isMobile": false, + "hasTouch": false, + "defaultBrowserType": "chromium" + }, + "Desktop Edge": { + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36 Edg/149.0.7827.55", + "screen": { + "width": 1920, + "height": 1080 + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "deviceScaleFactor": 1, + "isMobile": false, + "hasTouch": false, + "defaultBrowserType": "chromium" + }, + "Desktop Firefox": { + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:151.0) Gecko/20100101 Firefox/151.0", + "screen": { + "width": 1920, + "height": 1080 + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "deviceScaleFactor": 1, + "isMobile": false, + "hasTouch": false, + "defaultBrowserType": "firefox" + } +} \ No newline at end of file diff --git a/simulator/e2e/node_modules/playwright-core/lib/server/electron/loader.js b/simulator/e2e/node_modules/playwright-core/lib/server/electron/loader.js new file mode 100644 index 0000000..85427f6 --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/lib/server/electron/loader.js @@ -0,0 +1,115 @@ +"use strict"; + +// packages/playwright-core/src/server/electron/loader.ts +var import_electron = require("electron"); + +// packages/playwright-core/src/server/chromium/chromiumSwitches.ts +var disabledFeatures = [ + // See https://github.com/microsoft/playwright/issues/14047 + "AvoidUnnecessaryBeforeUnloadCheckSync", + // See https://github.com/microsoft/playwright/issues/38568 + "BoundaryEventDispatchTracksNodeRemoval", + "DestroyProfileOnBrowserClose", + // See https://github.com/microsoft/playwright/pull/13854 + "DialMediaRouteProvider", + "GlobalMediaControls", + // See https://github.com/microsoft/playwright/pull/27605 + "HttpsUpgrades", + // Hides the Lens feature in the URL address bar. Its not working in unofficial builds. + "LensOverlay", + // See https://github.com/microsoft/playwright/pull/8162 + "MediaRouter", + // See https://github.com/microsoft/playwright/issues/28023 + "PaintHolding", + // See https://github.com/microsoft/playwright/issues/32230 + "ThirdPartyStoragePartitioning", + // See https://github.com/microsoft/playwright/issues/16126 + "Translate", + // See https://issues.chromium.org/u/1/issues/435410220 + "AutoDeElevate", + // See https://github.com/microsoft/playwright/issues/37714 + "RenderDocument", + // Prevents downloading optimization hints on startup. + "OptimizationHints", + // Disables forced sign-in in Edge. + "msForceBrowserSignIn", + // Disables updating the preferred version in LaunchServices preferences on mac. + "msEdgeUpdateLaunchServicesPreferredVersion" +].filter(Boolean); +var chromiumSwitches = (options) => [ + "--disable-field-trial-config", + // https://source.chromium.org/chromium/chromium/src/+/main:testing/variations/README.md + "--disable-background-networking", + "--disable-background-timer-throttling", + "--disable-backgrounding-occluded-windows", + "--disable-back-forward-cache", + // Avoids surprises like main request not being intercepted during page.goBack(). + "--disable-breakpad", + "--disable-client-side-phishing-detection", + "--disable-component-extensions-with-background-pages", + "--disable-component-update", + // Avoids unneeded network activity after startup. + "--no-default-browser-check", + "--disable-default-apps", + "--disable-dev-shm-usage", + "--disable-edgeupdater", + // Disables Edge-specific updater on mac. + "--disable-extensions", + "--disable-features=" + disabledFeatures.join(","), + process.env.PLAYWRIGHT_LEGACY_SCREENSHOT ? "" : "--enable-features=CDPScreenshotNewSurface", + "--allow-pre-commit-input", + "--disable-hang-monitor", + "--disable-ipc-flooding-protection", + "--disable-popup-blocking", + "--disable-prompt-on-repost", + "--disable-renderer-backgrounding", + "--force-color-profile=srgb", + "--metrics-recording-only", + "--no-first-run", + "--password-store=basic", + "--use-mock-keychain", + // See https://chromium-review.googlesource.com/c/chromium/src/+/2436773 + "--no-service-autorun", + "--export-tagged-pdf", + // https://chromium-review.googlesource.com/c/chromium/src/+/4853540 + "--disable-search-engine-choice-screen", + // https://issues.chromium.org/41491762 + "--unsafely-disable-devtools-self-xss-warnings", + // Edge can potentially restart on Windows (msRelaunchNoCompatLayer) which looses its file descriptors (stdout/stderr) and CDP (3/4). Disable until fixed upstream. + "--edge-skip-compat-layer-relaunch", + // This disables Chrome for Testing infobar that is visible in the persistent context. + // The switch is ignored everywhere else, including Chromium/Chrome/Edge. + "--disable-infobars", + // Less annoying popups. + "--disable-search-engine-choice-screen", + // Prevents the "three dots" menu crash in IdentityManager::HasPrimaryAccount for ephemeral contexts. + options?.android ? "" : "--disable-sync" +].filter(Boolean); + +// packages/playwright-core/src/server/electron/loader.ts +process.argv.splice(1, process.argv.indexOf("--remote-debugging-port=0")); +for (const arg of chromiumSwitches()) { + const match = arg.match(/--([^=]*)=?(.*)/); + import_electron.app.commandLine.appendSwitch(match[1], match[2]); +} +var originalWhenReady = import_electron.app.whenReady(); +var originalEmit = import_electron.app.emit.bind(import_electron.app); +var readyEventArgs; +import_electron.app.emit = (event, ...args) => { + if (event === "ready") { + readyEventArgs = args; + return import_electron.app.listenerCount("ready") > 0; + } + return originalEmit(event, ...args); +}; +var isReady = false; +var whenReadyCallback; +var whenReadyPromise = new Promise((f) => whenReadyCallback = f); +import_electron.app.isReady = () => isReady; +import_electron.app.whenReady = () => whenReadyPromise; +globalThis.__playwright_run = async () => { + const event = await originalWhenReady; + isReady = true; + whenReadyCallback(event); + originalEmit("ready", ...readyEventArgs); +}; diff --git a/simulator/e2e/node_modules/playwright-core/lib/serverRegistry.js b/simulator/e2e/node_modules/playwright-core/lib/serverRegistry.js new file mode 100644 index 0000000..7c3d740 --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/lib/serverRegistry.js @@ -0,0 +1,7343 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __commonJS = (cb, mod) => function __require() { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; +}; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// node_modules/readdirp/node_modules/picomatch/lib/constants.js +var require_constants = __commonJS({ + "node_modules/readdirp/node_modules/picomatch/lib/constants.js"(exports2, module2) { + "use strict"; + var path3 = require("path"); + var WIN_SLASH = "\\\\/"; + var WIN_NO_SLASH = `[^${WIN_SLASH}]`; + var DEFAULT_MAX_EXTGLOB_RECURSION = 0; + var DOT_LITERAL = "\\."; + var PLUS_LITERAL = "\\+"; + var QMARK_LITERAL = "\\?"; + var SLASH_LITERAL = "\\/"; + var ONE_CHAR = "(?=.)"; + var QMARK = "[^/]"; + var END_ANCHOR = `(?:${SLASH_LITERAL}|$)`; + var START_ANCHOR = `(?:^|${SLASH_LITERAL})`; + var DOTS_SLASH = `${DOT_LITERAL}{1,2}${END_ANCHOR}`; + var NO_DOT = `(?!${DOT_LITERAL})`; + var NO_DOTS = `(?!${START_ANCHOR}${DOTS_SLASH})`; + var NO_DOT_SLASH = `(?!${DOT_LITERAL}{0,1}${END_ANCHOR})`; + var NO_DOTS_SLASH = `(?!${DOTS_SLASH})`; + var QMARK_NO_DOT = `[^.${SLASH_LITERAL}]`; + var STAR = `${QMARK}*?`; + var POSIX_CHARS = { + DOT_LITERAL, + PLUS_LITERAL, + QMARK_LITERAL, + SLASH_LITERAL, + ONE_CHAR, + QMARK, + END_ANCHOR, + DOTS_SLASH, + NO_DOT, + NO_DOTS, + NO_DOT_SLASH, + NO_DOTS_SLASH, + QMARK_NO_DOT, + STAR, + START_ANCHOR + }; + var WINDOWS_CHARS = { + ...POSIX_CHARS, + SLASH_LITERAL: `[${WIN_SLASH}]`, + QMARK: WIN_NO_SLASH, + STAR: `${WIN_NO_SLASH}*?`, + DOTS_SLASH: `${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$)`, + NO_DOT: `(?!${DOT_LITERAL})`, + NO_DOTS: `(?!(?:^|[${WIN_SLASH}])${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`, + NO_DOT_SLASH: `(?!${DOT_LITERAL}{0,1}(?:[${WIN_SLASH}]|$))`, + NO_DOTS_SLASH: `(?!${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`, + QMARK_NO_DOT: `[^.${WIN_SLASH}]`, + START_ANCHOR: `(?:^|[${WIN_SLASH}])`, + END_ANCHOR: `(?:[${WIN_SLASH}]|$)` + }; + var POSIX_REGEX_SOURCE = { + __proto__: null, + alnum: "a-zA-Z0-9", + alpha: "a-zA-Z", + ascii: "\\x00-\\x7F", + blank: " \\t", + cntrl: "\\x00-\\x1F\\x7F", + digit: "0-9", + graph: "\\x21-\\x7E", + lower: "a-z", + print: "\\x20-\\x7E ", + punct: "\\-!\"#$%&'()\\*+,./:;<=>?@[\\]^_`{|}~", + space: " \\t\\r\\n\\v\\f", + upper: "A-Z", + word: "A-Za-z0-9_", + xdigit: "A-Fa-f0-9" + }; + module2.exports = { + DEFAULT_MAX_EXTGLOB_RECURSION, + MAX_LENGTH: 1024 * 64, + POSIX_REGEX_SOURCE, + // regular expressions + REGEX_BACKSLASH: /\\(?![*+?^${}(|)[\]])/g, + REGEX_NON_SPECIAL_CHARS: /^[^@![\].,$*+?^{}()|\\/]+/, + REGEX_SPECIAL_CHARS: /[-*+?.^${}(|)[\]]/, + REGEX_SPECIAL_CHARS_BACKREF: /(\\?)((\W)(\3*))/g, + REGEX_SPECIAL_CHARS_GLOBAL: /([-*+?.^${}(|)[\]])/g, + REGEX_REMOVE_BACKSLASH: /(?:\[.*?[^\\]\]|\\(?=.))/g, + // Replace globs with equivalent patterns to reduce parsing time. + REPLACEMENTS: { + __proto__: null, + "***": "*", + "**/**": "**", + "**/**/**": "**" + }, + // Digits + CHAR_0: 48, + /* 0 */ + CHAR_9: 57, + /* 9 */ + // Alphabet chars. + CHAR_UPPERCASE_A: 65, + /* A */ + CHAR_LOWERCASE_A: 97, + /* a */ + CHAR_UPPERCASE_Z: 90, + /* Z */ + CHAR_LOWERCASE_Z: 122, + /* z */ + CHAR_LEFT_PARENTHESES: 40, + /* ( */ + CHAR_RIGHT_PARENTHESES: 41, + /* ) */ + CHAR_ASTERISK: 42, + /* * */ + // Non-alphabetic chars. + CHAR_AMPERSAND: 38, + /* & */ + CHAR_AT: 64, + /* @ */ + CHAR_BACKWARD_SLASH: 92, + /* \ */ + CHAR_CARRIAGE_RETURN: 13, + /* \r */ + CHAR_CIRCUMFLEX_ACCENT: 94, + /* ^ */ + CHAR_COLON: 58, + /* : */ + CHAR_COMMA: 44, + /* , */ + CHAR_DOT: 46, + /* . */ + CHAR_DOUBLE_QUOTE: 34, + /* " */ + CHAR_EQUAL: 61, + /* = */ + CHAR_EXCLAMATION_MARK: 33, + /* ! */ + CHAR_FORM_FEED: 12, + /* \f */ + CHAR_FORWARD_SLASH: 47, + /* / */ + CHAR_GRAVE_ACCENT: 96, + /* ` */ + CHAR_HASH: 35, + /* # */ + CHAR_HYPHEN_MINUS: 45, + /* - */ + CHAR_LEFT_ANGLE_BRACKET: 60, + /* < */ + CHAR_LEFT_CURLY_BRACE: 123, + /* { */ + CHAR_LEFT_SQUARE_BRACKET: 91, + /* [ */ + CHAR_LINE_FEED: 10, + /* \n */ + CHAR_NO_BREAK_SPACE: 160, + /* \u00A0 */ + CHAR_PERCENT: 37, + /* % */ + CHAR_PLUS: 43, + /* + */ + CHAR_QUESTION_MARK: 63, + /* ? */ + CHAR_RIGHT_ANGLE_BRACKET: 62, + /* > */ + CHAR_RIGHT_CURLY_BRACE: 125, + /* } */ + CHAR_RIGHT_SQUARE_BRACKET: 93, + /* ] */ + CHAR_SEMICOLON: 59, + /* ; */ + CHAR_SINGLE_QUOTE: 39, + /* ' */ + CHAR_SPACE: 32, + /* */ + CHAR_TAB: 9, + /* \t */ + CHAR_UNDERSCORE: 95, + /* _ */ + CHAR_VERTICAL_LINE: 124, + /* | */ + CHAR_ZERO_WIDTH_NOBREAK_SPACE: 65279, + /* \uFEFF */ + SEP: path3.sep, + /** + * Create EXTGLOB_CHARS + */ + extglobChars(chars) { + return { + "!": { type: "negate", open: "(?:(?!(?:", close: `))${chars.STAR})` }, + "?": { type: "qmark", open: "(?:", close: ")?" }, + "+": { type: "plus", open: "(?:", close: ")+" }, + "*": { type: "star", open: "(?:", close: ")*" }, + "@": { type: "at", open: "(?:", close: ")" } + }; + }, + /** + * Create GLOB_CHARS + */ + globChars(win32) { + return win32 === true ? WINDOWS_CHARS : POSIX_CHARS; + } + }; + } +}); + +// node_modules/readdirp/node_modules/picomatch/lib/utils.js +var require_utils = __commonJS({ + "node_modules/readdirp/node_modules/picomatch/lib/utils.js"(exports2) { + "use strict"; + var path3 = require("path"); + var win32 = process.platform === "win32"; + var { + REGEX_BACKSLASH, + REGEX_REMOVE_BACKSLASH, + REGEX_SPECIAL_CHARS, + REGEX_SPECIAL_CHARS_GLOBAL + } = require_constants(); + exports2.isObject = (val) => val !== null && typeof val === "object" && !Array.isArray(val); + exports2.hasRegexChars = (str) => REGEX_SPECIAL_CHARS.test(str); + exports2.isRegexChar = (str) => str.length === 1 && exports2.hasRegexChars(str); + exports2.escapeRegex = (str) => str.replace(REGEX_SPECIAL_CHARS_GLOBAL, "\\$1"); + exports2.toPosixSlashes = (str) => str.replace(REGEX_BACKSLASH, "/"); + exports2.removeBackslashes = (str) => { + return str.replace(REGEX_REMOVE_BACKSLASH, (match) => { + return match === "\\" ? "" : match; + }); + }; + exports2.supportsLookbehinds = () => { + const segs = process.version.slice(1).split(".").map(Number); + if (segs.length === 3 && segs[0] >= 9 || segs[0] === 8 && segs[1] >= 10) { + return true; + } + return false; + }; + exports2.isWindows = (options) => { + if (options && typeof options.windows === "boolean") { + return options.windows; + } + return win32 === true || path3.sep === "\\"; + }; + exports2.escapeLast = (input, char, lastIdx) => { + const idx = input.lastIndexOf(char, lastIdx); + if (idx === -1) return input; + if (input[idx - 1] === "\\") return exports2.escapeLast(input, char, idx - 1); + return `${input.slice(0, idx)}\\${input.slice(idx)}`; + }; + exports2.removePrefix = (input, state = {}) => { + let output = input; + if (output.startsWith("./")) { + output = output.slice(2); + state.prefix = "./"; + } + return output; + }; + exports2.wrapOutput = (input, state = {}, options = {}) => { + const prepend = options.contains ? "" : "^"; + const append = options.contains ? "" : "$"; + let output = `${prepend}(?:${input})${append}`; + if (state.negated === true) { + output = `(?:^(?!${output}).*$)`; + } + return output; + }; + } +}); + +// node_modules/readdirp/node_modules/picomatch/lib/scan.js +var require_scan = __commonJS({ + "node_modules/readdirp/node_modules/picomatch/lib/scan.js"(exports2, module2) { + "use strict"; + var utils = require_utils(); + var { + CHAR_ASTERISK, + /* * */ + CHAR_AT, + /* @ */ + CHAR_BACKWARD_SLASH, + /* \ */ + CHAR_COMMA, + /* , */ + CHAR_DOT, + /* . */ + CHAR_EXCLAMATION_MARK, + /* ! */ + CHAR_FORWARD_SLASH, + /* / */ + CHAR_LEFT_CURLY_BRACE, + /* { */ + CHAR_LEFT_PARENTHESES, + /* ( */ + CHAR_LEFT_SQUARE_BRACKET, + /* [ */ + CHAR_PLUS, + /* + */ + CHAR_QUESTION_MARK, + /* ? */ + CHAR_RIGHT_CURLY_BRACE, + /* } */ + CHAR_RIGHT_PARENTHESES, + /* ) */ + CHAR_RIGHT_SQUARE_BRACKET + /* ] */ + } = require_constants(); + var isPathSeparator = (code) => { + return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH; + }; + var depth = (token) => { + if (token.isPrefix !== true) { + token.depth = token.isGlobstar ? Infinity : 1; + } + }; + var scan = (input, options) => { + const opts = options || {}; + const length = input.length - 1; + const scanToEnd = opts.parts === true || opts.scanToEnd === true; + const slashes = []; + const tokens = []; + const parts = []; + let str = input; + let index = -1; + let start = 0; + let lastIndex = 0; + let isBrace = false; + let isBracket = false; + let isGlob = false; + let isExtglob = false; + let isGlobstar = false; + let braceEscaped = false; + let backslashes = false; + let negated = false; + let negatedExtglob = false; + let finished = false; + let braces = 0; + let prev; + let code; + let token = { value: "", depth: 0, isGlob: false }; + const eos = () => index >= length; + const peek = () => str.charCodeAt(index + 1); + const advance = () => { + prev = code; + return str.charCodeAt(++index); + }; + while (index < length) { + code = advance(); + let next; + if (code === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + code = advance(); + if (code === CHAR_LEFT_CURLY_BRACE) { + braceEscaped = true; + } + continue; + } + if (braceEscaped === true || code === CHAR_LEFT_CURLY_BRACE) { + braces++; + while (eos() !== true && (code = advance())) { + if (code === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + advance(); + continue; + } + if (code === CHAR_LEFT_CURLY_BRACE) { + braces++; + continue; + } + if (braceEscaped !== true && code === CHAR_DOT && (code = advance()) === CHAR_DOT) { + isBrace = token.isBrace = true; + isGlob = token.isGlob = true; + finished = true; + if (scanToEnd === true) { + continue; + } + break; + } + if (braceEscaped !== true && code === CHAR_COMMA) { + isBrace = token.isBrace = true; + isGlob = token.isGlob = true; + finished = true; + if (scanToEnd === true) { + continue; + } + break; + } + if (code === CHAR_RIGHT_CURLY_BRACE) { + braces--; + if (braces === 0) { + braceEscaped = false; + isBrace = token.isBrace = true; + finished = true; + break; + } + } + } + if (scanToEnd === true) { + continue; + } + break; + } + if (code === CHAR_FORWARD_SLASH) { + slashes.push(index); + tokens.push(token); + token = { value: "", depth: 0, isGlob: false }; + if (finished === true) continue; + if (prev === CHAR_DOT && index === start + 1) { + start += 2; + continue; + } + lastIndex = index + 1; + continue; + } + if (opts.noext !== true) { + const isExtglobChar = code === CHAR_PLUS || code === CHAR_AT || code === CHAR_ASTERISK || code === CHAR_QUESTION_MARK || code === CHAR_EXCLAMATION_MARK; + if (isExtglobChar === true && peek() === CHAR_LEFT_PARENTHESES) { + isGlob = token.isGlob = true; + isExtglob = token.isExtglob = true; + finished = true; + if (code === CHAR_EXCLAMATION_MARK && index === start) { + negatedExtglob = true; + } + if (scanToEnd === true) { + while (eos() !== true && (code = advance())) { + if (code === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + code = advance(); + continue; + } + if (code === CHAR_RIGHT_PARENTHESES) { + isGlob = token.isGlob = true; + finished = true; + break; + } + } + continue; + } + break; + } + } + if (code === CHAR_ASTERISK) { + if (prev === CHAR_ASTERISK) isGlobstar = token.isGlobstar = true; + isGlob = token.isGlob = true; + finished = true; + if (scanToEnd === true) { + continue; + } + break; + } + if (code === CHAR_QUESTION_MARK) { + isGlob = token.isGlob = true; + finished = true; + if (scanToEnd === true) { + continue; + } + break; + } + if (code === CHAR_LEFT_SQUARE_BRACKET) { + while (eos() !== true && (next = advance())) { + if (next === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + advance(); + continue; + } + if (next === CHAR_RIGHT_SQUARE_BRACKET) { + isBracket = token.isBracket = true; + isGlob = token.isGlob = true; + finished = true; + break; + } + } + if (scanToEnd === true) { + continue; + } + break; + } + if (opts.nonegate !== true && code === CHAR_EXCLAMATION_MARK && index === start) { + negated = token.negated = true; + start++; + continue; + } + if (opts.noparen !== true && code === CHAR_LEFT_PARENTHESES) { + isGlob = token.isGlob = true; + if (scanToEnd === true) { + while (eos() !== true && (code = advance())) { + if (code === CHAR_LEFT_PARENTHESES) { + backslashes = token.backslashes = true; + code = advance(); + continue; + } + if (code === CHAR_RIGHT_PARENTHESES) { + finished = true; + break; + } + } + continue; + } + break; + } + if (isGlob === true) { + finished = true; + if (scanToEnd === true) { + continue; + } + break; + } + } + if (opts.noext === true) { + isExtglob = false; + isGlob = false; + } + let base = str; + let prefix = ""; + let glob = ""; + if (start > 0) { + prefix = str.slice(0, start); + str = str.slice(start); + lastIndex -= start; + } + if (base && isGlob === true && lastIndex > 0) { + base = str.slice(0, lastIndex); + glob = str.slice(lastIndex); + } else if (isGlob === true) { + base = ""; + glob = str; + } else { + base = str; + } + if (base && base !== "" && base !== "/" && base !== str) { + if (isPathSeparator(base.charCodeAt(base.length - 1))) { + base = base.slice(0, -1); + } + } + if (opts.unescape === true) { + if (glob) glob = utils.removeBackslashes(glob); + if (base && backslashes === true) { + base = utils.removeBackslashes(base); + } + } + const state = { + prefix, + input, + start, + base, + glob, + isBrace, + isBracket, + isGlob, + isExtglob, + isGlobstar, + negated, + negatedExtglob + }; + if (opts.tokens === true) { + state.maxDepth = 0; + if (!isPathSeparator(code)) { + tokens.push(token); + } + state.tokens = tokens; + } + if (opts.parts === true || opts.tokens === true) { + let prevIndex; + for (let idx = 0; idx < slashes.length; idx++) { + const n = prevIndex ? prevIndex + 1 : start; + const i = slashes[idx]; + const value = input.slice(n, i); + if (opts.tokens) { + if (idx === 0 && start !== 0) { + tokens[idx].isPrefix = true; + tokens[idx].value = prefix; + } else { + tokens[idx].value = value; + } + depth(tokens[idx]); + state.maxDepth += tokens[idx].depth; + } + if (idx !== 0 || value !== "") { + parts.push(value); + } + prevIndex = i; + } + if (prevIndex && prevIndex + 1 < input.length) { + const value = input.slice(prevIndex + 1); + parts.push(value); + if (opts.tokens) { + tokens[tokens.length - 1].value = value; + depth(tokens[tokens.length - 1]); + state.maxDepth += tokens[tokens.length - 1].depth; + } + } + state.slashes = slashes; + state.parts = parts; + } + return state; + }; + module2.exports = scan; + } +}); + +// node_modules/readdirp/node_modules/picomatch/lib/parse.js +var require_parse = __commonJS({ + "node_modules/readdirp/node_modules/picomatch/lib/parse.js"(exports2, module2) { + "use strict"; + var constants = require_constants(); + var utils = require_utils(); + var { + MAX_LENGTH, + POSIX_REGEX_SOURCE, + REGEX_NON_SPECIAL_CHARS, + REGEX_SPECIAL_CHARS_BACKREF, + REPLACEMENTS + } = constants; + var expandRange = (args, options) => { + if (typeof options.expandRange === "function") { + return options.expandRange(...args, options); + } + args.sort(); + const value = `[${args.join("-")}]`; + try { + new RegExp(value); + } catch (ex) { + return args.map((v) => utils.escapeRegex(v)).join(".."); + } + return value; + }; + var syntaxError = (type, char) => { + return `Missing ${type}: "${char}" - use "\\\\${char}" to match literal characters`; + }; + var splitTopLevel = (input) => { + const parts = []; + let bracket = 0; + let paren = 0; + let quote = 0; + let value = ""; + let escaped = false; + for (const ch of input) { + if (escaped === true) { + value += ch; + escaped = false; + continue; + } + if (ch === "\\") { + value += ch; + escaped = true; + continue; + } + if (ch === '"') { + quote = quote === 1 ? 0 : 1; + value += ch; + continue; + } + if (quote === 0) { + if (ch === "[") { + bracket++; + } else if (ch === "]" && bracket > 0) { + bracket--; + } else if (bracket === 0) { + if (ch === "(") { + paren++; + } else if (ch === ")" && paren > 0) { + paren--; + } else if (ch === "|" && paren === 0) { + parts.push(value); + value = ""; + continue; + } + } + } + value += ch; + } + parts.push(value); + return parts; + }; + var isPlainBranch = (branch) => { + let escaped = false; + for (const ch of branch) { + if (escaped === true) { + escaped = false; + continue; + } + if (ch === "\\") { + escaped = true; + continue; + } + if (/[?*+@!()[\]{}]/.test(ch)) { + return false; + } + } + return true; + }; + var normalizeSimpleBranch = (branch) => { + let value = branch.trim(); + let changed = true; + while (changed === true) { + changed = false; + if (/^@\([^\\()[\]{}|]+\)$/.test(value)) { + value = value.slice(2, -1); + changed = true; + } + } + if (!isPlainBranch(value)) { + return; + } + return value.replace(/\\(.)/g, "$1"); + }; + var hasRepeatedCharPrefixOverlap = (branches) => { + const values = branches.map(normalizeSimpleBranch).filter(Boolean); + for (let i = 0; i < values.length; i++) { + for (let j = i + 1; j < values.length; j++) { + const a = values[i]; + const b = values[j]; + const char = a[0]; + if (!char || a !== char.repeat(a.length) || b !== char.repeat(b.length)) { + continue; + } + if (a === b || a.startsWith(b) || b.startsWith(a)) { + return true; + } + } + } + return false; + }; + var parseRepeatedExtglob = (pattern, requireEnd = true) => { + if (pattern[0] !== "+" && pattern[0] !== "*" || pattern[1] !== "(") { + return; + } + let bracket = 0; + let paren = 0; + let quote = 0; + let escaped = false; + for (let i = 1; i < pattern.length; i++) { + const ch = pattern[i]; + if (escaped === true) { + escaped = false; + continue; + } + if (ch === "\\") { + escaped = true; + continue; + } + if (ch === '"') { + quote = quote === 1 ? 0 : 1; + continue; + } + if (quote === 1) { + continue; + } + if (ch === "[") { + bracket++; + continue; + } + if (ch === "]" && bracket > 0) { + bracket--; + continue; + } + if (bracket > 0) { + continue; + } + if (ch === "(") { + paren++; + continue; + } + if (ch === ")") { + paren--; + if (paren === 0) { + if (requireEnd === true && i !== pattern.length - 1) { + return; + } + return { + type: pattern[0], + body: pattern.slice(2, i), + end: i + }; + } + } + } + }; + var getStarExtglobSequenceOutput = (pattern) => { + let index = 0; + const chars = []; + while (index < pattern.length) { + const match = parseRepeatedExtglob(pattern.slice(index), false); + if (!match || match.type !== "*") { + return; + } + const branches = splitTopLevel(match.body).map((branch2) => branch2.trim()); + if (branches.length !== 1) { + return; + } + const branch = normalizeSimpleBranch(branches[0]); + if (!branch || branch.length !== 1) { + return; + } + chars.push(branch); + index += match.end + 1; + } + if (chars.length < 1) { + return; + } + const source = chars.length === 1 ? utils.escapeRegex(chars[0]) : `[${chars.map((ch) => utils.escapeRegex(ch)).join("")}]`; + return `${source}*`; + }; + var repeatedExtglobRecursion = (pattern) => { + let depth = 0; + let value = pattern.trim(); + let match = parseRepeatedExtglob(value); + while (match) { + depth++; + value = match.body.trim(); + match = parseRepeatedExtglob(value); + } + return depth; + }; + var analyzeRepeatedExtglob = (body, options) => { + if (options.maxExtglobRecursion === false) { + return { risky: false }; + } + const max = typeof options.maxExtglobRecursion === "number" ? options.maxExtglobRecursion : constants.DEFAULT_MAX_EXTGLOB_RECURSION; + const branches = splitTopLevel(body).map((branch) => branch.trim()); + if (branches.length > 1) { + if (branches.some((branch) => branch === "") || branches.some((branch) => /^[*?]+$/.test(branch)) || hasRepeatedCharPrefixOverlap(branches)) { + return { risky: true }; + } + } + for (const branch of branches) { + const safeOutput = getStarExtglobSequenceOutput(branch); + if (safeOutput) { + return { risky: true, safeOutput }; + } + if (repeatedExtglobRecursion(branch) > max) { + return { risky: true }; + } + } + return { risky: false }; + }; + var parse = (input, options) => { + if (typeof input !== "string") { + throw new TypeError("Expected a string"); + } + input = REPLACEMENTS[input] || input; + const opts = { ...options }; + const max = typeof opts.maxLength === "number" ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; + let len = input.length; + if (len > max) { + throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`); + } + const bos = { type: "bos", value: "", output: opts.prepend || "" }; + const tokens = [bos]; + const capture = opts.capture ? "" : "?:"; + const win32 = utils.isWindows(options); + const PLATFORM_CHARS = constants.globChars(win32); + const EXTGLOB_CHARS = constants.extglobChars(PLATFORM_CHARS); + const { + DOT_LITERAL, + PLUS_LITERAL, + SLASH_LITERAL, + ONE_CHAR, + DOTS_SLASH, + NO_DOT, + NO_DOT_SLASH, + NO_DOTS_SLASH, + QMARK, + QMARK_NO_DOT, + STAR, + START_ANCHOR + } = PLATFORM_CHARS; + const globstar = (opts2) => { + return `(${capture}(?:(?!${START_ANCHOR}${opts2.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`; + }; + const nodot = opts.dot ? "" : NO_DOT; + const qmarkNoDot = opts.dot ? QMARK : QMARK_NO_DOT; + let star = opts.bash === true ? globstar(opts) : STAR; + if (opts.capture) { + star = `(${star})`; + } + if (typeof opts.noext === "boolean") { + opts.noextglob = opts.noext; + } + const state = { + input, + index: -1, + start: 0, + dot: opts.dot === true, + consumed: "", + output: "", + prefix: "", + backtrack: false, + negated: false, + brackets: 0, + braces: 0, + parens: 0, + quotes: 0, + globstar: false, + tokens + }; + input = utils.removePrefix(input, state); + len = input.length; + const extglobs = []; + const braces = []; + const stack = []; + let prev = bos; + let value; + const eos = () => state.index === len - 1; + const peek = state.peek = (n = 1) => input[state.index + n]; + const advance = state.advance = () => input[++state.index] || ""; + const remaining = () => input.slice(state.index + 1); + const consume = (value2 = "", num = 0) => { + state.consumed += value2; + state.index += num; + }; + const append = (token) => { + state.output += token.output != null ? token.output : token.value; + consume(token.value); + }; + const negate = () => { + let count = 1; + while (peek() === "!" && (peek(2) !== "(" || peek(3) === "?")) { + advance(); + state.start++; + count++; + } + if (count % 2 === 0) { + return false; + } + state.negated = true; + state.start++; + return true; + }; + const increment = (type) => { + state[type]++; + stack.push(type); + }; + const decrement = (type) => { + state[type]--; + stack.pop(); + }; + const push = (tok) => { + if (prev.type === "globstar") { + const isBrace = state.braces > 0 && (tok.type === "comma" || tok.type === "brace"); + const isExtglob = tok.extglob === true || extglobs.length && (tok.type === "pipe" || tok.type === "paren"); + if (tok.type !== "slash" && tok.type !== "paren" && !isBrace && !isExtglob) { + state.output = state.output.slice(0, -prev.output.length); + prev.type = "star"; + prev.value = "*"; + prev.output = star; + state.output += prev.output; + } + } + if (extglobs.length && tok.type !== "paren") { + extglobs[extglobs.length - 1].inner += tok.value; + } + if (tok.value || tok.output) append(tok); + if (prev && prev.type === "text" && tok.type === "text") { + prev.value += tok.value; + prev.output = (prev.output || "") + tok.value; + return; + } + tok.prev = prev; + tokens.push(tok); + prev = tok; + }; + const extglobOpen = (type, value2) => { + const token = { ...EXTGLOB_CHARS[value2], conditions: 1, inner: "" }; + token.prev = prev; + token.parens = state.parens; + token.output = state.output; + token.startIndex = state.index; + token.tokensIndex = tokens.length; + const output = (opts.capture ? "(" : "") + token.open; + increment("parens"); + push({ type, value: value2, output: state.output ? "" : ONE_CHAR }); + push({ type: "paren", extglob: true, value: advance(), output }); + extglobs.push(token); + }; + const extglobClose = (token) => { + const literal = input.slice(token.startIndex, state.index + 1); + const body = input.slice(token.startIndex + 2, state.index); + const analysis = analyzeRepeatedExtglob(body, opts); + if ((token.type === "plus" || token.type === "star") && analysis.risky) { + const safeOutput = analysis.safeOutput ? (token.output ? "" : ONE_CHAR) + (opts.capture ? `(${analysis.safeOutput})` : analysis.safeOutput) : void 0; + const open = tokens[token.tokensIndex]; + open.type = "text"; + open.value = literal; + open.output = safeOutput || utils.escapeRegex(literal); + for (let i = token.tokensIndex + 1; i < tokens.length; i++) { + tokens[i].value = ""; + tokens[i].output = ""; + delete tokens[i].suffix; + } + state.output = token.output + open.output; + state.backtrack = true; + push({ type: "paren", extglob: true, value, output: "" }); + decrement("parens"); + return; + } + let output = token.close + (opts.capture ? ")" : ""); + let rest; + if (token.type === "negate") { + let extglobStar = star; + if (token.inner && token.inner.length > 1 && token.inner.includes("/")) { + extglobStar = globstar(opts); + } + if (extglobStar !== star || eos() || /^\)+$/.test(remaining())) { + output = token.close = `)$))${extglobStar}`; + } + if (token.inner.includes("*") && (rest = remaining()) && /^\.[^\\/.]+$/.test(rest)) { + const expression = parse(rest, { ...options, fastpaths: false }).output; + output = token.close = `)${expression})${extglobStar})`; + } + if (token.prev.type === "bos") { + state.negatedExtglob = true; + } + } + push({ type: "paren", extglob: true, value, output }); + decrement("parens"); + }; + if (opts.fastpaths !== false && !/(^[*!]|[/()[\]{}"])/.test(input)) { + let backslashes = false; + let output = input.replace(REGEX_SPECIAL_CHARS_BACKREF, (m, esc, chars, first, rest, index) => { + if (first === "\\") { + backslashes = true; + return m; + } + if (first === "?") { + if (esc) { + return esc + first + (rest ? QMARK.repeat(rest.length) : ""); + } + if (index === 0) { + return qmarkNoDot + (rest ? QMARK.repeat(rest.length) : ""); + } + return QMARK.repeat(chars.length); + } + if (first === ".") { + return DOT_LITERAL.repeat(chars.length); + } + if (first === "*") { + if (esc) { + return esc + first + (rest ? star : ""); + } + return star; + } + return esc ? m : `\\${m}`; + }); + if (backslashes === true) { + if (opts.unescape === true) { + output = output.replace(/\\/g, ""); + } else { + output = output.replace(/\\+/g, (m) => { + return m.length % 2 === 0 ? "\\\\" : m ? "\\" : ""; + }); + } + } + if (output === input && opts.contains === true) { + state.output = input; + return state; + } + state.output = utils.wrapOutput(output, state, options); + return state; + } + while (!eos()) { + value = advance(); + if (value === "\0") { + continue; + } + if (value === "\\") { + const next = peek(); + if (next === "/" && opts.bash !== true) { + continue; + } + if (next === "." || next === ";") { + continue; + } + if (!next) { + value += "\\"; + push({ type: "text", value }); + continue; + } + const match = /^\\+/.exec(remaining()); + let slashes = 0; + if (match && match[0].length > 2) { + slashes = match[0].length; + state.index += slashes; + if (slashes % 2 !== 0) { + value += "\\"; + } + } + if (opts.unescape === true) { + value = advance(); + } else { + value += advance(); + } + if (state.brackets === 0) { + push({ type: "text", value }); + continue; + } + } + if (state.brackets > 0 && (value !== "]" || prev.value === "[" || prev.value === "[^")) { + if (opts.posix !== false && value === ":") { + const inner = prev.value.slice(1); + if (inner.includes("[")) { + prev.posix = true; + if (inner.includes(":")) { + const idx = prev.value.lastIndexOf("["); + const pre = prev.value.slice(0, idx); + const rest2 = prev.value.slice(idx + 2); + const posix = POSIX_REGEX_SOURCE[rest2]; + if (posix) { + prev.value = pre + posix; + state.backtrack = true; + advance(); + if (!bos.output && tokens.indexOf(prev) === 1) { + bos.output = ONE_CHAR; + } + continue; + } + } + } + } + if (value === "[" && peek() !== ":" || value === "-" && peek() === "]") { + value = `\\${value}`; + } + if (value === "]" && (prev.value === "[" || prev.value === "[^")) { + value = `\\${value}`; + } + if (opts.posix === true && value === "!" && prev.value === "[") { + value = "^"; + } + prev.value += value; + append({ value }); + continue; + } + if (state.quotes === 1 && value !== '"') { + value = utils.escapeRegex(value); + prev.value += value; + append({ value }); + continue; + } + if (value === '"') { + state.quotes = state.quotes === 1 ? 0 : 1; + if (opts.keepQuotes === true) { + push({ type: "text", value }); + } + continue; + } + if (value === "(") { + increment("parens"); + push({ type: "paren", value }); + continue; + } + if (value === ")") { + if (state.parens === 0 && opts.strictBrackets === true) { + throw new SyntaxError(syntaxError("opening", "(")); + } + const extglob = extglobs[extglobs.length - 1]; + if (extglob && state.parens === extglob.parens + 1) { + extglobClose(extglobs.pop()); + continue; + } + push({ type: "paren", value, output: state.parens ? ")" : "\\)" }); + decrement("parens"); + continue; + } + if (value === "[") { + if (opts.nobracket === true || !remaining().includes("]")) { + if (opts.nobracket !== true && opts.strictBrackets === true) { + throw new SyntaxError(syntaxError("closing", "]")); + } + value = `\\${value}`; + } else { + increment("brackets"); + } + push({ type: "bracket", value }); + continue; + } + if (value === "]") { + if (opts.nobracket === true || prev && prev.type === "bracket" && prev.value.length === 1) { + push({ type: "text", value, output: `\\${value}` }); + continue; + } + if (state.brackets === 0) { + if (opts.strictBrackets === true) { + throw new SyntaxError(syntaxError("opening", "[")); + } + push({ type: "text", value, output: `\\${value}` }); + continue; + } + decrement("brackets"); + const prevValue = prev.value.slice(1); + if (prev.posix !== true && prevValue[0] === "^" && !prevValue.includes("/")) { + value = `/${value}`; + } + prev.value += value; + append({ value }); + if (opts.literalBrackets === false || utils.hasRegexChars(prevValue)) { + continue; + } + const escaped = utils.escapeRegex(prev.value); + state.output = state.output.slice(0, -prev.value.length); + if (opts.literalBrackets === true) { + state.output += escaped; + prev.value = escaped; + continue; + } + prev.value = `(${capture}${escaped}|${prev.value})`; + state.output += prev.value; + continue; + } + if (value === "{" && opts.nobrace !== true) { + increment("braces"); + const open = { + type: "brace", + value, + output: "(", + outputIndex: state.output.length, + tokensIndex: state.tokens.length + }; + braces.push(open); + push(open); + continue; + } + if (value === "}") { + const brace = braces[braces.length - 1]; + if (opts.nobrace === true || !brace) { + push({ type: "text", value, output: value }); + continue; + } + let output = ")"; + if (brace.dots === true) { + const arr = tokens.slice(); + const range = []; + for (let i = arr.length - 1; i >= 0; i--) { + tokens.pop(); + if (arr[i].type === "brace") { + break; + } + if (arr[i].type !== "dots") { + range.unshift(arr[i].value); + } + } + output = expandRange(range, opts); + state.backtrack = true; + } + if (brace.comma !== true && brace.dots !== true) { + const out = state.output.slice(0, brace.outputIndex); + const toks = state.tokens.slice(brace.tokensIndex); + brace.value = brace.output = "\\{"; + value = output = "\\}"; + state.output = out; + for (const t of toks) { + state.output += t.output || t.value; + } + } + push({ type: "brace", value, output }); + decrement("braces"); + braces.pop(); + continue; + } + if (value === "|") { + if (extglobs.length > 0) { + extglobs[extglobs.length - 1].conditions++; + } + push({ type: "text", value }); + continue; + } + if (value === ",") { + let output = value; + const brace = braces[braces.length - 1]; + if (brace && stack[stack.length - 1] === "braces") { + brace.comma = true; + output = "|"; + } + push({ type: "comma", value, output }); + continue; + } + if (value === "/") { + if (prev.type === "dot" && state.index === state.start + 1) { + state.start = state.index + 1; + state.consumed = ""; + state.output = ""; + tokens.pop(); + prev = bos; + continue; + } + push({ type: "slash", value, output: SLASH_LITERAL }); + continue; + } + if (value === ".") { + if (state.braces > 0 && prev.type === "dot") { + if (prev.value === ".") prev.output = DOT_LITERAL; + const brace = braces[braces.length - 1]; + prev.type = "dots"; + prev.output += value; + prev.value += value; + brace.dots = true; + continue; + } + if (state.braces + state.parens === 0 && prev.type !== "bos" && prev.type !== "slash") { + push({ type: "text", value, output: DOT_LITERAL }); + continue; + } + push({ type: "dot", value, output: DOT_LITERAL }); + continue; + } + if (value === "?") { + const isGroup = prev && prev.value === "("; + if (!isGroup && opts.noextglob !== true && peek() === "(" && peek(2) !== "?") { + extglobOpen("qmark", value); + continue; + } + if (prev && prev.type === "paren") { + const next = peek(); + let output = value; + if (next === "<" && !utils.supportsLookbehinds()) { + throw new Error("Node.js v10 or higher is required for regex lookbehinds"); + } + if (prev.value === "(" && !/[!=<:]/.test(next) || next === "<" && !/<([!=]|\w+>)/.test(remaining())) { + output = `\\${value}`; + } + push({ type: "text", value, output }); + continue; + } + if (opts.dot !== true && (prev.type === "slash" || prev.type === "bos")) { + push({ type: "qmark", value, output: QMARK_NO_DOT }); + continue; + } + push({ type: "qmark", value, output: QMARK }); + continue; + } + if (value === "!") { + if (opts.noextglob !== true && peek() === "(") { + if (peek(2) !== "?" || !/[!=<:]/.test(peek(3))) { + extglobOpen("negate", value); + continue; + } + } + if (opts.nonegate !== true && state.index === 0) { + negate(); + continue; + } + } + if (value === "+") { + if (opts.noextglob !== true && peek() === "(" && peek(2) !== "?") { + extglobOpen("plus", value); + continue; + } + if (prev && prev.value === "(" || opts.regex === false) { + push({ type: "plus", value, output: PLUS_LITERAL }); + continue; + } + if (prev && (prev.type === "bracket" || prev.type === "paren" || prev.type === "brace") || state.parens > 0) { + push({ type: "plus", value }); + continue; + } + push({ type: "plus", value: PLUS_LITERAL }); + continue; + } + if (value === "@") { + if (opts.noextglob !== true && peek() === "(" && peek(2) !== "?") { + push({ type: "at", extglob: true, value, output: "" }); + continue; + } + push({ type: "text", value }); + continue; + } + if (value !== "*") { + if (value === "$" || value === "^") { + value = `\\${value}`; + } + const match = REGEX_NON_SPECIAL_CHARS.exec(remaining()); + if (match) { + value += match[0]; + state.index += match[0].length; + } + push({ type: "text", value }); + continue; + } + if (prev && (prev.type === "globstar" || prev.star === true)) { + prev.type = "star"; + prev.star = true; + prev.value += value; + prev.output = star; + state.backtrack = true; + state.globstar = true; + consume(value); + continue; + } + let rest = remaining(); + if (opts.noextglob !== true && /^\([^?]/.test(rest)) { + extglobOpen("star", value); + continue; + } + if (prev.type === "star") { + if (opts.noglobstar === true) { + consume(value); + continue; + } + const prior = prev.prev; + const before = prior.prev; + const isStart = prior.type === "slash" || prior.type === "bos"; + const afterStar = before && (before.type === "star" || before.type === "globstar"); + if (opts.bash === true && (!isStart || rest[0] && rest[0] !== "/")) { + push({ type: "star", value, output: "" }); + continue; + } + const isBrace = state.braces > 0 && (prior.type === "comma" || prior.type === "brace"); + const isExtglob = extglobs.length && (prior.type === "pipe" || prior.type === "paren"); + if (!isStart && prior.type !== "paren" && !isBrace && !isExtglob) { + push({ type: "star", value, output: "" }); + continue; + } + while (rest.slice(0, 3) === "/**") { + const after = input[state.index + 4]; + if (after && after !== "/") { + break; + } + rest = rest.slice(3); + consume("/**", 3); + } + if (prior.type === "bos" && eos()) { + prev.type = "globstar"; + prev.value += value; + prev.output = globstar(opts); + state.output = prev.output; + state.globstar = true; + consume(value); + continue; + } + if (prior.type === "slash" && prior.prev.type !== "bos" && !afterStar && eos()) { + state.output = state.output.slice(0, -(prior.output + prev.output).length); + prior.output = `(?:${prior.output}`; + prev.type = "globstar"; + prev.output = globstar(opts) + (opts.strictSlashes ? ")" : "|$)"); + prev.value += value; + state.globstar = true; + state.output += prior.output + prev.output; + consume(value); + continue; + } + if (prior.type === "slash" && prior.prev.type !== "bos" && rest[0] === "/") { + const end = rest[1] !== void 0 ? "|$" : ""; + state.output = state.output.slice(0, -(prior.output + prev.output).length); + prior.output = `(?:${prior.output}`; + prev.type = "globstar"; + prev.output = `${globstar(opts)}${SLASH_LITERAL}|${SLASH_LITERAL}${end})`; + prev.value += value; + state.output += prior.output + prev.output; + state.globstar = true; + consume(value + advance()); + push({ type: "slash", value: "/", output: "" }); + continue; + } + if (prior.type === "bos" && rest[0] === "/") { + prev.type = "globstar"; + prev.value += value; + prev.output = `(?:^|${SLASH_LITERAL}|${globstar(opts)}${SLASH_LITERAL})`; + state.output = prev.output; + state.globstar = true; + consume(value + advance()); + push({ type: "slash", value: "/", output: "" }); + continue; + } + state.output = state.output.slice(0, -prev.output.length); + prev.type = "globstar"; + prev.output = globstar(opts); + prev.value += value; + state.output += prev.output; + state.globstar = true; + consume(value); + continue; + } + const token = { type: "star", value, output: star }; + if (opts.bash === true) { + token.output = ".*?"; + if (prev.type === "bos" || prev.type === "slash") { + token.output = nodot + token.output; + } + push(token); + continue; + } + if (prev && (prev.type === "bracket" || prev.type === "paren") && opts.regex === true) { + token.output = value; + push(token); + continue; + } + if (state.index === state.start || prev.type === "slash" || prev.type === "dot") { + if (prev.type === "dot") { + state.output += NO_DOT_SLASH; + prev.output += NO_DOT_SLASH; + } else if (opts.dot === true) { + state.output += NO_DOTS_SLASH; + prev.output += NO_DOTS_SLASH; + } else { + state.output += nodot; + prev.output += nodot; + } + if (peek() !== "*") { + state.output += ONE_CHAR; + prev.output += ONE_CHAR; + } + } + push(token); + } + while (state.brackets > 0) { + if (opts.strictBrackets === true) throw new SyntaxError(syntaxError("closing", "]")); + state.output = utils.escapeLast(state.output, "["); + decrement("brackets"); + } + while (state.parens > 0) { + if (opts.strictBrackets === true) throw new SyntaxError(syntaxError("closing", ")")); + state.output = utils.escapeLast(state.output, "("); + decrement("parens"); + } + while (state.braces > 0) { + if (opts.strictBrackets === true) throw new SyntaxError(syntaxError("closing", "}")); + state.output = utils.escapeLast(state.output, "{"); + decrement("braces"); + } + if (opts.strictSlashes !== true && (prev.type === "star" || prev.type === "bracket")) { + push({ type: "maybe_slash", value: "", output: `${SLASH_LITERAL}?` }); + } + if (state.backtrack === true) { + state.output = ""; + for (const token of state.tokens) { + state.output += token.output != null ? token.output : token.value; + if (token.suffix) { + state.output += token.suffix; + } + } + } + return state; + }; + parse.fastpaths = (input, options) => { + const opts = { ...options }; + const max = typeof opts.maxLength === "number" ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; + const len = input.length; + if (len > max) { + throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`); + } + input = REPLACEMENTS[input] || input; + const win32 = utils.isWindows(options); + const { + DOT_LITERAL, + SLASH_LITERAL, + ONE_CHAR, + DOTS_SLASH, + NO_DOT, + NO_DOTS, + NO_DOTS_SLASH, + STAR, + START_ANCHOR + } = constants.globChars(win32); + const nodot = opts.dot ? NO_DOTS : NO_DOT; + const slashDot = opts.dot ? NO_DOTS_SLASH : NO_DOT; + const capture = opts.capture ? "" : "?:"; + const state = { negated: false, prefix: "" }; + let star = opts.bash === true ? ".*?" : STAR; + if (opts.capture) { + star = `(${star})`; + } + const globstar = (opts2) => { + if (opts2.noglobstar === true) return star; + return `(${capture}(?:(?!${START_ANCHOR}${opts2.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`; + }; + const create = (str) => { + switch (str) { + case "*": + return `${nodot}${ONE_CHAR}${star}`; + case ".*": + return `${DOT_LITERAL}${ONE_CHAR}${star}`; + case "*.*": + return `${nodot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`; + case "*/*": + return `${nodot}${star}${SLASH_LITERAL}${ONE_CHAR}${slashDot}${star}`; + case "**": + return nodot + globstar(opts); + case "**/*": + return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${ONE_CHAR}${star}`; + case "**/*.*": + return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`; + case "**/.*": + return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${DOT_LITERAL}${ONE_CHAR}${star}`; + default: { + const match = /^(.*?)\.(\w+)$/.exec(str); + if (!match) return; + const source2 = create(match[1]); + if (!source2) return; + return source2 + DOT_LITERAL + match[2]; + } + } + }; + const output = utils.removePrefix(input, state); + let source = create(output); + if (source && opts.strictSlashes !== true) { + source += `${SLASH_LITERAL}?`; + } + return source; + }; + module2.exports = parse; + } +}); + +// node_modules/readdirp/node_modules/picomatch/lib/picomatch.js +var require_picomatch = __commonJS({ + "node_modules/readdirp/node_modules/picomatch/lib/picomatch.js"(exports2, module2) { + "use strict"; + var path3 = require("path"); + var scan = require_scan(); + var parse = require_parse(); + var utils = require_utils(); + var constants = require_constants(); + var isObject = (val) => val && typeof val === "object" && !Array.isArray(val); + var picomatch = (glob, options, returnState = false) => { + if (Array.isArray(glob)) { + const fns = glob.map((input) => picomatch(input, options, returnState)); + const arrayMatcher = (str) => { + for (const isMatch of fns) { + const state2 = isMatch(str); + if (state2) return state2; + } + return false; + }; + return arrayMatcher; + } + const isState = isObject(glob) && glob.tokens && glob.input; + if (glob === "" || typeof glob !== "string" && !isState) { + throw new TypeError("Expected pattern to be a non-empty string"); + } + const opts = options || {}; + const posix = utils.isWindows(options); + const regex = isState ? picomatch.compileRe(glob, options) : picomatch.makeRe(glob, options, false, true); + const state = regex.state; + delete regex.state; + let isIgnored = () => false; + if (opts.ignore) { + const ignoreOpts = { ...options, ignore: null, onMatch: null, onResult: null }; + isIgnored = picomatch(opts.ignore, ignoreOpts, returnState); + } + const matcher = (input, returnObject = false) => { + const { isMatch, match, output } = picomatch.test(input, regex, options, { glob, posix }); + const result = { glob, state, regex, posix, input, output, match, isMatch }; + if (typeof opts.onResult === "function") { + opts.onResult(result); + } + if (isMatch === false) { + result.isMatch = false; + return returnObject ? result : false; + } + if (isIgnored(input)) { + if (typeof opts.onIgnore === "function") { + opts.onIgnore(result); + } + result.isMatch = false; + return returnObject ? result : false; + } + if (typeof opts.onMatch === "function") { + opts.onMatch(result); + } + return returnObject ? result : true; + }; + if (returnState) { + matcher.state = state; + } + return matcher; + }; + picomatch.test = (input, regex, options, { glob, posix } = {}) => { + if (typeof input !== "string") { + throw new TypeError("Expected input to be a string"); + } + if (input === "") { + return { isMatch: false, output: "" }; + } + const opts = options || {}; + const format = opts.format || (posix ? utils.toPosixSlashes : null); + let match = input === glob; + let output = match && format ? format(input) : input; + if (match === false) { + output = format ? format(input) : input; + match = output === glob; + } + if (match === false || opts.capture === true) { + if (opts.matchBase === true || opts.basename === true) { + match = picomatch.matchBase(input, regex, options, posix); + } else { + match = regex.exec(output); + } + } + return { isMatch: Boolean(match), match, output }; + }; + picomatch.matchBase = (input, glob, options, posix = utils.isWindows(options)) => { + const regex = glob instanceof RegExp ? glob : picomatch.makeRe(glob, options); + return regex.test(path3.basename(input)); + }; + picomatch.isMatch = (str, patterns, options) => picomatch(patterns, options)(str); + picomatch.parse = (pattern, options) => { + if (Array.isArray(pattern)) return pattern.map((p) => picomatch.parse(p, options)); + return parse(pattern, { ...options, fastpaths: false }); + }; + picomatch.scan = (input, options) => scan(input, options); + picomatch.compileRe = (state, options, returnOutput = false, returnState = false) => { + if (returnOutput === true) { + return state.output; + } + const opts = options || {}; + const prepend = opts.contains ? "" : "^"; + const append = opts.contains ? "" : "$"; + let source = `${prepend}(?:${state.output})${append}`; + if (state && state.negated === true) { + source = `^(?!${source}).*$`; + } + const regex = picomatch.toRegex(source, options); + if (returnState === true) { + regex.state = state; + } + return regex; + }; + picomatch.makeRe = (input, options = {}, returnOutput = false, returnState = false) => { + if (!input || typeof input !== "string") { + throw new TypeError("Expected a non-empty string"); + } + let parsed = { negated: false, fastpaths: true }; + if (options.fastpaths !== false && (input[0] === "." || input[0] === "*")) { + parsed.output = parse.fastpaths(input, options); + } + if (!parsed.output) { + parsed = parse(input, options); + } + return picomatch.compileRe(parsed, options, returnOutput, returnState); + }; + picomatch.toRegex = (source, options) => { + try { + const opts = options || {}; + return new RegExp(source, opts.flags || (opts.nocase ? "i" : "")); + } catch (err) { + if (options && options.debug === true) throw err; + return /$^/; + } + }; + picomatch.constants = constants; + module2.exports = picomatch; + } +}); + +// node_modules/readdirp/node_modules/picomatch/index.js +var require_picomatch2 = __commonJS({ + "node_modules/readdirp/node_modules/picomatch/index.js"(exports2, module2) { + "use strict"; + module2.exports = require_picomatch(); + } +}); + +// node_modules/readdirp/index.js +var require_readdirp = __commonJS({ + "node_modules/readdirp/index.js"(exports2, module2) { + "use strict"; + var fs2 = require("fs"); + var { Readable } = require("stream"); + var sysPath = require("path"); + var { promisify } = require("util"); + var picomatch = require_picomatch2(); + var readdir = promisify(fs2.readdir); + var stat = promisify(fs2.stat); + var lstat = promisify(fs2.lstat); + var realpath = promisify(fs2.realpath); + var BANG = "!"; + var RECURSIVE_ERROR_CODE = "READDIRP_RECURSIVE_ERROR"; + var NORMAL_FLOW_ERRORS = /* @__PURE__ */ new Set(["ENOENT", "EPERM", "EACCES", "ELOOP", RECURSIVE_ERROR_CODE]); + var FILE_TYPE = "files"; + var DIR_TYPE = "directories"; + var FILE_DIR_TYPE = "files_directories"; + var EVERYTHING_TYPE = "all"; + var ALL_TYPES = [FILE_TYPE, DIR_TYPE, FILE_DIR_TYPE, EVERYTHING_TYPE]; + var isNormalFlowError = (error) => NORMAL_FLOW_ERRORS.has(error.code); + var [maj, min] = process.versions.node.split(".").slice(0, 2).map((n) => Number.parseInt(n, 10)); + var wantBigintFsStats = process.platform === "win32" && (maj > 10 || maj === 10 && min >= 5); + var normalizeFilter = (filter) => { + if (filter === void 0) return; + if (typeof filter === "function") return filter; + if (typeof filter === "string") { + const glob = picomatch(filter.trim()); + return (entry) => glob(entry.basename); + } + if (Array.isArray(filter)) { + const positive = []; + const negative = []; + for (const item of filter) { + const trimmed = item.trim(); + if (trimmed.charAt(0) === BANG) { + negative.push(picomatch(trimmed.slice(1))); + } else { + positive.push(picomatch(trimmed)); + } + } + if (negative.length > 0) { + if (positive.length > 0) { + return (entry) => positive.some((f) => f(entry.basename)) && !negative.some((f) => f(entry.basename)); + } + return (entry) => !negative.some((f) => f(entry.basename)); + } + return (entry) => positive.some((f) => f(entry.basename)); + } + }; + var ReaddirpStream = class _ReaddirpStream extends Readable { + static get defaultOptions() { + return { + root: ".", + /* eslint-disable no-unused-vars */ + fileFilter: (path3) => true, + directoryFilter: (path3) => true, + /* eslint-enable no-unused-vars */ + type: FILE_TYPE, + lstat: false, + depth: 2147483648, + alwaysStat: false + }; + } + constructor(options = {}) { + super({ + objectMode: true, + autoDestroy: true, + highWaterMark: options.highWaterMark || 4096 + }); + const opts = { ..._ReaddirpStream.defaultOptions, ...options }; + const { root, type } = opts; + this._fileFilter = normalizeFilter(opts.fileFilter); + this._directoryFilter = normalizeFilter(opts.directoryFilter); + const statMethod = opts.lstat ? lstat : stat; + if (wantBigintFsStats) { + this._stat = (path3) => statMethod(path3, { bigint: true }); + } else { + this._stat = statMethod; + } + this._maxDepth = opts.depth; + this._wantsDir = [DIR_TYPE, FILE_DIR_TYPE, EVERYTHING_TYPE].includes(type); + this._wantsFile = [FILE_TYPE, FILE_DIR_TYPE, EVERYTHING_TYPE].includes(type); + this._wantsEverything = type === EVERYTHING_TYPE; + this._root = sysPath.resolve(root); + this._isDirent = "Dirent" in fs2 && !opts.alwaysStat; + this._statsProp = this._isDirent ? "dirent" : "stats"; + this._rdOptions = { encoding: "utf8", withFileTypes: this._isDirent }; + this.parents = [this._exploreDir(root, 1)]; + this.reading = false; + this.parent = void 0; + } + async _read(batch) { + if (this.reading) return; + this.reading = true; + try { + while (!this.destroyed && batch > 0) { + const { path: path3, depth, files = [] } = this.parent || {}; + if (files.length > 0) { + const slice = files.splice(0, batch).map((dirent) => this._formatEntry(dirent, path3)); + for (const entry of await Promise.all(slice)) { + if (this.destroyed) return; + const entryType = await this._getEntryType(entry); + if (entryType === "directory" && this._directoryFilter(entry)) { + if (depth <= this._maxDepth) { + this.parents.push(this._exploreDir(entry.fullPath, depth + 1)); + } + if (this._wantsDir) { + this.push(entry); + batch--; + } + } else if ((entryType === "file" || this._includeAsFile(entry)) && this._fileFilter(entry)) { + if (this._wantsFile) { + this.push(entry); + batch--; + } + } + } + } else { + const parent = this.parents.pop(); + if (!parent) { + this.push(null); + break; + } + this.parent = await parent; + if (this.destroyed) return; + } + } + } catch (error) { + this.destroy(error); + } finally { + this.reading = false; + } + } + async _exploreDir(path3, depth) { + let files; + try { + files = await readdir(path3, this._rdOptions); + } catch (error) { + this._onError(error); + } + return { files, depth, path: path3 }; + } + async _formatEntry(dirent, path3) { + let entry; + try { + const basename = this._isDirent ? dirent.name : dirent; + const fullPath = sysPath.resolve(sysPath.join(path3, basename)); + entry = { path: sysPath.relative(this._root, fullPath), fullPath, basename }; + entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath); + } catch (err) { + this._onError(err); + } + return entry; + } + _onError(err) { + if (isNormalFlowError(err) && !this.destroyed) { + this.emit("warn", err); + } else { + this.destroy(err); + } + } + async _getEntryType(entry) { + const stats = entry && entry[this._statsProp]; + if (!stats) { + return; + } + if (stats.isFile()) { + return "file"; + } + if (stats.isDirectory()) { + return "directory"; + } + if (stats && stats.isSymbolicLink()) { + const full = entry.fullPath; + try { + const entryRealPath = await realpath(full); + const entryRealPathStats = await lstat(entryRealPath); + if (entryRealPathStats.isFile()) { + return "file"; + } + if (entryRealPathStats.isDirectory()) { + const len = entryRealPath.length; + if (full.startsWith(entryRealPath) && full.substr(len, 1) === sysPath.sep) { + const recursiveError = new Error( + `Circular symlink detected: "${full}" points to "${entryRealPath}"` + ); + recursiveError.code = RECURSIVE_ERROR_CODE; + return this._onError(recursiveError); + } + return "directory"; + } + } catch (error) { + this._onError(error); + } + } + } + _includeAsFile(entry) { + const stats = entry && entry[this._statsProp]; + return stats && this._wantsEverything && !stats.isDirectory(); + } + }; + var readdirp = (root, options = {}) => { + let type = options.entryType || options.type; + if (type === "both") type = FILE_DIR_TYPE; + if (type) options.type = type; + if (!root) { + throw new Error("readdirp: root argument is required. Usage: readdirp(root, options)"); + } else if (typeof root !== "string") { + throw new TypeError("readdirp: root argument must be a string. Usage: readdirp(root, options)"); + } else if (type && !ALL_TYPES.includes(type)) { + throw new Error(`readdirp: Invalid type passed. Use one of ${ALL_TYPES.join(", ")}`); + } + options.root = root; + return new ReaddirpStream(options); + }; + var readdirpPromise = (root, options = {}) => { + return new Promise((resolve, reject) => { + const files = []; + readdirp(root, options).on("data", (entry) => files.push(entry)).on("end", () => resolve(files)).on("error", (error) => reject(error)); + }); + }; + readdirp.promise = readdirpPromise; + readdirp.ReaddirpStream = ReaddirpStream; + readdirp.default = readdirp; + module2.exports = readdirp; + } +}); + +// node_modules/anymatch/node_modules/picomatch/lib/constants.js +var require_constants2 = __commonJS({ + "node_modules/anymatch/node_modules/picomatch/lib/constants.js"(exports2, module2) { + "use strict"; + var path3 = require("path"); + var WIN_SLASH = "\\\\/"; + var WIN_NO_SLASH = `[^${WIN_SLASH}]`; + var DEFAULT_MAX_EXTGLOB_RECURSION = 0; + var DOT_LITERAL = "\\."; + var PLUS_LITERAL = "\\+"; + var QMARK_LITERAL = "\\?"; + var SLASH_LITERAL = "\\/"; + var ONE_CHAR = "(?=.)"; + var QMARK = "[^/]"; + var END_ANCHOR = `(?:${SLASH_LITERAL}|$)`; + var START_ANCHOR = `(?:^|${SLASH_LITERAL})`; + var DOTS_SLASH = `${DOT_LITERAL}{1,2}${END_ANCHOR}`; + var NO_DOT = `(?!${DOT_LITERAL})`; + var NO_DOTS = `(?!${START_ANCHOR}${DOTS_SLASH})`; + var NO_DOT_SLASH = `(?!${DOT_LITERAL}{0,1}${END_ANCHOR})`; + var NO_DOTS_SLASH = `(?!${DOTS_SLASH})`; + var QMARK_NO_DOT = `[^.${SLASH_LITERAL}]`; + var STAR = `${QMARK}*?`; + var POSIX_CHARS = { + DOT_LITERAL, + PLUS_LITERAL, + QMARK_LITERAL, + SLASH_LITERAL, + ONE_CHAR, + QMARK, + END_ANCHOR, + DOTS_SLASH, + NO_DOT, + NO_DOTS, + NO_DOT_SLASH, + NO_DOTS_SLASH, + QMARK_NO_DOT, + STAR, + START_ANCHOR + }; + var WINDOWS_CHARS = { + ...POSIX_CHARS, + SLASH_LITERAL: `[${WIN_SLASH}]`, + QMARK: WIN_NO_SLASH, + STAR: `${WIN_NO_SLASH}*?`, + DOTS_SLASH: `${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$)`, + NO_DOT: `(?!${DOT_LITERAL})`, + NO_DOTS: `(?!(?:^|[${WIN_SLASH}])${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`, + NO_DOT_SLASH: `(?!${DOT_LITERAL}{0,1}(?:[${WIN_SLASH}]|$))`, + NO_DOTS_SLASH: `(?!${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`, + QMARK_NO_DOT: `[^.${WIN_SLASH}]`, + START_ANCHOR: `(?:^|[${WIN_SLASH}])`, + END_ANCHOR: `(?:[${WIN_SLASH}]|$)` + }; + var POSIX_REGEX_SOURCE = { + __proto__: null, + alnum: "a-zA-Z0-9", + alpha: "a-zA-Z", + ascii: "\\x00-\\x7F", + blank: " \\t", + cntrl: "\\x00-\\x1F\\x7F", + digit: "0-9", + graph: "\\x21-\\x7E", + lower: "a-z", + print: "\\x20-\\x7E ", + punct: "\\-!\"#$%&'()\\*+,./:;<=>?@[\\]^_`{|}~", + space: " \\t\\r\\n\\v\\f", + upper: "A-Z", + word: "A-Za-z0-9_", + xdigit: "A-Fa-f0-9" + }; + module2.exports = { + DEFAULT_MAX_EXTGLOB_RECURSION, + MAX_LENGTH: 1024 * 64, + POSIX_REGEX_SOURCE, + // regular expressions + REGEX_BACKSLASH: /\\(?![*+?^${}(|)[\]])/g, + REGEX_NON_SPECIAL_CHARS: /^[^@![\].,$*+?^{}()|\\/]+/, + REGEX_SPECIAL_CHARS: /[-*+?.^${}(|)[\]]/, + REGEX_SPECIAL_CHARS_BACKREF: /(\\?)((\W)(\3*))/g, + REGEX_SPECIAL_CHARS_GLOBAL: /([-*+?.^${}(|)[\]])/g, + REGEX_REMOVE_BACKSLASH: /(?:\[.*?[^\\]\]|\\(?=.))/g, + // Replace globs with equivalent patterns to reduce parsing time. + REPLACEMENTS: { + __proto__: null, + "***": "*", + "**/**": "**", + "**/**/**": "**" + }, + // Digits + CHAR_0: 48, + /* 0 */ + CHAR_9: 57, + /* 9 */ + // Alphabet chars. + CHAR_UPPERCASE_A: 65, + /* A */ + CHAR_LOWERCASE_A: 97, + /* a */ + CHAR_UPPERCASE_Z: 90, + /* Z */ + CHAR_LOWERCASE_Z: 122, + /* z */ + CHAR_LEFT_PARENTHESES: 40, + /* ( */ + CHAR_RIGHT_PARENTHESES: 41, + /* ) */ + CHAR_ASTERISK: 42, + /* * */ + // Non-alphabetic chars. + CHAR_AMPERSAND: 38, + /* & */ + CHAR_AT: 64, + /* @ */ + CHAR_BACKWARD_SLASH: 92, + /* \ */ + CHAR_CARRIAGE_RETURN: 13, + /* \r */ + CHAR_CIRCUMFLEX_ACCENT: 94, + /* ^ */ + CHAR_COLON: 58, + /* : */ + CHAR_COMMA: 44, + /* , */ + CHAR_DOT: 46, + /* . */ + CHAR_DOUBLE_QUOTE: 34, + /* " */ + CHAR_EQUAL: 61, + /* = */ + CHAR_EXCLAMATION_MARK: 33, + /* ! */ + CHAR_FORM_FEED: 12, + /* \f */ + CHAR_FORWARD_SLASH: 47, + /* / */ + CHAR_GRAVE_ACCENT: 96, + /* ` */ + CHAR_HASH: 35, + /* # */ + CHAR_HYPHEN_MINUS: 45, + /* - */ + CHAR_LEFT_ANGLE_BRACKET: 60, + /* < */ + CHAR_LEFT_CURLY_BRACE: 123, + /* { */ + CHAR_LEFT_SQUARE_BRACKET: 91, + /* [ */ + CHAR_LINE_FEED: 10, + /* \n */ + CHAR_NO_BREAK_SPACE: 160, + /* \u00A0 */ + CHAR_PERCENT: 37, + /* % */ + CHAR_PLUS: 43, + /* + */ + CHAR_QUESTION_MARK: 63, + /* ? */ + CHAR_RIGHT_ANGLE_BRACKET: 62, + /* > */ + CHAR_RIGHT_CURLY_BRACE: 125, + /* } */ + CHAR_RIGHT_SQUARE_BRACKET: 93, + /* ] */ + CHAR_SEMICOLON: 59, + /* ; */ + CHAR_SINGLE_QUOTE: 39, + /* ' */ + CHAR_SPACE: 32, + /* */ + CHAR_TAB: 9, + /* \t */ + CHAR_UNDERSCORE: 95, + /* _ */ + CHAR_VERTICAL_LINE: 124, + /* | */ + CHAR_ZERO_WIDTH_NOBREAK_SPACE: 65279, + /* \uFEFF */ + SEP: path3.sep, + /** + * Create EXTGLOB_CHARS + */ + extglobChars(chars) { + return { + "!": { type: "negate", open: "(?:(?!(?:", close: `))${chars.STAR})` }, + "?": { type: "qmark", open: "(?:", close: ")?" }, + "+": { type: "plus", open: "(?:", close: ")+" }, + "*": { type: "star", open: "(?:", close: ")*" }, + "@": { type: "at", open: "(?:", close: ")" } + }; + }, + /** + * Create GLOB_CHARS + */ + globChars(win32) { + return win32 === true ? WINDOWS_CHARS : POSIX_CHARS; + } + }; + } +}); + +// node_modules/anymatch/node_modules/picomatch/lib/utils.js +var require_utils2 = __commonJS({ + "node_modules/anymatch/node_modules/picomatch/lib/utils.js"(exports2) { + "use strict"; + var path3 = require("path"); + var win32 = process.platform === "win32"; + var { + REGEX_BACKSLASH, + REGEX_REMOVE_BACKSLASH, + REGEX_SPECIAL_CHARS, + REGEX_SPECIAL_CHARS_GLOBAL + } = require_constants2(); + exports2.isObject = (val) => val !== null && typeof val === "object" && !Array.isArray(val); + exports2.hasRegexChars = (str) => REGEX_SPECIAL_CHARS.test(str); + exports2.isRegexChar = (str) => str.length === 1 && exports2.hasRegexChars(str); + exports2.escapeRegex = (str) => str.replace(REGEX_SPECIAL_CHARS_GLOBAL, "\\$1"); + exports2.toPosixSlashes = (str) => str.replace(REGEX_BACKSLASH, "/"); + exports2.removeBackslashes = (str) => { + return str.replace(REGEX_REMOVE_BACKSLASH, (match) => { + return match === "\\" ? "" : match; + }); + }; + exports2.supportsLookbehinds = () => { + const segs = process.version.slice(1).split(".").map(Number); + if (segs.length === 3 && segs[0] >= 9 || segs[0] === 8 && segs[1] >= 10) { + return true; + } + return false; + }; + exports2.isWindows = (options) => { + if (options && typeof options.windows === "boolean") { + return options.windows; + } + return win32 === true || path3.sep === "\\"; + }; + exports2.escapeLast = (input, char, lastIdx) => { + const idx = input.lastIndexOf(char, lastIdx); + if (idx === -1) return input; + if (input[idx - 1] === "\\") return exports2.escapeLast(input, char, idx - 1); + return `${input.slice(0, idx)}\\${input.slice(idx)}`; + }; + exports2.removePrefix = (input, state = {}) => { + let output = input; + if (output.startsWith("./")) { + output = output.slice(2); + state.prefix = "./"; + } + return output; + }; + exports2.wrapOutput = (input, state = {}, options = {}) => { + const prepend = options.contains ? "" : "^"; + const append = options.contains ? "" : "$"; + let output = `${prepend}(?:${input})${append}`; + if (state.negated === true) { + output = `(?:^(?!${output}).*$)`; + } + return output; + }; + } +}); + +// node_modules/anymatch/node_modules/picomatch/lib/scan.js +var require_scan2 = __commonJS({ + "node_modules/anymatch/node_modules/picomatch/lib/scan.js"(exports2, module2) { + "use strict"; + var utils = require_utils2(); + var { + CHAR_ASTERISK, + /* * */ + CHAR_AT, + /* @ */ + CHAR_BACKWARD_SLASH, + /* \ */ + CHAR_COMMA, + /* , */ + CHAR_DOT, + /* . */ + CHAR_EXCLAMATION_MARK, + /* ! */ + CHAR_FORWARD_SLASH, + /* / */ + CHAR_LEFT_CURLY_BRACE, + /* { */ + CHAR_LEFT_PARENTHESES, + /* ( */ + CHAR_LEFT_SQUARE_BRACKET, + /* [ */ + CHAR_PLUS, + /* + */ + CHAR_QUESTION_MARK, + /* ? */ + CHAR_RIGHT_CURLY_BRACE, + /* } */ + CHAR_RIGHT_PARENTHESES, + /* ) */ + CHAR_RIGHT_SQUARE_BRACKET + /* ] */ + } = require_constants2(); + var isPathSeparator = (code) => { + return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH; + }; + var depth = (token) => { + if (token.isPrefix !== true) { + token.depth = token.isGlobstar ? Infinity : 1; + } + }; + var scan = (input, options) => { + const opts = options || {}; + const length = input.length - 1; + const scanToEnd = opts.parts === true || opts.scanToEnd === true; + const slashes = []; + const tokens = []; + const parts = []; + let str = input; + let index = -1; + let start = 0; + let lastIndex = 0; + let isBrace = false; + let isBracket = false; + let isGlob = false; + let isExtglob = false; + let isGlobstar = false; + let braceEscaped = false; + let backslashes = false; + let negated = false; + let negatedExtglob = false; + let finished = false; + let braces = 0; + let prev; + let code; + let token = { value: "", depth: 0, isGlob: false }; + const eos = () => index >= length; + const peek = () => str.charCodeAt(index + 1); + const advance = () => { + prev = code; + return str.charCodeAt(++index); + }; + while (index < length) { + code = advance(); + let next; + if (code === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + code = advance(); + if (code === CHAR_LEFT_CURLY_BRACE) { + braceEscaped = true; + } + continue; + } + if (braceEscaped === true || code === CHAR_LEFT_CURLY_BRACE) { + braces++; + while (eos() !== true && (code = advance())) { + if (code === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + advance(); + continue; + } + if (code === CHAR_LEFT_CURLY_BRACE) { + braces++; + continue; + } + if (braceEscaped !== true && code === CHAR_DOT && (code = advance()) === CHAR_DOT) { + isBrace = token.isBrace = true; + isGlob = token.isGlob = true; + finished = true; + if (scanToEnd === true) { + continue; + } + break; + } + if (braceEscaped !== true && code === CHAR_COMMA) { + isBrace = token.isBrace = true; + isGlob = token.isGlob = true; + finished = true; + if (scanToEnd === true) { + continue; + } + break; + } + if (code === CHAR_RIGHT_CURLY_BRACE) { + braces--; + if (braces === 0) { + braceEscaped = false; + isBrace = token.isBrace = true; + finished = true; + break; + } + } + } + if (scanToEnd === true) { + continue; + } + break; + } + if (code === CHAR_FORWARD_SLASH) { + slashes.push(index); + tokens.push(token); + token = { value: "", depth: 0, isGlob: false }; + if (finished === true) continue; + if (prev === CHAR_DOT && index === start + 1) { + start += 2; + continue; + } + lastIndex = index + 1; + continue; + } + if (opts.noext !== true) { + const isExtglobChar = code === CHAR_PLUS || code === CHAR_AT || code === CHAR_ASTERISK || code === CHAR_QUESTION_MARK || code === CHAR_EXCLAMATION_MARK; + if (isExtglobChar === true && peek() === CHAR_LEFT_PARENTHESES) { + isGlob = token.isGlob = true; + isExtglob = token.isExtglob = true; + finished = true; + if (code === CHAR_EXCLAMATION_MARK && index === start) { + negatedExtglob = true; + } + if (scanToEnd === true) { + while (eos() !== true && (code = advance())) { + if (code === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + code = advance(); + continue; + } + if (code === CHAR_RIGHT_PARENTHESES) { + isGlob = token.isGlob = true; + finished = true; + break; + } + } + continue; + } + break; + } + } + if (code === CHAR_ASTERISK) { + if (prev === CHAR_ASTERISK) isGlobstar = token.isGlobstar = true; + isGlob = token.isGlob = true; + finished = true; + if (scanToEnd === true) { + continue; + } + break; + } + if (code === CHAR_QUESTION_MARK) { + isGlob = token.isGlob = true; + finished = true; + if (scanToEnd === true) { + continue; + } + break; + } + if (code === CHAR_LEFT_SQUARE_BRACKET) { + while (eos() !== true && (next = advance())) { + if (next === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + advance(); + continue; + } + if (next === CHAR_RIGHT_SQUARE_BRACKET) { + isBracket = token.isBracket = true; + isGlob = token.isGlob = true; + finished = true; + break; + } + } + if (scanToEnd === true) { + continue; + } + break; + } + if (opts.nonegate !== true && code === CHAR_EXCLAMATION_MARK && index === start) { + negated = token.negated = true; + start++; + continue; + } + if (opts.noparen !== true && code === CHAR_LEFT_PARENTHESES) { + isGlob = token.isGlob = true; + if (scanToEnd === true) { + while (eos() !== true && (code = advance())) { + if (code === CHAR_LEFT_PARENTHESES) { + backslashes = token.backslashes = true; + code = advance(); + continue; + } + if (code === CHAR_RIGHT_PARENTHESES) { + finished = true; + break; + } + } + continue; + } + break; + } + if (isGlob === true) { + finished = true; + if (scanToEnd === true) { + continue; + } + break; + } + } + if (opts.noext === true) { + isExtglob = false; + isGlob = false; + } + let base = str; + let prefix = ""; + let glob = ""; + if (start > 0) { + prefix = str.slice(0, start); + str = str.slice(start); + lastIndex -= start; + } + if (base && isGlob === true && lastIndex > 0) { + base = str.slice(0, lastIndex); + glob = str.slice(lastIndex); + } else if (isGlob === true) { + base = ""; + glob = str; + } else { + base = str; + } + if (base && base !== "" && base !== "/" && base !== str) { + if (isPathSeparator(base.charCodeAt(base.length - 1))) { + base = base.slice(0, -1); + } + } + if (opts.unescape === true) { + if (glob) glob = utils.removeBackslashes(glob); + if (base && backslashes === true) { + base = utils.removeBackslashes(base); + } + } + const state = { + prefix, + input, + start, + base, + glob, + isBrace, + isBracket, + isGlob, + isExtglob, + isGlobstar, + negated, + negatedExtglob + }; + if (opts.tokens === true) { + state.maxDepth = 0; + if (!isPathSeparator(code)) { + tokens.push(token); + } + state.tokens = tokens; + } + if (opts.parts === true || opts.tokens === true) { + let prevIndex; + for (let idx = 0; idx < slashes.length; idx++) { + const n = prevIndex ? prevIndex + 1 : start; + const i = slashes[idx]; + const value = input.slice(n, i); + if (opts.tokens) { + if (idx === 0 && start !== 0) { + tokens[idx].isPrefix = true; + tokens[idx].value = prefix; + } else { + tokens[idx].value = value; + } + depth(tokens[idx]); + state.maxDepth += tokens[idx].depth; + } + if (idx !== 0 || value !== "") { + parts.push(value); + } + prevIndex = i; + } + if (prevIndex && prevIndex + 1 < input.length) { + const value = input.slice(prevIndex + 1); + parts.push(value); + if (opts.tokens) { + tokens[tokens.length - 1].value = value; + depth(tokens[tokens.length - 1]); + state.maxDepth += tokens[tokens.length - 1].depth; + } + } + state.slashes = slashes; + state.parts = parts; + } + return state; + }; + module2.exports = scan; + } +}); + +// node_modules/anymatch/node_modules/picomatch/lib/parse.js +var require_parse2 = __commonJS({ + "node_modules/anymatch/node_modules/picomatch/lib/parse.js"(exports2, module2) { + "use strict"; + var constants = require_constants2(); + var utils = require_utils2(); + var { + MAX_LENGTH, + POSIX_REGEX_SOURCE, + REGEX_NON_SPECIAL_CHARS, + REGEX_SPECIAL_CHARS_BACKREF, + REPLACEMENTS + } = constants; + var expandRange = (args, options) => { + if (typeof options.expandRange === "function") { + return options.expandRange(...args, options); + } + args.sort(); + const value = `[${args.join("-")}]`; + try { + new RegExp(value); + } catch (ex) { + return args.map((v) => utils.escapeRegex(v)).join(".."); + } + return value; + }; + var syntaxError = (type, char) => { + return `Missing ${type}: "${char}" - use "\\\\${char}" to match literal characters`; + }; + var splitTopLevel = (input) => { + const parts = []; + let bracket = 0; + let paren = 0; + let quote = 0; + let value = ""; + let escaped = false; + for (const ch of input) { + if (escaped === true) { + value += ch; + escaped = false; + continue; + } + if (ch === "\\") { + value += ch; + escaped = true; + continue; + } + if (ch === '"') { + quote = quote === 1 ? 0 : 1; + value += ch; + continue; + } + if (quote === 0) { + if (ch === "[") { + bracket++; + } else if (ch === "]" && bracket > 0) { + bracket--; + } else if (bracket === 0) { + if (ch === "(") { + paren++; + } else if (ch === ")" && paren > 0) { + paren--; + } else if (ch === "|" && paren === 0) { + parts.push(value); + value = ""; + continue; + } + } + } + value += ch; + } + parts.push(value); + return parts; + }; + var isPlainBranch = (branch) => { + let escaped = false; + for (const ch of branch) { + if (escaped === true) { + escaped = false; + continue; + } + if (ch === "\\") { + escaped = true; + continue; + } + if (/[?*+@!()[\]{}]/.test(ch)) { + return false; + } + } + return true; + }; + var normalizeSimpleBranch = (branch) => { + let value = branch.trim(); + let changed = true; + while (changed === true) { + changed = false; + if (/^@\([^\\()[\]{}|]+\)$/.test(value)) { + value = value.slice(2, -1); + changed = true; + } + } + if (!isPlainBranch(value)) { + return; + } + return value.replace(/\\(.)/g, "$1"); + }; + var hasRepeatedCharPrefixOverlap = (branches) => { + const values = branches.map(normalizeSimpleBranch).filter(Boolean); + for (let i = 0; i < values.length; i++) { + for (let j = i + 1; j < values.length; j++) { + const a = values[i]; + const b = values[j]; + const char = a[0]; + if (!char || a !== char.repeat(a.length) || b !== char.repeat(b.length)) { + continue; + } + if (a === b || a.startsWith(b) || b.startsWith(a)) { + return true; + } + } + } + return false; + }; + var parseRepeatedExtglob = (pattern, requireEnd = true) => { + if (pattern[0] !== "+" && pattern[0] !== "*" || pattern[1] !== "(") { + return; + } + let bracket = 0; + let paren = 0; + let quote = 0; + let escaped = false; + for (let i = 1; i < pattern.length; i++) { + const ch = pattern[i]; + if (escaped === true) { + escaped = false; + continue; + } + if (ch === "\\") { + escaped = true; + continue; + } + if (ch === '"') { + quote = quote === 1 ? 0 : 1; + continue; + } + if (quote === 1) { + continue; + } + if (ch === "[") { + bracket++; + continue; + } + if (ch === "]" && bracket > 0) { + bracket--; + continue; + } + if (bracket > 0) { + continue; + } + if (ch === "(") { + paren++; + continue; + } + if (ch === ")") { + paren--; + if (paren === 0) { + if (requireEnd === true && i !== pattern.length - 1) { + return; + } + return { + type: pattern[0], + body: pattern.slice(2, i), + end: i + }; + } + } + } + }; + var getStarExtglobSequenceOutput = (pattern) => { + let index = 0; + const chars = []; + while (index < pattern.length) { + const match = parseRepeatedExtglob(pattern.slice(index), false); + if (!match || match.type !== "*") { + return; + } + const branches = splitTopLevel(match.body).map((branch2) => branch2.trim()); + if (branches.length !== 1) { + return; + } + const branch = normalizeSimpleBranch(branches[0]); + if (!branch || branch.length !== 1) { + return; + } + chars.push(branch); + index += match.end + 1; + } + if (chars.length < 1) { + return; + } + const source = chars.length === 1 ? utils.escapeRegex(chars[0]) : `[${chars.map((ch) => utils.escapeRegex(ch)).join("")}]`; + return `${source}*`; + }; + var repeatedExtglobRecursion = (pattern) => { + let depth = 0; + let value = pattern.trim(); + let match = parseRepeatedExtglob(value); + while (match) { + depth++; + value = match.body.trim(); + match = parseRepeatedExtglob(value); + } + return depth; + }; + var analyzeRepeatedExtglob = (body, options) => { + if (options.maxExtglobRecursion === false) { + return { risky: false }; + } + const max = typeof options.maxExtglobRecursion === "number" ? options.maxExtglobRecursion : constants.DEFAULT_MAX_EXTGLOB_RECURSION; + const branches = splitTopLevel(body).map((branch) => branch.trim()); + if (branches.length > 1) { + if (branches.some((branch) => branch === "") || branches.some((branch) => /^[*?]+$/.test(branch)) || hasRepeatedCharPrefixOverlap(branches)) { + return { risky: true }; + } + } + for (const branch of branches) { + const safeOutput = getStarExtglobSequenceOutput(branch); + if (safeOutput) { + return { risky: true, safeOutput }; + } + if (repeatedExtglobRecursion(branch) > max) { + return { risky: true }; + } + } + return { risky: false }; + }; + var parse = (input, options) => { + if (typeof input !== "string") { + throw new TypeError("Expected a string"); + } + input = REPLACEMENTS[input] || input; + const opts = { ...options }; + const max = typeof opts.maxLength === "number" ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; + let len = input.length; + if (len > max) { + throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`); + } + const bos = { type: "bos", value: "", output: opts.prepend || "" }; + const tokens = [bos]; + const capture = opts.capture ? "" : "?:"; + const win32 = utils.isWindows(options); + const PLATFORM_CHARS = constants.globChars(win32); + const EXTGLOB_CHARS = constants.extglobChars(PLATFORM_CHARS); + const { + DOT_LITERAL, + PLUS_LITERAL, + SLASH_LITERAL, + ONE_CHAR, + DOTS_SLASH, + NO_DOT, + NO_DOT_SLASH, + NO_DOTS_SLASH, + QMARK, + QMARK_NO_DOT, + STAR, + START_ANCHOR + } = PLATFORM_CHARS; + const globstar = (opts2) => { + return `(${capture}(?:(?!${START_ANCHOR}${opts2.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`; + }; + const nodot = opts.dot ? "" : NO_DOT; + const qmarkNoDot = opts.dot ? QMARK : QMARK_NO_DOT; + let star = opts.bash === true ? globstar(opts) : STAR; + if (opts.capture) { + star = `(${star})`; + } + if (typeof opts.noext === "boolean") { + opts.noextglob = opts.noext; + } + const state = { + input, + index: -1, + start: 0, + dot: opts.dot === true, + consumed: "", + output: "", + prefix: "", + backtrack: false, + negated: false, + brackets: 0, + braces: 0, + parens: 0, + quotes: 0, + globstar: false, + tokens + }; + input = utils.removePrefix(input, state); + len = input.length; + const extglobs = []; + const braces = []; + const stack = []; + let prev = bos; + let value; + const eos = () => state.index === len - 1; + const peek = state.peek = (n = 1) => input[state.index + n]; + const advance = state.advance = () => input[++state.index] || ""; + const remaining = () => input.slice(state.index + 1); + const consume = (value2 = "", num = 0) => { + state.consumed += value2; + state.index += num; + }; + const append = (token) => { + state.output += token.output != null ? token.output : token.value; + consume(token.value); + }; + const negate = () => { + let count = 1; + while (peek() === "!" && (peek(2) !== "(" || peek(3) === "?")) { + advance(); + state.start++; + count++; + } + if (count % 2 === 0) { + return false; + } + state.negated = true; + state.start++; + return true; + }; + const increment = (type) => { + state[type]++; + stack.push(type); + }; + const decrement = (type) => { + state[type]--; + stack.pop(); + }; + const push = (tok) => { + if (prev.type === "globstar") { + const isBrace = state.braces > 0 && (tok.type === "comma" || tok.type === "brace"); + const isExtglob = tok.extglob === true || extglobs.length && (tok.type === "pipe" || tok.type === "paren"); + if (tok.type !== "slash" && tok.type !== "paren" && !isBrace && !isExtglob) { + state.output = state.output.slice(0, -prev.output.length); + prev.type = "star"; + prev.value = "*"; + prev.output = star; + state.output += prev.output; + } + } + if (extglobs.length && tok.type !== "paren") { + extglobs[extglobs.length - 1].inner += tok.value; + } + if (tok.value || tok.output) append(tok); + if (prev && prev.type === "text" && tok.type === "text") { + prev.value += tok.value; + prev.output = (prev.output || "") + tok.value; + return; + } + tok.prev = prev; + tokens.push(tok); + prev = tok; + }; + const extglobOpen = (type, value2) => { + const token = { ...EXTGLOB_CHARS[value2], conditions: 1, inner: "" }; + token.prev = prev; + token.parens = state.parens; + token.output = state.output; + token.startIndex = state.index; + token.tokensIndex = tokens.length; + const output = (opts.capture ? "(" : "") + token.open; + increment("parens"); + push({ type, value: value2, output: state.output ? "" : ONE_CHAR }); + push({ type: "paren", extglob: true, value: advance(), output }); + extglobs.push(token); + }; + const extglobClose = (token) => { + const literal = input.slice(token.startIndex, state.index + 1); + const body = input.slice(token.startIndex + 2, state.index); + const analysis = analyzeRepeatedExtglob(body, opts); + if ((token.type === "plus" || token.type === "star") && analysis.risky) { + const safeOutput = analysis.safeOutput ? (token.output ? "" : ONE_CHAR) + (opts.capture ? `(${analysis.safeOutput})` : analysis.safeOutput) : void 0; + const open = tokens[token.tokensIndex]; + open.type = "text"; + open.value = literal; + open.output = safeOutput || utils.escapeRegex(literal); + for (let i = token.tokensIndex + 1; i < tokens.length; i++) { + tokens[i].value = ""; + tokens[i].output = ""; + delete tokens[i].suffix; + } + state.output = token.output + open.output; + state.backtrack = true; + push({ type: "paren", extglob: true, value, output: "" }); + decrement("parens"); + return; + } + let output = token.close + (opts.capture ? ")" : ""); + let rest; + if (token.type === "negate") { + let extglobStar = star; + if (token.inner && token.inner.length > 1 && token.inner.includes("/")) { + extglobStar = globstar(opts); + } + if (extglobStar !== star || eos() || /^\)+$/.test(remaining())) { + output = token.close = `)$))${extglobStar}`; + } + if (token.inner.includes("*") && (rest = remaining()) && /^\.[^\\/.]+$/.test(rest)) { + const expression = parse(rest, { ...options, fastpaths: false }).output; + output = token.close = `)${expression})${extglobStar})`; + } + if (token.prev.type === "bos") { + state.negatedExtglob = true; + } + } + push({ type: "paren", extglob: true, value, output }); + decrement("parens"); + }; + if (opts.fastpaths !== false && !/(^[*!]|[/()[\]{}"])/.test(input)) { + let backslashes = false; + let output = input.replace(REGEX_SPECIAL_CHARS_BACKREF, (m, esc, chars, first, rest, index) => { + if (first === "\\") { + backslashes = true; + return m; + } + if (first === "?") { + if (esc) { + return esc + first + (rest ? QMARK.repeat(rest.length) : ""); + } + if (index === 0) { + return qmarkNoDot + (rest ? QMARK.repeat(rest.length) : ""); + } + return QMARK.repeat(chars.length); + } + if (first === ".") { + return DOT_LITERAL.repeat(chars.length); + } + if (first === "*") { + if (esc) { + return esc + first + (rest ? star : ""); + } + return star; + } + return esc ? m : `\\${m}`; + }); + if (backslashes === true) { + if (opts.unescape === true) { + output = output.replace(/\\/g, ""); + } else { + output = output.replace(/\\+/g, (m) => { + return m.length % 2 === 0 ? "\\\\" : m ? "\\" : ""; + }); + } + } + if (output === input && opts.contains === true) { + state.output = input; + return state; + } + state.output = utils.wrapOutput(output, state, options); + return state; + } + while (!eos()) { + value = advance(); + if (value === "\0") { + continue; + } + if (value === "\\") { + const next = peek(); + if (next === "/" && opts.bash !== true) { + continue; + } + if (next === "." || next === ";") { + continue; + } + if (!next) { + value += "\\"; + push({ type: "text", value }); + continue; + } + const match = /^\\+/.exec(remaining()); + let slashes = 0; + if (match && match[0].length > 2) { + slashes = match[0].length; + state.index += slashes; + if (slashes % 2 !== 0) { + value += "\\"; + } + } + if (opts.unescape === true) { + value = advance(); + } else { + value += advance(); + } + if (state.brackets === 0) { + push({ type: "text", value }); + continue; + } + } + if (state.brackets > 0 && (value !== "]" || prev.value === "[" || prev.value === "[^")) { + if (opts.posix !== false && value === ":") { + const inner = prev.value.slice(1); + if (inner.includes("[")) { + prev.posix = true; + if (inner.includes(":")) { + const idx = prev.value.lastIndexOf("["); + const pre = prev.value.slice(0, idx); + const rest2 = prev.value.slice(idx + 2); + const posix = POSIX_REGEX_SOURCE[rest2]; + if (posix) { + prev.value = pre + posix; + state.backtrack = true; + advance(); + if (!bos.output && tokens.indexOf(prev) === 1) { + bos.output = ONE_CHAR; + } + continue; + } + } + } + } + if (value === "[" && peek() !== ":" || value === "-" && peek() === "]") { + value = `\\${value}`; + } + if (value === "]" && (prev.value === "[" || prev.value === "[^")) { + value = `\\${value}`; + } + if (opts.posix === true && value === "!" && prev.value === "[") { + value = "^"; + } + prev.value += value; + append({ value }); + continue; + } + if (state.quotes === 1 && value !== '"') { + value = utils.escapeRegex(value); + prev.value += value; + append({ value }); + continue; + } + if (value === '"') { + state.quotes = state.quotes === 1 ? 0 : 1; + if (opts.keepQuotes === true) { + push({ type: "text", value }); + } + continue; + } + if (value === "(") { + increment("parens"); + push({ type: "paren", value }); + continue; + } + if (value === ")") { + if (state.parens === 0 && opts.strictBrackets === true) { + throw new SyntaxError(syntaxError("opening", "(")); + } + const extglob = extglobs[extglobs.length - 1]; + if (extglob && state.parens === extglob.parens + 1) { + extglobClose(extglobs.pop()); + continue; + } + push({ type: "paren", value, output: state.parens ? ")" : "\\)" }); + decrement("parens"); + continue; + } + if (value === "[") { + if (opts.nobracket === true || !remaining().includes("]")) { + if (opts.nobracket !== true && opts.strictBrackets === true) { + throw new SyntaxError(syntaxError("closing", "]")); + } + value = `\\${value}`; + } else { + increment("brackets"); + } + push({ type: "bracket", value }); + continue; + } + if (value === "]") { + if (opts.nobracket === true || prev && prev.type === "bracket" && prev.value.length === 1) { + push({ type: "text", value, output: `\\${value}` }); + continue; + } + if (state.brackets === 0) { + if (opts.strictBrackets === true) { + throw new SyntaxError(syntaxError("opening", "[")); + } + push({ type: "text", value, output: `\\${value}` }); + continue; + } + decrement("brackets"); + const prevValue = prev.value.slice(1); + if (prev.posix !== true && prevValue[0] === "^" && !prevValue.includes("/")) { + value = `/${value}`; + } + prev.value += value; + append({ value }); + if (opts.literalBrackets === false || utils.hasRegexChars(prevValue)) { + continue; + } + const escaped = utils.escapeRegex(prev.value); + state.output = state.output.slice(0, -prev.value.length); + if (opts.literalBrackets === true) { + state.output += escaped; + prev.value = escaped; + continue; + } + prev.value = `(${capture}${escaped}|${prev.value})`; + state.output += prev.value; + continue; + } + if (value === "{" && opts.nobrace !== true) { + increment("braces"); + const open = { + type: "brace", + value, + output: "(", + outputIndex: state.output.length, + tokensIndex: state.tokens.length + }; + braces.push(open); + push(open); + continue; + } + if (value === "}") { + const brace = braces[braces.length - 1]; + if (opts.nobrace === true || !brace) { + push({ type: "text", value, output: value }); + continue; + } + let output = ")"; + if (brace.dots === true) { + const arr = tokens.slice(); + const range = []; + for (let i = arr.length - 1; i >= 0; i--) { + tokens.pop(); + if (arr[i].type === "brace") { + break; + } + if (arr[i].type !== "dots") { + range.unshift(arr[i].value); + } + } + output = expandRange(range, opts); + state.backtrack = true; + } + if (brace.comma !== true && brace.dots !== true) { + const out = state.output.slice(0, brace.outputIndex); + const toks = state.tokens.slice(brace.tokensIndex); + brace.value = brace.output = "\\{"; + value = output = "\\}"; + state.output = out; + for (const t of toks) { + state.output += t.output || t.value; + } + } + push({ type: "brace", value, output }); + decrement("braces"); + braces.pop(); + continue; + } + if (value === "|") { + if (extglobs.length > 0) { + extglobs[extglobs.length - 1].conditions++; + } + push({ type: "text", value }); + continue; + } + if (value === ",") { + let output = value; + const brace = braces[braces.length - 1]; + if (brace && stack[stack.length - 1] === "braces") { + brace.comma = true; + output = "|"; + } + push({ type: "comma", value, output }); + continue; + } + if (value === "/") { + if (prev.type === "dot" && state.index === state.start + 1) { + state.start = state.index + 1; + state.consumed = ""; + state.output = ""; + tokens.pop(); + prev = bos; + continue; + } + push({ type: "slash", value, output: SLASH_LITERAL }); + continue; + } + if (value === ".") { + if (state.braces > 0 && prev.type === "dot") { + if (prev.value === ".") prev.output = DOT_LITERAL; + const brace = braces[braces.length - 1]; + prev.type = "dots"; + prev.output += value; + prev.value += value; + brace.dots = true; + continue; + } + if (state.braces + state.parens === 0 && prev.type !== "bos" && prev.type !== "slash") { + push({ type: "text", value, output: DOT_LITERAL }); + continue; + } + push({ type: "dot", value, output: DOT_LITERAL }); + continue; + } + if (value === "?") { + const isGroup = prev && prev.value === "("; + if (!isGroup && opts.noextglob !== true && peek() === "(" && peek(2) !== "?") { + extglobOpen("qmark", value); + continue; + } + if (prev && prev.type === "paren") { + const next = peek(); + let output = value; + if (next === "<" && !utils.supportsLookbehinds()) { + throw new Error("Node.js v10 or higher is required for regex lookbehinds"); + } + if (prev.value === "(" && !/[!=<:]/.test(next) || next === "<" && !/<([!=]|\w+>)/.test(remaining())) { + output = `\\${value}`; + } + push({ type: "text", value, output }); + continue; + } + if (opts.dot !== true && (prev.type === "slash" || prev.type === "bos")) { + push({ type: "qmark", value, output: QMARK_NO_DOT }); + continue; + } + push({ type: "qmark", value, output: QMARK }); + continue; + } + if (value === "!") { + if (opts.noextglob !== true && peek() === "(") { + if (peek(2) !== "?" || !/[!=<:]/.test(peek(3))) { + extglobOpen("negate", value); + continue; + } + } + if (opts.nonegate !== true && state.index === 0) { + negate(); + continue; + } + } + if (value === "+") { + if (opts.noextglob !== true && peek() === "(" && peek(2) !== "?") { + extglobOpen("plus", value); + continue; + } + if (prev && prev.value === "(" || opts.regex === false) { + push({ type: "plus", value, output: PLUS_LITERAL }); + continue; + } + if (prev && (prev.type === "bracket" || prev.type === "paren" || prev.type === "brace") || state.parens > 0) { + push({ type: "plus", value }); + continue; + } + push({ type: "plus", value: PLUS_LITERAL }); + continue; + } + if (value === "@") { + if (opts.noextglob !== true && peek() === "(" && peek(2) !== "?") { + push({ type: "at", extglob: true, value, output: "" }); + continue; + } + push({ type: "text", value }); + continue; + } + if (value !== "*") { + if (value === "$" || value === "^") { + value = `\\${value}`; + } + const match = REGEX_NON_SPECIAL_CHARS.exec(remaining()); + if (match) { + value += match[0]; + state.index += match[0].length; + } + push({ type: "text", value }); + continue; + } + if (prev && (prev.type === "globstar" || prev.star === true)) { + prev.type = "star"; + prev.star = true; + prev.value += value; + prev.output = star; + state.backtrack = true; + state.globstar = true; + consume(value); + continue; + } + let rest = remaining(); + if (opts.noextglob !== true && /^\([^?]/.test(rest)) { + extglobOpen("star", value); + continue; + } + if (prev.type === "star") { + if (opts.noglobstar === true) { + consume(value); + continue; + } + const prior = prev.prev; + const before = prior.prev; + const isStart = prior.type === "slash" || prior.type === "bos"; + const afterStar = before && (before.type === "star" || before.type === "globstar"); + if (opts.bash === true && (!isStart || rest[0] && rest[0] !== "/")) { + push({ type: "star", value, output: "" }); + continue; + } + const isBrace = state.braces > 0 && (prior.type === "comma" || prior.type === "brace"); + const isExtglob = extglobs.length && (prior.type === "pipe" || prior.type === "paren"); + if (!isStart && prior.type !== "paren" && !isBrace && !isExtglob) { + push({ type: "star", value, output: "" }); + continue; + } + while (rest.slice(0, 3) === "/**") { + const after = input[state.index + 4]; + if (after && after !== "/") { + break; + } + rest = rest.slice(3); + consume("/**", 3); + } + if (prior.type === "bos" && eos()) { + prev.type = "globstar"; + prev.value += value; + prev.output = globstar(opts); + state.output = prev.output; + state.globstar = true; + consume(value); + continue; + } + if (prior.type === "slash" && prior.prev.type !== "bos" && !afterStar && eos()) { + state.output = state.output.slice(0, -(prior.output + prev.output).length); + prior.output = `(?:${prior.output}`; + prev.type = "globstar"; + prev.output = globstar(opts) + (opts.strictSlashes ? ")" : "|$)"); + prev.value += value; + state.globstar = true; + state.output += prior.output + prev.output; + consume(value); + continue; + } + if (prior.type === "slash" && prior.prev.type !== "bos" && rest[0] === "/") { + const end = rest[1] !== void 0 ? "|$" : ""; + state.output = state.output.slice(0, -(prior.output + prev.output).length); + prior.output = `(?:${prior.output}`; + prev.type = "globstar"; + prev.output = `${globstar(opts)}${SLASH_LITERAL}|${SLASH_LITERAL}${end})`; + prev.value += value; + state.output += prior.output + prev.output; + state.globstar = true; + consume(value + advance()); + push({ type: "slash", value: "/", output: "" }); + continue; + } + if (prior.type === "bos" && rest[0] === "/") { + prev.type = "globstar"; + prev.value += value; + prev.output = `(?:^|${SLASH_LITERAL}|${globstar(opts)}${SLASH_LITERAL})`; + state.output = prev.output; + state.globstar = true; + consume(value + advance()); + push({ type: "slash", value: "/", output: "" }); + continue; + } + state.output = state.output.slice(0, -prev.output.length); + prev.type = "globstar"; + prev.output = globstar(opts); + prev.value += value; + state.output += prev.output; + state.globstar = true; + consume(value); + continue; + } + const token = { type: "star", value, output: star }; + if (opts.bash === true) { + token.output = ".*?"; + if (prev.type === "bos" || prev.type === "slash") { + token.output = nodot + token.output; + } + push(token); + continue; + } + if (prev && (prev.type === "bracket" || prev.type === "paren") && opts.regex === true) { + token.output = value; + push(token); + continue; + } + if (state.index === state.start || prev.type === "slash" || prev.type === "dot") { + if (prev.type === "dot") { + state.output += NO_DOT_SLASH; + prev.output += NO_DOT_SLASH; + } else if (opts.dot === true) { + state.output += NO_DOTS_SLASH; + prev.output += NO_DOTS_SLASH; + } else { + state.output += nodot; + prev.output += nodot; + } + if (peek() !== "*") { + state.output += ONE_CHAR; + prev.output += ONE_CHAR; + } + } + push(token); + } + while (state.brackets > 0) { + if (opts.strictBrackets === true) throw new SyntaxError(syntaxError("closing", "]")); + state.output = utils.escapeLast(state.output, "["); + decrement("brackets"); + } + while (state.parens > 0) { + if (opts.strictBrackets === true) throw new SyntaxError(syntaxError("closing", ")")); + state.output = utils.escapeLast(state.output, "("); + decrement("parens"); + } + while (state.braces > 0) { + if (opts.strictBrackets === true) throw new SyntaxError(syntaxError("closing", "}")); + state.output = utils.escapeLast(state.output, "{"); + decrement("braces"); + } + if (opts.strictSlashes !== true && (prev.type === "star" || prev.type === "bracket")) { + push({ type: "maybe_slash", value: "", output: `${SLASH_LITERAL}?` }); + } + if (state.backtrack === true) { + state.output = ""; + for (const token of state.tokens) { + state.output += token.output != null ? token.output : token.value; + if (token.suffix) { + state.output += token.suffix; + } + } + } + return state; + }; + parse.fastpaths = (input, options) => { + const opts = { ...options }; + const max = typeof opts.maxLength === "number" ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; + const len = input.length; + if (len > max) { + throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`); + } + input = REPLACEMENTS[input] || input; + const win32 = utils.isWindows(options); + const { + DOT_LITERAL, + SLASH_LITERAL, + ONE_CHAR, + DOTS_SLASH, + NO_DOT, + NO_DOTS, + NO_DOTS_SLASH, + STAR, + START_ANCHOR + } = constants.globChars(win32); + const nodot = opts.dot ? NO_DOTS : NO_DOT; + const slashDot = opts.dot ? NO_DOTS_SLASH : NO_DOT; + const capture = opts.capture ? "" : "?:"; + const state = { negated: false, prefix: "" }; + let star = opts.bash === true ? ".*?" : STAR; + if (opts.capture) { + star = `(${star})`; + } + const globstar = (opts2) => { + if (opts2.noglobstar === true) return star; + return `(${capture}(?:(?!${START_ANCHOR}${opts2.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`; + }; + const create = (str) => { + switch (str) { + case "*": + return `${nodot}${ONE_CHAR}${star}`; + case ".*": + return `${DOT_LITERAL}${ONE_CHAR}${star}`; + case "*.*": + return `${nodot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`; + case "*/*": + return `${nodot}${star}${SLASH_LITERAL}${ONE_CHAR}${slashDot}${star}`; + case "**": + return nodot + globstar(opts); + case "**/*": + return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${ONE_CHAR}${star}`; + case "**/*.*": + return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`; + case "**/.*": + return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${DOT_LITERAL}${ONE_CHAR}${star}`; + default: { + const match = /^(.*?)\.(\w+)$/.exec(str); + if (!match) return; + const source2 = create(match[1]); + if (!source2) return; + return source2 + DOT_LITERAL + match[2]; + } + } + }; + const output = utils.removePrefix(input, state); + let source = create(output); + if (source && opts.strictSlashes !== true) { + source += `${SLASH_LITERAL}?`; + } + return source; + }; + module2.exports = parse; + } +}); + +// node_modules/anymatch/node_modules/picomatch/lib/picomatch.js +var require_picomatch3 = __commonJS({ + "node_modules/anymatch/node_modules/picomatch/lib/picomatch.js"(exports2, module2) { + "use strict"; + var path3 = require("path"); + var scan = require_scan2(); + var parse = require_parse2(); + var utils = require_utils2(); + var constants = require_constants2(); + var isObject = (val) => val && typeof val === "object" && !Array.isArray(val); + var picomatch = (glob, options, returnState = false) => { + if (Array.isArray(glob)) { + const fns = glob.map((input) => picomatch(input, options, returnState)); + const arrayMatcher = (str) => { + for (const isMatch of fns) { + const state2 = isMatch(str); + if (state2) return state2; + } + return false; + }; + return arrayMatcher; + } + const isState = isObject(glob) && glob.tokens && glob.input; + if (glob === "" || typeof glob !== "string" && !isState) { + throw new TypeError("Expected pattern to be a non-empty string"); + } + const opts = options || {}; + const posix = utils.isWindows(options); + const regex = isState ? picomatch.compileRe(glob, options) : picomatch.makeRe(glob, options, false, true); + const state = regex.state; + delete regex.state; + let isIgnored = () => false; + if (opts.ignore) { + const ignoreOpts = { ...options, ignore: null, onMatch: null, onResult: null }; + isIgnored = picomatch(opts.ignore, ignoreOpts, returnState); + } + const matcher = (input, returnObject = false) => { + const { isMatch, match, output } = picomatch.test(input, regex, options, { glob, posix }); + const result = { glob, state, regex, posix, input, output, match, isMatch }; + if (typeof opts.onResult === "function") { + opts.onResult(result); + } + if (isMatch === false) { + result.isMatch = false; + return returnObject ? result : false; + } + if (isIgnored(input)) { + if (typeof opts.onIgnore === "function") { + opts.onIgnore(result); + } + result.isMatch = false; + return returnObject ? result : false; + } + if (typeof opts.onMatch === "function") { + opts.onMatch(result); + } + return returnObject ? result : true; + }; + if (returnState) { + matcher.state = state; + } + return matcher; + }; + picomatch.test = (input, regex, options, { glob, posix } = {}) => { + if (typeof input !== "string") { + throw new TypeError("Expected input to be a string"); + } + if (input === "") { + return { isMatch: false, output: "" }; + } + const opts = options || {}; + const format = opts.format || (posix ? utils.toPosixSlashes : null); + let match = input === glob; + let output = match && format ? format(input) : input; + if (match === false) { + output = format ? format(input) : input; + match = output === glob; + } + if (match === false || opts.capture === true) { + if (opts.matchBase === true || opts.basename === true) { + match = picomatch.matchBase(input, regex, options, posix); + } else { + match = regex.exec(output); + } + } + return { isMatch: Boolean(match), match, output }; + }; + picomatch.matchBase = (input, glob, options, posix = utils.isWindows(options)) => { + const regex = glob instanceof RegExp ? glob : picomatch.makeRe(glob, options); + return regex.test(path3.basename(input)); + }; + picomatch.isMatch = (str, patterns, options) => picomatch(patterns, options)(str); + picomatch.parse = (pattern, options) => { + if (Array.isArray(pattern)) return pattern.map((p) => picomatch.parse(p, options)); + return parse(pattern, { ...options, fastpaths: false }); + }; + picomatch.scan = (input, options) => scan(input, options); + picomatch.compileRe = (state, options, returnOutput = false, returnState = false) => { + if (returnOutput === true) { + return state.output; + } + const opts = options || {}; + const prepend = opts.contains ? "" : "^"; + const append = opts.contains ? "" : "$"; + let source = `${prepend}(?:${state.output})${append}`; + if (state && state.negated === true) { + source = `^(?!${source}).*$`; + } + const regex = picomatch.toRegex(source, options); + if (returnState === true) { + regex.state = state; + } + return regex; + }; + picomatch.makeRe = (input, options = {}, returnOutput = false, returnState = false) => { + if (!input || typeof input !== "string") { + throw new TypeError("Expected a non-empty string"); + } + let parsed = { negated: false, fastpaths: true }; + if (options.fastpaths !== false && (input[0] === "." || input[0] === "*")) { + parsed.output = parse.fastpaths(input, options); + } + if (!parsed.output) { + parsed = parse(input, options); + } + return picomatch.compileRe(parsed, options, returnOutput, returnState); + }; + picomatch.toRegex = (source, options) => { + try { + const opts = options || {}; + return new RegExp(source, opts.flags || (opts.nocase ? "i" : "")); + } catch (err) { + if (options && options.debug === true) throw err; + return /$^/; + } + }; + picomatch.constants = constants; + module2.exports = picomatch; + } +}); + +// node_modules/anymatch/node_modules/picomatch/index.js +var require_picomatch4 = __commonJS({ + "node_modules/anymatch/node_modules/picomatch/index.js"(exports2, module2) { + "use strict"; + module2.exports = require_picomatch3(); + } +}); + +// node_modules/normalize-path/index.js +var require_normalize_path = __commonJS({ + "node_modules/normalize-path/index.js"(exports2, module2) { + module2.exports = function(path3, stripTrailing) { + if (typeof path3 !== "string") { + throw new TypeError("expected path to be a string"); + } + if (path3 === "\\" || path3 === "/") return "/"; + var len = path3.length; + if (len <= 1) return path3; + var prefix = ""; + if (len > 4 && path3[3] === "\\") { + var ch = path3[2]; + if ((ch === "?" || ch === ".") && path3.slice(0, 2) === "\\\\") { + path3 = path3.slice(2); + prefix = "//"; + } + } + var segs = path3.split(/[/\\]+/); + if (stripTrailing !== false && segs[segs.length - 1] === "") { + segs.pop(); + } + return prefix + segs.join("/"); + }; + } +}); + +// node_modules/anymatch/index.js +var require_anymatch = __commonJS({ + "node_modules/anymatch/index.js"(exports2, module2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + var picomatch = require_picomatch4(); + var normalizePath = require_normalize_path(); + var BANG = "!"; + var DEFAULT_OPTIONS = { returnIndex: false }; + var arrify = (item) => Array.isArray(item) ? item : [item]; + var createPattern = (matcher, options) => { + if (typeof matcher === "function") { + return matcher; + } + if (typeof matcher === "string") { + const glob = picomatch(matcher, options); + return (string) => matcher === string || glob(string); + } + if (matcher instanceof RegExp) { + return (string) => matcher.test(string); + } + return (string) => false; + }; + var matchPatterns = (patterns, negPatterns, args, returnIndex) => { + const isList = Array.isArray(args); + const _path = isList ? args[0] : args; + if (!isList && typeof _path !== "string") { + throw new TypeError("anymatch: second argument must be a string: got " + Object.prototype.toString.call(_path)); + } + const path3 = normalizePath(_path, false); + for (let index = 0; index < negPatterns.length; index++) { + const nglob = negPatterns[index]; + if (nglob(path3)) { + return returnIndex ? -1 : false; + } + } + const applied = isList && [path3].concat(args.slice(1)); + for (let index = 0; index < patterns.length; index++) { + const pattern = patterns[index]; + if (isList ? pattern(...applied) : pattern(path3)) { + return returnIndex ? index : true; + } + } + return returnIndex ? -1 : false; + }; + var anymatch = (matchers, testString, options = DEFAULT_OPTIONS) => { + if (matchers == null) { + throw new TypeError("anymatch: specify first argument"); + } + const opts = typeof options === "boolean" ? { returnIndex: options } : options; + const returnIndex = opts.returnIndex || false; + const mtchers = arrify(matchers); + const negatedGlobs = mtchers.filter((item) => typeof item === "string" && item.charAt(0) === BANG).map((item) => item.slice(1)).map((item) => picomatch(item, opts)); + const patterns = mtchers.filter((item) => typeof item !== "string" || typeof item === "string" && item.charAt(0) !== BANG).map((matcher) => createPattern(matcher, opts)); + if (testString == null) { + return (testString2, ri = false) => { + const returnIndex2 = typeof ri === "boolean" ? ri : false; + return matchPatterns(patterns, negatedGlobs, testString2, returnIndex2); + }; + } + return matchPatterns(patterns, negatedGlobs, testString, returnIndex); + }; + anymatch.default = anymatch; + module2.exports = anymatch; + } +}); + +// node_modules/is-extglob/index.js +var require_is_extglob = __commonJS({ + "node_modules/is-extglob/index.js"(exports2, module2) { + module2.exports = function isExtglob(str) { + if (typeof str !== "string" || str === "") { + return false; + } + var match; + while (match = /(\\).|([@?!+*]\(.*\))/g.exec(str)) { + if (match[2]) return true; + str = str.slice(match.index + match[0].length); + } + return false; + }; + } +}); + +// node_modules/is-glob/index.js +var require_is_glob = __commonJS({ + "node_modules/is-glob/index.js"(exports2, module2) { + var isExtglob = require_is_extglob(); + var chars = { "{": "}", "(": ")", "[": "]" }; + var strictCheck = function(str) { + if (str[0] === "!") { + return true; + } + var index = 0; + var pipeIndex = -2; + var closeSquareIndex = -2; + var closeCurlyIndex = -2; + var closeParenIndex = -2; + var backSlashIndex = -2; + while (index < str.length) { + if (str[index] === "*") { + return true; + } + if (str[index + 1] === "?" && /[\].+)]/.test(str[index])) { + return true; + } + if (closeSquareIndex !== -1 && str[index] === "[" && str[index + 1] !== "]") { + if (closeSquareIndex < index) { + closeSquareIndex = str.indexOf("]", index); + } + if (closeSquareIndex > index) { + if (backSlashIndex === -1 || backSlashIndex > closeSquareIndex) { + return true; + } + backSlashIndex = str.indexOf("\\", index); + if (backSlashIndex === -1 || backSlashIndex > closeSquareIndex) { + return true; + } + } + } + if (closeCurlyIndex !== -1 && str[index] === "{" && str[index + 1] !== "}") { + closeCurlyIndex = str.indexOf("}", index); + if (closeCurlyIndex > index) { + backSlashIndex = str.indexOf("\\", index); + if (backSlashIndex === -1 || backSlashIndex > closeCurlyIndex) { + return true; + } + } + } + if (closeParenIndex !== -1 && str[index] === "(" && str[index + 1] === "?" && /[:!=]/.test(str[index + 2]) && str[index + 3] !== ")") { + closeParenIndex = str.indexOf(")", index); + if (closeParenIndex > index) { + backSlashIndex = str.indexOf("\\", index); + if (backSlashIndex === -1 || backSlashIndex > closeParenIndex) { + return true; + } + } + } + if (pipeIndex !== -1 && str[index] === "(" && str[index + 1] !== "|") { + if (pipeIndex < index) { + pipeIndex = str.indexOf("|", index); + } + if (pipeIndex !== -1 && str[pipeIndex + 1] !== ")") { + closeParenIndex = str.indexOf(")", pipeIndex); + if (closeParenIndex > pipeIndex) { + backSlashIndex = str.indexOf("\\", pipeIndex); + if (backSlashIndex === -1 || backSlashIndex > closeParenIndex) { + return true; + } + } + } + } + if (str[index] === "\\") { + var open = str[index + 1]; + index += 2; + var close = chars[open]; + if (close) { + var n = str.indexOf(close, index); + if (n !== -1) { + index = n + 1; + } + } + if (str[index] === "!") { + return true; + } + } else { + index++; + } + } + return false; + }; + var relaxedCheck = function(str) { + if (str[0] === "!") { + return true; + } + var index = 0; + while (index < str.length) { + if (/[*?{}()[\]]/.test(str[index])) { + return true; + } + if (str[index] === "\\") { + var open = str[index + 1]; + index += 2; + var close = chars[open]; + if (close) { + var n = str.indexOf(close, index); + if (n !== -1) { + index = n + 1; + } + } + if (str[index] === "!") { + return true; + } + } else { + index++; + } + } + return false; + }; + module2.exports = function isGlob(str, options) { + if (typeof str !== "string" || str === "") { + return false; + } + if (isExtglob(str)) { + return true; + } + var check = strictCheck; + if (options && options.strict === false) { + check = relaxedCheck; + } + return check(str); + }; + } +}); + +// node_modules/glob-parent/index.js +var require_glob_parent = __commonJS({ + "node_modules/glob-parent/index.js"(exports2, module2) { + "use strict"; + var isGlob = require_is_glob(); + var pathPosixDirname = require("path").posix.dirname; + var isWin32 = require("os").platform() === "win32"; + var slash = "/"; + var backslash = /\\/g; + var enclosure = /[\{\[].*[\}\]]$/; + var globby = /(^|[^\\])([\{\[]|\([^\)]+$)/; + var escaped = /\\([\!\*\?\|\[\]\(\)\{\}])/g; + module2.exports = function globParent(str, opts) { + var options = Object.assign({ flipBackslashes: true }, opts); + if (options.flipBackslashes && isWin32 && str.indexOf(slash) < 0) { + str = str.replace(backslash, slash); + } + if (enclosure.test(str)) { + str += slash; + } + str += "a"; + do { + str = pathPosixDirname(str); + } while (isGlob(str) || globby.test(str)); + return str.replace(escaped, "$1"); + }; + } +}); + +// node_modules/braces/lib/utils.js +var require_utils3 = __commonJS({ + "node_modules/braces/lib/utils.js"(exports2) { + "use strict"; + exports2.isInteger = (num) => { + if (typeof num === "number") { + return Number.isInteger(num); + } + if (typeof num === "string" && num.trim() !== "") { + return Number.isInteger(Number(num)); + } + return false; + }; + exports2.find = (node, type) => node.nodes.find((node2) => node2.type === type); + exports2.exceedsLimit = (min, max, step = 1, limit) => { + if (limit === false) return false; + if (!exports2.isInteger(min) || !exports2.isInteger(max)) return false; + return (Number(max) - Number(min)) / Number(step) >= limit; + }; + exports2.escapeNode = (block, n = 0, type) => { + const node = block.nodes[n]; + if (!node) return; + if (type && node.type === type || node.type === "open" || node.type === "close") { + if (node.escaped !== true) { + node.value = "\\" + node.value; + node.escaped = true; + } + } + }; + exports2.encloseBrace = (node) => { + if (node.type !== "brace") return false; + if (node.commas >> 0 + node.ranges >> 0 === 0) { + node.invalid = true; + return true; + } + return false; + }; + exports2.isInvalidBrace = (block) => { + if (block.type !== "brace") return false; + if (block.invalid === true || block.dollar) return true; + if (block.commas >> 0 + block.ranges >> 0 === 0) { + block.invalid = true; + return true; + } + if (block.open !== true || block.close !== true) { + block.invalid = true; + return true; + } + return false; + }; + exports2.isOpenOrClose = (node) => { + if (node.type === "open" || node.type === "close") { + return true; + } + return node.open === true || node.close === true; + }; + exports2.reduce = (nodes) => nodes.reduce((acc, node) => { + if (node.type === "text") acc.push(node.value); + if (node.type === "range") node.type = "text"; + return acc; + }, []); + exports2.flatten = (...args) => { + const result = []; + const flat = (arr) => { + for (let i = 0; i < arr.length; i++) { + const ele = arr[i]; + if (Array.isArray(ele)) { + flat(ele); + continue; + } + if (ele !== void 0) { + result.push(ele); + } + } + return result; + }; + flat(args); + return result; + }; + } +}); + +// node_modules/braces/lib/stringify.js +var require_stringify = __commonJS({ + "node_modules/braces/lib/stringify.js"(exports2, module2) { + "use strict"; + var utils = require_utils3(); + module2.exports = (ast, options = {}) => { + const stringify = (node, parent = {}) => { + const invalidBlock = options.escapeInvalid && utils.isInvalidBrace(parent); + const invalidNode = node.invalid === true && options.escapeInvalid === true; + let output = ""; + if (node.value) { + if ((invalidBlock || invalidNode) && utils.isOpenOrClose(node)) { + return "\\" + node.value; + } + return node.value; + } + if (node.value) { + return node.value; + } + if (node.nodes) { + for (const child of node.nodes) { + output += stringify(child); + } + } + return output; + }; + return stringify(ast); + }; + } +}); + +// node_modules/is-number/index.js +var require_is_number = __commonJS({ + "node_modules/is-number/index.js"(exports2, module2) { + "use strict"; + module2.exports = function(num) { + if (typeof num === "number") { + return num - num === 0; + } + if (typeof num === "string" && num.trim() !== "") { + return Number.isFinite ? Number.isFinite(+num) : isFinite(+num); + } + return false; + }; + } +}); + +// node_modules/to-regex-range/index.js +var require_to_regex_range = __commonJS({ + "node_modules/to-regex-range/index.js"(exports2, module2) { + "use strict"; + var isNumber = require_is_number(); + var toRegexRange = (min, max, options) => { + if (isNumber(min) === false) { + throw new TypeError("toRegexRange: expected the first argument to be a number"); + } + if (max === void 0 || min === max) { + return String(min); + } + if (isNumber(max) === false) { + throw new TypeError("toRegexRange: expected the second argument to be a number."); + } + let opts = { relaxZeros: true, ...options }; + if (typeof opts.strictZeros === "boolean") { + opts.relaxZeros = opts.strictZeros === false; + } + let relax = String(opts.relaxZeros); + let shorthand = String(opts.shorthand); + let capture = String(opts.capture); + let wrap = String(opts.wrap); + let cacheKey = min + ":" + max + "=" + relax + shorthand + capture + wrap; + if (toRegexRange.cache.hasOwnProperty(cacheKey)) { + return toRegexRange.cache[cacheKey].result; + } + let a = Math.min(min, max); + let b = Math.max(min, max); + if (Math.abs(a - b) === 1) { + let result = min + "|" + max; + if (opts.capture) { + return `(${result})`; + } + if (opts.wrap === false) { + return result; + } + return `(?:${result})`; + } + let isPadded = hasPadding(min) || hasPadding(max); + let state = { min, max, a, b }; + let positives = []; + let negatives = []; + if (isPadded) { + state.isPadded = isPadded; + state.maxLen = String(state.max).length; + } + if (a < 0) { + let newMin = b < 0 ? Math.abs(b) : 1; + negatives = splitToPatterns(newMin, Math.abs(a), state, opts); + a = state.a = 0; + } + if (b >= 0) { + positives = splitToPatterns(a, b, state, opts); + } + state.negatives = negatives; + state.positives = positives; + state.result = collatePatterns(negatives, positives, opts); + if (opts.capture === true) { + state.result = `(${state.result})`; + } else if (opts.wrap !== false && positives.length + negatives.length > 1) { + state.result = `(?:${state.result})`; + } + toRegexRange.cache[cacheKey] = state; + return state.result; + }; + function collatePatterns(neg, pos, options) { + let onlyNegative = filterPatterns(neg, pos, "-", false, options) || []; + let onlyPositive = filterPatterns(pos, neg, "", false, options) || []; + let intersected = filterPatterns(neg, pos, "-?", true, options) || []; + let subpatterns = onlyNegative.concat(intersected).concat(onlyPositive); + return subpatterns.join("|"); + } + function splitToRanges(min, max) { + let nines = 1; + let zeros = 1; + let stop = countNines(min, nines); + let stops = /* @__PURE__ */ new Set([max]); + while (min <= stop && stop <= max) { + stops.add(stop); + nines += 1; + stop = countNines(min, nines); + } + stop = countZeros(max + 1, zeros) - 1; + while (min < stop && stop <= max) { + stops.add(stop); + zeros += 1; + stop = countZeros(max + 1, zeros) - 1; + } + stops = [...stops]; + stops.sort(compare); + return stops; + } + function rangeToPattern(start, stop, options) { + if (start === stop) { + return { pattern: start, count: [], digits: 0 }; + } + let zipped = zip(start, stop); + let digits = zipped.length; + let pattern = ""; + let count = 0; + for (let i = 0; i < digits; i++) { + let [startDigit, stopDigit] = zipped[i]; + if (startDigit === stopDigit) { + pattern += startDigit; + } else if (startDigit !== "0" || stopDigit !== "9") { + pattern += toCharacterClass(startDigit, stopDigit, options); + } else { + count++; + } + } + if (count) { + pattern += options.shorthand === true ? "\\d" : "[0-9]"; + } + return { pattern, count: [count], digits }; + } + function splitToPatterns(min, max, tok, options) { + let ranges = splitToRanges(min, max); + let tokens = []; + let start = min; + let prev; + for (let i = 0; i < ranges.length; i++) { + let max2 = ranges[i]; + let obj = rangeToPattern(String(start), String(max2), options); + let zeros = ""; + if (!tok.isPadded && prev && prev.pattern === obj.pattern) { + if (prev.count.length > 1) { + prev.count.pop(); + } + prev.count.push(obj.count[0]); + prev.string = prev.pattern + toQuantifier(prev.count); + start = max2 + 1; + continue; + } + if (tok.isPadded) { + zeros = padZeros(max2, tok, options); + } + obj.string = zeros + obj.pattern + toQuantifier(obj.count); + tokens.push(obj); + start = max2 + 1; + prev = obj; + } + return tokens; + } + function filterPatterns(arr, comparison, prefix, intersection, options) { + let result = []; + for (let ele of arr) { + let { string } = ele; + if (!intersection && !contains(comparison, "string", string)) { + result.push(prefix + string); + } + if (intersection && contains(comparison, "string", string)) { + result.push(prefix + string); + } + } + return result; + } + function zip(a, b) { + let arr = []; + for (let i = 0; i < a.length; i++) arr.push([a[i], b[i]]); + return arr; + } + function compare(a, b) { + return a > b ? 1 : b > a ? -1 : 0; + } + function contains(arr, key, val) { + return arr.some((ele) => ele[key] === val); + } + function countNines(min, len) { + return Number(String(min).slice(0, -len) + "9".repeat(len)); + } + function countZeros(integer, zeros) { + return integer - integer % Math.pow(10, zeros); + } + function toQuantifier(digits) { + let [start = 0, stop = ""] = digits; + if (stop || start > 1) { + return `{${start + (stop ? "," + stop : "")}}`; + } + return ""; + } + function toCharacterClass(a, b, options) { + return `[${a}${b - a === 1 ? "" : "-"}${b}]`; + } + function hasPadding(str) { + return /^-?(0+)\d/.test(str); + } + function padZeros(value, tok, options) { + if (!tok.isPadded) { + return value; + } + let diff = Math.abs(tok.maxLen - String(value).length); + let relax = options.relaxZeros !== false; + switch (diff) { + case 0: + return ""; + case 1: + return relax ? "0?" : "0"; + case 2: + return relax ? "0{0,2}" : "00"; + default: { + return relax ? `0{0,${diff}}` : `0{${diff}}`; + } + } + } + toRegexRange.cache = {}; + toRegexRange.clearCache = () => toRegexRange.cache = {}; + module2.exports = toRegexRange; + } +}); + +// node_modules/fill-range/index.js +var require_fill_range = __commonJS({ + "node_modules/fill-range/index.js"(exports2, module2) { + "use strict"; + var util = require("util"); + var toRegexRange = require_to_regex_range(); + var isObject = (val) => val !== null && typeof val === "object" && !Array.isArray(val); + var transform = (toNumber) => { + return (value) => toNumber === true ? Number(value) : String(value); + }; + var isValidValue = (value) => { + return typeof value === "number" || typeof value === "string" && value !== ""; + }; + var isNumber = (num) => Number.isInteger(+num); + var zeros = (input) => { + let value = `${input}`; + let index = -1; + if (value[0] === "-") value = value.slice(1); + if (value === "0") return false; + while (value[++index] === "0") ; + return index > 0; + }; + var stringify = (start, end, options) => { + if (typeof start === "string" || typeof end === "string") { + return true; + } + return options.stringify === true; + }; + var pad = (input, maxLength, toNumber) => { + if (maxLength > 0) { + let dash = input[0] === "-" ? "-" : ""; + if (dash) input = input.slice(1); + input = dash + input.padStart(dash ? maxLength - 1 : maxLength, "0"); + } + if (toNumber === false) { + return String(input); + } + return input; + }; + var toMaxLen = (input, maxLength) => { + let negative = input[0] === "-" ? "-" : ""; + if (negative) { + input = input.slice(1); + maxLength--; + } + while (input.length < maxLength) input = "0" + input; + return negative ? "-" + input : input; + }; + var toSequence = (parts, options, maxLen) => { + parts.negatives.sort((a, b) => a < b ? -1 : a > b ? 1 : 0); + parts.positives.sort((a, b) => a < b ? -1 : a > b ? 1 : 0); + let prefix = options.capture ? "" : "?:"; + let positives = ""; + let negatives = ""; + let result; + if (parts.positives.length) { + positives = parts.positives.map((v) => toMaxLen(String(v), maxLen)).join("|"); + } + if (parts.negatives.length) { + negatives = `-(${prefix}${parts.negatives.map((v) => toMaxLen(String(v), maxLen)).join("|")})`; + } + if (positives && negatives) { + result = `${positives}|${negatives}`; + } else { + result = positives || negatives; + } + if (options.wrap) { + return `(${prefix}${result})`; + } + return result; + }; + var toRange = (a, b, isNumbers, options) => { + if (isNumbers) { + return toRegexRange(a, b, { wrap: false, ...options }); + } + let start = String.fromCharCode(a); + if (a === b) return start; + let stop = String.fromCharCode(b); + return `[${start}-${stop}]`; + }; + var toRegex = (start, end, options) => { + if (Array.isArray(start)) { + let wrap = options.wrap === true; + let prefix = options.capture ? "" : "?:"; + return wrap ? `(${prefix}${start.join("|")})` : start.join("|"); + } + return toRegexRange(start, end, options); + }; + var rangeError = (...args) => { + return new RangeError("Invalid range arguments: " + util.inspect(...args)); + }; + var invalidRange = (start, end, options) => { + if (options.strictRanges === true) throw rangeError([start, end]); + return []; + }; + var invalidStep = (step, options) => { + if (options.strictRanges === true) { + throw new TypeError(`Expected step "${step}" to be a number`); + } + return []; + }; + var fillNumbers = (start, end, step = 1, options = {}) => { + let a = Number(start); + let b = Number(end); + if (!Number.isInteger(a) || !Number.isInteger(b)) { + if (options.strictRanges === true) throw rangeError([start, end]); + return []; + } + if (a === 0) a = 0; + if (b === 0) b = 0; + let descending = a > b; + let startString = String(start); + let endString = String(end); + let stepString = String(step); + step = Math.max(Math.abs(step), 1); + let padded = zeros(startString) || zeros(endString) || zeros(stepString); + let maxLen = padded ? Math.max(startString.length, endString.length, stepString.length) : 0; + let toNumber = padded === false && stringify(start, end, options) === false; + let format = options.transform || transform(toNumber); + if (options.toRegex && step === 1) { + return toRange(toMaxLen(start, maxLen), toMaxLen(end, maxLen), true, options); + } + let parts = { negatives: [], positives: [] }; + let push = (num) => parts[num < 0 ? "negatives" : "positives"].push(Math.abs(num)); + let range = []; + let index = 0; + while (descending ? a >= b : a <= b) { + if (options.toRegex === true && step > 1) { + push(a); + } else { + range.push(pad(format(a, index), maxLen, toNumber)); + } + a = descending ? a - step : a + step; + index++; + } + if (options.toRegex === true) { + return step > 1 ? toSequence(parts, options, maxLen) : toRegex(range, null, { wrap: false, ...options }); + } + return range; + }; + var fillLetters = (start, end, step = 1, options = {}) => { + if (!isNumber(start) && start.length > 1 || !isNumber(end) && end.length > 1) { + return invalidRange(start, end, options); + } + let format = options.transform || ((val) => String.fromCharCode(val)); + let a = `${start}`.charCodeAt(0); + let b = `${end}`.charCodeAt(0); + let descending = a > b; + let min = Math.min(a, b); + let max = Math.max(a, b); + if (options.toRegex && step === 1) { + return toRange(min, max, false, options); + } + let range = []; + let index = 0; + while (descending ? a >= b : a <= b) { + range.push(format(a, index)); + a = descending ? a - step : a + step; + index++; + } + if (options.toRegex === true) { + return toRegex(range, null, { wrap: false, options }); + } + return range; + }; + var fill = (start, end, step, options = {}) => { + if (end == null && isValidValue(start)) { + return [start]; + } + if (!isValidValue(start) || !isValidValue(end)) { + return invalidRange(start, end, options); + } + if (typeof step === "function") { + return fill(start, end, 1, { transform: step }); + } + if (isObject(step)) { + return fill(start, end, 0, step); + } + let opts = { ...options }; + if (opts.capture === true) opts.wrap = true; + step = step || opts.step || 1; + if (!isNumber(step)) { + if (step != null && !isObject(step)) return invalidStep(step, opts); + return fill(start, end, 1, step); + } + if (isNumber(start) && isNumber(end)) { + return fillNumbers(start, end, step, opts); + } + return fillLetters(start, end, Math.max(Math.abs(step), 1), opts); + }; + module2.exports = fill; + } +}); + +// node_modules/braces/lib/compile.js +var require_compile = __commonJS({ + "node_modules/braces/lib/compile.js"(exports2, module2) { + "use strict"; + var fill = require_fill_range(); + var utils = require_utils3(); + var compile = (ast, options = {}) => { + const walk = (node, parent = {}) => { + const invalidBlock = utils.isInvalidBrace(parent); + const invalidNode = node.invalid === true && options.escapeInvalid === true; + const invalid = invalidBlock === true || invalidNode === true; + const prefix = options.escapeInvalid === true ? "\\" : ""; + let output = ""; + if (node.isOpen === true) { + return prefix + node.value; + } + if (node.isClose === true) { + console.log("node.isClose", prefix, node.value); + return prefix + node.value; + } + if (node.type === "open") { + return invalid ? prefix + node.value : "("; + } + if (node.type === "close") { + return invalid ? prefix + node.value : ")"; + } + if (node.type === "comma") { + return node.prev.type === "comma" ? "" : invalid ? node.value : "|"; + } + if (node.value) { + return node.value; + } + if (node.nodes && node.ranges > 0) { + const args = utils.reduce(node.nodes); + const range = fill(...args, { ...options, wrap: false, toRegex: true, strictZeros: true }); + if (range.length !== 0) { + return args.length > 1 && range.length > 1 ? `(${range})` : range; + } + } + if (node.nodes) { + for (const child of node.nodes) { + output += walk(child, node); + } + } + return output; + }; + return walk(ast); + }; + module2.exports = compile; + } +}); + +// node_modules/braces/lib/expand.js +var require_expand = __commonJS({ + "node_modules/braces/lib/expand.js"(exports2, module2) { + "use strict"; + var fill = require_fill_range(); + var stringify = require_stringify(); + var utils = require_utils3(); + var append = (queue = "", stash = "", enclose = false) => { + const result = []; + queue = [].concat(queue); + stash = [].concat(stash); + if (!stash.length) return queue; + if (!queue.length) { + return enclose ? utils.flatten(stash).map((ele) => `{${ele}}`) : stash; + } + for (const item of queue) { + if (Array.isArray(item)) { + for (const value of item) { + result.push(append(value, stash, enclose)); + } + } else { + for (let ele of stash) { + if (enclose === true && typeof ele === "string") ele = `{${ele}}`; + result.push(Array.isArray(ele) ? append(item, ele, enclose) : item + ele); + } + } + } + return utils.flatten(result); + }; + var expand = (ast, options = {}) => { + const rangeLimit = options.rangeLimit === void 0 ? 1e3 : options.rangeLimit; + const walk = (node, parent = {}) => { + node.queue = []; + let p = parent; + let q = parent.queue; + while (p.type !== "brace" && p.type !== "root" && p.parent) { + p = p.parent; + q = p.queue; + } + if (node.invalid || node.dollar) { + q.push(append(q.pop(), stringify(node, options))); + return; + } + if (node.type === "brace" && node.invalid !== true && node.nodes.length === 2) { + q.push(append(q.pop(), ["{}"])); + return; + } + if (node.nodes && node.ranges > 0) { + const args = utils.reduce(node.nodes); + if (utils.exceedsLimit(...args, options.step, rangeLimit)) { + throw new RangeError("expanded array length exceeds range limit. Use options.rangeLimit to increase or disable the limit."); + } + let range = fill(...args, options); + if (range.length === 0) { + range = stringify(node, options); + } + q.push(append(q.pop(), range)); + node.nodes = []; + return; + } + const enclose = utils.encloseBrace(node); + let queue = node.queue; + let block = node; + while (block.type !== "brace" && block.type !== "root" && block.parent) { + block = block.parent; + queue = block.queue; + } + for (let i = 0; i < node.nodes.length; i++) { + const child = node.nodes[i]; + if (child.type === "comma" && node.type === "brace") { + if (i === 1) queue.push(""); + queue.push(""); + continue; + } + if (child.type === "close") { + q.push(append(q.pop(), queue, enclose)); + continue; + } + if (child.value && child.type !== "open") { + queue.push(append(queue.pop(), child.value)); + continue; + } + if (child.nodes) { + walk(child, node); + } + } + return queue; + }; + return utils.flatten(walk(ast)); + }; + module2.exports = expand; + } +}); + +// node_modules/braces/lib/constants.js +var require_constants3 = __commonJS({ + "node_modules/braces/lib/constants.js"(exports2, module2) { + "use strict"; + module2.exports = { + MAX_LENGTH: 1e4, + // Digits + CHAR_0: "0", + /* 0 */ + CHAR_9: "9", + /* 9 */ + // Alphabet chars. + CHAR_UPPERCASE_A: "A", + /* A */ + CHAR_LOWERCASE_A: "a", + /* a */ + CHAR_UPPERCASE_Z: "Z", + /* Z */ + CHAR_LOWERCASE_Z: "z", + /* z */ + CHAR_LEFT_PARENTHESES: "(", + /* ( */ + CHAR_RIGHT_PARENTHESES: ")", + /* ) */ + CHAR_ASTERISK: "*", + /* * */ + // Non-alphabetic chars. + CHAR_AMPERSAND: "&", + /* & */ + CHAR_AT: "@", + /* @ */ + CHAR_BACKSLASH: "\\", + /* \ */ + CHAR_BACKTICK: "`", + /* ` */ + CHAR_CARRIAGE_RETURN: "\r", + /* \r */ + CHAR_CIRCUMFLEX_ACCENT: "^", + /* ^ */ + CHAR_COLON: ":", + /* : */ + CHAR_COMMA: ",", + /* , */ + CHAR_DOLLAR: "$", + /* . */ + CHAR_DOT: ".", + /* . */ + CHAR_DOUBLE_QUOTE: '"', + /* " */ + CHAR_EQUAL: "=", + /* = */ + CHAR_EXCLAMATION_MARK: "!", + /* ! */ + CHAR_FORM_FEED: "\f", + /* \f */ + CHAR_FORWARD_SLASH: "/", + /* / */ + CHAR_HASH: "#", + /* # */ + CHAR_HYPHEN_MINUS: "-", + /* - */ + CHAR_LEFT_ANGLE_BRACKET: "<", + /* < */ + CHAR_LEFT_CURLY_BRACE: "{", + /* { */ + CHAR_LEFT_SQUARE_BRACKET: "[", + /* [ */ + CHAR_LINE_FEED: "\n", + /* \n */ + CHAR_NO_BREAK_SPACE: "\xA0", + /* \u00A0 */ + CHAR_PERCENT: "%", + /* % */ + CHAR_PLUS: "+", + /* + */ + CHAR_QUESTION_MARK: "?", + /* ? */ + CHAR_RIGHT_ANGLE_BRACKET: ">", + /* > */ + CHAR_RIGHT_CURLY_BRACE: "}", + /* } */ + CHAR_RIGHT_SQUARE_BRACKET: "]", + /* ] */ + CHAR_SEMICOLON: ";", + /* ; */ + CHAR_SINGLE_QUOTE: "'", + /* ' */ + CHAR_SPACE: " ", + /* */ + CHAR_TAB: " ", + /* \t */ + CHAR_UNDERSCORE: "_", + /* _ */ + CHAR_VERTICAL_LINE: "|", + /* | */ + CHAR_ZERO_WIDTH_NOBREAK_SPACE: "\uFEFF" + /* \uFEFF */ + }; + } +}); + +// node_modules/braces/lib/parse.js +var require_parse3 = __commonJS({ + "node_modules/braces/lib/parse.js"(exports2, module2) { + "use strict"; + var stringify = require_stringify(); + var { + MAX_LENGTH, + CHAR_BACKSLASH, + /* \ */ + CHAR_BACKTICK, + /* ` */ + CHAR_COMMA, + /* , */ + CHAR_DOT, + /* . */ + CHAR_LEFT_PARENTHESES, + /* ( */ + CHAR_RIGHT_PARENTHESES, + /* ) */ + CHAR_LEFT_CURLY_BRACE, + /* { */ + CHAR_RIGHT_CURLY_BRACE, + /* } */ + CHAR_LEFT_SQUARE_BRACKET, + /* [ */ + CHAR_RIGHT_SQUARE_BRACKET, + /* ] */ + CHAR_DOUBLE_QUOTE, + /* " */ + CHAR_SINGLE_QUOTE, + /* ' */ + CHAR_NO_BREAK_SPACE, + CHAR_ZERO_WIDTH_NOBREAK_SPACE + } = require_constants3(); + var parse = (input, options = {}) => { + if (typeof input !== "string") { + throw new TypeError("Expected a string"); + } + const opts = options || {}; + const max = typeof opts.maxLength === "number" ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; + if (input.length > max) { + throw new SyntaxError(`Input length (${input.length}), exceeds max characters (${max})`); + } + const ast = { type: "root", input, nodes: [] }; + const stack = [ast]; + let block = ast; + let prev = ast; + let brackets = 0; + const length = input.length; + let index = 0; + let depth = 0; + let value; + const advance = () => input[index++]; + const push = (node) => { + if (node.type === "text" && prev.type === "dot") { + prev.type = "text"; + } + if (prev && prev.type === "text" && node.type === "text") { + prev.value += node.value; + return; + } + block.nodes.push(node); + node.parent = block; + node.prev = prev; + prev = node; + return node; + }; + push({ type: "bos" }); + while (index < length) { + block = stack[stack.length - 1]; + value = advance(); + if (value === CHAR_ZERO_WIDTH_NOBREAK_SPACE || value === CHAR_NO_BREAK_SPACE) { + continue; + } + if (value === CHAR_BACKSLASH) { + push({ type: "text", value: (options.keepEscaping ? value : "") + advance() }); + continue; + } + if (value === CHAR_RIGHT_SQUARE_BRACKET) { + push({ type: "text", value: "\\" + value }); + continue; + } + if (value === CHAR_LEFT_SQUARE_BRACKET) { + brackets++; + let next; + while (index < length && (next = advance())) { + value += next; + if (next === CHAR_LEFT_SQUARE_BRACKET) { + brackets++; + continue; + } + if (next === CHAR_BACKSLASH) { + value += advance(); + continue; + } + if (next === CHAR_RIGHT_SQUARE_BRACKET) { + brackets--; + if (brackets === 0) { + break; + } + } + } + push({ type: "text", value }); + continue; + } + if (value === CHAR_LEFT_PARENTHESES) { + block = push({ type: "paren", nodes: [] }); + stack.push(block); + push({ type: "text", value }); + continue; + } + if (value === CHAR_RIGHT_PARENTHESES) { + if (block.type !== "paren") { + push({ type: "text", value }); + continue; + } + block = stack.pop(); + push({ type: "text", value }); + block = stack[stack.length - 1]; + continue; + } + if (value === CHAR_DOUBLE_QUOTE || value === CHAR_SINGLE_QUOTE || value === CHAR_BACKTICK) { + const open = value; + let next; + if (options.keepQuotes !== true) { + value = ""; + } + while (index < length && (next = advance())) { + if (next === CHAR_BACKSLASH) { + value += next + advance(); + continue; + } + if (next === open) { + if (options.keepQuotes === true) value += next; + break; + } + value += next; + } + push({ type: "text", value }); + continue; + } + if (value === CHAR_LEFT_CURLY_BRACE) { + depth++; + const dollar = prev.value && prev.value.slice(-1) === "$" || block.dollar === true; + const brace = { + type: "brace", + open: true, + close: false, + dollar, + depth, + commas: 0, + ranges: 0, + nodes: [] + }; + block = push(brace); + stack.push(block); + push({ type: "open", value }); + continue; + } + if (value === CHAR_RIGHT_CURLY_BRACE) { + if (block.type !== "brace") { + push({ type: "text", value }); + continue; + } + const type = "close"; + block = stack.pop(); + block.close = true; + push({ type, value }); + depth--; + block = stack[stack.length - 1]; + continue; + } + if (value === CHAR_COMMA && depth > 0) { + if (block.ranges > 0) { + block.ranges = 0; + const open = block.nodes.shift(); + block.nodes = [open, { type: "text", value: stringify(block) }]; + } + push({ type: "comma", value }); + block.commas++; + continue; + } + if (value === CHAR_DOT && depth > 0 && block.commas === 0) { + const siblings = block.nodes; + if (depth === 0 || siblings.length === 0) { + push({ type: "text", value }); + continue; + } + if (prev.type === "dot") { + block.range = []; + prev.value += value; + prev.type = "range"; + if (block.nodes.length !== 3 && block.nodes.length !== 5) { + block.invalid = true; + block.ranges = 0; + prev.type = "text"; + continue; + } + block.ranges++; + block.args = []; + continue; + } + if (prev.type === "range") { + siblings.pop(); + const before = siblings[siblings.length - 1]; + before.value += prev.value + value; + prev = before; + block.ranges--; + continue; + } + push({ type: "dot", value }); + continue; + } + push({ type: "text", value }); + } + do { + block = stack.pop(); + if (block.type !== "root") { + block.nodes.forEach((node) => { + if (!node.nodes) { + if (node.type === "open") node.isOpen = true; + if (node.type === "close") node.isClose = true; + if (!node.nodes) node.type = "text"; + node.invalid = true; + } + }); + const parent = stack[stack.length - 1]; + const index2 = parent.nodes.indexOf(block); + parent.nodes.splice(index2, 1, ...block.nodes); + } + } while (stack.length > 0); + push({ type: "eos" }); + return ast; + }; + module2.exports = parse; + } +}); + +// node_modules/braces/index.js +var require_braces = __commonJS({ + "node_modules/braces/index.js"(exports2, module2) { + "use strict"; + var stringify = require_stringify(); + var compile = require_compile(); + var expand = require_expand(); + var parse = require_parse3(); + var braces = (input, options = {}) => { + let output = []; + if (Array.isArray(input)) { + for (const pattern of input) { + const result = braces.create(pattern, options); + if (Array.isArray(result)) { + output.push(...result); + } else { + output.push(result); + } + } + } else { + output = [].concat(braces.create(input, options)); + } + if (options && options.expand === true && options.nodupes === true) { + output = [...new Set(output)]; + } + return output; + }; + braces.parse = (input, options = {}) => parse(input, options); + braces.stringify = (input, options = {}) => { + if (typeof input === "string") { + return stringify(braces.parse(input, options), options); + } + return stringify(input, options); + }; + braces.compile = (input, options = {}) => { + if (typeof input === "string") { + input = braces.parse(input, options); + } + return compile(input, options); + }; + braces.expand = (input, options = {}) => { + if (typeof input === "string") { + input = braces.parse(input, options); + } + let result = expand(input, options); + if (options.noempty === true) { + result = result.filter(Boolean); + } + if (options.nodupes === true) { + result = [...new Set(result)]; + } + return result; + }; + braces.create = (input, options = {}) => { + if (input === "" || input.length < 3) { + return [input]; + } + return options.expand !== true ? braces.compile(input, options) : braces.expand(input, options); + }; + module2.exports = braces; + } +}); + +// node_modules/binary-extensions/binary-extensions.json +var require_binary_extensions = __commonJS({ + "node_modules/binary-extensions/binary-extensions.json"(exports2, module2) { + module2.exports = [ + "3dm", + "3ds", + "3g2", + "3gp", + "7z", + "a", + "aac", + "adp", + "afdesign", + "afphoto", + "afpub", + "ai", + "aif", + "aiff", + "alz", + "ape", + "apk", + "appimage", + "ar", + "arj", + "asf", + "au", + "avi", + "bak", + "baml", + "bh", + "bin", + "bk", + "bmp", + "btif", + "bz2", + "bzip2", + "cab", + "caf", + "cgm", + "class", + "cmx", + "cpio", + "cr2", + "cur", + "dat", + "dcm", + "deb", + "dex", + "djvu", + "dll", + "dmg", + "dng", + "doc", + "docm", + "docx", + "dot", + "dotm", + "dra", + "DS_Store", + "dsk", + "dts", + "dtshd", + "dvb", + "dwg", + "dxf", + "ecelp4800", + "ecelp7470", + "ecelp9600", + "egg", + "eol", + "eot", + "epub", + "exe", + "f4v", + "fbs", + "fh", + "fla", + "flac", + "flatpak", + "fli", + "flv", + "fpx", + "fst", + "fvt", + "g3", + "gh", + "gif", + "graffle", + "gz", + "gzip", + "h261", + "h263", + "h264", + "icns", + "ico", + "ief", + "img", + "ipa", + "iso", + "jar", + "jpeg", + "jpg", + "jpgv", + "jpm", + "jxr", + "key", + "ktx", + "lha", + "lib", + "lvp", + "lz", + "lzh", + "lzma", + "lzo", + "m3u", + "m4a", + "m4v", + "mar", + "mdi", + "mht", + "mid", + "midi", + "mj2", + "mka", + "mkv", + "mmr", + "mng", + "mobi", + "mov", + "movie", + "mp3", + "mp4", + "mp4a", + "mpeg", + "mpg", + "mpga", + "mxu", + "nef", + "npx", + "numbers", + "nupkg", + "o", + "odp", + "ods", + "odt", + "oga", + "ogg", + "ogv", + "otf", + "ott", + "pages", + "pbm", + "pcx", + "pdb", + "pdf", + "pea", + "pgm", + "pic", + "png", + "pnm", + "pot", + "potm", + "potx", + "ppa", + "ppam", + "ppm", + "pps", + "ppsm", + "ppsx", + "ppt", + "pptm", + "pptx", + "psd", + "pya", + "pyc", + "pyo", + "pyv", + "qt", + "rar", + "ras", + "raw", + "resources", + "rgb", + "rip", + "rlc", + "rmf", + "rmvb", + "rpm", + "rtf", + "rz", + "s3m", + "s7z", + "scpt", + "sgi", + "shar", + "snap", + "sil", + "sketch", + "slk", + "smv", + "snk", + "so", + "stl", + "suo", + "sub", + "swf", + "tar", + "tbz", + "tbz2", + "tga", + "tgz", + "thmx", + "tif", + "tiff", + "tlz", + "ttc", + "ttf", + "txz", + "udf", + "uvh", + "uvi", + "uvm", + "uvp", + "uvs", + "uvu", + "viv", + "vob", + "war", + "wav", + "wax", + "wbmp", + "wdp", + "weba", + "webm", + "webp", + "whl", + "wim", + "wm", + "wma", + "wmv", + "wmx", + "woff", + "woff2", + "wrm", + "wvx", + "xbm", + "xif", + "xla", + "xlam", + "xls", + "xlsb", + "xlsm", + "xlsx", + "xlt", + "xltm", + "xltx", + "xm", + "xmind", + "xpi", + "xpm", + "xwd", + "xz", + "z", + "zip", + "zipx" + ]; + } +}); + +// node_modules/binary-extensions/index.js +var require_binary_extensions2 = __commonJS({ + "node_modules/binary-extensions/index.js"(exports2, module2) { + module2.exports = require_binary_extensions(); + } +}); + +// node_modules/is-binary-path/index.js +var require_is_binary_path = __commonJS({ + "node_modules/is-binary-path/index.js"(exports2, module2) { + "use strict"; + var path3 = require("path"); + var binaryExtensions = require_binary_extensions2(); + var extensions = new Set(binaryExtensions); + module2.exports = (filePath) => extensions.has(path3.extname(filePath).slice(1).toLowerCase()); + } +}); + +// node_modules/chokidar/lib/constants.js +var require_constants4 = __commonJS({ + "node_modules/chokidar/lib/constants.js"(exports2) { + "use strict"; + var { sep } = require("path"); + var { platform } = process; + var os2 = require("os"); + exports2.EV_ALL = "all"; + exports2.EV_READY = "ready"; + exports2.EV_ADD = "add"; + exports2.EV_CHANGE = "change"; + exports2.EV_ADD_DIR = "addDir"; + exports2.EV_UNLINK = "unlink"; + exports2.EV_UNLINK_DIR = "unlinkDir"; + exports2.EV_RAW = "raw"; + exports2.EV_ERROR = "error"; + exports2.STR_DATA = "data"; + exports2.STR_END = "end"; + exports2.STR_CLOSE = "close"; + exports2.FSEVENT_CREATED = "created"; + exports2.FSEVENT_MODIFIED = "modified"; + exports2.FSEVENT_DELETED = "deleted"; + exports2.FSEVENT_MOVED = "moved"; + exports2.FSEVENT_CLONED = "cloned"; + exports2.FSEVENT_UNKNOWN = "unknown"; + exports2.FSEVENT_FLAG_MUST_SCAN_SUBDIRS = 1; + exports2.FSEVENT_TYPE_FILE = "file"; + exports2.FSEVENT_TYPE_DIRECTORY = "directory"; + exports2.FSEVENT_TYPE_SYMLINK = "symlink"; + exports2.KEY_LISTENERS = "listeners"; + exports2.KEY_ERR = "errHandlers"; + exports2.KEY_RAW = "rawEmitters"; + exports2.HANDLER_KEYS = [exports2.KEY_LISTENERS, exports2.KEY_ERR, exports2.KEY_RAW]; + exports2.DOT_SLASH = `.${sep}`; + exports2.BACK_SLASH_RE = /\\/g; + exports2.DOUBLE_SLASH_RE = /\/\//; + exports2.SLASH_OR_BACK_SLASH_RE = /[/\\]/; + exports2.DOT_RE = /\..*\.(sw[px])$|~$|\.subl.*\.tmp/; + exports2.REPLACER_RE = /^\.[/\\]/; + exports2.SLASH = "/"; + exports2.SLASH_SLASH = "//"; + exports2.BRACE_START = "{"; + exports2.BANG = "!"; + exports2.ONE_DOT = "."; + exports2.TWO_DOTS = ".."; + exports2.STAR = "*"; + exports2.GLOBSTAR = "**"; + exports2.ROOT_GLOBSTAR = "/**/*"; + exports2.SLASH_GLOBSTAR = "/**"; + exports2.DIR_SUFFIX = "Dir"; + exports2.ANYMATCH_OPTS = { dot: true }; + exports2.STRING_TYPE = "string"; + exports2.FUNCTION_TYPE = "function"; + exports2.EMPTY_STR = ""; + exports2.EMPTY_FN = () => { + }; + exports2.IDENTITY_FN = (val) => val; + exports2.isWindows = platform === "win32"; + exports2.isMacos = platform === "darwin"; + exports2.isLinux = platform === "linux"; + exports2.isIBMi = os2.type() === "OS400"; + } +}); + +// node_modules/chokidar/lib/nodefs-handler.js +var require_nodefs_handler = __commonJS({ + "node_modules/chokidar/lib/nodefs-handler.js"(exports2, module2) { + "use strict"; + var fs2 = require("fs"); + var sysPath = require("path"); + var { promisify } = require("util"); + var isBinaryPath = require_is_binary_path(); + var { + isWindows, + isLinux, + EMPTY_FN, + EMPTY_STR, + KEY_LISTENERS, + KEY_ERR, + KEY_RAW, + HANDLER_KEYS, + EV_CHANGE, + EV_ADD, + EV_ADD_DIR, + EV_ERROR, + STR_DATA, + STR_END, + BRACE_START, + STAR + } = require_constants4(); + var THROTTLE_MODE_WATCH = "watch"; + var open = promisify(fs2.open); + var stat = promisify(fs2.stat); + var lstat = promisify(fs2.lstat); + var close = promisify(fs2.close); + var fsrealpath = promisify(fs2.realpath); + var statMethods = { lstat, stat }; + var foreach = (val, fn) => { + if (val instanceof Set) { + val.forEach(fn); + } else { + fn(val); + } + }; + var addAndConvert = (main, prop, item) => { + let container = main[prop]; + if (!(container instanceof Set)) { + main[prop] = container = /* @__PURE__ */ new Set([container]); + } + container.add(item); + }; + var clearItem = (cont) => (key) => { + const set = cont[key]; + if (set instanceof Set) { + set.clear(); + } else { + delete cont[key]; + } + }; + var delFromSet = (main, prop, item) => { + const container = main[prop]; + if (container instanceof Set) { + container.delete(item); + } else if (container === item) { + delete main[prop]; + } + }; + var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val; + var FsWatchInstances = /* @__PURE__ */ new Map(); + function createFsWatchInstance(path3, options, listener, errHandler, emitRaw) { + const handleEvent = (rawEvent, evPath) => { + listener(path3); + emitRaw(rawEvent, evPath, { watchedPath: path3 }); + if (evPath && path3 !== evPath) { + fsWatchBroadcast( + sysPath.resolve(path3, evPath), + KEY_LISTENERS, + sysPath.join(path3, evPath) + ); + } + }; + try { + return fs2.watch(path3, options, handleEvent); + } catch (error) { + errHandler(error); + } + } + var fsWatchBroadcast = (fullPath, type, val1, val2, val3) => { + const cont = FsWatchInstances.get(fullPath); + if (!cont) return; + foreach(cont[type], (listener) => { + listener(val1, val2, val3); + }); + }; + var setFsWatchListener = (path3, fullPath, options, handlers) => { + const { listener, errHandler, rawEmitter } = handlers; + let cont = FsWatchInstances.get(fullPath); + let watcher; + if (!options.persistent) { + watcher = createFsWatchInstance( + path3, + options, + listener, + errHandler, + rawEmitter + ); + return watcher.close.bind(watcher); + } + if (cont) { + addAndConvert(cont, KEY_LISTENERS, listener); + addAndConvert(cont, KEY_ERR, errHandler); + addAndConvert(cont, KEY_RAW, rawEmitter); + } else { + watcher = createFsWatchInstance( + path3, + options, + fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS), + errHandler, + // no need to use broadcast here + fsWatchBroadcast.bind(null, fullPath, KEY_RAW) + ); + if (!watcher) return; + watcher.on(EV_ERROR, async (error) => { + const broadcastErr = fsWatchBroadcast.bind(null, fullPath, KEY_ERR); + cont.watcherUnusable = true; + if (isWindows && error.code === "EPERM") { + try { + const fd = await open(path3, "r"); + await close(fd); + broadcastErr(error); + } catch (err) { + } + } else { + broadcastErr(error); + } + }); + cont = { + listeners: listener, + errHandlers: errHandler, + rawEmitters: rawEmitter, + watcher + }; + FsWatchInstances.set(fullPath, cont); + } + return () => { + delFromSet(cont, KEY_LISTENERS, listener); + delFromSet(cont, KEY_ERR, errHandler); + delFromSet(cont, KEY_RAW, rawEmitter); + if (isEmptySet(cont.listeners)) { + cont.watcher.close(); + FsWatchInstances.delete(fullPath); + HANDLER_KEYS.forEach(clearItem(cont)); + cont.watcher = void 0; + Object.freeze(cont); + } + }; + }; + var FsWatchFileInstances = /* @__PURE__ */ new Map(); + var setFsWatchFileListener = (path3, fullPath, options, handlers) => { + const { listener, rawEmitter } = handlers; + let cont = FsWatchFileInstances.get(fullPath); + let listeners = /* @__PURE__ */ new Set(); + let rawEmitters = /* @__PURE__ */ new Set(); + const copts = cont && cont.options; + if (copts && (copts.persistent < options.persistent || copts.interval > options.interval)) { + listeners = cont.listeners; + rawEmitters = cont.rawEmitters; + fs2.unwatchFile(fullPath); + cont = void 0; + } + if (cont) { + addAndConvert(cont, KEY_LISTENERS, listener); + addAndConvert(cont, KEY_RAW, rawEmitter); + } else { + cont = { + listeners: listener, + rawEmitters: rawEmitter, + options, + watcher: fs2.watchFile(fullPath, options, (curr, prev) => { + foreach(cont.rawEmitters, (rawEmitter2) => { + rawEmitter2(EV_CHANGE, fullPath, { curr, prev }); + }); + const currmtime = curr.mtimeMs; + if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) { + foreach(cont.listeners, (listener2) => listener2(path3, curr)); + } + }) + }; + FsWatchFileInstances.set(fullPath, cont); + } + return () => { + delFromSet(cont, KEY_LISTENERS, listener); + delFromSet(cont, KEY_RAW, rawEmitter); + if (isEmptySet(cont.listeners)) { + FsWatchFileInstances.delete(fullPath); + fs2.unwatchFile(fullPath); + cont.options = cont.watcher = void 0; + Object.freeze(cont); + } + }; + }; + var NodeFsHandler = class { + /** + * @param {import("../index").FSWatcher} fsW + */ + constructor(fsW) { + this.fsw = fsW; + this._boundHandleError = (error) => fsW._handleError(error); + } + /** + * Watch file for changes with fs_watchFile or fs_watch. + * @param {String} path to file or dir + * @param {Function} listener on fs change + * @returns {Function} closer for the watcher instance + */ + _watchWithNodeFs(path3, listener) { + const opts = this.fsw.options; + const directory = sysPath.dirname(path3); + const basename = sysPath.basename(path3); + const parent = this.fsw._getWatchedDir(directory); + parent.add(basename); + const absolutePath = sysPath.resolve(path3); + const options = { persistent: opts.persistent }; + if (!listener) listener = EMPTY_FN; + let closer; + if (opts.usePolling) { + options.interval = opts.enableBinaryInterval && isBinaryPath(basename) ? opts.binaryInterval : opts.interval; + closer = setFsWatchFileListener(path3, absolutePath, options, { + listener, + rawEmitter: this.fsw._emitRaw + }); + } else { + closer = setFsWatchListener(path3, absolutePath, options, { + listener, + errHandler: this._boundHandleError, + rawEmitter: this.fsw._emitRaw + }); + } + return closer; + } + /** + * Watch a file and emit add event if warranted. + * @param {Path} file Path + * @param {fs.Stats} stats result of fs_stat + * @param {Boolean} initialAdd was the file added at watch instantiation? + * @returns {Function} closer for the watcher instance + */ + _handleFile(file, stats, initialAdd) { + if (this.fsw.closed) { + return; + } + const dirname = sysPath.dirname(file); + const basename = sysPath.basename(file); + const parent = this.fsw._getWatchedDir(dirname); + let prevStats = stats; + if (parent.has(basename)) return; + const listener = async (path3, newStats) => { + if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5)) return; + if (!newStats || newStats.mtimeMs === 0) { + try { + const newStats2 = await stat(file); + if (this.fsw.closed) return; + const at = newStats2.atimeMs; + const mt = newStats2.mtimeMs; + if (!at || at <= mt || mt !== prevStats.mtimeMs) { + this.fsw._emit(EV_CHANGE, file, newStats2); + } + if (isLinux && prevStats.ino !== newStats2.ino) { + this.fsw._closeFile(path3); + prevStats = newStats2; + this.fsw._addPathCloser(path3, this._watchWithNodeFs(file, listener)); + } else { + prevStats = newStats2; + } + } catch (error) { + this.fsw._remove(dirname, basename); + } + } else if (parent.has(basename)) { + const at = newStats.atimeMs; + const mt = newStats.mtimeMs; + if (!at || at <= mt || mt !== prevStats.mtimeMs) { + this.fsw._emit(EV_CHANGE, file, newStats); + } + prevStats = newStats; + } + }; + const closer = this._watchWithNodeFs(file, listener); + if (!(initialAdd && this.fsw.options.ignoreInitial) && this.fsw._isntIgnored(file)) { + if (!this.fsw._throttle(EV_ADD, file, 0)) return; + this.fsw._emit(EV_ADD, file, stats); + } + return closer; + } + /** + * Handle symlinks encountered while reading a dir. + * @param {Object} entry returned by readdirp + * @param {String} directory path of dir being read + * @param {String} path of this item + * @param {String} item basename of this item + * @returns {Promise} true if no more processing is needed for this entry. + */ + async _handleSymlink(entry, directory, path3, item) { + if (this.fsw.closed) { + return; + } + const full = entry.fullPath; + const dir = this.fsw._getWatchedDir(directory); + if (!this.fsw.options.followSymlinks) { + this.fsw._incrReadyCount(); + let linkPath; + try { + linkPath = await fsrealpath(path3); + } catch (e) { + this.fsw._emitReady(); + return true; + } + if (this.fsw.closed) return; + if (dir.has(item)) { + if (this.fsw._symlinkPaths.get(full) !== linkPath) { + this.fsw._symlinkPaths.set(full, linkPath); + this.fsw._emit(EV_CHANGE, path3, entry.stats); + } + } else { + dir.add(item); + this.fsw._symlinkPaths.set(full, linkPath); + this.fsw._emit(EV_ADD, path3, entry.stats); + } + this.fsw._emitReady(); + return true; + } + if (this.fsw._symlinkPaths.has(full)) { + return true; + } + this.fsw._symlinkPaths.set(full, true); + } + _handleRead(directory, initialAdd, wh, target, dir, depth, throttler) { + directory = sysPath.join(directory, EMPTY_STR); + if (!wh.hasGlob) { + throttler = this.fsw._throttle("readdir", directory, 1e3); + if (!throttler) return; + } + const previous = this.fsw._getWatchedDir(wh.path); + const current = /* @__PURE__ */ new Set(); + let stream = this.fsw._readdirp(directory, { + fileFilter: (entry) => wh.filterPath(entry), + directoryFilter: (entry) => wh.filterDir(entry), + depth: 0 + }).on(STR_DATA, async (entry) => { + if (this.fsw.closed) { + stream = void 0; + return; + } + const item = entry.path; + let path3 = sysPath.join(directory, item); + current.add(item); + if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path3, item)) { + return; + } + if (this.fsw.closed) { + stream = void 0; + return; + } + if (item === target || !target && !previous.has(item)) { + this.fsw._incrReadyCount(); + path3 = sysPath.join(dir, sysPath.relative(dir, path3)); + this._addToNodeFs(path3, initialAdd, wh, depth + 1); + } + }).on(EV_ERROR, this._boundHandleError); + return new Promise( + (resolve) => stream.once(STR_END, () => { + if (this.fsw.closed) { + stream = void 0; + return; + } + const wasThrottled = throttler ? throttler.clear() : false; + resolve(); + previous.getChildren().filter((item) => { + return item !== directory && !current.has(item) && // in case of intersecting globs; + // a path may have been filtered out of this readdir, but + // shouldn't be removed because it matches a different glob + (!wh.hasGlob || wh.filterPath({ + fullPath: sysPath.resolve(directory, item) + })); + }).forEach((item) => { + this.fsw._remove(directory, item); + }); + stream = void 0; + if (wasThrottled) this._handleRead(directory, false, wh, target, dir, depth, throttler); + }) + ); + } + /** + * Read directory to add / remove files from `@watched` list and re-read it on change. + * @param {String} dir fs path + * @param {fs.Stats} stats + * @param {Boolean} initialAdd + * @param {Number} depth relative to user-supplied path + * @param {String} target child path targeted for watch + * @param {Object} wh Common watch helpers for this path + * @param {String} realpath + * @returns {Promise} closer for the watcher instance. + */ + async _handleDir(dir, stats, initialAdd, depth, target, wh, realpath) { + const parentDir = this.fsw._getWatchedDir(sysPath.dirname(dir)); + const tracked = parentDir.has(sysPath.basename(dir)); + if (!(initialAdd && this.fsw.options.ignoreInitial) && !target && !tracked) { + if (!wh.hasGlob || wh.globFilter(dir)) this.fsw._emit(EV_ADD_DIR, dir, stats); + } + parentDir.add(sysPath.basename(dir)); + this.fsw._getWatchedDir(dir); + let throttler; + let closer; + const oDepth = this.fsw.options.depth; + if ((oDepth == null || depth <= oDepth) && !this.fsw._symlinkPaths.has(realpath)) { + if (!target) { + await this._handleRead(dir, initialAdd, wh, target, dir, depth, throttler); + if (this.fsw.closed) return; + } + closer = this._watchWithNodeFs(dir, (dirPath, stats2) => { + if (stats2 && stats2.mtimeMs === 0) return; + this._handleRead(dirPath, false, wh, target, dir, depth, throttler); + }); + } + return closer; + } + /** + * Handle added file, directory, or glob pattern. + * Delegates call to _handleFile / _handleDir after checks. + * @param {String} path to file or ir + * @param {Boolean} initialAdd was the file added at watch instantiation? + * @param {Object} priorWh depth relative to user-supplied path + * @param {Number} depth Child path actually targeted for watch + * @param {String=} target Child path actually targeted for watch + * @returns {Promise} + */ + async _addToNodeFs(path3, initialAdd, priorWh, depth, target) { + const ready = this.fsw._emitReady; + if (this.fsw._isIgnored(path3) || this.fsw.closed) { + ready(); + return false; + } + const wh = this.fsw._getWatchHelpers(path3, depth); + if (!wh.hasGlob && priorWh) { + wh.hasGlob = priorWh.hasGlob; + wh.globFilter = priorWh.globFilter; + wh.filterPath = (entry) => priorWh.filterPath(entry); + wh.filterDir = (entry) => priorWh.filterDir(entry); + } + try { + const stats = await statMethods[wh.statMethod](wh.watchPath); + if (this.fsw.closed) return; + if (this.fsw._isIgnored(wh.watchPath, stats)) { + ready(); + return false; + } + const follow = this.fsw.options.followSymlinks && !path3.includes(STAR) && !path3.includes(BRACE_START); + let closer; + if (stats.isDirectory()) { + const absPath = sysPath.resolve(path3); + const targetPath = follow ? await fsrealpath(path3) : path3; + if (this.fsw.closed) return; + closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath); + if (this.fsw.closed) return; + if (absPath !== targetPath && targetPath !== void 0) { + this.fsw._symlinkPaths.set(absPath, targetPath); + } + } else if (stats.isSymbolicLink()) { + const targetPath = follow ? await fsrealpath(path3) : path3; + if (this.fsw.closed) return; + const parent = sysPath.dirname(wh.watchPath); + this.fsw._getWatchedDir(parent).add(wh.watchPath); + this.fsw._emit(EV_ADD, wh.watchPath, stats); + closer = await this._handleDir(parent, stats, initialAdd, depth, path3, wh, targetPath); + if (this.fsw.closed) return; + if (targetPath !== void 0) { + this.fsw._symlinkPaths.set(sysPath.resolve(path3), targetPath); + } + } else { + closer = this._handleFile(wh.watchPath, stats, initialAdd); + } + ready(); + this.fsw._addPathCloser(path3, closer); + return false; + } catch (error) { + if (this.fsw._handleError(error)) { + ready(); + return path3; + } + } + } + }; + module2.exports = NodeFsHandler; + } +}); + +// node_modules/chokidar/lib/fsevents-handler.js +var require_fsevents_handler = __commonJS({ + "node_modules/chokidar/lib/fsevents-handler.js"(exports2, module2) { + "use strict"; + var fs2 = require("fs"); + var sysPath = require("path"); + var { promisify } = require("util"); + var fsevents; + try { + fsevents = require("fsevents"); + } catch (error) { + if (process.env.CHOKIDAR_PRINT_FSEVENTS_REQUIRE_ERROR) console.error(error); + } + if (fsevents) { + const mtch = process.version.match(/v(\d+)\.(\d+)/); + if (mtch && mtch[1] && mtch[2]) { + const maj = Number.parseInt(mtch[1], 10); + const min = Number.parseInt(mtch[2], 10); + if (maj === 8 && min < 16) { + fsevents = void 0; + } + } + } + var { + EV_ADD, + EV_CHANGE, + EV_ADD_DIR, + EV_UNLINK, + EV_ERROR, + STR_DATA, + STR_END, + FSEVENT_CREATED, + FSEVENT_MODIFIED, + FSEVENT_DELETED, + FSEVENT_MOVED, + // FSEVENT_CLONED, + FSEVENT_UNKNOWN, + FSEVENT_FLAG_MUST_SCAN_SUBDIRS, + FSEVENT_TYPE_FILE, + FSEVENT_TYPE_DIRECTORY, + FSEVENT_TYPE_SYMLINK, + ROOT_GLOBSTAR, + DIR_SUFFIX, + DOT_SLASH, + FUNCTION_TYPE, + EMPTY_FN, + IDENTITY_FN + } = require_constants4(); + var Depth = (value) => isNaN(value) ? {} : { depth: value }; + var stat = promisify(fs2.stat); + var lstat = promisify(fs2.lstat); + var realpath = promisify(fs2.realpath); + var statMethods = { stat, lstat }; + var FSEventsWatchers = /* @__PURE__ */ new Map(); + var consolidateThreshhold = 10; + var wrongEventFlags = /* @__PURE__ */ new Set([ + 69888, + 70400, + 71424, + 72704, + 73472, + 131328, + 131840, + 262912 + ]); + var createFSEventsInstance = (path3, callback) => { + const stop = fsevents.watch(path3, callback); + return { stop }; + }; + function setFSEventsListener(path3, realPath, listener, rawEmitter) { + let watchPath = sysPath.extname(realPath) ? sysPath.dirname(realPath) : realPath; + const parentPath = sysPath.dirname(watchPath); + let cont = FSEventsWatchers.get(watchPath); + if (couldConsolidate(parentPath)) { + watchPath = parentPath; + } + const resolvedPath = sysPath.resolve(path3); + const hasSymlink = resolvedPath !== realPath; + const filteredListener = (fullPath, flags, info) => { + if (hasSymlink) fullPath = fullPath.replace(realPath, resolvedPath); + if (fullPath === resolvedPath || !fullPath.indexOf(resolvedPath + sysPath.sep)) listener(fullPath, flags, info); + }; + let watchedParent = false; + for (const watchedPath of FSEventsWatchers.keys()) { + if (realPath.indexOf(sysPath.resolve(watchedPath) + sysPath.sep) === 0) { + watchPath = watchedPath; + cont = FSEventsWatchers.get(watchPath); + watchedParent = true; + break; + } + } + if (cont || watchedParent) { + cont.listeners.add(filteredListener); + } else { + cont = { + listeners: /* @__PURE__ */ new Set([filteredListener]), + rawEmitter, + watcher: createFSEventsInstance(watchPath, (fullPath, flags) => { + if (!cont.listeners.size) return; + if (flags & FSEVENT_FLAG_MUST_SCAN_SUBDIRS) return; + const info = fsevents.getInfo(fullPath, flags); + cont.listeners.forEach((list) => { + list(fullPath, flags, info); + }); + cont.rawEmitter(info.event, fullPath, info); + }) + }; + FSEventsWatchers.set(watchPath, cont); + } + return () => { + const lst = cont.listeners; + lst.delete(filteredListener); + if (!lst.size) { + FSEventsWatchers.delete(watchPath); + if (cont.watcher) return cont.watcher.stop().then(() => { + cont.rawEmitter = cont.watcher = void 0; + Object.freeze(cont); + }); + } + }; + } + var couldConsolidate = (path3) => { + let count = 0; + for (const watchPath of FSEventsWatchers.keys()) { + if (watchPath.indexOf(path3) === 0) { + count++; + if (count >= consolidateThreshhold) { + return true; + } + } + } + return false; + }; + var canUse = () => fsevents && FSEventsWatchers.size < 128; + var calcDepth = (path3, root) => { + let i = 0; + while (!path3.indexOf(root) && (path3 = sysPath.dirname(path3)) !== root) i++; + return i; + }; + var sameTypes = (info, stats) => info.type === FSEVENT_TYPE_DIRECTORY && stats.isDirectory() || info.type === FSEVENT_TYPE_SYMLINK && stats.isSymbolicLink() || info.type === FSEVENT_TYPE_FILE && stats.isFile(); + var FsEventsHandler = class { + /** + * @param {import('../index').FSWatcher} fsw + */ + constructor(fsw) { + this.fsw = fsw; + } + checkIgnored(path3, stats) { + const ipaths = this.fsw._ignoredPaths; + if (this.fsw._isIgnored(path3, stats)) { + ipaths.add(path3); + if (stats && stats.isDirectory()) { + ipaths.add(path3 + ROOT_GLOBSTAR); + } + return true; + } + ipaths.delete(path3); + ipaths.delete(path3 + ROOT_GLOBSTAR); + } + addOrChange(path3, fullPath, realPath, parent, watchedDir, item, info, opts) { + const event = watchedDir.has(item) ? EV_CHANGE : EV_ADD; + this.handleEvent(event, path3, fullPath, realPath, parent, watchedDir, item, info, opts); + } + async checkExists(path3, fullPath, realPath, parent, watchedDir, item, info, opts) { + try { + const stats = await stat(path3); + if (this.fsw.closed) return; + if (sameTypes(info, stats)) { + this.addOrChange(path3, fullPath, realPath, parent, watchedDir, item, info, opts); + } else { + this.handleEvent(EV_UNLINK, path3, fullPath, realPath, parent, watchedDir, item, info, opts); + } + } catch (error) { + if (error.code === "EACCES") { + this.addOrChange(path3, fullPath, realPath, parent, watchedDir, item, info, opts); + } else { + this.handleEvent(EV_UNLINK, path3, fullPath, realPath, parent, watchedDir, item, info, opts); + } + } + } + handleEvent(event, path3, fullPath, realPath, parent, watchedDir, item, info, opts) { + if (this.fsw.closed || this.checkIgnored(path3)) return; + if (event === EV_UNLINK) { + const isDirectory = info.type === FSEVENT_TYPE_DIRECTORY; + if (isDirectory || watchedDir.has(item)) { + this.fsw._remove(parent, item, isDirectory); + } + } else { + if (event === EV_ADD) { + if (info.type === FSEVENT_TYPE_DIRECTORY) this.fsw._getWatchedDir(path3); + if (info.type === FSEVENT_TYPE_SYMLINK && opts.followSymlinks) { + const curDepth = opts.depth === void 0 ? void 0 : calcDepth(fullPath, realPath) + 1; + return this._addToFsEvents(path3, false, true, curDepth); + } + this.fsw._getWatchedDir(parent).add(item); + } + const eventName = info.type === FSEVENT_TYPE_DIRECTORY ? event + DIR_SUFFIX : event; + this.fsw._emit(eventName, path3); + if (eventName === EV_ADD_DIR) this._addToFsEvents(path3, false, true); + } + } + /** + * Handle symlinks encountered during directory scan + * @param {String} watchPath - file/dir path to be watched with fsevents + * @param {String} realPath - real path (in case of symlinks) + * @param {Function} transform - path transformer + * @param {Function} globFilter - path filter in case a glob pattern was provided + * @returns {Function} closer for the watcher instance + */ + _watchWithFsEvents(watchPath, realPath, transform, globFilter) { + if (this.fsw.closed || this.fsw._isIgnored(watchPath)) return; + const opts = this.fsw.options; + const watchCallback = async (fullPath, flags, info) => { + if (this.fsw.closed) return; + if (opts.depth !== void 0 && calcDepth(fullPath, realPath) > opts.depth) return; + const path3 = transform(sysPath.join( + watchPath, + sysPath.relative(watchPath, fullPath) + )); + if (globFilter && !globFilter(path3)) return; + const parent = sysPath.dirname(path3); + const item = sysPath.basename(path3); + const watchedDir = this.fsw._getWatchedDir( + info.type === FSEVENT_TYPE_DIRECTORY ? path3 : parent + ); + if (wrongEventFlags.has(flags) || info.event === FSEVENT_UNKNOWN) { + if (typeof opts.ignored === FUNCTION_TYPE) { + let stats; + try { + stats = await stat(path3); + } catch (error) { + } + if (this.fsw.closed) return; + if (this.checkIgnored(path3, stats)) return; + if (sameTypes(info, stats)) { + this.addOrChange(path3, fullPath, realPath, parent, watchedDir, item, info, opts); + } else { + this.handleEvent(EV_UNLINK, path3, fullPath, realPath, parent, watchedDir, item, info, opts); + } + } else { + this.checkExists(path3, fullPath, realPath, parent, watchedDir, item, info, opts); + } + } else { + switch (info.event) { + case FSEVENT_CREATED: + case FSEVENT_MODIFIED: + return this.addOrChange(path3, fullPath, realPath, parent, watchedDir, item, info, opts); + case FSEVENT_DELETED: + case FSEVENT_MOVED: + return this.checkExists(path3, fullPath, realPath, parent, watchedDir, item, info, opts); + } + } + }; + const closer = setFSEventsListener( + watchPath, + realPath, + watchCallback, + this.fsw._emitRaw + ); + this.fsw._emitReady(); + return closer; + } + /** + * Handle symlinks encountered during directory scan + * @param {String} linkPath path to symlink + * @param {String} fullPath absolute path to the symlink + * @param {Function} transform pre-existing path transformer + * @param {Number} curDepth level of subdirectories traversed to where symlink is + * @returns {Promise} + */ + async _handleFsEventsSymlink(linkPath, fullPath, transform, curDepth) { + if (this.fsw.closed || this.fsw._symlinkPaths.has(fullPath)) return; + this.fsw._symlinkPaths.set(fullPath, true); + this.fsw._incrReadyCount(); + try { + const linkTarget = await realpath(linkPath); + if (this.fsw.closed) return; + if (this.fsw._isIgnored(linkTarget)) { + return this.fsw._emitReady(); + } + this.fsw._incrReadyCount(); + this._addToFsEvents(linkTarget || linkPath, (path3) => { + let aliasedPath = linkPath; + if (linkTarget && linkTarget !== DOT_SLASH) { + aliasedPath = path3.replace(linkTarget, linkPath); + } else if (path3 !== DOT_SLASH) { + aliasedPath = sysPath.join(linkPath, path3); + } + return transform(aliasedPath); + }, false, curDepth); + } catch (error) { + if (this.fsw._handleError(error)) { + return this.fsw._emitReady(); + } + } + } + /** + * + * @param {Path} newPath + * @param {fs.Stats} stats + */ + emitAdd(newPath, stats, processPath, opts, forceAdd) { + const pp = processPath(newPath); + const isDir = stats.isDirectory(); + const dirObj = this.fsw._getWatchedDir(sysPath.dirname(pp)); + const base = sysPath.basename(pp); + if (isDir) this.fsw._getWatchedDir(pp); + if (dirObj.has(base)) return; + dirObj.add(base); + if (!opts.ignoreInitial || forceAdd === true) { + this.fsw._emit(isDir ? EV_ADD_DIR : EV_ADD, pp, stats); + } + } + initWatch(realPath, path3, wh, processPath) { + if (this.fsw.closed) return; + const closer = this._watchWithFsEvents( + wh.watchPath, + sysPath.resolve(realPath || wh.watchPath), + processPath, + wh.globFilter + ); + this.fsw._addPathCloser(path3, closer); + } + /** + * Handle added path with fsevents + * @param {String} path file/dir path or glob pattern + * @param {Function|Boolean=} transform converts working path to what the user expects + * @param {Boolean=} forceAdd ensure add is emitted + * @param {Number=} priorDepth Level of subdirectories already traversed. + * @returns {Promise} + */ + async _addToFsEvents(path3, transform, forceAdd, priorDepth) { + if (this.fsw.closed) { + return; + } + const opts = this.fsw.options; + const processPath = typeof transform === FUNCTION_TYPE ? transform : IDENTITY_FN; + const wh = this.fsw._getWatchHelpers(path3); + try { + const stats = await statMethods[wh.statMethod](wh.watchPath); + if (this.fsw.closed) return; + if (this.fsw._isIgnored(wh.watchPath, stats)) { + throw null; + } + if (stats.isDirectory()) { + if (!wh.globFilter) this.emitAdd(processPath(path3), stats, processPath, opts, forceAdd); + if (priorDepth && priorDepth > opts.depth) return; + this.fsw._readdirp(wh.watchPath, { + fileFilter: (entry) => wh.filterPath(entry), + directoryFilter: (entry) => wh.filterDir(entry), + ...Depth(opts.depth - (priorDepth || 0)) + }).on(STR_DATA, (entry) => { + if (this.fsw.closed) { + return; + } + if (entry.stats.isDirectory() && !wh.filterPath(entry)) return; + const joinedPath = sysPath.join(wh.watchPath, entry.path); + const { fullPath } = entry; + if (wh.followSymlinks && entry.stats.isSymbolicLink()) { + const curDepth = opts.depth === void 0 ? void 0 : calcDepth(joinedPath, sysPath.resolve(wh.watchPath)) + 1; + this._handleFsEventsSymlink(joinedPath, fullPath, processPath, curDepth); + } else { + this.emitAdd(joinedPath, entry.stats, processPath, opts, forceAdd); + } + }).on(EV_ERROR, EMPTY_FN).on(STR_END, () => { + this.fsw._emitReady(); + }); + } else { + this.emitAdd(wh.watchPath, stats, processPath, opts, forceAdd); + this.fsw._emitReady(); + } + } catch (error) { + if (!error || this.fsw._handleError(error)) { + this.fsw._emitReady(); + this.fsw._emitReady(); + } + } + if (opts.persistent && forceAdd !== true) { + if (typeof transform === FUNCTION_TYPE) { + this.initWatch(void 0, path3, wh, processPath); + } else { + let realPath; + try { + realPath = await realpath(wh.watchPath); + } catch (e) { + } + this.initWatch(realPath, path3, wh, processPath); + } + } + } + }; + module2.exports = FsEventsHandler; + module2.exports.canUse = canUse; + } +}); + +// node_modules/chokidar/index.js +var require_chokidar = __commonJS({ + "node_modules/chokidar/index.js"(exports2) { + "use strict"; + var { EventEmitter: EventEmitter2 } = require("events"); + var fs2 = require("fs"); + var sysPath = require("path"); + var { promisify } = require("util"); + var readdirp = require_readdirp(); + var anymatch = require_anymatch().default; + var globParent = require_glob_parent(); + var isGlob = require_is_glob(); + var braces = require_braces(); + var normalizePath = require_normalize_path(); + var NodeFsHandler = require_nodefs_handler(); + var FsEventsHandler = require_fsevents_handler(); + var { + EV_ALL, + EV_READY, + EV_ADD, + EV_CHANGE, + EV_UNLINK, + EV_ADD_DIR, + EV_UNLINK_DIR, + EV_RAW, + EV_ERROR, + STR_CLOSE, + STR_END, + BACK_SLASH_RE, + DOUBLE_SLASH_RE, + SLASH_OR_BACK_SLASH_RE, + DOT_RE, + REPLACER_RE, + SLASH, + SLASH_SLASH, + BRACE_START, + BANG, + ONE_DOT, + TWO_DOTS, + GLOBSTAR, + SLASH_GLOBSTAR, + ANYMATCH_OPTS, + STRING_TYPE, + FUNCTION_TYPE, + EMPTY_STR, + EMPTY_FN, + isWindows, + isMacos, + isIBMi + } = require_constants4(); + var stat = promisify(fs2.stat); + var readdir = promisify(fs2.readdir); + var arrify = (value = []) => Array.isArray(value) ? value : [value]; + var flatten = (list, result = []) => { + list.forEach((item) => { + if (Array.isArray(item)) { + flatten(item, result); + } else { + result.push(item); + } + }); + return result; + }; + var unifyPaths = (paths_) => { + const paths = flatten(arrify(paths_)); + if (!paths.every((p) => typeof p === STRING_TYPE)) { + throw new TypeError(`Non-string provided as watch path: ${paths}`); + } + return paths.map(normalizePathToUnix); + }; + var toUnix = (string) => { + let str = string.replace(BACK_SLASH_RE, SLASH); + let prepend = false; + if (str.startsWith(SLASH_SLASH)) { + prepend = true; + } + while (str.match(DOUBLE_SLASH_RE)) { + str = str.replace(DOUBLE_SLASH_RE, SLASH); + } + if (prepend) { + str = SLASH + str; + } + return str; + }; + var normalizePathToUnix = (path3) => toUnix(sysPath.normalize(toUnix(path3))); + var normalizeIgnored = (cwd = EMPTY_STR) => (path3) => { + if (typeof path3 !== STRING_TYPE) return path3; + return normalizePathToUnix(sysPath.isAbsolute(path3) ? path3 : sysPath.join(cwd, path3)); + }; + var getAbsolutePath = (path3, cwd) => { + if (sysPath.isAbsolute(path3)) { + return path3; + } + if (path3.startsWith(BANG)) { + return BANG + sysPath.join(cwd, path3.slice(1)); + } + return sysPath.join(cwd, path3); + }; + var undef = (opts, key) => opts[key] === void 0; + var DirEntry = class { + /** + * @param {Path} dir + * @param {Function} removeWatcher + */ + constructor(dir, removeWatcher) { + this.path = dir; + this._removeWatcher = removeWatcher; + this.items = /* @__PURE__ */ new Set(); + } + add(item) { + const { items } = this; + if (!items) return; + if (item !== ONE_DOT && item !== TWO_DOTS) items.add(item); + } + async remove(item) { + const { items } = this; + if (!items) return; + items.delete(item); + if (items.size > 0) return; + const dir = this.path; + try { + await readdir(dir); + } catch (err) { + if (this._removeWatcher) { + this._removeWatcher(sysPath.dirname(dir), sysPath.basename(dir)); + } + } + } + has(item) { + const { items } = this; + if (!items) return; + return items.has(item); + } + /** + * @returns {Array} + */ + getChildren() { + const { items } = this; + if (!items) return; + return [...items.values()]; + } + dispose() { + this.items.clear(); + delete this.path; + delete this._removeWatcher; + delete this.items; + Object.freeze(this); + } + }; + var STAT_METHOD_F = "stat"; + var STAT_METHOD_L = "lstat"; + var WatchHelper = class { + constructor(path3, watchPath, follow, fsw) { + this.fsw = fsw; + this.path = path3 = path3.replace(REPLACER_RE, EMPTY_STR); + this.watchPath = watchPath; + this.fullWatchPath = sysPath.resolve(watchPath); + this.hasGlob = watchPath !== path3; + if (path3 === EMPTY_STR) this.hasGlob = false; + this.globSymlink = this.hasGlob && follow ? void 0 : false; + this.globFilter = this.hasGlob ? anymatch(path3, void 0, ANYMATCH_OPTS) : false; + this.dirParts = this.getDirParts(path3); + this.dirParts.forEach((parts) => { + if (parts.length > 1) parts.pop(); + }); + this.followSymlinks = follow; + this.statMethod = follow ? STAT_METHOD_F : STAT_METHOD_L; + } + checkGlobSymlink(entry) { + if (this.globSymlink === void 0) { + this.globSymlink = entry.fullParentDir === this.fullWatchPath ? false : { realPath: entry.fullParentDir, linkPath: this.fullWatchPath }; + } + if (this.globSymlink) { + return entry.fullPath.replace(this.globSymlink.realPath, this.globSymlink.linkPath); + } + return entry.fullPath; + } + entryPath(entry) { + return sysPath.join( + this.watchPath, + sysPath.relative(this.watchPath, this.checkGlobSymlink(entry)) + ); + } + filterPath(entry) { + const { stats } = entry; + if (stats && stats.isSymbolicLink()) return this.filterDir(entry); + const resolvedPath = this.entryPath(entry); + const matchesGlob = this.hasGlob && typeof this.globFilter === FUNCTION_TYPE ? this.globFilter(resolvedPath) : true; + return matchesGlob && this.fsw._isntIgnored(resolvedPath, stats) && this.fsw._hasReadPermissions(stats); + } + getDirParts(path3) { + if (!this.hasGlob) return []; + const parts = []; + const expandedPath = path3.includes(BRACE_START) ? braces.expand(path3) : [path3]; + expandedPath.forEach((path4) => { + parts.push(sysPath.relative(this.watchPath, path4).split(SLASH_OR_BACK_SLASH_RE)); + }); + return parts; + } + filterDir(entry) { + if (this.hasGlob) { + const entryParts = this.getDirParts(this.checkGlobSymlink(entry)); + let globstar = false; + this.unmatchedGlob = !this.dirParts.some((parts) => { + return parts.every((part, i) => { + if (part === GLOBSTAR) globstar = true; + return globstar || !entryParts[0][i] || anymatch(part, entryParts[0][i], ANYMATCH_OPTS); + }); + }); + } + return !this.unmatchedGlob && this.fsw._isntIgnored(this.entryPath(entry), entry.stats); + } + }; + var FSWatcher = class extends EventEmitter2 { + // Not indenting methods for history sake; for now. + constructor(_opts) { + super(); + const opts = {}; + if (_opts) Object.assign(opts, _opts); + this._watched = /* @__PURE__ */ new Map(); + this._closers = /* @__PURE__ */ new Map(); + this._ignoredPaths = /* @__PURE__ */ new Set(); + this._throttled = /* @__PURE__ */ new Map(); + this._symlinkPaths = /* @__PURE__ */ new Map(); + this._streams = /* @__PURE__ */ new Set(); + this.closed = false; + if (undef(opts, "persistent")) opts.persistent = true; + if (undef(opts, "ignoreInitial")) opts.ignoreInitial = false; + if (undef(opts, "ignorePermissionErrors")) opts.ignorePermissionErrors = false; + if (undef(opts, "interval")) opts.interval = 100; + if (undef(opts, "binaryInterval")) opts.binaryInterval = 300; + if (undef(opts, "disableGlobbing")) opts.disableGlobbing = false; + opts.enableBinaryInterval = opts.binaryInterval !== opts.interval; + if (undef(opts, "useFsEvents")) opts.useFsEvents = !opts.usePolling; + const canUseFsEvents = FsEventsHandler.canUse(); + if (!canUseFsEvents) opts.useFsEvents = false; + if (undef(opts, "usePolling") && !opts.useFsEvents) { + opts.usePolling = isMacos; + } + if (isIBMi) { + opts.usePolling = true; + } + const envPoll = process.env.CHOKIDAR_USEPOLLING; + if (envPoll !== void 0) { + const envLower = envPoll.toLowerCase(); + if (envLower === "false" || envLower === "0") { + opts.usePolling = false; + } else if (envLower === "true" || envLower === "1") { + opts.usePolling = true; + } else { + opts.usePolling = !!envLower; + } + } + const envInterval = process.env.CHOKIDAR_INTERVAL; + if (envInterval) { + opts.interval = Number.parseInt(envInterval, 10); + } + if (undef(opts, "atomic")) opts.atomic = !opts.usePolling && !opts.useFsEvents; + if (opts.atomic) this._pendingUnlinks = /* @__PURE__ */ new Map(); + if (undef(opts, "followSymlinks")) opts.followSymlinks = true; + if (undef(opts, "awaitWriteFinish")) opts.awaitWriteFinish = false; + if (opts.awaitWriteFinish === true) opts.awaitWriteFinish = {}; + const awf = opts.awaitWriteFinish; + if (awf) { + if (!awf.stabilityThreshold) awf.stabilityThreshold = 2e3; + if (!awf.pollInterval) awf.pollInterval = 100; + this._pendingWrites = /* @__PURE__ */ new Map(); + } + if (opts.ignored) opts.ignored = arrify(opts.ignored); + let readyCalls = 0; + this._emitReady = () => { + readyCalls++; + if (readyCalls >= this._readyCount) { + this._emitReady = EMPTY_FN; + this._readyEmitted = true; + process.nextTick(() => this.emit(EV_READY)); + } + }; + this._emitRaw = (...args) => this.emit(EV_RAW, ...args); + this._readyEmitted = false; + this.options = opts; + if (opts.useFsEvents) { + this._fsEventsHandler = new FsEventsHandler(this); + } else { + this._nodeFsHandler = new NodeFsHandler(this); + } + Object.freeze(opts); + } + // Public methods + /** + * Adds paths to be watched on an existing FSWatcher instance + * @param {Path|Array} paths_ + * @param {String=} _origAdd private; for handling non-existent paths to be watched + * @param {Boolean=} _internal private; indicates a non-user add + * @returns {FSWatcher} for chaining + */ + add(paths_, _origAdd, _internal) { + const { cwd, disableGlobbing } = this.options; + this.closed = false; + let paths = unifyPaths(paths_); + if (cwd) { + paths = paths.map((path3) => { + const absPath = getAbsolutePath(path3, cwd); + if (disableGlobbing || !isGlob(path3)) { + return absPath; + } + return normalizePath(absPath); + }); + } + paths = paths.filter((path3) => { + if (path3.startsWith(BANG)) { + this._ignoredPaths.add(path3.slice(1)); + return false; + } + this._ignoredPaths.delete(path3); + this._ignoredPaths.delete(path3 + SLASH_GLOBSTAR); + this._userIgnored = void 0; + return true; + }); + if (this.options.useFsEvents && this._fsEventsHandler) { + if (!this._readyCount) this._readyCount = paths.length; + if (this.options.persistent) this._readyCount += paths.length; + paths.forEach((path3) => this._fsEventsHandler._addToFsEvents(path3)); + } else { + if (!this._readyCount) this._readyCount = 0; + this._readyCount += paths.length; + Promise.all( + paths.map(async (path3) => { + const res = await this._nodeFsHandler._addToNodeFs(path3, !_internal, 0, 0, _origAdd); + if (res) this._emitReady(); + return res; + }) + ).then((results) => { + if (this.closed) return; + results.filter((item) => item).forEach((item) => { + this.add(sysPath.dirname(item), sysPath.basename(_origAdd || item)); + }); + }); + } + return this; + } + /** + * Close watchers or start ignoring events from specified paths. + * @param {Path|Array} paths_ - string or array of strings, file/directory paths and/or globs + * @returns {FSWatcher} for chaining + */ + unwatch(paths_) { + if (this.closed) return this; + const paths = unifyPaths(paths_); + const { cwd } = this.options; + paths.forEach((path3) => { + if (!sysPath.isAbsolute(path3) && !this._closers.has(path3)) { + if (cwd) path3 = sysPath.join(cwd, path3); + path3 = sysPath.resolve(path3); + } + this._closePath(path3); + this._ignoredPaths.add(path3); + if (this._watched.has(path3)) { + this._ignoredPaths.add(path3 + SLASH_GLOBSTAR); + } + this._userIgnored = void 0; + }); + return this; + } + /** + * Close watchers and remove all listeners from watched paths. + * @returns {Promise}. + */ + close() { + if (this.closed) return this._closePromise; + this.closed = true; + this.removeAllListeners(); + const closers = []; + this._closers.forEach((closerList) => closerList.forEach((closer) => { + const promise = closer(); + if (promise instanceof Promise) closers.push(promise); + })); + this._streams.forEach((stream) => stream.destroy()); + this._userIgnored = void 0; + this._readyCount = 0; + this._readyEmitted = false; + this._watched.forEach((dirent) => dirent.dispose()); + ["closers", "watched", "streams", "symlinkPaths", "throttled"].forEach((key) => { + this[`_${key}`].clear(); + }); + this._closePromise = closers.length ? Promise.all(closers).then(() => void 0) : Promise.resolve(); + return this._closePromise; + } + /** + * Expose list of watched paths + * @returns {Object} for chaining + */ + getWatched() { + const watchList = {}; + this._watched.forEach((entry, dir) => { + const key = this.options.cwd ? sysPath.relative(this.options.cwd, dir) : dir; + watchList[key || ONE_DOT] = entry.getChildren().sort(); + }); + return watchList; + } + emitWithAll(event, args) { + this.emit(...args); + if (event !== EV_ERROR) this.emit(EV_ALL, ...args); + } + // Common helpers + // -------------- + /** + * Normalize and emit events. + * Calling _emit DOES NOT MEAN emit() would be called! + * @param {EventName} event Type of event + * @param {Path} path File or directory path + * @param {*=} val1 arguments to be passed with event + * @param {*=} val2 + * @param {*=} val3 + * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag + */ + async _emit(event, path3, val1, val2, val3) { + if (this.closed) return; + const opts = this.options; + if (isWindows) path3 = sysPath.normalize(path3); + if (opts.cwd) path3 = sysPath.relative(opts.cwd, path3); + const args = [event, path3]; + if (val3 !== void 0) args.push(val1, val2, val3); + else if (val2 !== void 0) args.push(val1, val2); + else if (val1 !== void 0) args.push(val1); + const awf = opts.awaitWriteFinish; + let pw; + if (awf && (pw = this._pendingWrites.get(path3))) { + pw.lastChange = /* @__PURE__ */ new Date(); + return this; + } + if (opts.atomic) { + if (event === EV_UNLINK) { + this._pendingUnlinks.set(path3, args); + setTimeout(() => { + this._pendingUnlinks.forEach((entry, path4) => { + this.emit(...entry); + this.emit(EV_ALL, ...entry); + this._pendingUnlinks.delete(path4); + }); + }, typeof opts.atomic === "number" ? opts.atomic : 100); + return this; + } + if (event === EV_ADD && this._pendingUnlinks.has(path3)) { + event = args[0] = EV_CHANGE; + this._pendingUnlinks.delete(path3); + } + } + if (awf && (event === EV_ADD || event === EV_CHANGE) && this._readyEmitted) { + const awfEmit = (err, stats) => { + if (err) { + event = args[0] = EV_ERROR; + args[1] = err; + this.emitWithAll(event, args); + } else if (stats) { + if (args.length > 2) { + args[2] = stats; + } else { + args.push(stats); + } + this.emitWithAll(event, args); + } + }; + this._awaitWriteFinish(path3, awf.stabilityThreshold, event, awfEmit); + return this; + } + if (event === EV_CHANGE) { + const isThrottled = !this._throttle(EV_CHANGE, path3, 50); + if (isThrottled) return this; + } + if (opts.alwaysStat && val1 === void 0 && (event === EV_ADD || event === EV_ADD_DIR || event === EV_CHANGE)) { + const fullPath = opts.cwd ? sysPath.join(opts.cwd, path3) : path3; + let stats; + try { + stats = await stat(fullPath); + } catch (err) { + } + if (!stats || this.closed) return; + args.push(stats); + } + this.emitWithAll(event, args); + return this; + } + /** + * Common handler for errors + * @param {Error} error + * @returns {Error|Boolean} The error if defined, otherwise the value of the FSWatcher instance's `closed` flag + */ + _handleError(error) { + const code = error && error.code; + if (error && code !== "ENOENT" && code !== "ENOTDIR" && (!this.options.ignorePermissionErrors || code !== "EPERM" && code !== "EACCES")) { + this.emit(EV_ERROR, error); + } + return error || this.closed; + } + /** + * Helper utility for throttling + * @param {ThrottleType} actionType type being throttled + * @param {Path} path being acted upon + * @param {Number} timeout duration of time to suppress duplicate actions + * @returns {Object|false} tracking object or false if action should be suppressed + */ + _throttle(actionType, path3, timeout) { + if (!this._throttled.has(actionType)) { + this._throttled.set(actionType, /* @__PURE__ */ new Map()); + } + const action = this._throttled.get(actionType); + const actionPath = action.get(path3); + if (actionPath) { + actionPath.count++; + return false; + } + let timeoutObject; + const clear = () => { + const item = action.get(path3); + const count = item ? item.count : 0; + action.delete(path3); + clearTimeout(timeoutObject); + if (item) clearTimeout(item.timeoutObject); + return count; + }; + timeoutObject = setTimeout(clear, timeout); + const thr = { timeoutObject, clear, count: 0 }; + action.set(path3, thr); + return thr; + } + _incrReadyCount() { + return this._readyCount++; + } + /** + * Awaits write operation to finish. + * Polls a newly created file for size variations. When files size does not change for 'threshold' milliseconds calls callback. + * @param {Path} path being acted upon + * @param {Number} threshold Time in milliseconds a file size must be fixed before acknowledging write OP is finished + * @param {EventName} event + * @param {Function} awfEmit Callback to be called when ready for event to be emitted. + */ + _awaitWriteFinish(path3, threshold, event, awfEmit) { + let timeoutHandler; + let fullPath = path3; + if (this.options.cwd && !sysPath.isAbsolute(path3)) { + fullPath = sysPath.join(this.options.cwd, path3); + } + const now = /* @__PURE__ */ new Date(); + const awaitWriteFinish = (prevStat) => { + fs2.stat(fullPath, (err, curStat) => { + if (err || !this._pendingWrites.has(path3)) { + if (err && err.code !== "ENOENT") awfEmit(err); + return; + } + const now2 = Number(/* @__PURE__ */ new Date()); + if (prevStat && curStat.size !== prevStat.size) { + this._pendingWrites.get(path3).lastChange = now2; + } + const pw = this._pendingWrites.get(path3); + const df = now2 - pw.lastChange; + if (df >= threshold) { + this._pendingWrites.delete(path3); + awfEmit(void 0, curStat); + } else { + timeoutHandler = setTimeout( + awaitWriteFinish, + this.options.awaitWriteFinish.pollInterval, + curStat + ); + } + }); + }; + if (!this._pendingWrites.has(path3)) { + this._pendingWrites.set(path3, { + lastChange: now, + cancelWait: () => { + this._pendingWrites.delete(path3); + clearTimeout(timeoutHandler); + return event; + } + }); + timeoutHandler = setTimeout( + awaitWriteFinish, + this.options.awaitWriteFinish.pollInterval + ); + } + } + _getGlobIgnored() { + return [...this._ignoredPaths.values()]; + } + /** + * Determines whether user has asked to ignore this path. + * @param {Path} path filepath or dir + * @param {fs.Stats=} stats result of fs.stat + * @returns {Boolean} + */ + _isIgnored(path3, stats) { + if (this.options.atomic && DOT_RE.test(path3)) return true; + if (!this._userIgnored) { + const { cwd } = this.options; + const ign = this.options.ignored; + const ignored = ign && ign.map(normalizeIgnored(cwd)); + const paths = arrify(ignored).filter((path4) => typeof path4 === STRING_TYPE && !isGlob(path4)).map((path4) => path4 + SLASH_GLOBSTAR); + const list = this._getGlobIgnored().map(normalizeIgnored(cwd)).concat(ignored, paths); + this._userIgnored = anymatch(list, void 0, ANYMATCH_OPTS); + } + return this._userIgnored([path3, stats]); + } + _isntIgnored(path3, stat2) { + return !this._isIgnored(path3, stat2); + } + /** + * Provides a set of common helpers and properties relating to symlink and glob handling. + * @param {Path} path file, directory, or glob pattern being watched + * @param {Number=} depth at any depth > 0, this isn't a glob + * @returns {WatchHelper} object containing helpers for this path + */ + _getWatchHelpers(path3, depth) { + const watchPath = depth || this.options.disableGlobbing || !isGlob(path3) ? path3 : globParent(path3); + const follow = this.options.followSymlinks; + return new WatchHelper(path3, watchPath, follow, this); + } + // Directory helpers + // ----------------- + /** + * Provides directory tracking objects + * @param {String} directory path of the directory + * @returns {DirEntry} the directory's tracking object + */ + _getWatchedDir(directory) { + if (!this._boundRemove) this._boundRemove = this._remove.bind(this); + const dir = sysPath.resolve(directory); + if (!this._watched.has(dir)) this._watched.set(dir, new DirEntry(dir, this._boundRemove)); + return this._watched.get(dir); + } + // File helpers + // ------------ + /** + * Check for read permissions. + * Based on this answer on SO: https://stackoverflow.com/a/11781404/1358405 + * @param {fs.Stats} stats - object, result of fs_stat + * @returns {Boolean} indicates whether the file can be read + */ + _hasReadPermissions(stats) { + if (this.options.ignorePermissionErrors) return true; + const md = stats && Number.parseInt(stats.mode, 10); + const st = md & 511; + const it = Number.parseInt(st.toString(8)[0], 10); + return Boolean(4 & it); + } + /** + * Handles emitting unlink events for + * files and directories, and via recursion, for + * files and directories within directories that are unlinked + * @param {String} directory within which the following item is located + * @param {String} item base path of item/directory + * @returns {void} + */ + _remove(directory, item, isDirectory) { + const path3 = sysPath.join(directory, item); + const fullPath = sysPath.resolve(path3); + isDirectory = isDirectory != null ? isDirectory : this._watched.has(path3) || this._watched.has(fullPath); + if (!this._throttle("remove", path3, 100)) return; + if (!isDirectory && !this.options.useFsEvents && this._watched.size === 1) { + this.add(directory, item, true); + } + const wp = this._getWatchedDir(path3); + const nestedDirectoryChildren = wp.getChildren(); + nestedDirectoryChildren.forEach((nested) => this._remove(path3, nested)); + const parent = this._getWatchedDir(directory); + const wasTracked = parent.has(item); + parent.remove(item); + if (this._symlinkPaths.has(fullPath)) { + this._symlinkPaths.delete(fullPath); + } + let relPath = path3; + if (this.options.cwd) relPath = sysPath.relative(this.options.cwd, path3); + if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) { + const event = this._pendingWrites.get(relPath).cancelWait(); + if (event === EV_ADD) return; + } + this._watched.delete(path3); + this._watched.delete(fullPath); + const eventName = isDirectory ? EV_UNLINK_DIR : EV_UNLINK; + if (wasTracked && !this._isIgnored(path3)) this._emit(eventName, path3); + if (!this.options.useFsEvents) { + this._closePath(path3); + } + } + /** + * Closes all watchers for a path + * @param {Path} path + */ + _closePath(path3) { + this._closeFile(path3); + const dir = sysPath.dirname(path3); + this._getWatchedDir(dir).remove(sysPath.basename(path3)); + } + /** + * Closes only file-specific watchers + * @param {Path} path + */ + _closeFile(path3) { + const closers = this._closers.get(path3); + if (!closers) return; + closers.forEach((closer) => closer()); + this._closers.delete(path3); + } + /** + * + * @param {Path} path + * @param {Function} closer + */ + _addPathCloser(path3, closer) { + if (!closer) return; + let list = this._closers.get(path3); + if (!list) { + list = []; + this._closers.set(path3, list); + } + list.push(closer); + } + _readdirp(root, opts) { + if (this.closed) return; + const options = { type: EV_ALL, alwaysStat: true, lstat: true, ...opts }; + let stream = readdirp(root, options); + this._streams.add(stream); + stream.once(STR_CLOSE, () => { + stream = void 0; + }); + stream.once(STR_END, () => { + if (stream) { + this._streams.delete(stream); + stream = void 0; + } + }); + return stream; + } + }; + exports2.FSWatcher = FSWatcher; + var watch = (paths, options) => { + const watcher = new FSWatcher(options); + watcher.add(paths); + return watcher; + }; + exports2.watch = watch; + } +}); + +// packages/playwright-core/src/serverRegistry.ts +var serverRegistry_exports = {}; +__export(serverRegistry_exports, { + serverRegistry: () => serverRegistry +}); +module.exports = __toCommonJS(serverRegistry_exports); +var import_events = require("events"); +var import_fs = __toESM(require("fs")); +var import_net = __toESM(require("net")); +var import_path2 = __toESM(require("path")); +var import_os = __toESM(require("os")); +var import_chokidar = __toESM(require_chokidar()); + +// packages/playwright-core/src/package.ts +var import_path = __toESM(require("path")); +var packageRoot = import_path.default.join(__dirname, ".."); +var packageJSON = require(import_path.default.join(packageRoot, "package.json")); +var binPath = import_path.default.join(packageRoot, "bin"); + +// packages/playwright-core/src/serverRegistry.ts +var packageVersion = packageJSON.version; +var ServerRegistry = class extends import_events.EventEmitter { + constructor() { + super(...arguments); + this._descriptors = /* @__PURE__ */ new Map(); + this._watcherRefs = 0; + } + on(event, listener) { + return super.on(event, listener); + } + off(event, listener) { + return super.off(event, listener); + } + watch() { + this._watcherRefs++; + if (!this._watcher) + this._startWatcher(); + let disposed = false; + return () => { + if (disposed) + return; + disposed = true; + this._watcherRefs--; + if (this._watcherRefs === 0) + this._stopWatcher(); + }; + } + ready() { + return this._ready ?? Promise.resolve(); + } + async list() { + const ownWatcher = !this._watcher; + let dispose; + if (ownWatcher) + dispose = this.watch(); + try { + await this._ready; + const statuses = await Promise.all( + [...this._descriptors.values()].map(async (descriptor) => { + const canConnect = await canConnectTo(descriptor); + return { descriptor, canConnect }; + }) + ); + const result = /* @__PURE__ */ new Map(); + for (const { descriptor, canConnect } of statuses) { + if (!canConnect) { + await import_fs.default.promises.unlink(import_path2.default.join(this._browsersDir(), descriptor.browser.guid)).catch(() => { + }); + continue; + } + const key = descriptor.workspaceDir ?? ""; + let list = result.get(key); + if (!list) { + list = []; + result.set(key, list); + } + list.push(descriptor); + } + return result; + } finally { + dispose?.(); + } + } + async create(browser, endpoint) { + const file = import_path2.default.join(this._browsersDir(), browser.guid); + await import_fs.default.promises.mkdir(this._browsersDir(), { recursive: true }); + const descriptor = { + playwrightVersion: packageVersion, + playwrightLib: packageRoot, + title: endpoint.title, + browser, + endpoint: endpoint.endpoint, + workspaceDir: endpoint.workspaceDir + }; + await import_fs.default.promises.writeFile(file, JSON.stringify(descriptor, null, 2), "utf-8"); + } + async delete(guid) { + const file = import_path2.default.join(this._browsersDir(), guid); + await import_fs.default.promises.unlink(file).catch(() => { + }); + } + async deleteUserData(guid) { + const filePath = import_path2.default.join(this._browsersDir(), guid); + const content = await import_fs.default.promises.readFile(filePath, "utf-8"); + const descriptor = JSON.parse(content); + if (descriptor.browser.userDataDir) + await import_fs.default.promises.rm(descriptor.browser.userDataDir, { recursive: true, force: true }); + await import_fs.default.promises.unlink(filePath); + } + readDescriptor(guid) { + const cached = this._descriptors.get(guid); + if (cached) + return cached; + const filePath = import_path2.default.join(this._browsersDir(), guid); + const content = import_fs.default.readFileSync(filePath, "utf-8"); + return JSON.parse(content); + } + async find(name) { + const entries = await this.list(); + for (const [, browsers] of entries) { + for (const browser of browsers) { + if (browser.title === name) + return browser; + } + } + return null; + } + _browsersDir() { + return process.env.PWTEST_SERVER_REGISTRY || registryDirectory; + } + _startWatcher() { + const dir = this._browsersDir(); + try { + import_fs.default.mkdirSync(dir, { recursive: true }); + } catch { + } + const watcher = import_chokidar.default.watch(dir, { + ignoreInitial: false, + depth: 0, + awaitWriteFinish: { stabilityThreshold: 50, pollInterval: 20 } + }); + this._watcher = watcher; + this._ready = new Promise((resolve, reject) => { + watcher.once("ready", () => resolve()); + watcher.once("error", reject); + }); + watcher.on("add", (file) => this._onAddOrChange(file, "added")); + watcher.on("change", (file) => this._onAddOrChange(file, "changed")); + watcher.on("unlink", (file) => { + const guid = import_path2.default.basename(file); + if (this._descriptors.delete(guid)) + this.emit("removed", guid); + }); + } + _onAddOrChange(file, event) { + const guid = import_path2.default.basename(file); + let descriptor; + try { + descriptor = JSON.parse(import_fs.default.readFileSync(file, "utf-8")); + } catch { + return; + } + this._descriptors.set(guid, descriptor); + this.emit(event, descriptor); + } + _stopWatcher() { + const watcher = this._watcher; + this._watcher = void 0; + this._ready = void 0; + this._descriptors.clear(); + void watcher?.close(); + } +}; +async function canConnectTo(descriptor) { + if (!descriptor.endpoint) + return false; + if (descriptor.endpoint.startsWith("ws://") || descriptor.endpoint.startsWith("wss://")) { + return await new Promise((resolve) => { + const url = new URL(descriptor.endpoint); + const socket = import_net.default.createConnection(Number(url.port), url.hostname, () => { + socket.destroy(); + resolve(true); + }); + socket.on("error", () => resolve(false)); + }); + } + return await new Promise((resolve) => { + const socket = import_net.default.createConnection(descriptor.endpoint ?? descriptor.pipeName, () => { + socket.destroy(); + resolve(true); + }); + socket.on("error", () => resolve(false)); + }); +} +var defaultCacheDirectory = (() => { + if (process.platform === "linux") + return process.env.XDG_CACHE_HOME || import_path2.default.join(import_os.default.homedir(), ".cache"); + if (process.platform === "darwin") + return import_path2.default.join(import_os.default.homedir(), "Library", "Caches"); + if (process.platform === "win32") + return process.env.LOCALAPPDATA || import_path2.default.join(import_os.default.homedir(), "AppData", "Local"); + throw new Error("Unsupported platform: " + process.platform); +})(); +var registryDirectory = import_path2.default.join(defaultCacheDirectory, "ms-playwright", "b"); +var serverRegistry = new ServerRegistry(); +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + serverRegistry +}); +/*! Bundled license information: + +normalize-path/index.js: + (*! + * normalize-path + * + * Copyright (c) 2014-2018, Jon Schlinkert. + * Released under the MIT License. + *) + +is-extglob/index.js: + (*! + * is-extglob + * + * Copyright (c) 2014-2016, Jon Schlinkert. + * Licensed under the MIT License. + *) + +is-glob/index.js: + (*! + * is-glob + * + * Copyright (c) 2014-2017, Jon Schlinkert. + * Released under the MIT License. + *) + +is-number/index.js: + (*! + * is-number + * + * Copyright (c) 2014-present, Jon Schlinkert. + * Released under the MIT License. + *) + +to-regex-range/index.js: + (*! + * to-regex-range + * + * Copyright (c) 2015-present, Jon Schlinkert. + * Released under the MIT License. + *) + +fill-range/index.js: + (*! + * fill-range + * + * Copyright (c) 2014-present, Jon Schlinkert. + * Licensed under the MIT License. + *) +*/ diff --git a/simulator/e2e/node_modules/playwright-core/lib/serverRegistry.js.LICENSE b/simulator/e2e/node_modules/playwright-core/lib/serverRegistry.js.LICENSE new file mode 100644 index 0000000..8e2592a --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/lib/serverRegistry.js.LICENSE @@ -0,0 +1,354 @@ +packages/playwright-core/lib/serverRegistry.js + +THIRD-PARTY SOFTWARE NOTICES AND INFORMATION + +The following npm packages are inlined into this bundle. + +- anymatch@3.1.3 (https://github.com/micromatch/anymatch) +- binary-extensions@2.3.0 (https://github.com/sindresorhus/binary-extensions) +- braces@3.0.3 (https://github.com/micromatch/braces) +- chokidar@3.6.0 (https://github.com/paulmillr/chokidar) +- fill-range@7.1.1 (https://github.com/jonschlinkert/fill-range) +- glob-parent@5.1.2 (https://github.com/gulpjs/glob-parent) +- is-binary-path@2.1.0 (https://github.com/sindresorhus/is-binary-path) +- is-extglob@2.1.1 (https://github.com/jonschlinkert/is-extglob) +- is-glob@4.0.3 (https://github.com/micromatch/is-glob) +- is-number@7.0.0 (https://github.com/jonschlinkert/is-number) +- normalize-path@3.0.0 (https://github.com/jonschlinkert/normalize-path) +- picomatch@2.3.2 (https://github.com/micromatch/picomatch) +- readdirp@3.6.0 (https://github.com/paulmillr/readdirp) +- to-regex-range@5.0.1 (https://github.com/micromatch/to-regex-range) + +%% anymatch@3.1.3 NOTICES AND INFORMATION BEGIN HERE +========================================= +The ISC License + +Copyright (c) 2019 Elan Shanker, Paul Miller (https://paulmillr.com) + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +========================================= +END OF anymatch@3.1.3 NOTICES AND INFORMATION + +%% binary-extensions@2.3.0 NOTICES AND INFORMATION BEGIN HERE +========================================= +MIT License + +Copyright (c) Sindre Sorhus (https://sindresorhus.com) +Copyright (c) Paul Miller (https://paulmillr.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF binary-extensions@2.3.0 NOTICES AND INFORMATION + +%% braces@3.0.3 NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2014-present, Jon Schlinkert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF braces@3.0.3 NOTICES AND INFORMATION + +%% chokidar@3.6.0 NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2012-2019 Paul Miller (https://paulmillr.com), Elan Shanker + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the โSoftwareโ), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED โAS ISโ, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF chokidar@3.6.0 NOTICES AND INFORMATION + +%% fill-range@7.1.1 NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2014-present, Jon Schlinkert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF fill-range@7.1.1 NOTICES AND INFORMATION + +%% glob-parent@5.1.2 NOTICES AND INFORMATION BEGIN HERE +========================================= +The ISC License + +Copyright (c) 2015, 2019 Elan Shanker + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +========================================= +END OF glob-parent@5.1.2 NOTICES AND INFORMATION + +%% is-binary-path@2.1.0 NOTICES AND INFORMATION BEGIN HERE +========================================= +MIT License + +Copyright (c) 2019 Sindre Sorhus (https://sindresorhus.com), Paul Miller (https://paulmillr.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF is-binary-path@2.1.0 NOTICES AND INFORMATION + +%% is-extglob@2.1.1 NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2014-2016, Jon Schlinkert + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF is-extglob@2.1.1 NOTICES AND INFORMATION + +%% is-glob@4.0.3 NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2014-2017, Jon Schlinkert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF is-glob@4.0.3 NOTICES AND INFORMATION + +%% is-number@7.0.0 NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2014-present, Jon Schlinkert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF is-number@7.0.0 NOTICES AND INFORMATION + +%% normalize-path@3.0.0 NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2014-2018, Jon Schlinkert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF normalize-path@3.0.0 NOTICES AND INFORMATION + +%% picomatch@2.3.2 NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2017-present, Jon Schlinkert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF picomatch@2.3.2 NOTICES AND INFORMATION + +%% readdirp@3.6.0 NOTICES AND INFORMATION BEGIN HERE +========================================= +MIT License + +Copyright (c) 2012-2019 Thorsten Lorenz, Paul Miller (https://paulmillr.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +========================================= +END OF readdirp@3.6.0 NOTICES AND INFORMATION + +%% to-regex-range@5.0.1 NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2015-present, Jon Schlinkert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF to-regex-range@5.0.1 NOTICES AND INFORMATION + +SUMMARY +========================================= +Total Packages: 14 +========================================= \ No newline at end of file diff --git a/simulator/e2e/node_modules/playwright-core/lib/tools/cli-client/channelSessions.js b/simulator/e2e/node_modules/playwright-core/lib/tools/cli-client/channelSessions.js new file mode 100644 index 0000000..68749aa --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/lib/tools/cli-client/channelSessions.js @@ -0,0 +1,141 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var channelSessions_exports = {}; +__export(channelSessions_exports, { + isKnownChannel: () => isKnownChannel, + listChannelSessions: () => listChannelSessions +}); +module.exports = __toCommonJS(channelSessions_exports); +var import_fs = __toESM(require("fs")); +var import_net = __toESM(require("net")); +var import_os = __toESM(require("os")); +var import_path = __toESM(require("path")); +var import_extension = require("../utils/extension"); +function isKnownChannel(name) { + return channelToUserDataDir.has(name); +} +async function listChannelSessions() { + if (process.env.PWTEST_CLI_CHANNEL_SCAN_DISABLED_FOR_TEST) + return []; + const result = []; + for (const [channel, dirs] of channelToUserDataDir) { + const userDataDir = dirs[process.platform]; + if (!userDataDir) + continue; + if (!await pathExists(userDataDir)) + continue; + const [endpoint, extensionInstalled] = await Promise.all([ + readEndpoint(userDataDir), + (0, import_extension.isPlaywrightExtensionInstalled)(userDataDir) + ]); + result.push({ channel, userDataDir, endpoint, extensionInstalled }); + } + return result; +} +async function pathExists(p) { + try { + await import_fs.default.promises.access(p); + return true; + } catch { + return false; + } +} +async function readEndpoint(userDataDir) { + let contents; + try { + contents = await import_fs.default.promises.readFile(import_path.default.join(userDataDir, "DevToolsActivePort"), "utf-8"); + } catch { + return void 0; + } + const port = parseInt(contents.trim().split("\n")[0], 10); + if (!Number.isFinite(port)) + return void 0; + if (!await isPortOpen(port)) + return void 0; + return `http://localhost:${port}`; +} +async function isPortOpen(port) { + return new Promise((resolve) => { + const socket = import_net.default.createConnection(port, "127.0.0.1"); + const done = (value) => { + socket.destroy(); + resolve(value); + }; + socket.once("connect", () => done(true)); + socket.once("error", () => done(false)); + socket.setTimeout(250, () => done(false)); + }); +} +const channelToUserDataDir = /* @__PURE__ */ new Map([ + ["chrome", { + "linux": import_path.default.join(import_os.default.homedir(), ".config", "google-chrome"), + "darwin": import_path.default.join(import_os.default.homedir(), "Library", "Application Support", "Google", "Chrome"), + "win32": import_path.default.join(process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local"), "Google", "Chrome", "User Data") + }], + ["chrome-beta", { + "linux": import_path.default.join(import_os.default.homedir(), ".config", "google-chrome-beta"), + "darwin": import_path.default.join(import_os.default.homedir(), "Library", "Application Support", "Google", "Chrome Beta"), + "win32": import_path.default.join(process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local"), "Google", "Chrome Beta", "User Data") + }], + ["chrome-dev", { + "linux": import_path.default.join(import_os.default.homedir(), ".config", "google-chrome-unstable"), + "darwin": import_path.default.join(import_os.default.homedir(), "Library", "Application Support", "Google", "Chrome Dev"), + "win32": import_path.default.join(process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local"), "Google", "Chrome Dev", "User Data") + }], + ["chrome-canary", { + "linux": import_path.default.join(import_os.default.homedir(), ".config", "google-chrome-canary"), + "darwin": import_path.default.join(import_os.default.homedir(), "Library", "Application Support", "Google", "Chrome Canary"), + "win32": import_path.default.join(process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local"), "Google", "Chrome SxS", "User Data") + }], + ["msedge", { + "linux": import_path.default.join(import_os.default.homedir(), ".config", "microsoft-edge"), + "darwin": import_path.default.join(import_os.default.homedir(), "Library", "Application Support", "Microsoft Edge"), + "win32": import_path.default.join(process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local"), "Microsoft", "Edge", "User Data") + }], + ["msedge-beta", { + "linux": import_path.default.join(import_os.default.homedir(), ".config", "microsoft-edge-beta"), + "darwin": import_path.default.join(import_os.default.homedir(), "Library", "Application Support", "Microsoft Edge Beta"), + "win32": import_path.default.join(process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local"), "Microsoft", "Edge Beta", "User Data") + }], + ["msedge-dev", { + "linux": import_path.default.join(import_os.default.homedir(), ".config", "microsoft-edge-dev"), + "darwin": import_path.default.join(import_os.default.homedir(), "Library", "Application Support", "Microsoft Edge Dev"), + "win32": import_path.default.join(process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local"), "Microsoft", "Edge Dev", "User Data") + }], + ["msedge-canary", { + "linux": import_path.default.join(import_os.default.homedir(), ".config", "microsoft-edge-canary"), + "darwin": import_path.default.join(import_os.default.homedir(), "Library", "Application Support", "Microsoft Edge Canary"), + "win32": import_path.default.join(process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local"), "Microsoft", "Edge SxS", "User Data") + }] +]); +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + isKnownChannel, + listChannelSessions +}); diff --git a/simulator/e2e/node_modules/playwright-core/lib/tools/cli-client/cli.js b/simulator/e2e/node_modules/playwright-core/lib/tools/cli-client/cli.js new file mode 100644 index 0000000..52ec58a --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/lib/tools/cli-client/cli.js @@ -0,0 +1,6 @@ +"use strict"; +var import_program = require("./program"); +(0, import_program.program)().catch((e) => { + console.error(e.message); + process.exit(1); +}); diff --git a/simulator/e2e/node_modules/playwright-core/lib/tools/cli-client/help.json b/simulator/e2e/node_modules/playwright-core/lib/tools/cli-client/help.json new file mode 100644 index 0000000..d1ef427 --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/lib/tools/cli-client/help.json @@ -0,0 +1,693 @@ +{ + "global": "Usage: playwright-cli [args] [options]\nUsage: playwright-cli -s=
", "filter by status code").option("--failed", "only show failed requests (status >= 400)").action(async (options) => { + traceRequests(options).catch(logErrorAndExit2); + }); + traceCommand.command("request ").description("show details of a specific network request").action(async (requestId) => { + traceRequest(requestId).catch(logErrorAndExit2); + }); + traceCommand.command("console").description("show console messages").option("--errors-only", "only show errors").option("--warnings", "show errors and warnings").option("--browser", "only browser console messages").option("--stdio", "only stdout/stderr").action(async (options) => { + traceConsole(options).catch(logErrorAndExit2); + }); + traceCommand.command("errors").description("show errors with stack traces").action(async () => { + traceErrors().catch(logErrorAndExit2); + }); + traceCommand.command("snapshot ").description("run a playwright-cli command against a DOM snapshot").option("--name ", "snapshot phase: before, input, or after").option("--serve", "serve snapshot on localhost and keep running").allowUnknownOption(true).allowExcessArguments(true).action(async (actionId, options, cmd) => { + try { + const browserArgs = cmd.args.slice(1); + await traceSnapshot(actionId, { ...options, browserArgs }); + } catch (e) { + logErrorAndExit2(e); + } + }); + traceCommand.command("screenshot ").description("save screencast screenshot for an action").option("-o, --output ", "output file path").action(async (actionId, options) => { + traceScreenshot(actionId, options).catch(logErrorAndExit2); + }); + traceCommand.command("attachments").description("list trace attachments").action(async () => { + traceAttachments().catch(logErrorAndExit2); + }); + traceCommand.command("attachment ").description("extract a trace attachment by its number").option("-o, --output ", "output file path").action(async (attachmentId, options) => { + traceAttachment(attachmentId, options).catch(logErrorAndExit2); + }); + traceCommand.command("install-skill").description("install SKILL.md for LLM integration").action(async () => { + installSkill().catch(logErrorAndExit2); + }); +} +var init_traceCli = __esm({ + "packages/playwright-core/src/tools/trace/traceCli.ts"() { + "use strict"; + init_traceOpen(); + init_traceUtils2(); + init_traceActions(); + init_traceActions(); + init_traceRequests(); + init_traceRequests(); + init_traceConsole(); + init_traceErrors(); + init_traceSnapshot(); + init_traceScreenshot(); + init_traceAttachments(); + init_traceAttachments(); + init_installSkill(); + } +}); + +// packages/playwright-core/src/cli/driver.ts +function printApiJson() { + console.log(JSON.stringify(require(import_path43.default.join(packageRoot, "api.json")))); +} +function runDriver() { + const dispatcherConnection = new DispatcherConnection(); + new RootDispatcher(dispatcherConnection, async (rootScope, { sdkLanguage }) => { + const playwright2 = createPlaywright({ sdkLanguage, isClientCollocatedWithServer: true }); + return new PlaywrightDispatcher(rootScope, playwright2); + }); + const transport = new PipeTransport(process.stdout, process.stdin); + transport.onmessage = (message) => dispatcherConnection.dispatch(JSON.parse(message)); + const isJavaScriptLanguageBinding = !process.env.PW_LANG_NAME || process.env.PW_LANG_NAME === "javascript"; + const replacer = !isJavaScriptLanguageBinding && String.prototype.toWellFormed ? (key, value2) => { + if (typeof value2 === "string") + return value2.toWellFormed(); + return value2; + } : void 0; + dispatcherConnection.onmessage = (message) => transport.send(JSON.stringify(message, replacer)); + transport.onclose = () => { + dispatcherConnection.onmessage = () => { + }; + gracefullyProcessExitDoNotHang(0); + }; + process.on("SIGINT", () => { + }); +} +async function runServer(options) { + const { + port, + host, + path: path59 = "/", + maxConnections = Infinity, + extension, + artifactsDir, + unsafe + } = options; + const server = new PlaywrightServer({ mode: extension ? "extension" : "default", path: path59, maxConnections, artifactsDir, unsafe }); + const wsEndpoint = await server.listen(port, host); + process.on("exit", () => server.close().catch(console.error)); + console.log("Listening on " + wsEndpoint); + process.stdin.on("close", () => gracefullyProcessExitDoNotHang(0)); +} +async function launchBrowserServer(browserName, configFile) { + let options = {}; + if (configFile) + options = JSON.parse(import_fs48.default.readFileSync(configFile).toString()); + const browserType = playwright[browserName]; + const server = await browserType.launchServer(options); + console.log(server.wsEndpoint()); +} +var import_fs48, import_path43; +var init_driver = __esm({ + "packages/playwright-core/src/cli/driver.ts"() { + "use strict"; + import_fs48 = __toESM(require("fs")); + import_path43 = __toESM(require("path")); + init_pipeTransport(); + init_processLauncher(); + init_package(); + init_inprocess(); + init_playwrightServer(); + init_server(); + } +}); + +// packages/playwright-core/src/cli/installActions.ts +function printInstalledBrowsers(browsers) { + const browserPaths = /* @__PURE__ */ new Set(); + for (const browser of browsers) + browserPaths.add(browser.browserPath); + console.log(` Browsers:`); + for (const browserPath of [...browserPaths].sort()) + console.log(` ${browserPath}`); + console.log(` References:`); + const references = /* @__PURE__ */ new Set(); + for (const browser of browsers) + references.add(browser.referenceDir); + for (const reference of [...references].sort()) + console.log(` ${reference}`); +} +function printGroupedByPlaywrightVersion(browsers) { + const dirToVersion = /* @__PURE__ */ new Map(); + for (const browser of browsers) { + if (dirToVersion.has(browser.referenceDir)) + continue; + const packageJSON2 = require(import_path44.default.join(browser.referenceDir, "package.json")); + const version3 = packageJSON2.version; + dirToVersion.set(browser.referenceDir, version3); + } + const groupedByPlaywrightMinorVersion = /* @__PURE__ */ new Map(); + for (const browser of browsers) { + const version3 = dirToVersion.get(browser.referenceDir); + let entries = groupedByPlaywrightMinorVersion.get(version3); + if (!entries) { + entries = []; + groupedByPlaywrightMinorVersion.set(version3, entries); + } + entries.push(browser); + } + const sortedVersions = [...groupedByPlaywrightMinorVersion.keys()].sort((a, b) => { + const aComponents = a.split("."); + const bComponents = b.split("."); + const aMajor = parseInt(aComponents[0], 10); + const bMajor = parseInt(bComponents[0], 10); + if (aMajor !== bMajor) + return aMajor - bMajor; + const aMinor = parseInt(aComponents[1], 10); + const bMinor = parseInt(bComponents[1], 10); + if (aMinor !== bMinor) + return aMinor - bMinor; + return aComponents.slice(2).join(".").localeCompare(bComponents.slice(2).join(".")); + }); + for (const version3 of sortedVersions) { + console.log(` +Playwright version: ${version3}`); + printInstalledBrowsers(groupedByPlaywrightMinorVersion.get(version3)); + } +} +async function markDockerImage(dockerImageNameTemplate) { + assert(dockerImageNameTemplate, "dockerImageNameTemplate is required"); + await writeDockerVersion(dockerImageNameTemplate); +} +async function installBrowsers(args, options) { + if (options.progress === false) + process.env.PLAYWRIGHT_DOWNLOAD_NO_PROGRESS = "1"; + if (isLikelyNpxGlobal()) { + console.error(wrapInASCIIBox([ + `WARNING: It looks like you are running 'npx playwright install' without first`, + `installing your project's dependencies.`, + ``, + `To avoid unexpected behavior, please install your dependencies first, and`, + `then run Playwright's install command:`, + ``, + ` npm install`, + ` npx playwright install`, + ``, + `If your project does not yet depend on Playwright, first install the`, + `applicable npm package (most commonly @playwright/test), and`, + `then run Playwright's install command to download the browsers:`, + ``, + ` npm install @playwright/test`, + ` npx playwright install`, + `` + ].join("\n"), 1)); + } + if (options.shell === false && options.onlyShell) + throw new Error(`Only one of --no-shell and --only-shell can be specified`); + const shell = options.shell === false ? "no" : options.onlyShell ? "only" : void 0; + const executables = registry.resolveBrowsers(args, { shell }); + if (options.withDeps) + await registry.installDeps(executables, !!options.dryRun); + if (options.dryRun && options.list) + throw new Error(`Only one of --dry-run and --list can be specified`); + if (options.dryRun) { + for (const executable of executables) { + console.log(registry.calculateDownloadTitle(executable)); + console.log(` Install location: ${executable.directory ?? ""}`); + if (executable.downloadURLs?.length) { + const [url2, ...fallbacks] = executable.downloadURLs; + console.log(` Download url: ${url2}`); + for (let i = 0; i < fallbacks.length; ++i) + console.log(` Download fallback ${i + 1}: ${fallbacks[i]}`); + } + console.log(``); + } + } else if (options.list) { + const browsers = await registry.listInstalledBrowsers(); + printGroupedByPlaywrightVersion(browsers); + } else { + await registry.install(executables, { force: options.force }); + await registry.validateHostRequirementsForExecutablesIfNeeded(executables, process.env.PW_LANG_NAME || "javascript").catch((e) => { + e.name = "Playwright Host validation warning"; + console.error(e); + }); + } +} +async function uninstallBrowsers(options) { + delete process.env.PLAYWRIGHT_SKIP_BROWSER_GC; + await registry.uninstall(!!options.all).then(({ numberOfBrowsersLeft }) => { + if (!options.all && numberOfBrowsersLeft > 0) { + console.log("Successfully uninstalled Playwright browsers for the current Playwright installation."); + console.log(`There are still ${numberOfBrowsersLeft} browsers left, used by other Playwright installations. +To uninstall Playwright browsers for all installations, re-run with --all flag.`); + } + }); +} +async function installDeps(args, options) { + await registry.installDeps(registry.resolveBrowsers(args, {}), !!options.dryRun); +} +var import_path44; +var init_installActions = __esm({ + "packages/playwright-core/src/cli/installActions.ts"() { + "use strict"; + import_path44 = __toESM(require("path")); + init_ascii(); + init_env(); + init_assert(); + init_registry(); + } +}); + +// packages/playwright-core/src/cli/browserActions.ts +async function launchContext(options, extraOptions) { + validateOptions(options); + const browserType = lookupBrowserType(options); + const launchOptions = extraOptions; + if (options.channel) + launchOptions.channel = options.channel; + launchOptions.handleSIGINT = false; + const contextOptions = ( + // Copy the device descriptor since we have to compare and modify the options. + options.device ? { ...playwright.devices[options.device] } : {} + ); + if (!extraOptions.headless) + contextOptions.deviceScaleFactor = import_os20.default.platform() === "darwin" ? 2 : 1; + if (browserType.name() === "webkit" && process.platform === "linux") { + delete contextOptions.hasTouch; + delete contextOptions.isMobile; + } + if (options.blockServiceWorkers) + contextOptions.serviceWorkers = "block"; + if (options.proxyServer) { + launchOptions.proxy = { + server: options.proxyServer + }; + if (options.proxyBypass) + launchOptions.proxy.bypass = options.proxyBypass; + } + if (options.viewportSize) { + try { + const [width, height] = options.viewportSize.split(",").map((n) => +n); + if (isNaN(width) || isNaN(height)) + throw new Error("bad values"); + contextOptions.viewport = { width, height }; + } catch (e) { + throw new Error('Invalid viewport size format: use "width,height", for example --viewport-size="800,600"'); + } + } + if (options.geolocation) { + try { + const [latitude, longitude] = options.geolocation.split(",").map((n) => parseFloat(n.trim())); + contextOptions.geolocation = { + latitude, + longitude + }; + } catch (e) { + throw new Error('Invalid geolocation format, should be "lat,long". For example --geolocation="37.819722,-122.478611"'); + } + contextOptions.permissions = ["geolocation"]; + } + if (options.userAgent) + contextOptions.userAgent = options.userAgent; + if (options.lang) + contextOptions.locale = options.lang; + if (options.colorScheme) + contextOptions.colorScheme = options.colorScheme; + if (options.timezone) + contextOptions.timezoneId = options.timezone; + if (options.loadStorage) + contextOptions.storageState = options.loadStorage; + if (options.ignoreHttpsErrors) + contextOptions.ignoreHTTPSErrors = true; + if (options.saveHar) { + contextOptions.recordHar = { path: import_path45.default.resolve(process.cwd(), options.saveHar), mode: "minimal" }; + if (options.saveHarGlob) + contextOptions.recordHar.urlFilter = options.saveHarGlob; + contextOptions.serviceWorkers = "block"; + } + let browser; + let context2; + if (options.userDataDir) { + context2 = await browserType.launchPersistentContext(options.userDataDir, { ...launchOptions, ...contextOptions }); + browser = context2.browser(); + } else { + browser = await browserType.launch(launchOptions); + context2 = await browser.newContext(contextOptions); + } + let closingBrowser = false; + async function closeBrowser() { + if (closingBrowser) + return; + closingBrowser = true; + if (options.saveStorage) + await context2.storageState({ path: options.saveStorage }).catch((e) => null); + if (options.saveHar) + await context2.close(); + await browser.close(); + } + context2.on("page", (page) => { + page.on("dialog", () => { + }); + page.on("close", () => { + const hasPage = browser.contexts().some((context3) => context3.pages().length > 0); + if (hasPage) + return; + closeBrowser().catch(() => { + }); + }); + }); + process.on("SIGINT", async () => { + await closeBrowser(); + gracefullyProcessExitDoNotHang(130); + }); + const timeout = options.timeout ? parseInt(options.timeout, 10) : 0; + context2.setDefaultTimeout(timeout); + context2.setDefaultNavigationTimeout(timeout); + delete launchOptions.headless; + delete launchOptions.executablePath; + delete launchOptions.handleSIGINT; + delete contextOptions.deviceScaleFactor; + return { browser, browserName: browserType.name(), context: context2, contextOptions, launchOptions, closeBrowser }; +} +async function openPage(context2, url2) { + let page = context2.pages()[0]; + if (!page) + page = await context2.newPage(); + if (url2) { + if (import_fs49.default.existsSync(url2)) + url2 = "file://" + import_path45.default.resolve(url2); + else if (!url2.startsWith("http") && !url2.startsWith("file://") && !url2.startsWith("about:") && !url2.startsWith("data:")) + url2 = "http://" + url2; + await page.goto(url2); + } + return page; +} +async function open4(options, url2) { + const { context: context2 } = await launchContext(options, { headless: !!process.env.PWTEST_CLI_HEADLESS, executablePath: process.env.PWTEST_CLI_EXECUTABLE_PATH }); + await context2._exposeConsoleApi(); + await openPage(context2, url2); +} +async function codegen(options, url2) { + const { target: language, output: outputFile2, testIdAttribute: testIdAttributeName2 } = options; + const tracesDir = import_path45.default.join(import_os20.default.tmpdir(), `playwright-recorder-trace-${Date.now()}`); + const { context: context2, browser, launchOptions, contextOptions, closeBrowser } = await launchContext(options, { + headless: !!process.env.PWTEST_CLI_HEADLESS, + executablePath: process.env.PWTEST_CLI_EXECUTABLE_PATH, + tracesDir + }); + const donePromise = new ManualPromise(); + maybeSetupTestHooks(browser, closeBrowser, donePromise); + dotenv.config({ path: "playwright.env" }); + await context2._enableRecorder({ + language, + launchOptions, + contextOptions, + device: options.device, + saveStorage: options.saveStorage, + mode: "recording", + testIdAttributeName: testIdAttributeName2, + outputFile: outputFile2 ? import_path45.default.resolve(outputFile2) : void 0, + handleSIGINT: false + }); + await openPage(context2, url2); + donePromise.resolve(); +} +async function maybeSetupTestHooks(browser, closeBrowser, donePromise) { + if (!process.env.PWTEST_CLI_IS_UNDER_TEST) + return; + const logs = []; + debug11.log = (...args) => { + const line = require("util").format(...args) + "\n"; + logs.push(line); + process.stderr.write(line); + }; + browser.on("disconnected", () => { + const hasCrashLine = logs.some((line) => line.includes("process did exit:") && !line.includes("process did exit: exitCode=0, signal=null")); + if (hasCrashLine) { + process.stderr.write("Detected browser crash.\n"); + gracefullyProcessExitDoNotHang(1); + } + }); + const close3 = async () => { + await donePromise; + await closeBrowser(); + }; + if (process.env.PWTEST_CLI_EXIT_AFTER_TIMEOUT) { + setTimeout(close3, +process.env.PWTEST_CLI_EXIT_AFTER_TIMEOUT); + return; + } + let stdin = ""; + process.stdin.on("data", (data) => { + stdin += data.toString(); + if (stdin.startsWith("exit")) { + process.stdin.destroy(); + close3(); + } + }); +} +async function waitForPage(page, captureOptions) { + if (captureOptions.waitForSelector) { + console.log(`Waiting for selector ${captureOptions.waitForSelector}...`); + await page.waitForSelector(captureOptions.waitForSelector); + } + if (captureOptions.waitForTimeout) { + console.log(`Waiting for timeout ${captureOptions.waitForTimeout}...`); + await page.waitForTimeout(parseInt(captureOptions.waitForTimeout, 10)); + } +} +async function screenshot3(options, captureOptions, url2, path59) { + const { context: context2 } = await launchContext(options, { headless: true }); + console.log("Navigating to " + url2); + const page = await openPage(context2, url2); + await waitForPage(page, captureOptions); + console.log("Capturing screenshot into " + path59); + await page.screenshot({ path: path59, fullPage: !!captureOptions.fullPage }); + await page.close(); +} +async function pdf2(options, captureOptions, url2, path59) { + if (options.browser !== "chromium") + throw new Error("PDF creation is only working with Chromium"); + const { context: context2 } = await launchContext({ ...options, browser: "chromium" }, { headless: true }); + console.log("Navigating to " + url2); + const page = await openPage(context2, url2); + await waitForPage(page, captureOptions); + console.log("Saving as pdf into " + path59); + await page.pdf({ path: path59, format: captureOptions.paperFormat }); + await page.close(); +} +function lookupBrowserType(options) { + let name = options.browser; + if (options.device) { + const device = playwright.devices[options.device]; + name = device.defaultBrowserType; + } + let browserType; + switch (name) { + case "chromium": + browserType = playwright.chromium; + break; + case "webkit": + browserType = playwright.webkit; + break; + case "firefox": + browserType = playwright.firefox; + break; + case "cr": + browserType = playwright.chromium; + break; + case "wk": + browserType = playwright.webkit; + break; + case "ff": + browserType = playwright.firefox; + break; + } + if (browserType) + return browserType; + program.help(); +} +function validateOptions(options) { + if (options.device && !(options.device in playwright.devices)) { + const lines = [`Device descriptor not found: '${options.device}', available devices are:`]; + for (const name in playwright.devices) + lines.push(` "${name}"`); + throw new Error(lines.join("\n")); + } + if (options.colorScheme && !["light", "dark"].includes(options.colorScheme)) + throw new Error('Invalid color scheme, should be one of "light", "dark"'); +} +var import_fs49, import_os20, import_path45, debug11, dotenv, program; +var init_browserActions = __esm({ + "packages/playwright-core/src/cli/browserActions.ts"() { + "use strict"; + import_fs49 = __toESM(require("fs")); + import_os20 = __toESM(require("os")); + import_path45 = __toESM(require("path")); + init_processLauncher(); + init_manualPromise(); + init_inprocess(); + debug11 = require("./utilsBundle").debug; + dotenv = require("./utilsBundle").dotenv; + ({ program } = require("./utilsBundle")); + } +}); + +// packages/playwright-core/src/tools/utils/extension.ts +async function isPlaywrightExtensionInstalled(userDataDir) { + let entries; + try { + entries = await import_fs50.default.promises.readdir(userDataDir); + } catch { + return false; + } + for (const entry of entries) { + if (entry !== "Default" && !entry.startsWith("Profile ")) + continue; + if (await isExtensionInstalledInProfile(import_path46.default.join(userDataDir, entry))) + return true; + } + return false; +} +async function isExtensionInstalledInProfile(profileDir2) { + if (await pathExists(import_path46.default.join(profileDir2, "Extensions", playwrightExtensionId))) + return true; + try { + const prefs = await import_fs50.default.promises.readFile(import_path46.default.join(profileDir2, "Preferences"), "utf-8"); + return prefs.includes(`"${playwrightExtensionId}"`); + } catch { + return false; + } +} +async function pathExists(p) { + try { + await import_fs50.default.promises.access(p); + return true; + } catch { + return false; + } +} +var import_fs50, import_path46, playwrightExtensionId, playwrightExtensionInstallUrl; +var init_extension = __esm({ + "packages/playwright-core/src/tools/utils/extension.ts"() { + "use strict"; + import_fs50 = __toESM(require("fs")); + import_path46 = __toESM(require("path")); + playwrightExtensionId = "mmlmfjhmonkocbjadbfplnigmagldckm"; + playwrightExtensionInstallUrl = `https://chromewebstore.google.com/detail/playwright-extension/${playwrightExtensionId}`; + } +}); + +// packages/playwright-core/src/tools/cli-client/channelSessions.ts +function isKnownChannel(name) { + return channelToUserDataDir.has(name); +} +async function listChannelSessions() { + if (process.env.PWTEST_CLI_CHANNEL_SCAN_DISABLED_FOR_TEST) + return []; + const result2 = []; + for (const [channel, dirs] of channelToUserDataDir) { + const userDataDir = dirs[process.platform]; + if (!userDataDir) + continue; + if (!await pathExists2(userDataDir)) + continue; + const [endpoint, extensionInstalled] = await Promise.all([ + readEndpoint(userDataDir), + isPlaywrightExtensionInstalled(userDataDir) + ]); + result2.push({ channel, userDataDir, endpoint, extensionInstalled }); + } + return result2; +} +async function pathExists2(p) { + try { + await import_fs51.default.promises.access(p); + return true; + } catch { + return false; + } +} +async function readEndpoint(userDataDir) { + let contents; + try { + contents = await import_fs51.default.promises.readFile(import_path47.default.join(userDataDir, "DevToolsActivePort"), "utf-8"); + } catch { + return void 0; + } + const port = parseInt(contents.trim().split("\n")[0], 10); + if (!Number.isFinite(port)) + return void 0; + if (!await isPortOpen(port)) + return void 0; + return `http://localhost:${port}`; +} +async function isPortOpen(port) { + return new Promise((resolve) => { + const socket = import_net8.default.createConnection(port, "127.0.0.1"); + const done = (value2) => { + socket.destroy(); + resolve(value2); + }; + socket.once("connect", () => done(true)); + socket.once("error", () => done(false)); + socket.setTimeout(250, () => done(false)); + }); +} +var import_fs51, import_net8, import_os21, import_path47, channelToUserDataDir; +var init_channelSessions = __esm({ + "packages/playwright-core/src/tools/cli-client/channelSessions.ts"() { + "use strict"; + import_fs51 = __toESM(require("fs")); + import_net8 = __toESM(require("net")); + import_os21 = __toESM(require("os")); + import_path47 = __toESM(require("path")); + init_extension(); + channelToUserDataDir = /* @__PURE__ */ new Map([ + ["chrome", { + "linux": import_path47.default.join(import_os21.default.homedir(), ".config", "google-chrome"), + "darwin": import_path47.default.join(import_os21.default.homedir(), "Library", "Application Support", "Google", "Chrome"), + "win32": import_path47.default.join(process.env.LOCALAPPDATA || import_path47.default.join(import_os21.default.homedir(), "AppData", "Local"), "Google", "Chrome", "User Data") + }], + ["chrome-beta", { + "linux": import_path47.default.join(import_os21.default.homedir(), ".config", "google-chrome-beta"), + "darwin": import_path47.default.join(import_os21.default.homedir(), "Library", "Application Support", "Google", "Chrome Beta"), + "win32": import_path47.default.join(process.env.LOCALAPPDATA || import_path47.default.join(import_os21.default.homedir(), "AppData", "Local"), "Google", "Chrome Beta", "User Data") + }], + ["chrome-dev", { + "linux": import_path47.default.join(import_os21.default.homedir(), ".config", "google-chrome-unstable"), + "darwin": import_path47.default.join(import_os21.default.homedir(), "Library", "Application Support", "Google", "Chrome Dev"), + "win32": import_path47.default.join(process.env.LOCALAPPDATA || import_path47.default.join(import_os21.default.homedir(), "AppData", "Local"), "Google", "Chrome Dev", "User Data") + }], + ["chrome-canary", { + "linux": import_path47.default.join(import_os21.default.homedir(), ".config", "google-chrome-canary"), + "darwin": import_path47.default.join(import_os21.default.homedir(), "Library", "Application Support", "Google", "Chrome Canary"), + "win32": import_path47.default.join(process.env.LOCALAPPDATA || import_path47.default.join(import_os21.default.homedir(), "AppData", "Local"), "Google", "Chrome SxS", "User Data") + }], + ["msedge", { + "linux": import_path47.default.join(import_os21.default.homedir(), ".config", "microsoft-edge"), + "darwin": import_path47.default.join(import_os21.default.homedir(), "Library", "Application Support", "Microsoft Edge"), + "win32": import_path47.default.join(process.env.LOCALAPPDATA || import_path47.default.join(import_os21.default.homedir(), "AppData", "Local"), "Microsoft", "Edge", "User Data") + }], + ["msedge-beta", { + "linux": import_path47.default.join(import_os21.default.homedir(), ".config", "microsoft-edge-beta"), + "darwin": import_path47.default.join(import_os21.default.homedir(), "Library", "Application Support", "Microsoft Edge Beta"), + "win32": import_path47.default.join(process.env.LOCALAPPDATA || import_path47.default.join(import_os21.default.homedir(), "AppData", "Local"), "Microsoft", "Edge Beta", "User Data") + }], + ["msedge-dev", { + "linux": import_path47.default.join(import_os21.default.homedir(), ".config", "microsoft-edge-dev"), + "darwin": import_path47.default.join(import_os21.default.homedir(), "Library", "Application Support", "Microsoft Edge Dev"), + "win32": import_path47.default.join(process.env.LOCALAPPDATA || import_path47.default.join(import_os21.default.homedir(), "AppData", "Local"), "Microsoft", "Edge Dev", "User Data") + }], + ["msedge-canary", { + "linux": import_path47.default.join(import_os21.default.homedir(), ".config", "microsoft-edge-canary"), + "darwin": import_path47.default.join(import_os21.default.homedir(), "Library", "Application Support", "Microsoft Edge Canary"), + "win32": import_path47.default.join(process.env.LOCALAPPDATA || import_path47.default.join(import_os21.default.homedir(), "AppData", "Local"), "Microsoft", "Edge SxS", "User Data") + }] + ]); + } +}); + +// packages/playwright-core/src/tools/cli-client/output.ts +function parseJsonText(text2) { + try { + return JSON.parse(text2); + } catch { + return text2; + } +} +function renderBrowser(browser) { + const lines = [`- ${browser.name}:`]; + lines.push(` - status: ${browser.status}`); + if (browser.status === "open" && !browser.compatible) + lines.push(` - version: v${browser.version} [incompatible please re-open]`); + if (browser.browserType) + lines.push(` - browser-type: ${browser.browserType}${browser.attached ? " (attached)" : ""}`); + if (!browser.attached) { + if (browser.userDataDir === null) + lines.push(` - user-data-dir: `); + else + lines.push(` - user-data-dir: ${browser.userDataDir}`); + if (browser.headed !== void 0) + lines.push(` - headed: ${browser.headed}`); + } + return lines.join("\n"); +} +function renderServer(server) { + const lines = [`- browser "${server.title}":`]; + lines.push(` - browser: ${server.browser.browserName}`); + lines.push(` - version: v${server.playwrightVersion}`); + if (server.browser.userDataDir) + lines.push(` - data-dir: ${server.browser.userDataDir}`); + else + lines.push(` - data-dir: `); + lines.push(` - run \`playwright-cli attach "${server.title}"\` to attach`); + return lines.join("\n"); +} +function renderChannelSession(session2) { + const lines = [`- ${session2.channel}:`]; + lines.push(` - data-dir: ${session2.userDataDir}`); + if (session2.extensionInstalled) + lines.push(` - attach (extension): \`playwright-cli attach --extension=${session2.channel}\``); + else + lines.push(` - attach (extension): install at ${playwrightExtensionInstallUrl}`); + if (session2.endpoint) { + lines.push(` - attach (remote debugging): \`playwright-cli attach --cdp=${session2.channel}\``); + } else { + const inspectScheme = session2.channel.startsWith("msedge") ? "edge" : "chrome"; + lines.push(` - attach (remote debugging): enable at ${inspectScheme}://inspect/#remote-debugging`); + } + return lines.join("\n"); +} +var import_path48, TextOutput, JsonOutput; +var init_output = __esm({ + "packages/playwright-core/src/tools/cli-client/output.ts"() { + "use strict"; + import_path48 = __toESM(require("path")); + init_extension(); + TextOutput = class { + constructor() { + this.json = false; + } + version(v) { + console.log(v); + } + help(text2) { + console.log(text2); + } + errorUnknownCommand(name, globalHelp) { + console.error(`Unknown command: ${name} +`); + console.log(globalHelp); + return process.exit(1); + } + errorUnknownOption(opts, commandHelp) { + console.error(`Unknown option${opts.length > 1 ? "s" : ""}: ${opts.map((f) => `--${f}`).join(", ")}`); + console.log(""); + console.log(commandHelp); + return process.exit(1); + } + errorTooManyArguments(expected, received, commandHelp) { + console.error(`error: too many arguments: expected ${expected}, received ${received}`); + console.log(""); + console.log(commandHelp); + return process.exit(1); + } + errorAttachConflict() { + console.error(`Error: only one of [name], --cdp, --endpoint, or --extension can be specified`); + return process.exit(1); + } + errorDetachNotAttached(session2) { + console.error(`Error: session '${session2}' was not attached; use \`playwright-cli${session2 !== "default" ? ` -s=${session2}` : ""} close\` to stop it.`); + return process.exit(1); + } + errorBrowserNotOpenForTool(session2) { + console.log(`The browser '${session2}' is not open, please run open first`); + console.log(""); + console.log(` playwright-cli${session2 !== "default" ? ` -s=${session2}` : ""} open [params]`); + return process.exit(1); + } + errorAttachNoTarget() { + console.error(`Error: no target specified for attach command; use one of [name], --cdp, --endpoint, or --extension to specify the target to attach to.`); + return process.exit(1); + } + list({ all, browsers, servers, channelSessions }) { + const byWorkspace = /* @__PURE__ */ new Map(); + for (const browser of browsers) { + let list = byWorkspace.get(browser.workspace); + if (!list) { + list = []; + byWorkspace.set(browser.workspace, list); + } + list.push(browser); + } + let count = 0; + for (const [workspaceKey, list] of byWorkspace) { + if (count === 0) + console.log("### Browsers"); + if (all) + console.log(`${import_path48.default.relative(process.cwd(), workspaceKey) || "/"}:`); + for (const browser of list) + console.log(renderBrowser(browser)); + count += list.length; + } + if (!all) { + if (!count) + console.log(" (no browsers)"); + return; + } + if (servers?.length) { + if (count) + console.log(""); + console.log("### Browser servers available for attach"); + const serversByWorkspace = /* @__PURE__ */ new Map(); + for (const server of servers) { + let list = serversByWorkspace.get(server.workspaceDir ?? ""); + if (!list) { + list = []; + serversByWorkspace.set(server.workspaceDir ?? "", list); + } + list.push(server); + } + for (const [workspaceKey, list] of serversByWorkspace) { + if (workspaceKey) + console.log(`${import_path48.default.relative(process.cwd(), workspaceKey) || "/"}:`); + for (const server of list) + console.log(renderServer(server)); + } + count += servers.length; + } + if (!count) + console.log(" (no browsers)"); + if (channelSessions?.length) { + console.log(""); + console.log("### Browsers available to attach via CDP"); + for (const session2 of channelSessions) + console.log(renderChannelSession(session2)); + } + } + closeAll(_sessions) { + } + deleteData(session2, result2) { + if (!result2.existed) { + console.log(`No user data found for browser '${session2}'.`); + return; + } + if (result2.deletedUserDataDir) + console.log(`Deleted user data for browser '${session2}'.`); + } + killAll(pids) { + for (const pid of pids) + console.log(`Killed daemon process ${pid}`); + if (pids.length === 0) + console.log("No daemon processes found."); + else + console.log(`Killed ${pids.length} daemon process${pids.length === 1 ? "" : "es"}.`); + } + open(session2, pid, toolResult) { + console.log(`### Browser \`${session2}\` opened with pid ${pid}.`); + if (toolResult) + console.log(toolResult); + } + attach(session2, pid, endpoint, toolResult) { + if (endpoint) { + console.log(`### Session \`${session2}\` created, attached to \`${endpoint}\`.`); + console.log(`Run commands with: playwright-cli --s=${session2} `); + console.log(""); + } else { + console.log(`### Browser \`${session2}\` opened with pid ${pid}.`); + } + if (toolResult) + console.log(toolResult); + } + close(session2, wasOpen) { + if (!wasOpen) { + console.log(`Browser '${session2}' is not open.`); + return; + } + console.log(`Browser '${session2}' closed +`); + } + detach(session2, wasAttached) { + if (!wasAttached) { + console.log(`Browser '${session2}' is not attached.`); + return; + } + console.log(`Browser '${session2}' detached +`); + } + installed() { + } + show(_session, pid) { + if (process.env.PWTEST_PRINT_DASHBOARD_PID_FOR_TEST) + console.log(`### Dashboard opened with pid ${pid}.`); + } + toolResult(text2) { + console.log(text2); + } + installStdio() { + return "inherit"; + } + }; + JsonOutput = class { + constructor() { + this.json = true; + } + version(v) { + this._emit({ version: v }); + } + help(text2) { + this._emit({ help: text2 }); + } + errorUnknownCommand(name, _globalHelp) { + this._emit({ isError: true, error: `Unknown command: ${name}` }); + return process.exit(1); + } + errorUnknownOption(opts, _commandHelp) { + this._emit({ isError: true, error: `Unknown option${opts.length > 1 ? "s" : ""}: ${opts.map((f) => `--${f}`).join(", ")}` }); + return process.exit(1); + } + errorTooManyArguments(expected, received, _commandHelp) { + this._emit({ isError: true, error: `error: too many arguments: expected ${expected}, received ${received}` }); + return process.exit(1); + } + errorAttachConflict() { + this._emit({ isError: true, error: `only one of [name], --cdp, --endpoint, or --extension can be specified` }); + return process.exit(1); + } + errorDetachNotAttached(session2) { + this._emit({ isError: true, error: `session '${session2}' was not attached; use close to stop it.` }); + return process.exit(1); + } + errorBrowserNotOpenForTool(session2) { + this._emit({ isError: true, error: `The browser '${session2}' is not open, please run open first` }); + return process.exit(1); + } + errorAttachNoTarget() { + this._emit({ isError: true, error: `no target specified for attach command; use one of [name], --cdp, --endpoint, or --extension to specify the target to attach to.` }); + return process.exit(1); + } + list({ all, browsers, servers, channelSessions }) { + const payload = { browsers }; + if (all) { + payload.servers = servers ?? []; + payload.channelSessions = channelSessions ?? []; + } + this._emit(payload); + } + closeAll(sessions) { + this._emit({ closed: sessions }); + } + deleteData(session2, result2) { + this._emit({ session: session2, deleted: result2.existed }); + } + killAll(pids) { + this._emit({ killed: pids.length, pids }); + } + open(session2, pid, toolResult) { + this._emit({ session: session2, pid, result: parseJsonText(toolResult) }); + } + attach(session2, pid, endpoint, toolResult) { + this._emit({ + session: session2, + pid, + ...endpoint ? { endpoint } : {}, + result: parseJsonText(toolResult) + }); + } + close(session2, wasOpen) { + this._emit({ session: session2, status: wasOpen ? "closed" : "not-open" }); + } + detach(session2, wasAttached) { + this._emit({ session: session2, status: wasAttached ? "detached" : "not-attached" }); + } + installed() { + this._emit({ installed: true }); + } + show(session2, pid) { + this._emit({ session: session2, pid }); + } + toolResult(text2) { + console.log(text2); + } + installStdio() { + return "ignore"; + } + _emit(value2) { + console.log(JSON.stringify(value2, null, 2)); + } + }; + } +}); + +// packages/playwright-core/src/tools/cli-client/registry.ts +function clientKey(clientInfo) { + return clientInfo.workspaceDir || clientInfo.workspaceDirHash; +} +function createClientInfo() { + const workspaceDir = findWorkspaceDir(process.cwd()); + const version3 = process.env.PLAYWRIGHT_CLI_VERSION_FOR_TEST || packageJSON.version; + const hash = import_crypto26.default.createHash("sha1"); + hash.update(workspaceDir || packageRoot); + const workspaceDirHash = hash.digest("hex").substring(0, 16); + return { + version: version3, + workspaceDir, + workspaceDirHash, + daemonProfilesDir: daemonProfilesDir(workspaceDirHash), + homeDir: import_os22.default.homedir() + }; +} +function findWorkspaceDir(startDir) { + let dir = startDir; + for (let i = 0; i < 10; i++) { + if (import_fs52.default.existsSync(import_path49.default.join(dir, ".playwright"))) + return dir; + const parentDir = import_path49.default.dirname(dir); + if (parentDir === dir) + break; + dir = parentDir; + } + return void 0; +} +function explicitSessionName(sessionName) { + return sessionName || process.env.PLAYWRIGHT_CLI_SESSION; +} +function resolveSessionName(sessionName) { + return explicitSessionName(sessionName) || "default"; +} +var import_crypto26, import_fs52, import_os22, import_path49, Registry2, baseDaemonDir, daemonProfilesDir; +var init_registry2 = __esm({ + "packages/playwright-core/src/tools/cli-client/registry.ts"() { + "use strict"; + import_crypto26 = __toESM(require("crypto")); + import_fs52 = __toESM(require("fs")); + import_os22 = __toESM(require("os")); + import_path49 = __toESM(require("path")); + init_package(); + Registry2 = class _Registry { + constructor(files) { + this._files = files; + } + entry(clientInfo, sessionName) { + const key = clientKey(clientInfo); + const entries = this._files.get(key) || []; + return entries.find((entry) => entry.config.name === sessionName); + } + entries(clientInfo) { + return this._files.get(clientKey(clientInfo)) || []; + } + entryMap() { + return this._files; + } + async loadEntry(clientInfo, sessionName) { + const entry = await _Registry._loadSessionEntry(clientInfo.daemonProfilesDir, sessionName + ".session"); + if (!entry) + throw new Error(`Could not start the session "${sessionName}"`); + const key = clientKey(clientInfo); + let list = this._files.get(key); + if (!list) { + list = []; + this._files.set(key, list); + } + const oldIndex = list.findIndex((e) => e.config.name === sessionName); + if (oldIndex !== -1) + list.splice(oldIndex, 1); + list.push(entry); + return entry; + } + static async _loadSessionEntry(daemonDir, file) { + try { + const fileName = import_path49.default.join(daemonDir, file); + const data = await import_fs52.default.promises.readFile(fileName, "utf-8"); + const config = JSON.parse(data); + if (!config.name) + config.name = import_path49.default.basename(file, ".session"); + if (!config.timestamp) + config.timestamp = 0; + return { file: fileName, config, daemonDir }; + } catch { + return void 0; + } + } + static async load() { + const sessions = /* @__PURE__ */ new Map(); + const hashDirs = await import_fs52.default.promises.readdir(baseDaemonDir).catch(() => []); + for (const workspaceDirHash of hashDirs) { + const daemonDir = import_path49.default.join(baseDaemonDir, workspaceDirHash); + const stat = await import_fs52.default.promises.stat(daemonDir); + if (!stat.isDirectory()) + continue; + const files = await import_fs52.default.promises.readdir(daemonDir).catch(() => []); + for (const file of files) { + if (!file.endsWith(".session")) + continue; + const entry = await _Registry._loadSessionEntry(daemonDir, file); + if (!entry) + continue; + const key = entry.config.workspaceDir || workspaceDirHash; + let list = sessions.get(key); + if (!list) { + list = []; + sessions.set(key, list); + } + list.push(entry); + } + } + return new _Registry(sessions); + } + }; + baseDaemonDir = (() => { + if (process.env.PWTEST_DAEMON_SESSION_DIR) + return process.env.PWTEST_DAEMON_SESSION_DIR; + let localCacheDir; + if (process.platform === "linux") + localCacheDir = process.env.XDG_CACHE_HOME || import_path49.default.join(import_os22.default.homedir(), ".cache"); + if (process.platform === "darwin") + localCacheDir = import_path49.default.join(import_os22.default.homedir(), "Library", "Caches"); + if (process.platform === "win32") + localCacheDir = process.env.LOCALAPPDATA || import_path49.default.join(import_os22.default.homedir(), "AppData", "Local"); + if (!localCacheDir) + throw new Error("Unsupported platform: " + process.platform); + return import_path49.default.join(localCacheDir, "ms-playwright", "daemon"); + })(); + daemonProfilesDir = (workspaceDirHash) => { + return import_path49.default.join(baseDaemonDir, workspaceDirHash); + }; + } +}); + +// packages/playwright-core/src/tools/utils/socketConnection.ts +function compareSemver(a, b) { + const aBase = a.replace(/-.*$/, ""); + const bBase = b.replace(/-.*$/, ""); + const aParts = aBase.split(".").map(Number); + const bParts = bBase.split(".").map(Number); + for (let i = 0; i < 3; i++) { + if (aParts[i] > bParts[i]) + return 1; + if (aParts[i] < bParts[i]) + return -1; + } + const aTimestamp = parseSuffixTimestamp(a); + const bTimestamp = parseSuffixTimestamp(b); + if (aTimestamp > bTimestamp) + return 1; + if (aTimestamp < bTimestamp) + return -1; + return 0; +} +function parseSuffixTimestamp(version3) { + const match = version3.match(/^\d+\.\d+\.\d+-(?:alpha|beta)-(.+)$/); + if (!match) + return Infinity; + const suffix = match[1]; + if (/^\d{4}-\d{2}-\d{2}$/.test(suffix)) + return new Date(suffix).getTime(); + return Number(suffix); +} +var SocketConnection; +var init_socketConnection = __esm({ + "packages/playwright-core/src/tools/utils/socketConnection.ts"() { + "use strict"; + SocketConnection = class { + constructor(socket) { + this._pendingBuffers = []; + this._socket = socket; + socket.on("data", (buffer) => this._onData(buffer)); + socket.on("close", () => { + this.onclose?.(); + }); + socket.on("error", (e) => console.error(`error: ${e.message}`)); + } + async send(message) { + await new Promise((resolve, reject) => { + this._socket.write(`${JSON.stringify(message)} +`, (error) => { + if (error) + reject(error); + else + resolve(void 0); + }); + }); + } + close() { + this._socket.destroy(); + } + _onData(buffer) { + let end = buffer.indexOf("\n"); + if (end === -1) { + this._pendingBuffers.push(buffer); + return; + } + this._pendingBuffers.push(buffer.slice(0, end)); + const message = Buffer.concat(this._pendingBuffers).toString(); + this._dispatchMessage(message); + let start3 = end + 1; + end = buffer.indexOf("\n", start3); + while (end !== -1) { + const message2 = buffer.toString(void 0, start3, end); + this._dispatchMessage(message2); + start3 = end + 1; + end = buffer.indexOf("\n", start3); + } + this._pendingBuffers = [buffer.slice(start3)]; + } + _dispatchMessage(message) { + try { + this.onmessage?.(JSON.parse(message)); + } catch (e) { + console.error("failed to dispatch message", e); + } + } + }; + } +}); + +// packages/playwright-core/src/tools/cli-client/session.ts +var import_child_process4, import_fs53, import_net9, import_os23, import_path50, Session2, SocketConnectionClient; +var init_session = __esm({ + "packages/playwright-core/src/tools/cli-client/session.ts"() { + "use strict"; + import_child_process4 = require("child_process"); + import_fs53 = __toESM(require("fs")); + import_net9 = __toESM(require("net")); + import_os23 = __toESM(require("os")); + import_path50 = __toESM(require("path")); + init_package(); + init_socketConnection(); + init_registry2(); + Session2 = class { + constructor(sessionFile) { + this.config = sessionFile.config; + this.name = this.config.name; + this._sessionFile = sessionFile; + } + isCompatible(clientInfo) { + return compareSemver(clientInfo.version, this.config.version) >= 0; + } + async run(clientInfo, args, options) { + if (!this.isCompatible(clientInfo)) + throw new Error(`Client is v${clientInfo.version}, session '${this.name}' is v${this.config.version}. Run + + playwright-cli${this.name !== "default" ? ` -s=${this.name}` : ""} open + +to restart the browser session.`); + const { socket } = await this._connect(); + if (!socket) + throw new Error(`Browser '${this.name}' is not open. Run + + playwright-cli${this.name !== "default" ? ` -s=${this.name}` : ""} open + +to start the browser session.`); + return await SocketConnectionClient.sendAndClose(socket, "run", { args, cwd: process.cwd(), raw: options?.raw, json: options?.json }); + } + async stop() { + if (!await this.canConnect()) + return { wasOpen: false }; + await this._stopDaemon(); + return { wasOpen: true }; + } + async deleteData() { + await this.stop(); + const dataDirs = await import_fs53.default.promises.readdir(this._sessionFile.daemonDir).catch(() => []); + const matchingEntries = dataDirs.filter((file) => file === `${this.name}.session` || file.startsWith(`ud-${this.name}-`)); + if (matchingEntries.length === 0) + return { existed: false, deletedUserDataDir: false }; + let deletedUserDataDir = false; + for (const entry of matchingEntries) { + const userDataDir = import_path50.default.resolve(this._sessionFile.daemonDir, entry); + for (let i = 0; i < 5; i++) { + try { + await import_fs53.default.promises.rm(userDataDir, { recursive: true }); + if (entry.startsWith("ud-")) + deletedUserDataDir = true; + break; + } catch (e) { + if (e.code === "ENOENT") + break; + await new Promise((resolve) => setTimeout(resolve, 1e3)); + if (i === 4) + throw e; + } + } + } + return { existed: true, deletedUserDataDir }; + } + async _connect() { + return await new Promise((resolve) => { + const socket = import_net9.default.createConnection(this.config.socketPath, () => { + resolve({ socket }); + }); + socket.on("error", (error) => { + if (import_os23.default.platform() !== "win32") + void import_fs53.default.promises.unlink(this.config.socketPath).catch(() => { + }).then(() => resolve({ error })); + else + resolve({ error }); + }); + }); + } + async canConnect() { + const { socket } = await this._connect(); + if (socket) { + socket.destroy(); + return true; + } + return false; + } + static async startDaemon(clientInfo, cliArgs, mode) { + await import_fs53.default.promises.mkdir(clientInfo.daemonProfilesDir, { recursive: true }); + const cliPath = libPath("entry", "cliDaemon.js"); + const sessionName = resolveSessionName(cliArgs.session); + const errLog = import_path50.default.join(clientInfo.daemonProfilesDir, sessionName + ".err"); + const err = import_fs53.default.openSync(errLog, "w"); + const args = [ + cliPath, + sessionName + ]; + if (cliArgs.headed) + args.push("--headed"); + if (cliArgs.browser) + args.push(`--browser=${cliArgs.browser}`); + if (cliArgs.persistent) + args.push("--persistent"); + if (cliArgs.profile) + args.push(`--profile=${cliArgs.profile}`); + if (cliArgs.config) + args.push(`--config=${cliArgs.config}`); + if (cliArgs.extension) + args.push("--extension"); + else if (cliArgs.cdp) + args.push(`--cdp=${cliArgs.cdp}`); + else if (cliArgs.endpoint) + args.push(`--endpoint=${cliArgs.endpoint}`); + const child = (0, import_child_process4.spawn)(process.execPath, args, { + detached: true, + stdio: ["ignore", "pipe", err], + cwd: process.cwd() + // Will be used as root. + }); + let signalled = false; + const sigintHandler2 = () => { + signalled = true; + child.kill("SIGINT"); + }; + const sigtermHandler2 = () => { + signalled = true; + child.kill("SIGTERM"); + }; + process.on("SIGINT", sigintHandler2); + process.on("SIGTERM", sigtermHandler2); + let outLog = ""; + const rejectWithPid = (reject, message) => reject(Object.assign(new Error(`Daemon pid=${child.pid}: ${message}`), { daemonPid: child.pid })); + await new Promise((resolve, reject) => { + child.stdout.on("data", (data) => { + outLog += data.toString(); + if (outLog.includes("Daemon listening on")) + resolve(); + }); + child.on("close", (code) => { + if (!signalled) { + const errLogContent = import_fs53.default.readFileSync(errLog, "utf-8"); + rejectWithPid(reject, `Daemon process exited with code ${code}` + (outLog ? "\n" + outLog : "") + (errLogContent ? "\n" + errLogContent : "")); + } + }); + }); + process.off("SIGINT", sigintHandler2); + process.off("SIGTERM", sigtermHandler2); + child.stdout.destroy(); + child.unref(); + return { pid: child.pid, sessionName, endpoint: cliArgs.endpoint }; + } + async _stopDaemon() { + const { socket } = await this._connect(); + if (!socket) + return; + let error; + await SocketConnectionClient.sendAndClose(socket, "stop", {}).catch((e) => error = e); + if (error && !error?.message?.includes("Session closed")) + throw error; + } + async deleteSessionConfig() { + await import_fs53.default.promises.rm(this._sessionFile.file).catch(() => { + }); + } + }; + SocketConnectionClient = class _SocketConnectionClient { + constructor(socket) { + this._nextMessageId = 1; + this._callbacks = /* @__PURE__ */ new Map(); + this._connection = new SocketConnection(socket); + this._connection.onmessage = (message) => this._onMessage(message); + this._connection.onclose = () => this._rejectCallbacks(); + } + async send(method, params2 = {}) { + const messageId = this._nextMessageId++; + const message = { + id: messageId, + method, + params: params2 + }; + const responsePromise = new Promise((resolve, reject) => { + this._callbacks.set(messageId, { resolve, reject, method, params: params2 }); + }); + const [result2] = await Promise.all([responsePromise, this._connection.send(message)]); + return result2; + } + static async sendAndClose(socket, method, params2 = {}) { + const connection = new _SocketConnectionClient(socket); + try { + return await connection.send(method, params2); + } finally { + connection.close(); + } + } + close() { + this._connection.close(); + } + _onMessage(object) { + if (object.id && this._callbacks.has(object.id)) { + const callback = this._callbacks.get(object.id); + this._callbacks.delete(object.id); + if (object.error) + callback.reject(new Error(object.error)); + else + callback.resolve(object.result); + } else if (object.id) { + throw new Error(`Unexpected message id: ${object.id}`); + } else { + throw new Error(`Unexpected message without id: ${JSON.stringify(object)}`); + } + } + _rejectCallbacks() { + for (const callback of this._callbacks.values()) + callback.reject(new Error("Session closed")); + this._callbacks.clear(); + } + }; + } +}); + +// packages/playwright-core/src/tools/cli-client/program.ts +async function program2(options) { + const clientInfo = createClientInfo(); + const help = require(libPath("tools", "cli-client", "help.json")); + const argv = process.argv.slice(2); + const boolean = [...help.booleanOptions, ...booleanOptions]; + const args = minimist(argv, { boolean, string: ["_"] }); + if (args.s) { + args.session = args.s; + delete args.s; + } + const output = args.json ? new JsonOutput() : new TextOutput(); + const commandName = args._?.[0]; + if (args.version || args.v) { + output.version(options?.embedderVersion ?? clientInfo.version); + process.exit(0); + } + const command = commandName && help.commands[commandName]; + if (args.help || args.h || !commandName) { + if (command) { + output.help(command.help); + } else { + const lines = ["playwright-cli - run playwright mcp commands from terminal"]; + if (process.env.CLAUDECODE || process.env.COPILOT_CLI) + lines.push(`Agent skill: ${import_path51.default.relative(process.cwd(), libPath("tools", "cli-client", "skill", "SKILL.md"))}`); + lines.push(help.global); + output.help(lines.join("\n\n")); + } + process.exit(0); + } + if (!command) + output.errorUnknownCommand(commandName, help.global); + validateFlags(args, command, output); + validateArgs(args, command, output); + const registry2 = await Registry2.load(); + const sessionName = resolveSessionName(args.session); + switch (commandName) { + case "list": { + const data = await collectList(registry2, clientInfo, !!args.all); + output.list(data); + return; + } + case "close-all": { + const entries = registry2.entries(clientInfo); + const closed = []; + for (const entry of entries) { + await new Session2(entry).stop(); + closed.push(entry.config.name); + } + output.closeAll(closed); + return; + } + case "delete-data": { + const entry = registry2.entry(clientInfo, sessionName); + if (!entry) { + output.deleteData(sessionName, { existed: false, deletedUserDataDir: false }); + return; + } + const result2 = await new Session2(entry).deleteData(); + output.deleteData(sessionName, result2); + return; + } + case "kill-all": { + const pids = await killAllDaemons(); + output.killAll(pids); + return; + } + case "open": { + const { pid } = await startSession(sessionName, registry2, clientInfo, args, "open"); + const newEntry = await registry2.loadEntry(clientInfo, sessionName); + const params2 = args._.slice(1); + const toolText = await runInSessionOrStop(newEntry, clientInfo, { _: ["goto", ...params2.length ? params2 : ["about:blank"]] }, output); + output.open(sessionName, pid, toolText); + return; + } + case "attach": { + const attachTarget = args._[1]; + const targetCount = (attachTarget ? 1 : 0) + (args.cdp ? 1 : 0) + (args.endpoint ? 1 : 0) + (args.extension ? 1 : 0); + if (targetCount > 1) + output.errorAttachConflict(); + if (attachTarget) + args.endpoint = attachTarget; + const extensionChannel = typeof args.extension === "string" ? args.extension : void 0; + if (extensionChannel) { + args.browser = extensionChannel; + args.extension = true; + } + const cdpChannel = typeof args.cdp === "string" && isKnownChannel(args.cdp) ? args.cdp : void 0; + const targetName = attachTarget ?? cdpChannel ?? extensionChannel ?? args.endpoint ?? args.cdp; + if (!targetName) + output.errorAttachNoTarget(); + const attachSessionName = explicitSessionName(args.session) ?? attachTarget ?? cdpChannel ?? extensionChannel ?? sessionName; + args.session = attachSessionName; + const { pid } = await startSession(attachSessionName, registry2, clientInfo, args, "attach"); + const newEntry = await registry2.loadEntry(clientInfo, attachSessionName); + const toolText = await runInSessionOrStop(newEntry, clientInfo, { _: ["snapshot"], filename: "" }, output); + output.attach(attachSessionName, pid, targetName, toolText); + return; + } + case "close": { + const closeEntry = registry2.entry(clientInfo, sessionName); + const { wasOpen } = closeEntry ? await new Session2(closeEntry).stop() : { wasOpen: false }; + output.close(sessionName, wasOpen); + return; + } + case "detach": { + const detachEntry = registry2.entry(clientInfo, sessionName); + if (detachEntry && !detachEntry.config.attached) + output.errorDetachNotAttached(sessionName); + const { wasOpen } = detachEntry ? await new Session2(detachEntry).stop() : { wasOpen: false }; + output.detach(sessionName, wasOpen); + return; + } + case "install": + await runInitWorkspace(args, output); + output.installed(); + return; + case "install-browser": + await installBrowser2(); + output.installed(); + return; + case "show": { + const daemonScript = libPath("entry", "dashboardApp.js"); + const daemonArgs = [ + daemonScript, + `--workspaceDir=${clientInfo.workspaceDir ?? ""}` + ]; + const explicit = explicitSessionName(args.session); + if (explicit) + daemonArgs.push(`--sessionName=${explicit}`); + if (args.port !== void 0) + daemonArgs.push(`--port=${args.port}`); + if (args.host !== void 0) + daemonArgs.push(`--host=${args.host}`); + if (args.kill) { + daemonArgs.push(`--kill`); + const child2 = (0, import_child_process5.spawn)(process.execPath, daemonArgs, { stdio: "ignore" }); + await new Promise((resolve) => child2.on("exit", () => resolve())); + return; + } + if (args.annotate) { + const entry = registry2.entry(clientInfo, sessionName); + if (!entry) + output.errorBrowserNotOpenForTool(sessionName); + args.raw = true; + const text2 = await runInSession(entry, clientInfo, args, output); + output.toolResult(text2); + return; + } + const foreground = args.port !== void 0; + const child = (0, import_child_process5.spawn)(process.execPath, daemonArgs, { + detached: !foreground, + stdio: foreground ? "inherit" : ["pipe", "pipe", "ignore"] + }); + if (foreground) { + await new Promise((resolve) => child.on("exit", () => resolve())); + return; + } + const timer = setTimeout(() => child.stdin.destroy(), 6e4); + child.unref(); + let daemonPid; + try { + await new Promise((resolve, reject) => { + let outLog = ""; + child.stdout.on("data", (data) => { + outLog += data.toString(); + const match = outLog.match(/Dashboard is running pid=(\d+)/); + if (match) { + daemonPid = Number(match[1]); + resolve(); + } + }); + child.once("exit", (code, signal) => reject(new Error(`Dashboard daemon exited (code=${code}, signal=${signal}) before signaling READY${outLog ? "\n" + outLog : ""}`))); + }); + } finally { + clearTimeout(timer); + child.removeAllListeners("exit"); + child.stdin.destroy(); + child.stdout.destroy(); + } + output.show(sessionName, daemonPid); + return; + } + default: { + const entry = registry2.entry(clientInfo, sessionName); + if (!entry) + output.errorBrowserNotOpenForTool(sessionName); + if (command.raw) + args.raw = true; + const text2 = await runInSession(entry, clientInfo, args, output); + output.toolResult(text2); + } + } +} +async function startSession(sessionName, registry2, clientInfo, args, mode) { + const entry = registry2.entry(clientInfo, sessionName); + if (entry) + await new Session2(entry).stop(); + return await Session2.startDaemon(clientInfo, args, mode); +} +async function runInSession(entry, clientInfo, args, output) { + const raw = !!args.raw; + for (const globalOption of globalOptions) + delete args[globalOption]; + const session2 = new Session2(entry); + const result2 = await session2.run(clientInfo, args, { raw, json: output.json }); + return result2.text; +} +async function runInSessionOrStop(entry, clientInfo, args, output) { + try { + return await runInSession(entry, clientInfo, args, output); + } catch (e) { + await new Session2(entry).stop().catch(() => { + }); + throw e; + } +} +async function runInitWorkspace(args, output) { + const cliPath = libPath("entry", "cliDaemon.js"); + const daemonArgs = [cliPath, "--init-workspace", ...args.skills ? ["--init-skills", String(args.skills)] : []]; + await new Promise((resolve, reject) => { + const child = (0, import_child_process5.spawn)(process.execPath, daemonArgs, { + stdio: output.installStdio(), + cwd: process.cwd() + }); + child.on("close", (code) => { + if (code === 0) + resolve(); + else + reject(new Error(`Workspace initialization failed with exit code ${code}`)); + }); + }); +} +async function installBrowser2() { + const argv = process.argv.map((arg) => arg === "install-browser" ? "install" : arg); + const { libCli } = (init_coreBundle(), __toCommonJS(coreBundle_exports)); + const { program: program4 } = require("./utilsBundle"); + if (!program4.version()) + libCli.decorateProgram(program4); + program4.parse(argv); +} +async function killAllDaemons() { + const platform = import_os24.default.platform(); + const pidFilterEnv = process.env.PWTEST_KILL_ALL_PID_FILTER_FOR_TEST; + const pidFilter = pidFilterEnv ? new Set(pidFilterEnv.split(",").map((p) => parseInt(p, 10)).filter((n) => !isNaN(n))) : void 0; + const killed = []; + try { + if (platform === "win32") { + const clauses = [`(${daemonProcessPatterns.map((p) => `$_.CommandLine -like '*${p}*'`).join(" -or ")})`]; + if (pidFilter) + clauses.push(`(${[...pidFilter].map((p) => `$_.ProcessId -eq ${p}`).join(" -or ")})`); + const whereClause = clauses.join(" -and "); + const result2 = (0, import_child_process5.execSync)( + `powershell -NoProfile -NonInteractive -Command "Get-CimInstance Win32_Process | Where-Object { ${whereClause} } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue; $_.ProcessId }"`, + { encoding: "utf-8" } + ); + const pids = result2.split("\n").map((line) => line.trim()).filter((line) => /^\d+$/.test(line)); + for (const pid of pids) + killed.push(parseInt(pid, 10)); + } else { + const result2 = (0, import_child_process5.execSync)("ps auxww", { encoding: "utf-8" }); + const lines = result2.split("\n"); + for (const line of lines) { + if (daemonProcessPatterns.some((p) => line.includes(p))) { + const parts = line.trim().split(/\s+/); + const pid = parts[1]; + if (pid && /^\d+$/.test(pid)) { + const numericPid = parseInt(pid, 10); + if (pidFilter && !pidFilter.has(numericPid)) + continue; + try { + process.kill(numericPid, "SIGKILL"); + killed.push(numericPid); + } catch { + } + } + } + } + } + } catch (e) { + } + return killed; +} +async function collectList(registry2, clientInfo, all) { + const browsers = []; + const entries = registry2.entryMap(); + const serverEntries = await serverRegistry.list(); + const key = clientKey(clientInfo); + for (const [workspaceKey, list] of entries) { + if (!all && workspaceKey !== key) + continue; + for (const entry of list) { + const session2 = new Session2(entry); + const canConnect = await session2.canConnect(); + if (!canConnect) { + await session2.deleteSessionConfig(); + continue; + } + const config = session2.config; + const channel = config.browser?.launchOptions.channel ?? config.browser?.browserName; + browsers.push({ + name: session2.name, + workspace: workspaceKey, + status: canConnect ? "open" : "closed", + browserType: channel, + userDataDir: config.browser?.userDataDir ?? null, + headed: config.browser ? !config.browser.launchOptions.headless : void 0, + persistent: !!config.cli.persistent, + attached: !!config.attached, + compatible: session2.isCompatible(clientInfo), + version: config.version + }); + } + } + if (!all) + return { all, browsers }; + const servers = [...serverEntries.values()].flat(); + return { all, browsers, servers, channelSessions: await listChannelSessions() }; +} +function validateFlags(args, command, output) { + const unknownFlags = []; + for (const key of Object.keys(args)) { + if (key === "_") + continue; + if (globalOptions.includes(key)) + continue; + if (!(key in command.flags)) + unknownFlags.push(key); + } + if (unknownFlags.length) + output.errorUnknownOption(unknownFlags, command.help); +} +function validateArgs(args, command, output) { + const positional = args._.slice(1); + if (positional.length > command.args.length) + output.errorTooManyArguments(command.args.length, positional.length, command.help); +} +var import_child_process5, import_os24, import_path51, globalOptions, booleanOptions, daemonProcessPatterns; +var init_program = __esm({ + "packages/playwright-core/src/tools/cli-client/program.ts"() { + "use strict"; + import_child_process5 = require("child_process"); + import_os24 = __toESM(require("os")); + import_path51 = __toESM(require("path")); + init_channelSessions(); + init_output(); + init_registry2(); + init_session(); + init_package(); + init_serverRegistry(); + init_minimist(); + globalOptions = [ + "json", + "raw", + "session" + ]; + booleanOptions = [ + "all", + "help", + "json", + "raw", + "version" + ]; + daemonProcessPatterns = ["run-mcp-server", "run-cli-server", "cli-daemon", "cliDaemon.js", "dashboardApp.js"]; + } +}); + +// packages/playwright-core/src/cli/program.ts +var program_exports = {}; +__export(program_exports, { + decorateProgram: () => decorateProgram +}); +function decorateProgram(program4) { + program4.version("Version " + (process.env.PW_CLI_DISPLAY_VERSION || packageJSON.version)).name(buildBasePlaywrightCLICommand(process.env.PW_LANG_NAME)); + program4.command("mark-docker-image [dockerImageNameTemplate]", { hidden: true }).description("mark docker image").allowUnknownOption(true).action(async function(dockerImageNameTemplate) { + markDockerImage(dockerImageNameTemplate).catch(logErrorAndExit); + }); + commandWithOpenOptions("open [url]", "open page in browser specified via -b, --browser", []).action(async function(url2, options) { + open4(options, url2).catch(logErrorAndExit); + }).addHelpText("afterAll", ` + Examples: + + $ open + $ open -b webkit https://example.com`); + commandWithOpenOptions( + "codegen [url]", + "open page and generate code for user actions", + [ + ["-o, --output ", "saves the generated script to a file"], + ["--target ", `language to generate, one of javascript, playwright-test, python, python-async, python-pytest, csharp, csharp-mstest, csharp-nunit, csharp-xunit, java, java-junit`, codegenId()], + ["--test-id-attribute ", "use the specified attribute to generate data test ID selectors"] + ] + ).action(async function(url2, options) { + await codegen(options, url2); + }).addHelpText("afterAll", ` + Examples: + + $ codegen + $ codegen --target=python + $ codegen -b webkit https://example.com`); + program4.command("install [browser...]").description("ensure browsers necessary for this version of Playwright are installed").option("--with-deps", "install system dependencies for browsers").option("--dry-run", "do not execute installation, only print information").option("--list", "prints list of browsers from all playwright installations").option("--force", "force reinstall of already installed browsers").option("--only-shell", "only install headless shell when installing chromium").option("--no-shell", "do not install chromium headless shell").option("--no-progress", "do not show download progress bars").action(async function(args, options) { + try { + await installBrowsers(args, options); + } catch (e) { + console.log(`Failed to install browsers +${e}`); + gracefullyProcessExitDoNotHang(1); + } + }).addHelpText("afterAll", ` + + Examples: + - $ install + Install default browsers. + + - $ install chrome firefox + Install custom browsers, supports chromium, firefox, webkit, chromium-headless-shell.`); + program4.command("uninstall").description("Removes browsers used by this installation of Playwright from the system (chromium, firefox, webkit, ffmpeg). This does not include branded channels.").option("--all", "Removes all browsers used by any Playwright installation from the system.").action(async (options) => { + uninstallBrowsers(options).catch(logErrorAndExit); + }); + program4.command("install-deps [browser...]").description("install dependencies necessary to run browsers (will ask for sudo permissions)").option("--dry-run", "Do not modify the system. On Linux, simulate the install via apt-get and exit with a non-zero code if any required packages are missing; on Windows, print the install command.").action(async function(args, options) { + try { + await installDeps(args, options); + } catch (e) { + console.log(`Failed to install browser dependencies +${e}`); + gracefullyProcessExitDoNotHang(1); + } + }).addHelpText("afterAll", ` + Examples: + - $ install-deps + Install dependencies for default browsers. + + - $ install-deps chrome firefox + Install dependencies for specific browsers, supports chromium, firefox, webkit, chromium-headless-shell. + + - $ install-deps --dry-run + Report which required system dependencies are missing without modifying the system. Exits non-zero if any are missing. Useful for non-interactive verification scripts.`); + const browsers = [ + { alias: "cr", name: "Chromium", type: "chromium" }, + { alias: "ff", name: "Firefox", type: "firefox" }, + { alias: "wk", name: "WebKit", type: "webkit" } + ]; + for (const { alias, name, type: type3 } of browsers) { + commandWithOpenOptions(`${alias} [url]`, `open page in ${name}`, []).action(async function(url2, options) { + open4({ ...options, browser: type3 }, url2).catch(logErrorAndExit); + }).addHelpText("afterAll", ` + Examples: + + $ ${alias} https://example.com`); + } + commandWithOpenOptions( + "screenshot ", + "capture a page screenshot", + [ + ["--wait-for-selector ", "wait for selector before taking a screenshot"], + ["--wait-for-timeout ", "wait for timeout in milliseconds before taking a screenshot"], + ["--full-page", "whether to take a full page screenshot (entire scrollable area)"] + ] + ).action(async function(url2, filename, command) { + screenshot3(command, command, url2, filename).catch(logErrorAndExit); + }).addHelpText("afterAll", ` + Examples: + + $ screenshot -b webkit https://example.com example.png`); + commandWithOpenOptions( + "pdf ", + "save page as pdf", + [ + ["--paper-format ", "paper format: Letter, Legal, Tabloid, Ledger, A0, A1, A2, A3, A4, A5, A6"], + ["--wait-for-selector ", "wait for given selector before saving as pdf"], + ["--wait-for-timeout ", "wait for given timeout in milliseconds before saving as pdf"] + ] + ).action(async function(url2, filename, options) { + pdf2(options, options, url2, filename).catch(logErrorAndExit); + }).addHelpText("afterAll", ` + Examples: + + $ pdf https://example.com example.pdf`); + program4.command("run-driver", { hidden: true }).action(async function(options) { + runDriver(); + }); + program4.command("run-server", { hidden: true }).option("--port ", "Server port").option("--host ", "Server host").option("--path ", "Endpoint Path", "/").option("--max-clients ", "Maximum clients").option("--mode ", 'Server mode, either "default" or "extension"').option("--artifacts-dir ", "Artifacts directory").option("--unsafe", "Allow clients to set unsafe launch options (args, executablePath, ignoreAllDefaultArgs, etc)").action(async function(options) { + runServer({ + port: options.port ? +options.port : void 0, + host: options.host, + path: options.path, + maxConnections: options.maxClients ? +options.maxClients : Infinity, + extension: options.mode === "extension" || !!process.env.PW_EXTENSION_MODE, + artifactsDir: options.artifactsDir, + unsafe: !!options.unsafe + }).catch(logErrorAndExit); + }); + program4.command("print-api-json", { hidden: true }).action(async function(options) { + printApiJson(); + }); + program4.command("launch-server", { hidden: true }).requiredOption("--browser ", 'Browser name, one of "chromium", "firefox" or "webkit"').option("--config ", "JSON file with launchServer options").action(async function(options) { + launchBrowserServer(options.browser, options.config); + }); + program4.command("show-trace [trace]").option("-b, --browser ", "browser to use, one of cr, chromium, ff, firefox, wk, webkit", "chromium").option("-h, --host ", "Host to serve trace on; specifying this option opens trace in a browser tab").option("-p, --port ", "Port to serve trace on, 0 for any free port; specifying this option opens trace in a browser tab").option("--stdin", "Accept trace URLs over stdin to update the viewer").description("show trace viewer").action(async function(trace, options) { + if (options.browser === "cr") + options.browser = "chromium"; + if (options.browser === "ff") + options.browser = "firefox"; + if (options.browser === "wk") + options.browser = "webkit"; + const openOptions = { + host: options.host, + port: +options.port, + isServer: !!options.stdin + }; + if (options.port !== void 0 || options.host !== void 0) + runTraceInBrowser(trace, openOptions).catch(logErrorAndExit); + else + runTraceViewerApp(trace, options.browser, openOptions).catch(logErrorAndExit); + }).addHelpText("afterAll", ` + Examples: + + $ show-trace + $ show-trace https://example.com/trace.zip`); + addTraceCommands(program4, logErrorAndExit); + program4.command("cli", { hidden: true }).allowExcessArguments(true).allowUnknownOption(true).helpOption(false).action(async (options) => { + process.argv.splice(process.argv.indexOf("cli"), 1); + program2().catch(logErrorAndExit); + }); +} +function logErrorAndExit(e) { + if (process.env.PWDEBUGIMPL) + console.error(e); + else + console.error(e.name + ": " + e.message); + gracefullyProcessExitDoNotHang(1); +} +function codegenId() { + return process.env.PW_LANG_NAME || "playwright-test"; +} +function commandWithOpenOptions(command, description, options) { + let result2 = program3.command(command).description(description); + for (const option of options) + result2 = result2.option(option[0], ...option.slice(1)); + return result2.option("-b, --browser ", "browser to use, one of cr, chromium, ff, firefox, wk, webkit", "chromium").option("--block-service-workers", "block service workers").option("--channel ", 'Chromium distribution channel, "chrome", "chrome-beta", "msedge-dev", etc').option("--color-scheme ", 'emulate preferred color scheme, "light" or "dark"').option("--device ", 'emulate device, for example "iPhone 11"').option("--geolocation ", 'specify geolocation coordinates, for example "37.819722,-122.478611"').option("--ignore-https-errors", "ignore https errors").option("--load-storage ", "load context storage state from the file, previously saved with --save-storage").option("--lang ", 'specify language / locale, for example "en-GB"').option("--proxy-server ", 'specify proxy server, for example "http://myproxy:3128" or "socks5://myproxy:8080"').option("--proxy-bypass ", 'comma-separated domains to bypass proxy, for example ".com,chromium.org,.domain.com"').option("--save-har ", "save HAR file with all network activity at the end").option("--save-har-glob ", "filter entries in the HAR by matching url against this glob pattern").option("--save-storage ", "save context storage state at the end, for later use with --load-storage").option("--timezone ", 'time zone to emulate, for example "Europe/Rome"').option("--timeout ", "timeout for Playwright actions in milliseconds, no timeout by default").option("--user-agent ", "specify user agent string").option("--user-data-dir ", "use the specified user data directory instead of a new context").option("--viewport-size ", 'specify browser viewport size in pixels, for example "1280, 720"'); +} +function buildBasePlaywrightCLICommand(cliTargetLang) { + switch (cliTargetLang) { + case "python": + return `playwright`; + case "java": + return `mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="...options.."`; + case "csharp": + return `pwsh bin/Debug/netX/playwright.ps1`; + default: { + const packageManagerCommand = getPackageManagerExecCommand(); + return `${packageManagerCommand} playwright`; + } + } +} +var program3; +var init_program2 = __esm({ + "packages/playwright-core/src/cli/program.ts"() { + "use strict"; + init_bootstrap(); + init_processLauncher(); + init_env(); + init_package(); + init_traceCli(); + init_driver(); + init_installActions(); + init_browserActions(); + init_installActions(); + init_traceViewer(); + init_browserActions(); + init_program(); + ({ program: program3 } = require("./utilsBundle")); + } +}); + +// packages/playwright-core/src/cli/programWithTestStub.ts +var programWithTestStub_exports = {}; +__export(programWithTestStub_exports, { + decorateProgram: () => decorateProgram2 +}); +function printPlaywrightTestError(command) { + const packages = []; + for (const pkg of ["playwright", "playwright-chromium", "playwright-firefox", "playwright-webkit"]) { + try { + require.resolve(pkg); + packages.push(pkg); + } catch (e) { + } + } + if (!packages.length) + packages.push("playwright"); + const packageManager = getPackageManager(); + if (packageManager === "yarn") { + console.error(`Please install @playwright/test package before running "yarn playwright ${command}"`); + console.error(` yarn remove ${packages.join(" ")}`); + console.error(" yarn add -D @playwright/test"); + } else if (packageManager === "pnpm") { + console.error(`Please install @playwright/test package before running "pnpm exec playwright ${command}"`); + console.error(` pnpm remove ${packages.join(" ")}`); + console.error(" pnpm add -D @playwright/test"); + } else { + console.error(`Please install @playwright/test package before running "npx playwright ${command}"`); + console.error(` npm uninstall ${packages.join(" ")}`); + console.error(" npm install -D @playwright/test"); + } +} +function addExternalPlaywrightTestCommands(program4) { + for (const [command, description] of kExternalPlaywrightTestCommands) { + const playwrightTest = program4.command(command).allowUnknownOption(true).allowExcessArguments(true); + playwrightTest.description(`${description} Available in @playwright/test package.`); + playwrightTest.action(async () => { + printPlaywrightTestError(command); + gracefullyProcessExitDoNotHang(1); + }); + } +} +function decorateProgram2(program4) { + if (!process.env.PW_LANG_NAME) + addExternalPlaywrightTestCommands(program4); +} +var kExternalPlaywrightTestCommands; +var init_programWithTestStub = __esm({ + "packages/playwright-core/src/cli/programWithTestStub.ts"() { + "use strict"; + init_processLauncher(); + init_env(); + kExternalPlaywrightTestCommands = [ + ["test", "Run tests with Playwright Test."], + ["show-report", "Show Playwright Test HTML report."], + ["merge-reports", "Merge Playwright Test Blob reports"] + ]; + } +}); + +// packages/playwright-core/src/outofprocess.ts +var outofprocess_exports = {}; +__export(outofprocess_exports, { + start: () => start +}); +async function start(env = {}) { + const client = new PlaywrightClient(env); + const playwright2 = await client._playwright; + playwright2.driverProcess = client._driverProcess; + return { playwright: playwright2, stop: () => client.stop() }; +} +var childProcess4, import_path52, PlaywrightClient; +var init_outofprocess = __esm({ + "packages/playwright-core/src/outofprocess.ts"() { + "use strict"; + childProcess4 = __toESM(require("child_process")); + import_path52 = __toESM(require("path")); + init_pipeTransport(); + init_nodePlatform(); + init_manualPromise(); + init_connection(); + init_package(); + PlaywrightClient = class { + constructor(env) { + this._closePromise = new ManualPromise(); + this._driverProcess = childProcess4.fork(import_path52.default.join(packageRoot, "cli.js"), ["run-driver"], { + stdio: "pipe", + detached: true, + env: { + ...process.env, + ...env + } + }); + this._driverProcess.unref(); + this._driverProcess.stderr.on("data", (data) => process.stderr.write(data)); + const connection = new Connection(nodePlatform(packageRoot)); + const transport = new PipeTransport(this._driverProcess.stdin, this._driverProcess.stdout); + connection.onmessage = (message) => transport.send(JSON.stringify(message)); + transport.onmessage = (message) => connection.dispatch(JSON.parse(message)); + transport.onclose = () => this._closePromise.resolve(); + this._playwright = connection.initializePlaywright(); + } + async stop() { + this._driverProcess.stdin.destroy(); + this._driverProcess.stdout.destroy(); + this._driverProcess.stderr.destroy(); + await this._closePromise; + } + }; + } +}); + +// packages/playwright-core/src/tools/mcp/log.ts +function logUnhandledError(error) { + errorDebug(error); +} +var debug12, errorDebug, testDebug2; +var init_log = __esm({ + "packages/playwright-core/src/tools/mcp/log.ts"() { + "use strict"; + debug12 = require("./utilsBundle").debug; + errorDebug = debug12("pw:mcp:error"); + testDebug2 = debug12("pw:mcp:test"); + } +}); + +// packages/playwright-core/src/tools/mcp/watchdog.ts +function setupExitWatchdog() { + let isExiting = false; + const handleExit = async (signal) => { + if (isExiting) + return; + isExiting = true; + setTimeout(() => process.exit(0), 15e3); + testDebug2("gracefully closing " + gracefullyCloseSet.size); + await gracefullyCloseAll(); + process.exit(0); + }; + process.stdin.on("close", () => handleExit("close")); + process.on("SIGINT", () => handleExit("SIGINT")); + process.on("SIGTERM", () => handleExit("SIGTERM")); +} +var init_watchdog = __esm({ + "packages/playwright-core/src/tools/mcp/watchdog.ts"() { + "use strict"; + init_processLauncher(); + init_log(); + } +}); + +// packages/playwright-core/src/tools/utils/mcp/http.ts +async function startMcpHttpServer(config, serverBackendFactory, allowedHosts) { + const httpServer = createHttpServer(); + await startHttpServer(httpServer, config); + return await installHttpTransport(httpServer, serverBackendFactory, allowedHosts); +} +function addressToString(address, options) { + (0, import_assert54.default)(address, "Could not bind server socket"); + if (typeof address === "string") + throw new Error("Unexpected address type: " + address); + let host = urlHostFromAddress(address); + if (options.normalizeLoopback && (host === "0.0.0.0" || host === "[::]" || host === "[::1]" || host === "127.0.0.1")) + host = "localhost"; + return `${options.protocol}://${host}:${address.port}`; +} +async function installHttpTransport(httpServer, serverBackendFactory, allowedHosts) { + const url2 = addressToString(httpServer.address(), { protocol: "http", normalizeLoopback: true }); + const host = new URL(url2).host; + allowedHosts = (allowedHosts || [host]).map((h) => h.toLowerCase()); + const allowAnyHost = allowedHosts.includes("*"); + const sseSessions = /* @__PURE__ */ new Map(); + const streamableSessions = /* @__PURE__ */ new Map(); + httpServer.on("request", async (req, res) => { + if (!allowAnyHost) { + const host2 = req.headers.host?.toLowerCase(); + if (!host2) { + res.statusCode = 400; + return res.end("Missing host"); + } + if (!allowedHosts.includes(host2)) { + res.statusCode = 403; + return res.end("Access is only allowed at " + allowedHosts.join(", ")); + } + } + const url3 = new URL(`http://localhost${req.url}`); + if (url3.pathname === "/killkillkill") { + if (req.method !== "POST" || req.headers["x-pw-mcp-kill"] !== "1") { + res.statusCode = 405; + return res.end(); + } + res.statusCode = 200; + res.end("Killing process"); + process.emit("SIGINT"); + return; + } + if (url3.pathname.startsWith("/sse")) + await handleSSE(serverBackendFactory, req, res, url3, sseSessions); + else + await handleStreamable(serverBackendFactory, req, res, streamableSessions); + }); + return url2; +} +async function handleSSE(serverBackendFactory, req, res, url2, sessions) { + if (req.method === "POST") { + const sessionId = url2.searchParams.get("sessionId"); + if (!sessionId) { + res.statusCode = 400; + return res.end("Missing sessionId"); + } + const transport = sessions.get(sessionId); + if (!transport) { + res.statusCode = 404; + return res.end("Session not found"); + } + return await transport.handlePostMessage(req, res); + } else if (req.method === "GET") { + const transport = new SSEServerTransport("/sse", res); + sessions.set(transport.sessionId, transport); + testDebug3(`create SSE session`); + await connect(serverBackendFactory, transport, false); + res.on("close", () => { + testDebug3(`delete SSE session`); + sessions.delete(transport.sessionId); + }); + return; + } + res.statusCode = 405; + res.end("Method not allowed"); +} +async function handleStreamable(serverBackendFactory, req, res, sessions) { + const sessionId = req.headers["mcp-session-id"]; + if (sessionId) { + const transport = sessions.get(sessionId); + if (!transport) { + res.statusCode = 404; + res.end("Session not found"); + return; + } + return await transport.handleRequest(req, res); + } + if (req.method === "POST") { + const transport = new StreamableHTTPServerTransport({ + sessionIdGenerator: () => import_crypto27.default.randomUUID(), + onsessioninitialized: async (sessionId2) => { + testDebug3(`create http session`); + await connect(serverBackendFactory, transport, true); + sessions.set(sessionId2, transport); + } + }); + transport.onclose = () => { + if (!transport.sessionId) + return; + sessions.delete(transport.sessionId); + testDebug3(`delete http session`); + }; + await transport.handleRequest(req, res); + return; + } + res.statusCode = 400; + res.end("Invalid request"); +} +var import_assert54, import_crypto27, debug13, SSEServerTransport, StreamableHTTPServerTransport, testDebug3; +var init_http = __esm({ + "packages/playwright-core/src/tools/utils/mcp/http.ts"() { + "use strict"; + import_assert54 = __toESM(require("assert")); + import_crypto27 = __toESM(require("crypto")); + init_httpServer(); + init_network(); + init_server2(); + debug13 = require("./utilsBundle").debug; + ({ SSEServerTransport } = require("./utilsBundle")); + ({ StreamableHTTPServerTransport } = require("./utilsBundle")); + testDebug3 = debug13("pw:mcp:test"); + } +}); + +// packages/playwright-core/src/tools/utils/mcp/tool.ts +function toMcpTool(tool) { + const readOnly = tool.type === "readOnly" || tool.type === "assertion"; + return { + name: tool.name, + description: tool.description, + inputSchema: zod.toJSONSchema(tool.inputSchema), + annotations: { + title: tool.title, + readOnlyHint: readOnly, + destructiveHint: !readOnly, + openWorldHint: true + } + }; +} +var zod; +var init_tool2 = __esm({ + "packages/playwright-core/src/tools/utils/mcp/tool.ts"() { + "use strict"; + zod = require("./utilsBundle").z; + } +}); + +// packages/playwright-core/src/tools/utils/mcp/server.ts +async function connect(factory, transport, runHeartbeat) { + const server = createServer(factory.name, factory.version, factory, runHeartbeat); + await server.connect(transport); +} +function createServer(name, version3, factory, runHeartbeat) { + const server = new Server({ name, version: version3 }, { + capabilities: { + tools: {} + } + }); + server.setRequestHandler(ListToolsRequestSchema, async () => { + serverDebug("listTools"); + return { tools: factory.toolSchemas.map((s) => toMcpTool(s)) }; + }); + let backendPromise; + const onClose = () => backendPromise?.then((b) => backendManager.disposeBackend(b)).catch(serverDebug); + addServerListener(server, "close", onClose); + server.setRequestHandler(CallToolRequestSchema, async (request2, extra) => { + serverDebug("callTool", request2); + try { + if (!backendPromise) { + backendPromise = initializeServer(server, factory, runHeartbeat).catch((e) => { + backendPromise = void 0; + throw e; + }); + } + const backend = await backendPromise; + const toolResult = await backend.callTool(request2.params.name, request2.params.arguments || {}, extra.signal); + if (toolResult.isClose) { + await backendManager.disposeBackend(backend).catch(serverDebug); + backendPromise = void 0; + delete toolResult.isClose; + } + const mergedResult = mergeTextParts(toolResult); + serverDebugResponse("callResult", mergedResult); + return mergedResult; + } catch (error) { + return { + content: [{ type: "text", text: "### Error\n" + String(error) }], + isError: true + }; + } + }); + return server; +} +function addServerListener(server, event, listener) { + const oldListener = server[`on${event}`]; + server[`on${event}`] = () => { + oldListener?.(); + listener(); + }; +} +async function start2(serverBackendFactory, options = {}) { + if (options.port === void 0) { + const transport = new StdioServerTransport(); + process.stdin.on("end", () => void transport.close()); + await connect(serverBackendFactory, transport, false); + return; + } + const url2 = await startMcpHttpServer(options, serverBackendFactory, options.allowedHosts); + const mcpConfig = { mcpServers: {} }; + mcpConfig.mcpServers[serverBackendFactory.nameInConfig] = { + url: `${url2}/mcp` + }; + const message = [ + `Listening on ${url2}`, + "Put this in your client config:", + JSON.stringify(mcpConfig, void 0, 2), + "For legacy SSE transport support, you can use the /sse endpoint instead." + ].join("\n"); + console.error(message); +} +function firstRootPath(roots) { + return allRootPaths(roots)[0]; +} +function allRootPaths(roots) { + const paths = []; + for (const root of roots) { + const url2 = new URL(root.uri); + let rootPath; + try { + rootPath = (0, import_url2.fileURLToPath)(url2); + } catch (e) { + if (e.code === "ERR_INVALID_FILE_URL_PATH" && process.platform === "win32") + rootPath = decodeURIComponent(url2.pathname); + } + if (!rootPath) + continue; + paths.push(rootPath); + } + if (paths.length === 0) + paths.push(process.cwd()); + return paths; +} +function mergeTextParts(result2) { + const content = []; + const testParts = []; + for (const part of result2.content) { + if (part.type === "text") { + testParts.push(part.text); + continue; + } + if (testParts.length > 0) { + content.push({ type: "text", text: testParts.join("\n") }); + testParts.length = 0; + } + content.push(part); + } + if (testParts.length > 0) + content.push({ type: "text", text: testParts.join("\n") }); + return { + ...result2, + content + }; +} +var import_url2, CallToolRequestSchema, ListToolsRequestSchema, debug14, Server, StdioServerTransport, serverDebug, serverDebugResponse, BackendManager, backendManager, initializeServer, startHeartbeat; +var init_server2 = __esm({ + "packages/playwright-core/src/tools/utils/mcp/server.ts"() { + "use strict"; + import_url2 = require("url"); + init_http(); + init_tool2(); + ({ CallToolRequestSchema, ListToolsRequestSchema } = require("./utilsBundle")); + debug14 = require("./utilsBundle").debug; + ({ Server } = require("./utilsBundle")); + ({ StdioServerTransport } = require("./utilsBundle")); + serverDebug = debug14("pw:mcp:server"); + serverDebugResponse = debug14("pw:mcp:server:response"); + BackendManager = class { + constructor() { + this._backends = /* @__PURE__ */ new Map(); + } + async createBackend(factory, clientInfo) { + const backend = await factory.create(clientInfo); + await backend.initialize?.(clientInfo); + this._backends.set(backend, factory); + return backend; + } + async disposeBackend(backend) { + const factory = this._backends.get(backend); + if (!factory) + return; + await backend.dispose?.(); + await factory.disposed(backend).catch(serverDebug); + this._backends.delete(backend); + } + }; + backendManager = new BackendManager(); + initializeServer = async (server, factory, runHeartbeat) => { + const capabilities = server.getClientCapabilities(); + let clientRoots = []; + if (capabilities?.roots) { + const { roots } = await server.listRoots().catch((e) => { + serverDebug(e); + return { roots: [] }; + }); + clientRoots = roots; + } + const clientInfo = { + cwd: firstRootPath(clientRoots), + clientName: server.getClientVersion()?.name ?? "Playwright MCP" + }; + const backend = await backendManager.createBackend(factory, clientInfo); + if (runHeartbeat) + startHeartbeat(server); + return backend; + }; + startHeartbeat = (server) => { + const beat = () => { + Promise.race([ + server.ping(), + new Promise((_, reject) => setTimeout(() => reject(new Error("ping timeout")), 5e3)) + ]).then(() => { + setTimeout(beat, 3e3); + }).catch(() => { + void server.close(); + }); + }; + beat(); + }; + } +}); + +// packages/playwright-core/src/tools/mcp/configIni.ts +function configFromIniFile(filePath) { + const content = import_fs54.default.readFileSync(filePath, "utf8"); + const parsed = ini.parse(content); + return iniEntriesToConfig(parsed); +} +function iniEntriesToConfig(entries) { + const config = {}; + for (const [targetPath, rawValue] of Object.entries(entries)) { + const type3 = longhandTypes[targetPath]; + const value2 = type3 ? coerceToType(rawValue, type3) : coerceIniValue(rawValue); + setNestedValue(config, targetPath, value2); + } + return config; +} +function coerceToType(value2, type3) { + switch (type3) { + case "string": + return String(value2); + case "number": + return Number(value2); + case "boolean": + if (typeof value2 === "boolean") + return value2; + return value2 === "true" || value2 === "1"; + case "string[]": + if (Array.isArray(value2)) + return value2.map(String); + return [String(value2)]; + case "size": { + if (typeof value2 === "string" && value2.includes("x")) { + const [w, h] = value2.split("x").map(Number); + if (!isNaN(w) && !isNaN(h) && w > 0 && h > 0) + return { width: w, height: h }; + } + return void 0; + } + } +} +function coerceIniValue(value2) { + if (typeof value2 !== "string") + return value2; + const trimmed = value2.trim(); + if (trimmed === "") + return trimmed; + const num = Number(trimmed); + if (!isNaN(num)) + return num; + return value2; +} +function setNestedValue(obj, dotPath, value2) { + const parts = dotPath.split("."); + let current = obj; + for (let i = 0; i < parts.length - 1; i++) { + const part = parts[i]; + if (!(part in current) || typeof current[part] !== "object" || current[part] === null) + current[part] = {}; + current = current[part]; + } + current[parts[parts.length - 1]] = value2; +} +var import_fs54, ini, longhandTypes; +var init_configIni = __esm({ + "packages/playwright-core/src/tools/mcp/configIni.ts"() { + "use strict"; + import_fs54 = __toESM(require("fs")); + ini = require("./utilsBundle").ini; + longhandTypes = { + // browser direct + "browser.browserName": "string", + "browser.isolated": "boolean", + "browser.userDataDir": "string", + "browser.cdpEndpoint": "string", + "browser.cdpTimeout": "number", + "browser.remoteEndpoint": "string", + "browser.initPage": "string[]", + "browser.initScript": "string[]", + // browser.launchOptions + "browser.launchOptions.channel": "string", + "browser.launchOptions.headless": "boolean", + "browser.launchOptions.executablePath": "string", + "browser.launchOptions.chromiumSandbox": "boolean", + "browser.launchOptions.args": "string[]", + "browser.launchOptions.downloadsPath": "string", + "browser.launchOptions.handleSIGHUP": "boolean", + "browser.launchOptions.handleSIGINT": "boolean", + "browser.launchOptions.handleSIGTERM": "boolean", + "browser.launchOptions.slowMo": "number", + "browser.launchOptions.timeout": "number", + "browser.launchOptions.tracesDir": "string", + "browser.launchOptions.proxy.server": "string", + "browser.launchOptions.proxy.bypass": "string", + "browser.launchOptions.proxy.username": "string", + "browser.launchOptions.proxy.password": "string", + // browser.contextOptions + "browser.contextOptions.acceptDownloads": "boolean", + "browser.contextOptions.baseURL": "string", + "browser.contextOptions.bypassCSP": "boolean", + "browser.contextOptions.colorScheme": "string", + "browser.contextOptions.contrast": "string", + "browser.contextOptions.deviceScaleFactor": "number", + "browser.contextOptions.forcedColors": "string", + "browser.contextOptions.hasTouch": "boolean", + "browser.contextOptions.ignoreHTTPSErrors": "boolean", + "browser.contextOptions.isMobile": "boolean", + "browser.contextOptions.javaScriptEnabled": "boolean", + "browser.contextOptions.locale": "string", + "browser.contextOptions.offline": "boolean", + "browser.contextOptions.permissions": "string[]", + "browser.contextOptions.reducedMotion": "string", + "browser.contextOptions.screen": "size", + "browser.contextOptions.serviceWorkers": "string", + "browser.contextOptions.storageState": "string", + "browser.contextOptions.strictSelectors": "boolean", + "browser.contextOptions.timezoneId": "string", + "browser.contextOptions.userAgent": "string", + "browser.contextOptions.viewport": "size", + // top-level + "extension": "boolean", + "capabilities": "string[]", + "saveSession": "boolean", + "saveVideo": "size", + "sharedBrowserContext": "boolean", + "outputDir": "string", + "outputMaxSize": "number", + "imageResponses": "string", + "allowUnrestrictedFileAccess": "boolean", + "codegen": "string", + "testIdAttribute": "string", + // server + "server.port": "number", + "server.host": "string", + "server.allowedHosts": "string[]", + // console + "console.level": "string", + // network + "network.allowedOrigins": "string[]", + "network.blockedOrigins": "string[]", + // timeouts + "timeouts.action": "number", + "timeouts.navigation": "number", + // snapshot + "snapshot.mode": "string" + }; + } +}); + +// packages/playwright-core/src/tools/mcp/config.ts +async function fileExistsAsync(resolved) { + try { + return (await import_fs55.default.promises.stat(resolved)).isFile(); + } catch { + return false; + } +} +async function resolveConfig(config) { + const merged = mergeConfig(defaultConfig, config); + const browser = await validateBrowserConfig(merged.browser); + return { ...merged, browser }; +} +async function resolveCLIConfigForMCP(cliOptions, env) { + const envOverrides = configFromEnv(env); + const cliOverrides = configFromCLIOptions(cliOptions); + const configFile = cliOverrides.configFile ?? envOverrides.configFile; + const configInFile = await loadConfig(configFile); + const configDir = configFile ? import_path53.default.dirname(import_path53.default.resolve(configFile)) : process.cwd(); + let result2 = defaultConfig; + result2 = mergeConfig(result2, resolveConfigPaths(configInFile, configDir)); + result2 = mergeConfig(result2, resolveConfigPaths(envOverrides, process.cwd())); + result2 = mergeConfig(result2, resolveConfigPaths(cliOverrides, process.cwd())); + const browser = await validateBrowserConfig(result2.browser); + if (browser.launchOptions.headless === void 0) + browser.launchOptions.headless = import_os25.default.platform() === "linux" && !process.env.DISPLAY; + validateOutputDir(result2.outputDir); + return { ...result2, browser, configFile }; +} +function validateOutputDir(outputDir2) { + if (!outputDir2) + return; + if (isSystemDirectory(outputDir2)) + throw new Error(`--output-dir cannot point to a system directory: ${import_path53.default.resolve(outputDir2)}.`); +} +async function resolveCLIConfigForCLI(daemonProfilesDir2, sessionName, options, env) { + const config = options.config ? import_path53.default.resolve(options.config) : void 0; + try { + const defaultConfigFile2 = import_path53.default.resolve(".playwright", "cli.config.json"); + if (!config && import_fs55.default.existsSync(defaultConfigFile2)) + options.config = defaultConfigFile2; + } catch { + } + const daemonOverrides = configFromCLIOptions({ + endpoint: options.endpoint, + cdpEndpoint: options.cdp, + config: options.config, + browser: options.browser, + headless: options.headed ? false : void 0, + extension: options.extension, + userDataDir: options.profile, + snapshotMode: "full" + }); + const envOverrides = configFromEnv(env); + const configFile = daemonOverrides.configFile ?? envOverrides.configFile; + const configInFile = await loadConfig(configFile); + const configDir = configFile ? import_path53.default.dirname(import_path53.default.resolve(configFile)) : process.cwd(); + const globalConfigPath = import_path53.default.join((env ?? process.env)["PWTEST_CLI_GLOBAL_CONFIG"] ?? import_os25.default.homedir(), ".playwright", "cli.config.json"); + const globalConfigExists = import_fs55.default.existsSync(globalConfigPath); + const globalConfigInFile = await loadConfig(globalConfigExists ? globalConfigPath : void 0); + const globalConfigDir = globalConfigExists ? import_path53.default.dirname(globalConfigPath) : process.cwd(); + let result2 = defaultConfig; + result2 = mergeConfig(result2, resolveConfigPaths(globalConfigInFile, globalConfigDir)); + result2 = mergeConfig(result2, resolveConfigPaths(configInFile, configDir)); + result2 = mergeConfig(result2, resolveConfigPaths(envOverrides, process.cwd())); + result2 = mergeConfig(result2, resolveConfigPaths(daemonOverrides, process.cwd())); + if (result2.browser.isolated === void 0) + result2.browser.isolated = !options.profile && !options.persistent && !result2.browser.userDataDir && !result2.browser.remoteEndpoint && !result2.browser.cdpEndpoint && !result2.extension; + if (result2.browser.launchOptions.headless === void 0) + result2.browser.launchOptions.headless = true; + const browser = await validateBrowserConfig(result2.browser); + validateOutputDir(result2.outputDir); + if (!result2.extension && !browser.isolated && !browser.userDataDir && !browser.remoteEndpoint && !browser.cdpEndpoint) { + const browserToken = browser.launchOptions?.channel ?? browser?.browserName; + const userDataDir = import_path53.default.resolve(daemonProfilesDir2, `ud-${sessionName}-${browserToken}`); + browser.userDataDir = userDataDir; + } + return { ...result2, browser, configFile, skillMode: true }; +} +function resolveExtensionOptions(cliOptions) { + const browser = cliOptions.browser ?? envToString(process.env.PLAYWRIGHT_MCP_BROWSER); + const { channel } = resolveBrowserParam(browser); + const executablePath = cliOptions.executablePath ?? envToString(process.env.PLAYWRIGHT_MCP_EXECUTABLE_PATH); + return { channel: channel ?? "chrome", executablePath }; +} +async function validateBrowserConfig(browser) { + let browserName = browser.browserName; + if (!browserName) { + browserName = "chromium"; + if (browser.launchOptions.channel === void 0) + browser.launchOptions.channel = "chrome"; + } + if (browser.browserName === "chromium" && browser.launchOptions.chromiumSandbox === void 0) { + if (process.platform === "linux") + browser.launchOptions.chromiumSandbox = browser.launchOptions.channel !== "chromium" && browser.launchOptions.channel !== "chrome-for-testing"; + else + browser.launchOptions.chromiumSandbox = true; + } + if (browser.isolated && browser.userDataDir) + throw new Error("Browser userDataDir is not supported in isolated mode."); + if (browser.initScript) { + for (const script of browser.initScript) { + if (!await fileExistsAsync(script)) + throw new Error(`Init script file does not exist: ${script}`); + } + } + if (browser.initPage) { + for (const page of browser.initPage) { + if (!await fileExistsAsync(page)) + throw new Error(`Init page file does not exist: ${page}`); + } + } + if (browser.contextOptions.viewport === void 0) { + if (browser.launchOptions.headless) + browser.contextOptions.viewport = { width: 1280, height: 720 }; + else + browser.contextOptions.viewport = null; + } + if (browserName === "chromium") { + browser.launchOptions.args = browser.launchOptions.args ?? []; + if (!browser.launchOptions.args.some((a) => a.includes("--disable-blink-features"))) + browser.launchOptions.args.push(`--disable-blink-features=AutomationControlled`); + } + return { ...browser, browserName }; +} +function resolveBrowserParam(browserOption) { + switch (browserOption) { + case "chrome": + case "chrome-beta": + case "chrome-canary": + case "chrome-dev": + case "msedge": + case "msedge-beta": + case "msedge-canary": + case "msedge-dev": + return { browserName: "chromium", channel: browserOption }; + case "chromium": + return { browserName: "chromium", channel: "chrome-for-testing" }; + case "firefox": + return { browserName: "firefox" }; + case "moz-firefox": + case "moz-firefox-beta": + case "moz-firefox-nightly": + return { browserName: "firefox", channel: browserOption }; + case "webkit": + return { browserName: "webkit" }; + default: + return {}; + } +} +function configFromCLIOptions(cliOptions) { + const { browserName, channel } = resolveBrowserParam(cliOptions.browser); + const launchOptions = { + channel, + executablePath: cliOptions.executablePath, + headless: cliOptions.headless + }; + if (cliOptions.sandbox !== void 0) + launchOptions.chromiumSandbox = cliOptions.sandbox; + if (cliOptions.device && cliOptions.cdpEndpoint) + throw new Error("Device emulation is not supported with cdpEndpoint."); + const contextOptions = cliOptions.device ? playwright.devices[cliOptions.device] : {}; + if (cliOptions.proxyServer) { + const proxy = { server: cliOptions.proxyServer }; + if (cliOptions.proxyBypass) + proxy.bypass = cliOptions.proxyBypass; + launchOptions.proxy = proxy; + contextOptions.proxy = proxy; + } + if (cliOptions.storageState) + contextOptions.storageState = cliOptions.storageState; + if (cliOptions.userAgent) + contextOptions.userAgent = cliOptions.userAgent; + if (cliOptions.viewportSize) + contextOptions.viewport = cliOptions.viewportSize; + if (cliOptions.ignoreHttpsErrors) + contextOptions.ignoreHTTPSErrors = true; + if (cliOptions.blockServiceWorkers) + contextOptions.serviceWorkers = "block"; + if (cliOptions.grantPermissions) + contextOptions.permissions = cliOptions.grantPermissions; + const config = { + browser: { + browserName, + isolated: cliOptions.isolated, + userDataDir: cliOptions.userDataDir, + launchOptions, + contextOptions, + cdpEndpoint: cliOptions.cdpEndpoint, + cdpHeaders: cliOptions.cdpHeader, + cdpTimeout: cliOptions.cdpTimeout, + initPage: cliOptions.initPage, + initScript: cliOptions.initScript, + remoteEndpoint: cliOptions.endpoint + }, + extension: cliOptions.extension, + server: { + port: cliOptions.port, + host: cliOptions.host, + allowedHosts: cliOptions.allowedHosts + }, + capabilities: cliOptions.caps, + console: { + level: cliOptions.consoleLevel + }, + network: { + allowedOrigins: cliOptions.allowedOrigins, + blockedOrigins: cliOptions.blockedOrigins + }, + allowUnrestrictedFileAccess: cliOptions.allowUnrestrictedFileAccess, + codegen: cliOptions.codegen, + saveSession: cliOptions.saveSession, + secrets: cliOptions.secrets, + sharedBrowserContext: cliOptions.sharedBrowserContext, + snapshot: cliOptions.snapshotMode ? { mode: cliOptions.snapshotMode } : void 0, + outputDir: cliOptions.outputDir, + outputMaxSize: cliOptions.outputMaxSize, + imageResponses: cliOptions.imageResponses, + testIdAttribute: cliOptions.testIdAttribute, + timeouts: { + action: cliOptions.timeoutAction, + navigation: cliOptions.timeoutNavigation + } + }; + if (cliOptions.remoteHeader) + config.browser.remoteHeaders = cliOptions.remoteHeader; + return { ...config, configFile: cliOptions.config }; +} +function configFromEnv(env) { + const e = env ?? process.env; + const options = {}; + options.allowedHosts = commaSeparatedList(e.PLAYWRIGHT_MCP_ALLOWED_HOSTS); + options.allowedOrigins = semicolonSeparatedList(e.PLAYWRIGHT_MCP_ALLOWED_ORIGINS); + options.allowUnrestrictedFileAccess = envToBoolean(e.PLAYWRIGHT_MCP_ALLOW_UNRESTRICTED_FILE_ACCESS); + options.blockedOrigins = semicolonSeparatedList(e.PLAYWRIGHT_MCP_BLOCKED_ORIGINS); + options.blockServiceWorkers = envToBoolean(e.PLAYWRIGHT_MCP_BLOCK_SERVICE_WORKERS); + options.browser = envToString(e.PLAYWRIGHT_MCP_BROWSER); + options.caps = commaSeparatedList(e.PLAYWRIGHT_MCP_CAPS); + options.cdpEndpoint = envToString(e.PLAYWRIGHT_MCP_CDP_ENDPOINT); + options.cdpHeader = headerParser(envToString(e.PLAYWRIGHT_MCP_CDP_HEADERS)); + options.cdpTimeout = numberParser(e.PLAYWRIGHT_MCP_CDP_TIMEOUT); + options.config = envToString(e.PLAYWRIGHT_MCP_CONFIG); + if (e.PLAYWRIGHT_MCP_CONSOLE_LEVEL) + options.consoleLevel = enumParser("--console-level", ["error", "warning", "info", "debug"], e.PLAYWRIGHT_MCP_CONSOLE_LEVEL); + options.device = envToString(e.PLAYWRIGHT_MCP_DEVICE); + options.executablePath = envToString(e.PLAYWRIGHT_MCP_EXECUTABLE_PATH); + options.extension = envToBoolean(e.PLAYWRIGHT_MCP_EXTENSION); + options.grantPermissions = commaSeparatedList(e.PLAYWRIGHT_MCP_GRANT_PERMISSIONS); + options.headless = envToBoolean(e.PLAYWRIGHT_MCP_HEADLESS); + options.host = envToString(e.PLAYWRIGHT_MCP_HOST); + options.ignoreHttpsErrors = envToBoolean(e.PLAYWRIGHT_MCP_IGNORE_HTTPS_ERRORS); + const initPage = envToString(e.PLAYWRIGHT_MCP_INIT_PAGE); + if (initPage) + options.initPage = [initPage]; + const initScript = envToString(e.PLAYWRIGHT_MCP_INIT_SCRIPT); + if (initScript) + options.initScript = [initScript]; + options.isolated = envToBoolean(e.PLAYWRIGHT_MCP_ISOLATED); + if (e.PLAYWRIGHT_MCP_IMAGE_RESPONSES) + options.imageResponses = enumParser("--image-responses", ["allow", "omit"], e.PLAYWRIGHT_MCP_IMAGE_RESPONSES); + options.sandbox = envToBoolean(e.PLAYWRIGHT_MCP_SANDBOX); + options.outputDir = envToString(e.PLAYWRIGHT_MCP_OUTPUT_DIR); + options.outputMaxSize = numberParser(e.PLAYWRIGHT_MCP_OUTPUT_MAX_SIZE); + options.port = numberParser(e.PLAYWRIGHT_MCP_PORT); + options.proxyBypass = envToString(e.PLAYWRIGHT_MCP_PROXY_BYPASS); + options.proxyServer = envToString(e.PLAYWRIGHT_MCP_PROXY_SERVER); + options.remoteHeader = headerParser(envToString(e.PLAYWRIGHT_MCP_REMOTE_HEADERS)); + options.secrets = dotenvFileLoader(e.PLAYWRIGHT_MCP_SECRETS_FILE); + options.storageState = envToString(e.PLAYWRIGHT_MCP_STORAGE_STATE); + options.testIdAttribute = envToString(e.PLAYWRIGHT_MCP_TEST_ID_ATTRIBUTE); + options.timeoutAction = numberParser(e.PLAYWRIGHT_MCP_TIMEOUT_ACTION); + options.timeoutNavigation = numberParser(e.PLAYWRIGHT_MCP_TIMEOUT_NAVIGATION); + options.userAgent = envToString(e.PLAYWRIGHT_MCP_USER_AGENT); + options.userDataDir = envToString(e.PLAYWRIGHT_MCP_USER_DATA_DIR); + options.viewportSize = resolutionParser("--viewport-size", e.PLAYWRIGHT_MCP_VIEWPORT_SIZE); + return configFromCLIOptions(options); +} +async function loadConfig(configFile) { + if (!configFile) + return {}; + if (configFile.endsWith(".ini")) + return configFromIniFile(configFile); + try { + const data = await import_fs55.default.promises.readFile(configFile, "utf8"); + return JSON.parse(data.charCodeAt(0) === 65279 ? data.slice(1) : data); + } catch { + return configFromIniFile(configFile); + } +} +function resolveConfigPaths(config, baseDir) { + if (config.browser?.initPage) + config.browser.initPage = config.browser.initPage.map((p) => import_path53.default.resolve(baseDir, p)); + if (config.browser?.initScript) + config.browser.initScript = config.browser.initScript.map((p) => import_path53.default.resolve(baseDir, p)); + return config; +} +function pickDefined(obj) { + return Object.fromEntries( + Object.entries(obj ?? {}).filter(([_, v]) => v !== void 0) + ); +} +function mergeConfig(base, overrides) { + const browser = { + ...pickDefined(base.browser), + ...pickDefined(overrides.browser), + browserName: overrides.browser?.browserName ?? base.browser?.browserName, + isolated: overrides.browser?.isolated ?? base.browser?.isolated, + launchOptions: { + ...pickDefined(base.browser?.launchOptions), + ...pickDefined(overrides.browser?.launchOptions) + }, + contextOptions: { + ...pickDefined(base.browser?.contextOptions), + ...pickDefined(overrides.browser?.contextOptions) + } + }; + if (browser.browserName !== "chromium" && browser.launchOptions && !browser.launchOptions.channel?.startsWith("moz-")) + delete browser.launchOptions.channel; + return { + ...pickDefined(base), + ...pickDefined(overrides), + browser, + console: { + ...pickDefined(base.console), + ...pickDefined(overrides.console) + }, + network: { + ...pickDefined(base.network), + ...pickDefined(overrides.network) + }, + server: { + ...pickDefined(base.server), + ...pickDefined(overrides.server) + }, + snapshot: { + ...pickDefined(base.snapshot), + ...pickDefined(overrides.snapshot) + }, + timeouts: { + ...pickDefined(base.timeouts), + ...pickDefined(overrides.timeouts) + } + }; +} +function semicolonSeparatedList(value2) { + if (!value2) + return void 0; + return value2.split(";").map((v) => v.trim()); +} +function commaSeparatedList(value2) { + if (!value2) + return void 0; + return value2.split(",").map((v) => v.trim()); +} +function dotenvFileLoader(value2) { + if (!value2) + return void 0; + return dotenv2.parse(import_fs55.default.readFileSync(value2, "utf8")); +} +function numberParser(value2) { + if (!value2) + return void 0; + return +value2; +} +function resolutionParser(name, value2) { + if (!value2) + return void 0; + if (value2.includes("x")) { + const [width, height] = value2.split("x").map((v) => +v); + if (isNaN(width) || isNaN(height) || width <= 0 || height <= 0) + throw new Error(`Invalid resolution format: use ${name}="800x600"`); + return { width, height }; + } + if (value2.includes(",")) { + const [width, height] = value2.split(",").map((v) => +v); + if (isNaN(width) || isNaN(height) || width <= 0 || height <= 0) + throw new Error(`Invalid resolution format: use ${name}="800x600"`); + return { width, height }; + } + throw new Error(`Invalid resolution format: use ${name}="800x600"`); +} +function headerParser(arg, previous) { + if (!arg) + return previous; + const result2 = { ...previous ?? {} }; + const colonIndex = arg.indexOf(":"); + const name = colonIndex === -1 ? arg.trim() : arg.substring(0, colonIndex).trim(); + const value2 = colonIndex === -1 ? "" : arg.substring(colonIndex + 1).trim(); + result2[name] = value2; + return result2; +} +function enumParser(name, options, value2) { + if (!options.includes(value2)) + throw new Error(`Invalid ${name}: ${value2}. Valid values are: ${options.join(", ")}`); + return value2; +} +function envToBoolean(value2) { + if (value2 === "true" || value2 === "1") + return true; + if (value2 === "false" || value2 === "0") + return false; + return void 0; +} +function envToString(value2) { + return value2 ? value2.trim() : void 0; +} +var import_fs55, import_path53, import_os25, dotenv2, defaultConfig; +var init_config2 = __esm({ + "packages/playwright-core/src/tools/mcp/config.ts"() { + "use strict"; + import_fs55 = __toESM(require("fs")); + import_path53 = __toESM(require("path")); + import_os25 = __toESM(require("os")); + init_fileUtils(); + init_inprocess(); + init_configIni(); + dotenv2 = require("./utilsBundle").dotenv; + defaultConfig = { + browser: { + launchOptions: {}, + contextOptions: {} + }, + timeouts: { + action: 5e3, + navigation: 6e4, + expect: 5e3 + } + }; + } +}); + +// packages/playwright-core/src/tools/mcp/cdpRelayV1.ts +var ExtensionProtocolV1; +var init_cdpRelayV1 = __esm({ + "packages/playwright-core/src/tools/mcp/cdpRelayV1.ts"() { + "use strict"; + ExtensionProtocolV1 = class { + constructor(sendCommand) { + this._sendToCDPClient = null; + this._nextSessionId = 1; + this._sendCommand = sendCommand; + } + connectOverCDP(sendToCDPClient) { + this._sendToCDPClient = sendToCDPClient; + } + handleExtensionEvent(method, params2) { + switch (method) { + case "forwardCDPEvent": { + const p = params2; + const sessionId = p.sessionId || this._connectedTabInfo?.sessionId; + this._sendToCDPClient?.({ + sessionId, + method: p.method, + params: p.params + }); + break; + } + } + } + async handleCDPCommand(method, params2, sessionId) { + switch (method) { + case "Target.setAutoAttach": { + if (sessionId) + return void 0; + const { targetInfo } = await this._sendCommand("attachToTab", {}); + this._connectedTabInfo = { + targetInfo, + sessionId: `pw-tab-${this._nextSessionId++}` + }; + this._sendToCDPClient?.({ + method: "Target.attachedToTarget", + params: { + sessionId: this._connectedTabInfo.sessionId, + targetInfo: { + ...this._connectedTabInfo.targetInfo, + attached: true + }, + waitingForDebugger: false + } + }); + return { result: {} }; + } + case "Target.getTargetInfo": { + return { result: this._connectedTabInfo?.targetInfo }; + } + case "Target.createTarget": { + throw new Error("Tab creation is not supported yet."); + } + } + return void 0; + } + async forwardToExtension(method, params2, sessionId) { + if (this._connectedTabInfo?.sessionId === sessionId) + sessionId = void 0; + return await this._sendCommand("forwardCDPCommand", { sessionId, method, params: params2 }); + } + ready() { + return Promise.resolve(); + } + onExtensionDisconnect(_reason) { + } + }; + } +}); + +// packages/playwright-core/src/tools/mcp/browserModel.ts +var BrowserModel; +var init_browserModel = __esm({ + "packages/playwright-core/src/tools/mcp/browserModel.ts"() { + "use strict"; + init_log(); + BrowserModel = class { + constructor(sendToExtension) { + // Set only while a Playwright CDP connection is attached (see + // `connectOverCDP`). Before that, any attempt to emit to Playwright is a + // no-op โ the model is observation-only during the extension handshake. + this._sendToCDPClient = null; + // Tabs observed via chrome.tabs.onCreated, whether or not the debugger is attached yet. + this._knownTabs = /* @__PURE__ */ new Map(); + // Subset of _knownTabs we've attached the debugger to and assigned a sessionId. + this._tabSessions = /* @__PURE__ */ new Map(); + this._autoAttach = false; + this._nextSessionId = 1; + this._sendToExtension = sendToExtension; + } + // Wires the model's CDP output sink. Called by the handler once the + // extension handshake is done and a Playwright CDP client is ready to + // receive events. Before this call the model is observation-only โ + // `Target.attachedToTarget` etc. would be answered into a black hole. + connectOverCDP(sendToCDPClient) { + this._sendToCDPClient = sendToCDPClient; + } + _emit(message) { + this._sendToCDPClient?.(message); + } + // โโโ Extension โ model inputs โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ + onTabCreated(tab2) { + if (tab2.id === void 0) + return; + this._knownTabs.set(tab2.id, tab2); + if (this._autoAttach) + void this._attachTab(tab2.id).catch(logUnhandledError); + } + onTabRemoved(tabId) { + this._knownTabs.delete(tabId); + this._detachTab(tabId); + } + onDebuggerEvent(source11, method, params2) { + if (source11.tabId === void 0) + return; + const tabSession = this._tabSessions.get(source11.tabId); + if (!tabSession) + return; + const childSessionId = params2?.sessionId; + if (method === "Target.attachedToTarget" && childSessionId) + tabSession.childSessions.add(childSessionId); + else if (method === "Target.detachedFromTarget" && childSessionId) + tabSession.childSessions.delete(childSessionId); + const sessionId = source11.sessionId || tabSession.sessionId; + this._emit({ sessionId, method, params: params2 }); + } + onDebuggerDetach(source11) { + if (source11.tabId !== void 0) + this._detachTab(source11.tabId); + } + // โโโ Playwright โ model commands โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ + // Turn auto-attach on and attach to every tab we already know about. + // Called in response to Playwright's `Target.setAutoAttach` on the root session. + async enableAutoAttach() { + this._autoAttach = true; + const tabIds = [...this._knownTabs.keys()]; + await Promise.all(tabIds.map((tabId) => this._attachTab(tabId).catch(logUnhandledError))); + } + async createTarget(url2) { + const tab2 = await this._sendToExtension("chrome.tabs.create", [{ url: url2 }]); + if (tab2?.id === void 0) + throw new Error("Failed to create tab"); + this._knownTabs.set(tab2.id, tab2); + const tabSession = await this._attachTab(tab2.id); + return { targetId: tabSession.targetInfo?.targetId }; + } + async closeTarget(targetId) { + const tabSession = targetId ? this._findTabSession((s) => s.targetInfo?.targetId === targetId) : void 0; + if (!tabSession) + return { success: false }; + await this._sendToExtension("chrome.tabs.remove", [tabSession.tabId]); + return { success: true }; + } + getTargetInfo(sessionId) { + if (!sessionId) + return void 0; + return this._findTabSession((s) => s.sessionId === sessionId)?.targetInfo; + } + // Forward a browser-level CDP command (Storage.*, Browser.*, etc.) that is + // not associated with a specific tab. chrome.debugger.sendCommand requires a + // target, so we route through any attached tab; the command itself is + // browser-scoped and returns the same result regardless of which tab is used. + async sendBrowserCommand(method, params2) { + const tabSession = this._tabSessions.values().next().value; + if (!tabSession) + throw new Error(`No attached tab to forward browser-level command: ${method}`); + return await this._sendToExtension("chrome.debugger.sendCommand", [ + { tabId: tabSession.tabId }, + method, + params2 + ]); + } + // Forward a CDP command from Playwright to the tab its sessionId resolves to. + async sendCommand(sessionId, method, params2) { + let tabSession = this._findTabSession((s) => s.sessionId === sessionId); + let cdpSessionId; + if (!tabSession) { + tabSession = this._findTabSession((s) => s.childSessions.has(sessionId)); + cdpSessionId = sessionId; + } + if (!tabSession) + throw new Error(`No tab found for sessionId: ${sessionId}`); + return await this._sendToExtension("chrome.debugger.sendCommand", [ + { tabId: tabSession.tabId, sessionId: cdpSessionId }, + method, + params2 + ]); + } + // โโโ Internals โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ + async _attachTab(tabId) { + const existing = this._tabSessions.get(tabId); + if (existing) + return existing; + await this._sendToExtension("chrome.debugger.attach", [{ tabId }, "1.3"]); + const result2 = await this._sendToExtension("chrome.debugger.sendCommand", [ + { tabId }, + "Target.getTargetInfo" + ]); + const targetInfo = result2?.targetInfo; + const sessionId = `pw-tab-${this._nextSessionId++}`; + const tabSession = { tabId, sessionId, targetInfo, childSessions: /* @__PURE__ */ new Set() }; + this._tabSessions.set(tabId, tabSession); + this._emit({ + method: "Target.attachedToTarget", + params: { + sessionId, + targetInfo: { ...targetInfo, attached: true }, + waitingForDebugger: false + } + }); + return tabSession; + } + _detachTab(tabId) { + const tabSession = this._tabSessions.get(tabId); + if (!tabSession) + return; + this._tabSessions.delete(tabId); + this._emit({ + method: "Target.detachedFromTarget", + params: { + sessionId: tabSession.sessionId, + targetId: tabSession.targetInfo?.targetId + } + }); + } + _findTabSession(predicate) { + for (const session2 of this._tabSessions.values()) { + if (predicate(session2)) + return session2; + } + return void 0; + } + }; + } +}); + +// packages/playwright-core/src/tools/mcp/cdpRelayV2.ts +var ExtensionProtocolV2; +var init_cdpRelayV2 = __esm({ + "packages/playwright-core/src/tools/mcp/cdpRelayV2.ts"() { + "use strict"; + init_manualPromise(); + init_log(); + init_browserModel(); + ExtensionProtocolV2 = class { + constructor(sendCommand) { + // Resolved by `extension.initialized`. Purely a handshake signal for the + // relay โ the model itself is oblivious to this phase. + this._ready = new ManualPromise(); + this._model = new BrowserModel(sendCommand); + void this._ready.catch(logUnhandledError); + } + ready() { + return this._ready; + } + connectOverCDP(sendToCDPClient) { + this._model.connectOverCDP(sendToCDPClient); + } + onExtensionDisconnect(reason) { + if (!this._ready.isDone()) + this._ready.reject(new Error(`Extension disconnected before initialization: ${reason}`)); + } + handleExtensionEvent(method, params2) { + switch (method) { + case "chrome.debugger.onEvent": { + const [source11, cdpMethod, cdpParams] = params2; + this._model.onDebuggerEvent(source11, cdpMethod, cdpParams); + break; + } + case "chrome.debugger.onDetach": { + const [source11] = params2; + this._model.onDebuggerDetach(source11); + break; + } + case "chrome.tabs.onCreated": { + const [tab2] = params2; + this._model.onTabCreated(tab2); + break; + } + case "chrome.tabs.onRemoved": { + const [tabId] = params2; + this._model.onTabRemoved(tabId); + break; + } + case "extension.initialized": { + this._ready.resolve(); + break; + } + } + } + async handleCDPCommand(method, params2, sessionId) { + switch (method) { + case "Target.setAutoAttach": { + if (sessionId) + return void 0; + await this._model.enableAutoAttach(); + return { result: {} }; + } + case "Target.createTarget": + return { result: await this._model.createTarget(params2?.url) }; + case "Target.closeTarget": + return { result: await this._model.closeTarget(params2?.targetId) }; + case "Target.getTargetInfo": + return { result: this._model.getTargetInfo(sessionId) }; + } + return void 0; + } + async forwardToExtension(method, params2, sessionId) { + if (!sessionId) + return await this._model.sendBrowserCommand(method, params2); + return await this._model.sendCommand(sessionId, method, params2); + } + }; + } +}); + +// packages/playwright-core/src/tools/mcp/protocol.ts +var DEFAULT_VERSION; +var init_protocol = __esm({ + "packages/playwright-core/src/tools/mcp/protocol.ts"() { + "use strict"; + DEFAULT_VERSION = 2; + } +}); + +// packages/playwright-core/src/tools/mcp/cdpRelay.ts +var import_child_process6, import_os26, debug15, ws3, wsServer3, debugLogger2, CDPRelayServer, ExtensionConnection; +var init_cdpRelay = __esm({ + "packages/playwright-core/src/tools/mcp/cdpRelay.ts"() { + "use strict"; + import_child_process6 = require("child_process"); + import_os26 = __toESM(require("os")); + init_manualPromise(); + init_registry(); + init_extension(); + init_http(); + init_log(); + init_cdpRelayV1(); + init_cdpRelayV2(); + init_protocol(); + debug15 = require("./utilsBundle").debug; + ws3 = require("./utilsBundle").ws; + ({ wsServer: wsServer3 } = require("./utilsBundle")); + debugLogger2 = debug15("pw:mcp:relay"); + CDPRelayServer = class { + constructor(server, browserChannel, executablePath) { + this._cdpConnection = null; + this._extensionConnection = null; + this._extensionConnectionPromise = new ManualPromise(); + this._wsHost = addressToString(server.address(), { protocol: "ws" }); + this._browserChannel = browserChannel; + this._executablePath = executablePath; + this._protocolVersion = parseInt(process.env.PLAYWRIGHT_EXTENSION_PROTOCOL ?? DEFAULT_VERSION.toString(), 10); + const sendCommand = (method, params2) => { + if (!this._extensionConnection) + throw new Error("Extension not connected"); + return this._extensionConnection.send(method, params2); + }; + if (this._protocolVersion >= 2) + this._handler = new ExtensionProtocolV2(sendCommand); + else + this._handler = new ExtensionProtocolV1(sendCommand); + const uuid = crypto.randomUUID(); + this._cdpPath = `/cdp/${uuid}`; + this._extensionPath = `/extension/${uuid}`; + void this._extensionConnectionPromise.catch(logUnhandledError); + this._wss = new wsServer3({ server }); + this._wss.on("connection", this._onConnection.bind(this)); + } + cdpEndpoint() { + return `${this._wsHost}${this._cdpPath}`; + } + extensionEndpoint() { + return `${this._wsHost}${this._extensionPath}`; + } + async establishExtensionConnection(clientName) { + debugLogger2("Establishing extension connection"); + this._openConnectPageInBrowser(clientName); + debugLogger2("Waiting for incoming extension connection"); + await this._extensionConnectionPromise; + await this._handler.ready(); + debugLogger2("Extension connection established"); + } + _openConnectPageInBrowser(clientName) { + const mcpRelayEndpoint = `${this._wsHost}${this._extensionPath}`; + const url2 = new URL(`chrome-extension://${playwrightExtensionId}/connect.html`); + url2.searchParams.set("mcpRelayUrl", mcpRelayEndpoint); + const client = { + name: clientName, + // Not used anymore. + version: void 0 + }; + url2.searchParams.set("client", JSON.stringify(client)); + url2.searchParams.set("protocolVersion", this._protocolVersion.toString()); + const token = process.env.PLAYWRIGHT_MCP_EXTENSION_TOKEN; + if (token) + url2.searchParams.set("token", token); + const href = url2.toString(); + const channel = registry.isChromiumAlias(this._browserChannel) ? "chromium" : this._browserChannel; + let executablePath = this._executablePath; + if (!executablePath) { + const executableInfo = registry.findExecutable(channel); + if (!executableInfo) + throw new Error(`Unsupported channel: "${this._browserChannel}"`); + executablePath = executableInfo.executablePath(); + if (!executablePath) + throw new Error(`"${this._browserChannel}" executable not found. Make sure it is installed at a standard location.`); + } + const args = []; + const userDataDir = process.env.PWTEST_EXTENSION_USER_DATA_DIR; + if (userDataDir) + args.push(`--user-data-dir=${userDataDir}`); + if (import_os26.default.platform() === "linux" && channel === "chromium") + args.push("--no-sandbox"); + args.push(href); + (0, import_child_process6.spawn)(executablePath, args, { + windowsHide: true, + detached: true, + shell: false, + stdio: "ignore" + }); + } + stop() { + this._closeConnections("Server stopped"); + this._wss.close(); + } + _closeConnections(reason) { + this._closeCDPConnection(reason); + this._closeExtensionConnection(reason); + } + _onConnection(ws4, request2) { + const url2 = new URL(`http://localhost${request2.url}`); + debugLogger2(`New connection to ${url2.pathname}`); + if (url2.pathname === this._cdpPath) { + this._handlePlaywrightConnection(ws4); + } else if (url2.pathname === this._extensionPath) { + this._handleExtensionConnection(ws4); + } else { + debugLogger2(`Invalid path: ${url2.pathname}`); + ws4.close(4004, "Invalid path"); + } + } + _handlePlaywrightConnection(ws4) { + if (!this._extensionConnection) { + debugLogger2("Rejecting Playwright connection: extension not connected"); + ws4.close(1e3, "Extension not connected"); + return; + } + if (this._cdpConnection) { + debugLogger2("Rejecting second Playwright connection"); + ws4.close(1e3, "Another CDP client already connected"); + return; + } + this._cdpConnection = ws4; + this._handler.connectOverCDP((msg) => this._sendToCDPClient(msg)); + ws4.on("message", async (data) => { + try { + await this._handlePlaywrightMessage(JSON.parse(data.toString())); + } catch (error) { + debugLogger2(`Error while handling Playwright message +${data.toString()} +`, error); + } + }); + ws4.on("close", () => { + this._closeExtensionConnection("Playwright client disconnected"); + debugLogger2("Playwright WebSocket closed"); + }); + ws4.on("error", (error) => { + debugLogger2("Playwright WebSocket error:", error); + }); + debugLogger2("Playwright MCP connected"); + } + _closeExtensionConnection(reason) { + this._extensionConnection?.close(reason); + if (!this._extensionConnectionPromise.isDone()) + this._extensionConnectionPromise.reject(new Error(reason)); + } + _closeCDPConnection(reason) { + if (this._cdpConnection?.readyState === ws3.OPEN) + this._cdpConnection.close(1e3, reason); + } + _handleExtensionConnection(ws4) { + if (this._extensionConnection) { + ws4.close(1e3, "Another extension connection already established"); + return; + } + this._extensionConnection = new ExtensionConnection(ws4); + this._extensionConnection.onclose = (reason) => { + debugLogger2("Extension WebSocket closed:", reason); + this._handler.onExtensionDisconnect(reason); + this._closeCDPConnection(`Extension disconnected: ${reason}`); + }; + this._extensionConnection.onmessage = (method, params2) => this._handler.handleExtensionEvent(method, params2); + this._extensionConnectionPromise.resolve(); + } + async _handlePlaywrightMessage(message) { + debugLogger2("\u2190 Playwright:", `${message.method} (id=${message.id})`); + const { id, sessionId, method, params: params2 } = message; + try { + const result2 = await this._handleCDPCommand(method, params2, sessionId); + this._sendToCDPClient({ id, sessionId, result: result2 }); + } catch (e) { + debugLogger2("Error in the extension:", e); + this._sendToCDPClient({ + id, + sessionId, + error: { message: e.message } + }); + } + } + async _handleCDPCommand(method, params2, sessionId) { + switch (method) { + case "Browser.getVersion": { + return { + protocolVersion: "1.3", + product: "Chrome/Extension-Bridge", + userAgent: "CDP-Bridge-Server/1.0.0" + }; + } + case "Browser.setDownloadBehavior": { + return {}; + } + } + const handled = await this._handler.handleCDPCommand(method, params2, sessionId); + if (handled) + return handled.result; + return await this._handler.forwardToExtension(method, params2, sessionId); + } + _sendToCDPClient(message) { + debugLogger2("\u2192 Playwright:", `${message.method ?? `response(id=${message.id})`}`); + this._cdpConnection?.send(JSON.stringify(message)); + } + }; + ExtensionConnection = class { + constructor(ws4) { + this._callbacks = /* @__PURE__ */ new Map(); + this._lastId = 0; + this._ws = ws4; + this._ws.on("message", this._onMessage.bind(this)); + this._ws.on("close", this._onClose.bind(this)); + this._ws.on("error", this._onError.bind(this)); + } + async send(method, params2) { + if (this._ws.readyState !== ws3.OPEN) + throw new Error(`Unexpected WebSocket state: ${this._ws.readyState}`); + const id = ++this._lastId; + this._ws.send(JSON.stringify({ id, method, params: params2 })); + const error = new Error(`Protocol error: ${method}`); + return new Promise((resolve, reject) => { + this._callbacks.set(id, { resolve, reject, error }); + }); + } + close(message) { + debugLogger2("closing extension connection:", message); + if (this._ws.readyState === ws3.OPEN) + this._ws.close(1e3, message); + } + _onMessage(event) { + const eventData = event.toString(); + let parsedJson; + try { + parsedJson = JSON.parse(eventData); + } catch (e) { + debugLogger2(` Closing websocket due to malformed JSON. eventData=${eventData} e=${e?.message}`); + this._ws.close(); + return; + } + try { + this._handleParsedMessage(parsedJson); + } catch (e) { + debugLogger2(` Closing websocket due to failed onmessage callback. eventData=${eventData} e=${e?.message}`); + this._ws.close(); + } + } + _handleParsedMessage(object) { + if (object.id && this._callbacks.has(object.id)) { + const callback = this._callbacks.get(object.id); + this._callbacks.delete(object.id); + if (object.error) { + const error = callback.error; + error.message = object.error; + callback.reject(error); + } else { + callback.resolve(object.result); + } + } else if (object.id) { + debugLogger2("\u2190 Extension: unexpected response", object); + } else { + this.onmessage?.(object.method, object.params); + } + } + _onClose(event) { + debugLogger2(` code=${event.code} reason=${event.reason}`); + this._dispose(); + this.onclose?.(event.reason); + } + _onError(event) { + debugLogger2(` message=${event.message} type=${event.type} target=${event.target}`); + this._dispose(); + } + _dispose() { + for (const callback of this._callbacks.values()) + callback.reject(new Error("WebSocket closed")); + this._callbacks.clear(); + } + }; + } +}); + +// packages/playwright-core/src/tools/mcp/extensionContextFactory.ts +async function createExtensionBrowser(channel, executablePath, clientName) { + if (!executablePath) { + const userDataDir = process.env.PWTEST_EXTENSION_USER_DATA_DIR ?? defaultUserDataDirForChannel(channel); + if (userDataDir && !await isPlaywrightExtensionInstalled(userDataDir)) + throw new Error(`Playwright Extension not found in "${userDataDir}". Install it from ${playwrightExtensionInstallUrl}`); + } + const httpServer = createHttpServer(); + await startHttpServer(httpServer, {}); + const relay = new CDPRelayServer(httpServer, channel, executablePath); + debugLogger3(`CDP relay server started, extension endpoint: ${relay.extensionEndpoint()}.`); + try { + await relay.establishExtensionConnection(clientName); + return await playwright.chromium.connectOverCDP(relay.cdpEndpoint(), { isLocal: true, timeout: 0 }); + } catch (error) { + relay.stop(); + httpServer.close(); + throw error; + } +} +var debug16, debugLogger3; +var init_extensionContextFactory = __esm({ + "packages/playwright-core/src/tools/mcp/extensionContextFactory.ts"() { + "use strict"; + init_network(); + init_chromiumChannels(); + init_inprocess(); + init_extension(); + init_cdpRelay(); + debug16 = require("./utilsBundle").debug; + debugLogger3 = debug16("pw:mcp:relay"); + } +}); + +// packages/playwright-core/src/tools/utils/connect.ts +async function connectToBrowserAcrossVersions(descriptor) { + const pw = require(descriptor.playwrightLib); + const browserType = pw[descriptor.browser.browserName]; + return await browserType.connect(descriptor.endpoint ?? descriptor.pipeName); +} +var init_connect2 = __esm({ + "packages/playwright-core/src/tools/utils/connect.ts"() { + "use strict"; + } +}); + +// packages/playwright-core/src/tools/mcp/browserFactory.ts +async function createBrowserWithInfo(config, clientInfo, cliOptions) { + if (config.browser.remoteEndpoint) + return await createRemoteBrowser(config); + let browser; + let canBind = false; + let ownership = "own"; + if (config.browser.cdpEndpoint) { + browser = await createCDPBrowser(config, clientInfo); + canBind = true; + ownership = "attached"; + } else if (config.browser.isolated) { + browser = await createIsolatedBrowser(config, clientInfo); + canBind = true; + ownership = "own"; + } else if (config.extension) { + const { channel, executablePath } = resolveExtensionOptions(cliOptions); + browser = await createExtensionBrowser(channel, executablePath, clientInfo.clientName); + ownership = "attached"; + } else { + browser = await createPersistentBrowser(config, clientInfo); + canBind = true; + ownership = "own"; + } + return { browser, browserInfo: browserInfo(browser, config), canBind, ownership }; +} +function browserInfo(browser, config) { + return { + // eslint-disable-next-line no-restricted-syntax + guid: browser._guid, + browserName: config.browser.browserName, + launchOptions: config.browser.launchOptions, + userDataDir: config.browser.userDataDir + }; +} +async function createIsolatedBrowser(config, clientInfo) { + testDebug2("create browser (isolated)"); + const browserType = playwright[config.browser.browserName]; + const tracesDir = await computeTracesDir(config, clientInfo); + const browser = await browserType.launch({ + tracesDir, + ...config.browser.launchOptions, + handleSIGINT: false, + handleSIGTERM: false + }).catch((error) => { + throwIfExecutableMissing(error, config); + throw error; + }); + return browser; +} +async function createCDPBrowser(config, clientInfo) { + testDebug2("create browser (cdp)"); + const artifactsDir = await computeTracesDir(config, clientInfo); + const browser = await playwright.chromium.connectOverCDP(config.browser.cdpEndpoint, { + headers: config.browser.cdpHeaders, + timeout: config.browser.cdpTimeout, + artifactsDir + }); + return browser; +} +async function createRemoteBrowser(config) { + testDebug2("create browser (remote)"); + const remote = config.browser.remoteEndpoint; + const remoteHeaders = config.browser.remoteHeaders; + const remoteOptions = typeof remote === "string" ? { endpoint: remote, headers: remoteHeaders } : { ...remote, headers: { ...remoteHeaders, ...remote.headers } }; + const descriptor = await serverRegistry.find(remoteOptions.endpoint); + if (descriptor) { + const browser2 = await connectToBrowserAcrossVersions(descriptor); + return { + browser: browser2, + browserInfo: { + guid: descriptor.browser.guid, + browserName: descriptor.browser.browserName, + launchOptions: descriptor.browser.launchOptions, + userDataDir: descriptor.browser.userDataDir + }, + canBind: false, + ownership: "attached" + }; + } + const playwrightObject = playwright; + const browser = await connectToBrowser(playwrightObject, remoteOptions); + browser._connectToBrowserType(playwrightObject[browser._browserName], {}, void 0); + if (!browser.contexts().length) + await browser.newContext(config.browser.contextOptions); + return { browser, browserInfo: browserInfo(browser, config), canBind: false, ownership: "attached" }; +} +async function createPersistentBrowser(config, clientInfo) { + testDebug2("create browser (persistent)"); + const userDataDir = config.browser.userDataDir ?? await createUserDataDir(config, clientInfo); + const tracesDir = await computeTracesDir(config, clientInfo); + if (await isProfileLocked5Times(userDataDir)) + throw new Error(`Browser is already in use for ${userDataDir}, use --isolated to run multiple instances of the same browser`); + const browserType = playwright[config.browser.browserName]; + const configIgnoreDefaultArgs = config.browser.launchOptions?.ignoreDefaultArgs; + const launchOptions = { + tracesDir, + ...config.browser.launchOptions, + ...config.browser.contextOptions, + handleSIGINT: false, + handleSIGTERM: false, + ignoreDefaultArgs: configIgnoreDefaultArgs === true ? true : [ + "--disable-extensions", + ...Array.isArray(configIgnoreDefaultArgs) ? configIgnoreDefaultArgs : [] + ] + }; + try { + const browserContext = await browserType.launchPersistentContext(userDataDir, launchOptions); + const browser = browserContext.browser(); + return browser; + } catch (error) { + throwIfExecutableMissing(error, config); + if (error.message.includes("cannot open shared object file: No such file or directory")) { + const browserName = launchOptions.channel ?? config.browser.browserName; + throw new Error(`Missing system dependencies required to run browser ${browserName}. Install them with: sudo npx playwright install-deps ${browserName}`); + } + if (error.message.includes("ProcessSingleton") || error.message.includes("exitCode=21")) + throw new Error(`Browser is already in use for ${userDataDir}, use --isolated to run multiple instances of the same browser`); + throw error; + } +} +async function createUserDataDir(config, clientInfo) { + const dir = process.env.PWMCP_PROFILES_DIR_FOR_TEST ?? import_path54.default.join(defaultCacheDirectory, "ms-playwright-mcp"); + const browserToken = config.browser.launchOptions?.channel ?? config.browser?.browserName; + const rootPathToken = createHash(clientInfo.cwd); + const result2 = import_path54.default.join(dir, `mcp-${browserToken}-${rootPathToken}`); + await import_fs56.default.promises.mkdir(result2, { recursive: true }); + return result2; +} +function createHash(data) { + return import_crypto28.default.createHash("sha256").update(data).digest("hex").slice(0, 7); +} +async function computeTracesDir(config, clientInfo) { + return import_path54.default.resolve(outputDir({ config, cwd: clientInfo.cwd }), "traces"); +} +async function isProfileLocked5Times(userDataDir) { + for (let i = 0; i < 5; i++) { + if (!isProfileLocked(userDataDir)) + return false; + await new Promise((f) => setTimeout(f, 1e3)); + } + return true; +} +function isProfileLocked(userDataDir) { + const lockFile = process.platform === "win32" ? "lockfile" : "SingletonLock"; + const lockPath = import_path54.default.join(userDataDir, lockFile); + if (process.platform === "win32") { + try { + const fd = import_fs56.default.openSync(lockPath, "r+"); + import_fs56.default.closeSync(fd); + return false; + } catch (e) { + return e.code !== "ENOENT"; + } + } + try { + const target = import_fs56.default.readlinkSync(lockPath); + const pid = parseInt(target.split("-").pop() || "", 10); + if (isNaN(pid)) + return false; + process.kill(pid, 0); + return true; + } catch { + return false; + } +} +function throwIfExecutableMissing(error, config) { + if (!error.message.includes(`Executable doesn't exist`)) + return; + const target = error.message.includes("ffmpeg") ? "ffmpeg" : config.browser.launchOptions?.channel ?? config.browser.browserName; + const label = target === "ffmpeg" ? "FFmpeg" : `Browser "${target}"`; + if (config.skillMode) + throw new Error(`${label} is not installed. Run \`playwright-cli install-browser ${target}\` to install`); + throw new Error(`${label} is not installed. Run \`npx @playwright/mcp install-browser ${target}\` to install`); +} +var import_crypto28, import_fs56, import_path54; +var init_browserFactory = __esm({ + "packages/playwright-core/src/tools/mcp/browserFactory.ts"() { + "use strict"; + import_crypto28 = __toESM(require("crypto")); + import_fs56 = __toESM(require("fs")); + import_path54 = __toESM(require("path")); + init_inprocess(); + init_registry(); + init_log(); + init_context(); + init_extensionContextFactory(); + init_connect2(); + init_serverRegistry(); + init_config2(); + init_connect(); + } +}); + +// packages/playwright-core/src/tools/mcp/index.ts +async function createConnection(userConfig = {}, contextGetter) { + const config = await resolveConfig(userConfig); + const tools = filteredTools(config); + const backendFactory = { + name: "api", + nameInConfig: "api", + version: packageJSON.version, + toolSchemas: tools.map((tool) => tool.schema), + create: async (clientInfo) => { + const browser = contextGetter ? new SimpleBrowser(await contextGetter()) : (await createBrowserWithInfo(config, clientInfo, {})).browser; + const context2 = config.browser.isolated ? await browser.newContext(config.browser.contextOptions) : browser.contexts()[0]; + return new BrowserBackend(config, context2, tools); + }, + disposed: async () => { + } + }; + return createServer("api", packageJSON.version, backendFactory, false); +} +var SimpleBrowser; +var init_mcp = __esm({ + "packages/playwright-core/src/tools/mcp/index.ts"() { + "use strict"; + init_config2(); + init_tools(); + init_browserFactory(); + init_browserBackend(); + init_server2(); + init_package(); + SimpleBrowser = class { + constructor(context2) { + this._context = context2; + } + contexts() { + return [this._context]; + } + async newContext() { + throw new Error("Creating a new context is not supported in SimpleBrowserContextFactory."); + } + }; + } +}); + +// packages/playwright-core/src/tools/mcp/program.ts +function decorateMCPCommand(command) { + command.option("--allowed-hosts ", "comma-separated list of hosts this server is allowed to serve from. Defaults to the host the server is bound to. Pass '*' to disable the host check.", commaSeparatedList).option("--allowed-origins ", "semicolon-separated list of TRUSTED origins to allow the browser to request. Default is to allow all.\nImportant: *does not* serve as a security boundary and *does not* affect redirects. ", semicolonSeparatedList).option("--allow-unrestricted-file-access", "allow access to files outside of the workspace roots. Also allows unrestricted access to file:// URLs. By default access to file system is restricted to workspace root directories (or cwd if no roots are configured) only, and navigation to file:// URLs is blocked.").option("--blocked-origins ", "semicolon-separated list of origins to block the browser from requesting. Blocklist is evaluated before allowlist. If used without the allowlist, requests not matching the blocklist are still allowed.\nImportant: *does not* serve as a security boundary and *does not* affect redirects.", semicolonSeparatedList).option("--block-service-workers", "block service workers").option("--browser ", "browser or chrome channel to use, possible values: chrome, firefox, webkit, msedge.").option("--caps ", "comma-separated list of additional capabilities to enable, possible values: vision, pdf, devtools.", commaSeparatedList).option("--cdp-endpoint ", "CDP endpoint to connect to.").option("--cdp-header ", "CDP headers to send with the connect request, multiple can be specified.", headerParser).option("--cdp-timeout ", "timeout in milliseconds for connecting to CDP endpoint, defaults to 30000ms", numberParser).option("--codegen ", 'specify the language to use for code generation, possible values: "typescript", "none". Default is "typescript".', enumParser.bind(null, "--codegen", ["none", "typescript"])).option("--config ", "path to the configuration file.").option("--console-level ", 'level of console messages to return: "error", "warning", "info", "debug". Each level includes the messages of more severe levels.', enumParser.bind(null, "--console-level", ["error", "warning", "info", "debug"])).option("--device ", 'device to emulate, for example: "iPhone 15"').option("--executable-path ", "path to the browser executable.").option("--extension", 'Connect to a running browser instance (Edge/Chrome only). Requires the "Playwright Extension" to be installed.').option("--endpoint ", "Bound browser endpoint to connect to.").option("--grant-permissions ", 'List of permissions to grant to the browser context, for example "geolocation", "clipboard-read", "clipboard-write".', commaSeparatedList).option("--headless", "run browser in headless mode, headed by default").option("--host ", "host to bind server to. Default is localhost. Use 0.0.0.0 to bind to all interfaces.").option("--ignore-https-errors", "ignore https errors").option("--init-page ", "path to TypeScript file to evaluate on Playwright page object").option("--init-script ", "path to JavaScript file to add as an initialization script. The script will be evaluated in every page before any of the page's scripts. Can be specified multiple times.").option("--isolated", "keep the browser profile in memory, do not save it to disk.").option("--image-responses ", 'whether to send image responses to the client. Can be "allow" or "omit", Defaults to "allow".', enumParser.bind(null, "--image-responses", ["allow", "omit"])).option("--no-sandbox", "disable the sandbox for all process types that are normally sandboxed.").option("--output-dir ", "path to the directory for output files.").option("--output-max-size ", "Threshold for evicting old output files, in bytes.", numberParser).option("--output-mode ", 'whether to save snapshots, console messages, network logs to a file or to the standard output. Can be "file" or "stdout". Default is "stdout".', enumParser.bind(null, "--output-mode", ["file", "stdout"])).option("--port ", "port to listen on for SSE transport.").option("--proxy-bypass ", 'comma-separated domains to bypass proxy, for example ".com,chromium.org,.domain.com"').option("--proxy-server ", 'specify proxy server, for example "http://myproxy:3128" or "socks5://myproxy:8080"').addOption(new ProgramOption("--remote-header ", "headers to send with the remote endpoint connect request, multiple can be specified.").argParser(headerParser).hideHelp()).option("--sandbox", "enable the sandbox for all process types that are normally not sandboxed.").option("--save-session", "Whether to save the Playwright MCP session into the output directory.").option("--secrets ", "path to a file containing secrets in the dotenv format", dotenvFileLoader).option("--shared-browser-context", "reuse the same browser context between all connected HTTP clients.").option("--snapshot-mode ", 'when taking snapshots for responses, specifies the mode to use. Can be "full" or "none". Default is "full".').option("--storage-state ", "path to the storage state file for isolated sessions.").option("--test-id-attribute ", 'specify the attribute to use for test ids, defaults to "data-testid"').option("--timeout-action ", "specify action timeout in milliseconds, defaults to 5000ms", numberParser).option("--timeout-navigation ", "specify navigation timeout in milliseconds, defaults to 60000ms", numberParser).option("--user-agent ", "specify user agent string").option("--user-data-dir ", "path to the user data directory. If not specified, a temporary directory will be created.").option("--viewport-size ", 'specify browser viewport size in pixels, for example "1280x720"', resolutionParser.bind(null, "--viewport-size")).addOption(new ProgramOption("--vision", "Legacy option, use --caps=vision instead").hideHelp()).action(async (options) => { + options.sandbox = options.sandbox === true ? void 0 : false; + setupExitWatchdog(); + if (options.vision) { + console.error("The --vision option is deprecated, use --caps=vision instead"); + options.caps ??= []; + options.caps.push("vision"); + } + if (options.caps?.includes("tracing")) + options.caps.push("devtools"); + const config = await resolveCLIConfigForMCP(options); + const tools = filteredTools(config); + const useSharedBrowser = config.sharedBrowserContext || config.browser.isolated; + let sharedBrowserPromise; + let clientCount = 0; + const clientNameCounters = /* @__PURE__ */ new Map(); + const factory = { + name: "Playwright", + nameInConfig: "playwright", + version: version2, + toolSchemas: tools.map((tool) => tool.schema), + create: async (clientInfo) => { + if (useSharedBrowser && !sharedBrowserPromise) { + sharedBrowserPromise = (async () => { + const { browser: browser2, canBind: canBind2 } = await createBrowserWithInfo(config, clientInfo, options); + if (canBind2) + await browser2.bind(clientInfo.clientName, { workspaceDir: clientInfo.cwd }); + return browser2; + })().catch((error) => { + sharedBrowserPromise = void 0; + throw error; + }); + } + clientCount++; + const { browser, canBind } = sharedBrowserPromise ? { browser: await sharedBrowserPromise, canBind: false } : await createBrowserWithInfo(config, clientInfo, options); + if (canBind) { + const count = (clientNameCounters.get(clientInfo.clientName) ?? 0) + 1; + clientNameCounters.set(clientInfo.clientName, count); + const sessionName = count > 1 ? `${clientInfo.clientName} (${count})` : clientInfo.clientName; + await browser.bind(sessionName, { workspaceDir: clientInfo.cwd }); + } + const browserContext = config.browser.isolated ? await browser.newContext(config.browser.contextOptions) : browser.contexts()[0]; + return new BrowserBackend(config, browserContext, tools); + }, + disposed: async (backend) => { + clientCount--; + if (sharedBrowserPromise && clientCount > 0) + return; + testDebug2("close browser"); + sharedBrowserPromise = void 0; + const browserContext = backend.browserContext; + await browserContext.close().catch(() => { + }); + await browserContext.browser()?.close().catch(() => { + }); + } + }; + await start2(factory, config.server); + }); +} +var ProgramOption, version2; +var init_program3 = __esm({ + "packages/playwright-core/src/tools/mcp/program.ts"() { + "use strict"; + init_server2(); + init_config2(); + init_watchdog(); + init_browserFactory(); + init_browserBackend(); + init_tools(); + init_log(); + init_package(); + ({ ProgramOption } = require("./utilsBundle")); + version2 = packageJSON.version; + } +}); + +// packages/playwright-core/src/tools/cli-daemon/helpGenerator.ts +function commandArgs(command) { + const args = []; + const shape = command.args ? command.args.shape : {}; + for (const [name, schema] of Object.entries(shape)) { + const zodSchema = schema; + const description = zodSchema.description ?? ""; + args.push({ name, description, optional: zodSchema.safeParse(void 0).success }); + } + return args; +} +function commandArgsText(args) { + return args.map((a) => a.optional ? `[${a.name}]` : `<${a.name}>`).join(" "); +} +function generateCommandHelp(command) { + const args = commandArgs(command); + const lines = [ + `playwright-cli ${command.name} ${commandArgsText(args)}`, + "", + command.description, + "" + ]; + if (args.length) { + lines.push("Arguments:"); + lines.push(...args.map((a) => formatWithGap(` ${a.optional ? `[${a.name}]` : `<${a.name}>`}`, a.description.toLowerCase()))); + } + if (command.options) { + lines.push("Options:"); + const optionsShape = command.options.shape; + for (const [name, schema] of Object.entries(optionsShape)) { + const zodSchema = schema; + const description = (zodSchema.description ?? "").toLowerCase(); + lines.push(formatWithGap(` --${name}`, description)); + } + } + return lines.join("\n"); +} +function generateHelp() { + const lines = []; + lines.push("Usage: playwright-cli [args] [options]"); + lines.push("Usage: playwright-cli -s= [args] [options]"); + const commandsByCategory = /* @__PURE__ */ new Map(); + for (const c of categories) + commandsByCategory.set(c.name, []); + for (const command of Object.values(commands)) { + if (command.hidden) + continue; + commandsByCategory.get(command.category).push(command); + } + for (const c of categories) { + const cc = commandsByCategory.get(c.name); + if (!cc.length) + continue; + lines.push(` +${c.title}:`); + for (const command of cc) + lines.push(generateHelpEntry(command)); + } + lines.push("\nGlobal options:"); + lines.push(formatWithGap(" --help [command]", "print help")); + lines.push(formatWithGap(" --json", "output response as JSON")); + lines.push(formatWithGap(" --raw", "output only the result value, without status and code")); + lines.push(formatWithGap(" --version", "print version")); + return lines.join("\n"); +} +function generateHelpEntry(command) { + const args = commandArgs(command); + const prefix = ` ${command.name} ${commandArgsText(args)}`; + const suffix = command.description.toLowerCase(); + return formatWithGap(prefix, suffix); +} +function unwrapZodType(schema) { + if ("unwrap" in schema && typeof schema.unwrap === "function") + return unwrapZodType(schema.unwrap()); + return schema; +} +function isBooleanSchema(schema) { + return unwrapZodType(schema) instanceof z30.ZodBoolean; +} +function generateHelpJSON() { + const booleanOptions2 = /* @__PURE__ */ new Set(); + const commandEntries = {}; + for (const [name, command] of Object.entries(commands)) { + const flags = {}; + if (command.options) { + const optionsShape = command.options.shape; + for (const [flagName, schema] of Object.entries(optionsShape)) { + const isBoolean = isBooleanSchema(schema); + flags[flagName] = isBoolean ? "boolean" : "string"; + if (isBoolean) + booleanOptions2.add(flagName); + } + } + const args = command.args ? Object.keys(command.args.shape) : []; + commandEntries[name] = { help: generateCommandHelp(command), flags, args }; + if (command.raw) + commandEntries[name].raw = true; + } + return { + global: generateHelp(), + commands: commandEntries, + booleanOptions: [...booleanOptions2] + }; +} +function formatWithGap(prefix, text2, threshold = 30) { + const indent = Math.max(1, threshold - prefix.length); + return prefix + " ".repeat(indent) + text2; +} +var z30, categories; +var init_helpGenerator = __esm({ + "packages/playwright-core/src/tools/cli-daemon/helpGenerator.ts"() { + "use strict"; + init_commands(); + z30 = require("./utilsBundle").z; + categories = [ + { name: "core", title: "Core" }, + { name: "navigation", title: "Navigation" }, + { name: "keyboard", title: "Keyboard" }, + { name: "mouse", title: "Mouse" }, + { name: "export", title: "Save as" }, + { name: "tabs", title: "Tabs" }, + { name: "storage", title: "Storage" }, + { name: "network", title: "Network" }, + { name: "devtools", title: "DevTools" }, + { name: "install", title: "Install" }, + { name: "config", title: "Configuration" }, + { name: "browsers", title: "Browser sessions" } + ]; + } +}); + +// packages/playwright-core/src/tools/cli-daemon/daemon.ts +async function socketExists(socketPath) { + try { + const stat = await import_fs57.default.promises.stat(socketPath); + if (stat?.isSocket()) + return true; + } catch (e) { + } + return false; +} +async function startCliDaemonServer(sessionName, browserContext, browserInfo2, contextConfig, clientInfo, mcpClientInfo, options) { + const sessionConfig = createSessionConfig(clientInfo, sessionName, browserInfo2, options); + const { socketPath } = sessionConfig; + if (process.platform !== "win32" && await socketExists(socketPath)) { + try { + await import_fs57.default.promises.unlink(socketPath); + } catch (error) { + throw error; + } + } + const backend = new BrowserBackend(contextConfig, browserContext, browserTools); + await backend.initialize(mcpClientInfo); + if (browserContext.isClosed()) + throw new Error("Browser context was closed before the daemon could start"); + const server = import_net10.default.createServer((socket) => { + const connection = new SocketConnection(socket); + const abortController = new AbortController(); + connection.onclose = () => abortController.abort(); + connection.onmessage = async (message) => { + const { id, method, params: params2 } = message; + try { + if (method === "stop") { + await deleteSessionFile(clientInfo, sessionConfig); + const sendAck = async () => connection.send({ id, result: "ok" }).catch(() => { + }); + if (options?.exitOnClose) + gracefullyProcessExitDoNotHang(0, () => sendAck()); + else + await sendAck(); + } else if (method === "run") { + const { toolName, toolParams } = parseCliCommand(params2.args); + toolParams._meta = { cwd: params2.cwd, raw: params2.raw || params2.json, json: !!params2.json }; + const response2 = await backend.callTool(toolName, toolParams, abortController.signal); + await connection.send({ id, result: formatResult(response2) }); + } else { + throw new Error(`Unknown method: ${method}`); + } + } catch (e) { + const error = process.env.PWDEBUGIMPL ? e.stack || e.message : e.message; + connection.send({ id, error }).catch(() => { + }); + } + }; + }); + decorateServer(server); + browserContext.on("close", () => Promise.resolve().then(async () => { + await deleteSessionFile(clientInfo, sessionConfig); + if (options?.exitOnClose) + gracefullyProcessExitDoNotHang(0); + })); + await new Promise((resolve, reject) => { + server.on("error", reject); + server.listen(socketPath, () => resolve()); + }); + await saveSessionFile(clientInfo, sessionConfig); + return socketPath; +} +async function saveSessionFile(clientInfo, sessionConfig) { + await import_fs57.default.promises.mkdir(clientInfo.daemonProfilesDir, { recursive: true }); + const sessionFile = import_path55.default.join(clientInfo.daemonProfilesDir, `${sessionConfig.name}.session`); + await import_fs57.default.promises.writeFile(sessionFile, JSON.stringify(sessionConfig, null, 2)); +} +async function deleteSessionFile(clientInfo, sessionConfig) { + await import_fs57.default.promises.unlink(sessionConfig.socketPath).catch(() => { + }); + if (!sessionConfig.cli.persistent) { + const sessionFile = import_path55.default.join(clientInfo.daemonProfilesDir, `${sessionConfig.name}.session`); + await import_fs57.default.promises.rm(sessionFile).catch(() => { + }); + } +} +function formatResult(result2) { + const isError4 = result2.isError; + const text2 = result2.content[0].type === "text" ? result2.content[0].text : void 0; + return { isError: isError4, text: text2 }; +} +function parseCliCommand(args) { + const command = commands[args._[0]]; + if (!command) + throw new Error("Command is required"); + return parseCommand(command, args); +} +function daemonSocketPath(clientInfo, sessionName) { + return makeSocketPath("cli", `${clientInfo.workspaceDirHash}-${sessionName}`); +} +function createSessionConfig(clientInfo, sessionName, browserInfo2, options = {}) { + return { + name: sessionName, + version: clientInfo.version, + timestamp: Date.now(), + socketPath: daemonSocketPath(clientInfo, sessionName), + workspaceDir: clientInfo.workspaceDir, + attached: options.ownership === "attached" ? true : void 0, + cli: { persistent: options.persistent }, + browser: { + browserName: browserInfo2.browserName, + launchOptions: browserInfo2.launchOptions, + userDataDir: browserInfo2.userDataDir + } + }; +} +var import_fs57, import_net10, import_path55; +var init_daemon = __esm({ + "packages/playwright-core/src/tools/cli-daemon/daemon.ts"() { + "use strict"; + import_fs57 = __toESM(require("fs")); + import_net10 = __toESM(require("net")); + import_path55 = __toESM(require("path")); + init_network(); + init_fileUtils(); + init_processLauncher(); + init_browserBackend(); + init_tools(); + init_command(); + init_commands(); + init_socketConnection(); + } +}); + +// packages/playwright-core/src/tools/cli-daemon/program.ts +function decorateProgram3(program4) { + program4.argument("[session-name]", "name of the session to create or connect to", "default").option("--headed", "run in headed mode (non-headless)").option("--extension", "run with the extension").option("--browser ", "browser to use (chromium, chrome, firefox, webkit)").option("--persistent", "use a persistent browser context").option("--profile ", "path to the user data dir").option("--config ", "path to the config file; by default uses .playwright/cli.config.json in the project directory and ~/.playwright/cli.config.json as global config").option("--cdp ", "connect to an existing browser via CDP endpoint URL").option("--endpoint ", "attach to a running Playwright browser endpoint").option("--init-workspace", "initialize workspace").option("--init-skills ", 'install skills for the given agent type ("claude" or "agents")').action(async (sessionName, options) => { + if (options.initWorkspace) { + await initWorkspace(options.initSkills); + return; + } + setupExitWatchdog(); + const clientInfo = createClientInfo(); + const mcpConfig = await resolveCLIConfigForCLI(clientInfo.daemonProfilesDir, sessionName, options); + const mcpClientInfo = { + cwd: process.cwd(), + clientName: guessClientName() + }; + try { + const { browser, browserInfo: browserInfo2, canBind, ownership } = await createBrowserWithInfo(mcpConfig, mcpClientInfo, options); + if (canBind) + await browser.bind(sessionName, { workspaceDir: clientInfo.workspaceDir }); + const browserContext = mcpConfig.browser.isolated ? await browser.newContext(mcpConfig.browser.contextOptions) : browser.contexts()[0]; + if (!browserContext) + throw new Error("Error: unable to connect to a browser that does not have any contexts"); + const persistent = options.persistent || options.profile || mcpConfig.browser.userDataDir ? true : void 0; + const socketPath = await startCliDaemonServer(sessionName, browserContext, browserInfo2, mcpConfig, clientInfo, mcpClientInfo, { persistent, exitOnClose: true, ownership }); + console.log(`Daemon listening on ${socketPath} +`); + } catch (error) { + console.log(error); + gracefullyProcessExitDoNotHang(1); + } + }); +} +function defaultConfigFile() { + return import_path56.default.resolve(".playwright", "cli.config.json"); +} +function globalConfigFile() { + return import_path56.default.join(process.env["PWTEST_CLI_GLOBAL_CONFIG"] ?? import_os27.default.homedir(), ".playwright", "cli.config.json"); +} +async function initWorkspace(initSkills) { + const cwd = process.cwd(); + const playwrightDir = import_path56.default.join(cwd, ".playwright"); + await import_fs58.default.promises.mkdir(playwrightDir, { recursive: true }); + console.log(`\u2705 Workspace initialized at \`${cwd}\`.`); + if (initSkills) { + const skillSourceDir = libPath("tools", "cli-client", "skill"); + const target = initSkills === "agents" ? "agents" : "claude"; + const skillDestDir = import_path56.default.join(cwd, `.${target}`, "skills", "playwright-cli"); + if (!import_fs58.default.existsSync(skillSourceDir)) { + console.error("\u274C Skills source directory not found:", skillSourceDir); + process.exit(1); + } + await import_fs58.default.promises.cp(skillSourceDir, skillDestDir, { recursive: true }); + console.log(`\u2705 Skills installed to \`${import_path56.default.relative(cwd, skillDestDir)}\`.`); + } + await ensureConfiguredBrowserInstalled(); +} +async function ensureConfiguredBrowserInstalled() { + if (getAsBooleanFromENV("PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD")) + return; + if (import_fs58.default.existsSync(defaultConfigFile()) || import_fs58.default.existsSync(globalConfigFile())) { + const clientInfo = createClientInfo(); + const config = await resolveCLIConfigForCLI(clientInfo.daemonProfilesDir, "default", {}); + const browserName = config.browser.browserName; + const channel = config.browser.launchOptions.channel; + if (!channel || channel.startsWith("chromium")) + await resolveAndInstall(channel ?? browserName); + } else { + const channel = await findOrInstallDefaultBrowser(); + if (channel !== "chrome") + await createDefaultConfig(channel); + } +} +async function findOrInstallDefaultBrowser() { + const channels = ["chrome", "msedge"]; + for (const channel of channels) { + const executable = registry.findExecutable(channel); + if (!executable?.executablePath()) + continue; + await resolveAndInstall("ffmpeg"); + console.log(`\u2705 Found ${channel}, will use it as the default browser.`); + return channel; + } + await resolveAndInstall("chromium"); + return "chromium"; +} +async function resolveAndInstall(nameOrChannel) { + const executables = registry.resolveBrowsers([nameOrChannel], { shell: "no" }); + await registry.install(executables); +} +async function createDefaultConfig(channel) { + const config = { + browser: { + browserName: "chromium", + launchOptions: { channel } + } + }; + await import_fs58.default.promises.writeFile(defaultConfigFile(), JSON.stringify(config, null, 2)); + console.log(`\u2705 Created default config for ${channel} at ${import_path56.default.relative(process.cwd(), defaultConfigFile())}.`); +} +var import_fs58, import_os27, import_path56; +var init_program4 = __esm({ + "packages/playwright-core/src/tools/cli-daemon/program.ts"() { + "use strict"; + import_fs58 = __toESM(require("fs")); + import_os27 = __toESM(require("os")); + import_path56 = __toESM(require("path")); + init_env(); + init_processLauncher(); + init_package(); + init_daemon(); + init_watchdog(); + init_browserFactory(); + init_config2(); + init_registry2(); + init_registry(); + } +}); + +// packages/playwright-core/src/tools/dashboard/sessionProvider.ts +var SessionProviderEvent; +var init_sessionProvider = __esm({ + "packages/playwright-core/src/tools/dashboard/sessionProvider.ts"() { + "use strict"; + SessionProviderEvent = { + SessionsChanged: "sessionsChanged", + TabsChanged: "tabsChanged", + ContextClosed: "contextClosed", + AttachRequested: "attachRequested" + }; + } +}); + +// packages/playwright-core/src/tools/dashboard/dashboardController.ts +function browserId(browser) { + return browser._guid; +} +function pageId(p) { + return p._guid; +} +function contextId(c) { + return c._guid; +} +async function faviconUrl(page) { + const url2 = page.evaluate(async () => { + const response2 = await fetch(document.querySelector('link[rel~="icon"]')?.href ?? "/favicon.ico"); + if (!response2.ok) + return void 0; + const blob = await response2.blob(); + if (!blob.type.startsWith("image/")) + return void 0; + return await new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onloadend = () => resolve(reader.result); + reader.onerror = reject; + reader.readAsDataURL(blob); + }); + }).catch(() => void 0); + const timeout = new Promise((resolve) => setTimeout(() => resolve(void 0), 3e3)); + return await Promise.race([url2, timeout]); +} +var import_path57, import_os28, import_fs59, import_crypto29, import_child_process7, DashboardConnection, AttachedPage; +var init_dashboardController = __esm({ + "packages/playwright-core/src/tools/dashboard/dashboardController.ts"() { + "use strict"; + import_path57 = __toESM(require("path")); + import_os28 = __toESM(require("os")); + import_fs59 = __toESM(require("fs")); + import_crypto29 = __toESM(require("crypto")); + import_child_process7 = require("child_process"); + init_manualPromise(); + init_eventsHelper(); + init_registry2(); + init_sessionProvider(); + DashboardConnection = class { + constructor(provider, onclose, onconnected) { + this._pushTabsScheduled = false; + this._visible = true; + this._streams = /* @__PURE__ */ new Map(); + this._provider = provider; + this._onclose = onclose; + this._onconnected = onconnected; + this._recordingDir = import_fs59.default.mkdtempSync(import_path57.default.join(import_os28.default.tmpdir(), "playwright-recordings-")); + } + onconnect() { + this._provider.on(SessionProviderEvent.SessionsChanged, () => { + this._pushSessions(); + void this._tryRevealPending(); + }); + this._provider.on(SessionProviderEvent.TabsChanged, () => { + this._pushTabs(); + void this._tryRevealPending(); + }); + this._provider.on(SessionProviderEvent.ContextClosed, (context2) => { + if (this._attachedPage?.page.context() === context2) { + this._attachedPage.dispose(); + this._attachedPage = void 0; + } + }); + this._provider.on(SessionProviderEvent.AttachRequested, (page) => { + void this._switchAttachedTo(page); + }); + this._provider.start(); + this._onconnected?.(); + } + onclose() { + this._provider.dispose(); + this._attachedPage?.dispose(); + this._attachedPage = void 0; + this._pendingReveal?.done.reject(new Error("Dashboard connection closed")); + this._pendingReveal = void 0; + this._resolvePendingAnnotate({ type: "cancelled" }); + for (const stream3 of this._streams.values()) { + void stream3.handle.close().catch(() => { + }).then(() => import_fs59.default.promises.unlink(stream3.path)).catch(() => { + }); + } + this._streams.clear(); + void import_fs59.default.promises.rm(this._recordingDir, { recursive: true, force: true }).catch(() => { + }); + this._onclose(); + } + async dispatch(method, params2) { + const handler = this[method]; + if (typeof handler === "function") + return handler.call(this, params2); + const attached = this._attachedPage; + if (!attached) + return; + const onAtt = attached[method]; + if (typeof onAtt === "function") + return onAtt.call(attached, params2); + } + async selectTab(params2) { + const page = this._provider.findPage(params2); + if (page) + await this._switchAttachedTo(page); + this._pushTabs(); + } + async newTab(params2) { + const context2 = this._provider.findContext(params2); + if (!context2) + return; + const page = await context2.newPage(); + await this._switchAttachedTo(page); + this._pushTabs(); + } + async closeTab(params2) { + const page = this._provider.findPage(params2); + await page?.close({ reason: "Closed in Dashboard" }); + } + async closeSession(params2) { + await this._provider.closeSession(params2.browser); + } + async setVisible(params2) { + if (this._visible === params2.visible) + return; + this._visible = params2.visible; + await this._attachedPage?.setScreencastActive(params2.visible); + } + revealSession(sessionName, workspaceDir) { + const existing = this._pendingReveal; + if (existing && existing.pageId === void 0 && existing.sessionName === sessionName && existing.workspaceDir === workspaceDir) + return existing.done; + existing?.done.reject(new Error("Reveal superseded")); + const done = new ManualPromise(); + this._pendingReveal = { sessionName, workspaceDir, done }; + void this._tryRevealPending(); + return done; + } + revealPage(pageId4) { + const existing = this._pendingReveal; + if (existing && existing.pageId === pageId4) + return existing.done; + existing?.done.reject(new Error("Reveal superseded")); + const done = new ManualPromise(); + this._pendingReveal = { pageId: pageId4, done }; + void this._tryRevealPending(); + return done; + } + async _tryRevealPending() { + const pending = this._pendingReveal; + if (!pending) + return; + const allPages = this._provider.contextEntries().flatMap((e) => e.context.pages().map((page2) => ({ entry: e, page: page2 }))); + let page; + if (pending.pageId !== void 0) { + page = allPages.find(({ page: p }) => pageId(p) === pending.pageId)?.page; + } else if (pending.sessionName !== void 0) { + page = allPages.find(({ entry }) => entry.descriptor.title === pending.sessionName && (pending.workspaceDir === void 0 || entry.descriptor.workspaceDir === pending.workspaceDir))?.page; + } + if (!page) + return; + this._pendingReveal = void 0; + try { + await this._switchAttachedTo(page); + this._pushTabs(); + pending.done.resolve(); + } catch (e) { + pending.done.reject(e instanceof Error ? e : new Error(String(e))); + throw e; + } + } + async submitAnnotation(params2) { + this._resolvePendingAnnotate({ type: "submitted", frames: params2.frames, feedback: params2.feedback }); + } + async cancelAnnotation() { + this._resolvePendingAnnotate({ type: "cancelled" }); + } + async reveal(params2) { + switch (import_os28.default.platform()) { + case "darwin": + (0, import_child_process7.execFile)("open", ["-R", params2.path]); + break; + case "win32": + (0, import_child_process7.execFile)("explorer", ["/select,", params2.path]); + break; + case "linux": + (0, import_child_process7.execFile)("xdg-open", [import_path57.default.dirname(params2.path)]); + break; + } + } + async readStream(params2) { + const stream3 = this._streams.get(params2.streamId); + if (!stream3) + throw new Error(`Unknown stream: ${params2.streamId}`); + const buffer = Buffer.alloc(256 * 1024); + const { bytesRead } = await stream3.handle.read(buffer, 0, buffer.length); + if (bytesRead === 0) { + this._streams.delete(params2.streamId); + await stream3.handle.close().catch(() => { + }); + await import_fs59.default.promises.unlink(stream3.path).catch(() => { + }); + return { data: "", eof: true }; + } + return { data: buffer.subarray(0, bytesRead).toString("base64"), eof: false }; + } + visible() { + return this._visible; + } + emitSessions(sessions) { + this.sendEvent?.("sessions", { sessions, clientInfo: createClientInfo() }); + } + emitTabs(tabs) { + this.sendEvent?.("tabs", { tabs }); + } + emitFrame(data, viewportWidth, viewportHeight) { + this.sendEvent?.("frame", { data, viewportWidth, viewportHeight }); + } + emitAnnotate({ signal }) { + return new Promise((resolve) => { + if (signal.aborted) { + resolve({ type: "cancelled" }); + return; + } + this._resolvePendingAnnotate({ type: "cancelled" }); + const onAbort = () => { + if (this._pendingAnnotate !== pending) + return; + this._pendingAnnotate = void 0; + pending.dispose(); + this.sendEvent?.("cancelAnnotate", {}); + resolve({ type: "cancelled" }); + }; + const pending = { + resolve, + dispose: () => signal.removeEventListener("abort", onAbort) + }; + this._pendingAnnotate = pending; + signal.addEventListener("abort", onAbort); + this._tryFireAnnotate(); + }); + } + _tryFireAnnotate() { + if (!this._pendingAnnotate || !this._attachedPage) + return; + this.sendEvent?.("annotate", {}); + } + _resolvePendingAnnotate(result2) { + this._pendingAnnotate?.resolve(result2); + this._pendingAnnotate?.dispose(); + this._pendingAnnotate = void 0; + } + artifactsDirFor(context2) { + for (const entry of this._provider.contextEntries()) { + if (entry.context === context2) + return entry.descriptor.browser.launchOptions.artifactsDir ?? this._recordingDir; + } + return this._recordingDir; + } + _pushTabs() { + if (this._pushTabsScheduled) + return; + this._pushTabsScheduled = true; + queueMicrotask(async () => { + this._pushTabsScheduled = false; + try { + const tabs = await this._aggregateTabs(); + this.emitTabs(tabs); + } catch { + } + }); + } + _pushSessions() { + void (async () => { + try { + const sessions = await this._provider.sessions(); + this.emitSessions(sessions); + } catch { + } + })(); + } + async _aggregateTabs() { + const attachedPage = this._attachedPage?.page; + const tasks = []; + for (const { browser, context: context2 } of this._provider.contextEntries()) { + for (const page of context2.pages()) { + tasks.push((async () => ({ + browser: browserId(browser), + context: contextId(context2), + page: pageId(page), + title: await page.title().catch(() => ""), + url: page.url(), + selected: page === attachedPage, + faviconUrl: await faviconUrl(page) + }))()); + } + } + return await Promise.all(tasks); + } + async _switchAttachedTo(page) { + if (this._attachedPage?.page === page) + return; + this._attachedPage?.dispose(); + const attached = new AttachedPage(this, page); + this._attachedPage = attached; + try { + await attached.init(); + } catch (e) { + if (this._attachedPage === attached) + this._attachedPage = void 0; + attached.dispose(); + throw e; + } + if (this._attachedPage === attached) + this._tryFireAnnotate(); + } + _handleAttachedPageClose(context2) { + this._attachedPage?.dispose(); + this._attachedPage = void 0; + const next = context2.pages()[0]; + if (next) + void this._switchAttachedTo(next); + this._pushTabs(); + } + }; + AttachedPage = class { + constructor(owner, page) { + this._listeners = []; + this._screencastRunning = false; + this._recordingPath = null; + this._disposed = false; + this._owner = owner; + this._page = page; + } + get page() { + return this._page; + } + async init() { + this._listeners.push( + eventsHelper.addEventListener(this._page, "close", () => { + this._owner._handleAttachedPageClose(this._page.context()); + }), + eventsHelper.addEventListener(this._page, "framenavigated", (frame) => { + if (frame === this._page.mainFrame()) + this._owner._pushTabs(); + }) + ); + this._owner._pushTabs(); + if (this._owner.visible()) { + this._screencastRunning = true; + await this._startScreencast(this._page); + } + } + dispose() { + this._disposed = true; + this._listeners.forEach((d) => d.dispose()); + this._listeners = []; + if (this._screencastRunning) + this._page.screencast.stop().catch(() => { + }); + this._screencastRunning = false; + this._recordingPath = null; + } + async setScreencastActive(active) { + if (active && !this._screencastRunning) { + this._screencastRunning = true; + await this._startScreencast(this._page); + } else if (!active && this._screencastRunning) { + this._screencastRunning = false; + await this._page.screencast.stop().catch(() => { + }); + } + } + async navigate(params2) { + if (!params2.url) + return; + await this._page.goto(params2.url); + } + async back() { + await this._page.goBack(); + } + async forward() { + await this._page.goForward(); + } + async reload() { + await this._page.reload(); + } + async mousemove(params2) { + await this._page.mouse.move(params2.x, params2.y); + } + async mousedown(params2) { + await this._page.mouse.move(params2.x, params2.y); + await this._page.mouse.down({ button: params2.button || "left" }); + } + async mouseup(params2) { + await this._page.mouse.move(params2.x, params2.y); + await this._page.mouse.up({ button: params2.button || "left" }); + } + async wheel(params2) { + await this._page.mouse.wheel(params2.deltaX, params2.deltaY); + } + async keydown(params2) { + await this._page.keyboard.down(params2.key); + } + async keyup(params2) { + await this._page.keyboard.up(params2.key); + } + async startRecording() { + const artifactsDir = this._owner.artifactsDirFor(this._page.context()); + this._recordingPath = import_path57.default.join(artifactsDir, `recording-${Date.now()}.webm`); + if (this._screencastRunning) + await this._restartScreencast(this._page); + } + async stopRecording() { + const p = this._recordingPath; + if (!p) + throw new Error("No recording in progress"); + this._recordingPath = null; + if (this._screencastRunning) + await this._restartScreencast(this._page); + const handle = await import_fs59.default.promises.open(p, "r"); + const streamId = import_crypto29.default.randomUUID(); + this._owner._streams.set(streamId, { handle, path: p }); + return { streamId }; + } + async screenshot() { + const buffer = await this._page.screenshot({ type: "png" }); + const ariaSnapshot = await this._page.ariaSnapshot({ boxes: true, mode: "ai" }); + const vp = await this._viewportSize(); + return { + data: buffer.toString("base64"), + viewportWidth: vp.width, + viewportHeight: vp.height, + ariaSnapshot + }; + } + async _viewportSize() { + const vp = this._page.viewportSize(); + if (vp) + return vp; + return await this._page.evaluate(() => ({ width: window.innerWidth, height: window.innerHeight })); + } + async _startScreencast(page) { + await page.screencast.start({ + onFrame: ({ data, viewportWidth, viewportHeight }) => { + if (this._disposed) + return; + this._owner.emitFrame(data.toString("base64"), viewportWidth, viewportHeight); + }, + size: { width: 1280, height: 800 }, + ...this._recordingPath ? { path: this._recordingPath } : {} + }); + } + async _restartScreencast(page) { + await page.screencast.stop().catch(() => { + }); + await this._startScreencast(page); + } + }; + } +}); + +// packages/playwright-core/src/tools/dashboard/registrySessionProvider.ts +function pageId2(p) { + return p._guid; +} +function contextId2(c) { + return c._guid; +} +var import_events29, BrowserTracker, RegistrySessionProvider; +var init_registrySessionProvider = __esm({ + "packages/playwright-core/src/tools/dashboard/registrySessionProvider.ts"() { + "use strict"; + import_events29 = require("events"); + init_eventsHelper(); + init_connect2(); + init_serverRegistry(); + init_sessionProvider(); + BrowserTracker = class _BrowserTracker { + constructor(descriptor, browser, callbacks) { + this._contextListeners = /* @__PURE__ */ new Map(); + this._browserListeners = []; + this.descriptor = descriptor; + this.browser = browser; + this._callbacks = callbacks; + } + static async create(descriptor, callbacks) { + try { + const browser = await connectToBrowserAcrossVersions(descriptor); + const slot = new _BrowserTracker(descriptor, browser, callbacks); + for (const context2 of browser.contexts()) + slot._wireContext(context2); + slot._browserListeners.push(eventsHelper.addEventListener(browser, "context", (context2) => { + slot._wireContext(context2); + })); + return slot; + } catch { + return void 0; + } + } + contexts() { + return this.browser.contexts(); + } + dispose() { + this._browserListeners.forEach((d) => d.dispose()); + this._browserListeners = []; + for (const listeners of this._contextListeners.values()) + listeners.forEach((d) => d.dispose()); + this._contextListeners.clear(); + void this.browser.close().catch(() => { + }); + } + _wireContext(context2) { + if (this._contextListeners.has(context2)) + return; + const onTabsChanged = () => this._callbacks.onTabsChanged(); + const listeners = [ + eventsHelper.addEventListener(context2, "page", onTabsChanged), + eventsHelper.addEventListener(context2, "pageload", onTabsChanged), + eventsHelper.addEventListener(context2, "pageclose", onTabsChanged), + eventsHelper.addEventListener(context2, "framenavigated", (frame) => { + if (frame === frame.page().mainFrame()) + this._callbacks.onTabsChanged(); + }), + eventsHelper.addEventListener(context2, "close", () => { + const ls = this._contextListeners.get(context2); + if (ls) { + ls.forEach((d) => d.dispose()); + this._contextListeners.delete(context2); + } + this._callbacks.onContextClosed(context2); + this._callbacks.onTabsChanged(); + }) + ]; + this._contextListeners.set(context2, listeners); + this._callbacks.onTabsChanged(); + } + }; + RegistrySessionProvider = class extends import_events29.EventEmitter { + constructor() { + super(...arguments); + this._trackers = /* @__PURE__ */ new Map(); + this._pushSessionsScheduled = false; + this._scheduleSessions = () => { + if (this._pushSessionsScheduled) + return; + this._pushSessionsScheduled = true; + queueMicrotask(async () => { + this._pushSessionsScheduled = false; + try { + const sessions = await this.sessions(); + await this._reconcile(sessions); + this.emit(SessionProviderEvent.SessionsChanged); + this.emit(SessionProviderEvent.TabsChanged); + } catch { + } + }); + }; + } + start() { + this._serverRegistryDispose = serverRegistry.watch(); + serverRegistry.on("added", this._scheduleSessions); + serverRegistry.on("removed", this._scheduleSessions); + serverRegistry.on("changed", this._scheduleSessions); + this._scheduleSessions(); + } + dispose() { + serverRegistry.off("added", this._scheduleSessions); + serverRegistry.off("removed", this._scheduleSessions); + serverRegistry.off("changed", this._scheduleSessions); + this._serverRegistryDispose?.(); + this._serverRegistryDispose = void 0; + for (const tracker of this._trackers.values()) + tracker.dispose(); + this._trackers.clear(); + this.removeAllListeners(); + } + async sessions() { + const byWs = await serverRegistry.list(); + const sessions = []; + for (const list of byWs.values()) { + for (const status of list) { + if (status.title.startsWith("--playwright-internal")) + continue; + sessions.push(status); + } + } + return sessions; + } + contextEntries() { + const entries = []; + for (const tracker of this._trackers.values()) { + for (const context2 of tracker.contexts()) + entries.push({ browser: tracker.browser, context: context2, descriptor: tracker.descriptor }); + } + return entries; + } + findContext(params2) { + const tracker = this._trackers.get(params2.browser); + if (!tracker) + return void 0; + return tracker.contexts().find((c) => contextId2(c) === params2.context); + } + findPage(params2) { + const context2 = this.findContext(params2); + return context2?.pages().find((p) => pageId2(p) === params2.page); + } + async closeSession(browserId2) { + const descriptor = serverRegistry.readDescriptor(browserId2); + const browser = await connectToBrowserAcrossVersions(descriptor); + try { + await Promise.all(browser.contexts().map((context2) => context2.close())); + await browser.close(); + } catch { + } + } + async _reconcile(sessions) { + const connectable = /* @__PURE__ */ new Map(); + for (const status of sessions) + connectable.set(status.browser.guid, status); + for (const [guid, tracker] of this._trackers) { + if (connectable.has(guid)) + continue; + tracker.dispose(); + this._trackers.delete(guid); + } + for (const [guid, status] of connectable) { + if (this._trackers.has(guid)) + continue; + const tracker = await BrowserTracker.create(status, { + onTabsChanged: () => this.emit(SessionProviderEvent.TabsChanged), + onContextClosed: (context2) => this.emit(SessionProviderEvent.ContextClosed, context2) + }); + if (!tracker) + continue; + if (this._trackers.has(guid)) { + tracker.dispose(); + continue; + } + this._trackers.set(guid, tracker); + } + } + }; + } +}); + +// packages/playwright-core/src/tools/dashboard/identitySessionProvider.ts +function synthesiseDescriptor(browser) { + const browserName = browser.browserType().name(); + const browserInfo2 = { + // eslint-disable-next-line no-restricted-syntax -- _guid is very conservative. + guid: browser._guid, + browserName, + launchOptions: {} + }; + return { + title: "Playwright", + playwrightVersion: packageJSON.version, + playwrightLib: packageRoot, + browser: browserInfo2 + }; +} +function pageId3(p) { + return p._guid; +} +function contextId3(c) { + return c._guid; +} +var import_events30, IdentitySessionProvider; +var init_identitySessionProvider = __esm({ + "packages/playwright-core/src/tools/dashboard/identitySessionProvider.ts"() { + "use strict"; + import_events30 = require("events"); + init_eventsHelper(); + init_package(); + init_sessionProvider(); + IdentitySessionProvider = class extends import_events30.EventEmitter { + constructor(context2) { + super(); + this._listeners = []; + this._closed = false; + const browser = context2.browser(); + if (!browser) + throw new Error("SingleContextDashboardProvider requires a context with an attached browser"); + this._context = context2; + this._browser = browser; + this._descriptor = synthesiseDescriptor(browser); + } + start() { + const emitTabsChanged = () => this.emit(SessionProviderEvent.TabsChanged); + this._listeners.push( + eventsHelper.addEventListener(this._context, "page", emitTabsChanged), + eventsHelper.addEventListener(this._context, "pageload", emitTabsChanged), + eventsHelper.addEventListener(this._context, "pageclose", emitTabsChanged), + eventsHelper.addEventListener(this._context, "framenavigated", (frame) => { + if (frame === frame.page().mainFrame()) + this.emit(SessionProviderEvent.TabsChanged); + }), + eventsHelper.addEventListener(this._context, "close", () => { + this._closed = true; + this.emit(SessionProviderEvent.ContextClosed, this._context); + this.emit(SessionProviderEvent.TabsChanged); + }) + ); + this.emit(SessionProviderEvent.SessionsChanged); + this.emit(SessionProviderEvent.TabsChanged); + const firstPage = this._context.pages()[0]; + if (firstPage) + this.emit(SessionProviderEvent.AttachRequested, firstPage); + } + dispose() { + this._listeners.forEach((d) => d.dispose()); + this._listeners = []; + this.removeAllListeners(); + } + async sessions() { + return this._closed ? [] : [this._descriptor]; + } + contextEntries() { + if (this._closed) + return []; + return [{ browser: this._browser, context: this._context, descriptor: this._descriptor }]; + } + findContext(params2) { + if (this._closed) + return void 0; + if (params2.browser !== this._descriptor.browser.guid) + return void 0; + if (contextId3(this._context) !== params2.context) + return void 0; + return this._context; + } + findPage(params2) { + const context2 = this.findContext(params2); + return context2?.pages().find((p) => pageId3(p) === params2.page); + } + async closeSession() { + } + }; + } +}); + +// packages/playwright-core/src/tools/dashboard/dashboardApp.ts +async function startDashboardServer(provider, options) { + const dashboardDir = libPath("vite", "dashboard"); + const httpServer = new HttpServer(dashboardDir); + const connections = /* @__PURE__ */ new Set(); + let connectionLanded = new ManualPromise(); + httpServer.createWebSocket(() => { + let connection; + connection = new DashboardConnection(provider, () => { + connections.delete(connection); + if (connections.size === 0) + connectionLanded = new ManualPromise(); + }, () => { + connectionLanded.resolve(); + }); + connections.add(connection); + return connection; + }); + const wsGuid = httpServer.wsGuid(); + httpServer.routePath("/", (_, response2) => { + response2.statusCode = 302; + response2.setHeader("Location", `/index.html?ws=${wsGuid}`); + response2.end(); + return true; + }); + if (false) + await attachDashboardDevServer(httpServer); + else + attachDashboardStaticServer(httpServer, dashboardDir); + await httpServer.start({ port: options.port, host: options.host }); + const reveal = async (next) => { + await connectionLanded; + await Promise.all([...connections].map(async (c) => { + if (next.pageId) + await c.revealPage(next.pageId); + else if (next.sessionName) + await c.revealSession(next.sessionName, next.workspaceDir); + })); + }; + const triggerAnnotate = async (cancellation) => { + await connectionLanded; + if (cancellation.aborted || connections.size === 0) + return { type: "cancelled" }; + return await Promise.race([...connections].map((c) => c.emitAnnotate({ signal: cancellation }))); + }; + const close3 = async () => { + for (const c of connections) + c.close?.(); + await httpServer.stop(); + }; + return { url: httpServer.urlPrefix("human-readable"), reveal, triggerAnnotate, close: close3 }; +} +function attachDashboardStaticServer(httpServer, dashboardDir) { + httpServer.routePrefix("/", (request2, response2) => { + const pathname = new URL(request2.url, `http://${request2.headers.host}`).pathname; + const filePath = pathname === "/" ? "index.html" : pathname.substring(1); + const resolved = import_path58.default.join(dashboardDir, filePath); + return httpServer.serveFile(request2, response2, resolved); + }); +} +async function innerOpenDashboardApp(options) { + const server = await startDashboardServer(new RegistrySessionProvider(), options); + void server.reveal(options).catch(() => { + }); + const { page } = await launchApp2("dashboard", { onClose: () => gracefullyProcessExitDoNotHang(0) }); + await page.goto(server.url); + return { page, server }; +} +async function launchApp2(appName, options) { + const channel = findChromiumChannelBestEffort("javascript"); + const context2 = await playwright.chromium.launchPersistentContext("", { + ignoreDefaultArgs: ["--enable-automation"], + channel, + headless: !!process.env.PWTEST_DASHBOARD_APP_BIND_TITLE, + args: [ + "--app=data:text/html,", + "--test-type=", + `--window-size=1280,800`, + `--window-position=100,100` + ], + viewport: null + }); + if (process.env.PWTEST_DASHBOARD_APP_BIND_TITLE) + await context2.browser()?.bind(process.env.PWTEST_DASHBOARD_APP_BIND_TITLE, { workspaceDir: process.cwd() }); + const [page] = context2.pages(); + if (process.platform === "darwin") { + context2.on("page", async (newPage) => { + if (newPage.mainFrame().url() === "chrome://new-tab-page/") { + await page.bringToFront(); + await newPage.close(); + } + }); + } + page.on("close", () => options?.onClose?.()); + const image = await import_fs60.default.promises.readFile(libPath("tools", "dashboard", "appIcon.png")); + await page._setDockTile?.(image); + await syncLocalStorageWithSettings2(page, appName); + return { context: context2, page }; +} +async function syncLocalStorageWithSettings2(page, appName) { + const settingsFile = process.env.PWTEST_DASHBOARD_SETTINGS_FILE ?? import_path58.default.join(registryDirectory, ".settings", `${appName}.json`); + await page.exposeBinding("_saveSerializedSettings", (_, settings2) => { + import_fs60.default.mkdirSync(import_path58.default.dirname(settingsFile), { recursive: true }); + import_fs60.default.writeFileSync(settingsFile, settings2); + }); + const settings = await import_fs60.default.promises.readFile(settingsFile, "utf-8").catch(() => "{}"); + await page.addInitScript( + `(${String((settings2) => { + if (location && location.protocol === "data:") + return; + if (window.top !== window) + return; + Object.entries(settings2).map(([k, v]) => localStorage[k] = v); + window.saveSettings = () => { + window._saveSerializedSettings(JSON.stringify({ ...localStorage })); + }; + })})(${settings}); + ` + ); +} +function dashboardSocketPath() { + return makeSocketPath("dashboard", "app"); +} +function parseOpenArgs() { + const args = minimist(process.argv.slice(2), { string: ["sessionName", "workspaceDir", "host", "pageId"], boolean: ["annotate", "kill"] }); + const portStr = args.port; + return { + sessionName: args.sessionName, + workspaceDir: args.workspaceDir, + pageId: args.pageId, + port: portStr !== void 0 ? Number(portStr) : void 0, + host: args.host, + annotate: !!args.annotate, + kill: !!args.kill + }; +} +async function acquireSingleton(options) { + const socketPath = dashboardSocketPath(); + if (process.platform !== "win32") + await import_fs60.default.promises.mkdir(import_path58.default.dirname(socketPath), { recursive: true }); + return await new Promise((resolve, reject) => { + const server = import_net11.default.createServer(); + server.listen(socketPath, () => resolve({ role: "winner", server })); + server.on("error", (err) => { + if (err.code !== "EADDRINUSE" && err.code !== "EEXIST") + return reject(err); + let ackBuffer = ""; + const client = import_net11.default.connect(socketPath, () => { + client.write(JSON.stringify(options) + "\n"); + }); + client.on("data", (chunk) => { + ackBuffer += chunk.toString(); + }); + client.on("end", () => { + try { + const { pid } = JSON.parse(ackBuffer.trim()); + resolve({ role: "loser", daemonPid: pid }); + } catch (e) { + reject(e); + } + }); + client.on("error", () => { + if (process.platform !== "win32") + import_fs60.default.unlinkSync(socketPath); + server.listen(socketPath, () => resolve({ role: "winner", server })); + }); + }); + }); +} +async function openDashboardApp() { + const options = parseOpenArgs(); + if (options.kill) { + await runKillClient(); + return; + } + if (options.annotate) { + await runAnnotateClient(options); + return; + } + process.on("unhandledRejection", (error) => { + console.error("Unhandled promise rejection:", error); + }); + if (options.port !== void 0) { + const server2 = await startDashboardServer(new RegistrySessionProvider(), options); + void server2.reveal(options).catch(() => { + }); + console.log(`Listening on ${server2.url}`); + await new Promise((f) => process.stdout.write("", f)); + selfDestructOnParentGone(); + return; + } + const stopSelfDestruct = selfDestructOnParentGone(); + const acquired = await acquireSingleton(options); + if (acquired.role === "loser") { + stopSelfDestruct(); + console.log(`Dashboard is running pid=${acquired.daemonPid}`); + await new Promise((f) => process.stdout.write("", f)); + return; + } + const { server } = acquired; + process.on("exit", () => server.close()); + try { + await startApp(server, options); + stopSelfDestruct(); + console.log(`Dashboard is running pid=${process.pid}`); + } catch (error) { + console.log(error); + gracefullyProcessExitDoNotHang(1); + } +} +async function startApp(server, options) { + const statePromise = innerOpenDashboardApp(options); + server.on("connection", (socket) => { + let buffer = ""; + socket.on("data", async (data) => { + buffer += data.toString(); + const newlineIndex = buffer.indexOf("\n"); + if (newlineIndex === -1) + return; + const line = buffer.slice(0, newlineIndex); + buffer = buffer.slice(newlineIndex + 1); + let parsed; + try { + parsed = JSON.parse(line); + } catch { + } + if (!parsed) { + socket.end(); + return; + } + const { page, server: dashboard } = await statePromise; + if (parsed.annotate) { + const cancellation = new AbortController(); + socket.on("close", () => cancellation.abort()); + socket.on("error", () => cancellation.abort()); + try { + await page?.bringToFront(); + await dashboard.reveal(parsed); + const result2 = await dashboard.triggerAnnotate(cancellation.signal); + socket.end(JSON.stringify(result2)); + } catch (e) { + socket.end(e); + } + } else if (parsed.kill) { + await dashboard.close().catch(() => { + }); + gracefullyProcessExitDoNotHang(0, () => new Promise((r) => socket.end(r))); + } else { + try { + await page?.bringToFront(); + await dashboard.reveal(parsed); + socket.end(JSON.stringify({ pid: process.pid }) + "\n"); + } catch (e) { + socket.end(e); + } + } + }); + }); + await statePromise; +} +async function openDashboardForContext(context2) { + const server = await startDashboardServer(new IdentitySessionProvider(context2), {}); + let closed = false; + const close3 = async () => { + if (closed) + return; + closed = true; + await server.close(); + }; + const { page } = await launchApp2("dashboard", { onClose: () => { + void close3(); + } }); + context2.on("close", () => { + void close3(); + }); + await page.goto(server.url); +} +async function runKillClient() { + const socketPath = dashboardSocketPath(); + await new Promise((resolve) => { + const client = import_net11.default.connect(socketPath); + client.once("connect", () => { + client.write(JSON.stringify({ kill: true }) + "\n"); + }); + client.once("end", () => resolve()); + client.once("error", () => resolve()); + }); +} +async function runAnnotateClient(options) { + selfDestructOnParentGone(); + const socketPath = dashboardSocketPath(); + const tryConnect = () => new Promise((resolve) => { + const s = import_net11.default.connect(socketPath); + const onError = () => { + s.destroy(); + resolve(void 0); + }; + s.once("connect", () => { + s.off("error", onError); + resolve(s); + }); + s.once("error", onError); + }); + const deadline = Date.now() + 15e3; + let socket; + while (Date.now() < deadline) { + socket = await tryConnect(); + if (socket) + break; + await new Promise((r) => setTimeout(r, 200)); + } + if (!socket) { + console.error("Dashboard did not start in time."); + gracefullyProcessExitDoNotHang(1); + return; + } + socket.write(JSON.stringify(options) + "\n"); + const chunks = []; + await new Promise((resolve, reject) => { + socket.on("data", (chunk) => chunks.push(chunk)); + socket.on("end", () => resolve()); + socket.on("error", reject); + }); + socket.destroy(); + const text2 = Buffer.concat(chunks).toString(); + if (!text2) + return; + console.log(text2); +} +function selfDestructOnParentGone() { + const onClose = () => gracefullyProcessExitDoNotHang(0); + process.stdin.on("close", onClose); + return () => process.stdin.off("close", onClose); +} +var import_fs60, import_path58, import_net11; +var init_dashboardApp = __esm({ + "packages/playwright-core/src/tools/dashboard/dashboardApp.ts"() { + "use strict"; + import_fs60 = __toESM(require("fs")); + import_path58 = __toESM(require("path")); + import_net11 = __toESM(require("net")); + init_httpServer(); + init_fileUtils(); + init_processLauncher(); + init_manualPromise(); + init_package(); + init_inprocess(); + init_registry(); + init_minimist(); + init_dashboardController(); + init_registrySessionProvider(); + init_identitySessionProvider(); + } +}); + +// packages/playwright-core/src/tools/index.ts +var tools_exports = {}; +__export(tools_exports, { + BrowserBackend: () => BrowserBackend, + DirTraceLoaderBackend: () => DirTraceLoaderBackend, + Tab: () => Tab, + browserTools: () => browserTools, + cliProgram: () => program2, + compareSemver: () => compareSemver, + createConnection: () => createConnection, + decorateCliDaemonProgram: () => decorateProgram3, + decorateMCPCommand: () => decorateMCPCommand, + extractTrace: () => extractTrace, + filteredTools: () => filteredTools, + generateHelp: () => generateHelp, + generateHelpJSON: () => generateHelpJSON, + isProfileLocked: () => isProfileLocked, + isSystemDirectory: () => isSystemDirectory, + openDashboardApp: () => openDashboardApp, + openDashboardForContext: () => openDashboardForContext, + outputDir: () => outputDir, + parseResponse: () => parseResponse, + resolveCLIConfigForCLI: () => resolveCLIConfigForCLI, + resolveCLIConfigForMCP: () => resolveCLIConfigForMCP, + setupExitWatchdog: () => setupExitWatchdog, + start: () => start2 +}); +var init_tools2 = __esm({ + "packages/playwright-core/src/tools/index.ts"() { + "use strict"; + init_watchdog(); + init_browserBackend(); + init_response(); + init_tab(); + init_tools(); + init_server2(); + init_mcp(); + init_config2(); + init_context(); + init_fileUtils(); + init_browserFactory(); + init_socketConnection(); + init_traceParser(); + init_program3(); + init_program(); + init_helpGenerator(); + init_program4(); + init_dashboardApp(); + } +}); + +// packages/playwright-core/src/coreBundle.ts +var coreBundle_exports = {}; +__export(coreBundle_exports, { + clientEventEmitter: () => EventEmitter3, + getPlaywrightVersion: () => getPlaywrightVersion, + getUserAgent: () => getUserAgent, + inprocess: () => inprocess_exports, + iso: () => isomorphic_exports, + libCli: () => program_exports, + libCliTestStub: () => programWithTestStub_exports, + oop: () => outofprocess_exports, + registry: () => registry_exports, + remote: () => playwrightServer_exports, + server: () => server_exports, + tools: () => tools_exports, + utils: () => utils_exports +}); +module.exports = __toCommonJS(coreBundle_exports); +var init_coreBundle = __esm({ + "packages/playwright-core/src/coreBundle.ts"() { + init_isomorphic(); + init_utils(); + init_eventEmitter(); + init_program2(); + init_programWithTestStub(); + init_inprocess(); + init_outofprocess(); + init_playwrightServer(); + init_registry(); + init_server(); + init_tools2(); + init_userAgent(); + } +}); +init_coreBundle(); +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + clientEventEmitter, + getPlaywrightVersion, + getUserAgent, + inprocess, + iso, + libCli, + libCliTestStub, + oop, + registry, + remote, + server, + tools, + utils +}); +/** + * @license + * Copyright 2024 Google Inc. + * Modifications copyright (c) Microsoft Corporation. + * SPDX-License-Identifier: Apache-2.0 + */ +/** + * @license + * Copyright 2023 Google Inc. + * SPDX-License-Identifier: Apache-2.0 + */ diff --git a/simulator/e2e/node_modules/playwright-core/lib/entry/cliDaemon.js b/simulator/e2e/node_modules/playwright-core/lib/entry/cliDaemon.js new file mode 100644 index 0000000..480f3b9 --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/lib/entry/cliDaemon.js @@ -0,0 +1,5 @@ +"use strict"; +var import_coreBundle = require("../coreBundle"); +const { program } = require("../utilsBundle"); +import_coreBundle.tools.decorateCliDaemonProgram(program); +void program.parseAsync(); diff --git a/simulator/e2e/node_modules/playwright-core/lib/entry/dashboardApp.js b/simulator/e2e/node_modules/playwright-core/lib/entry/dashboardApp.js new file mode 100644 index 0000000..5b7e30a --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/lib/entry/dashboardApp.js @@ -0,0 +1,3 @@ +"use strict"; +var import_coreBundle = require("../coreBundle"); +import_coreBundle.tools.openDashboardApp(); diff --git a/simulator/e2e/node_modules/playwright-core/lib/entry/mcp.js b/simulator/e2e/node_modules/playwright-core/lib/entry/mcp.js new file mode 100644 index 0000000..2b57633 --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/lib/entry/mcp.js @@ -0,0 +1,10 @@ +"use strict"; +var import_coreBundle = require("../coreBundle"); +var import_package = require("../package"); +const { program } = require("../utilsBundle"); +const p = program.version("Version " + import_package.packageJSON.version).name("Playwright MCP"); +import_coreBundle.tools.decorateMCPCommand(p); +program.parseAsync(process.argv).catch((e) => { + console.error(e.message); + import_coreBundle.utils.gracefullyProcessExitDoNotHang(1); +}); diff --git a/simulator/e2e/node_modules/playwright-core/lib/entry/oopBrowserDownload.js b/simulator/e2e/node_modules/playwright-core/lib/entry/oopBrowserDownload.js new file mode 100644 index 0000000..62dab85 --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/lib/entry/oopBrowserDownload.js @@ -0,0 +1,3 @@ +"use strict"; +var import_coreBundle = require("../coreBundle"); +import_coreBundle.registry.runOopDownloadBrowserMain(); diff --git a/simulator/e2e/node_modules/playwright-core/lib/package.js b/simulator/e2e/node_modules/playwright-core/lib/package.js new file mode 100644 index 0000000..607f39e --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/lib/package.js @@ -0,0 +1,50 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var package_exports = {}; +__export(package_exports, { + binPath: () => binPath, + libPath: () => libPath, + packageJSON: () => packageJSON, + packageRoot: () => packageRoot +}); +module.exports = __toCommonJS(package_exports); +var import_path = __toESM(require("path")); +const packageRoot = import_path.default.join(__dirname, ".."); +const packageJSON = require(import_path.default.join(packageRoot, "package.json")); +const binPath = import_path.default.join(packageRoot, "bin"); +function libPath(...parts) { + return import_path.default.join(packageRoot, "lib", ...parts); +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + binPath, + libPath, + packageJSON, + packageRoot +}); diff --git a/simulator/e2e/node_modules/playwright-core/lib/server/chromium/appIcon.png b/simulator/e2e/node_modules/playwright-core/lib/server/chromium/appIcon.png new file mode 100644 index 0000000..1c89846 Binary files /dev/null and b/simulator/e2e/node_modules/playwright-core/lib/server/chromium/appIcon.png differ diff --git a/simulator/e2e/node_modules/playwright-core/lib/server/deviceDescriptorsSource.json b/simulator/e2e/node_modules/playwright-core/lib/server/deviceDescriptorsSource.json new file mode 100644 index 0000000..9b93a48 --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/lib/server/deviceDescriptorsSource.json @@ -0,0 +1,2739 @@ +{ + "Blackberry PlayBook": { + "userAgent": "Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML like Gecko) Version/26.5 Safari/536.2+", + "viewport": { + "width": 600, + "height": 1024 + }, + "deviceScaleFactor": 1, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "Blackberry PlayBook landscape": { + "userAgent": "Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML like Gecko) Version/26.5 Safari/536.2+", + "viewport": { + "width": 1024, + "height": 600 + }, + "deviceScaleFactor": 1, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "BlackBerry Z30": { + "userAgent": "Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) Version/26.5 Mobile Safari/537.10+", + "viewport": { + "width": 360, + "height": 640 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "BlackBerry Z30 landscape": { + "userAgent": "Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) Version/26.5 Mobile Safari/537.10+", + "viewport": { + "width": 640, + "height": 360 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "Galaxy Note 3": { + "userAgent": "Mozilla/5.0 (Linux; U; Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/26.5 Mobile Safari/534.30", + "viewport": { + "width": 360, + "height": 640 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "Galaxy Note 3 landscape": { + "userAgent": "Mozilla/5.0 (Linux; U; Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/26.5 Mobile Safari/534.30", + "viewport": { + "width": 640, + "height": 360 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "Galaxy Note II": { + "userAgent": "Mozilla/5.0 (Linux; U; Android 4.1; en-us; GT-N7100 Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/26.5 Mobile Safari/534.30", + "viewport": { + "width": 360, + "height": 640 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "Galaxy Note II landscape": { + "userAgent": "Mozilla/5.0 (Linux; U; Android 4.1; en-us; GT-N7100 Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/26.5 Mobile Safari/534.30", + "viewport": { + "width": 640, + "height": 360 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "Galaxy S III": { + "userAgent": "Mozilla/5.0 (Linux; U; Android 4.0; en-us; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/26.5 Mobile Safari/534.30", + "viewport": { + "width": 360, + "height": 640 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "Galaxy S III landscape": { + "userAgent": "Mozilla/5.0 (Linux; U; Android 4.0; en-us; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/26.5 Mobile Safari/534.30", + "viewport": { + "width": 640, + "height": 360 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "Galaxy S5": { + "userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 360, + "height": 640 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy S5 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 640, + "height": 360 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy S8": { + "userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 360, + "height": 740 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy S8 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 740, + "height": 360 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy S9+": { + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 320, + "height": 658 + }, + "deviceScaleFactor": 4.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy S9+ landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 658, + "height": 320 + }, + "deviceScaleFactor": 4.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy S24": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; SM-S921U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 360, + "height": 780 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy S24 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; SM-S921U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 780, + "height": 360 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy A55": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; SM-A556B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 480, + "height": 1040 + }, + "deviceScaleFactor": 2.25, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy A55 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; SM-A556B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 1040, + "height": 480 + }, + "deviceScaleFactor": 2.25, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Tab S4": { + "userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36", + "viewport": { + "width": 712, + "height": 1138 + }, + "deviceScaleFactor": 2.25, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Tab S4 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36", + "viewport": { + "width": 1138, + "height": 712 + }, + "deviceScaleFactor": 2.25, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Tab S9": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; SM-X710) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36", + "viewport": { + "width": 640, + "height": 1024 + }, + "deviceScaleFactor": 2.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Tab S9 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; SM-X710) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36", + "viewport": { + "width": 1024, + "height": 640 + }, + "deviceScaleFactor": 2.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Z Fold 6": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-F956U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 928, + "height": 1080 + }, + "viewport": { + "width": 928, + "height": 1004 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Z Fold 6 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-F956U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 1080, + "height": 928 + }, + "viewport": { + "width": 1028, + "height": 876 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Z Fold 6 Cover": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-F956U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 484, + "height": 1188 + }, + "viewport": { + "width": 484, + "height": 1112 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Z Fold 6 Cover landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-F956U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 1188, + "height": 484 + }, + "viewport": { + "width": 1136, + "height": 432 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Z Fold 7": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-F966U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 984, + "height": 1092 + }, + "viewport": { + "width": 984, + "height": 1016 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Z Fold 7 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-F966U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 1092, + "height": 984 + }, + "viewport": { + "width": 1040, + "height": 932 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Z Fold 7 Cover": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-F966U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 360, + "height": 840 + }, + "viewport": { + "width": 360, + "height": 764 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Z Fold 7 Cover landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-F966U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 840, + "height": 360 + }, + "viewport": { + "width": 788, + "height": 308 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Z Flip 6": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-F741U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 360, + "height": 880 + }, + "viewport": { + "width": 360, + "height": 804 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Z Flip 6 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-F741U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 880, + "height": 360 + }, + "viewport": { + "width": 828, + "height": 308 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Z Flip 6 Cover": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-F741U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 360, + "height": 374 + }, + "viewport": { + "width": 360, + "height": 298 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Z Flip 6 Cover landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-F741U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 374, + "height": 360 + }, + "viewport": { + "width": 322, + "height": 308 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Z Flip 7": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-F761U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 360, + "height": 840 + }, + "viewport": { + "width": 360, + "height": 764 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Z Flip 7 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-F761U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 840, + "height": 360 + }, + "viewport": { + "width": 788, + "height": 308 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Z Flip 7 Cover": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-F761U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 474, + "height": 524 + }, + "viewport": { + "width": 474, + "height": 448 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Galaxy Z Flip 7 Cover landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-F761U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 524, + "height": 474 + }, + "viewport": { + "width": 472, + "height": 422 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "iPad (gen 5)": { + "userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "viewport": { + "width": 768, + "height": 1024 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPad (gen 5) landscape": { + "userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "viewport": { + "width": 1024, + "height": 768 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPad (gen 6)": { + "userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "viewport": { + "width": 768, + "height": 1024 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPad (gen 6) landscape": { + "userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "viewport": { + "width": 1024, + "height": 768 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPad (gen 7)": { + "userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "viewport": { + "width": 810, + "height": 1080 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPad (gen 7) landscape": { + "userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "viewport": { + "width": 1080, + "height": 810 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPad (gen 11)": { + "userAgent": "Mozilla/5.0 (iPad; CPU OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/19E241 Safari/604.1", + "viewport": { + "width": 656, + "height": 944 + }, + "deviceScaleFactor": 2.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPad (gen 11) landscape": { + "userAgent": "Mozilla/5.0 (iPad; CPU OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/19E241 Safari/604.1", + "viewport": { + "width": 944, + "height": 656 + }, + "deviceScaleFactor": 2.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPad Mini": { + "userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "viewport": { + "width": 768, + "height": 1024 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPad Mini landscape": { + "userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "viewport": { + "width": 1024, + "height": 768 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPad Pro 11": { + "userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "viewport": { + "width": 834, + "height": 1194 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPad Pro 11 landscape": { + "userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "viewport": { + "width": 1194, + "height": 834 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 6": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + "viewport": { + "width": 375, + "height": 667 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 6 landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + "viewport": { + "width": 667, + "height": 375 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 6 Plus": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + "viewport": { + "width": 414, + "height": 736 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 6 Plus landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + "viewport": { + "width": 736, + "height": 414 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 7": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + "viewport": { + "width": 375, + "height": 667 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 7 landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + "viewport": { + "width": 667, + "height": 375 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 7 Plus": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + "viewport": { + "width": 414, + "height": 736 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 7 Plus landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + "viewport": { + "width": 736, + "height": 414 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 8": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + "viewport": { + "width": 375, + "height": 667 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 8 landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + "viewport": { + "width": 667, + "height": 375 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 8 Plus": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + "viewport": { + "width": 414, + "height": 736 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 8 Plus landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + "viewport": { + "width": 736, + "height": 414 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone SE": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/26.5 Mobile/14E304 Safari/602.1", + "viewport": { + "width": 320, + "height": 568 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone SE landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/26.5 Mobile/14E304 Safari/602.1", + "viewport": { + "width": 568, + "height": 320 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone SE (3rd gen)": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/26.5 Mobile/19E241 Safari/602.1", + "viewport": { + "width": 375, + "height": 667 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone SE (3rd gen) landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/26.5 Mobile/19E241 Safari/602.1", + "viewport": { + "width": 667, + "height": 375 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone X": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + "viewport": { + "width": 375, + "height": 812 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone X landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.5 Mobile/15A372 Safari/604.1", + "viewport": { + "width": 812, + "height": 375 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone XR": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "viewport": { + "width": 414, + "height": 896 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone XR landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "viewport": { + "width": 896, + "height": 414 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 11": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 414, + "height": 896 + }, + "viewport": { + "width": 414, + "height": 715 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 11 landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 414, + "height": 896 + }, + "viewport": { + "width": 800, + "height": 364 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 11 Pro": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 375, + "height": 812 + }, + "viewport": { + "width": 375, + "height": 635 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 11 Pro landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 375, + "height": 812 + }, + "viewport": { + "width": 724, + "height": 325 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 11 Pro Max": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 414, + "height": 896 + }, + "viewport": { + "width": 414, + "height": 715 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 11 Pro Max landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 414, + "height": 896 + }, + "viewport": { + "width": 808, + "height": 364 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 12": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 390, + "height": 844 + }, + "viewport": { + "width": 390, + "height": 664 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 12 landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 390, + "height": 844 + }, + "viewport": { + "width": 750, + "height": 340 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 12 Pro": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 390, + "height": 844 + }, + "viewport": { + "width": 390, + "height": 664 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 12 Pro landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 390, + "height": 844 + }, + "viewport": { + "width": 750, + "height": 340 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 12 Pro Max": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 428, + "height": 926 + }, + "viewport": { + "width": 428, + "height": 746 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 12 Pro Max landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 428, + "height": 926 + }, + "viewport": { + "width": 832, + "height": 378 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 12 Mini": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 375, + "height": 812 + }, + "viewport": { + "width": 375, + "height": 629 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 12 Mini landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 375, + "height": 812 + }, + "viewport": { + "width": 712, + "height": 325 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 13": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 390, + "height": 844 + }, + "viewport": { + "width": 390, + "height": 664 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 13 landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 390, + "height": 844 + }, + "viewport": { + "width": 750, + "height": 342 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 13 Pro": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 390, + "height": 844 + }, + "viewport": { + "width": 390, + "height": 664 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 13 Pro landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 390, + "height": 844 + }, + "viewport": { + "width": 750, + "height": 342 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 13 Pro Max": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 428, + "height": 926 + }, + "viewport": { + "width": 428, + "height": 746 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 13 Pro Max landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 428, + "height": 926 + }, + "viewport": { + "width": 832, + "height": 380 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 13 Mini": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 375, + "height": 812 + }, + "viewport": { + "width": 375, + "height": 629 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 13 Mini landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 375, + "height": 812 + }, + "viewport": { + "width": 712, + "height": 327 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 14": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 390, + "height": 844 + }, + "viewport": { + "width": 390, + "height": 664 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 14 landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 390, + "height": 844 + }, + "viewport": { + "width": 750, + "height": 340 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 14 Plus": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 428, + "height": 926 + }, + "viewport": { + "width": 428, + "height": 746 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 14 Plus landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 428, + "height": 926 + }, + "viewport": { + "width": 832, + "height": 378 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 14 Pro": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 393, + "height": 852 + }, + "viewport": { + "width": 393, + "height": 660 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 14 Pro landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 393, + "height": 852 + }, + "viewport": { + "width": 734, + "height": 343 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 14 Pro Max": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 430, + "height": 932 + }, + "viewport": { + "width": 430, + "height": 740 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 14 Pro Max landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 430, + "height": 932 + }, + "viewport": { + "width": 814, + "height": 380 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 15": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 393, + "height": 852 + }, + "viewport": { + "width": 393, + "height": 659 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 15 landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 393, + "height": 852 + }, + "viewport": { + "width": 734, + "height": 343 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 15 Plus": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 430, + "height": 932 + }, + "viewport": { + "width": 430, + "height": 739 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 15 Plus landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 430, + "height": 932 + }, + "viewport": { + "width": 814, + "height": 380 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 15 Pro": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 393, + "height": 852 + }, + "viewport": { + "width": 393, + "height": 659 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 15 Pro landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 393, + "height": 852 + }, + "viewport": { + "width": 734, + "height": 343 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 15 Pro Max": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 430, + "height": 932 + }, + "viewport": { + "width": 430, + "height": 739 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 15 Pro Max landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 430, + "height": 932 + }, + "viewport": { + "width": 814, + "height": 380 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 16": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 393, + "height": 852 + }, + "viewport": { + "width": 393, + "height": 659 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 16 landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 393, + "height": 852 + }, + "viewport": { + "width": 734, + "height": 343 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 16 Plus": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 430, + "height": 932 + }, + "viewport": { + "width": 430, + "height": 739 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 16 Plus landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 430, + "height": 932 + }, + "viewport": { + "width": 814, + "height": 380 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 16 Pro": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 402, + "height": 874 + }, + "viewport": { + "width": 402, + "height": 681 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 16 Pro landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 402, + "height": 874 + }, + "viewport": { + "width": 756, + "height": 352 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 16 Pro Max": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 440, + "height": 956 + }, + "viewport": { + "width": 440, + "height": 763 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 16 Pro Max landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 440, + "height": 956 + }, + "viewport": { + "width": 838, + "height": 390 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 16e": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 390, + "height": 844 + }, + "viewport": { + "width": 390, + "height": 651 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 16e landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 390, + "height": 844 + }, + "viewport": { + "width": 726, + "height": 340 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 17": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 402, + "height": 874 + }, + "viewport": { + "width": 402, + "height": 681 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 17 landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 402, + "height": 874 + }, + "viewport": { + "width": 756, + "height": 352 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone Air": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 420, + "height": 912 + }, + "viewport": { + "width": 420, + "height": 719 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone Air landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 420, + "height": 912 + }, + "viewport": { + "width": 794, + "height": 370 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 17 Pro": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 402, + "height": 874 + }, + "viewport": { + "width": 402, + "height": 681 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 17 Pro landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 402, + "height": 874 + }, + "viewport": { + "width": 756, + "height": 352 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 17 Pro Max": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 440, + "height": 956 + }, + "viewport": { + "width": 440, + "height": 763 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 17 Pro Max landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 440, + "height": 956 + }, + "viewport": { + "width": 838, + "height": 390 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 17e": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 390, + "height": 844 + }, + "viewport": { + "width": 390, + "height": 651 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "iPhone 17e landscape": { + "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1", + "screen": { + "width": 390, + "height": 844 + }, + "viewport": { + "width": 726, + "height": 340 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "Kindle Fire HDX": { + "userAgent": "Mozilla/5.0 (Linux; U; en-us; KFAPWI Build/JDQ39) AppleWebKit/535.19 (KHTML, like Gecko) Silk/3.13 Safari/535.19 Silk-Accelerated=true", + "viewport": { + "width": 800, + "height": 1280 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "Kindle Fire HDX landscape": { + "userAgent": "Mozilla/5.0 (Linux; U; en-us; KFAPWI Build/JDQ39) AppleWebKit/535.19 (KHTML, like Gecko) Silk/3.13 Safari/535.19 Silk-Accelerated=true", + "viewport": { + "width": 1280, + "height": 800 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "LG Optimus L70": { + "userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 384, + "height": 640 + }, + "deviceScaleFactor": 1.25, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "LG Optimus L70 landscape": { + "userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 640, + "height": 384 + }, + "deviceScaleFactor": 1.25, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Microsoft Lumia 550": { + "userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36 Edge/14.14263", + "viewport": { + "width": 360, + "height": 640 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Microsoft Lumia 550 landscape": { + "userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36 Edge/14.14263", + "viewport": { + "width": 640, + "height": 360 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Microsoft Lumia 950": { + "userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36 Edge/14.14263", + "viewport": { + "width": 360, + "height": 640 + }, + "deviceScaleFactor": 4, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Microsoft Lumia 950 landscape": { + "userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36 Edge/14.14263", + "viewport": { + "width": 640, + "height": 360 + }, + "deviceScaleFactor": 4, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nexus 10": { + "userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36", + "viewport": { + "width": 800, + "height": 1280 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nexus 10 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36", + "viewport": { + "width": 1280, + "height": 800 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nexus 4": { + "userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 384, + "height": 640 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nexus 4 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 640, + "height": 384 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nexus 5": { + "userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 360, + "height": 640 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nexus 5 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 640, + "height": 360 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nexus 5X": { + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 412, + "height": 732 + }, + "deviceScaleFactor": 2.625, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nexus 5X landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 732, + "height": 412 + }, + "deviceScaleFactor": 2.625, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nexus 6": { + "userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 412, + "height": 732 + }, + "deviceScaleFactor": 3.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nexus 6 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 732, + "height": 412 + }, + "deviceScaleFactor": 3.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nexus 6P": { + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 412, + "height": 732 + }, + "deviceScaleFactor": 3.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nexus 6P landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 732, + "height": 412 + }, + "deviceScaleFactor": 3.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nexus 7": { + "userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36", + "viewport": { + "width": 600, + "height": 960 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nexus 7 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36", + "viewport": { + "width": 960, + "height": 600 + }, + "deviceScaleFactor": 2, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nokia Lumia 520": { + "userAgent": "Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 520)", + "viewport": { + "width": 320, + "height": 533 + }, + "deviceScaleFactor": 1.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nokia Lumia 520 landscape": { + "userAgent": "Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 520)", + "viewport": { + "width": 533, + "height": 320 + }, + "deviceScaleFactor": 1.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Nokia N9": { + "userAgent": "Mozilla/5.0 (MeeGo; NokiaN9) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13", + "viewport": { + "width": 480, + "height": 854 + }, + "deviceScaleFactor": 1, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "Nokia N9 landscape": { + "userAgent": "Mozilla/5.0 (MeeGo; NokiaN9) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13", + "viewport": { + "width": 854, + "height": 480 + }, + "deviceScaleFactor": 1, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "webkit" + }, + "Pixel 2": { + "userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 411, + "height": 731 + }, + "deviceScaleFactor": 2.625, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 2 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 731, + "height": 411 + }, + "deviceScaleFactor": 2.625, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 2 XL": { + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 411, + "height": 823 + }, + "deviceScaleFactor": 3.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 2 XL landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 823, + "height": 411 + }, + "deviceScaleFactor": 3.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 3": { + "userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 393, + "height": 786 + }, + "deviceScaleFactor": 2.75, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 3 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 786, + "height": 393 + }, + "deviceScaleFactor": 2.75, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 4": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 353, + "height": 745 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 4 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 745, + "height": 353 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 4a (5G)": { + "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 412, + "height": 892 + }, + "viewport": { + "width": 412, + "height": 765 + }, + "deviceScaleFactor": 2.63, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 4a (5G) landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "height": 892, + "width": 412 + }, + "viewport": { + "width": 840, + "height": 312 + }, + "deviceScaleFactor": 2.63, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 5": { + "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 393, + "height": 851 + }, + "viewport": { + "width": 393, + "height": 727 + }, + "deviceScaleFactor": 2.75, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 5 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 851, + "height": 393 + }, + "viewport": { + "width": 802, + "height": 293 + }, + "deviceScaleFactor": 2.75, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 6": { + "userAgent": "Mozilla/5.0 (Linux; Android 12; Pixel 6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 412, + "height": 915 + }, + "viewport": { + "width": 412, + "height": 839 + }, + "deviceScaleFactor": 2.625, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 6 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 12; Pixel 6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 915, + "height": 412 + }, + "viewport": { + "width": 863, + "height": 360 + }, + "deviceScaleFactor": 2.625, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 6 Pro": { + "userAgent": "Mozilla/5.0 (Linux; Android 12; Pixel 6 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 412, + "height": 892 + }, + "viewport": { + "width": 412, + "height": 816 + }, + "deviceScaleFactor": 3.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 6 Pro landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 12; Pixel 6 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 892, + "height": 412 + }, + "viewport": { + "width": 840, + "height": 360 + }, + "deviceScaleFactor": 3.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 6a": { + "userAgent": "Mozilla/5.0 (Linux; Android 12; Pixel 6a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 412, + "height": 915 + }, + "viewport": { + "width": 412, + "height": 839 + }, + "deviceScaleFactor": 2.625, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 6a landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 12; Pixel 6a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 915, + "height": 412 + }, + "viewport": { + "width": 863, + "height": 360 + }, + "deviceScaleFactor": 2.625, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 7": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 412, + "height": 915 + }, + "viewport": { + "width": 412, + "height": 839 + }, + "deviceScaleFactor": 2.625, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 7 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 915, + "height": 412 + }, + "viewport": { + "width": 863, + "height": 360 + }, + "deviceScaleFactor": 2.625, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 7 Pro": { + "userAgent": "Mozilla/5.0 (Linux; Android 13; Pixel 7 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 412, + "height": 892 + }, + "viewport": { + "width": 412, + "height": 816 + }, + "deviceScaleFactor": 3.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 7 Pro landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 13; Pixel 7 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 892, + "height": 412 + }, + "viewport": { + "width": 840, + "height": 360 + }, + "deviceScaleFactor": 3.5, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 7a": { + "userAgent": "Mozilla/5.0 (Linux; Android 13; Pixel 7a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 412, + "height": 915 + }, + "viewport": { + "width": 412, + "height": 839 + }, + "deviceScaleFactor": 2.625, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 7a landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 13; Pixel 7a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 915, + "height": 412 + }, + "viewport": { + "width": 863, + "height": 360 + }, + "deviceScaleFactor": 2.625, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 8": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 412, + "height": 915 + }, + "viewport": { + "width": 412, + "height": 839 + }, + "deviceScaleFactor": 2.625, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 8 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 915, + "height": 412 + }, + "viewport": { + "width": 863, + "height": 360 + }, + "deviceScaleFactor": 2.625, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 8 Pro": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 8 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 448, + "height": 997 + }, + "viewport": { + "width": 448, + "height": 921 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 8 Pro landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 8 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 997, + "height": 448 + }, + "viewport": { + "width": 945, + "height": 396 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 8a": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 8a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 412, + "height": 915 + }, + "viewport": { + "width": 412, + "height": 839 + }, + "deviceScaleFactor": 2.625, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 8a landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 8a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 915, + "height": 412 + }, + "viewport": { + "width": 863, + "height": 360 + }, + "deviceScaleFactor": 2.625, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 9": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 9) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 360, + "height": 808 + }, + "viewport": { + "width": 360, + "height": 732 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 9 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 9) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 808, + "height": 360 + }, + "viewport": { + "width": 756, + "height": 308 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 9 Pro": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 9 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 427, + "height": 952 + }, + "viewport": { + "width": 427, + "height": 876 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 9 Pro landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 9 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 952, + "height": 427 + }, + "viewport": { + "width": 900, + "height": 375 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 9 Pro XL": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 9 Pro XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 448, + "height": 997 + }, + "viewport": { + "width": 448, + "height": 921 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 9 Pro XL landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 9 Pro XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 997, + "height": 448 + }, + "viewport": { + "width": 945, + "height": 396 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 10": { + "userAgent": "Mozilla/5.0 (Linux; Android 16; Pixel 10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 360, + "height": 808 + }, + "viewport": { + "width": 360, + "height": 732 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 10 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 16; Pixel 10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 808, + "height": 360 + }, + "viewport": { + "width": 756, + "height": 308 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 10 Pro": { + "userAgent": "Mozilla/5.0 (Linux; Android 16; Pixel 10 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 427, + "height": 952 + }, + "viewport": { + "width": 427, + "height": 876 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 10 Pro landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 16; Pixel 10 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 952, + "height": 427 + }, + "viewport": { + "width": 900, + "height": 375 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 10 Pro XL": { + "userAgent": "Mozilla/5.0 (Linux; Android 16; Pixel 10 Pro XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 448, + "height": 997 + }, + "viewport": { + "width": 448, + "height": 921 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Pixel 10 Pro XL landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 16; Pixel 10 Pro XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "screen": { + "width": 997, + "height": 448 + }, + "viewport": { + "width": 945, + "height": 396 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Moto G4": { + "userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 360, + "height": 640 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Moto G4 landscape": { + "userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36", + "viewport": { + "width": 640, + "height": 360 + }, + "deviceScaleFactor": 3, + "isMobile": true, + "hasTouch": true, + "defaultBrowserType": "chromium" + }, + "Desktop Chrome HiDPI": { + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36", + "screen": { + "width": 1792, + "height": 1120 + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "deviceScaleFactor": 2, + "isMobile": false, + "hasTouch": false, + "defaultBrowserType": "chromium" + }, + "Desktop Edge HiDPI": { + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36 Edg/149.0.7827.55", + "screen": { + "width": 1792, + "height": 1120 + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "deviceScaleFactor": 2, + "isMobile": false, + "hasTouch": false, + "defaultBrowserType": "chromium" + }, + "Desktop Firefox HiDPI": { + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:151.0) Gecko/20100101 Firefox/151.0", + "screen": { + "width": 1792, + "height": 1120 + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "deviceScaleFactor": 2, + "isMobile": false, + "hasTouch": false, + "defaultBrowserType": "firefox" + }, + "Desktop Safari": { + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Safari/605.1.15", + "screen": { + "width": 1792, + "height": 1120 + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "deviceScaleFactor": 2, + "isMobile": false, + "hasTouch": false, + "defaultBrowserType": "webkit" + }, + "Desktop Chrome": { + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36", + "screen": { + "width": 1920, + "height": 1080 + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "deviceScaleFactor": 1, + "isMobile": false, + "hasTouch": false, + "defaultBrowserType": "chromium" + }, + "Desktop Edge": { + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36 Edg/149.0.7827.55", + "screen": { + "width": 1920, + "height": 1080 + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "deviceScaleFactor": 1, + "isMobile": false, + "hasTouch": false, + "defaultBrowserType": "chromium" + }, + "Desktop Firefox": { + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:151.0) Gecko/20100101 Firefox/151.0", + "screen": { + "width": 1920, + "height": 1080 + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "deviceScaleFactor": 1, + "isMobile": false, + "hasTouch": false, + "defaultBrowserType": "firefox" + } +} \ No newline at end of file diff --git a/simulator/e2e/node_modules/playwright-core/lib/server/electron/loader.js b/simulator/e2e/node_modules/playwright-core/lib/server/electron/loader.js new file mode 100644 index 0000000..85427f6 --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/lib/server/electron/loader.js @@ -0,0 +1,115 @@ +"use strict"; + +// packages/playwright-core/src/server/electron/loader.ts +var import_electron = require("electron"); + +// packages/playwright-core/src/server/chromium/chromiumSwitches.ts +var disabledFeatures = [ + // See https://github.com/microsoft/playwright/issues/14047 + "AvoidUnnecessaryBeforeUnloadCheckSync", + // See https://github.com/microsoft/playwright/issues/38568 + "BoundaryEventDispatchTracksNodeRemoval", + "DestroyProfileOnBrowserClose", + // See https://github.com/microsoft/playwright/pull/13854 + "DialMediaRouteProvider", + "GlobalMediaControls", + // See https://github.com/microsoft/playwright/pull/27605 + "HttpsUpgrades", + // Hides the Lens feature in the URL address bar. Its not working in unofficial builds. + "LensOverlay", + // See https://github.com/microsoft/playwright/pull/8162 + "MediaRouter", + // See https://github.com/microsoft/playwright/issues/28023 + "PaintHolding", + // See https://github.com/microsoft/playwright/issues/32230 + "ThirdPartyStoragePartitioning", + // See https://github.com/microsoft/playwright/issues/16126 + "Translate", + // See https://issues.chromium.org/u/1/issues/435410220 + "AutoDeElevate", + // See https://github.com/microsoft/playwright/issues/37714 + "RenderDocument", + // Prevents downloading optimization hints on startup. + "OptimizationHints", + // Disables forced sign-in in Edge. + "msForceBrowserSignIn", + // Disables updating the preferred version in LaunchServices preferences on mac. + "msEdgeUpdateLaunchServicesPreferredVersion" +].filter(Boolean); +var chromiumSwitches = (options) => [ + "--disable-field-trial-config", + // https://source.chromium.org/chromium/chromium/src/+/main:testing/variations/README.md + "--disable-background-networking", + "--disable-background-timer-throttling", + "--disable-backgrounding-occluded-windows", + "--disable-back-forward-cache", + // Avoids surprises like main request not being intercepted during page.goBack(). + "--disable-breakpad", + "--disable-client-side-phishing-detection", + "--disable-component-extensions-with-background-pages", + "--disable-component-update", + // Avoids unneeded network activity after startup. + "--no-default-browser-check", + "--disable-default-apps", + "--disable-dev-shm-usage", + "--disable-edgeupdater", + // Disables Edge-specific updater on mac. + "--disable-extensions", + "--disable-features=" + disabledFeatures.join(","), + process.env.PLAYWRIGHT_LEGACY_SCREENSHOT ? "" : "--enable-features=CDPScreenshotNewSurface", + "--allow-pre-commit-input", + "--disable-hang-monitor", + "--disable-ipc-flooding-protection", + "--disable-popup-blocking", + "--disable-prompt-on-repost", + "--disable-renderer-backgrounding", + "--force-color-profile=srgb", + "--metrics-recording-only", + "--no-first-run", + "--password-store=basic", + "--use-mock-keychain", + // See https://chromium-review.googlesource.com/c/chromium/src/+/2436773 + "--no-service-autorun", + "--export-tagged-pdf", + // https://chromium-review.googlesource.com/c/chromium/src/+/4853540 + "--disable-search-engine-choice-screen", + // https://issues.chromium.org/41491762 + "--unsafely-disable-devtools-self-xss-warnings", + // Edge can potentially restart on Windows (msRelaunchNoCompatLayer) which looses its file descriptors (stdout/stderr) and CDP (3/4). Disable until fixed upstream. + "--edge-skip-compat-layer-relaunch", + // This disables Chrome for Testing infobar that is visible in the persistent context. + // The switch is ignored everywhere else, including Chromium/Chrome/Edge. + "--disable-infobars", + // Less annoying popups. + "--disable-search-engine-choice-screen", + // Prevents the "three dots" menu crash in IdentityManager::HasPrimaryAccount for ephemeral contexts. + options?.android ? "" : "--disable-sync" +].filter(Boolean); + +// packages/playwright-core/src/server/electron/loader.ts +process.argv.splice(1, process.argv.indexOf("--remote-debugging-port=0")); +for (const arg of chromiumSwitches()) { + const match = arg.match(/--([^=]*)=?(.*)/); + import_electron.app.commandLine.appendSwitch(match[1], match[2]); +} +var originalWhenReady = import_electron.app.whenReady(); +var originalEmit = import_electron.app.emit.bind(import_electron.app); +var readyEventArgs; +import_electron.app.emit = (event, ...args) => { + if (event === "ready") { + readyEventArgs = args; + return import_electron.app.listenerCount("ready") > 0; + } + return originalEmit(event, ...args); +}; +var isReady = false; +var whenReadyCallback; +var whenReadyPromise = new Promise((f) => whenReadyCallback = f); +import_electron.app.isReady = () => isReady; +import_electron.app.whenReady = () => whenReadyPromise; +globalThis.__playwright_run = async () => { + const event = await originalWhenReady; + isReady = true; + whenReadyCallback(event); + originalEmit("ready", ...readyEventArgs); +}; diff --git a/simulator/e2e/node_modules/playwright-core/lib/serverRegistry.js b/simulator/e2e/node_modules/playwright-core/lib/serverRegistry.js new file mode 100644 index 0000000..7c3d740 --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/lib/serverRegistry.js @@ -0,0 +1,7343 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __commonJS = (cb, mod) => function __require() { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; +}; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// node_modules/readdirp/node_modules/picomatch/lib/constants.js +var require_constants = __commonJS({ + "node_modules/readdirp/node_modules/picomatch/lib/constants.js"(exports2, module2) { + "use strict"; + var path3 = require("path"); + var WIN_SLASH = "\\\\/"; + var WIN_NO_SLASH = `[^${WIN_SLASH}]`; + var DEFAULT_MAX_EXTGLOB_RECURSION = 0; + var DOT_LITERAL = "\\."; + var PLUS_LITERAL = "\\+"; + var QMARK_LITERAL = "\\?"; + var SLASH_LITERAL = "\\/"; + var ONE_CHAR = "(?=.)"; + var QMARK = "[^/]"; + var END_ANCHOR = `(?:${SLASH_LITERAL}|$)`; + var START_ANCHOR = `(?:^|${SLASH_LITERAL})`; + var DOTS_SLASH = `${DOT_LITERAL}{1,2}${END_ANCHOR}`; + var NO_DOT = `(?!${DOT_LITERAL})`; + var NO_DOTS = `(?!${START_ANCHOR}${DOTS_SLASH})`; + var NO_DOT_SLASH = `(?!${DOT_LITERAL}{0,1}${END_ANCHOR})`; + var NO_DOTS_SLASH = `(?!${DOTS_SLASH})`; + var QMARK_NO_DOT = `[^.${SLASH_LITERAL}]`; + var STAR = `${QMARK}*?`; + var POSIX_CHARS = { + DOT_LITERAL, + PLUS_LITERAL, + QMARK_LITERAL, + SLASH_LITERAL, + ONE_CHAR, + QMARK, + END_ANCHOR, + DOTS_SLASH, + NO_DOT, + NO_DOTS, + NO_DOT_SLASH, + NO_DOTS_SLASH, + QMARK_NO_DOT, + STAR, + START_ANCHOR + }; + var WINDOWS_CHARS = { + ...POSIX_CHARS, + SLASH_LITERAL: `[${WIN_SLASH}]`, + QMARK: WIN_NO_SLASH, + STAR: `${WIN_NO_SLASH}*?`, + DOTS_SLASH: `${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$)`, + NO_DOT: `(?!${DOT_LITERAL})`, + NO_DOTS: `(?!(?:^|[${WIN_SLASH}])${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`, + NO_DOT_SLASH: `(?!${DOT_LITERAL}{0,1}(?:[${WIN_SLASH}]|$))`, + NO_DOTS_SLASH: `(?!${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`, + QMARK_NO_DOT: `[^.${WIN_SLASH}]`, + START_ANCHOR: `(?:^|[${WIN_SLASH}])`, + END_ANCHOR: `(?:[${WIN_SLASH}]|$)` + }; + var POSIX_REGEX_SOURCE = { + __proto__: null, + alnum: "a-zA-Z0-9", + alpha: "a-zA-Z", + ascii: "\\x00-\\x7F", + blank: " \\t", + cntrl: "\\x00-\\x1F\\x7F", + digit: "0-9", + graph: "\\x21-\\x7E", + lower: "a-z", + print: "\\x20-\\x7E ", + punct: "\\-!\"#$%&'()\\*+,./:;<=>?@[\\]^_`{|}~", + space: " \\t\\r\\n\\v\\f", + upper: "A-Z", + word: "A-Za-z0-9_", + xdigit: "A-Fa-f0-9" + }; + module2.exports = { + DEFAULT_MAX_EXTGLOB_RECURSION, + MAX_LENGTH: 1024 * 64, + POSIX_REGEX_SOURCE, + // regular expressions + REGEX_BACKSLASH: /\\(?![*+?^${}(|)[\]])/g, + REGEX_NON_SPECIAL_CHARS: /^[^@![\].,$*+?^{}()|\\/]+/, + REGEX_SPECIAL_CHARS: /[-*+?.^${}(|)[\]]/, + REGEX_SPECIAL_CHARS_BACKREF: /(\\?)((\W)(\3*))/g, + REGEX_SPECIAL_CHARS_GLOBAL: /([-*+?.^${}(|)[\]])/g, + REGEX_REMOVE_BACKSLASH: /(?:\[.*?[^\\]\]|\\(?=.))/g, + // Replace globs with equivalent patterns to reduce parsing time. + REPLACEMENTS: { + __proto__: null, + "***": "*", + "**/**": "**", + "**/**/**": "**" + }, + // Digits + CHAR_0: 48, + /* 0 */ + CHAR_9: 57, + /* 9 */ + // Alphabet chars. + CHAR_UPPERCASE_A: 65, + /* A */ + CHAR_LOWERCASE_A: 97, + /* a */ + CHAR_UPPERCASE_Z: 90, + /* Z */ + CHAR_LOWERCASE_Z: 122, + /* z */ + CHAR_LEFT_PARENTHESES: 40, + /* ( */ + CHAR_RIGHT_PARENTHESES: 41, + /* ) */ + CHAR_ASTERISK: 42, + /* * */ + // Non-alphabetic chars. + CHAR_AMPERSAND: 38, + /* & */ + CHAR_AT: 64, + /* @ */ + CHAR_BACKWARD_SLASH: 92, + /* \ */ + CHAR_CARRIAGE_RETURN: 13, + /* \r */ + CHAR_CIRCUMFLEX_ACCENT: 94, + /* ^ */ + CHAR_COLON: 58, + /* : */ + CHAR_COMMA: 44, + /* , */ + CHAR_DOT: 46, + /* . */ + CHAR_DOUBLE_QUOTE: 34, + /* " */ + CHAR_EQUAL: 61, + /* = */ + CHAR_EXCLAMATION_MARK: 33, + /* ! */ + CHAR_FORM_FEED: 12, + /* \f */ + CHAR_FORWARD_SLASH: 47, + /* / */ + CHAR_GRAVE_ACCENT: 96, + /* ` */ + CHAR_HASH: 35, + /* # */ + CHAR_HYPHEN_MINUS: 45, + /* - */ + CHAR_LEFT_ANGLE_BRACKET: 60, + /* < */ + CHAR_LEFT_CURLY_BRACE: 123, + /* { */ + CHAR_LEFT_SQUARE_BRACKET: 91, + /* [ */ + CHAR_LINE_FEED: 10, + /* \n */ + CHAR_NO_BREAK_SPACE: 160, + /* \u00A0 */ + CHAR_PERCENT: 37, + /* % */ + CHAR_PLUS: 43, + /* + */ + CHAR_QUESTION_MARK: 63, + /* ? */ + CHAR_RIGHT_ANGLE_BRACKET: 62, + /* > */ + CHAR_RIGHT_CURLY_BRACE: 125, + /* } */ + CHAR_RIGHT_SQUARE_BRACKET: 93, + /* ] */ + CHAR_SEMICOLON: 59, + /* ; */ + CHAR_SINGLE_QUOTE: 39, + /* ' */ + CHAR_SPACE: 32, + /* */ + CHAR_TAB: 9, + /* \t */ + CHAR_UNDERSCORE: 95, + /* _ */ + CHAR_VERTICAL_LINE: 124, + /* | */ + CHAR_ZERO_WIDTH_NOBREAK_SPACE: 65279, + /* \uFEFF */ + SEP: path3.sep, + /** + * Create EXTGLOB_CHARS + */ + extglobChars(chars) { + return { + "!": { type: "negate", open: "(?:(?!(?:", close: `))${chars.STAR})` }, + "?": { type: "qmark", open: "(?:", close: ")?" }, + "+": { type: "plus", open: "(?:", close: ")+" }, + "*": { type: "star", open: "(?:", close: ")*" }, + "@": { type: "at", open: "(?:", close: ")" } + }; + }, + /** + * Create GLOB_CHARS + */ + globChars(win32) { + return win32 === true ? WINDOWS_CHARS : POSIX_CHARS; + } + }; + } +}); + +// node_modules/readdirp/node_modules/picomatch/lib/utils.js +var require_utils = __commonJS({ + "node_modules/readdirp/node_modules/picomatch/lib/utils.js"(exports2) { + "use strict"; + var path3 = require("path"); + var win32 = process.platform === "win32"; + var { + REGEX_BACKSLASH, + REGEX_REMOVE_BACKSLASH, + REGEX_SPECIAL_CHARS, + REGEX_SPECIAL_CHARS_GLOBAL + } = require_constants(); + exports2.isObject = (val) => val !== null && typeof val === "object" && !Array.isArray(val); + exports2.hasRegexChars = (str) => REGEX_SPECIAL_CHARS.test(str); + exports2.isRegexChar = (str) => str.length === 1 && exports2.hasRegexChars(str); + exports2.escapeRegex = (str) => str.replace(REGEX_SPECIAL_CHARS_GLOBAL, "\\$1"); + exports2.toPosixSlashes = (str) => str.replace(REGEX_BACKSLASH, "/"); + exports2.removeBackslashes = (str) => { + return str.replace(REGEX_REMOVE_BACKSLASH, (match) => { + return match === "\\" ? "" : match; + }); + }; + exports2.supportsLookbehinds = () => { + const segs = process.version.slice(1).split(".").map(Number); + if (segs.length === 3 && segs[0] >= 9 || segs[0] === 8 && segs[1] >= 10) { + return true; + } + return false; + }; + exports2.isWindows = (options) => { + if (options && typeof options.windows === "boolean") { + return options.windows; + } + return win32 === true || path3.sep === "\\"; + }; + exports2.escapeLast = (input, char, lastIdx) => { + const idx = input.lastIndexOf(char, lastIdx); + if (idx === -1) return input; + if (input[idx - 1] === "\\") return exports2.escapeLast(input, char, idx - 1); + return `${input.slice(0, idx)}\\${input.slice(idx)}`; + }; + exports2.removePrefix = (input, state = {}) => { + let output = input; + if (output.startsWith("./")) { + output = output.slice(2); + state.prefix = "./"; + } + return output; + }; + exports2.wrapOutput = (input, state = {}, options = {}) => { + const prepend = options.contains ? "" : "^"; + const append = options.contains ? "" : "$"; + let output = `${prepend}(?:${input})${append}`; + if (state.negated === true) { + output = `(?:^(?!${output}).*$)`; + } + return output; + }; + } +}); + +// node_modules/readdirp/node_modules/picomatch/lib/scan.js +var require_scan = __commonJS({ + "node_modules/readdirp/node_modules/picomatch/lib/scan.js"(exports2, module2) { + "use strict"; + var utils = require_utils(); + var { + CHAR_ASTERISK, + /* * */ + CHAR_AT, + /* @ */ + CHAR_BACKWARD_SLASH, + /* \ */ + CHAR_COMMA, + /* , */ + CHAR_DOT, + /* . */ + CHAR_EXCLAMATION_MARK, + /* ! */ + CHAR_FORWARD_SLASH, + /* / */ + CHAR_LEFT_CURLY_BRACE, + /* { */ + CHAR_LEFT_PARENTHESES, + /* ( */ + CHAR_LEFT_SQUARE_BRACKET, + /* [ */ + CHAR_PLUS, + /* + */ + CHAR_QUESTION_MARK, + /* ? */ + CHAR_RIGHT_CURLY_BRACE, + /* } */ + CHAR_RIGHT_PARENTHESES, + /* ) */ + CHAR_RIGHT_SQUARE_BRACKET + /* ] */ + } = require_constants(); + var isPathSeparator = (code) => { + return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH; + }; + var depth = (token) => { + if (token.isPrefix !== true) { + token.depth = token.isGlobstar ? Infinity : 1; + } + }; + var scan = (input, options) => { + const opts = options || {}; + const length = input.length - 1; + const scanToEnd = opts.parts === true || opts.scanToEnd === true; + const slashes = []; + const tokens = []; + const parts = []; + let str = input; + let index = -1; + let start = 0; + let lastIndex = 0; + let isBrace = false; + let isBracket = false; + let isGlob = false; + let isExtglob = false; + let isGlobstar = false; + let braceEscaped = false; + let backslashes = false; + let negated = false; + let negatedExtglob = false; + let finished = false; + let braces = 0; + let prev; + let code; + let token = { value: "", depth: 0, isGlob: false }; + const eos = () => index >= length; + const peek = () => str.charCodeAt(index + 1); + const advance = () => { + prev = code; + return str.charCodeAt(++index); + }; + while (index < length) { + code = advance(); + let next; + if (code === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + code = advance(); + if (code === CHAR_LEFT_CURLY_BRACE) { + braceEscaped = true; + } + continue; + } + if (braceEscaped === true || code === CHAR_LEFT_CURLY_BRACE) { + braces++; + while (eos() !== true && (code = advance())) { + if (code === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + advance(); + continue; + } + if (code === CHAR_LEFT_CURLY_BRACE) { + braces++; + continue; + } + if (braceEscaped !== true && code === CHAR_DOT && (code = advance()) === CHAR_DOT) { + isBrace = token.isBrace = true; + isGlob = token.isGlob = true; + finished = true; + if (scanToEnd === true) { + continue; + } + break; + } + if (braceEscaped !== true && code === CHAR_COMMA) { + isBrace = token.isBrace = true; + isGlob = token.isGlob = true; + finished = true; + if (scanToEnd === true) { + continue; + } + break; + } + if (code === CHAR_RIGHT_CURLY_BRACE) { + braces--; + if (braces === 0) { + braceEscaped = false; + isBrace = token.isBrace = true; + finished = true; + break; + } + } + } + if (scanToEnd === true) { + continue; + } + break; + } + if (code === CHAR_FORWARD_SLASH) { + slashes.push(index); + tokens.push(token); + token = { value: "", depth: 0, isGlob: false }; + if (finished === true) continue; + if (prev === CHAR_DOT && index === start + 1) { + start += 2; + continue; + } + lastIndex = index + 1; + continue; + } + if (opts.noext !== true) { + const isExtglobChar = code === CHAR_PLUS || code === CHAR_AT || code === CHAR_ASTERISK || code === CHAR_QUESTION_MARK || code === CHAR_EXCLAMATION_MARK; + if (isExtglobChar === true && peek() === CHAR_LEFT_PARENTHESES) { + isGlob = token.isGlob = true; + isExtglob = token.isExtglob = true; + finished = true; + if (code === CHAR_EXCLAMATION_MARK && index === start) { + negatedExtglob = true; + } + if (scanToEnd === true) { + while (eos() !== true && (code = advance())) { + if (code === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + code = advance(); + continue; + } + if (code === CHAR_RIGHT_PARENTHESES) { + isGlob = token.isGlob = true; + finished = true; + break; + } + } + continue; + } + break; + } + } + if (code === CHAR_ASTERISK) { + if (prev === CHAR_ASTERISK) isGlobstar = token.isGlobstar = true; + isGlob = token.isGlob = true; + finished = true; + if (scanToEnd === true) { + continue; + } + break; + } + if (code === CHAR_QUESTION_MARK) { + isGlob = token.isGlob = true; + finished = true; + if (scanToEnd === true) { + continue; + } + break; + } + if (code === CHAR_LEFT_SQUARE_BRACKET) { + while (eos() !== true && (next = advance())) { + if (next === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + advance(); + continue; + } + if (next === CHAR_RIGHT_SQUARE_BRACKET) { + isBracket = token.isBracket = true; + isGlob = token.isGlob = true; + finished = true; + break; + } + } + if (scanToEnd === true) { + continue; + } + break; + } + if (opts.nonegate !== true && code === CHAR_EXCLAMATION_MARK && index === start) { + negated = token.negated = true; + start++; + continue; + } + if (opts.noparen !== true && code === CHAR_LEFT_PARENTHESES) { + isGlob = token.isGlob = true; + if (scanToEnd === true) { + while (eos() !== true && (code = advance())) { + if (code === CHAR_LEFT_PARENTHESES) { + backslashes = token.backslashes = true; + code = advance(); + continue; + } + if (code === CHAR_RIGHT_PARENTHESES) { + finished = true; + break; + } + } + continue; + } + break; + } + if (isGlob === true) { + finished = true; + if (scanToEnd === true) { + continue; + } + break; + } + } + if (opts.noext === true) { + isExtglob = false; + isGlob = false; + } + let base = str; + let prefix = ""; + let glob = ""; + if (start > 0) { + prefix = str.slice(0, start); + str = str.slice(start); + lastIndex -= start; + } + if (base && isGlob === true && lastIndex > 0) { + base = str.slice(0, lastIndex); + glob = str.slice(lastIndex); + } else if (isGlob === true) { + base = ""; + glob = str; + } else { + base = str; + } + if (base && base !== "" && base !== "/" && base !== str) { + if (isPathSeparator(base.charCodeAt(base.length - 1))) { + base = base.slice(0, -1); + } + } + if (opts.unescape === true) { + if (glob) glob = utils.removeBackslashes(glob); + if (base && backslashes === true) { + base = utils.removeBackslashes(base); + } + } + const state = { + prefix, + input, + start, + base, + glob, + isBrace, + isBracket, + isGlob, + isExtglob, + isGlobstar, + negated, + negatedExtglob + }; + if (opts.tokens === true) { + state.maxDepth = 0; + if (!isPathSeparator(code)) { + tokens.push(token); + } + state.tokens = tokens; + } + if (opts.parts === true || opts.tokens === true) { + let prevIndex; + for (let idx = 0; idx < slashes.length; idx++) { + const n = prevIndex ? prevIndex + 1 : start; + const i = slashes[idx]; + const value = input.slice(n, i); + if (opts.tokens) { + if (idx === 0 && start !== 0) { + tokens[idx].isPrefix = true; + tokens[idx].value = prefix; + } else { + tokens[idx].value = value; + } + depth(tokens[idx]); + state.maxDepth += tokens[idx].depth; + } + if (idx !== 0 || value !== "") { + parts.push(value); + } + prevIndex = i; + } + if (prevIndex && prevIndex + 1 < input.length) { + const value = input.slice(prevIndex + 1); + parts.push(value); + if (opts.tokens) { + tokens[tokens.length - 1].value = value; + depth(tokens[tokens.length - 1]); + state.maxDepth += tokens[tokens.length - 1].depth; + } + } + state.slashes = slashes; + state.parts = parts; + } + return state; + }; + module2.exports = scan; + } +}); + +// node_modules/readdirp/node_modules/picomatch/lib/parse.js +var require_parse = __commonJS({ + "node_modules/readdirp/node_modules/picomatch/lib/parse.js"(exports2, module2) { + "use strict"; + var constants = require_constants(); + var utils = require_utils(); + var { + MAX_LENGTH, + POSIX_REGEX_SOURCE, + REGEX_NON_SPECIAL_CHARS, + REGEX_SPECIAL_CHARS_BACKREF, + REPLACEMENTS + } = constants; + var expandRange = (args, options) => { + if (typeof options.expandRange === "function") { + return options.expandRange(...args, options); + } + args.sort(); + const value = `[${args.join("-")}]`; + try { + new RegExp(value); + } catch (ex) { + return args.map((v) => utils.escapeRegex(v)).join(".."); + } + return value; + }; + var syntaxError = (type, char) => { + return `Missing ${type}: "${char}" - use "\\\\${char}" to match literal characters`; + }; + var splitTopLevel = (input) => { + const parts = []; + let bracket = 0; + let paren = 0; + let quote = 0; + let value = ""; + let escaped = false; + for (const ch of input) { + if (escaped === true) { + value += ch; + escaped = false; + continue; + } + if (ch === "\\") { + value += ch; + escaped = true; + continue; + } + if (ch === '"') { + quote = quote === 1 ? 0 : 1; + value += ch; + continue; + } + if (quote === 0) { + if (ch === "[") { + bracket++; + } else if (ch === "]" && bracket > 0) { + bracket--; + } else if (bracket === 0) { + if (ch === "(") { + paren++; + } else if (ch === ")" && paren > 0) { + paren--; + } else if (ch === "|" && paren === 0) { + parts.push(value); + value = ""; + continue; + } + } + } + value += ch; + } + parts.push(value); + return parts; + }; + var isPlainBranch = (branch) => { + let escaped = false; + for (const ch of branch) { + if (escaped === true) { + escaped = false; + continue; + } + if (ch === "\\") { + escaped = true; + continue; + } + if (/[?*+@!()[\]{}]/.test(ch)) { + return false; + } + } + return true; + }; + var normalizeSimpleBranch = (branch) => { + let value = branch.trim(); + let changed = true; + while (changed === true) { + changed = false; + if (/^@\([^\\()[\]{}|]+\)$/.test(value)) { + value = value.slice(2, -1); + changed = true; + } + } + if (!isPlainBranch(value)) { + return; + } + return value.replace(/\\(.)/g, "$1"); + }; + var hasRepeatedCharPrefixOverlap = (branches) => { + const values = branches.map(normalizeSimpleBranch).filter(Boolean); + for (let i = 0; i < values.length; i++) { + for (let j = i + 1; j < values.length; j++) { + const a = values[i]; + const b = values[j]; + const char = a[0]; + if (!char || a !== char.repeat(a.length) || b !== char.repeat(b.length)) { + continue; + } + if (a === b || a.startsWith(b) || b.startsWith(a)) { + return true; + } + } + } + return false; + }; + var parseRepeatedExtglob = (pattern, requireEnd = true) => { + if (pattern[0] !== "+" && pattern[0] !== "*" || pattern[1] !== "(") { + return; + } + let bracket = 0; + let paren = 0; + let quote = 0; + let escaped = false; + for (let i = 1; i < pattern.length; i++) { + const ch = pattern[i]; + if (escaped === true) { + escaped = false; + continue; + } + if (ch === "\\") { + escaped = true; + continue; + } + if (ch === '"') { + quote = quote === 1 ? 0 : 1; + continue; + } + if (quote === 1) { + continue; + } + if (ch === "[") { + bracket++; + continue; + } + if (ch === "]" && bracket > 0) { + bracket--; + continue; + } + if (bracket > 0) { + continue; + } + if (ch === "(") { + paren++; + continue; + } + if (ch === ")") { + paren--; + if (paren === 0) { + if (requireEnd === true && i !== pattern.length - 1) { + return; + } + return { + type: pattern[0], + body: pattern.slice(2, i), + end: i + }; + } + } + } + }; + var getStarExtglobSequenceOutput = (pattern) => { + let index = 0; + const chars = []; + while (index < pattern.length) { + const match = parseRepeatedExtglob(pattern.slice(index), false); + if (!match || match.type !== "*") { + return; + } + const branches = splitTopLevel(match.body).map((branch2) => branch2.trim()); + if (branches.length !== 1) { + return; + } + const branch = normalizeSimpleBranch(branches[0]); + if (!branch || branch.length !== 1) { + return; + } + chars.push(branch); + index += match.end + 1; + } + if (chars.length < 1) { + return; + } + const source = chars.length === 1 ? utils.escapeRegex(chars[0]) : `[${chars.map((ch) => utils.escapeRegex(ch)).join("")}]`; + return `${source}*`; + }; + var repeatedExtglobRecursion = (pattern) => { + let depth = 0; + let value = pattern.trim(); + let match = parseRepeatedExtglob(value); + while (match) { + depth++; + value = match.body.trim(); + match = parseRepeatedExtglob(value); + } + return depth; + }; + var analyzeRepeatedExtglob = (body, options) => { + if (options.maxExtglobRecursion === false) { + return { risky: false }; + } + const max = typeof options.maxExtglobRecursion === "number" ? options.maxExtglobRecursion : constants.DEFAULT_MAX_EXTGLOB_RECURSION; + const branches = splitTopLevel(body).map((branch) => branch.trim()); + if (branches.length > 1) { + if (branches.some((branch) => branch === "") || branches.some((branch) => /^[*?]+$/.test(branch)) || hasRepeatedCharPrefixOverlap(branches)) { + return { risky: true }; + } + } + for (const branch of branches) { + const safeOutput = getStarExtglobSequenceOutput(branch); + if (safeOutput) { + return { risky: true, safeOutput }; + } + if (repeatedExtglobRecursion(branch) > max) { + return { risky: true }; + } + } + return { risky: false }; + }; + var parse = (input, options) => { + if (typeof input !== "string") { + throw new TypeError("Expected a string"); + } + input = REPLACEMENTS[input] || input; + const opts = { ...options }; + const max = typeof opts.maxLength === "number" ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; + let len = input.length; + if (len > max) { + throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`); + } + const bos = { type: "bos", value: "", output: opts.prepend || "" }; + const tokens = [bos]; + const capture = opts.capture ? "" : "?:"; + const win32 = utils.isWindows(options); + const PLATFORM_CHARS = constants.globChars(win32); + const EXTGLOB_CHARS = constants.extglobChars(PLATFORM_CHARS); + const { + DOT_LITERAL, + PLUS_LITERAL, + SLASH_LITERAL, + ONE_CHAR, + DOTS_SLASH, + NO_DOT, + NO_DOT_SLASH, + NO_DOTS_SLASH, + QMARK, + QMARK_NO_DOT, + STAR, + START_ANCHOR + } = PLATFORM_CHARS; + const globstar = (opts2) => { + return `(${capture}(?:(?!${START_ANCHOR}${opts2.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`; + }; + const nodot = opts.dot ? "" : NO_DOT; + const qmarkNoDot = opts.dot ? QMARK : QMARK_NO_DOT; + let star = opts.bash === true ? globstar(opts) : STAR; + if (opts.capture) { + star = `(${star})`; + } + if (typeof opts.noext === "boolean") { + opts.noextglob = opts.noext; + } + const state = { + input, + index: -1, + start: 0, + dot: opts.dot === true, + consumed: "", + output: "", + prefix: "", + backtrack: false, + negated: false, + brackets: 0, + braces: 0, + parens: 0, + quotes: 0, + globstar: false, + tokens + }; + input = utils.removePrefix(input, state); + len = input.length; + const extglobs = []; + const braces = []; + const stack = []; + let prev = bos; + let value; + const eos = () => state.index === len - 1; + const peek = state.peek = (n = 1) => input[state.index + n]; + const advance = state.advance = () => input[++state.index] || ""; + const remaining = () => input.slice(state.index + 1); + const consume = (value2 = "", num = 0) => { + state.consumed += value2; + state.index += num; + }; + const append = (token) => { + state.output += token.output != null ? token.output : token.value; + consume(token.value); + }; + const negate = () => { + let count = 1; + while (peek() === "!" && (peek(2) !== "(" || peek(3) === "?")) { + advance(); + state.start++; + count++; + } + if (count % 2 === 0) { + return false; + } + state.negated = true; + state.start++; + return true; + }; + const increment = (type) => { + state[type]++; + stack.push(type); + }; + const decrement = (type) => { + state[type]--; + stack.pop(); + }; + const push = (tok) => { + if (prev.type === "globstar") { + const isBrace = state.braces > 0 && (tok.type === "comma" || tok.type === "brace"); + const isExtglob = tok.extglob === true || extglobs.length && (tok.type === "pipe" || tok.type === "paren"); + if (tok.type !== "slash" && tok.type !== "paren" && !isBrace && !isExtglob) { + state.output = state.output.slice(0, -prev.output.length); + prev.type = "star"; + prev.value = "*"; + prev.output = star; + state.output += prev.output; + } + } + if (extglobs.length && tok.type !== "paren") { + extglobs[extglobs.length - 1].inner += tok.value; + } + if (tok.value || tok.output) append(tok); + if (prev && prev.type === "text" && tok.type === "text") { + prev.value += tok.value; + prev.output = (prev.output || "") + tok.value; + return; + } + tok.prev = prev; + tokens.push(tok); + prev = tok; + }; + const extglobOpen = (type, value2) => { + const token = { ...EXTGLOB_CHARS[value2], conditions: 1, inner: "" }; + token.prev = prev; + token.parens = state.parens; + token.output = state.output; + token.startIndex = state.index; + token.tokensIndex = tokens.length; + const output = (opts.capture ? "(" : "") + token.open; + increment("parens"); + push({ type, value: value2, output: state.output ? "" : ONE_CHAR }); + push({ type: "paren", extglob: true, value: advance(), output }); + extglobs.push(token); + }; + const extglobClose = (token) => { + const literal = input.slice(token.startIndex, state.index + 1); + const body = input.slice(token.startIndex + 2, state.index); + const analysis = analyzeRepeatedExtglob(body, opts); + if ((token.type === "plus" || token.type === "star") && analysis.risky) { + const safeOutput = analysis.safeOutput ? (token.output ? "" : ONE_CHAR) + (opts.capture ? `(${analysis.safeOutput})` : analysis.safeOutput) : void 0; + const open = tokens[token.tokensIndex]; + open.type = "text"; + open.value = literal; + open.output = safeOutput || utils.escapeRegex(literal); + for (let i = token.tokensIndex + 1; i < tokens.length; i++) { + tokens[i].value = ""; + tokens[i].output = ""; + delete tokens[i].suffix; + } + state.output = token.output + open.output; + state.backtrack = true; + push({ type: "paren", extglob: true, value, output: "" }); + decrement("parens"); + return; + } + let output = token.close + (opts.capture ? ")" : ""); + let rest; + if (token.type === "negate") { + let extglobStar = star; + if (token.inner && token.inner.length > 1 && token.inner.includes("/")) { + extglobStar = globstar(opts); + } + if (extglobStar !== star || eos() || /^\)+$/.test(remaining())) { + output = token.close = `)$))${extglobStar}`; + } + if (token.inner.includes("*") && (rest = remaining()) && /^\.[^\\/.]+$/.test(rest)) { + const expression = parse(rest, { ...options, fastpaths: false }).output; + output = token.close = `)${expression})${extglobStar})`; + } + if (token.prev.type === "bos") { + state.negatedExtglob = true; + } + } + push({ type: "paren", extglob: true, value, output }); + decrement("parens"); + }; + if (opts.fastpaths !== false && !/(^[*!]|[/()[\]{}"])/.test(input)) { + let backslashes = false; + let output = input.replace(REGEX_SPECIAL_CHARS_BACKREF, (m, esc, chars, first, rest, index) => { + if (first === "\\") { + backslashes = true; + return m; + } + if (first === "?") { + if (esc) { + return esc + first + (rest ? QMARK.repeat(rest.length) : ""); + } + if (index === 0) { + return qmarkNoDot + (rest ? QMARK.repeat(rest.length) : ""); + } + return QMARK.repeat(chars.length); + } + if (first === ".") { + return DOT_LITERAL.repeat(chars.length); + } + if (first === "*") { + if (esc) { + return esc + first + (rest ? star : ""); + } + return star; + } + return esc ? m : `\\${m}`; + }); + if (backslashes === true) { + if (opts.unescape === true) { + output = output.replace(/\\/g, ""); + } else { + output = output.replace(/\\+/g, (m) => { + return m.length % 2 === 0 ? "\\\\" : m ? "\\" : ""; + }); + } + } + if (output === input && opts.contains === true) { + state.output = input; + return state; + } + state.output = utils.wrapOutput(output, state, options); + return state; + } + while (!eos()) { + value = advance(); + if (value === "\0") { + continue; + } + if (value === "\\") { + const next = peek(); + if (next === "/" && opts.bash !== true) { + continue; + } + if (next === "." || next === ";") { + continue; + } + if (!next) { + value += "\\"; + push({ type: "text", value }); + continue; + } + const match = /^\\+/.exec(remaining()); + let slashes = 0; + if (match && match[0].length > 2) { + slashes = match[0].length; + state.index += slashes; + if (slashes % 2 !== 0) { + value += "\\"; + } + } + if (opts.unescape === true) { + value = advance(); + } else { + value += advance(); + } + if (state.brackets === 0) { + push({ type: "text", value }); + continue; + } + } + if (state.brackets > 0 && (value !== "]" || prev.value === "[" || prev.value === "[^")) { + if (opts.posix !== false && value === ":") { + const inner = prev.value.slice(1); + if (inner.includes("[")) { + prev.posix = true; + if (inner.includes(":")) { + const idx = prev.value.lastIndexOf("["); + const pre = prev.value.slice(0, idx); + const rest2 = prev.value.slice(idx + 2); + const posix = POSIX_REGEX_SOURCE[rest2]; + if (posix) { + prev.value = pre + posix; + state.backtrack = true; + advance(); + if (!bos.output && tokens.indexOf(prev) === 1) { + bos.output = ONE_CHAR; + } + continue; + } + } + } + } + if (value === "[" && peek() !== ":" || value === "-" && peek() === "]") { + value = `\\${value}`; + } + if (value === "]" && (prev.value === "[" || prev.value === "[^")) { + value = `\\${value}`; + } + if (opts.posix === true && value === "!" && prev.value === "[") { + value = "^"; + } + prev.value += value; + append({ value }); + continue; + } + if (state.quotes === 1 && value !== '"') { + value = utils.escapeRegex(value); + prev.value += value; + append({ value }); + continue; + } + if (value === '"') { + state.quotes = state.quotes === 1 ? 0 : 1; + if (opts.keepQuotes === true) { + push({ type: "text", value }); + } + continue; + } + if (value === "(") { + increment("parens"); + push({ type: "paren", value }); + continue; + } + if (value === ")") { + if (state.parens === 0 && opts.strictBrackets === true) { + throw new SyntaxError(syntaxError("opening", "(")); + } + const extglob = extglobs[extglobs.length - 1]; + if (extglob && state.parens === extglob.parens + 1) { + extglobClose(extglobs.pop()); + continue; + } + push({ type: "paren", value, output: state.parens ? ")" : "\\)" }); + decrement("parens"); + continue; + } + if (value === "[") { + if (opts.nobracket === true || !remaining().includes("]")) { + if (opts.nobracket !== true && opts.strictBrackets === true) { + throw new SyntaxError(syntaxError("closing", "]")); + } + value = `\\${value}`; + } else { + increment("brackets"); + } + push({ type: "bracket", value }); + continue; + } + if (value === "]") { + if (opts.nobracket === true || prev && prev.type === "bracket" && prev.value.length === 1) { + push({ type: "text", value, output: `\\${value}` }); + continue; + } + if (state.brackets === 0) { + if (opts.strictBrackets === true) { + throw new SyntaxError(syntaxError("opening", "[")); + } + push({ type: "text", value, output: `\\${value}` }); + continue; + } + decrement("brackets"); + const prevValue = prev.value.slice(1); + if (prev.posix !== true && prevValue[0] === "^" && !prevValue.includes("/")) { + value = `/${value}`; + } + prev.value += value; + append({ value }); + if (opts.literalBrackets === false || utils.hasRegexChars(prevValue)) { + continue; + } + const escaped = utils.escapeRegex(prev.value); + state.output = state.output.slice(0, -prev.value.length); + if (opts.literalBrackets === true) { + state.output += escaped; + prev.value = escaped; + continue; + } + prev.value = `(${capture}${escaped}|${prev.value})`; + state.output += prev.value; + continue; + } + if (value === "{" && opts.nobrace !== true) { + increment("braces"); + const open = { + type: "brace", + value, + output: "(", + outputIndex: state.output.length, + tokensIndex: state.tokens.length + }; + braces.push(open); + push(open); + continue; + } + if (value === "}") { + const brace = braces[braces.length - 1]; + if (opts.nobrace === true || !brace) { + push({ type: "text", value, output: value }); + continue; + } + let output = ")"; + if (brace.dots === true) { + const arr = tokens.slice(); + const range = []; + for (let i = arr.length - 1; i >= 0; i--) { + tokens.pop(); + if (arr[i].type === "brace") { + break; + } + if (arr[i].type !== "dots") { + range.unshift(arr[i].value); + } + } + output = expandRange(range, opts); + state.backtrack = true; + } + if (brace.comma !== true && brace.dots !== true) { + const out = state.output.slice(0, brace.outputIndex); + const toks = state.tokens.slice(brace.tokensIndex); + brace.value = brace.output = "\\{"; + value = output = "\\}"; + state.output = out; + for (const t of toks) { + state.output += t.output || t.value; + } + } + push({ type: "brace", value, output }); + decrement("braces"); + braces.pop(); + continue; + } + if (value === "|") { + if (extglobs.length > 0) { + extglobs[extglobs.length - 1].conditions++; + } + push({ type: "text", value }); + continue; + } + if (value === ",") { + let output = value; + const brace = braces[braces.length - 1]; + if (brace && stack[stack.length - 1] === "braces") { + brace.comma = true; + output = "|"; + } + push({ type: "comma", value, output }); + continue; + } + if (value === "/") { + if (prev.type === "dot" && state.index === state.start + 1) { + state.start = state.index + 1; + state.consumed = ""; + state.output = ""; + tokens.pop(); + prev = bos; + continue; + } + push({ type: "slash", value, output: SLASH_LITERAL }); + continue; + } + if (value === ".") { + if (state.braces > 0 && prev.type === "dot") { + if (prev.value === ".") prev.output = DOT_LITERAL; + const brace = braces[braces.length - 1]; + prev.type = "dots"; + prev.output += value; + prev.value += value; + brace.dots = true; + continue; + } + if (state.braces + state.parens === 0 && prev.type !== "bos" && prev.type !== "slash") { + push({ type: "text", value, output: DOT_LITERAL }); + continue; + } + push({ type: "dot", value, output: DOT_LITERAL }); + continue; + } + if (value === "?") { + const isGroup = prev && prev.value === "("; + if (!isGroup && opts.noextglob !== true && peek() === "(" && peek(2) !== "?") { + extglobOpen("qmark", value); + continue; + } + if (prev && prev.type === "paren") { + const next = peek(); + let output = value; + if (next === "<" && !utils.supportsLookbehinds()) { + throw new Error("Node.js v10 or higher is required for regex lookbehinds"); + } + if (prev.value === "(" && !/[!=<:]/.test(next) || next === "<" && !/<([!=]|\w+>)/.test(remaining())) { + output = `\\${value}`; + } + push({ type: "text", value, output }); + continue; + } + if (opts.dot !== true && (prev.type === "slash" || prev.type === "bos")) { + push({ type: "qmark", value, output: QMARK_NO_DOT }); + continue; + } + push({ type: "qmark", value, output: QMARK }); + continue; + } + if (value === "!") { + if (opts.noextglob !== true && peek() === "(") { + if (peek(2) !== "?" || !/[!=<:]/.test(peek(3))) { + extglobOpen("negate", value); + continue; + } + } + if (opts.nonegate !== true && state.index === 0) { + negate(); + continue; + } + } + if (value === "+") { + if (opts.noextglob !== true && peek() === "(" && peek(2) !== "?") { + extglobOpen("plus", value); + continue; + } + if (prev && prev.value === "(" || opts.regex === false) { + push({ type: "plus", value, output: PLUS_LITERAL }); + continue; + } + if (prev && (prev.type === "bracket" || prev.type === "paren" || prev.type === "brace") || state.parens > 0) { + push({ type: "plus", value }); + continue; + } + push({ type: "plus", value: PLUS_LITERAL }); + continue; + } + if (value === "@") { + if (opts.noextglob !== true && peek() === "(" && peek(2) !== "?") { + push({ type: "at", extglob: true, value, output: "" }); + continue; + } + push({ type: "text", value }); + continue; + } + if (value !== "*") { + if (value === "$" || value === "^") { + value = `\\${value}`; + } + const match = REGEX_NON_SPECIAL_CHARS.exec(remaining()); + if (match) { + value += match[0]; + state.index += match[0].length; + } + push({ type: "text", value }); + continue; + } + if (prev && (prev.type === "globstar" || prev.star === true)) { + prev.type = "star"; + prev.star = true; + prev.value += value; + prev.output = star; + state.backtrack = true; + state.globstar = true; + consume(value); + continue; + } + let rest = remaining(); + if (opts.noextglob !== true && /^\([^?]/.test(rest)) { + extglobOpen("star", value); + continue; + } + if (prev.type === "star") { + if (opts.noglobstar === true) { + consume(value); + continue; + } + const prior = prev.prev; + const before = prior.prev; + const isStart = prior.type === "slash" || prior.type === "bos"; + const afterStar = before && (before.type === "star" || before.type === "globstar"); + if (opts.bash === true && (!isStart || rest[0] && rest[0] !== "/")) { + push({ type: "star", value, output: "" }); + continue; + } + const isBrace = state.braces > 0 && (prior.type === "comma" || prior.type === "brace"); + const isExtglob = extglobs.length && (prior.type === "pipe" || prior.type === "paren"); + if (!isStart && prior.type !== "paren" && !isBrace && !isExtglob) { + push({ type: "star", value, output: "" }); + continue; + } + while (rest.slice(0, 3) === "/**") { + const after = input[state.index + 4]; + if (after && after !== "/") { + break; + } + rest = rest.slice(3); + consume("/**", 3); + } + if (prior.type === "bos" && eos()) { + prev.type = "globstar"; + prev.value += value; + prev.output = globstar(opts); + state.output = prev.output; + state.globstar = true; + consume(value); + continue; + } + if (prior.type === "slash" && prior.prev.type !== "bos" && !afterStar && eos()) { + state.output = state.output.slice(0, -(prior.output + prev.output).length); + prior.output = `(?:${prior.output}`; + prev.type = "globstar"; + prev.output = globstar(opts) + (opts.strictSlashes ? ")" : "|$)"); + prev.value += value; + state.globstar = true; + state.output += prior.output + prev.output; + consume(value); + continue; + } + if (prior.type === "slash" && prior.prev.type !== "bos" && rest[0] === "/") { + const end = rest[1] !== void 0 ? "|$" : ""; + state.output = state.output.slice(0, -(prior.output + prev.output).length); + prior.output = `(?:${prior.output}`; + prev.type = "globstar"; + prev.output = `${globstar(opts)}${SLASH_LITERAL}|${SLASH_LITERAL}${end})`; + prev.value += value; + state.output += prior.output + prev.output; + state.globstar = true; + consume(value + advance()); + push({ type: "slash", value: "/", output: "" }); + continue; + } + if (prior.type === "bos" && rest[0] === "/") { + prev.type = "globstar"; + prev.value += value; + prev.output = `(?:^|${SLASH_LITERAL}|${globstar(opts)}${SLASH_LITERAL})`; + state.output = prev.output; + state.globstar = true; + consume(value + advance()); + push({ type: "slash", value: "/", output: "" }); + continue; + } + state.output = state.output.slice(0, -prev.output.length); + prev.type = "globstar"; + prev.output = globstar(opts); + prev.value += value; + state.output += prev.output; + state.globstar = true; + consume(value); + continue; + } + const token = { type: "star", value, output: star }; + if (opts.bash === true) { + token.output = ".*?"; + if (prev.type === "bos" || prev.type === "slash") { + token.output = nodot + token.output; + } + push(token); + continue; + } + if (prev && (prev.type === "bracket" || prev.type === "paren") && opts.regex === true) { + token.output = value; + push(token); + continue; + } + if (state.index === state.start || prev.type === "slash" || prev.type === "dot") { + if (prev.type === "dot") { + state.output += NO_DOT_SLASH; + prev.output += NO_DOT_SLASH; + } else if (opts.dot === true) { + state.output += NO_DOTS_SLASH; + prev.output += NO_DOTS_SLASH; + } else { + state.output += nodot; + prev.output += nodot; + } + if (peek() !== "*") { + state.output += ONE_CHAR; + prev.output += ONE_CHAR; + } + } + push(token); + } + while (state.brackets > 0) { + if (opts.strictBrackets === true) throw new SyntaxError(syntaxError("closing", "]")); + state.output = utils.escapeLast(state.output, "["); + decrement("brackets"); + } + while (state.parens > 0) { + if (opts.strictBrackets === true) throw new SyntaxError(syntaxError("closing", ")")); + state.output = utils.escapeLast(state.output, "("); + decrement("parens"); + } + while (state.braces > 0) { + if (opts.strictBrackets === true) throw new SyntaxError(syntaxError("closing", "}")); + state.output = utils.escapeLast(state.output, "{"); + decrement("braces"); + } + if (opts.strictSlashes !== true && (prev.type === "star" || prev.type === "bracket")) { + push({ type: "maybe_slash", value: "", output: `${SLASH_LITERAL}?` }); + } + if (state.backtrack === true) { + state.output = ""; + for (const token of state.tokens) { + state.output += token.output != null ? token.output : token.value; + if (token.suffix) { + state.output += token.suffix; + } + } + } + return state; + }; + parse.fastpaths = (input, options) => { + const opts = { ...options }; + const max = typeof opts.maxLength === "number" ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; + const len = input.length; + if (len > max) { + throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`); + } + input = REPLACEMENTS[input] || input; + const win32 = utils.isWindows(options); + const { + DOT_LITERAL, + SLASH_LITERAL, + ONE_CHAR, + DOTS_SLASH, + NO_DOT, + NO_DOTS, + NO_DOTS_SLASH, + STAR, + START_ANCHOR + } = constants.globChars(win32); + const nodot = opts.dot ? NO_DOTS : NO_DOT; + const slashDot = opts.dot ? NO_DOTS_SLASH : NO_DOT; + const capture = opts.capture ? "" : "?:"; + const state = { negated: false, prefix: "" }; + let star = opts.bash === true ? ".*?" : STAR; + if (opts.capture) { + star = `(${star})`; + } + const globstar = (opts2) => { + if (opts2.noglobstar === true) return star; + return `(${capture}(?:(?!${START_ANCHOR}${opts2.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`; + }; + const create = (str) => { + switch (str) { + case "*": + return `${nodot}${ONE_CHAR}${star}`; + case ".*": + return `${DOT_LITERAL}${ONE_CHAR}${star}`; + case "*.*": + return `${nodot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`; + case "*/*": + return `${nodot}${star}${SLASH_LITERAL}${ONE_CHAR}${slashDot}${star}`; + case "**": + return nodot + globstar(opts); + case "**/*": + return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${ONE_CHAR}${star}`; + case "**/*.*": + return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`; + case "**/.*": + return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${DOT_LITERAL}${ONE_CHAR}${star}`; + default: { + const match = /^(.*?)\.(\w+)$/.exec(str); + if (!match) return; + const source2 = create(match[1]); + if (!source2) return; + return source2 + DOT_LITERAL + match[2]; + } + } + }; + const output = utils.removePrefix(input, state); + let source = create(output); + if (source && opts.strictSlashes !== true) { + source += `${SLASH_LITERAL}?`; + } + return source; + }; + module2.exports = parse; + } +}); + +// node_modules/readdirp/node_modules/picomatch/lib/picomatch.js +var require_picomatch = __commonJS({ + "node_modules/readdirp/node_modules/picomatch/lib/picomatch.js"(exports2, module2) { + "use strict"; + var path3 = require("path"); + var scan = require_scan(); + var parse = require_parse(); + var utils = require_utils(); + var constants = require_constants(); + var isObject = (val) => val && typeof val === "object" && !Array.isArray(val); + var picomatch = (glob, options, returnState = false) => { + if (Array.isArray(glob)) { + const fns = glob.map((input) => picomatch(input, options, returnState)); + const arrayMatcher = (str) => { + for (const isMatch of fns) { + const state2 = isMatch(str); + if (state2) return state2; + } + return false; + }; + return arrayMatcher; + } + const isState = isObject(glob) && glob.tokens && glob.input; + if (glob === "" || typeof glob !== "string" && !isState) { + throw new TypeError("Expected pattern to be a non-empty string"); + } + const opts = options || {}; + const posix = utils.isWindows(options); + const regex = isState ? picomatch.compileRe(glob, options) : picomatch.makeRe(glob, options, false, true); + const state = regex.state; + delete regex.state; + let isIgnored = () => false; + if (opts.ignore) { + const ignoreOpts = { ...options, ignore: null, onMatch: null, onResult: null }; + isIgnored = picomatch(opts.ignore, ignoreOpts, returnState); + } + const matcher = (input, returnObject = false) => { + const { isMatch, match, output } = picomatch.test(input, regex, options, { glob, posix }); + const result = { glob, state, regex, posix, input, output, match, isMatch }; + if (typeof opts.onResult === "function") { + opts.onResult(result); + } + if (isMatch === false) { + result.isMatch = false; + return returnObject ? result : false; + } + if (isIgnored(input)) { + if (typeof opts.onIgnore === "function") { + opts.onIgnore(result); + } + result.isMatch = false; + return returnObject ? result : false; + } + if (typeof opts.onMatch === "function") { + opts.onMatch(result); + } + return returnObject ? result : true; + }; + if (returnState) { + matcher.state = state; + } + return matcher; + }; + picomatch.test = (input, regex, options, { glob, posix } = {}) => { + if (typeof input !== "string") { + throw new TypeError("Expected input to be a string"); + } + if (input === "") { + return { isMatch: false, output: "" }; + } + const opts = options || {}; + const format = opts.format || (posix ? utils.toPosixSlashes : null); + let match = input === glob; + let output = match && format ? format(input) : input; + if (match === false) { + output = format ? format(input) : input; + match = output === glob; + } + if (match === false || opts.capture === true) { + if (opts.matchBase === true || opts.basename === true) { + match = picomatch.matchBase(input, regex, options, posix); + } else { + match = regex.exec(output); + } + } + return { isMatch: Boolean(match), match, output }; + }; + picomatch.matchBase = (input, glob, options, posix = utils.isWindows(options)) => { + const regex = glob instanceof RegExp ? glob : picomatch.makeRe(glob, options); + return regex.test(path3.basename(input)); + }; + picomatch.isMatch = (str, patterns, options) => picomatch(patterns, options)(str); + picomatch.parse = (pattern, options) => { + if (Array.isArray(pattern)) return pattern.map((p) => picomatch.parse(p, options)); + return parse(pattern, { ...options, fastpaths: false }); + }; + picomatch.scan = (input, options) => scan(input, options); + picomatch.compileRe = (state, options, returnOutput = false, returnState = false) => { + if (returnOutput === true) { + return state.output; + } + const opts = options || {}; + const prepend = opts.contains ? "" : "^"; + const append = opts.contains ? "" : "$"; + let source = `${prepend}(?:${state.output})${append}`; + if (state && state.negated === true) { + source = `^(?!${source}).*$`; + } + const regex = picomatch.toRegex(source, options); + if (returnState === true) { + regex.state = state; + } + return regex; + }; + picomatch.makeRe = (input, options = {}, returnOutput = false, returnState = false) => { + if (!input || typeof input !== "string") { + throw new TypeError("Expected a non-empty string"); + } + let parsed = { negated: false, fastpaths: true }; + if (options.fastpaths !== false && (input[0] === "." || input[0] === "*")) { + parsed.output = parse.fastpaths(input, options); + } + if (!parsed.output) { + parsed = parse(input, options); + } + return picomatch.compileRe(parsed, options, returnOutput, returnState); + }; + picomatch.toRegex = (source, options) => { + try { + const opts = options || {}; + return new RegExp(source, opts.flags || (opts.nocase ? "i" : "")); + } catch (err) { + if (options && options.debug === true) throw err; + return /$^/; + } + }; + picomatch.constants = constants; + module2.exports = picomatch; + } +}); + +// node_modules/readdirp/node_modules/picomatch/index.js +var require_picomatch2 = __commonJS({ + "node_modules/readdirp/node_modules/picomatch/index.js"(exports2, module2) { + "use strict"; + module2.exports = require_picomatch(); + } +}); + +// node_modules/readdirp/index.js +var require_readdirp = __commonJS({ + "node_modules/readdirp/index.js"(exports2, module2) { + "use strict"; + var fs2 = require("fs"); + var { Readable } = require("stream"); + var sysPath = require("path"); + var { promisify } = require("util"); + var picomatch = require_picomatch2(); + var readdir = promisify(fs2.readdir); + var stat = promisify(fs2.stat); + var lstat = promisify(fs2.lstat); + var realpath = promisify(fs2.realpath); + var BANG = "!"; + var RECURSIVE_ERROR_CODE = "READDIRP_RECURSIVE_ERROR"; + var NORMAL_FLOW_ERRORS = /* @__PURE__ */ new Set(["ENOENT", "EPERM", "EACCES", "ELOOP", RECURSIVE_ERROR_CODE]); + var FILE_TYPE = "files"; + var DIR_TYPE = "directories"; + var FILE_DIR_TYPE = "files_directories"; + var EVERYTHING_TYPE = "all"; + var ALL_TYPES = [FILE_TYPE, DIR_TYPE, FILE_DIR_TYPE, EVERYTHING_TYPE]; + var isNormalFlowError = (error) => NORMAL_FLOW_ERRORS.has(error.code); + var [maj, min] = process.versions.node.split(".").slice(0, 2).map((n) => Number.parseInt(n, 10)); + var wantBigintFsStats = process.platform === "win32" && (maj > 10 || maj === 10 && min >= 5); + var normalizeFilter = (filter) => { + if (filter === void 0) return; + if (typeof filter === "function") return filter; + if (typeof filter === "string") { + const glob = picomatch(filter.trim()); + return (entry) => glob(entry.basename); + } + if (Array.isArray(filter)) { + const positive = []; + const negative = []; + for (const item of filter) { + const trimmed = item.trim(); + if (trimmed.charAt(0) === BANG) { + negative.push(picomatch(trimmed.slice(1))); + } else { + positive.push(picomatch(trimmed)); + } + } + if (negative.length > 0) { + if (positive.length > 0) { + return (entry) => positive.some((f) => f(entry.basename)) && !negative.some((f) => f(entry.basename)); + } + return (entry) => !negative.some((f) => f(entry.basename)); + } + return (entry) => positive.some((f) => f(entry.basename)); + } + }; + var ReaddirpStream = class _ReaddirpStream extends Readable { + static get defaultOptions() { + return { + root: ".", + /* eslint-disable no-unused-vars */ + fileFilter: (path3) => true, + directoryFilter: (path3) => true, + /* eslint-enable no-unused-vars */ + type: FILE_TYPE, + lstat: false, + depth: 2147483648, + alwaysStat: false + }; + } + constructor(options = {}) { + super({ + objectMode: true, + autoDestroy: true, + highWaterMark: options.highWaterMark || 4096 + }); + const opts = { ..._ReaddirpStream.defaultOptions, ...options }; + const { root, type } = opts; + this._fileFilter = normalizeFilter(opts.fileFilter); + this._directoryFilter = normalizeFilter(opts.directoryFilter); + const statMethod = opts.lstat ? lstat : stat; + if (wantBigintFsStats) { + this._stat = (path3) => statMethod(path3, { bigint: true }); + } else { + this._stat = statMethod; + } + this._maxDepth = opts.depth; + this._wantsDir = [DIR_TYPE, FILE_DIR_TYPE, EVERYTHING_TYPE].includes(type); + this._wantsFile = [FILE_TYPE, FILE_DIR_TYPE, EVERYTHING_TYPE].includes(type); + this._wantsEverything = type === EVERYTHING_TYPE; + this._root = sysPath.resolve(root); + this._isDirent = "Dirent" in fs2 && !opts.alwaysStat; + this._statsProp = this._isDirent ? "dirent" : "stats"; + this._rdOptions = { encoding: "utf8", withFileTypes: this._isDirent }; + this.parents = [this._exploreDir(root, 1)]; + this.reading = false; + this.parent = void 0; + } + async _read(batch) { + if (this.reading) return; + this.reading = true; + try { + while (!this.destroyed && batch > 0) { + const { path: path3, depth, files = [] } = this.parent || {}; + if (files.length > 0) { + const slice = files.splice(0, batch).map((dirent) => this._formatEntry(dirent, path3)); + for (const entry of await Promise.all(slice)) { + if (this.destroyed) return; + const entryType = await this._getEntryType(entry); + if (entryType === "directory" && this._directoryFilter(entry)) { + if (depth <= this._maxDepth) { + this.parents.push(this._exploreDir(entry.fullPath, depth + 1)); + } + if (this._wantsDir) { + this.push(entry); + batch--; + } + } else if ((entryType === "file" || this._includeAsFile(entry)) && this._fileFilter(entry)) { + if (this._wantsFile) { + this.push(entry); + batch--; + } + } + } + } else { + const parent = this.parents.pop(); + if (!parent) { + this.push(null); + break; + } + this.parent = await parent; + if (this.destroyed) return; + } + } + } catch (error) { + this.destroy(error); + } finally { + this.reading = false; + } + } + async _exploreDir(path3, depth) { + let files; + try { + files = await readdir(path3, this._rdOptions); + } catch (error) { + this._onError(error); + } + return { files, depth, path: path3 }; + } + async _formatEntry(dirent, path3) { + let entry; + try { + const basename = this._isDirent ? dirent.name : dirent; + const fullPath = sysPath.resolve(sysPath.join(path3, basename)); + entry = { path: sysPath.relative(this._root, fullPath), fullPath, basename }; + entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath); + } catch (err) { + this._onError(err); + } + return entry; + } + _onError(err) { + if (isNormalFlowError(err) && !this.destroyed) { + this.emit("warn", err); + } else { + this.destroy(err); + } + } + async _getEntryType(entry) { + const stats = entry && entry[this._statsProp]; + if (!stats) { + return; + } + if (stats.isFile()) { + return "file"; + } + if (stats.isDirectory()) { + return "directory"; + } + if (stats && stats.isSymbolicLink()) { + const full = entry.fullPath; + try { + const entryRealPath = await realpath(full); + const entryRealPathStats = await lstat(entryRealPath); + if (entryRealPathStats.isFile()) { + return "file"; + } + if (entryRealPathStats.isDirectory()) { + const len = entryRealPath.length; + if (full.startsWith(entryRealPath) && full.substr(len, 1) === sysPath.sep) { + const recursiveError = new Error( + `Circular symlink detected: "${full}" points to "${entryRealPath}"` + ); + recursiveError.code = RECURSIVE_ERROR_CODE; + return this._onError(recursiveError); + } + return "directory"; + } + } catch (error) { + this._onError(error); + } + } + } + _includeAsFile(entry) { + const stats = entry && entry[this._statsProp]; + return stats && this._wantsEverything && !stats.isDirectory(); + } + }; + var readdirp = (root, options = {}) => { + let type = options.entryType || options.type; + if (type === "both") type = FILE_DIR_TYPE; + if (type) options.type = type; + if (!root) { + throw new Error("readdirp: root argument is required. Usage: readdirp(root, options)"); + } else if (typeof root !== "string") { + throw new TypeError("readdirp: root argument must be a string. Usage: readdirp(root, options)"); + } else if (type && !ALL_TYPES.includes(type)) { + throw new Error(`readdirp: Invalid type passed. Use one of ${ALL_TYPES.join(", ")}`); + } + options.root = root; + return new ReaddirpStream(options); + }; + var readdirpPromise = (root, options = {}) => { + return new Promise((resolve, reject) => { + const files = []; + readdirp(root, options).on("data", (entry) => files.push(entry)).on("end", () => resolve(files)).on("error", (error) => reject(error)); + }); + }; + readdirp.promise = readdirpPromise; + readdirp.ReaddirpStream = ReaddirpStream; + readdirp.default = readdirp; + module2.exports = readdirp; + } +}); + +// node_modules/anymatch/node_modules/picomatch/lib/constants.js +var require_constants2 = __commonJS({ + "node_modules/anymatch/node_modules/picomatch/lib/constants.js"(exports2, module2) { + "use strict"; + var path3 = require("path"); + var WIN_SLASH = "\\\\/"; + var WIN_NO_SLASH = `[^${WIN_SLASH}]`; + var DEFAULT_MAX_EXTGLOB_RECURSION = 0; + var DOT_LITERAL = "\\."; + var PLUS_LITERAL = "\\+"; + var QMARK_LITERAL = "\\?"; + var SLASH_LITERAL = "\\/"; + var ONE_CHAR = "(?=.)"; + var QMARK = "[^/]"; + var END_ANCHOR = `(?:${SLASH_LITERAL}|$)`; + var START_ANCHOR = `(?:^|${SLASH_LITERAL})`; + var DOTS_SLASH = `${DOT_LITERAL}{1,2}${END_ANCHOR}`; + var NO_DOT = `(?!${DOT_LITERAL})`; + var NO_DOTS = `(?!${START_ANCHOR}${DOTS_SLASH})`; + var NO_DOT_SLASH = `(?!${DOT_LITERAL}{0,1}${END_ANCHOR})`; + var NO_DOTS_SLASH = `(?!${DOTS_SLASH})`; + var QMARK_NO_DOT = `[^.${SLASH_LITERAL}]`; + var STAR = `${QMARK}*?`; + var POSIX_CHARS = { + DOT_LITERAL, + PLUS_LITERAL, + QMARK_LITERAL, + SLASH_LITERAL, + ONE_CHAR, + QMARK, + END_ANCHOR, + DOTS_SLASH, + NO_DOT, + NO_DOTS, + NO_DOT_SLASH, + NO_DOTS_SLASH, + QMARK_NO_DOT, + STAR, + START_ANCHOR + }; + var WINDOWS_CHARS = { + ...POSIX_CHARS, + SLASH_LITERAL: `[${WIN_SLASH}]`, + QMARK: WIN_NO_SLASH, + STAR: `${WIN_NO_SLASH}*?`, + DOTS_SLASH: `${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$)`, + NO_DOT: `(?!${DOT_LITERAL})`, + NO_DOTS: `(?!(?:^|[${WIN_SLASH}])${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`, + NO_DOT_SLASH: `(?!${DOT_LITERAL}{0,1}(?:[${WIN_SLASH}]|$))`, + NO_DOTS_SLASH: `(?!${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`, + QMARK_NO_DOT: `[^.${WIN_SLASH}]`, + START_ANCHOR: `(?:^|[${WIN_SLASH}])`, + END_ANCHOR: `(?:[${WIN_SLASH}]|$)` + }; + var POSIX_REGEX_SOURCE = { + __proto__: null, + alnum: "a-zA-Z0-9", + alpha: "a-zA-Z", + ascii: "\\x00-\\x7F", + blank: " \\t", + cntrl: "\\x00-\\x1F\\x7F", + digit: "0-9", + graph: "\\x21-\\x7E", + lower: "a-z", + print: "\\x20-\\x7E ", + punct: "\\-!\"#$%&'()\\*+,./:;<=>?@[\\]^_`{|}~", + space: " \\t\\r\\n\\v\\f", + upper: "A-Z", + word: "A-Za-z0-9_", + xdigit: "A-Fa-f0-9" + }; + module2.exports = { + DEFAULT_MAX_EXTGLOB_RECURSION, + MAX_LENGTH: 1024 * 64, + POSIX_REGEX_SOURCE, + // regular expressions + REGEX_BACKSLASH: /\\(?![*+?^${}(|)[\]])/g, + REGEX_NON_SPECIAL_CHARS: /^[^@![\].,$*+?^{}()|\\/]+/, + REGEX_SPECIAL_CHARS: /[-*+?.^${}(|)[\]]/, + REGEX_SPECIAL_CHARS_BACKREF: /(\\?)((\W)(\3*))/g, + REGEX_SPECIAL_CHARS_GLOBAL: /([-*+?.^${}(|)[\]])/g, + REGEX_REMOVE_BACKSLASH: /(?:\[.*?[^\\]\]|\\(?=.))/g, + // Replace globs with equivalent patterns to reduce parsing time. + REPLACEMENTS: { + __proto__: null, + "***": "*", + "**/**": "**", + "**/**/**": "**" + }, + // Digits + CHAR_0: 48, + /* 0 */ + CHAR_9: 57, + /* 9 */ + // Alphabet chars. + CHAR_UPPERCASE_A: 65, + /* A */ + CHAR_LOWERCASE_A: 97, + /* a */ + CHAR_UPPERCASE_Z: 90, + /* Z */ + CHAR_LOWERCASE_Z: 122, + /* z */ + CHAR_LEFT_PARENTHESES: 40, + /* ( */ + CHAR_RIGHT_PARENTHESES: 41, + /* ) */ + CHAR_ASTERISK: 42, + /* * */ + // Non-alphabetic chars. + CHAR_AMPERSAND: 38, + /* & */ + CHAR_AT: 64, + /* @ */ + CHAR_BACKWARD_SLASH: 92, + /* \ */ + CHAR_CARRIAGE_RETURN: 13, + /* \r */ + CHAR_CIRCUMFLEX_ACCENT: 94, + /* ^ */ + CHAR_COLON: 58, + /* : */ + CHAR_COMMA: 44, + /* , */ + CHAR_DOT: 46, + /* . */ + CHAR_DOUBLE_QUOTE: 34, + /* " */ + CHAR_EQUAL: 61, + /* = */ + CHAR_EXCLAMATION_MARK: 33, + /* ! */ + CHAR_FORM_FEED: 12, + /* \f */ + CHAR_FORWARD_SLASH: 47, + /* / */ + CHAR_GRAVE_ACCENT: 96, + /* ` */ + CHAR_HASH: 35, + /* # */ + CHAR_HYPHEN_MINUS: 45, + /* - */ + CHAR_LEFT_ANGLE_BRACKET: 60, + /* < */ + CHAR_LEFT_CURLY_BRACE: 123, + /* { */ + CHAR_LEFT_SQUARE_BRACKET: 91, + /* [ */ + CHAR_LINE_FEED: 10, + /* \n */ + CHAR_NO_BREAK_SPACE: 160, + /* \u00A0 */ + CHAR_PERCENT: 37, + /* % */ + CHAR_PLUS: 43, + /* + */ + CHAR_QUESTION_MARK: 63, + /* ? */ + CHAR_RIGHT_ANGLE_BRACKET: 62, + /* > */ + CHAR_RIGHT_CURLY_BRACE: 125, + /* } */ + CHAR_RIGHT_SQUARE_BRACKET: 93, + /* ] */ + CHAR_SEMICOLON: 59, + /* ; */ + CHAR_SINGLE_QUOTE: 39, + /* ' */ + CHAR_SPACE: 32, + /* */ + CHAR_TAB: 9, + /* \t */ + CHAR_UNDERSCORE: 95, + /* _ */ + CHAR_VERTICAL_LINE: 124, + /* | */ + CHAR_ZERO_WIDTH_NOBREAK_SPACE: 65279, + /* \uFEFF */ + SEP: path3.sep, + /** + * Create EXTGLOB_CHARS + */ + extglobChars(chars) { + return { + "!": { type: "negate", open: "(?:(?!(?:", close: `))${chars.STAR})` }, + "?": { type: "qmark", open: "(?:", close: ")?" }, + "+": { type: "plus", open: "(?:", close: ")+" }, + "*": { type: "star", open: "(?:", close: ")*" }, + "@": { type: "at", open: "(?:", close: ")" } + }; + }, + /** + * Create GLOB_CHARS + */ + globChars(win32) { + return win32 === true ? WINDOWS_CHARS : POSIX_CHARS; + } + }; + } +}); + +// node_modules/anymatch/node_modules/picomatch/lib/utils.js +var require_utils2 = __commonJS({ + "node_modules/anymatch/node_modules/picomatch/lib/utils.js"(exports2) { + "use strict"; + var path3 = require("path"); + var win32 = process.platform === "win32"; + var { + REGEX_BACKSLASH, + REGEX_REMOVE_BACKSLASH, + REGEX_SPECIAL_CHARS, + REGEX_SPECIAL_CHARS_GLOBAL + } = require_constants2(); + exports2.isObject = (val) => val !== null && typeof val === "object" && !Array.isArray(val); + exports2.hasRegexChars = (str) => REGEX_SPECIAL_CHARS.test(str); + exports2.isRegexChar = (str) => str.length === 1 && exports2.hasRegexChars(str); + exports2.escapeRegex = (str) => str.replace(REGEX_SPECIAL_CHARS_GLOBAL, "\\$1"); + exports2.toPosixSlashes = (str) => str.replace(REGEX_BACKSLASH, "/"); + exports2.removeBackslashes = (str) => { + return str.replace(REGEX_REMOVE_BACKSLASH, (match) => { + return match === "\\" ? "" : match; + }); + }; + exports2.supportsLookbehinds = () => { + const segs = process.version.slice(1).split(".").map(Number); + if (segs.length === 3 && segs[0] >= 9 || segs[0] === 8 && segs[1] >= 10) { + return true; + } + return false; + }; + exports2.isWindows = (options) => { + if (options && typeof options.windows === "boolean") { + return options.windows; + } + return win32 === true || path3.sep === "\\"; + }; + exports2.escapeLast = (input, char, lastIdx) => { + const idx = input.lastIndexOf(char, lastIdx); + if (idx === -1) return input; + if (input[idx - 1] === "\\") return exports2.escapeLast(input, char, idx - 1); + return `${input.slice(0, idx)}\\${input.slice(idx)}`; + }; + exports2.removePrefix = (input, state = {}) => { + let output = input; + if (output.startsWith("./")) { + output = output.slice(2); + state.prefix = "./"; + } + return output; + }; + exports2.wrapOutput = (input, state = {}, options = {}) => { + const prepend = options.contains ? "" : "^"; + const append = options.contains ? "" : "$"; + let output = `${prepend}(?:${input})${append}`; + if (state.negated === true) { + output = `(?:^(?!${output}).*$)`; + } + return output; + }; + } +}); + +// node_modules/anymatch/node_modules/picomatch/lib/scan.js +var require_scan2 = __commonJS({ + "node_modules/anymatch/node_modules/picomatch/lib/scan.js"(exports2, module2) { + "use strict"; + var utils = require_utils2(); + var { + CHAR_ASTERISK, + /* * */ + CHAR_AT, + /* @ */ + CHAR_BACKWARD_SLASH, + /* \ */ + CHAR_COMMA, + /* , */ + CHAR_DOT, + /* . */ + CHAR_EXCLAMATION_MARK, + /* ! */ + CHAR_FORWARD_SLASH, + /* / */ + CHAR_LEFT_CURLY_BRACE, + /* { */ + CHAR_LEFT_PARENTHESES, + /* ( */ + CHAR_LEFT_SQUARE_BRACKET, + /* [ */ + CHAR_PLUS, + /* + */ + CHAR_QUESTION_MARK, + /* ? */ + CHAR_RIGHT_CURLY_BRACE, + /* } */ + CHAR_RIGHT_PARENTHESES, + /* ) */ + CHAR_RIGHT_SQUARE_BRACKET + /* ] */ + } = require_constants2(); + var isPathSeparator = (code) => { + return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH; + }; + var depth = (token) => { + if (token.isPrefix !== true) { + token.depth = token.isGlobstar ? Infinity : 1; + } + }; + var scan = (input, options) => { + const opts = options || {}; + const length = input.length - 1; + const scanToEnd = opts.parts === true || opts.scanToEnd === true; + const slashes = []; + const tokens = []; + const parts = []; + let str = input; + let index = -1; + let start = 0; + let lastIndex = 0; + let isBrace = false; + let isBracket = false; + let isGlob = false; + let isExtglob = false; + let isGlobstar = false; + let braceEscaped = false; + let backslashes = false; + let negated = false; + let negatedExtglob = false; + let finished = false; + let braces = 0; + let prev; + let code; + let token = { value: "", depth: 0, isGlob: false }; + const eos = () => index >= length; + const peek = () => str.charCodeAt(index + 1); + const advance = () => { + prev = code; + return str.charCodeAt(++index); + }; + while (index < length) { + code = advance(); + let next; + if (code === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + code = advance(); + if (code === CHAR_LEFT_CURLY_BRACE) { + braceEscaped = true; + } + continue; + } + if (braceEscaped === true || code === CHAR_LEFT_CURLY_BRACE) { + braces++; + while (eos() !== true && (code = advance())) { + if (code === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + advance(); + continue; + } + if (code === CHAR_LEFT_CURLY_BRACE) { + braces++; + continue; + } + if (braceEscaped !== true && code === CHAR_DOT && (code = advance()) === CHAR_DOT) { + isBrace = token.isBrace = true; + isGlob = token.isGlob = true; + finished = true; + if (scanToEnd === true) { + continue; + } + break; + } + if (braceEscaped !== true && code === CHAR_COMMA) { + isBrace = token.isBrace = true; + isGlob = token.isGlob = true; + finished = true; + if (scanToEnd === true) { + continue; + } + break; + } + if (code === CHAR_RIGHT_CURLY_BRACE) { + braces--; + if (braces === 0) { + braceEscaped = false; + isBrace = token.isBrace = true; + finished = true; + break; + } + } + } + if (scanToEnd === true) { + continue; + } + break; + } + if (code === CHAR_FORWARD_SLASH) { + slashes.push(index); + tokens.push(token); + token = { value: "", depth: 0, isGlob: false }; + if (finished === true) continue; + if (prev === CHAR_DOT && index === start + 1) { + start += 2; + continue; + } + lastIndex = index + 1; + continue; + } + if (opts.noext !== true) { + const isExtglobChar = code === CHAR_PLUS || code === CHAR_AT || code === CHAR_ASTERISK || code === CHAR_QUESTION_MARK || code === CHAR_EXCLAMATION_MARK; + if (isExtglobChar === true && peek() === CHAR_LEFT_PARENTHESES) { + isGlob = token.isGlob = true; + isExtglob = token.isExtglob = true; + finished = true; + if (code === CHAR_EXCLAMATION_MARK && index === start) { + negatedExtglob = true; + } + if (scanToEnd === true) { + while (eos() !== true && (code = advance())) { + if (code === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + code = advance(); + continue; + } + if (code === CHAR_RIGHT_PARENTHESES) { + isGlob = token.isGlob = true; + finished = true; + break; + } + } + continue; + } + break; + } + } + if (code === CHAR_ASTERISK) { + if (prev === CHAR_ASTERISK) isGlobstar = token.isGlobstar = true; + isGlob = token.isGlob = true; + finished = true; + if (scanToEnd === true) { + continue; + } + break; + } + if (code === CHAR_QUESTION_MARK) { + isGlob = token.isGlob = true; + finished = true; + if (scanToEnd === true) { + continue; + } + break; + } + if (code === CHAR_LEFT_SQUARE_BRACKET) { + while (eos() !== true && (next = advance())) { + if (next === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + advance(); + continue; + } + if (next === CHAR_RIGHT_SQUARE_BRACKET) { + isBracket = token.isBracket = true; + isGlob = token.isGlob = true; + finished = true; + break; + } + } + if (scanToEnd === true) { + continue; + } + break; + } + if (opts.nonegate !== true && code === CHAR_EXCLAMATION_MARK && index === start) { + negated = token.negated = true; + start++; + continue; + } + if (opts.noparen !== true && code === CHAR_LEFT_PARENTHESES) { + isGlob = token.isGlob = true; + if (scanToEnd === true) { + while (eos() !== true && (code = advance())) { + if (code === CHAR_LEFT_PARENTHESES) { + backslashes = token.backslashes = true; + code = advance(); + continue; + } + if (code === CHAR_RIGHT_PARENTHESES) { + finished = true; + break; + } + } + continue; + } + break; + } + if (isGlob === true) { + finished = true; + if (scanToEnd === true) { + continue; + } + break; + } + } + if (opts.noext === true) { + isExtglob = false; + isGlob = false; + } + let base = str; + let prefix = ""; + let glob = ""; + if (start > 0) { + prefix = str.slice(0, start); + str = str.slice(start); + lastIndex -= start; + } + if (base && isGlob === true && lastIndex > 0) { + base = str.slice(0, lastIndex); + glob = str.slice(lastIndex); + } else if (isGlob === true) { + base = ""; + glob = str; + } else { + base = str; + } + if (base && base !== "" && base !== "/" && base !== str) { + if (isPathSeparator(base.charCodeAt(base.length - 1))) { + base = base.slice(0, -1); + } + } + if (opts.unescape === true) { + if (glob) glob = utils.removeBackslashes(glob); + if (base && backslashes === true) { + base = utils.removeBackslashes(base); + } + } + const state = { + prefix, + input, + start, + base, + glob, + isBrace, + isBracket, + isGlob, + isExtglob, + isGlobstar, + negated, + negatedExtglob + }; + if (opts.tokens === true) { + state.maxDepth = 0; + if (!isPathSeparator(code)) { + tokens.push(token); + } + state.tokens = tokens; + } + if (opts.parts === true || opts.tokens === true) { + let prevIndex; + for (let idx = 0; idx < slashes.length; idx++) { + const n = prevIndex ? prevIndex + 1 : start; + const i = slashes[idx]; + const value = input.slice(n, i); + if (opts.tokens) { + if (idx === 0 && start !== 0) { + tokens[idx].isPrefix = true; + tokens[idx].value = prefix; + } else { + tokens[idx].value = value; + } + depth(tokens[idx]); + state.maxDepth += tokens[idx].depth; + } + if (idx !== 0 || value !== "") { + parts.push(value); + } + prevIndex = i; + } + if (prevIndex && prevIndex + 1 < input.length) { + const value = input.slice(prevIndex + 1); + parts.push(value); + if (opts.tokens) { + tokens[tokens.length - 1].value = value; + depth(tokens[tokens.length - 1]); + state.maxDepth += tokens[tokens.length - 1].depth; + } + } + state.slashes = slashes; + state.parts = parts; + } + return state; + }; + module2.exports = scan; + } +}); + +// node_modules/anymatch/node_modules/picomatch/lib/parse.js +var require_parse2 = __commonJS({ + "node_modules/anymatch/node_modules/picomatch/lib/parse.js"(exports2, module2) { + "use strict"; + var constants = require_constants2(); + var utils = require_utils2(); + var { + MAX_LENGTH, + POSIX_REGEX_SOURCE, + REGEX_NON_SPECIAL_CHARS, + REGEX_SPECIAL_CHARS_BACKREF, + REPLACEMENTS + } = constants; + var expandRange = (args, options) => { + if (typeof options.expandRange === "function") { + return options.expandRange(...args, options); + } + args.sort(); + const value = `[${args.join("-")}]`; + try { + new RegExp(value); + } catch (ex) { + return args.map((v) => utils.escapeRegex(v)).join(".."); + } + return value; + }; + var syntaxError = (type, char) => { + return `Missing ${type}: "${char}" - use "\\\\${char}" to match literal characters`; + }; + var splitTopLevel = (input) => { + const parts = []; + let bracket = 0; + let paren = 0; + let quote = 0; + let value = ""; + let escaped = false; + for (const ch of input) { + if (escaped === true) { + value += ch; + escaped = false; + continue; + } + if (ch === "\\") { + value += ch; + escaped = true; + continue; + } + if (ch === '"') { + quote = quote === 1 ? 0 : 1; + value += ch; + continue; + } + if (quote === 0) { + if (ch === "[") { + bracket++; + } else if (ch === "]" && bracket > 0) { + bracket--; + } else if (bracket === 0) { + if (ch === "(") { + paren++; + } else if (ch === ")" && paren > 0) { + paren--; + } else if (ch === "|" && paren === 0) { + parts.push(value); + value = ""; + continue; + } + } + } + value += ch; + } + parts.push(value); + return parts; + }; + var isPlainBranch = (branch) => { + let escaped = false; + for (const ch of branch) { + if (escaped === true) { + escaped = false; + continue; + } + if (ch === "\\") { + escaped = true; + continue; + } + if (/[?*+@!()[\]{}]/.test(ch)) { + return false; + } + } + return true; + }; + var normalizeSimpleBranch = (branch) => { + let value = branch.trim(); + let changed = true; + while (changed === true) { + changed = false; + if (/^@\([^\\()[\]{}|]+\)$/.test(value)) { + value = value.slice(2, -1); + changed = true; + } + } + if (!isPlainBranch(value)) { + return; + } + return value.replace(/\\(.)/g, "$1"); + }; + var hasRepeatedCharPrefixOverlap = (branches) => { + const values = branches.map(normalizeSimpleBranch).filter(Boolean); + for (let i = 0; i < values.length; i++) { + for (let j = i + 1; j < values.length; j++) { + const a = values[i]; + const b = values[j]; + const char = a[0]; + if (!char || a !== char.repeat(a.length) || b !== char.repeat(b.length)) { + continue; + } + if (a === b || a.startsWith(b) || b.startsWith(a)) { + return true; + } + } + } + return false; + }; + var parseRepeatedExtglob = (pattern, requireEnd = true) => { + if (pattern[0] !== "+" && pattern[0] !== "*" || pattern[1] !== "(") { + return; + } + let bracket = 0; + let paren = 0; + let quote = 0; + let escaped = false; + for (let i = 1; i < pattern.length; i++) { + const ch = pattern[i]; + if (escaped === true) { + escaped = false; + continue; + } + if (ch === "\\") { + escaped = true; + continue; + } + if (ch === '"') { + quote = quote === 1 ? 0 : 1; + continue; + } + if (quote === 1) { + continue; + } + if (ch === "[") { + bracket++; + continue; + } + if (ch === "]" && bracket > 0) { + bracket--; + continue; + } + if (bracket > 0) { + continue; + } + if (ch === "(") { + paren++; + continue; + } + if (ch === ")") { + paren--; + if (paren === 0) { + if (requireEnd === true && i !== pattern.length - 1) { + return; + } + return { + type: pattern[0], + body: pattern.slice(2, i), + end: i + }; + } + } + } + }; + var getStarExtglobSequenceOutput = (pattern) => { + let index = 0; + const chars = []; + while (index < pattern.length) { + const match = parseRepeatedExtglob(pattern.slice(index), false); + if (!match || match.type !== "*") { + return; + } + const branches = splitTopLevel(match.body).map((branch2) => branch2.trim()); + if (branches.length !== 1) { + return; + } + const branch = normalizeSimpleBranch(branches[0]); + if (!branch || branch.length !== 1) { + return; + } + chars.push(branch); + index += match.end + 1; + } + if (chars.length < 1) { + return; + } + const source = chars.length === 1 ? utils.escapeRegex(chars[0]) : `[${chars.map((ch) => utils.escapeRegex(ch)).join("")}]`; + return `${source}*`; + }; + var repeatedExtglobRecursion = (pattern) => { + let depth = 0; + let value = pattern.trim(); + let match = parseRepeatedExtglob(value); + while (match) { + depth++; + value = match.body.trim(); + match = parseRepeatedExtglob(value); + } + return depth; + }; + var analyzeRepeatedExtglob = (body, options) => { + if (options.maxExtglobRecursion === false) { + return { risky: false }; + } + const max = typeof options.maxExtglobRecursion === "number" ? options.maxExtglobRecursion : constants.DEFAULT_MAX_EXTGLOB_RECURSION; + const branches = splitTopLevel(body).map((branch) => branch.trim()); + if (branches.length > 1) { + if (branches.some((branch) => branch === "") || branches.some((branch) => /^[*?]+$/.test(branch)) || hasRepeatedCharPrefixOverlap(branches)) { + return { risky: true }; + } + } + for (const branch of branches) { + const safeOutput = getStarExtglobSequenceOutput(branch); + if (safeOutput) { + return { risky: true, safeOutput }; + } + if (repeatedExtglobRecursion(branch) > max) { + return { risky: true }; + } + } + return { risky: false }; + }; + var parse = (input, options) => { + if (typeof input !== "string") { + throw new TypeError("Expected a string"); + } + input = REPLACEMENTS[input] || input; + const opts = { ...options }; + const max = typeof opts.maxLength === "number" ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; + let len = input.length; + if (len > max) { + throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`); + } + const bos = { type: "bos", value: "", output: opts.prepend || "" }; + const tokens = [bos]; + const capture = opts.capture ? "" : "?:"; + const win32 = utils.isWindows(options); + const PLATFORM_CHARS = constants.globChars(win32); + const EXTGLOB_CHARS = constants.extglobChars(PLATFORM_CHARS); + const { + DOT_LITERAL, + PLUS_LITERAL, + SLASH_LITERAL, + ONE_CHAR, + DOTS_SLASH, + NO_DOT, + NO_DOT_SLASH, + NO_DOTS_SLASH, + QMARK, + QMARK_NO_DOT, + STAR, + START_ANCHOR + } = PLATFORM_CHARS; + const globstar = (opts2) => { + return `(${capture}(?:(?!${START_ANCHOR}${opts2.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`; + }; + const nodot = opts.dot ? "" : NO_DOT; + const qmarkNoDot = opts.dot ? QMARK : QMARK_NO_DOT; + let star = opts.bash === true ? globstar(opts) : STAR; + if (opts.capture) { + star = `(${star})`; + } + if (typeof opts.noext === "boolean") { + opts.noextglob = opts.noext; + } + const state = { + input, + index: -1, + start: 0, + dot: opts.dot === true, + consumed: "", + output: "", + prefix: "", + backtrack: false, + negated: false, + brackets: 0, + braces: 0, + parens: 0, + quotes: 0, + globstar: false, + tokens + }; + input = utils.removePrefix(input, state); + len = input.length; + const extglobs = []; + const braces = []; + const stack = []; + let prev = bos; + let value; + const eos = () => state.index === len - 1; + const peek = state.peek = (n = 1) => input[state.index + n]; + const advance = state.advance = () => input[++state.index] || ""; + const remaining = () => input.slice(state.index + 1); + const consume = (value2 = "", num = 0) => { + state.consumed += value2; + state.index += num; + }; + const append = (token) => { + state.output += token.output != null ? token.output : token.value; + consume(token.value); + }; + const negate = () => { + let count = 1; + while (peek() === "!" && (peek(2) !== "(" || peek(3) === "?")) { + advance(); + state.start++; + count++; + } + if (count % 2 === 0) { + return false; + } + state.negated = true; + state.start++; + return true; + }; + const increment = (type) => { + state[type]++; + stack.push(type); + }; + const decrement = (type) => { + state[type]--; + stack.pop(); + }; + const push = (tok) => { + if (prev.type === "globstar") { + const isBrace = state.braces > 0 && (tok.type === "comma" || tok.type === "brace"); + const isExtglob = tok.extglob === true || extglobs.length && (tok.type === "pipe" || tok.type === "paren"); + if (tok.type !== "slash" && tok.type !== "paren" && !isBrace && !isExtglob) { + state.output = state.output.slice(0, -prev.output.length); + prev.type = "star"; + prev.value = "*"; + prev.output = star; + state.output += prev.output; + } + } + if (extglobs.length && tok.type !== "paren") { + extglobs[extglobs.length - 1].inner += tok.value; + } + if (tok.value || tok.output) append(tok); + if (prev && prev.type === "text" && tok.type === "text") { + prev.value += tok.value; + prev.output = (prev.output || "") + tok.value; + return; + } + tok.prev = prev; + tokens.push(tok); + prev = tok; + }; + const extglobOpen = (type, value2) => { + const token = { ...EXTGLOB_CHARS[value2], conditions: 1, inner: "" }; + token.prev = prev; + token.parens = state.parens; + token.output = state.output; + token.startIndex = state.index; + token.tokensIndex = tokens.length; + const output = (opts.capture ? "(" : "") + token.open; + increment("parens"); + push({ type, value: value2, output: state.output ? "" : ONE_CHAR }); + push({ type: "paren", extglob: true, value: advance(), output }); + extglobs.push(token); + }; + const extglobClose = (token) => { + const literal = input.slice(token.startIndex, state.index + 1); + const body = input.slice(token.startIndex + 2, state.index); + const analysis = analyzeRepeatedExtglob(body, opts); + if ((token.type === "plus" || token.type === "star") && analysis.risky) { + const safeOutput = analysis.safeOutput ? (token.output ? "" : ONE_CHAR) + (opts.capture ? `(${analysis.safeOutput})` : analysis.safeOutput) : void 0; + const open = tokens[token.tokensIndex]; + open.type = "text"; + open.value = literal; + open.output = safeOutput || utils.escapeRegex(literal); + for (let i = token.tokensIndex + 1; i < tokens.length; i++) { + tokens[i].value = ""; + tokens[i].output = ""; + delete tokens[i].suffix; + } + state.output = token.output + open.output; + state.backtrack = true; + push({ type: "paren", extglob: true, value, output: "" }); + decrement("parens"); + return; + } + let output = token.close + (opts.capture ? ")" : ""); + let rest; + if (token.type === "negate") { + let extglobStar = star; + if (token.inner && token.inner.length > 1 && token.inner.includes("/")) { + extglobStar = globstar(opts); + } + if (extglobStar !== star || eos() || /^\)+$/.test(remaining())) { + output = token.close = `)$))${extglobStar}`; + } + if (token.inner.includes("*") && (rest = remaining()) && /^\.[^\\/.]+$/.test(rest)) { + const expression = parse(rest, { ...options, fastpaths: false }).output; + output = token.close = `)${expression})${extglobStar})`; + } + if (token.prev.type === "bos") { + state.negatedExtglob = true; + } + } + push({ type: "paren", extglob: true, value, output }); + decrement("parens"); + }; + if (opts.fastpaths !== false && !/(^[*!]|[/()[\]{}"])/.test(input)) { + let backslashes = false; + let output = input.replace(REGEX_SPECIAL_CHARS_BACKREF, (m, esc, chars, first, rest, index) => { + if (first === "\\") { + backslashes = true; + return m; + } + if (first === "?") { + if (esc) { + return esc + first + (rest ? QMARK.repeat(rest.length) : ""); + } + if (index === 0) { + return qmarkNoDot + (rest ? QMARK.repeat(rest.length) : ""); + } + return QMARK.repeat(chars.length); + } + if (first === ".") { + return DOT_LITERAL.repeat(chars.length); + } + if (first === "*") { + if (esc) { + return esc + first + (rest ? star : ""); + } + return star; + } + return esc ? m : `\\${m}`; + }); + if (backslashes === true) { + if (opts.unescape === true) { + output = output.replace(/\\/g, ""); + } else { + output = output.replace(/\\+/g, (m) => { + return m.length % 2 === 0 ? "\\\\" : m ? "\\" : ""; + }); + } + } + if (output === input && opts.contains === true) { + state.output = input; + return state; + } + state.output = utils.wrapOutput(output, state, options); + return state; + } + while (!eos()) { + value = advance(); + if (value === "\0") { + continue; + } + if (value === "\\") { + const next = peek(); + if (next === "/" && opts.bash !== true) { + continue; + } + if (next === "." || next === ";") { + continue; + } + if (!next) { + value += "\\"; + push({ type: "text", value }); + continue; + } + const match = /^\\+/.exec(remaining()); + let slashes = 0; + if (match && match[0].length > 2) { + slashes = match[0].length; + state.index += slashes; + if (slashes % 2 !== 0) { + value += "\\"; + } + } + if (opts.unescape === true) { + value = advance(); + } else { + value += advance(); + } + if (state.brackets === 0) { + push({ type: "text", value }); + continue; + } + } + if (state.brackets > 0 && (value !== "]" || prev.value === "[" || prev.value === "[^")) { + if (opts.posix !== false && value === ":") { + const inner = prev.value.slice(1); + if (inner.includes("[")) { + prev.posix = true; + if (inner.includes(":")) { + const idx = prev.value.lastIndexOf("["); + const pre = prev.value.slice(0, idx); + const rest2 = prev.value.slice(idx + 2); + const posix = POSIX_REGEX_SOURCE[rest2]; + if (posix) { + prev.value = pre + posix; + state.backtrack = true; + advance(); + if (!bos.output && tokens.indexOf(prev) === 1) { + bos.output = ONE_CHAR; + } + continue; + } + } + } + } + if (value === "[" && peek() !== ":" || value === "-" && peek() === "]") { + value = `\\${value}`; + } + if (value === "]" && (prev.value === "[" || prev.value === "[^")) { + value = `\\${value}`; + } + if (opts.posix === true && value === "!" && prev.value === "[") { + value = "^"; + } + prev.value += value; + append({ value }); + continue; + } + if (state.quotes === 1 && value !== '"') { + value = utils.escapeRegex(value); + prev.value += value; + append({ value }); + continue; + } + if (value === '"') { + state.quotes = state.quotes === 1 ? 0 : 1; + if (opts.keepQuotes === true) { + push({ type: "text", value }); + } + continue; + } + if (value === "(") { + increment("parens"); + push({ type: "paren", value }); + continue; + } + if (value === ")") { + if (state.parens === 0 && opts.strictBrackets === true) { + throw new SyntaxError(syntaxError("opening", "(")); + } + const extglob = extglobs[extglobs.length - 1]; + if (extglob && state.parens === extglob.parens + 1) { + extglobClose(extglobs.pop()); + continue; + } + push({ type: "paren", value, output: state.parens ? ")" : "\\)" }); + decrement("parens"); + continue; + } + if (value === "[") { + if (opts.nobracket === true || !remaining().includes("]")) { + if (opts.nobracket !== true && opts.strictBrackets === true) { + throw new SyntaxError(syntaxError("closing", "]")); + } + value = `\\${value}`; + } else { + increment("brackets"); + } + push({ type: "bracket", value }); + continue; + } + if (value === "]") { + if (opts.nobracket === true || prev && prev.type === "bracket" && prev.value.length === 1) { + push({ type: "text", value, output: `\\${value}` }); + continue; + } + if (state.brackets === 0) { + if (opts.strictBrackets === true) { + throw new SyntaxError(syntaxError("opening", "[")); + } + push({ type: "text", value, output: `\\${value}` }); + continue; + } + decrement("brackets"); + const prevValue = prev.value.slice(1); + if (prev.posix !== true && prevValue[0] === "^" && !prevValue.includes("/")) { + value = `/${value}`; + } + prev.value += value; + append({ value }); + if (opts.literalBrackets === false || utils.hasRegexChars(prevValue)) { + continue; + } + const escaped = utils.escapeRegex(prev.value); + state.output = state.output.slice(0, -prev.value.length); + if (opts.literalBrackets === true) { + state.output += escaped; + prev.value = escaped; + continue; + } + prev.value = `(${capture}${escaped}|${prev.value})`; + state.output += prev.value; + continue; + } + if (value === "{" && opts.nobrace !== true) { + increment("braces"); + const open = { + type: "brace", + value, + output: "(", + outputIndex: state.output.length, + tokensIndex: state.tokens.length + }; + braces.push(open); + push(open); + continue; + } + if (value === "}") { + const brace = braces[braces.length - 1]; + if (opts.nobrace === true || !brace) { + push({ type: "text", value, output: value }); + continue; + } + let output = ")"; + if (brace.dots === true) { + const arr = tokens.slice(); + const range = []; + for (let i = arr.length - 1; i >= 0; i--) { + tokens.pop(); + if (arr[i].type === "brace") { + break; + } + if (arr[i].type !== "dots") { + range.unshift(arr[i].value); + } + } + output = expandRange(range, opts); + state.backtrack = true; + } + if (brace.comma !== true && brace.dots !== true) { + const out = state.output.slice(0, brace.outputIndex); + const toks = state.tokens.slice(brace.tokensIndex); + brace.value = brace.output = "\\{"; + value = output = "\\}"; + state.output = out; + for (const t of toks) { + state.output += t.output || t.value; + } + } + push({ type: "brace", value, output }); + decrement("braces"); + braces.pop(); + continue; + } + if (value === "|") { + if (extglobs.length > 0) { + extglobs[extglobs.length - 1].conditions++; + } + push({ type: "text", value }); + continue; + } + if (value === ",") { + let output = value; + const brace = braces[braces.length - 1]; + if (brace && stack[stack.length - 1] === "braces") { + brace.comma = true; + output = "|"; + } + push({ type: "comma", value, output }); + continue; + } + if (value === "/") { + if (prev.type === "dot" && state.index === state.start + 1) { + state.start = state.index + 1; + state.consumed = ""; + state.output = ""; + tokens.pop(); + prev = bos; + continue; + } + push({ type: "slash", value, output: SLASH_LITERAL }); + continue; + } + if (value === ".") { + if (state.braces > 0 && prev.type === "dot") { + if (prev.value === ".") prev.output = DOT_LITERAL; + const brace = braces[braces.length - 1]; + prev.type = "dots"; + prev.output += value; + prev.value += value; + brace.dots = true; + continue; + } + if (state.braces + state.parens === 0 && prev.type !== "bos" && prev.type !== "slash") { + push({ type: "text", value, output: DOT_LITERAL }); + continue; + } + push({ type: "dot", value, output: DOT_LITERAL }); + continue; + } + if (value === "?") { + const isGroup = prev && prev.value === "("; + if (!isGroup && opts.noextglob !== true && peek() === "(" && peek(2) !== "?") { + extglobOpen("qmark", value); + continue; + } + if (prev && prev.type === "paren") { + const next = peek(); + let output = value; + if (next === "<" && !utils.supportsLookbehinds()) { + throw new Error("Node.js v10 or higher is required for regex lookbehinds"); + } + if (prev.value === "(" && !/[!=<:]/.test(next) || next === "<" && !/<([!=]|\w+>)/.test(remaining())) { + output = `\\${value}`; + } + push({ type: "text", value, output }); + continue; + } + if (opts.dot !== true && (prev.type === "slash" || prev.type === "bos")) { + push({ type: "qmark", value, output: QMARK_NO_DOT }); + continue; + } + push({ type: "qmark", value, output: QMARK }); + continue; + } + if (value === "!") { + if (opts.noextglob !== true && peek() === "(") { + if (peek(2) !== "?" || !/[!=<:]/.test(peek(3))) { + extglobOpen("negate", value); + continue; + } + } + if (opts.nonegate !== true && state.index === 0) { + negate(); + continue; + } + } + if (value === "+") { + if (opts.noextglob !== true && peek() === "(" && peek(2) !== "?") { + extglobOpen("plus", value); + continue; + } + if (prev && prev.value === "(" || opts.regex === false) { + push({ type: "plus", value, output: PLUS_LITERAL }); + continue; + } + if (prev && (prev.type === "bracket" || prev.type === "paren" || prev.type === "brace") || state.parens > 0) { + push({ type: "plus", value }); + continue; + } + push({ type: "plus", value: PLUS_LITERAL }); + continue; + } + if (value === "@") { + if (opts.noextglob !== true && peek() === "(" && peek(2) !== "?") { + push({ type: "at", extglob: true, value, output: "" }); + continue; + } + push({ type: "text", value }); + continue; + } + if (value !== "*") { + if (value === "$" || value === "^") { + value = `\\${value}`; + } + const match = REGEX_NON_SPECIAL_CHARS.exec(remaining()); + if (match) { + value += match[0]; + state.index += match[0].length; + } + push({ type: "text", value }); + continue; + } + if (prev && (prev.type === "globstar" || prev.star === true)) { + prev.type = "star"; + prev.star = true; + prev.value += value; + prev.output = star; + state.backtrack = true; + state.globstar = true; + consume(value); + continue; + } + let rest = remaining(); + if (opts.noextglob !== true && /^\([^?]/.test(rest)) { + extglobOpen("star", value); + continue; + } + if (prev.type === "star") { + if (opts.noglobstar === true) { + consume(value); + continue; + } + const prior = prev.prev; + const before = prior.prev; + const isStart = prior.type === "slash" || prior.type === "bos"; + const afterStar = before && (before.type === "star" || before.type === "globstar"); + if (opts.bash === true && (!isStart || rest[0] && rest[0] !== "/")) { + push({ type: "star", value, output: "" }); + continue; + } + const isBrace = state.braces > 0 && (prior.type === "comma" || prior.type === "brace"); + const isExtglob = extglobs.length && (prior.type === "pipe" || prior.type === "paren"); + if (!isStart && prior.type !== "paren" && !isBrace && !isExtglob) { + push({ type: "star", value, output: "" }); + continue; + } + while (rest.slice(0, 3) === "/**") { + const after = input[state.index + 4]; + if (after && after !== "/") { + break; + } + rest = rest.slice(3); + consume("/**", 3); + } + if (prior.type === "bos" && eos()) { + prev.type = "globstar"; + prev.value += value; + prev.output = globstar(opts); + state.output = prev.output; + state.globstar = true; + consume(value); + continue; + } + if (prior.type === "slash" && prior.prev.type !== "bos" && !afterStar && eos()) { + state.output = state.output.slice(0, -(prior.output + prev.output).length); + prior.output = `(?:${prior.output}`; + prev.type = "globstar"; + prev.output = globstar(opts) + (opts.strictSlashes ? ")" : "|$)"); + prev.value += value; + state.globstar = true; + state.output += prior.output + prev.output; + consume(value); + continue; + } + if (prior.type === "slash" && prior.prev.type !== "bos" && rest[0] === "/") { + const end = rest[1] !== void 0 ? "|$" : ""; + state.output = state.output.slice(0, -(prior.output + prev.output).length); + prior.output = `(?:${prior.output}`; + prev.type = "globstar"; + prev.output = `${globstar(opts)}${SLASH_LITERAL}|${SLASH_LITERAL}${end})`; + prev.value += value; + state.output += prior.output + prev.output; + state.globstar = true; + consume(value + advance()); + push({ type: "slash", value: "/", output: "" }); + continue; + } + if (prior.type === "bos" && rest[0] === "/") { + prev.type = "globstar"; + prev.value += value; + prev.output = `(?:^|${SLASH_LITERAL}|${globstar(opts)}${SLASH_LITERAL})`; + state.output = prev.output; + state.globstar = true; + consume(value + advance()); + push({ type: "slash", value: "/", output: "" }); + continue; + } + state.output = state.output.slice(0, -prev.output.length); + prev.type = "globstar"; + prev.output = globstar(opts); + prev.value += value; + state.output += prev.output; + state.globstar = true; + consume(value); + continue; + } + const token = { type: "star", value, output: star }; + if (opts.bash === true) { + token.output = ".*?"; + if (prev.type === "bos" || prev.type === "slash") { + token.output = nodot + token.output; + } + push(token); + continue; + } + if (prev && (prev.type === "bracket" || prev.type === "paren") && opts.regex === true) { + token.output = value; + push(token); + continue; + } + if (state.index === state.start || prev.type === "slash" || prev.type === "dot") { + if (prev.type === "dot") { + state.output += NO_DOT_SLASH; + prev.output += NO_DOT_SLASH; + } else if (opts.dot === true) { + state.output += NO_DOTS_SLASH; + prev.output += NO_DOTS_SLASH; + } else { + state.output += nodot; + prev.output += nodot; + } + if (peek() !== "*") { + state.output += ONE_CHAR; + prev.output += ONE_CHAR; + } + } + push(token); + } + while (state.brackets > 0) { + if (opts.strictBrackets === true) throw new SyntaxError(syntaxError("closing", "]")); + state.output = utils.escapeLast(state.output, "["); + decrement("brackets"); + } + while (state.parens > 0) { + if (opts.strictBrackets === true) throw new SyntaxError(syntaxError("closing", ")")); + state.output = utils.escapeLast(state.output, "("); + decrement("parens"); + } + while (state.braces > 0) { + if (opts.strictBrackets === true) throw new SyntaxError(syntaxError("closing", "}")); + state.output = utils.escapeLast(state.output, "{"); + decrement("braces"); + } + if (opts.strictSlashes !== true && (prev.type === "star" || prev.type === "bracket")) { + push({ type: "maybe_slash", value: "", output: `${SLASH_LITERAL}?` }); + } + if (state.backtrack === true) { + state.output = ""; + for (const token of state.tokens) { + state.output += token.output != null ? token.output : token.value; + if (token.suffix) { + state.output += token.suffix; + } + } + } + return state; + }; + parse.fastpaths = (input, options) => { + const opts = { ...options }; + const max = typeof opts.maxLength === "number" ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; + const len = input.length; + if (len > max) { + throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`); + } + input = REPLACEMENTS[input] || input; + const win32 = utils.isWindows(options); + const { + DOT_LITERAL, + SLASH_LITERAL, + ONE_CHAR, + DOTS_SLASH, + NO_DOT, + NO_DOTS, + NO_DOTS_SLASH, + STAR, + START_ANCHOR + } = constants.globChars(win32); + const nodot = opts.dot ? NO_DOTS : NO_DOT; + const slashDot = opts.dot ? NO_DOTS_SLASH : NO_DOT; + const capture = opts.capture ? "" : "?:"; + const state = { negated: false, prefix: "" }; + let star = opts.bash === true ? ".*?" : STAR; + if (opts.capture) { + star = `(${star})`; + } + const globstar = (opts2) => { + if (opts2.noglobstar === true) return star; + return `(${capture}(?:(?!${START_ANCHOR}${opts2.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`; + }; + const create = (str) => { + switch (str) { + case "*": + return `${nodot}${ONE_CHAR}${star}`; + case ".*": + return `${DOT_LITERAL}${ONE_CHAR}${star}`; + case "*.*": + return `${nodot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`; + case "*/*": + return `${nodot}${star}${SLASH_LITERAL}${ONE_CHAR}${slashDot}${star}`; + case "**": + return nodot + globstar(opts); + case "**/*": + return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${ONE_CHAR}${star}`; + case "**/*.*": + return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`; + case "**/.*": + return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${DOT_LITERAL}${ONE_CHAR}${star}`; + default: { + const match = /^(.*?)\.(\w+)$/.exec(str); + if (!match) return; + const source2 = create(match[1]); + if (!source2) return; + return source2 + DOT_LITERAL + match[2]; + } + } + }; + const output = utils.removePrefix(input, state); + let source = create(output); + if (source && opts.strictSlashes !== true) { + source += `${SLASH_LITERAL}?`; + } + return source; + }; + module2.exports = parse; + } +}); + +// node_modules/anymatch/node_modules/picomatch/lib/picomatch.js +var require_picomatch3 = __commonJS({ + "node_modules/anymatch/node_modules/picomatch/lib/picomatch.js"(exports2, module2) { + "use strict"; + var path3 = require("path"); + var scan = require_scan2(); + var parse = require_parse2(); + var utils = require_utils2(); + var constants = require_constants2(); + var isObject = (val) => val && typeof val === "object" && !Array.isArray(val); + var picomatch = (glob, options, returnState = false) => { + if (Array.isArray(glob)) { + const fns = glob.map((input) => picomatch(input, options, returnState)); + const arrayMatcher = (str) => { + for (const isMatch of fns) { + const state2 = isMatch(str); + if (state2) return state2; + } + return false; + }; + return arrayMatcher; + } + const isState = isObject(glob) && glob.tokens && glob.input; + if (glob === "" || typeof glob !== "string" && !isState) { + throw new TypeError("Expected pattern to be a non-empty string"); + } + const opts = options || {}; + const posix = utils.isWindows(options); + const regex = isState ? picomatch.compileRe(glob, options) : picomatch.makeRe(glob, options, false, true); + const state = regex.state; + delete regex.state; + let isIgnored = () => false; + if (opts.ignore) { + const ignoreOpts = { ...options, ignore: null, onMatch: null, onResult: null }; + isIgnored = picomatch(opts.ignore, ignoreOpts, returnState); + } + const matcher = (input, returnObject = false) => { + const { isMatch, match, output } = picomatch.test(input, regex, options, { glob, posix }); + const result = { glob, state, regex, posix, input, output, match, isMatch }; + if (typeof opts.onResult === "function") { + opts.onResult(result); + } + if (isMatch === false) { + result.isMatch = false; + return returnObject ? result : false; + } + if (isIgnored(input)) { + if (typeof opts.onIgnore === "function") { + opts.onIgnore(result); + } + result.isMatch = false; + return returnObject ? result : false; + } + if (typeof opts.onMatch === "function") { + opts.onMatch(result); + } + return returnObject ? result : true; + }; + if (returnState) { + matcher.state = state; + } + return matcher; + }; + picomatch.test = (input, regex, options, { glob, posix } = {}) => { + if (typeof input !== "string") { + throw new TypeError("Expected input to be a string"); + } + if (input === "") { + return { isMatch: false, output: "" }; + } + const opts = options || {}; + const format = opts.format || (posix ? utils.toPosixSlashes : null); + let match = input === glob; + let output = match && format ? format(input) : input; + if (match === false) { + output = format ? format(input) : input; + match = output === glob; + } + if (match === false || opts.capture === true) { + if (opts.matchBase === true || opts.basename === true) { + match = picomatch.matchBase(input, regex, options, posix); + } else { + match = regex.exec(output); + } + } + return { isMatch: Boolean(match), match, output }; + }; + picomatch.matchBase = (input, glob, options, posix = utils.isWindows(options)) => { + const regex = glob instanceof RegExp ? glob : picomatch.makeRe(glob, options); + return regex.test(path3.basename(input)); + }; + picomatch.isMatch = (str, patterns, options) => picomatch(patterns, options)(str); + picomatch.parse = (pattern, options) => { + if (Array.isArray(pattern)) return pattern.map((p) => picomatch.parse(p, options)); + return parse(pattern, { ...options, fastpaths: false }); + }; + picomatch.scan = (input, options) => scan(input, options); + picomatch.compileRe = (state, options, returnOutput = false, returnState = false) => { + if (returnOutput === true) { + return state.output; + } + const opts = options || {}; + const prepend = opts.contains ? "" : "^"; + const append = opts.contains ? "" : "$"; + let source = `${prepend}(?:${state.output})${append}`; + if (state && state.negated === true) { + source = `^(?!${source}).*$`; + } + const regex = picomatch.toRegex(source, options); + if (returnState === true) { + regex.state = state; + } + return regex; + }; + picomatch.makeRe = (input, options = {}, returnOutput = false, returnState = false) => { + if (!input || typeof input !== "string") { + throw new TypeError("Expected a non-empty string"); + } + let parsed = { negated: false, fastpaths: true }; + if (options.fastpaths !== false && (input[0] === "." || input[0] === "*")) { + parsed.output = parse.fastpaths(input, options); + } + if (!parsed.output) { + parsed = parse(input, options); + } + return picomatch.compileRe(parsed, options, returnOutput, returnState); + }; + picomatch.toRegex = (source, options) => { + try { + const opts = options || {}; + return new RegExp(source, opts.flags || (opts.nocase ? "i" : "")); + } catch (err) { + if (options && options.debug === true) throw err; + return /$^/; + } + }; + picomatch.constants = constants; + module2.exports = picomatch; + } +}); + +// node_modules/anymatch/node_modules/picomatch/index.js +var require_picomatch4 = __commonJS({ + "node_modules/anymatch/node_modules/picomatch/index.js"(exports2, module2) { + "use strict"; + module2.exports = require_picomatch3(); + } +}); + +// node_modules/normalize-path/index.js +var require_normalize_path = __commonJS({ + "node_modules/normalize-path/index.js"(exports2, module2) { + module2.exports = function(path3, stripTrailing) { + if (typeof path3 !== "string") { + throw new TypeError("expected path to be a string"); + } + if (path3 === "\\" || path3 === "/") return "/"; + var len = path3.length; + if (len <= 1) return path3; + var prefix = ""; + if (len > 4 && path3[3] === "\\") { + var ch = path3[2]; + if ((ch === "?" || ch === ".") && path3.slice(0, 2) === "\\\\") { + path3 = path3.slice(2); + prefix = "//"; + } + } + var segs = path3.split(/[/\\]+/); + if (stripTrailing !== false && segs[segs.length - 1] === "") { + segs.pop(); + } + return prefix + segs.join("/"); + }; + } +}); + +// node_modules/anymatch/index.js +var require_anymatch = __commonJS({ + "node_modules/anymatch/index.js"(exports2, module2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + var picomatch = require_picomatch4(); + var normalizePath = require_normalize_path(); + var BANG = "!"; + var DEFAULT_OPTIONS = { returnIndex: false }; + var arrify = (item) => Array.isArray(item) ? item : [item]; + var createPattern = (matcher, options) => { + if (typeof matcher === "function") { + return matcher; + } + if (typeof matcher === "string") { + const glob = picomatch(matcher, options); + return (string) => matcher === string || glob(string); + } + if (matcher instanceof RegExp) { + return (string) => matcher.test(string); + } + return (string) => false; + }; + var matchPatterns = (patterns, negPatterns, args, returnIndex) => { + const isList = Array.isArray(args); + const _path = isList ? args[0] : args; + if (!isList && typeof _path !== "string") { + throw new TypeError("anymatch: second argument must be a string: got " + Object.prototype.toString.call(_path)); + } + const path3 = normalizePath(_path, false); + for (let index = 0; index < negPatterns.length; index++) { + const nglob = negPatterns[index]; + if (nglob(path3)) { + return returnIndex ? -1 : false; + } + } + const applied = isList && [path3].concat(args.slice(1)); + for (let index = 0; index < patterns.length; index++) { + const pattern = patterns[index]; + if (isList ? pattern(...applied) : pattern(path3)) { + return returnIndex ? index : true; + } + } + return returnIndex ? -1 : false; + }; + var anymatch = (matchers, testString, options = DEFAULT_OPTIONS) => { + if (matchers == null) { + throw new TypeError("anymatch: specify first argument"); + } + const opts = typeof options === "boolean" ? { returnIndex: options } : options; + const returnIndex = opts.returnIndex || false; + const mtchers = arrify(matchers); + const negatedGlobs = mtchers.filter((item) => typeof item === "string" && item.charAt(0) === BANG).map((item) => item.slice(1)).map((item) => picomatch(item, opts)); + const patterns = mtchers.filter((item) => typeof item !== "string" || typeof item === "string" && item.charAt(0) !== BANG).map((matcher) => createPattern(matcher, opts)); + if (testString == null) { + return (testString2, ri = false) => { + const returnIndex2 = typeof ri === "boolean" ? ri : false; + return matchPatterns(patterns, negatedGlobs, testString2, returnIndex2); + }; + } + return matchPatterns(patterns, negatedGlobs, testString, returnIndex); + }; + anymatch.default = anymatch; + module2.exports = anymatch; + } +}); + +// node_modules/is-extglob/index.js +var require_is_extglob = __commonJS({ + "node_modules/is-extglob/index.js"(exports2, module2) { + module2.exports = function isExtglob(str) { + if (typeof str !== "string" || str === "") { + return false; + } + var match; + while (match = /(\\).|([@?!+*]\(.*\))/g.exec(str)) { + if (match[2]) return true; + str = str.slice(match.index + match[0].length); + } + return false; + }; + } +}); + +// node_modules/is-glob/index.js +var require_is_glob = __commonJS({ + "node_modules/is-glob/index.js"(exports2, module2) { + var isExtglob = require_is_extglob(); + var chars = { "{": "}", "(": ")", "[": "]" }; + var strictCheck = function(str) { + if (str[0] === "!") { + return true; + } + var index = 0; + var pipeIndex = -2; + var closeSquareIndex = -2; + var closeCurlyIndex = -2; + var closeParenIndex = -2; + var backSlashIndex = -2; + while (index < str.length) { + if (str[index] === "*") { + return true; + } + if (str[index + 1] === "?" && /[\].+)]/.test(str[index])) { + return true; + } + if (closeSquareIndex !== -1 && str[index] === "[" && str[index + 1] !== "]") { + if (closeSquareIndex < index) { + closeSquareIndex = str.indexOf("]", index); + } + if (closeSquareIndex > index) { + if (backSlashIndex === -1 || backSlashIndex > closeSquareIndex) { + return true; + } + backSlashIndex = str.indexOf("\\", index); + if (backSlashIndex === -1 || backSlashIndex > closeSquareIndex) { + return true; + } + } + } + if (closeCurlyIndex !== -1 && str[index] === "{" && str[index + 1] !== "}") { + closeCurlyIndex = str.indexOf("}", index); + if (closeCurlyIndex > index) { + backSlashIndex = str.indexOf("\\", index); + if (backSlashIndex === -1 || backSlashIndex > closeCurlyIndex) { + return true; + } + } + } + if (closeParenIndex !== -1 && str[index] === "(" && str[index + 1] === "?" && /[:!=]/.test(str[index + 2]) && str[index + 3] !== ")") { + closeParenIndex = str.indexOf(")", index); + if (closeParenIndex > index) { + backSlashIndex = str.indexOf("\\", index); + if (backSlashIndex === -1 || backSlashIndex > closeParenIndex) { + return true; + } + } + } + if (pipeIndex !== -1 && str[index] === "(" && str[index + 1] !== "|") { + if (pipeIndex < index) { + pipeIndex = str.indexOf("|", index); + } + if (pipeIndex !== -1 && str[pipeIndex + 1] !== ")") { + closeParenIndex = str.indexOf(")", pipeIndex); + if (closeParenIndex > pipeIndex) { + backSlashIndex = str.indexOf("\\", pipeIndex); + if (backSlashIndex === -1 || backSlashIndex > closeParenIndex) { + return true; + } + } + } + } + if (str[index] === "\\") { + var open = str[index + 1]; + index += 2; + var close = chars[open]; + if (close) { + var n = str.indexOf(close, index); + if (n !== -1) { + index = n + 1; + } + } + if (str[index] === "!") { + return true; + } + } else { + index++; + } + } + return false; + }; + var relaxedCheck = function(str) { + if (str[0] === "!") { + return true; + } + var index = 0; + while (index < str.length) { + if (/[*?{}()[\]]/.test(str[index])) { + return true; + } + if (str[index] === "\\") { + var open = str[index + 1]; + index += 2; + var close = chars[open]; + if (close) { + var n = str.indexOf(close, index); + if (n !== -1) { + index = n + 1; + } + } + if (str[index] === "!") { + return true; + } + } else { + index++; + } + } + return false; + }; + module2.exports = function isGlob(str, options) { + if (typeof str !== "string" || str === "") { + return false; + } + if (isExtglob(str)) { + return true; + } + var check = strictCheck; + if (options && options.strict === false) { + check = relaxedCheck; + } + return check(str); + }; + } +}); + +// node_modules/glob-parent/index.js +var require_glob_parent = __commonJS({ + "node_modules/glob-parent/index.js"(exports2, module2) { + "use strict"; + var isGlob = require_is_glob(); + var pathPosixDirname = require("path").posix.dirname; + var isWin32 = require("os").platform() === "win32"; + var slash = "/"; + var backslash = /\\/g; + var enclosure = /[\{\[].*[\}\]]$/; + var globby = /(^|[^\\])([\{\[]|\([^\)]+$)/; + var escaped = /\\([\!\*\?\|\[\]\(\)\{\}])/g; + module2.exports = function globParent(str, opts) { + var options = Object.assign({ flipBackslashes: true }, opts); + if (options.flipBackslashes && isWin32 && str.indexOf(slash) < 0) { + str = str.replace(backslash, slash); + } + if (enclosure.test(str)) { + str += slash; + } + str += "a"; + do { + str = pathPosixDirname(str); + } while (isGlob(str) || globby.test(str)); + return str.replace(escaped, "$1"); + }; + } +}); + +// node_modules/braces/lib/utils.js +var require_utils3 = __commonJS({ + "node_modules/braces/lib/utils.js"(exports2) { + "use strict"; + exports2.isInteger = (num) => { + if (typeof num === "number") { + return Number.isInteger(num); + } + if (typeof num === "string" && num.trim() !== "") { + return Number.isInteger(Number(num)); + } + return false; + }; + exports2.find = (node, type) => node.nodes.find((node2) => node2.type === type); + exports2.exceedsLimit = (min, max, step = 1, limit) => { + if (limit === false) return false; + if (!exports2.isInteger(min) || !exports2.isInteger(max)) return false; + return (Number(max) - Number(min)) / Number(step) >= limit; + }; + exports2.escapeNode = (block, n = 0, type) => { + const node = block.nodes[n]; + if (!node) return; + if (type && node.type === type || node.type === "open" || node.type === "close") { + if (node.escaped !== true) { + node.value = "\\" + node.value; + node.escaped = true; + } + } + }; + exports2.encloseBrace = (node) => { + if (node.type !== "brace") return false; + if (node.commas >> 0 + node.ranges >> 0 === 0) { + node.invalid = true; + return true; + } + return false; + }; + exports2.isInvalidBrace = (block) => { + if (block.type !== "brace") return false; + if (block.invalid === true || block.dollar) return true; + if (block.commas >> 0 + block.ranges >> 0 === 0) { + block.invalid = true; + return true; + } + if (block.open !== true || block.close !== true) { + block.invalid = true; + return true; + } + return false; + }; + exports2.isOpenOrClose = (node) => { + if (node.type === "open" || node.type === "close") { + return true; + } + return node.open === true || node.close === true; + }; + exports2.reduce = (nodes) => nodes.reduce((acc, node) => { + if (node.type === "text") acc.push(node.value); + if (node.type === "range") node.type = "text"; + return acc; + }, []); + exports2.flatten = (...args) => { + const result = []; + const flat = (arr) => { + for (let i = 0; i < arr.length; i++) { + const ele = arr[i]; + if (Array.isArray(ele)) { + flat(ele); + continue; + } + if (ele !== void 0) { + result.push(ele); + } + } + return result; + }; + flat(args); + return result; + }; + } +}); + +// node_modules/braces/lib/stringify.js +var require_stringify = __commonJS({ + "node_modules/braces/lib/stringify.js"(exports2, module2) { + "use strict"; + var utils = require_utils3(); + module2.exports = (ast, options = {}) => { + const stringify = (node, parent = {}) => { + const invalidBlock = options.escapeInvalid && utils.isInvalidBrace(parent); + const invalidNode = node.invalid === true && options.escapeInvalid === true; + let output = ""; + if (node.value) { + if ((invalidBlock || invalidNode) && utils.isOpenOrClose(node)) { + return "\\" + node.value; + } + return node.value; + } + if (node.value) { + return node.value; + } + if (node.nodes) { + for (const child of node.nodes) { + output += stringify(child); + } + } + return output; + }; + return stringify(ast); + }; + } +}); + +// node_modules/is-number/index.js +var require_is_number = __commonJS({ + "node_modules/is-number/index.js"(exports2, module2) { + "use strict"; + module2.exports = function(num) { + if (typeof num === "number") { + return num - num === 0; + } + if (typeof num === "string" && num.trim() !== "") { + return Number.isFinite ? Number.isFinite(+num) : isFinite(+num); + } + return false; + }; + } +}); + +// node_modules/to-regex-range/index.js +var require_to_regex_range = __commonJS({ + "node_modules/to-regex-range/index.js"(exports2, module2) { + "use strict"; + var isNumber = require_is_number(); + var toRegexRange = (min, max, options) => { + if (isNumber(min) === false) { + throw new TypeError("toRegexRange: expected the first argument to be a number"); + } + if (max === void 0 || min === max) { + return String(min); + } + if (isNumber(max) === false) { + throw new TypeError("toRegexRange: expected the second argument to be a number."); + } + let opts = { relaxZeros: true, ...options }; + if (typeof opts.strictZeros === "boolean") { + opts.relaxZeros = opts.strictZeros === false; + } + let relax = String(opts.relaxZeros); + let shorthand = String(opts.shorthand); + let capture = String(opts.capture); + let wrap = String(opts.wrap); + let cacheKey = min + ":" + max + "=" + relax + shorthand + capture + wrap; + if (toRegexRange.cache.hasOwnProperty(cacheKey)) { + return toRegexRange.cache[cacheKey].result; + } + let a = Math.min(min, max); + let b = Math.max(min, max); + if (Math.abs(a - b) === 1) { + let result = min + "|" + max; + if (opts.capture) { + return `(${result})`; + } + if (opts.wrap === false) { + return result; + } + return `(?:${result})`; + } + let isPadded = hasPadding(min) || hasPadding(max); + let state = { min, max, a, b }; + let positives = []; + let negatives = []; + if (isPadded) { + state.isPadded = isPadded; + state.maxLen = String(state.max).length; + } + if (a < 0) { + let newMin = b < 0 ? Math.abs(b) : 1; + negatives = splitToPatterns(newMin, Math.abs(a), state, opts); + a = state.a = 0; + } + if (b >= 0) { + positives = splitToPatterns(a, b, state, opts); + } + state.negatives = negatives; + state.positives = positives; + state.result = collatePatterns(negatives, positives, opts); + if (opts.capture === true) { + state.result = `(${state.result})`; + } else if (opts.wrap !== false && positives.length + negatives.length > 1) { + state.result = `(?:${state.result})`; + } + toRegexRange.cache[cacheKey] = state; + return state.result; + }; + function collatePatterns(neg, pos, options) { + let onlyNegative = filterPatterns(neg, pos, "-", false, options) || []; + let onlyPositive = filterPatterns(pos, neg, "", false, options) || []; + let intersected = filterPatterns(neg, pos, "-?", true, options) || []; + let subpatterns = onlyNegative.concat(intersected).concat(onlyPositive); + return subpatterns.join("|"); + } + function splitToRanges(min, max) { + let nines = 1; + let zeros = 1; + let stop = countNines(min, nines); + let stops = /* @__PURE__ */ new Set([max]); + while (min <= stop && stop <= max) { + stops.add(stop); + nines += 1; + stop = countNines(min, nines); + } + stop = countZeros(max + 1, zeros) - 1; + while (min < stop && stop <= max) { + stops.add(stop); + zeros += 1; + stop = countZeros(max + 1, zeros) - 1; + } + stops = [...stops]; + stops.sort(compare); + return stops; + } + function rangeToPattern(start, stop, options) { + if (start === stop) { + return { pattern: start, count: [], digits: 0 }; + } + let zipped = zip(start, stop); + let digits = zipped.length; + let pattern = ""; + let count = 0; + for (let i = 0; i < digits; i++) { + let [startDigit, stopDigit] = zipped[i]; + if (startDigit === stopDigit) { + pattern += startDigit; + } else if (startDigit !== "0" || stopDigit !== "9") { + pattern += toCharacterClass(startDigit, stopDigit, options); + } else { + count++; + } + } + if (count) { + pattern += options.shorthand === true ? "\\d" : "[0-9]"; + } + return { pattern, count: [count], digits }; + } + function splitToPatterns(min, max, tok, options) { + let ranges = splitToRanges(min, max); + let tokens = []; + let start = min; + let prev; + for (let i = 0; i < ranges.length; i++) { + let max2 = ranges[i]; + let obj = rangeToPattern(String(start), String(max2), options); + let zeros = ""; + if (!tok.isPadded && prev && prev.pattern === obj.pattern) { + if (prev.count.length > 1) { + prev.count.pop(); + } + prev.count.push(obj.count[0]); + prev.string = prev.pattern + toQuantifier(prev.count); + start = max2 + 1; + continue; + } + if (tok.isPadded) { + zeros = padZeros(max2, tok, options); + } + obj.string = zeros + obj.pattern + toQuantifier(obj.count); + tokens.push(obj); + start = max2 + 1; + prev = obj; + } + return tokens; + } + function filterPatterns(arr, comparison, prefix, intersection, options) { + let result = []; + for (let ele of arr) { + let { string } = ele; + if (!intersection && !contains(comparison, "string", string)) { + result.push(prefix + string); + } + if (intersection && contains(comparison, "string", string)) { + result.push(prefix + string); + } + } + return result; + } + function zip(a, b) { + let arr = []; + for (let i = 0; i < a.length; i++) arr.push([a[i], b[i]]); + return arr; + } + function compare(a, b) { + return a > b ? 1 : b > a ? -1 : 0; + } + function contains(arr, key, val) { + return arr.some((ele) => ele[key] === val); + } + function countNines(min, len) { + return Number(String(min).slice(0, -len) + "9".repeat(len)); + } + function countZeros(integer, zeros) { + return integer - integer % Math.pow(10, zeros); + } + function toQuantifier(digits) { + let [start = 0, stop = ""] = digits; + if (stop || start > 1) { + return `{${start + (stop ? "," + stop : "")}}`; + } + return ""; + } + function toCharacterClass(a, b, options) { + return `[${a}${b - a === 1 ? "" : "-"}${b}]`; + } + function hasPadding(str) { + return /^-?(0+)\d/.test(str); + } + function padZeros(value, tok, options) { + if (!tok.isPadded) { + return value; + } + let diff = Math.abs(tok.maxLen - String(value).length); + let relax = options.relaxZeros !== false; + switch (diff) { + case 0: + return ""; + case 1: + return relax ? "0?" : "0"; + case 2: + return relax ? "0{0,2}" : "00"; + default: { + return relax ? `0{0,${diff}}` : `0{${diff}}`; + } + } + } + toRegexRange.cache = {}; + toRegexRange.clearCache = () => toRegexRange.cache = {}; + module2.exports = toRegexRange; + } +}); + +// node_modules/fill-range/index.js +var require_fill_range = __commonJS({ + "node_modules/fill-range/index.js"(exports2, module2) { + "use strict"; + var util = require("util"); + var toRegexRange = require_to_regex_range(); + var isObject = (val) => val !== null && typeof val === "object" && !Array.isArray(val); + var transform = (toNumber) => { + return (value) => toNumber === true ? Number(value) : String(value); + }; + var isValidValue = (value) => { + return typeof value === "number" || typeof value === "string" && value !== ""; + }; + var isNumber = (num) => Number.isInteger(+num); + var zeros = (input) => { + let value = `${input}`; + let index = -1; + if (value[0] === "-") value = value.slice(1); + if (value === "0") return false; + while (value[++index] === "0") ; + return index > 0; + }; + var stringify = (start, end, options) => { + if (typeof start === "string" || typeof end === "string") { + return true; + } + return options.stringify === true; + }; + var pad = (input, maxLength, toNumber) => { + if (maxLength > 0) { + let dash = input[0] === "-" ? "-" : ""; + if (dash) input = input.slice(1); + input = dash + input.padStart(dash ? maxLength - 1 : maxLength, "0"); + } + if (toNumber === false) { + return String(input); + } + return input; + }; + var toMaxLen = (input, maxLength) => { + let negative = input[0] === "-" ? "-" : ""; + if (negative) { + input = input.slice(1); + maxLength--; + } + while (input.length < maxLength) input = "0" + input; + return negative ? "-" + input : input; + }; + var toSequence = (parts, options, maxLen) => { + parts.negatives.sort((a, b) => a < b ? -1 : a > b ? 1 : 0); + parts.positives.sort((a, b) => a < b ? -1 : a > b ? 1 : 0); + let prefix = options.capture ? "" : "?:"; + let positives = ""; + let negatives = ""; + let result; + if (parts.positives.length) { + positives = parts.positives.map((v) => toMaxLen(String(v), maxLen)).join("|"); + } + if (parts.negatives.length) { + negatives = `-(${prefix}${parts.negatives.map((v) => toMaxLen(String(v), maxLen)).join("|")})`; + } + if (positives && negatives) { + result = `${positives}|${negatives}`; + } else { + result = positives || negatives; + } + if (options.wrap) { + return `(${prefix}${result})`; + } + return result; + }; + var toRange = (a, b, isNumbers, options) => { + if (isNumbers) { + return toRegexRange(a, b, { wrap: false, ...options }); + } + let start = String.fromCharCode(a); + if (a === b) return start; + let stop = String.fromCharCode(b); + return `[${start}-${stop}]`; + }; + var toRegex = (start, end, options) => { + if (Array.isArray(start)) { + let wrap = options.wrap === true; + let prefix = options.capture ? "" : "?:"; + return wrap ? `(${prefix}${start.join("|")})` : start.join("|"); + } + return toRegexRange(start, end, options); + }; + var rangeError = (...args) => { + return new RangeError("Invalid range arguments: " + util.inspect(...args)); + }; + var invalidRange = (start, end, options) => { + if (options.strictRanges === true) throw rangeError([start, end]); + return []; + }; + var invalidStep = (step, options) => { + if (options.strictRanges === true) { + throw new TypeError(`Expected step "${step}" to be a number`); + } + return []; + }; + var fillNumbers = (start, end, step = 1, options = {}) => { + let a = Number(start); + let b = Number(end); + if (!Number.isInteger(a) || !Number.isInteger(b)) { + if (options.strictRanges === true) throw rangeError([start, end]); + return []; + } + if (a === 0) a = 0; + if (b === 0) b = 0; + let descending = a > b; + let startString = String(start); + let endString = String(end); + let stepString = String(step); + step = Math.max(Math.abs(step), 1); + let padded = zeros(startString) || zeros(endString) || zeros(stepString); + let maxLen = padded ? Math.max(startString.length, endString.length, stepString.length) : 0; + let toNumber = padded === false && stringify(start, end, options) === false; + let format = options.transform || transform(toNumber); + if (options.toRegex && step === 1) { + return toRange(toMaxLen(start, maxLen), toMaxLen(end, maxLen), true, options); + } + let parts = { negatives: [], positives: [] }; + let push = (num) => parts[num < 0 ? "negatives" : "positives"].push(Math.abs(num)); + let range = []; + let index = 0; + while (descending ? a >= b : a <= b) { + if (options.toRegex === true && step > 1) { + push(a); + } else { + range.push(pad(format(a, index), maxLen, toNumber)); + } + a = descending ? a - step : a + step; + index++; + } + if (options.toRegex === true) { + return step > 1 ? toSequence(parts, options, maxLen) : toRegex(range, null, { wrap: false, ...options }); + } + return range; + }; + var fillLetters = (start, end, step = 1, options = {}) => { + if (!isNumber(start) && start.length > 1 || !isNumber(end) && end.length > 1) { + return invalidRange(start, end, options); + } + let format = options.transform || ((val) => String.fromCharCode(val)); + let a = `${start}`.charCodeAt(0); + let b = `${end}`.charCodeAt(0); + let descending = a > b; + let min = Math.min(a, b); + let max = Math.max(a, b); + if (options.toRegex && step === 1) { + return toRange(min, max, false, options); + } + let range = []; + let index = 0; + while (descending ? a >= b : a <= b) { + range.push(format(a, index)); + a = descending ? a - step : a + step; + index++; + } + if (options.toRegex === true) { + return toRegex(range, null, { wrap: false, options }); + } + return range; + }; + var fill = (start, end, step, options = {}) => { + if (end == null && isValidValue(start)) { + return [start]; + } + if (!isValidValue(start) || !isValidValue(end)) { + return invalidRange(start, end, options); + } + if (typeof step === "function") { + return fill(start, end, 1, { transform: step }); + } + if (isObject(step)) { + return fill(start, end, 0, step); + } + let opts = { ...options }; + if (opts.capture === true) opts.wrap = true; + step = step || opts.step || 1; + if (!isNumber(step)) { + if (step != null && !isObject(step)) return invalidStep(step, opts); + return fill(start, end, 1, step); + } + if (isNumber(start) && isNumber(end)) { + return fillNumbers(start, end, step, opts); + } + return fillLetters(start, end, Math.max(Math.abs(step), 1), opts); + }; + module2.exports = fill; + } +}); + +// node_modules/braces/lib/compile.js +var require_compile = __commonJS({ + "node_modules/braces/lib/compile.js"(exports2, module2) { + "use strict"; + var fill = require_fill_range(); + var utils = require_utils3(); + var compile = (ast, options = {}) => { + const walk = (node, parent = {}) => { + const invalidBlock = utils.isInvalidBrace(parent); + const invalidNode = node.invalid === true && options.escapeInvalid === true; + const invalid = invalidBlock === true || invalidNode === true; + const prefix = options.escapeInvalid === true ? "\\" : ""; + let output = ""; + if (node.isOpen === true) { + return prefix + node.value; + } + if (node.isClose === true) { + console.log("node.isClose", prefix, node.value); + return prefix + node.value; + } + if (node.type === "open") { + return invalid ? prefix + node.value : "("; + } + if (node.type === "close") { + return invalid ? prefix + node.value : ")"; + } + if (node.type === "comma") { + return node.prev.type === "comma" ? "" : invalid ? node.value : "|"; + } + if (node.value) { + return node.value; + } + if (node.nodes && node.ranges > 0) { + const args = utils.reduce(node.nodes); + const range = fill(...args, { ...options, wrap: false, toRegex: true, strictZeros: true }); + if (range.length !== 0) { + return args.length > 1 && range.length > 1 ? `(${range})` : range; + } + } + if (node.nodes) { + for (const child of node.nodes) { + output += walk(child, node); + } + } + return output; + }; + return walk(ast); + }; + module2.exports = compile; + } +}); + +// node_modules/braces/lib/expand.js +var require_expand = __commonJS({ + "node_modules/braces/lib/expand.js"(exports2, module2) { + "use strict"; + var fill = require_fill_range(); + var stringify = require_stringify(); + var utils = require_utils3(); + var append = (queue = "", stash = "", enclose = false) => { + const result = []; + queue = [].concat(queue); + stash = [].concat(stash); + if (!stash.length) return queue; + if (!queue.length) { + return enclose ? utils.flatten(stash).map((ele) => `{${ele}}`) : stash; + } + for (const item of queue) { + if (Array.isArray(item)) { + for (const value of item) { + result.push(append(value, stash, enclose)); + } + } else { + for (let ele of stash) { + if (enclose === true && typeof ele === "string") ele = `{${ele}}`; + result.push(Array.isArray(ele) ? append(item, ele, enclose) : item + ele); + } + } + } + return utils.flatten(result); + }; + var expand = (ast, options = {}) => { + const rangeLimit = options.rangeLimit === void 0 ? 1e3 : options.rangeLimit; + const walk = (node, parent = {}) => { + node.queue = []; + let p = parent; + let q = parent.queue; + while (p.type !== "brace" && p.type !== "root" && p.parent) { + p = p.parent; + q = p.queue; + } + if (node.invalid || node.dollar) { + q.push(append(q.pop(), stringify(node, options))); + return; + } + if (node.type === "brace" && node.invalid !== true && node.nodes.length === 2) { + q.push(append(q.pop(), ["{}"])); + return; + } + if (node.nodes && node.ranges > 0) { + const args = utils.reduce(node.nodes); + if (utils.exceedsLimit(...args, options.step, rangeLimit)) { + throw new RangeError("expanded array length exceeds range limit. Use options.rangeLimit to increase or disable the limit."); + } + let range = fill(...args, options); + if (range.length === 0) { + range = stringify(node, options); + } + q.push(append(q.pop(), range)); + node.nodes = []; + return; + } + const enclose = utils.encloseBrace(node); + let queue = node.queue; + let block = node; + while (block.type !== "brace" && block.type !== "root" && block.parent) { + block = block.parent; + queue = block.queue; + } + for (let i = 0; i < node.nodes.length; i++) { + const child = node.nodes[i]; + if (child.type === "comma" && node.type === "brace") { + if (i === 1) queue.push(""); + queue.push(""); + continue; + } + if (child.type === "close") { + q.push(append(q.pop(), queue, enclose)); + continue; + } + if (child.value && child.type !== "open") { + queue.push(append(queue.pop(), child.value)); + continue; + } + if (child.nodes) { + walk(child, node); + } + } + return queue; + }; + return utils.flatten(walk(ast)); + }; + module2.exports = expand; + } +}); + +// node_modules/braces/lib/constants.js +var require_constants3 = __commonJS({ + "node_modules/braces/lib/constants.js"(exports2, module2) { + "use strict"; + module2.exports = { + MAX_LENGTH: 1e4, + // Digits + CHAR_0: "0", + /* 0 */ + CHAR_9: "9", + /* 9 */ + // Alphabet chars. + CHAR_UPPERCASE_A: "A", + /* A */ + CHAR_LOWERCASE_A: "a", + /* a */ + CHAR_UPPERCASE_Z: "Z", + /* Z */ + CHAR_LOWERCASE_Z: "z", + /* z */ + CHAR_LEFT_PARENTHESES: "(", + /* ( */ + CHAR_RIGHT_PARENTHESES: ")", + /* ) */ + CHAR_ASTERISK: "*", + /* * */ + // Non-alphabetic chars. + CHAR_AMPERSAND: "&", + /* & */ + CHAR_AT: "@", + /* @ */ + CHAR_BACKSLASH: "\\", + /* \ */ + CHAR_BACKTICK: "`", + /* ` */ + CHAR_CARRIAGE_RETURN: "\r", + /* \r */ + CHAR_CIRCUMFLEX_ACCENT: "^", + /* ^ */ + CHAR_COLON: ":", + /* : */ + CHAR_COMMA: ",", + /* , */ + CHAR_DOLLAR: "$", + /* . */ + CHAR_DOT: ".", + /* . */ + CHAR_DOUBLE_QUOTE: '"', + /* " */ + CHAR_EQUAL: "=", + /* = */ + CHAR_EXCLAMATION_MARK: "!", + /* ! */ + CHAR_FORM_FEED: "\f", + /* \f */ + CHAR_FORWARD_SLASH: "/", + /* / */ + CHAR_HASH: "#", + /* # */ + CHAR_HYPHEN_MINUS: "-", + /* - */ + CHAR_LEFT_ANGLE_BRACKET: "<", + /* < */ + CHAR_LEFT_CURLY_BRACE: "{", + /* { */ + CHAR_LEFT_SQUARE_BRACKET: "[", + /* [ */ + CHAR_LINE_FEED: "\n", + /* \n */ + CHAR_NO_BREAK_SPACE: "\xA0", + /* \u00A0 */ + CHAR_PERCENT: "%", + /* % */ + CHAR_PLUS: "+", + /* + */ + CHAR_QUESTION_MARK: "?", + /* ? */ + CHAR_RIGHT_ANGLE_BRACKET: ">", + /* > */ + CHAR_RIGHT_CURLY_BRACE: "}", + /* } */ + CHAR_RIGHT_SQUARE_BRACKET: "]", + /* ] */ + CHAR_SEMICOLON: ";", + /* ; */ + CHAR_SINGLE_QUOTE: "'", + /* ' */ + CHAR_SPACE: " ", + /* */ + CHAR_TAB: " ", + /* \t */ + CHAR_UNDERSCORE: "_", + /* _ */ + CHAR_VERTICAL_LINE: "|", + /* | */ + CHAR_ZERO_WIDTH_NOBREAK_SPACE: "\uFEFF" + /* \uFEFF */ + }; + } +}); + +// node_modules/braces/lib/parse.js +var require_parse3 = __commonJS({ + "node_modules/braces/lib/parse.js"(exports2, module2) { + "use strict"; + var stringify = require_stringify(); + var { + MAX_LENGTH, + CHAR_BACKSLASH, + /* \ */ + CHAR_BACKTICK, + /* ` */ + CHAR_COMMA, + /* , */ + CHAR_DOT, + /* . */ + CHAR_LEFT_PARENTHESES, + /* ( */ + CHAR_RIGHT_PARENTHESES, + /* ) */ + CHAR_LEFT_CURLY_BRACE, + /* { */ + CHAR_RIGHT_CURLY_BRACE, + /* } */ + CHAR_LEFT_SQUARE_BRACKET, + /* [ */ + CHAR_RIGHT_SQUARE_BRACKET, + /* ] */ + CHAR_DOUBLE_QUOTE, + /* " */ + CHAR_SINGLE_QUOTE, + /* ' */ + CHAR_NO_BREAK_SPACE, + CHAR_ZERO_WIDTH_NOBREAK_SPACE + } = require_constants3(); + var parse = (input, options = {}) => { + if (typeof input !== "string") { + throw new TypeError("Expected a string"); + } + const opts = options || {}; + const max = typeof opts.maxLength === "number" ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; + if (input.length > max) { + throw new SyntaxError(`Input length (${input.length}), exceeds max characters (${max})`); + } + const ast = { type: "root", input, nodes: [] }; + const stack = [ast]; + let block = ast; + let prev = ast; + let brackets = 0; + const length = input.length; + let index = 0; + let depth = 0; + let value; + const advance = () => input[index++]; + const push = (node) => { + if (node.type === "text" && prev.type === "dot") { + prev.type = "text"; + } + if (prev && prev.type === "text" && node.type === "text") { + prev.value += node.value; + return; + } + block.nodes.push(node); + node.parent = block; + node.prev = prev; + prev = node; + return node; + }; + push({ type: "bos" }); + while (index < length) { + block = stack[stack.length - 1]; + value = advance(); + if (value === CHAR_ZERO_WIDTH_NOBREAK_SPACE || value === CHAR_NO_BREAK_SPACE) { + continue; + } + if (value === CHAR_BACKSLASH) { + push({ type: "text", value: (options.keepEscaping ? value : "") + advance() }); + continue; + } + if (value === CHAR_RIGHT_SQUARE_BRACKET) { + push({ type: "text", value: "\\" + value }); + continue; + } + if (value === CHAR_LEFT_SQUARE_BRACKET) { + brackets++; + let next; + while (index < length && (next = advance())) { + value += next; + if (next === CHAR_LEFT_SQUARE_BRACKET) { + brackets++; + continue; + } + if (next === CHAR_BACKSLASH) { + value += advance(); + continue; + } + if (next === CHAR_RIGHT_SQUARE_BRACKET) { + brackets--; + if (brackets === 0) { + break; + } + } + } + push({ type: "text", value }); + continue; + } + if (value === CHAR_LEFT_PARENTHESES) { + block = push({ type: "paren", nodes: [] }); + stack.push(block); + push({ type: "text", value }); + continue; + } + if (value === CHAR_RIGHT_PARENTHESES) { + if (block.type !== "paren") { + push({ type: "text", value }); + continue; + } + block = stack.pop(); + push({ type: "text", value }); + block = stack[stack.length - 1]; + continue; + } + if (value === CHAR_DOUBLE_QUOTE || value === CHAR_SINGLE_QUOTE || value === CHAR_BACKTICK) { + const open = value; + let next; + if (options.keepQuotes !== true) { + value = ""; + } + while (index < length && (next = advance())) { + if (next === CHAR_BACKSLASH) { + value += next + advance(); + continue; + } + if (next === open) { + if (options.keepQuotes === true) value += next; + break; + } + value += next; + } + push({ type: "text", value }); + continue; + } + if (value === CHAR_LEFT_CURLY_BRACE) { + depth++; + const dollar = prev.value && prev.value.slice(-1) === "$" || block.dollar === true; + const brace = { + type: "brace", + open: true, + close: false, + dollar, + depth, + commas: 0, + ranges: 0, + nodes: [] + }; + block = push(brace); + stack.push(block); + push({ type: "open", value }); + continue; + } + if (value === CHAR_RIGHT_CURLY_BRACE) { + if (block.type !== "brace") { + push({ type: "text", value }); + continue; + } + const type = "close"; + block = stack.pop(); + block.close = true; + push({ type, value }); + depth--; + block = stack[stack.length - 1]; + continue; + } + if (value === CHAR_COMMA && depth > 0) { + if (block.ranges > 0) { + block.ranges = 0; + const open = block.nodes.shift(); + block.nodes = [open, { type: "text", value: stringify(block) }]; + } + push({ type: "comma", value }); + block.commas++; + continue; + } + if (value === CHAR_DOT && depth > 0 && block.commas === 0) { + const siblings = block.nodes; + if (depth === 0 || siblings.length === 0) { + push({ type: "text", value }); + continue; + } + if (prev.type === "dot") { + block.range = []; + prev.value += value; + prev.type = "range"; + if (block.nodes.length !== 3 && block.nodes.length !== 5) { + block.invalid = true; + block.ranges = 0; + prev.type = "text"; + continue; + } + block.ranges++; + block.args = []; + continue; + } + if (prev.type === "range") { + siblings.pop(); + const before = siblings[siblings.length - 1]; + before.value += prev.value + value; + prev = before; + block.ranges--; + continue; + } + push({ type: "dot", value }); + continue; + } + push({ type: "text", value }); + } + do { + block = stack.pop(); + if (block.type !== "root") { + block.nodes.forEach((node) => { + if (!node.nodes) { + if (node.type === "open") node.isOpen = true; + if (node.type === "close") node.isClose = true; + if (!node.nodes) node.type = "text"; + node.invalid = true; + } + }); + const parent = stack[stack.length - 1]; + const index2 = parent.nodes.indexOf(block); + parent.nodes.splice(index2, 1, ...block.nodes); + } + } while (stack.length > 0); + push({ type: "eos" }); + return ast; + }; + module2.exports = parse; + } +}); + +// node_modules/braces/index.js +var require_braces = __commonJS({ + "node_modules/braces/index.js"(exports2, module2) { + "use strict"; + var stringify = require_stringify(); + var compile = require_compile(); + var expand = require_expand(); + var parse = require_parse3(); + var braces = (input, options = {}) => { + let output = []; + if (Array.isArray(input)) { + for (const pattern of input) { + const result = braces.create(pattern, options); + if (Array.isArray(result)) { + output.push(...result); + } else { + output.push(result); + } + } + } else { + output = [].concat(braces.create(input, options)); + } + if (options && options.expand === true && options.nodupes === true) { + output = [...new Set(output)]; + } + return output; + }; + braces.parse = (input, options = {}) => parse(input, options); + braces.stringify = (input, options = {}) => { + if (typeof input === "string") { + return stringify(braces.parse(input, options), options); + } + return stringify(input, options); + }; + braces.compile = (input, options = {}) => { + if (typeof input === "string") { + input = braces.parse(input, options); + } + return compile(input, options); + }; + braces.expand = (input, options = {}) => { + if (typeof input === "string") { + input = braces.parse(input, options); + } + let result = expand(input, options); + if (options.noempty === true) { + result = result.filter(Boolean); + } + if (options.nodupes === true) { + result = [...new Set(result)]; + } + return result; + }; + braces.create = (input, options = {}) => { + if (input === "" || input.length < 3) { + return [input]; + } + return options.expand !== true ? braces.compile(input, options) : braces.expand(input, options); + }; + module2.exports = braces; + } +}); + +// node_modules/binary-extensions/binary-extensions.json +var require_binary_extensions = __commonJS({ + "node_modules/binary-extensions/binary-extensions.json"(exports2, module2) { + module2.exports = [ + "3dm", + "3ds", + "3g2", + "3gp", + "7z", + "a", + "aac", + "adp", + "afdesign", + "afphoto", + "afpub", + "ai", + "aif", + "aiff", + "alz", + "ape", + "apk", + "appimage", + "ar", + "arj", + "asf", + "au", + "avi", + "bak", + "baml", + "bh", + "bin", + "bk", + "bmp", + "btif", + "bz2", + "bzip2", + "cab", + "caf", + "cgm", + "class", + "cmx", + "cpio", + "cr2", + "cur", + "dat", + "dcm", + "deb", + "dex", + "djvu", + "dll", + "dmg", + "dng", + "doc", + "docm", + "docx", + "dot", + "dotm", + "dra", + "DS_Store", + "dsk", + "dts", + "dtshd", + "dvb", + "dwg", + "dxf", + "ecelp4800", + "ecelp7470", + "ecelp9600", + "egg", + "eol", + "eot", + "epub", + "exe", + "f4v", + "fbs", + "fh", + "fla", + "flac", + "flatpak", + "fli", + "flv", + "fpx", + "fst", + "fvt", + "g3", + "gh", + "gif", + "graffle", + "gz", + "gzip", + "h261", + "h263", + "h264", + "icns", + "ico", + "ief", + "img", + "ipa", + "iso", + "jar", + "jpeg", + "jpg", + "jpgv", + "jpm", + "jxr", + "key", + "ktx", + "lha", + "lib", + "lvp", + "lz", + "lzh", + "lzma", + "lzo", + "m3u", + "m4a", + "m4v", + "mar", + "mdi", + "mht", + "mid", + "midi", + "mj2", + "mka", + "mkv", + "mmr", + "mng", + "mobi", + "mov", + "movie", + "mp3", + "mp4", + "mp4a", + "mpeg", + "mpg", + "mpga", + "mxu", + "nef", + "npx", + "numbers", + "nupkg", + "o", + "odp", + "ods", + "odt", + "oga", + "ogg", + "ogv", + "otf", + "ott", + "pages", + "pbm", + "pcx", + "pdb", + "pdf", + "pea", + "pgm", + "pic", + "png", + "pnm", + "pot", + "potm", + "potx", + "ppa", + "ppam", + "ppm", + "pps", + "ppsm", + "ppsx", + "ppt", + "pptm", + "pptx", + "psd", + "pya", + "pyc", + "pyo", + "pyv", + "qt", + "rar", + "ras", + "raw", + "resources", + "rgb", + "rip", + "rlc", + "rmf", + "rmvb", + "rpm", + "rtf", + "rz", + "s3m", + "s7z", + "scpt", + "sgi", + "shar", + "snap", + "sil", + "sketch", + "slk", + "smv", + "snk", + "so", + "stl", + "suo", + "sub", + "swf", + "tar", + "tbz", + "tbz2", + "tga", + "tgz", + "thmx", + "tif", + "tiff", + "tlz", + "ttc", + "ttf", + "txz", + "udf", + "uvh", + "uvi", + "uvm", + "uvp", + "uvs", + "uvu", + "viv", + "vob", + "war", + "wav", + "wax", + "wbmp", + "wdp", + "weba", + "webm", + "webp", + "whl", + "wim", + "wm", + "wma", + "wmv", + "wmx", + "woff", + "woff2", + "wrm", + "wvx", + "xbm", + "xif", + "xla", + "xlam", + "xls", + "xlsb", + "xlsm", + "xlsx", + "xlt", + "xltm", + "xltx", + "xm", + "xmind", + "xpi", + "xpm", + "xwd", + "xz", + "z", + "zip", + "zipx" + ]; + } +}); + +// node_modules/binary-extensions/index.js +var require_binary_extensions2 = __commonJS({ + "node_modules/binary-extensions/index.js"(exports2, module2) { + module2.exports = require_binary_extensions(); + } +}); + +// node_modules/is-binary-path/index.js +var require_is_binary_path = __commonJS({ + "node_modules/is-binary-path/index.js"(exports2, module2) { + "use strict"; + var path3 = require("path"); + var binaryExtensions = require_binary_extensions2(); + var extensions = new Set(binaryExtensions); + module2.exports = (filePath) => extensions.has(path3.extname(filePath).slice(1).toLowerCase()); + } +}); + +// node_modules/chokidar/lib/constants.js +var require_constants4 = __commonJS({ + "node_modules/chokidar/lib/constants.js"(exports2) { + "use strict"; + var { sep } = require("path"); + var { platform } = process; + var os2 = require("os"); + exports2.EV_ALL = "all"; + exports2.EV_READY = "ready"; + exports2.EV_ADD = "add"; + exports2.EV_CHANGE = "change"; + exports2.EV_ADD_DIR = "addDir"; + exports2.EV_UNLINK = "unlink"; + exports2.EV_UNLINK_DIR = "unlinkDir"; + exports2.EV_RAW = "raw"; + exports2.EV_ERROR = "error"; + exports2.STR_DATA = "data"; + exports2.STR_END = "end"; + exports2.STR_CLOSE = "close"; + exports2.FSEVENT_CREATED = "created"; + exports2.FSEVENT_MODIFIED = "modified"; + exports2.FSEVENT_DELETED = "deleted"; + exports2.FSEVENT_MOVED = "moved"; + exports2.FSEVENT_CLONED = "cloned"; + exports2.FSEVENT_UNKNOWN = "unknown"; + exports2.FSEVENT_FLAG_MUST_SCAN_SUBDIRS = 1; + exports2.FSEVENT_TYPE_FILE = "file"; + exports2.FSEVENT_TYPE_DIRECTORY = "directory"; + exports2.FSEVENT_TYPE_SYMLINK = "symlink"; + exports2.KEY_LISTENERS = "listeners"; + exports2.KEY_ERR = "errHandlers"; + exports2.KEY_RAW = "rawEmitters"; + exports2.HANDLER_KEYS = [exports2.KEY_LISTENERS, exports2.KEY_ERR, exports2.KEY_RAW]; + exports2.DOT_SLASH = `.${sep}`; + exports2.BACK_SLASH_RE = /\\/g; + exports2.DOUBLE_SLASH_RE = /\/\//; + exports2.SLASH_OR_BACK_SLASH_RE = /[/\\]/; + exports2.DOT_RE = /\..*\.(sw[px])$|~$|\.subl.*\.tmp/; + exports2.REPLACER_RE = /^\.[/\\]/; + exports2.SLASH = "/"; + exports2.SLASH_SLASH = "//"; + exports2.BRACE_START = "{"; + exports2.BANG = "!"; + exports2.ONE_DOT = "."; + exports2.TWO_DOTS = ".."; + exports2.STAR = "*"; + exports2.GLOBSTAR = "**"; + exports2.ROOT_GLOBSTAR = "/**/*"; + exports2.SLASH_GLOBSTAR = "/**"; + exports2.DIR_SUFFIX = "Dir"; + exports2.ANYMATCH_OPTS = { dot: true }; + exports2.STRING_TYPE = "string"; + exports2.FUNCTION_TYPE = "function"; + exports2.EMPTY_STR = ""; + exports2.EMPTY_FN = () => { + }; + exports2.IDENTITY_FN = (val) => val; + exports2.isWindows = platform === "win32"; + exports2.isMacos = platform === "darwin"; + exports2.isLinux = platform === "linux"; + exports2.isIBMi = os2.type() === "OS400"; + } +}); + +// node_modules/chokidar/lib/nodefs-handler.js +var require_nodefs_handler = __commonJS({ + "node_modules/chokidar/lib/nodefs-handler.js"(exports2, module2) { + "use strict"; + var fs2 = require("fs"); + var sysPath = require("path"); + var { promisify } = require("util"); + var isBinaryPath = require_is_binary_path(); + var { + isWindows, + isLinux, + EMPTY_FN, + EMPTY_STR, + KEY_LISTENERS, + KEY_ERR, + KEY_RAW, + HANDLER_KEYS, + EV_CHANGE, + EV_ADD, + EV_ADD_DIR, + EV_ERROR, + STR_DATA, + STR_END, + BRACE_START, + STAR + } = require_constants4(); + var THROTTLE_MODE_WATCH = "watch"; + var open = promisify(fs2.open); + var stat = promisify(fs2.stat); + var lstat = promisify(fs2.lstat); + var close = promisify(fs2.close); + var fsrealpath = promisify(fs2.realpath); + var statMethods = { lstat, stat }; + var foreach = (val, fn) => { + if (val instanceof Set) { + val.forEach(fn); + } else { + fn(val); + } + }; + var addAndConvert = (main, prop, item) => { + let container = main[prop]; + if (!(container instanceof Set)) { + main[prop] = container = /* @__PURE__ */ new Set([container]); + } + container.add(item); + }; + var clearItem = (cont) => (key) => { + const set = cont[key]; + if (set instanceof Set) { + set.clear(); + } else { + delete cont[key]; + } + }; + var delFromSet = (main, prop, item) => { + const container = main[prop]; + if (container instanceof Set) { + container.delete(item); + } else if (container === item) { + delete main[prop]; + } + }; + var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val; + var FsWatchInstances = /* @__PURE__ */ new Map(); + function createFsWatchInstance(path3, options, listener, errHandler, emitRaw) { + const handleEvent = (rawEvent, evPath) => { + listener(path3); + emitRaw(rawEvent, evPath, { watchedPath: path3 }); + if (evPath && path3 !== evPath) { + fsWatchBroadcast( + sysPath.resolve(path3, evPath), + KEY_LISTENERS, + sysPath.join(path3, evPath) + ); + } + }; + try { + return fs2.watch(path3, options, handleEvent); + } catch (error) { + errHandler(error); + } + } + var fsWatchBroadcast = (fullPath, type, val1, val2, val3) => { + const cont = FsWatchInstances.get(fullPath); + if (!cont) return; + foreach(cont[type], (listener) => { + listener(val1, val2, val3); + }); + }; + var setFsWatchListener = (path3, fullPath, options, handlers) => { + const { listener, errHandler, rawEmitter } = handlers; + let cont = FsWatchInstances.get(fullPath); + let watcher; + if (!options.persistent) { + watcher = createFsWatchInstance( + path3, + options, + listener, + errHandler, + rawEmitter + ); + return watcher.close.bind(watcher); + } + if (cont) { + addAndConvert(cont, KEY_LISTENERS, listener); + addAndConvert(cont, KEY_ERR, errHandler); + addAndConvert(cont, KEY_RAW, rawEmitter); + } else { + watcher = createFsWatchInstance( + path3, + options, + fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS), + errHandler, + // no need to use broadcast here + fsWatchBroadcast.bind(null, fullPath, KEY_RAW) + ); + if (!watcher) return; + watcher.on(EV_ERROR, async (error) => { + const broadcastErr = fsWatchBroadcast.bind(null, fullPath, KEY_ERR); + cont.watcherUnusable = true; + if (isWindows && error.code === "EPERM") { + try { + const fd = await open(path3, "r"); + await close(fd); + broadcastErr(error); + } catch (err) { + } + } else { + broadcastErr(error); + } + }); + cont = { + listeners: listener, + errHandlers: errHandler, + rawEmitters: rawEmitter, + watcher + }; + FsWatchInstances.set(fullPath, cont); + } + return () => { + delFromSet(cont, KEY_LISTENERS, listener); + delFromSet(cont, KEY_ERR, errHandler); + delFromSet(cont, KEY_RAW, rawEmitter); + if (isEmptySet(cont.listeners)) { + cont.watcher.close(); + FsWatchInstances.delete(fullPath); + HANDLER_KEYS.forEach(clearItem(cont)); + cont.watcher = void 0; + Object.freeze(cont); + } + }; + }; + var FsWatchFileInstances = /* @__PURE__ */ new Map(); + var setFsWatchFileListener = (path3, fullPath, options, handlers) => { + const { listener, rawEmitter } = handlers; + let cont = FsWatchFileInstances.get(fullPath); + let listeners = /* @__PURE__ */ new Set(); + let rawEmitters = /* @__PURE__ */ new Set(); + const copts = cont && cont.options; + if (copts && (copts.persistent < options.persistent || copts.interval > options.interval)) { + listeners = cont.listeners; + rawEmitters = cont.rawEmitters; + fs2.unwatchFile(fullPath); + cont = void 0; + } + if (cont) { + addAndConvert(cont, KEY_LISTENERS, listener); + addAndConvert(cont, KEY_RAW, rawEmitter); + } else { + cont = { + listeners: listener, + rawEmitters: rawEmitter, + options, + watcher: fs2.watchFile(fullPath, options, (curr, prev) => { + foreach(cont.rawEmitters, (rawEmitter2) => { + rawEmitter2(EV_CHANGE, fullPath, { curr, prev }); + }); + const currmtime = curr.mtimeMs; + if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) { + foreach(cont.listeners, (listener2) => listener2(path3, curr)); + } + }) + }; + FsWatchFileInstances.set(fullPath, cont); + } + return () => { + delFromSet(cont, KEY_LISTENERS, listener); + delFromSet(cont, KEY_RAW, rawEmitter); + if (isEmptySet(cont.listeners)) { + FsWatchFileInstances.delete(fullPath); + fs2.unwatchFile(fullPath); + cont.options = cont.watcher = void 0; + Object.freeze(cont); + } + }; + }; + var NodeFsHandler = class { + /** + * @param {import("../index").FSWatcher} fsW + */ + constructor(fsW) { + this.fsw = fsW; + this._boundHandleError = (error) => fsW._handleError(error); + } + /** + * Watch file for changes with fs_watchFile or fs_watch. + * @param {String} path to file or dir + * @param {Function} listener on fs change + * @returns {Function} closer for the watcher instance + */ + _watchWithNodeFs(path3, listener) { + const opts = this.fsw.options; + const directory = sysPath.dirname(path3); + const basename = sysPath.basename(path3); + const parent = this.fsw._getWatchedDir(directory); + parent.add(basename); + const absolutePath = sysPath.resolve(path3); + const options = { persistent: opts.persistent }; + if (!listener) listener = EMPTY_FN; + let closer; + if (opts.usePolling) { + options.interval = opts.enableBinaryInterval && isBinaryPath(basename) ? opts.binaryInterval : opts.interval; + closer = setFsWatchFileListener(path3, absolutePath, options, { + listener, + rawEmitter: this.fsw._emitRaw + }); + } else { + closer = setFsWatchListener(path3, absolutePath, options, { + listener, + errHandler: this._boundHandleError, + rawEmitter: this.fsw._emitRaw + }); + } + return closer; + } + /** + * Watch a file and emit add event if warranted. + * @param {Path} file Path + * @param {fs.Stats} stats result of fs_stat + * @param {Boolean} initialAdd was the file added at watch instantiation? + * @returns {Function} closer for the watcher instance + */ + _handleFile(file, stats, initialAdd) { + if (this.fsw.closed) { + return; + } + const dirname = sysPath.dirname(file); + const basename = sysPath.basename(file); + const parent = this.fsw._getWatchedDir(dirname); + let prevStats = stats; + if (parent.has(basename)) return; + const listener = async (path3, newStats) => { + if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5)) return; + if (!newStats || newStats.mtimeMs === 0) { + try { + const newStats2 = await stat(file); + if (this.fsw.closed) return; + const at = newStats2.atimeMs; + const mt = newStats2.mtimeMs; + if (!at || at <= mt || mt !== prevStats.mtimeMs) { + this.fsw._emit(EV_CHANGE, file, newStats2); + } + if (isLinux && prevStats.ino !== newStats2.ino) { + this.fsw._closeFile(path3); + prevStats = newStats2; + this.fsw._addPathCloser(path3, this._watchWithNodeFs(file, listener)); + } else { + prevStats = newStats2; + } + } catch (error) { + this.fsw._remove(dirname, basename); + } + } else if (parent.has(basename)) { + const at = newStats.atimeMs; + const mt = newStats.mtimeMs; + if (!at || at <= mt || mt !== prevStats.mtimeMs) { + this.fsw._emit(EV_CHANGE, file, newStats); + } + prevStats = newStats; + } + }; + const closer = this._watchWithNodeFs(file, listener); + if (!(initialAdd && this.fsw.options.ignoreInitial) && this.fsw._isntIgnored(file)) { + if (!this.fsw._throttle(EV_ADD, file, 0)) return; + this.fsw._emit(EV_ADD, file, stats); + } + return closer; + } + /** + * Handle symlinks encountered while reading a dir. + * @param {Object} entry returned by readdirp + * @param {String} directory path of dir being read + * @param {String} path of this item + * @param {String} item basename of this item + * @returns {Promise} true if no more processing is needed for this entry. + */ + async _handleSymlink(entry, directory, path3, item) { + if (this.fsw.closed) { + return; + } + const full = entry.fullPath; + const dir = this.fsw._getWatchedDir(directory); + if (!this.fsw.options.followSymlinks) { + this.fsw._incrReadyCount(); + let linkPath; + try { + linkPath = await fsrealpath(path3); + } catch (e) { + this.fsw._emitReady(); + return true; + } + if (this.fsw.closed) return; + if (dir.has(item)) { + if (this.fsw._symlinkPaths.get(full) !== linkPath) { + this.fsw._symlinkPaths.set(full, linkPath); + this.fsw._emit(EV_CHANGE, path3, entry.stats); + } + } else { + dir.add(item); + this.fsw._symlinkPaths.set(full, linkPath); + this.fsw._emit(EV_ADD, path3, entry.stats); + } + this.fsw._emitReady(); + return true; + } + if (this.fsw._symlinkPaths.has(full)) { + return true; + } + this.fsw._symlinkPaths.set(full, true); + } + _handleRead(directory, initialAdd, wh, target, dir, depth, throttler) { + directory = sysPath.join(directory, EMPTY_STR); + if (!wh.hasGlob) { + throttler = this.fsw._throttle("readdir", directory, 1e3); + if (!throttler) return; + } + const previous = this.fsw._getWatchedDir(wh.path); + const current = /* @__PURE__ */ new Set(); + let stream = this.fsw._readdirp(directory, { + fileFilter: (entry) => wh.filterPath(entry), + directoryFilter: (entry) => wh.filterDir(entry), + depth: 0 + }).on(STR_DATA, async (entry) => { + if (this.fsw.closed) { + stream = void 0; + return; + } + const item = entry.path; + let path3 = sysPath.join(directory, item); + current.add(item); + if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path3, item)) { + return; + } + if (this.fsw.closed) { + stream = void 0; + return; + } + if (item === target || !target && !previous.has(item)) { + this.fsw._incrReadyCount(); + path3 = sysPath.join(dir, sysPath.relative(dir, path3)); + this._addToNodeFs(path3, initialAdd, wh, depth + 1); + } + }).on(EV_ERROR, this._boundHandleError); + return new Promise( + (resolve) => stream.once(STR_END, () => { + if (this.fsw.closed) { + stream = void 0; + return; + } + const wasThrottled = throttler ? throttler.clear() : false; + resolve(); + previous.getChildren().filter((item) => { + return item !== directory && !current.has(item) && // in case of intersecting globs; + // a path may have been filtered out of this readdir, but + // shouldn't be removed because it matches a different glob + (!wh.hasGlob || wh.filterPath({ + fullPath: sysPath.resolve(directory, item) + })); + }).forEach((item) => { + this.fsw._remove(directory, item); + }); + stream = void 0; + if (wasThrottled) this._handleRead(directory, false, wh, target, dir, depth, throttler); + }) + ); + } + /** + * Read directory to add / remove files from `@watched` list and re-read it on change. + * @param {String} dir fs path + * @param {fs.Stats} stats + * @param {Boolean} initialAdd + * @param {Number} depth relative to user-supplied path + * @param {String} target child path targeted for watch + * @param {Object} wh Common watch helpers for this path + * @param {String} realpath + * @returns {Promise} closer for the watcher instance. + */ + async _handleDir(dir, stats, initialAdd, depth, target, wh, realpath) { + const parentDir = this.fsw._getWatchedDir(sysPath.dirname(dir)); + const tracked = parentDir.has(sysPath.basename(dir)); + if (!(initialAdd && this.fsw.options.ignoreInitial) && !target && !tracked) { + if (!wh.hasGlob || wh.globFilter(dir)) this.fsw._emit(EV_ADD_DIR, dir, stats); + } + parentDir.add(sysPath.basename(dir)); + this.fsw._getWatchedDir(dir); + let throttler; + let closer; + const oDepth = this.fsw.options.depth; + if ((oDepth == null || depth <= oDepth) && !this.fsw._symlinkPaths.has(realpath)) { + if (!target) { + await this._handleRead(dir, initialAdd, wh, target, dir, depth, throttler); + if (this.fsw.closed) return; + } + closer = this._watchWithNodeFs(dir, (dirPath, stats2) => { + if (stats2 && stats2.mtimeMs === 0) return; + this._handleRead(dirPath, false, wh, target, dir, depth, throttler); + }); + } + return closer; + } + /** + * Handle added file, directory, or glob pattern. + * Delegates call to _handleFile / _handleDir after checks. + * @param {String} path to file or ir + * @param {Boolean} initialAdd was the file added at watch instantiation? + * @param {Object} priorWh depth relative to user-supplied path + * @param {Number} depth Child path actually targeted for watch + * @param {String=} target Child path actually targeted for watch + * @returns {Promise} + */ + async _addToNodeFs(path3, initialAdd, priorWh, depth, target) { + const ready = this.fsw._emitReady; + if (this.fsw._isIgnored(path3) || this.fsw.closed) { + ready(); + return false; + } + const wh = this.fsw._getWatchHelpers(path3, depth); + if (!wh.hasGlob && priorWh) { + wh.hasGlob = priorWh.hasGlob; + wh.globFilter = priorWh.globFilter; + wh.filterPath = (entry) => priorWh.filterPath(entry); + wh.filterDir = (entry) => priorWh.filterDir(entry); + } + try { + const stats = await statMethods[wh.statMethod](wh.watchPath); + if (this.fsw.closed) return; + if (this.fsw._isIgnored(wh.watchPath, stats)) { + ready(); + return false; + } + const follow = this.fsw.options.followSymlinks && !path3.includes(STAR) && !path3.includes(BRACE_START); + let closer; + if (stats.isDirectory()) { + const absPath = sysPath.resolve(path3); + const targetPath = follow ? await fsrealpath(path3) : path3; + if (this.fsw.closed) return; + closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath); + if (this.fsw.closed) return; + if (absPath !== targetPath && targetPath !== void 0) { + this.fsw._symlinkPaths.set(absPath, targetPath); + } + } else if (stats.isSymbolicLink()) { + const targetPath = follow ? await fsrealpath(path3) : path3; + if (this.fsw.closed) return; + const parent = sysPath.dirname(wh.watchPath); + this.fsw._getWatchedDir(parent).add(wh.watchPath); + this.fsw._emit(EV_ADD, wh.watchPath, stats); + closer = await this._handleDir(parent, stats, initialAdd, depth, path3, wh, targetPath); + if (this.fsw.closed) return; + if (targetPath !== void 0) { + this.fsw._symlinkPaths.set(sysPath.resolve(path3), targetPath); + } + } else { + closer = this._handleFile(wh.watchPath, stats, initialAdd); + } + ready(); + this.fsw._addPathCloser(path3, closer); + return false; + } catch (error) { + if (this.fsw._handleError(error)) { + ready(); + return path3; + } + } + } + }; + module2.exports = NodeFsHandler; + } +}); + +// node_modules/chokidar/lib/fsevents-handler.js +var require_fsevents_handler = __commonJS({ + "node_modules/chokidar/lib/fsevents-handler.js"(exports2, module2) { + "use strict"; + var fs2 = require("fs"); + var sysPath = require("path"); + var { promisify } = require("util"); + var fsevents; + try { + fsevents = require("fsevents"); + } catch (error) { + if (process.env.CHOKIDAR_PRINT_FSEVENTS_REQUIRE_ERROR) console.error(error); + } + if (fsevents) { + const mtch = process.version.match(/v(\d+)\.(\d+)/); + if (mtch && mtch[1] && mtch[2]) { + const maj = Number.parseInt(mtch[1], 10); + const min = Number.parseInt(mtch[2], 10); + if (maj === 8 && min < 16) { + fsevents = void 0; + } + } + } + var { + EV_ADD, + EV_CHANGE, + EV_ADD_DIR, + EV_UNLINK, + EV_ERROR, + STR_DATA, + STR_END, + FSEVENT_CREATED, + FSEVENT_MODIFIED, + FSEVENT_DELETED, + FSEVENT_MOVED, + // FSEVENT_CLONED, + FSEVENT_UNKNOWN, + FSEVENT_FLAG_MUST_SCAN_SUBDIRS, + FSEVENT_TYPE_FILE, + FSEVENT_TYPE_DIRECTORY, + FSEVENT_TYPE_SYMLINK, + ROOT_GLOBSTAR, + DIR_SUFFIX, + DOT_SLASH, + FUNCTION_TYPE, + EMPTY_FN, + IDENTITY_FN + } = require_constants4(); + var Depth = (value) => isNaN(value) ? {} : { depth: value }; + var stat = promisify(fs2.stat); + var lstat = promisify(fs2.lstat); + var realpath = promisify(fs2.realpath); + var statMethods = { stat, lstat }; + var FSEventsWatchers = /* @__PURE__ */ new Map(); + var consolidateThreshhold = 10; + var wrongEventFlags = /* @__PURE__ */ new Set([ + 69888, + 70400, + 71424, + 72704, + 73472, + 131328, + 131840, + 262912 + ]); + var createFSEventsInstance = (path3, callback) => { + const stop = fsevents.watch(path3, callback); + return { stop }; + }; + function setFSEventsListener(path3, realPath, listener, rawEmitter) { + let watchPath = sysPath.extname(realPath) ? sysPath.dirname(realPath) : realPath; + const parentPath = sysPath.dirname(watchPath); + let cont = FSEventsWatchers.get(watchPath); + if (couldConsolidate(parentPath)) { + watchPath = parentPath; + } + const resolvedPath = sysPath.resolve(path3); + const hasSymlink = resolvedPath !== realPath; + const filteredListener = (fullPath, flags, info) => { + if (hasSymlink) fullPath = fullPath.replace(realPath, resolvedPath); + if (fullPath === resolvedPath || !fullPath.indexOf(resolvedPath + sysPath.sep)) listener(fullPath, flags, info); + }; + let watchedParent = false; + for (const watchedPath of FSEventsWatchers.keys()) { + if (realPath.indexOf(sysPath.resolve(watchedPath) + sysPath.sep) === 0) { + watchPath = watchedPath; + cont = FSEventsWatchers.get(watchPath); + watchedParent = true; + break; + } + } + if (cont || watchedParent) { + cont.listeners.add(filteredListener); + } else { + cont = { + listeners: /* @__PURE__ */ new Set([filteredListener]), + rawEmitter, + watcher: createFSEventsInstance(watchPath, (fullPath, flags) => { + if (!cont.listeners.size) return; + if (flags & FSEVENT_FLAG_MUST_SCAN_SUBDIRS) return; + const info = fsevents.getInfo(fullPath, flags); + cont.listeners.forEach((list) => { + list(fullPath, flags, info); + }); + cont.rawEmitter(info.event, fullPath, info); + }) + }; + FSEventsWatchers.set(watchPath, cont); + } + return () => { + const lst = cont.listeners; + lst.delete(filteredListener); + if (!lst.size) { + FSEventsWatchers.delete(watchPath); + if (cont.watcher) return cont.watcher.stop().then(() => { + cont.rawEmitter = cont.watcher = void 0; + Object.freeze(cont); + }); + } + }; + } + var couldConsolidate = (path3) => { + let count = 0; + for (const watchPath of FSEventsWatchers.keys()) { + if (watchPath.indexOf(path3) === 0) { + count++; + if (count >= consolidateThreshhold) { + return true; + } + } + } + return false; + }; + var canUse = () => fsevents && FSEventsWatchers.size < 128; + var calcDepth = (path3, root) => { + let i = 0; + while (!path3.indexOf(root) && (path3 = sysPath.dirname(path3)) !== root) i++; + return i; + }; + var sameTypes = (info, stats) => info.type === FSEVENT_TYPE_DIRECTORY && stats.isDirectory() || info.type === FSEVENT_TYPE_SYMLINK && stats.isSymbolicLink() || info.type === FSEVENT_TYPE_FILE && stats.isFile(); + var FsEventsHandler = class { + /** + * @param {import('../index').FSWatcher} fsw + */ + constructor(fsw) { + this.fsw = fsw; + } + checkIgnored(path3, stats) { + const ipaths = this.fsw._ignoredPaths; + if (this.fsw._isIgnored(path3, stats)) { + ipaths.add(path3); + if (stats && stats.isDirectory()) { + ipaths.add(path3 + ROOT_GLOBSTAR); + } + return true; + } + ipaths.delete(path3); + ipaths.delete(path3 + ROOT_GLOBSTAR); + } + addOrChange(path3, fullPath, realPath, parent, watchedDir, item, info, opts) { + const event = watchedDir.has(item) ? EV_CHANGE : EV_ADD; + this.handleEvent(event, path3, fullPath, realPath, parent, watchedDir, item, info, opts); + } + async checkExists(path3, fullPath, realPath, parent, watchedDir, item, info, opts) { + try { + const stats = await stat(path3); + if (this.fsw.closed) return; + if (sameTypes(info, stats)) { + this.addOrChange(path3, fullPath, realPath, parent, watchedDir, item, info, opts); + } else { + this.handleEvent(EV_UNLINK, path3, fullPath, realPath, parent, watchedDir, item, info, opts); + } + } catch (error) { + if (error.code === "EACCES") { + this.addOrChange(path3, fullPath, realPath, parent, watchedDir, item, info, opts); + } else { + this.handleEvent(EV_UNLINK, path3, fullPath, realPath, parent, watchedDir, item, info, opts); + } + } + } + handleEvent(event, path3, fullPath, realPath, parent, watchedDir, item, info, opts) { + if (this.fsw.closed || this.checkIgnored(path3)) return; + if (event === EV_UNLINK) { + const isDirectory = info.type === FSEVENT_TYPE_DIRECTORY; + if (isDirectory || watchedDir.has(item)) { + this.fsw._remove(parent, item, isDirectory); + } + } else { + if (event === EV_ADD) { + if (info.type === FSEVENT_TYPE_DIRECTORY) this.fsw._getWatchedDir(path3); + if (info.type === FSEVENT_TYPE_SYMLINK && opts.followSymlinks) { + const curDepth = opts.depth === void 0 ? void 0 : calcDepth(fullPath, realPath) + 1; + return this._addToFsEvents(path3, false, true, curDepth); + } + this.fsw._getWatchedDir(parent).add(item); + } + const eventName = info.type === FSEVENT_TYPE_DIRECTORY ? event + DIR_SUFFIX : event; + this.fsw._emit(eventName, path3); + if (eventName === EV_ADD_DIR) this._addToFsEvents(path3, false, true); + } + } + /** + * Handle symlinks encountered during directory scan + * @param {String} watchPath - file/dir path to be watched with fsevents + * @param {String} realPath - real path (in case of symlinks) + * @param {Function} transform - path transformer + * @param {Function} globFilter - path filter in case a glob pattern was provided + * @returns {Function} closer for the watcher instance + */ + _watchWithFsEvents(watchPath, realPath, transform, globFilter) { + if (this.fsw.closed || this.fsw._isIgnored(watchPath)) return; + const opts = this.fsw.options; + const watchCallback = async (fullPath, flags, info) => { + if (this.fsw.closed) return; + if (opts.depth !== void 0 && calcDepth(fullPath, realPath) > opts.depth) return; + const path3 = transform(sysPath.join( + watchPath, + sysPath.relative(watchPath, fullPath) + )); + if (globFilter && !globFilter(path3)) return; + const parent = sysPath.dirname(path3); + const item = sysPath.basename(path3); + const watchedDir = this.fsw._getWatchedDir( + info.type === FSEVENT_TYPE_DIRECTORY ? path3 : parent + ); + if (wrongEventFlags.has(flags) || info.event === FSEVENT_UNKNOWN) { + if (typeof opts.ignored === FUNCTION_TYPE) { + let stats; + try { + stats = await stat(path3); + } catch (error) { + } + if (this.fsw.closed) return; + if (this.checkIgnored(path3, stats)) return; + if (sameTypes(info, stats)) { + this.addOrChange(path3, fullPath, realPath, parent, watchedDir, item, info, opts); + } else { + this.handleEvent(EV_UNLINK, path3, fullPath, realPath, parent, watchedDir, item, info, opts); + } + } else { + this.checkExists(path3, fullPath, realPath, parent, watchedDir, item, info, opts); + } + } else { + switch (info.event) { + case FSEVENT_CREATED: + case FSEVENT_MODIFIED: + return this.addOrChange(path3, fullPath, realPath, parent, watchedDir, item, info, opts); + case FSEVENT_DELETED: + case FSEVENT_MOVED: + return this.checkExists(path3, fullPath, realPath, parent, watchedDir, item, info, opts); + } + } + }; + const closer = setFSEventsListener( + watchPath, + realPath, + watchCallback, + this.fsw._emitRaw + ); + this.fsw._emitReady(); + return closer; + } + /** + * Handle symlinks encountered during directory scan + * @param {String} linkPath path to symlink + * @param {String} fullPath absolute path to the symlink + * @param {Function} transform pre-existing path transformer + * @param {Number} curDepth level of subdirectories traversed to where symlink is + * @returns {Promise} + */ + async _handleFsEventsSymlink(linkPath, fullPath, transform, curDepth) { + if (this.fsw.closed || this.fsw._symlinkPaths.has(fullPath)) return; + this.fsw._symlinkPaths.set(fullPath, true); + this.fsw._incrReadyCount(); + try { + const linkTarget = await realpath(linkPath); + if (this.fsw.closed) return; + if (this.fsw._isIgnored(linkTarget)) { + return this.fsw._emitReady(); + } + this.fsw._incrReadyCount(); + this._addToFsEvents(linkTarget || linkPath, (path3) => { + let aliasedPath = linkPath; + if (linkTarget && linkTarget !== DOT_SLASH) { + aliasedPath = path3.replace(linkTarget, linkPath); + } else if (path3 !== DOT_SLASH) { + aliasedPath = sysPath.join(linkPath, path3); + } + return transform(aliasedPath); + }, false, curDepth); + } catch (error) { + if (this.fsw._handleError(error)) { + return this.fsw._emitReady(); + } + } + } + /** + * + * @param {Path} newPath + * @param {fs.Stats} stats + */ + emitAdd(newPath, stats, processPath, opts, forceAdd) { + const pp = processPath(newPath); + const isDir = stats.isDirectory(); + const dirObj = this.fsw._getWatchedDir(sysPath.dirname(pp)); + const base = sysPath.basename(pp); + if (isDir) this.fsw._getWatchedDir(pp); + if (dirObj.has(base)) return; + dirObj.add(base); + if (!opts.ignoreInitial || forceAdd === true) { + this.fsw._emit(isDir ? EV_ADD_DIR : EV_ADD, pp, stats); + } + } + initWatch(realPath, path3, wh, processPath) { + if (this.fsw.closed) return; + const closer = this._watchWithFsEvents( + wh.watchPath, + sysPath.resolve(realPath || wh.watchPath), + processPath, + wh.globFilter + ); + this.fsw._addPathCloser(path3, closer); + } + /** + * Handle added path with fsevents + * @param {String} path file/dir path or glob pattern + * @param {Function|Boolean=} transform converts working path to what the user expects + * @param {Boolean=} forceAdd ensure add is emitted + * @param {Number=} priorDepth Level of subdirectories already traversed. + * @returns {Promise} + */ + async _addToFsEvents(path3, transform, forceAdd, priorDepth) { + if (this.fsw.closed) { + return; + } + const opts = this.fsw.options; + const processPath = typeof transform === FUNCTION_TYPE ? transform : IDENTITY_FN; + const wh = this.fsw._getWatchHelpers(path3); + try { + const stats = await statMethods[wh.statMethod](wh.watchPath); + if (this.fsw.closed) return; + if (this.fsw._isIgnored(wh.watchPath, stats)) { + throw null; + } + if (stats.isDirectory()) { + if (!wh.globFilter) this.emitAdd(processPath(path3), stats, processPath, opts, forceAdd); + if (priorDepth && priorDepth > opts.depth) return; + this.fsw._readdirp(wh.watchPath, { + fileFilter: (entry) => wh.filterPath(entry), + directoryFilter: (entry) => wh.filterDir(entry), + ...Depth(opts.depth - (priorDepth || 0)) + }).on(STR_DATA, (entry) => { + if (this.fsw.closed) { + return; + } + if (entry.stats.isDirectory() && !wh.filterPath(entry)) return; + const joinedPath = sysPath.join(wh.watchPath, entry.path); + const { fullPath } = entry; + if (wh.followSymlinks && entry.stats.isSymbolicLink()) { + const curDepth = opts.depth === void 0 ? void 0 : calcDepth(joinedPath, sysPath.resolve(wh.watchPath)) + 1; + this._handleFsEventsSymlink(joinedPath, fullPath, processPath, curDepth); + } else { + this.emitAdd(joinedPath, entry.stats, processPath, opts, forceAdd); + } + }).on(EV_ERROR, EMPTY_FN).on(STR_END, () => { + this.fsw._emitReady(); + }); + } else { + this.emitAdd(wh.watchPath, stats, processPath, opts, forceAdd); + this.fsw._emitReady(); + } + } catch (error) { + if (!error || this.fsw._handleError(error)) { + this.fsw._emitReady(); + this.fsw._emitReady(); + } + } + if (opts.persistent && forceAdd !== true) { + if (typeof transform === FUNCTION_TYPE) { + this.initWatch(void 0, path3, wh, processPath); + } else { + let realPath; + try { + realPath = await realpath(wh.watchPath); + } catch (e) { + } + this.initWatch(realPath, path3, wh, processPath); + } + } + } + }; + module2.exports = FsEventsHandler; + module2.exports.canUse = canUse; + } +}); + +// node_modules/chokidar/index.js +var require_chokidar = __commonJS({ + "node_modules/chokidar/index.js"(exports2) { + "use strict"; + var { EventEmitter: EventEmitter2 } = require("events"); + var fs2 = require("fs"); + var sysPath = require("path"); + var { promisify } = require("util"); + var readdirp = require_readdirp(); + var anymatch = require_anymatch().default; + var globParent = require_glob_parent(); + var isGlob = require_is_glob(); + var braces = require_braces(); + var normalizePath = require_normalize_path(); + var NodeFsHandler = require_nodefs_handler(); + var FsEventsHandler = require_fsevents_handler(); + var { + EV_ALL, + EV_READY, + EV_ADD, + EV_CHANGE, + EV_UNLINK, + EV_ADD_DIR, + EV_UNLINK_DIR, + EV_RAW, + EV_ERROR, + STR_CLOSE, + STR_END, + BACK_SLASH_RE, + DOUBLE_SLASH_RE, + SLASH_OR_BACK_SLASH_RE, + DOT_RE, + REPLACER_RE, + SLASH, + SLASH_SLASH, + BRACE_START, + BANG, + ONE_DOT, + TWO_DOTS, + GLOBSTAR, + SLASH_GLOBSTAR, + ANYMATCH_OPTS, + STRING_TYPE, + FUNCTION_TYPE, + EMPTY_STR, + EMPTY_FN, + isWindows, + isMacos, + isIBMi + } = require_constants4(); + var stat = promisify(fs2.stat); + var readdir = promisify(fs2.readdir); + var arrify = (value = []) => Array.isArray(value) ? value : [value]; + var flatten = (list, result = []) => { + list.forEach((item) => { + if (Array.isArray(item)) { + flatten(item, result); + } else { + result.push(item); + } + }); + return result; + }; + var unifyPaths = (paths_) => { + const paths = flatten(arrify(paths_)); + if (!paths.every((p) => typeof p === STRING_TYPE)) { + throw new TypeError(`Non-string provided as watch path: ${paths}`); + } + return paths.map(normalizePathToUnix); + }; + var toUnix = (string) => { + let str = string.replace(BACK_SLASH_RE, SLASH); + let prepend = false; + if (str.startsWith(SLASH_SLASH)) { + prepend = true; + } + while (str.match(DOUBLE_SLASH_RE)) { + str = str.replace(DOUBLE_SLASH_RE, SLASH); + } + if (prepend) { + str = SLASH + str; + } + return str; + }; + var normalizePathToUnix = (path3) => toUnix(sysPath.normalize(toUnix(path3))); + var normalizeIgnored = (cwd = EMPTY_STR) => (path3) => { + if (typeof path3 !== STRING_TYPE) return path3; + return normalizePathToUnix(sysPath.isAbsolute(path3) ? path3 : sysPath.join(cwd, path3)); + }; + var getAbsolutePath = (path3, cwd) => { + if (sysPath.isAbsolute(path3)) { + return path3; + } + if (path3.startsWith(BANG)) { + return BANG + sysPath.join(cwd, path3.slice(1)); + } + return sysPath.join(cwd, path3); + }; + var undef = (opts, key) => opts[key] === void 0; + var DirEntry = class { + /** + * @param {Path} dir + * @param {Function} removeWatcher + */ + constructor(dir, removeWatcher) { + this.path = dir; + this._removeWatcher = removeWatcher; + this.items = /* @__PURE__ */ new Set(); + } + add(item) { + const { items } = this; + if (!items) return; + if (item !== ONE_DOT && item !== TWO_DOTS) items.add(item); + } + async remove(item) { + const { items } = this; + if (!items) return; + items.delete(item); + if (items.size > 0) return; + const dir = this.path; + try { + await readdir(dir); + } catch (err) { + if (this._removeWatcher) { + this._removeWatcher(sysPath.dirname(dir), sysPath.basename(dir)); + } + } + } + has(item) { + const { items } = this; + if (!items) return; + return items.has(item); + } + /** + * @returns {Array} + */ + getChildren() { + const { items } = this; + if (!items) return; + return [...items.values()]; + } + dispose() { + this.items.clear(); + delete this.path; + delete this._removeWatcher; + delete this.items; + Object.freeze(this); + } + }; + var STAT_METHOD_F = "stat"; + var STAT_METHOD_L = "lstat"; + var WatchHelper = class { + constructor(path3, watchPath, follow, fsw) { + this.fsw = fsw; + this.path = path3 = path3.replace(REPLACER_RE, EMPTY_STR); + this.watchPath = watchPath; + this.fullWatchPath = sysPath.resolve(watchPath); + this.hasGlob = watchPath !== path3; + if (path3 === EMPTY_STR) this.hasGlob = false; + this.globSymlink = this.hasGlob && follow ? void 0 : false; + this.globFilter = this.hasGlob ? anymatch(path3, void 0, ANYMATCH_OPTS) : false; + this.dirParts = this.getDirParts(path3); + this.dirParts.forEach((parts) => { + if (parts.length > 1) parts.pop(); + }); + this.followSymlinks = follow; + this.statMethod = follow ? STAT_METHOD_F : STAT_METHOD_L; + } + checkGlobSymlink(entry) { + if (this.globSymlink === void 0) { + this.globSymlink = entry.fullParentDir === this.fullWatchPath ? false : { realPath: entry.fullParentDir, linkPath: this.fullWatchPath }; + } + if (this.globSymlink) { + return entry.fullPath.replace(this.globSymlink.realPath, this.globSymlink.linkPath); + } + return entry.fullPath; + } + entryPath(entry) { + return sysPath.join( + this.watchPath, + sysPath.relative(this.watchPath, this.checkGlobSymlink(entry)) + ); + } + filterPath(entry) { + const { stats } = entry; + if (stats && stats.isSymbolicLink()) return this.filterDir(entry); + const resolvedPath = this.entryPath(entry); + const matchesGlob = this.hasGlob && typeof this.globFilter === FUNCTION_TYPE ? this.globFilter(resolvedPath) : true; + return matchesGlob && this.fsw._isntIgnored(resolvedPath, stats) && this.fsw._hasReadPermissions(stats); + } + getDirParts(path3) { + if (!this.hasGlob) return []; + const parts = []; + const expandedPath = path3.includes(BRACE_START) ? braces.expand(path3) : [path3]; + expandedPath.forEach((path4) => { + parts.push(sysPath.relative(this.watchPath, path4).split(SLASH_OR_BACK_SLASH_RE)); + }); + return parts; + } + filterDir(entry) { + if (this.hasGlob) { + const entryParts = this.getDirParts(this.checkGlobSymlink(entry)); + let globstar = false; + this.unmatchedGlob = !this.dirParts.some((parts) => { + return parts.every((part, i) => { + if (part === GLOBSTAR) globstar = true; + return globstar || !entryParts[0][i] || anymatch(part, entryParts[0][i], ANYMATCH_OPTS); + }); + }); + } + return !this.unmatchedGlob && this.fsw._isntIgnored(this.entryPath(entry), entry.stats); + } + }; + var FSWatcher = class extends EventEmitter2 { + // Not indenting methods for history sake; for now. + constructor(_opts) { + super(); + const opts = {}; + if (_opts) Object.assign(opts, _opts); + this._watched = /* @__PURE__ */ new Map(); + this._closers = /* @__PURE__ */ new Map(); + this._ignoredPaths = /* @__PURE__ */ new Set(); + this._throttled = /* @__PURE__ */ new Map(); + this._symlinkPaths = /* @__PURE__ */ new Map(); + this._streams = /* @__PURE__ */ new Set(); + this.closed = false; + if (undef(opts, "persistent")) opts.persistent = true; + if (undef(opts, "ignoreInitial")) opts.ignoreInitial = false; + if (undef(opts, "ignorePermissionErrors")) opts.ignorePermissionErrors = false; + if (undef(opts, "interval")) opts.interval = 100; + if (undef(opts, "binaryInterval")) opts.binaryInterval = 300; + if (undef(opts, "disableGlobbing")) opts.disableGlobbing = false; + opts.enableBinaryInterval = opts.binaryInterval !== opts.interval; + if (undef(opts, "useFsEvents")) opts.useFsEvents = !opts.usePolling; + const canUseFsEvents = FsEventsHandler.canUse(); + if (!canUseFsEvents) opts.useFsEvents = false; + if (undef(opts, "usePolling") && !opts.useFsEvents) { + opts.usePolling = isMacos; + } + if (isIBMi) { + opts.usePolling = true; + } + const envPoll = process.env.CHOKIDAR_USEPOLLING; + if (envPoll !== void 0) { + const envLower = envPoll.toLowerCase(); + if (envLower === "false" || envLower === "0") { + opts.usePolling = false; + } else if (envLower === "true" || envLower === "1") { + opts.usePolling = true; + } else { + opts.usePolling = !!envLower; + } + } + const envInterval = process.env.CHOKIDAR_INTERVAL; + if (envInterval) { + opts.interval = Number.parseInt(envInterval, 10); + } + if (undef(opts, "atomic")) opts.atomic = !opts.usePolling && !opts.useFsEvents; + if (opts.atomic) this._pendingUnlinks = /* @__PURE__ */ new Map(); + if (undef(opts, "followSymlinks")) opts.followSymlinks = true; + if (undef(opts, "awaitWriteFinish")) opts.awaitWriteFinish = false; + if (opts.awaitWriteFinish === true) opts.awaitWriteFinish = {}; + const awf = opts.awaitWriteFinish; + if (awf) { + if (!awf.stabilityThreshold) awf.stabilityThreshold = 2e3; + if (!awf.pollInterval) awf.pollInterval = 100; + this._pendingWrites = /* @__PURE__ */ new Map(); + } + if (opts.ignored) opts.ignored = arrify(opts.ignored); + let readyCalls = 0; + this._emitReady = () => { + readyCalls++; + if (readyCalls >= this._readyCount) { + this._emitReady = EMPTY_FN; + this._readyEmitted = true; + process.nextTick(() => this.emit(EV_READY)); + } + }; + this._emitRaw = (...args) => this.emit(EV_RAW, ...args); + this._readyEmitted = false; + this.options = opts; + if (opts.useFsEvents) { + this._fsEventsHandler = new FsEventsHandler(this); + } else { + this._nodeFsHandler = new NodeFsHandler(this); + } + Object.freeze(opts); + } + // Public methods + /** + * Adds paths to be watched on an existing FSWatcher instance + * @param {Path|Array} paths_ + * @param {String=} _origAdd private; for handling non-existent paths to be watched + * @param {Boolean=} _internal private; indicates a non-user add + * @returns {FSWatcher} for chaining + */ + add(paths_, _origAdd, _internal) { + const { cwd, disableGlobbing } = this.options; + this.closed = false; + let paths = unifyPaths(paths_); + if (cwd) { + paths = paths.map((path3) => { + const absPath = getAbsolutePath(path3, cwd); + if (disableGlobbing || !isGlob(path3)) { + return absPath; + } + return normalizePath(absPath); + }); + } + paths = paths.filter((path3) => { + if (path3.startsWith(BANG)) { + this._ignoredPaths.add(path3.slice(1)); + return false; + } + this._ignoredPaths.delete(path3); + this._ignoredPaths.delete(path3 + SLASH_GLOBSTAR); + this._userIgnored = void 0; + return true; + }); + if (this.options.useFsEvents && this._fsEventsHandler) { + if (!this._readyCount) this._readyCount = paths.length; + if (this.options.persistent) this._readyCount += paths.length; + paths.forEach((path3) => this._fsEventsHandler._addToFsEvents(path3)); + } else { + if (!this._readyCount) this._readyCount = 0; + this._readyCount += paths.length; + Promise.all( + paths.map(async (path3) => { + const res = await this._nodeFsHandler._addToNodeFs(path3, !_internal, 0, 0, _origAdd); + if (res) this._emitReady(); + return res; + }) + ).then((results) => { + if (this.closed) return; + results.filter((item) => item).forEach((item) => { + this.add(sysPath.dirname(item), sysPath.basename(_origAdd || item)); + }); + }); + } + return this; + } + /** + * Close watchers or start ignoring events from specified paths. + * @param {Path|Array} paths_ - string or array of strings, file/directory paths and/or globs + * @returns {FSWatcher} for chaining + */ + unwatch(paths_) { + if (this.closed) return this; + const paths = unifyPaths(paths_); + const { cwd } = this.options; + paths.forEach((path3) => { + if (!sysPath.isAbsolute(path3) && !this._closers.has(path3)) { + if (cwd) path3 = sysPath.join(cwd, path3); + path3 = sysPath.resolve(path3); + } + this._closePath(path3); + this._ignoredPaths.add(path3); + if (this._watched.has(path3)) { + this._ignoredPaths.add(path3 + SLASH_GLOBSTAR); + } + this._userIgnored = void 0; + }); + return this; + } + /** + * Close watchers and remove all listeners from watched paths. + * @returns {Promise}. + */ + close() { + if (this.closed) return this._closePromise; + this.closed = true; + this.removeAllListeners(); + const closers = []; + this._closers.forEach((closerList) => closerList.forEach((closer) => { + const promise = closer(); + if (promise instanceof Promise) closers.push(promise); + })); + this._streams.forEach((stream) => stream.destroy()); + this._userIgnored = void 0; + this._readyCount = 0; + this._readyEmitted = false; + this._watched.forEach((dirent) => dirent.dispose()); + ["closers", "watched", "streams", "symlinkPaths", "throttled"].forEach((key) => { + this[`_${key}`].clear(); + }); + this._closePromise = closers.length ? Promise.all(closers).then(() => void 0) : Promise.resolve(); + return this._closePromise; + } + /** + * Expose list of watched paths + * @returns {Object} for chaining + */ + getWatched() { + const watchList = {}; + this._watched.forEach((entry, dir) => { + const key = this.options.cwd ? sysPath.relative(this.options.cwd, dir) : dir; + watchList[key || ONE_DOT] = entry.getChildren().sort(); + }); + return watchList; + } + emitWithAll(event, args) { + this.emit(...args); + if (event !== EV_ERROR) this.emit(EV_ALL, ...args); + } + // Common helpers + // -------------- + /** + * Normalize and emit events. + * Calling _emit DOES NOT MEAN emit() would be called! + * @param {EventName} event Type of event + * @param {Path} path File or directory path + * @param {*=} val1 arguments to be passed with event + * @param {*=} val2 + * @param {*=} val3 + * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag + */ + async _emit(event, path3, val1, val2, val3) { + if (this.closed) return; + const opts = this.options; + if (isWindows) path3 = sysPath.normalize(path3); + if (opts.cwd) path3 = sysPath.relative(opts.cwd, path3); + const args = [event, path3]; + if (val3 !== void 0) args.push(val1, val2, val3); + else if (val2 !== void 0) args.push(val1, val2); + else if (val1 !== void 0) args.push(val1); + const awf = opts.awaitWriteFinish; + let pw; + if (awf && (pw = this._pendingWrites.get(path3))) { + pw.lastChange = /* @__PURE__ */ new Date(); + return this; + } + if (opts.atomic) { + if (event === EV_UNLINK) { + this._pendingUnlinks.set(path3, args); + setTimeout(() => { + this._pendingUnlinks.forEach((entry, path4) => { + this.emit(...entry); + this.emit(EV_ALL, ...entry); + this._pendingUnlinks.delete(path4); + }); + }, typeof opts.atomic === "number" ? opts.atomic : 100); + return this; + } + if (event === EV_ADD && this._pendingUnlinks.has(path3)) { + event = args[0] = EV_CHANGE; + this._pendingUnlinks.delete(path3); + } + } + if (awf && (event === EV_ADD || event === EV_CHANGE) && this._readyEmitted) { + const awfEmit = (err, stats) => { + if (err) { + event = args[0] = EV_ERROR; + args[1] = err; + this.emitWithAll(event, args); + } else if (stats) { + if (args.length > 2) { + args[2] = stats; + } else { + args.push(stats); + } + this.emitWithAll(event, args); + } + }; + this._awaitWriteFinish(path3, awf.stabilityThreshold, event, awfEmit); + return this; + } + if (event === EV_CHANGE) { + const isThrottled = !this._throttle(EV_CHANGE, path3, 50); + if (isThrottled) return this; + } + if (opts.alwaysStat && val1 === void 0 && (event === EV_ADD || event === EV_ADD_DIR || event === EV_CHANGE)) { + const fullPath = opts.cwd ? sysPath.join(opts.cwd, path3) : path3; + let stats; + try { + stats = await stat(fullPath); + } catch (err) { + } + if (!stats || this.closed) return; + args.push(stats); + } + this.emitWithAll(event, args); + return this; + } + /** + * Common handler for errors + * @param {Error} error + * @returns {Error|Boolean} The error if defined, otherwise the value of the FSWatcher instance's `closed` flag + */ + _handleError(error) { + const code = error && error.code; + if (error && code !== "ENOENT" && code !== "ENOTDIR" && (!this.options.ignorePermissionErrors || code !== "EPERM" && code !== "EACCES")) { + this.emit(EV_ERROR, error); + } + return error || this.closed; + } + /** + * Helper utility for throttling + * @param {ThrottleType} actionType type being throttled + * @param {Path} path being acted upon + * @param {Number} timeout duration of time to suppress duplicate actions + * @returns {Object|false} tracking object or false if action should be suppressed + */ + _throttle(actionType, path3, timeout) { + if (!this._throttled.has(actionType)) { + this._throttled.set(actionType, /* @__PURE__ */ new Map()); + } + const action = this._throttled.get(actionType); + const actionPath = action.get(path3); + if (actionPath) { + actionPath.count++; + return false; + } + let timeoutObject; + const clear = () => { + const item = action.get(path3); + const count = item ? item.count : 0; + action.delete(path3); + clearTimeout(timeoutObject); + if (item) clearTimeout(item.timeoutObject); + return count; + }; + timeoutObject = setTimeout(clear, timeout); + const thr = { timeoutObject, clear, count: 0 }; + action.set(path3, thr); + return thr; + } + _incrReadyCount() { + return this._readyCount++; + } + /** + * Awaits write operation to finish. + * Polls a newly created file for size variations. When files size does not change for 'threshold' milliseconds calls callback. + * @param {Path} path being acted upon + * @param {Number} threshold Time in milliseconds a file size must be fixed before acknowledging write OP is finished + * @param {EventName} event + * @param {Function} awfEmit Callback to be called when ready for event to be emitted. + */ + _awaitWriteFinish(path3, threshold, event, awfEmit) { + let timeoutHandler; + let fullPath = path3; + if (this.options.cwd && !sysPath.isAbsolute(path3)) { + fullPath = sysPath.join(this.options.cwd, path3); + } + const now = /* @__PURE__ */ new Date(); + const awaitWriteFinish = (prevStat) => { + fs2.stat(fullPath, (err, curStat) => { + if (err || !this._pendingWrites.has(path3)) { + if (err && err.code !== "ENOENT") awfEmit(err); + return; + } + const now2 = Number(/* @__PURE__ */ new Date()); + if (prevStat && curStat.size !== prevStat.size) { + this._pendingWrites.get(path3).lastChange = now2; + } + const pw = this._pendingWrites.get(path3); + const df = now2 - pw.lastChange; + if (df >= threshold) { + this._pendingWrites.delete(path3); + awfEmit(void 0, curStat); + } else { + timeoutHandler = setTimeout( + awaitWriteFinish, + this.options.awaitWriteFinish.pollInterval, + curStat + ); + } + }); + }; + if (!this._pendingWrites.has(path3)) { + this._pendingWrites.set(path3, { + lastChange: now, + cancelWait: () => { + this._pendingWrites.delete(path3); + clearTimeout(timeoutHandler); + return event; + } + }); + timeoutHandler = setTimeout( + awaitWriteFinish, + this.options.awaitWriteFinish.pollInterval + ); + } + } + _getGlobIgnored() { + return [...this._ignoredPaths.values()]; + } + /** + * Determines whether user has asked to ignore this path. + * @param {Path} path filepath or dir + * @param {fs.Stats=} stats result of fs.stat + * @returns {Boolean} + */ + _isIgnored(path3, stats) { + if (this.options.atomic && DOT_RE.test(path3)) return true; + if (!this._userIgnored) { + const { cwd } = this.options; + const ign = this.options.ignored; + const ignored = ign && ign.map(normalizeIgnored(cwd)); + const paths = arrify(ignored).filter((path4) => typeof path4 === STRING_TYPE && !isGlob(path4)).map((path4) => path4 + SLASH_GLOBSTAR); + const list = this._getGlobIgnored().map(normalizeIgnored(cwd)).concat(ignored, paths); + this._userIgnored = anymatch(list, void 0, ANYMATCH_OPTS); + } + return this._userIgnored([path3, stats]); + } + _isntIgnored(path3, stat2) { + return !this._isIgnored(path3, stat2); + } + /** + * Provides a set of common helpers and properties relating to symlink and glob handling. + * @param {Path} path file, directory, or glob pattern being watched + * @param {Number=} depth at any depth > 0, this isn't a glob + * @returns {WatchHelper} object containing helpers for this path + */ + _getWatchHelpers(path3, depth) { + const watchPath = depth || this.options.disableGlobbing || !isGlob(path3) ? path3 : globParent(path3); + const follow = this.options.followSymlinks; + return new WatchHelper(path3, watchPath, follow, this); + } + // Directory helpers + // ----------------- + /** + * Provides directory tracking objects + * @param {String} directory path of the directory + * @returns {DirEntry} the directory's tracking object + */ + _getWatchedDir(directory) { + if (!this._boundRemove) this._boundRemove = this._remove.bind(this); + const dir = sysPath.resolve(directory); + if (!this._watched.has(dir)) this._watched.set(dir, new DirEntry(dir, this._boundRemove)); + return this._watched.get(dir); + } + // File helpers + // ------------ + /** + * Check for read permissions. + * Based on this answer on SO: https://stackoverflow.com/a/11781404/1358405 + * @param {fs.Stats} stats - object, result of fs_stat + * @returns {Boolean} indicates whether the file can be read + */ + _hasReadPermissions(stats) { + if (this.options.ignorePermissionErrors) return true; + const md = stats && Number.parseInt(stats.mode, 10); + const st = md & 511; + const it = Number.parseInt(st.toString(8)[0], 10); + return Boolean(4 & it); + } + /** + * Handles emitting unlink events for + * files and directories, and via recursion, for + * files and directories within directories that are unlinked + * @param {String} directory within which the following item is located + * @param {String} item base path of item/directory + * @returns {void} + */ + _remove(directory, item, isDirectory) { + const path3 = sysPath.join(directory, item); + const fullPath = sysPath.resolve(path3); + isDirectory = isDirectory != null ? isDirectory : this._watched.has(path3) || this._watched.has(fullPath); + if (!this._throttle("remove", path3, 100)) return; + if (!isDirectory && !this.options.useFsEvents && this._watched.size === 1) { + this.add(directory, item, true); + } + const wp = this._getWatchedDir(path3); + const nestedDirectoryChildren = wp.getChildren(); + nestedDirectoryChildren.forEach((nested) => this._remove(path3, nested)); + const parent = this._getWatchedDir(directory); + const wasTracked = parent.has(item); + parent.remove(item); + if (this._symlinkPaths.has(fullPath)) { + this._symlinkPaths.delete(fullPath); + } + let relPath = path3; + if (this.options.cwd) relPath = sysPath.relative(this.options.cwd, path3); + if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) { + const event = this._pendingWrites.get(relPath).cancelWait(); + if (event === EV_ADD) return; + } + this._watched.delete(path3); + this._watched.delete(fullPath); + const eventName = isDirectory ? EV_UNLINK_DIR : EV_UNLINK; + if (wasTracked && !this._isIgnored(path3)) this._emit(eventName, path3); + if (!this.options.useFsEvents) { + this._closePath(path3); + } + } + /** + * Closes all watchers for a path + * @param {Path} path + */ + _closePath(path3) { + this._closeFile(path3); + const dir = sysPath.dirname(path3); + this._getWatchedDir(dir).remove(sysPath.basename(path3)); + } + /** + * Closes only file-specific watchers + * @param {Path} path + */ + _closeFile(path3) { + const closers = this._closers.get(path3); + if (!closers) return; + closers.forEach((closer) => closer()); + this._closers.delete(path3); + } + /** + * + * @param {Path} path + * @param {Function} closer + */ + _addPathCloser(path3, closer) { + if (!closer) return; + let list = this._closers.get(path3); + if (!list) { + list = []; + this._closers.set(path3, list); + } + list.push(closer); + } + _readdirp(root, opts) { + if (this.closed) return; + const options = { type: EV_ALL, alwaysStat: true, lstat: true, ...opts }; + let stream = readdirp(root, options); + this._streams.add(stream); + stream.once(STR_CLOSE, () => { + stream = void 0; + }); + stream.once(STR_END, () => { + if (stream) { + this._streams.delete(stream); + stream = void 0; + } + }); + return stream; + } + }; + exports2.FSWatcher = FSWatcher; + var watch = (paths, options) => { + const watcher = new FSWatcher(options); + watcher.add(paths); + return watcher; + }; + exports2.watch = watch; + } +}); + +// packages/playwright-core/src/serverRegistry.ts +var serverRegistry_exports = {}; +__export(serverRegistry_exports, { + serverRegistry: () => serverRegistry +}); +module.exports = __toCommonJS(serverRegistry_exports); +var import_events = require("events"); +var import_fs = __toESM(require("fs")); +var import_net = __toESM(require("net")); +var import_path2 = __toESM(require("path")); +var import_os = __toESM(require("os")); +var import_chokidar = __toESM(require_chokidar()); + +// packages/playwright-core/src/package.ts +var import_path = __toESM(require("path")); +var packageRoot = import_path.default.join(__dirname, ".."); +var packageJSON = require(import_path.default.join(packageRoot, "package.json")); +var binPath = import_path.default.join(packageRoot, "bin"); + +// packages/playwright-core/src/serverRegistry.ts +var packageVersion = packageJSON.version; +var ServerRegistry = class extends import_events.EventEmitter { + constructor() { + super(...arguments); + this._descriptors = /* @__PURE__ */ new Map(); + this._watcherRefs = 0; + } + on(event, listener) { + return super.on(event, listener); + } + off(event, listener) { + return super.off(event, listener); + } + watch() { + this._watcherRefs++; + if (!this._watcher) + this._startWatcher(); + let disposed = false; + return () => { + if (disposed) + return; + disposed = true; + this._watcherRefs--; + if (this._watcherRefs === 0) + this._stopWatcher(); + }; + } + ready() { + return this._ready ?? Promise.resolve(); + } + async list() { + const ownWatcher = !this._watcher; + let dispose; + if (ownWatcher) + dispose = this.watch(); + try { + await this._ready; + const statuses = await Promise.all( + [...this._descriptors.values()].map(async (descriptor) => { + const canConnect = await canConnectTo(descriptor); + return { descriptor, canConnect }; + }) + ); + const result = /* @__PURE__ */ new Map(); + for (const { descriptor, canConnect } of statuses) { + if (!canConnect) { + await import_fs.default.promises.unlink(import_path2.default.join(this._browsersDir(), descriptor.browser.guid)).catch(() => { + }); + continue; + } + const key = descriptor.workspaceDir ?? ""; + let list = result.get(key); + if (!list) { + list = []; + result.set(key, list); + } + list.push(descriptor); + } + return result; + } finally { + dispose?.(); + } + } + async create(browser, endpoint) { + const file = import_path2.default.join(this._browsersDir(), browser.guid); + await import_fs.default.promises.mkdir(this._browsersDir(), { recursive: true }); + const descriptor = { + playwrightVersion: packageVersion, + playwrightLib: packageRoot, + title: endpoint.title, + browser, + endpoint: endpoint.endpoint, + workspaceDir: endpoint.workspaceDir + }; + await import_fs.default.promises.writeFile(file, JSON.stringify(descriptor, null, 2), "utf-8"); + } + async delete(guid) { + const file = import_path2.default.join(this._browsersDir(), guid); + await import_fs.default.promises.unlink(file).catch(() => { + }); + } + async deleteUserData(guid) { + const filePath = import_path2.default.join(this._browsersDir(), guid); + const content = await import_fs.default.promises.readFile(filePath, "utf-8"); + const descriptor = JSON.parse(content); + if (descriptor.browser.userDataDir) + await import_fs.default.promises.rm(descriptor.browser.userDataDir, { recursive: true, force: true }); + await import_fs.default.promises.unlink(filePath); + } + readDescriptor(guid) { + const cached = this._descriptors.get(guid); + if (cached) + return cached; + const filePath = import_path2.default.join(this._browsersDir(), guid); + const content = import_fs.default.readFileSync(filePath, "utf-8"); + return JSON.parse(content); + } + async find(name) { + const entries = await this.list(); + for (const [, browsers] of entries) { + for (const browser of browsers) { + if (browser.title === name) + return browser; + } + } + return null; + } + _browsersDir() { + return process.env.PWTEST_SERVER_REGISTRY || registryDirectory; + } + _startWatcher() { + const dir = this._browsersDir(); + try { + import_fs.default.mkdirSync(dir, { recursive: true }); + } catch { + } + const watcher = import_chokidar.default.watch(dir, { + ignoreInitial: false, + depth: 0, + awaitWriteFinish: { stabilityThreshold: 50, pollInterval: 20 } + }); + this._watcher = watcher; + this._ready = new Promise((resolve, reject) => { + watcher.once("ready", () => resolve()); + watcher.once("error", reject); + }); + watcher.on("add", (file) => this._onAddOrChange(file, "added")); + watcher.on("change", (file) => this._onAddOrChange(file, "changed")); + watcher.on("unlink", (file) => { + const guid = import_path2.default.basename(file); + if (this._descriptors.delete(guid)) + this.emit("removed", guid); + }); + } + _onAddOrChange(file, event) { + const guid = import_path2.default.basename(file); + let descriptor; + try { + descriptor = JSON.parse(import_fs.default.readFileSync(file, "utf-8")); + } catch { + return; + } + this._descriptors.set(guid, descriptor); + this.emit(event, descriptor); + } + _stopWatcher() { + const watcher = this._watcher; + this._watcher = void 0; + this._ready = void 0; + this._descriptors.clear(); + void watcher?.close(); + } +}; +async function canConnectTo(descriptor) { + if (!descriptor.endpoint) + return false; + if (descriptor.endpoint.startsWith("ws://") || descriptor.endpoint.startsWith("wss://")) { + return await new Promise((resolve) => { + const url = new URL(descriptor.endpoint); + const socket = import_net.default.createConnection(Number(url.port), url.hostname, () => { + socket.destroy(); + resolve(true); + }); + socket.on("error", () => resolve(false)); + }); + } + return await new Promise((resolve) => { + const socket = import_net.default.createConnection(descriptor.endpoint ?? descriptor.pipeName, () => { + socket.destroy(); + resolve(true); + }); + socket.on("error", () => resolve(false)); + }); +} +var defaultCacheDirectory = (() => { + if (process.platform === "linux") + return process.env.XDG_CACHE_HOME || import_path2.default.join(import_os.default.homedir(), ".cache"); + if (process.platform === "darwin") + return import_path2.default.join(import_os.default.homedir(), "Library", "Caches"); + if (process.platform === "win32") + return process.env.LOCALAPPDATA || import_path2.default.join(import_os.default.homedir(), "AppData", "Local"); + throw new Error("Unsupported platform: " + process.platform); +})(); +var registryDirectory = import_path2.default.join(defaultCacheDirectory, "ms-playwright", "b"); +var serverRegistry = new ServerRegistry(); +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + serverRegistry +}); +/*! Bundled license information: + +normalize-path/index.js: + (*! + * normalize-path + * + * Copyright (c) 2014-2018, Jon Schlinkert. + * Released under the MIT License. + *) + +is-extglob/index.js: + (*! + * is-extglob + * + * Copyright (c) 2014-2016, Jon Schlinkert. + * Licensed under the MIT License. + *) + +is-glob/index.js: + (*! + * is-glob + * + * Copyright (c) 2014-2017, Jon Schlinkert. + * Released under the MIT License. + *) + +is-number/index.js: + (*! + * is-number + * + * Copyright (c) 2014-present, Jon Schlinkert. + * Released under the MIT License. + *) + +to-regex-range/index.js: + (*! + * to-regex-range + * + * Copyright (c) 2015-present, Jon Schlinkert. + * Released under the MIT License. + *) + +fill-range/index.js: + (*! + * fill-range + * + * Copyright (c) 2014-present, Jon Schlinkert. + * Licensed under the MIT License. + *) +*/ diff --git a/simulator/e2e/node_modules/playwright-core/lib/serverRegistry.js.LICENSE b/simulator/e2e/node_modules/playwright-core/lib/serverRegistry.js.LICENSE new file mode 100644 index 0000000..8e2592a --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/lib/serverRegistry.js.LICENSE @@ -0,0 +1,354 @@ +packages/playwright-core/lib/serverRegistry.js + +THIRD-PARTY SOFTWARE NOTICES AND INFORMATION + +The following npm packages are inlined into this bundle. + +- anymatch@3.1.3 (https://github.com/micromatch/anymatch) +- binary-extensions@2.3.0 (https://github.com/sindresorhus/binary-extensions) +- braces@3.0.3 (https://github.com/micromatch/braces) +- chokidar@3.6.0 (https://github.com/paulmillr/chokidar) +- fill-range@7.1.1 (https://github.com/jonschlinkert/fill-range) +- glob-parent@5.1.2 (https://github.com/gulpjs/glob-parent) +- is-binary-path@2.1.0 (https://github.com/sindresorhus/is-binary-path) +- is-extglob@2.1.1 (https://github.com/jonschlinkert/is-extglob) +- is-glob@4.0.3 (https://github.com/micromatch/is-glob) +- is-number@7.0.0 (https://github.com/jonschlinkert/is-number) +- normalize-path@3.0.0 (https://github.com/jonschlinkert/normalize-path) +- picomatch@2.3.2 (https://github.com/micromatch/picomatch) +- readdirp@3.6.0 (https://github.com/paulmillr/readdirp) +- to-regex-range@5.0.1 (https://github.com/micromatch/to-regex-range) + +%% anymatch@3.1.3 NOTICES AND INFORMATION BEGIN HERE +========================================= +The ISC License + +Copyright (c) 2019 Elan Shanker, Paul Miller (https://paulmillr.com) + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +========================================= +END OF anymatch@3.1.3 NOTICES AND INFORMATION + +%% binary-extensions@2.3.0 NOTICES AND INFORMATION BEGIN HERE +========================================= +MIT License + +Copyright (c) Sindre Sorhus (https://sindresorhus.com) +Copyright (c) Paul Miller (https://paulmillr.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF binary-extensions@2.3.0 NOTICES AND INFORMATION + +%% braces@3.0.3 NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2014-present, Jon Schlinkert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF braces@3.0.3 NOTICES AND INFORMATION + +%% chokidar@3.6.0 NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2012-2019 Paul Miller (https://paulmillr.com), Elan Shanker + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the โSoftwareโ), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED โAS ISโ, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF chokidar@3.6.0 NOTICES AND INFORMATION + +%% fill-range@7.1.1 NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2014-present, Jon Schlinkert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF fill-range@7.1.1 NOTICES AND INFORMATION + +%% glob-parent@5.1.2 NOTICES AND INFORMATION BEGIN HERE +========================================= +The ISC License + +Copyright (c) 2015, 2019 Elan Shanker + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +========================================= +END OF glob-parent@5.1.2 NOTICES AND INFORMATION + +%% is-binary-path@2.1.0 NOTICES AND INFORMATION BEGIN HERE +========================================= +MIT License + +Copyright (c) 2019 Sindre Sorhus (https://sindresorhus.com), Paul Miller (https://paulmillr.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF is-binary-path@2.1.0 NOTICES AND INFORMATION + +%% is-extglob@2.1.1 NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2014-2016, Jon Schlinkert + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF is-extglob@2.1.1 NOTICES AND INFORMATION + +%% is-glob@4.0.3 NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2014-2017, Jon Schlinkert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF is-glob@4.0.3 NOTICES AND INFORMATION + +%% is-number@7.0.0 NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2014-present, Jon Schlinkert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF is-number@7.0.0 NOTICES AND INFORMATION + +%% normalize-path@3.0.0 NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2014-2018, Jon Schlinkert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF normalize-path@3.0.0 NOTICES AND INFORMATION + +%% picomatch@2.3.2 NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2017-present, Jon Schlinkert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF picomatch@2.3.2 NOTICES AND INFORMATION + +%% readdirp@3.6.0 NOTICES AND INFORMATION BEGIN HERE +========================================= +MIT License + +Copyright (c) 2012-2019 Thorsten Lorenz, Paul Miller (https://paulmillr.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +========================================= +END OF readdirp@3.6.0 NOTICES AND INFORMATION + +%% to-regex-range@5.0.1 NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2015-present, Jon Schlinkert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF to-regex-range@5.0.1 NOTICES AND INFORMATION + +SUMMARY +========================================= +Total Packages: 14 +========================================= \ No newline at end of file diff --git a/simulator/e2e/node_modules/playwright-core/lib/tools/cli-client/channelSessions.js b/simulator/e2e/node_modules/playwright-core/lib/tools/cli-client/channelSessions.js new file mode 100644 index 0000000..68749aa --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/lib/tools/cli-client/channelSessions.js @@ -0,0 +1,141 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var channelSessions_exports = {}; +__export(channelSessions_exports, { + isKnownChannel: () => isKnownChannel, + listChannelSessions: () => listChannelSessions +}); +module.exports = __toCommonJS(channelSessions_exports); +var import_fs = __toESM(require("fs")); +var import_net = __toESM(require("net")); +var import_os = __toESM(require("os")); +var import_path = __toESM(require("path")); +var import_extension = require("../utils/extension"); +function isKnownChannel(name) { + return channelToUserDataDir.has(name); +} +async function listChannelSessions() { + if (process.env.PWTEST_CLI_CHANNEL_SCAN_DISABLED_FOR_TEST) + return []; + const result = []; + for (const [channel, dirs] of channelToUserDataDir) { + const userDataDir = dirs[process.platform]; + if (!userDataDir) + continue; + if (!await pathExists(userDataDir)) + continue; + const [endpoint, extensionInstalled] = await Promise.all([ + readEndpoint(userDataDir), + (0, import_extension.isPlaywrightExtensionInstalled)(userDataDir) + ]); + result.push({ channel, userDataDir, endpoint, extensionInstalled }); + } + return result; +} +async function pathExists(p) { + try { + await import_fs.default.promises.access(p); + return true; + } catch { + return false; + } +} +async function readEndpoint(userDataDir) { + let contents; + try { + contents = await import_fs.default.promises.readFile(import_path.default.join(userDataDir, "DevToolsActivePort"), "utf-8"); + } catch { + return void 0; + } + const port = parseInt(contents.trim().split("\n")[0], 10); + if (!Number.isFinite(port)) + return void 0; + if (!await isPortOpen(port)) + return void 0; + return `http://localhost:${port}`; +} +async function isPortOpen(port) { + return new Promise((resolve) => { + const socket = import_net.default.createConnection(port, "127.0.0.1"); + const done = (value) => { + socket.destroy(); + resolve(value); + }; + socket.once("connect", () => done(true)); + socket.once("error", () => done(false)); + socket.setTimeout(250, () => done(false)); + }); +} +const channelToUserDataDir = /* @__PURE__ */ new Map([ + ["chrome", { + "linux": import_path.default.join(import_os.default.homedir(), ".config", "google-chrome"), + "darwin": import_path.default.join(import_os.default.homedir(), "Library", "Application Support", "Google", "Chrome"), + "win32": import_path.default.join(process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local"), "Google", "Chrome", "User Data") + }], + ["chrome-beta", { + "linux": import_path.default.join(import_os.default.homedir(), ".config", "google-chrome-beta"), + "darwin": import_path.default.join(import_os.default.homedir(), "Library", "Application Support", "Google", "Chrome Beta"), + "win32": import_path.default.join(process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local"), "Google", "Chrome Beta", "User Data") + }], + ["chrome-dev", { + "linux": import_path.default.join(import_os.default.homedir(), ".config", "google-chrome-unstable"), + "darwin": import_path.default.join(import_os.default.homedir(), "Library", "Application Support", "Google", "Chrome Dev"), + "win32": import_path.default.join(process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local"), "Google", "Chrome Dev", "User Data") + }], + ["chrome-canary", { + "linux": import_path.default.join(import_os.default.homedir(), ".config", "google-chrome-canary"), + "darwin": import_path.default.join(import_os.default.homedir(), "Library", "Application Support", "Google", "Chrome Canary"), + "win32": import_path.default.join(process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local"), "Google", "Chrome SxS", "User Data") + }], + ["msedge", { + "linux": import_path.default.join(import_os.default.homedir(), ".config", "microsoft-edge"), + "darwin": import_path.default.join(import_os.default.homedir(), "Library", "Application Support", "Microsoft Edge"), + "win32": import_path.default.join(process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local"), "Microsoft", "Edge", "User Data") + }], + ["msedge-beta", { + "linux": import_path.default.join(import_os.default.homedir(), ".config", "microsoft-edge-beta"), + "darwin": import_path.default.join(import_os.default.homedir(), "Library", "Application Support", "Microsoft Edge Beta"), + "win32": import_path.default.join(process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local"), "Microsoft", "Edge Beta", "User Data") + }], + ["msedge-dev", { + "linux": import_path.default.join(import_os.default.homedir(), ".config", "microsoft-edge-dev"), + "darwin": import_path.default.join(import_os.default.homedir(), "Library", "Application Support", "Microsoft Edge Dev"), + "win32": import_path.default.join(process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local"), "Microsoft", "Edge Dev", "User Data") + }], + ["msedge-canary", { + "linux": import_path.default.join(import_os.default.homedir(), ".config", "microsoft-edge-canary"), + "darwin": import_path.default.join(import_os.default.homedir(), "Library", "Application Support", "Microsoft Edge Canary"), + "win32": import_path.default.join(process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local"), "Microsoft", "Edge SxS", "User Data") + }] +]); +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + isKnownChannel, + listChannelSessions +}); diff --git a/simulator/e2e/node_modules/playwright-core/lib/tools/cli-client/cli.js b/simulator/e2e/node_modules/playwright-core/lib/tools/cli-client/cli.js new file mode 100644 index 0000000..52ec58a --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/lib/tools/cli-client/cli.js @@ -0,0 +1,6 @@ +"use strict"; +var import_program = require("./program"); +(0, import_program.program)().catch((e) => { + console.error(e.message); + process.exit(1); +}); diff --git a/simulator/e2e/node_modules/playwright-core/lib/tools/cli-client/help.json b/simulator/e2e/node_modules/playwright-core/lib/tools/cli-client/help.json new file mode 100644 index 0000000..d1ef427 --- /dev/null +++ b/simulator/e2e/node_modules/playwright-core/lib/tools/cli-client/help.json @@ -0,0 +1,693 @@ +{ + "global": "Usage: playwright-cli [args] [options]\nUsage: playwright-cli -s=