commit 5aa0777fd380907dee27a1a4d57dfa2d504d0dcb
Author: jknapp
Date: Thu Aug 28 19:35:28 2025 -0700
First Commit
diff --git a/.playwright-mcp/Blank-Fillable-Character-Sheet.pdf b/.playwright-mcp/Blank-Fillable-Character-Sheet.pdf
new file mode 100644
index 0000000..25e5c19
Binary files /dev/null and b/.playwright-mcp/Blank-Fillable-Character-Sheet.pdf differ
diff --git a/.playwright-mcp/product-page-desktop-current.png b/.playwright-mcp/product-page-desktop-current.png
new file mode 100644
index 0000000..0dd27a4
Binary files /dev/null and b/.playwright-mcp/product-page-desktop-current.png differ
diff --git a/.playwright-mcp/product-page-desktop-improved.png b/.playwright-mcp/product-page-desktop-improved.png
new file mode 100644
index 0000000..1cd3c56
Binary files /dev/null and b/.playwright-mcp/product-page-desktop-improved.png differ
diff --git a/.playwright-mcp/product-page-layout-fixed.png b/.playwright-mcp/product-page-layout-fixed.png
new file mode 100644
index 0000000..14ed9ce
Binary files /dev/null and b/.playwright-mcp/product-page-layout-fixed.png differ
diff --git a/.playwright-mcp/product-page-layout-issue.png b/.playwright-mcp/product-page-layout-issue.png
new file mode 100644
index 0000000..5da0790
Binary files /dev/null and b/.playwright-mcp/product-page-layout-issue.png differ
diff --git a/.playwright-mcp/product-page-mobile.png b/.playwright-mcp/product-page-mobile.png
new file mode 100644
index 0000000..bc13deb
Binary files /dev/null and b/.playwright-mcp/product-page-mobile.png differ
diff --git a/.playwright-mcp/product-page-tablet.png b/.playwright-mcp/product-page-tablet.png
new file mode 100644
index 0000000..b48ea96
Binary files /dev/null and b/.playwright-mcp/product-page-tablet.png differ
diff --git a/.playwright-mcp/wpdd-watermark-68afb08774031.png b/.playwright-mcp/wpdd-watermark-68afb08774031.png
new file mode 100644
index 0000000..63e75e0
Binary files /dev/null and b/.playwright-mcp/wpdd-watermark-68afb08774031.png differ
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..e96e5e2
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,142 @@
+# CLAUDE.md
+
+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
+
+## WordPress Digital Download Plugin
+
+This is a comprehensive WordPress plugin for creating a digital download marketplace. The plugin allows creators to sell digital products with PayPal integration, secure file protection, and watermarking capabilities.
+
+## Development Commands
+
+### Testing
+- `npm test` - Run all Playwright E2E tests
+- `npm run test:headed` - Run tests with browser UI visible
+- `npm run test:debug` - Run tests in debug mode
+- `npm run test:ui` - Run tests with Playwright UI
+
+### E2E Testing Configuration
+- Tests are configured for `https://streamers.channel` (live site)
+- Test files located in `tests/e2e/`
+- Uses Playwright with Chromium browser
+- Admin credentials stored in test file for WordPress login tests
+
+## Architecture Overview
+
+### Plugin Structure
+The plugin follows WordPress standards with a singleton main class `WP_Digital_Download` that orchestrates all components:
+
+**Main Plugin File**: `wp-digital-download.php`
+- Defines plugin constants and initializes the main class
+- Handles dependency loading and hook initialization
+- Manages script/style enqueuing with cache busting
+
+### Core Components
+
+**Product Management** (`includes/class-wpdd-post-types.php`, `includes/class-wpdd-metaboxes.php`)
+- Custom post type `wpdd_product` for digital products
+- Rich metadata system for pricing, files, download limits
+- Creator attribution and sales tracking
+
+**Payment Processing** (`includes/class-wpdd-paypal.php`)
+- PayPal API integration (sandbox/live modes)
+- Order processing and webhook handling
+- Support for both paid and free products
+
+**File Security** (`includes/class-wpdd-file-protection.php`, `includes/class-wpdd-download-handler.php`)
+- Secure file storage outside web root
+- Token-based download authentication
+- Time-limited and usage-limited downloads
+- Download tracking and logging
+
+**User Management** (`includes/class-wpdd-roles.php`, `includes/class-wpdd-customer.php`)
+- Custom roles: Digital Customer, Digital Creator
+- Customer purchase history and account management
+- Guest checkout support
+
+**Frontend Display** (`includes/class-wpdd-shortcodes.php`)
+- Shortcodes: `[wpdd_shop]`, `[wpdd_checkout]`, `[wpdd_customer_purchases]`, etc.
+- Product grids, filtering, and pagination
+- Responsive design support
+
+**Admin Interface** (`admin/class-wpdd-admin.php`, `admin/class-wpdd-settings.php`)
+- Admin dashboard for orders and sales management
+- Plugin settings and PayPal configuration
+- Product editing interface
+
+**Additional Features**
+- Watermarking (`includes/class-wpdd-watermark.php`): Dynamic image/PDF watermarks
+- AJAX handlers (`includes/class-wpdd-ajax.php`): Frontend interactions
+- Installation routines (`includes/class-wpdd-install.php`): Database setup and pages
+
+### Database Schema
+- `wp_wpdd_orders` - Purchase records
+- `wp_wpdd_downloads` - Download tracking
+- `wp_wpdd_download_links` - Secure download tokens
+- Product metadata stored in `wp_postmeta`
+
+### Key Shortcodes
+- `[wpdd_shop]` - Main product storefront with filtering/sorting
+- `[wpdd_customer_purchases]` - Customer purchase history (login required)
+- `[wpdd_checkout]` - Payment processing form
+- `[wpdd_thank_you]` - Order confirmation page
+- `[wpdd_product id="123"]` - Single product display
+
+## Development Notes
+
+### File Loading Pattern
+All classes are loaded conditionally with existence checks and error logging. Admin classes are only loaded in admin context.
+
+### Security Considerations
+- CSRF protection via nonces on all forms
+- Input sanitization and validation
+- Capability checks for admin functions
+- Secure file delivery system
+- XSS prevention in user inputs
+
+### Frontend Assets
+- Cache busting using file modification times
+- Separate CSS/JS for admin and frontend
+- jQuery dependencies for interactive features
+- Localized AJAX endpoints with nonces
+
+### Testing Strategy
+Comprehensive Playwright E2E tests covering:
+- Product display and search functionality
+- Free download workflow (with/without account creation)
+- Admin panel integration
+- Security validation (CSRF, XSS prevention)
+- Responsive design testing
+- Performance benchmarks
+
+## Remote Deployment
+
+- Website plugin folder is mounted locally via sshfs at /home/jknapp/remote-sftp
+- Live site hosted at `https://streamers.channel`
+- Test admin user: `playwright` (credentials in test files)
+
+## Important Patterns
+
+### Error Handling
+Extensive error logging throughout with descriptive messages for missing classes/files.
+
+If you find a bug or error, you should fix it in the code, deploy the changes, and test again. You should continue the process until the issue is fixed. Once you fix the issue, continue testing.
+
+### Hook System
+Uses WordPress actions/filters:
+- `wpdd_order_completed` - Post-purchase processing
+- `wpdd_download_started` - Pre-download hooks
+- `wpdd_customer_registered` - New customer events
+
+### Search Integration
+Plugin automatically includes `wpdd_product` post type in WordPress search results via `pre_get_posts` filter.
+
+### Website Current Status
+- The site is currently hooked up to PayPal Sandbox
+- The website has some test products
+ - One of the products is a free image and gets watermarked
+ - One is a PDF for $10 and should also be watermarked
+
+### PayPal Sandbox Customer Credentials
+These are sandbox only PayPal Test account. The credentials should be used to test the purchase process.
+- sb-a7cpw45634739@personal.example.com
+- 3[I$ppb?
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..6a225dd
--- /dev/null
+++ b/README.md
@@ -0,0 +1,219 @@
+# WP Digital Download
+
+A comprehensive WordPress plugin for creating a digital download marketplace where creators can sell digital products with PayPal integration.
+
+## Features
+
+### Core Functionality
+- **Custom Post Type**: Products with rich metadata
+- **User Roles**: Separate roles for customers and creators
+- **File Management**: Secure file upload and protection
+- **PayPal Integration**: Complete payment processing
+- **Download Protection**: Secure, time-limited downloads
+- **Watermarking**: Automatic watermarking for images and PDFs
+- **Purchase History**: Customer account management
+- **Admin Dashboard**: Complete order and sales management
+
+### Key Components
+
+#### Custom Post Types
+- **wpdd_product**: Digital products with pricing, files, and settings
+- **Product Categories & Tags**: Organization and filtering
+- **Creator Attribution**: Products linked to their creators
+
+#### User Management
+- **Digital Customer Role**: Purchase and download permissions
+- **Digital Creator Role**: Product management permissions
+- **Admin Capabilities**: Full system management
+
+#### Payment Processing
+- **PayPal Integration**: Sandbox and live modes
+- **Free Products**: No-payment downloads
+- **Guest Checkout**: Optional account creation
+- **Order Management**: Complete transaction tracking
+
+#### File Protection
+- **Protected Directory**: Files stored outside web root access
+- **Secure Downloads**: Token-based download links
+- **Download Limits**: Configurable per product
+- **Expiry Dates**: Time-limited access
+
+#### Watermarking
+- **Image Watermarking**: PNG, JPG, GIF support
+- **PDF Watermarking**: Text overlay on PDF files
+- **Dynamic Content**: Customer info in watermarks
+- **Configurable Settings**: Per-product control
+
+## Installation
+
+1. Upload the plugin files to `/wp-content/plugins/wp-digital-download/`
+2. Activate the plugin through the WordPress admin
+3. Configure PayPal settings in Digital Products > Settings
+4. Create your first product
+5. Add the shop shortcode `[wpdd_shop]` to a page
+
+## Configuration
+
+### PayPal Setup
+1. Create a PayPal Developer account
+2. Create a new application
+3. Get Client ID and Secret
+4. Configure in Settings > PayPal Settings
+
+### Pages Setup
+The plugin automatically creates these pages:
+- **Shop**: `[wpdd_shop]` - Product listing
+- **Checkout**: `[wpdd_checkout]` - Payment processing
+- **My Purchases**: `[wpdd_customer_purchases]` - Customer downloads
+- **Thank You**: `[wpdd_thank_you]` - Order confirmation
+
+## Shortcodes
+
+### Main Shortcodes
+- `[wpdd_shop]` - Display product storefront
+- `[wpdd_customer_purchases]` - Customer purchase history
+- `[wpdd_checkout]` - Checkout form
+- `[wpdd_thank_you]` - Thank you page
+- `[wpdd_product id="123"]` - Single product display
+
+### Shop Shortcode Options
+```
+[wpdd_shop columns="3" per_page="12" category="design" orderby="date" order="DESC" show_filters="yes"]
+```
+
+## Database Structure
+
+### Tables Created
+- `wp_wpdd_orders` - Purchase records
+- `wp_wpdd_downloads` - Download tracking
+- `wp_wpdd_download_links` - Secure download tokens
+
+### Key Meta Fields
+- `_wpdd_price` - Product price
+- `_wpdd_sale_price` - Sale price
+- `_wpdd_is_free` - Free product flag
+- `_wpdd_files` - Associated files
+- `_wpdd_download_limit` - Download restrictions
+- `_wpdd_download_expiry` - Access duration
+- `_wpdd_enable_watermark` - Watermark settings
+- `_wpdd_sales_count` - Sales tracking
+
+## File Structure
+
+```
+wp-digital-download/
+├── wp-digital-download.php # Main plugin file
+├── includes/ # Core functionality
+│ ├── class-wpdd-install.php # Installation routines
+│ ├── class-wpdd-post-types.php # Custom post types
+│ ├── class-wpdd-roles.php # User roles
+│ ├── class-wpdd-metaboxes.php # Product editing
+│ ├── class-wpdd-shortcodes.php # Frontend display
+│ ├── class-wpdd-paypal.php # Payment processing
+│ ├── class-wpdd-download-handler.php # File delivery
+│ ├── class-wpdd-customer.php # Customer management
+│ ├── class-wpdd-orders.php # Order processing
+│ ├── class-wpdd-file-protection.php # Security
+│ ├── class-wpdd-watermark.php # Image processing
+│ └── class-wpdd-ajax.php # AJAX handlers
+├── admin/ # Admin interface
+│ ├── class-wpdd-admin.php # Admin pages
+│ └── class-wpdd-settings.php # Configuration
+└── assets/ # Frontend resources
+ ├── css/
+ │ ├── frontend.css # Public styling
+ │ └── admin.css # Admin styling
+ └── js/
+ ├── frontend.js # Public scripts
+ ├── admin.js # Admin scripts
+ └── paypal.js # Payment integration
+```
+
+## Hooks & Filters
+
+### Actions
+- `wpdd_order_completed` - Fired when order is completed
+- `wpdd_download_started` - Before file delivery
+- `wpdd_customer_registered` - New customer account
+
+### Filters
+- `wpdd_product_price` - Modify displayed price
+- `wpdd_download_url` - Customize download URLs
+- `wpdd_watermark_text` - Modify watermark content
+- `wpdd_email_content` - Customize email templates
+
+## Security Features
+
+### File Protection
+- Files stored in protected directory with .htaccess rules
+- Token-based download authentication
+- Time-limited access links
+- IP and user agent logging
+
+### Input Validation
+- All user inputs sanitized
+- Nonce verification on forms
+- Capability checks for admin functions
+- SQL injection prevention
+
+### Payment Security
+- PayPal webhook verification
+- Transaction ID tracking
+- Duplicate payment prevention
+- Secure credential storage
+
+## Customization
+
+### Template Override
+Create templates in your theme:
+```
+your-theme/
+└── wpdd-templates/
+ ├── single-product.php
+ ├── shop.php
+ └── checkout.php
+```
+
+### CSS Customization
+Override default styles:
+```css
+.wpdd-product-card {
+ /* Your custom styles */
+}
+```
+
+### Adding Payment Methods
+Extend payment options by hooking into:
+```php
+add_action('wpdd_payment_methods', 'add_stripe_payment');
+```
+
+## Requirements
+
+- WordPress 5.0+
+- PHP 7.4+
+- MySQL 5.6+
+- cURL extension
+- GD library (for watermarking)
+
+## Changelog
+
+### Version 1.0.0
+- Initial release
+- PayPal integration
+- File protection system
+- Watermarking capabilities
+- Customer management
+- Admin dashboard
+
+## Support
+
+For support and feature requests, please create an issue in the GitHub repository.
+
+## License
+
+This plugin is licensed under the GPL v2 or later.
+
+---
+
+**Note**: This plugin handles financial transactions. Always test thoroughly in a development environment before deploying to production.
\ No newline at end of file
diff --git a/admin/class-wpdd-admin-payouts.php b/admin/class-wpdd-admin-payouts.php
new file mode 100644
index 0000000..3ba64db
--- /dev/null
+++ b/admin/class-wpdd-admin-payouts.php
@@ -0,0 +1,435 @@
+ admin_url('admin-ajax.php'),
+ 'nonce' => wp_create_nonce('wpdd_payouts'),
+ 'confirm_payout' => __('Are you sure you want to process this payout?', 'wp-digital-download'),
+ 'confirm_bulk' => __('Are you sure you want to process all selected payouts?', 'wp-digital-download')
+ ));
+ }
+
+ public static function render_page() {
+ global $wpdb;
+
+ // Get filter parameters
+ $status_filter = isset($_GET['status']) ? sanitize_text_field($_GET['status']) : 'pending';
+ $creator_filter = isset($_GET['creator']) ? intval($_GET['creator']) : 0;
+
+ // Get creators with balance
+ $creators = WPDD_Creator::get_creators_with_balance();
+ $currency = get_option('wpdd_currency', 'USD');
+ $threshold = floatval(get_option('wpdd_payout_threshold', 0));
+
+ // Get payout history
+ $query = "SELECT p.*, u.display_name, u.user_email
+ FROM {$wpdb->prefix}wpdd_payouts p
+ INNER JOIN {$wpdb->users} u ON p.creator_id = u.ID
+ WHERE 1=1";
+
+ if ($status_filter && $status_filter !== 'all') {
+ $query .= $wpdb->prepare(" AND p.status = %s", $status_filter);
+ }
+
+ if ($creator_filter) {
+ $query .= $wpdb->prepare(" AND p.creator_id = %d", $creator_filter);
+ }
+
+ $query .= " ORDER BY p.created_at DESC LIMIT 100";
+
+ $payouts = $wpdb->get_results($query);
+
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ >
+ >
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ processed_by ? get_userdata($payout->processed_by) : null;
+ ?>
+
+
+ created_at))); ?>
+ processed_at) : ?>
+ processed_at))); ?>
+
+
+
+ display_name); ?>
+ user_email); ?>
+
+ amount, $payout->currency); ?>
+ paypal_email); ?>
+
+ transaction_id ?: '-'); ?>
+ notes) : ?>
+ notes); ?>
+
+
+
+ status) {
+ case 'completed':
+ $status_class = 'notice-success';
+ break;
+ case 'failed':
+ $status_class = 'notice-error';
+ break;
+ case 'pending':
+ $status_class = 'notice-warning';
+ break;
+ }
+ ?>
+
+ status)); ?>
+
+
+ payout_method)); ?>
+
+
+ display_name); ?>
+
+ payout_method === 'automatic' ? __('System', 'wp-digital-download') : '-'; ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0) {
+ $creators = WPDD_Creator::get_creators_with_balance();
+ foreach ($creators as $creator) {
+ if (floatval($creator->balance) >= $threshold && !empty($creator->paypal_email)) {
+ self::create_payout($creator->ID);
+ }
+ }
+ }
+ } else {
+ // Process selected creators
+ $creator_ids = isset($_POST['creator_ids']) ? array_map('intval', $_POST['creator_ids']) : array();
+ foreach ($creator_ids as $creator_id) {
+ self::create_payout($creator_id);
+ }
+ }
+
+ wp_redirect(admin_url('edit.php?post_type=wpdd_product&page=wpdd-payouts&message=success'));
+ exit;
+ }
+
+ public static function create_payout($creator_id, $method = 'manual') {
+ global $wpdb;
+
+ $balance = WPDD_Creator::get_creator_balance($creator_id);
+ $paypal_email = get_user_meta($creator_id, 'wpdd_paypal_email', true);
+
+ if ($balance <= 0 || empty($paypal_email)) {
+ return false;
+ }
+
+ $currency = get_option('wpdd_currency', 'USD');
+ $current_user_id = get_current_user_id();
+
+ // Create payout record
+ $wpdb->insert(
+ $wpdb->prefix . 'wpdd_payouts',
+ array(
+ 'creator_id' => $creator_id,
+ 'amount' => $balance,
+ 'currency' => $currency,
+ 'paypal_email' => $paypal_email,
+ 'status' => 'pending',
+ 'payout_method' => $method,
+ 'processed_by' => $current_user_id,
+ 'created_at' => current_time('mysql')
+ ),
+ array('%d', '%f', '%s', '%s', '%s', '%s', '%d', '%s')
+ );
+
+ $payout_id = $wpdb->insert_id;
+
+ // Try to process via PayPal API
+ $result = WPDD_PayPal_Payouts::process_payout($payout_id);
+
+ if ($result['success']) {
+ // Update payout status
+ $wpdb->update(
+ $wpdb->prefix . 'wpdd_payouts',
+ array(
+ 'status' => 'completed',
+ 'transaction_id' => $result['transaction_id'],
+ 'processed_at' => current_time('mysql')
+ ),
+ array('id' => $payout_id),
+ array('%s', '%s', '%s'),
+ array('%d')
+ );
+
+ // Reset creator balance
+ update_user_meta($creator_id, 'wpdd_creator_balance', 0);
+
+ return true;
+ } else {
+ // Update with error
+ $wpdb->update(
+ $wpdb->prefix . 'wpdd_payouts',
+ array(
+ 'status' => 'failed',
+ 'notes' => $result['error']
+ ),
+ array('id' => $payout_id),
+ array('%s', '%s'),
+ array('%d')
+ );
+
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/admin/class-wpdd-admin.php b/admin/class-wpdd-admin.php
new file mode 100644
index 0000000..7ef9d78
--- /dev/null
+++ b/admin/class-wpdd-admin.php
@@ -0,0 +1,920 @@
+ $value) {
+ $new_columns[$key] = $value;
+
+ if ($key === 'title') {
+ $new_columns['wpdd_price'] = __('Price', 'wp-digital-download');
+ $new_columns['wpdd_sales'] = __('Sales', 'wp-digital-download');
+ $new_columns['wpdd_revenue'] = __('Revenue', 'wp-digital-download');
+ $new_columns['wpdd_files'] = __('Files', 'wp-digital-download');
+ }
+ }
+
+ return $new_columns;
+ }
+
+ public static function render_product_columns($column, $post_id) {
+ switch ($column) {
+ case 'wpdd_price':
+ $price = get_post_meta($post_id, '_wpdd_price', true);
+ $sale_price = get_post_meta($post_id, '_wpdd_sale_price', true);
+ $is_free = get_post_meta($post_id, '_wpdd_is_free', true);
+
+ if ($is_free) {
+ echo '' . __('Free', 'wp-digital-download') . ' ';
+ } elseif ($sale_price && $sale_price < $price) {
+ echo '$' . number_format($price, 2) . ' ';
+ echo '$' . number_format($sale_price, 2) . ' ';
+ } else {
+ echo '$' . number_format($price, 2);
+ }
+ break;
+
+ case 'wpdd_sales':
+ global $wpdb;
+ $sales = $wpdb->get_var($wpdb->prepare(
+ "SELECT COUNT(*) FROM {$wpdb->prefix}wpdd_orders
+ WHERE product_id = %d AND status = 'completed'",
+ $post_id
+ ));
+ echo intval($sales);
+ break;
+
+ case 'wpdd_revenue':
+ global $wpdb;
+ $revenue = $wpdb->get_var($wpdb->prepare(
+ "SELECT SUM(amount) FROM {$wpdb->prefix}wpdd_orders
+ WHERE product_id = %d AND status = 'completed'",
+ $post_id
+ ));
+ echo '$' . number_format($revenue ?: 0, 2);
+ break;
+
+ case 'wpdd_files':
+ $files = get_post_meta($post_id, '_wpdd_files', true);
+ $count = is_array($files) ? count($files) : 0;
+ echo $count;
+ break;
+ }
+ }
+
+ public static function make_columns_sortable($columns) {
+ $columns['wpdd_price'] = 'wpdd_price';
+ $columns['wpdd_sales'] = 'wpdd_sales';
+ $columns['wpdd_revenue'] = 'wpdd_revenue';
+ return $columns;
+ }
+
+ public static function sort_products_by_column($query) {
+ if (!is_admin() || !$query->is_main_query()) {
+ return;
+ }
+
+ if ($query->get('post_type') !== 'wpdd_product') {
+ return;
+ }
+
+ $orderby = $query->get('orderby');
+
+ switch ($orderby) {
+ case 'wpdd_price':
+ $query->set('meta_key', '_wpdd_price');
+ $query->set('orderby', 'meta_value_num');
+ break;
+
+ case 'wpdd_sales':
+ $query->set('meta_key', '_wpdd_sales_count');
+ $query->set('orderby', 'meta_value_num');
+ break;
+ }
+ }
+
+ public static function render_orders_page() {
+ global $wpdb;
+
+ $page = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1;
+ $per_page = 20;
+ $offset = ($page - 1) * $per_page;
+
+ $where = array('1=1');
+
+ if (isset($_GET['status']) && $_GET['status']) {
+ $where[] = $wpdb->prepare("o.status = %s", sanitize_text_field($_GET['status']));
+ }
+
+ if (isset($_GET['product_id']) && $_GET['product_id']) {
+ $where[] = $wpdb->prepare("o.product_id = %d", intval($_GET['product_id']));
+ }
+
+ if (isset($_GET['search']) && $_GET['search']) {
+ $search = '%' . $wpdb->esc_like($_GET['search']) . '%';
+ $where[] = $wpdb->prepare(
+ "(o.order_number LIKE %s OR o.customer_email LIKE %s OR o.customer_name LIKE %s)",
+ $search, $search, $search
+ );
+ }
+
+ $where_clause = implode(' AND ', $where);
+
+ $total = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}wpdd_orders o WHERE {$where_clause}");
+
+ $orders = $wpdb->get_results($wpdb->prepare(
+ "SELECT o.*, p.post_title as product_name
+ FROM {$wpdb->prefix}wpdd_orders o
+ LEFT JOIN {$wpdb->posts} p ON o.product_id = p.ID
+ WHERE {$where_clause}
+ ORDER BY o.purchase_date DESC
+ LIMIT %d OFFSET %d",
+ $per_page,
+ $offset
+ ));
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #order_number); ?>
+
+
+ product_name); ?>
+
+
+
+ customer_name); ?>
+ customer_email); ?>
+
+ $amount, 2); ?>
+
+
+ status); ?>
+
+
+ purchase_date)); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1) {
+ echo '
';
+ echo paginate_links(array(
+ 'base' => add_query_arg('paged', '%#%'),
+ 'format' => '',
+ 'current' => $page,
+ 'total' => $total_pages
+ ));
+ echo '
';
+ }
+ ?>
+
+
+
+ get_row($wpdb->prepare(
+ "SELECT
+ COUNT(*) as total_orders,
+ SUM(amount) as total_revenue,
+ COUNT(DISTINCT customer_id) as unique_customers,
+ COUNT(DISTINCT product_id) as products_sold
+ FROM {$wpdb->prefix}wpdd_orders
+ WHERE status = 'completed'
+ AND purchase_date >= %s",
+ $start_date
+ ));
+
+ $top_products = $wpdb->get_results($wpdb->prepare(
+ "SELECT
+ p.ID,
+ p.post_title,
+ COUNT(o.id) as sales,
+ SUM(o.amount) as revenue
+ FROM {$wpdb->prefix}wpdd_orders o
+ INNER JOIN {$wpdb->posts} p ON o.product_id = p.ID
+ WHERE o.status = 'completed'
+ AND o.purchase_date >= %s
+ GROUP BY p.ID
+ ORDER BY revenue DESC
+ LIMIT 10",
+ $start_date
+ ));
+
+ $top_creators = $wpdb->get_results($wpdb->prepare(
+ "SELECT
+ u.ID,
+ u.display_name,
+ COUNT(o.id) as sales,
+ SUM(o.amount) as revenue
+ FROM {$wpdb->prefix}wpdd_orders o
+ INNER JOIN {$wpdb->users} u ON o.creator_id = u.ID
+ WHERE o.status = 'completed'
+ AND o.purchase_date >= %s
+ GROUP BY u.ID
+ ORDER BY revenue DESC
+ LIMIT 10",
+ $start_date
+ ));
+ ?>
+
+
+
+
+
+
+
+
+
+ >
+
+
+ >
+
+
+ >
+
+
+ >
+
+
+
+
+
+
+
+
+
+
$total_revenue ?: 0, 2); ?>
+
+
+
+
+
+
+
unique_customers); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ post_title); ?>
+
+
+ sales); ?>
+ $revenue, 2); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ get_results(
+ "SELECT
+ u.ID,
+ u.user_email,
+ u.display_name,
+ u.user_registered,
+ COUNT(o.id) as total_orders,
+ SUM(o.amount) as total_spent,
+ MAX(o.purchase_date) as last_order_date
+ FROM {$wpdb->users} u
+ INNER JOIN {$wpdb->prefix}wpdd_orders o ON u.ID = o.customer_id AND o.status = 'completed'
+ GROUP BY u.ID
+ ORDER BY total_spent DESC"
+ );
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ display_name); ?>
+
+ user_email); ?>
+ total_orders); ?>
+ $total_spent ?: 0, 2); ?>
+ user_registered)); ?>
+
+ last_order_date
+ ? date_i18n(get_option('date_format'), strtotime($customer->last_order_date))
+ : '-';
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ get_row($wpdb->prepare(
+ "SELECT o.*, p.post_title as product_name
+ FROM {$wpdb->prefix}wpdd_orders o
+ LEFT JOIN {$wpdb->posts} p ON o.product_id = p.ID
+ WHERE o.id = %d",
+ $order_id
+ ));
+
+ if (!$order) {
+ wp_die(__('Order not found.', 'wp-digital-download'));
+ }
+
+ include WPDD_PLUGIN_PATH . 'admin/views/order-details.php';
+ exit;
+ }
+
+ public static function render_shortcodes_page() {
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+ [wpdd_shop]
+
+
+
+
+
+ posts_per_page - Number of products per page (default: 12)
+ columns - Grid columns (default: 3, options: 1-6)
+ orderby - Sort order (date, title, price, menu_order)
+ order - ASC or DESC (default: DESC)
+ category - Show only specific categories (comma separated slugs)
+ show_filters - Show search/filter form (yes/no, default: yes)
+
+
+
+
+ [wpdd_shop posts_per_page="6" columns="2"]
+ [wpdd_shop category="music,videos" show_filters="no"]
+ [wpdd_shop orderby="price" order="ASC" columns="4"]
+
+
+
+
+
+
+
+ [wpdd_checkout]
+
+
+
+
+
+
+
+
+
+
+ [wpdd_customer_purchases]
+
+
+
+
+
+
+
+
+
+
+ [wpdd_thank_you]
+
+
+
+
+
+
+
+
+
+
+ [wpdd_single_product id="123"]
+
+
+
+
+
+ id - Product ID (required)
+
+
+
+
+ [wpdd_single_product id="456"]
+
+
+
+
+
+
+
+ [wpdd_buy_button id="123"]
+
+
+
+
+
+ id - Product ID (default: current post ID)
+ text - Button text (default: "Buy Now")
+ class - CSS class for styling
+
+
+
+
+ [wpdd_buy_button id="789" text="Purchase Now"]
+ [wpdd_buy_button text="Get This Product" class="my-custom-button"]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [wpdd_shop]
+
+
+ ✓ ' . __('Created', 'wp-digital-download') . '';
+ echo ' (' . __('Edit', 'wp-digital-download') . ' )';
+ } else {
+ echo '✗ ' . __('Missing', 'wp-digital-download') . ' ';
+ }
+ ?>
+
+
+
+
+ [wpdd_checkout]
+
+
+ ✓ ' . __('Created', 'wp-digital-download') . '';
+ echo ' (' . __('Edit', 'wp-digital-download') . ' )';
+ } else {
+ echo '✗ ' . __('Missing', 'wp-digital-download') . ' ';
+ }
+ ?>
+
+
+
+
+ [wpdd_customer_purchases]
+
+
+ ✓ ' . __('Created', 'wp-digital-download') . '';
+ echo ' (' . __('Edit', 'wp-digital-download') . ' )';
+ } else {
+ echo '✗ ' . __('Missing', 'wp-digital-download') . ' ';
+ }
+ ?>
+
+
+
+
+ [wpdd_thank_you]
+
+
+ ✓ ' . __('Created', 'wp-digital-download') . '';
+ echo ' (' . __('Edit', 'wp-digital-download') . ' )';
+ } else {
+ echo '✗ ' . __('Missing', 'wp-digital-download') . ' ';
+ }
+ ?>
+
+
+
+
+
+
+
+
+
+
+ array(__CLASS__, 'sanitize_commission_rate')
+ ));
+ register_setting('wpdd_settings', 'wpdd_payout_threshold', array(
+ 'sanitize_callback' => array(__CLASS__, 'sanitize_payout_threshold')
+ ));
+ register_setting('wpdd_settings', 'wpdd_file_access_method');
+ register_setting('wpdd_settings', 'wpdd_disable_admin_bar');
+
+ add_settings_section(
+ 'wpdd_general_settings',
+ __('General Settings', 'wp-digital-download'),
+ array(__CLASS__, 'general_section_callback'),
+ 'wpdd_settings'
+ );
+
+ add_settings_section(
+ 'wpdd_paypal_settings',
+ __('PayPal Settings', 'wp-digital-download'),
+ array(__CLASS__, 'paypal_section_callback'),
+ 'wpdd_settings'
+ );
+
+ add_settings_section(
+ 'wpdd_email_settings',
+ __('Email Settings', 'wp-digital-download'),
+ array(__CLASS__, 'email_section_callback'),
+ 'wpdd_settings'
+ );
+
+ add_settings_section(
+ 'wpdd_download_settings',
+ __('Download Settings', 'wp-digital-download'),
+ array(__CLASS__, 'download_section_callback'),
+ 'wpdd_settings'
+ );
+
+ add_settings_section(
+ 'wpdd_watermark_settings',
+ __('Watermark Settings', 'wp-digital-download'),
+ array(__CLASS__, 'watermark_section_callback'),
+ 'wpdd_settings'
+ );
+
+ self::add_general_fields();
+ self::add_paypal_fields();
+ self::add_email_fields();
+ self::add_download_fields();
+ self::add_watermark_fields();
+ }
+
+ private static function add_general_fields() {
+ add_settings_field(
+ 'wpdd_currency',
+ __('Currency', 'wp-digital-download'),
+ array(__CLASS__, 'currency_field'),
+ 'wpdd_settings',
+ 'wpdd_general_settings',
+ array(
+ 'name' => 'wpdd_currency'
+ )
+ );
+
+ add_settings_field(
+ 'wpdd_enable_guest_checkout',
+ __('Guest Checkout', 'wp-digital-download'),
+ array(__CLASS__, 'checkbox_field'),
+ 'wpdd_settings',
+ 'wpdd_general_settings',
+ array(
+ 'name' => 'wpdd_enable_guest_checkout',
+ 'label' => __('Allow guest customers to purchase without creating an account', 'wp-digital-download')
+ )
+ );
+
+ add_settings_field(
+ 'wpdd_commission_rate',
+ __('Platform Commission Rate (%)', 'wp-digital-download'),
+ array(__CLASS__, 'number_field'),
+ 'wpdd_settings',
+ 'wpdd_general_settings',
+ array(
+ 'name' => 'wpdd_commission_rate',
+ 'description' => __('Platform commission rate from sales (0-100). Creators receive the remainder.', 'wp-digital-download'),
+ 'min' => 0,
+ 'max' => 100,
+ 'step' => 0.01
+ )
+ );
+
+ add_settings_field(
+ 'wpdd_payout_threshold',
+ __('Automatic Payout Threshold ($)', 'wp-digital-download'),
+ array(__CLASS__, 'number_field'),
+ 'wpdd_settings',
+ 'wpdd_general_settings',
+ array(
+ 'name' => 'wpdd_payout_threshold',
+ 'description' => __('Minimum balance for automatic payouts (0 to disable)', 'wp-digital-download'),
+ 'min' => 0,
+ 'step' => 0.01
+ )
+ );
+
+ add_settings_field(
+ 'wpdd_terms_page',
+ __('Terms & Conditions Page', 'wp-digital-download'),
+ array(__CLASS__, 'page_dropdown_field'),
+ 'wpdd_settings',
+ 'wpdd_general_settings',
+ array('name' => 'wpdd_terms_page')
+ );
+
+ add_settings_field(
+ 'wpdd_privacy_page',
+ __('Privacy Policy Page', 'wp-digital-download'),
+ array(__CLASS__, 'page_dropdown_field'),
+ 'wpdd_settings',
+ 'wpdd_general_settings',
+ array('name' => 'wpdd_privacy_page')
+ );
+ }
+
+ private static function add_paypal_fields() {
+ add_settings_field(
+ 'wpdd_paypal_mode',
+ __('PayPal Mode', 'wp-digital-download'),
+ array(__CLASS__, 'select_field'),
+ 'wpdd_settings',
+ 'wpdd_paypal_settings',
+ array(
+ 'name' => 'wpdd_paypal_mode',
+ 'options' => array(
+ 'sandbox' => __('Sandbox (Testing)', 'wp-digital-download'),
+ 'live' => __('Live (Production)', 'wp-digital-download')
+ )
+ )
+ );
+
+ add_settings_field(
+ 'wpdd_paypal_client_id',
+ __('PayPal Client ID', 'wp-digital-download'),
+ array(__CLASS__, 'text_field'),
+ 'wpdd_settings',
+ 'wpdd_paypal_settings',
+ array('name' => 'wpdd_paypal_client_id')
+ );
+
+ add_settings_field(
+ 'wpdd_paypal_secret',
+ __('PayPal Secret', 'wp-digital-download'),
+ array(__CLASS__, 'password_field'),
+ 'wpdd_settings',
+ 'wpdd_paypal_settings',
+ array('name' => 'wpdd_paypal_secret')
+ );
+ }
+
+ private static function add_email_fields() {
+ add_settings_field(
+ 'wpdd_admin_email',
+ __('Admin Email', 'wp-digital-download'),
+ array(__CLASS__, 'email_field'),
+ 'wpdd_settings',
+ 'wpdd_email_settings',
+ array(
+ 'name' => 'wpdd_admin_email',
+ 'description' => __('Email address for admin notifications', 'wp-digital-download')
+ )
+ );
+
+ add_settings_field(
+ 'wpdd_from_name',
+ __('From Name', 'wp-digital-download'),
+ array(__CLASS__, 'text_field'),
+ 'wpdd_settings',
+ 'wpdd_email_settings',
+ array(
+ 'name' => 'wpdd_from_name',
+ 'description' => __('Name shown in email headers', 'wp-digital-download')
+ )
+ );
+
+ add_settings_field(
+ 'wpdd_from_email',
+ __('From Email', 'wp-digital-download'),
+ array(__CLASS__, 'email_field'),
+ 'wpdd_settings',
+ 'wpdd_email_settings',
+ array(
+ 'name' => 'wpdd_from_email',
+ 'description' => __('Email address shown in email headers', 'wp-digital-download')
+ )
+ );
+ }
+
+ private static function add_download_fields() {
+ add_settings_field(
+ 'wpdd_default_download_limit',
+ __('Default Download Limit', 'wp-digital-download'),
+ array(__CLASS__, 'number_field'),
+ 'wpdd_settings',
+ 'wpdd_download_settings',
+ array(
+ 'name' => 'wpdd_default_download_limit',
+ 'description' => __('Default number of downloads allowed per purchase (0 = unlimited)', 'wp-digital-download'),
+ 'min' => 0
+ )
+ );
+
+ add_settings_field(
+ 'wpdd_default_download_expiry',
+ __('Default Download Expiry (days)', 'wp-digital-download'),
+ array(__CLASS__, 'number_field'),
+ 'wpdd_settings',
+ 'wpdd_download_settings',
+ array(
+ 'name' => 'wpdd_default_download_expiry',
+ 'description' => __('Default number of days downloads remain available (0 = never expires)', 'wp-digital-download'),
+ 'min' => 0
+ )
+ );
+
+ add_settings_field(
+ 'wpdd_file_access_method',
+ __('File Access Method', 'wp-digital-download'),
+ array(__CLASS__, 'select_field'),
+ 'wpdd_settings',
+ 'wpdd_download_settings',
+ array(
+ 'name' => 'wpdd_file_access_method',
+ 'options' => array(
+ 'direct' => __('Direct Download', 'wp-digital-download'),
+ 'redirect' => __('Redirect to File', 'wp-digital-download'),
+ 'force' => __('Force Download', 'wp-digital-download')
+ ),
+ 'description' => __('How files are delivered to customers', 'wp-digital-download')
+ )
+ );
+ }
+
+ private static function add_watermark_fields() {
+ add_settings_field(
+ 'wpdd_enable_watermark',
+ __('Enable Watermarking', 'wp-digital-download'),
+ array(__CLASS__, 'checkbox_field'),
+ 'wpdd_settings',
+ 'wpdd_watermark_settings',
+ array(
+ 'name' => 'wpdd_enable_watermark',
+ 'label' => __('Enable watermarking for images and PDFs by default', 'wp-digital-download')
+ )
+ );
+
+ add_settings_field(
+ 'wpdd_watermark_text',
+ __('Default Watermark Text', 'wp-digital-download'),
+ array(__CLASS__, 'text_field'),
+ 'wpdd_settings',
+ 'wpdd_watermark_settings',
+ array(
+ 'name' => 'wpdd_watermark_text',
+ 'description' => __('Available placeholders: {customer_name}, {customer_email}, {order_id}, {date}, {site_name}', 'wp-digital-download')
+ )
+ );
+ }
+
+ public static function render_settings_page() {
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+ ' . __('Configure basic plugin settings.', 'wp-digital-download') . '';
+ }
+
+ public static function paypal_section_callback() {
+ echo '' . __('Configure PayPal payment settings. You can get your API credentials from the PayPal Developer Dashboard.', 'wp-digital-download') . '
';
+ }
+
+ public static function email_section_callback() {
+ echo '' . __('Configure email settings for purchase notifications.', 'wp-digital-download') . '
';
+ }
+
+ public static function download_section_callback() {
+ echo '' . __('Configure default download and file protection settings.', 'wp-digital-download') . '
';
+ }
+
+ public static function watermark_section_callback() {
+ echo '' . __('Configure watermarking settings for images and PDF files.', 'wp-digital-download') . '
';
+ }
+
+ public static function text_field($args) {
+ $name = $args['name'] ?? '';
+ $value = get_option($name, '');
+ $description = isset($args['description']) ? $args['description'] : '';
+
+ if (empty($name)) {
+ echo 'Error: Field name not provided
';
+ return;
+ }
+
+ printf(
+ ' ',
+ esc_attr($name),
+ esc_attr($name),
+ esc_attr($value)
+ );
+
+ if ($description) {
+ printf('%s
', esc_html($description));
+ }
+ }
+
+ public static function password_field($args) {
+ $name = $args['name'] ?? '';
+ $value = get_option($name, '');
+ $description = isset($args['description']) ? $args['description'] : '';
+
+ if (empty($name)) {
+ echo 'Error: Field name not provided
';
+ return;
+ }
+
+ printf(
+ ' ',
+ esc_attr($name),
+ esc_attr($name),
+ esc_attr($value)
+ );
+
+ if ($description) {
+ printf('%s
', esc_html($description));
+ }
+ }
+
+ public static function email_field($args) {
+ $name = $args['name'] ?? '';
+ $value = get_option($name, '');
+ $description = isset($args['description']) ? $args['description'] : '';
+
+ if (empty($name)) {
+ echo 'Error: Field name not provided
';
+ return;
+ }
+
+ printf(
+ ' ',
+ esc_attr($name),
+ esc_attr($name),
+ esc_attr($value)
+ );
+
+ if ($description) {
+ printf('%s
', esc_html($description));
+ }
+ }
+
+ public static function number_field($args) {
+ $name = $args['name'] ?? '';
+ $value = get_option($name, '');
+ $description = isset($args['description']) ? $args['description'] : '';
+ $min = isset($args['min']) ? $args['min'] : 0;
+ $max = isset($args['max']) ? $args['max'] : '';
+
+ if (empty($name)) {
+ echo 'Error: Field name not provided
';
+ return;
+ }
+
+ printf(
+ ' ',
+ esc_attr($name),
+ esc_attr($name),
+ esc_attr($value),
+ esc_attr($min),
+ $max ? 'max="' . esc_attr($max) . '"' : ''
+ );
+
+ if ($description) {
+ printf('%s
', esc_html($description));
+ }
+ }
+
+ public static function checkbox_field($args) {
+ $name = $args['name'] ?? '';
+ $value = get_option($name, 0);
+ $label = $args['label'] ?? '';
+
+ if (empty($name)) {
+ echo 'Error: Field name not provided
';
+ return;
+ }
+
+ printf(
+ ' %s ',
+ esc_attr($name),
+ esc_attr($name),
+ checked($value, 1, false),
+ esc_html($label)
+ );
+ }
+
+ public static function select_field($args) {
+ $name = $args['name'] ?? '';
+ $value = get_option($name, '');
+ $options = $args['options'] ?? array();
+ $description = isset($args['description']) ? $args['description'] : '';
+
+ if (empty($name)) {
+ echo 'Error: Field name not provided
';
+ return;
+ }
+
+ printf('', esc_attr($name), esc_attr($name));
+
+ foreach ($options as $option_value => $option_label) {
+ printf(
+ '%s ',
+ esc_attr($option_value),
+ selected($value, $option_value, false),
+ esc_html($option_label)
+ );
+ }
+
+ echo ' ';
+
+ if ($description) {
+ printf('%s
', esc_html($description));
+ }
+ }
+
+ public static function currency_field($args) {
+ $currencies = array(
+ 'USD' => 'US Dollar ($)',
+ 'EUR' => 'Euro (€)',
+ 'GBP' => 'British Pound (£)',
+ 'CAD' => 'Canadian Dollar (C$)',
+ 'AUD' => 'Australian Dollar (A$)',
+ 'JPY' => 'Japanese Yen (¥)'
+ );
+
+ $args['options'] = $currencies;
+ self::select_field($args);
+ }
+
+ public static function page_dropdown_field($args) {
+ $name = $args['name'] ?? '';
+ $value = get_option($name, '');
+
+ if (empty($name)) {
+ echo 'Error: Field name not provided
';
+ return;
+ }
+
+ wp_dropdown_pages(array(
+ 'name' => $name,
+ 'id' => $name,
+ 'selected' => $value,
+ 'show_option_none' => __('— Select —', 'wp-digital-download'),
+ 'option_none_value' => ''
+ ));
+ }
+
+ private static function system_status() {
+ $status = array();
+
+ $upload_dir = wp_upload_dir();
+ $protected_dir = trailingslashit($upload_dir['basedir']) . WPDD_UPLOADS_DIR;
+
+ if (is_writable($upload_dir['basedir'])) {
+ $status[] = array(
+ 'label' => __('Upload Directory', 'wp-digital-download'),
+ 'value' => __('Writable', 'wp-digital-download'),
+ 'class' => 'wpdd-status-good'
+ );
+ } else {
+ $status[] = array(
+ 'label' => __('Upload Directory', 'wp-digital-download'),
+ 'value' => __('Not Writable', 'wp-digital-download'),
+ 'class' => 'wpdd-status-error'
+ );
+ }
+
+ if (file_exists($protected_dir)) {
+ $status[] = array(
+ 'label' => __('Protected Directory', 'wp-digital-download'),
+ 'value' => __('Exists', 'wp-digital-download'),
+ 'class' => 'wpdd-status-good'
+ );
+ } else {
+ $status[] = array(
+ 'label' => __('Protected Directory', 'wp-digital-download'),
+ 'value' => __('Missing', 'wp-digital-download'),
+ 'class' => 'wpdd-status-warning'
+ );
+ }
+
+ if (function_exists('imagecreatefrompng')) {
+ $status[] = array(
+ 'label' => __('GD Library', 'wp-digital-download'),
+ 'value' => __('Available', 'wp-digital-download'),
+ 'class' => 'wpdd-status-good'
+ );
+ } else {
+ $status[] = array(
+ 'label' => __('GD Library', 'wp-digital-download'),
+ 'value' => __('Not Available', 'wp-digital-download'),
+ 'class' => 'wpdd-status-warning'
+ );
+ }
+
+ $paypal_client_id = get_option('wpdd_paypal_client_id');
+ if ($paypal_client_id) {
+ $status[] = array(
+ 'label' => __('PayPal', 'wp-digital-download'),
+ 'value' => __('Configured', 'wp-digital-download'),
+ 'class' => 'wpdd-status-good'
+ );
+ } else {
+ $status[] = array(
+ 'label' => __('PayPal', 'wp-digital-download'),
+ 'value' => __('Not Configured', 'wp-digital-download'),
+ 'class' => 'wpdd-status-warning'
+ );
+ }
+
+ echo '';
+ foreach ($status as $item) {
+ printf(
+ '%s: %s ',
+ esc_html($item['label']),
+ esc_attr($item['class']),
+ esc_html($item['value'])
+ );
+ }
+ echo ' ';
+ }
+
+ public static function sanitize_commission_rate($input) {
+ $value = floatval($input);
+ if ($value < 0) {
+ $value = 0;
+ add_settings_error('wpdd_commission_rate', 'invalid_rate', __('Commission rate cannot be less than 0%. Set to 0%.', 'wp-digital-download'));
+ } elseif ($value > 100) {
+ $value = 100;
+ add_settings_error('wpdd_commission_rate', 'invalid_rate', __('Commission rate cannot exceed 100%. Set to 100%.', 'wp-digital-download'));
+ }
+ return $value;
+ }
+
+ public static function sanitize_payout_threshold($input) {
+ $value = floatval($input);
+ if ($value < 0) {
+ $value = 0;
+ add_settings_error('wpdd_payout_threshold', 'invalid_threshold', __('Payout threshold cannot be negative. Set to 0 (disabled).', 'wp-digital-download'));
+ }
+ return $value;
+ }
+}
\ No newline at end of file
diff --git a/assets/css/admin.css b/assets/css/admin.css
new file mode 100644
index 0000000..73cf36a
--- /dev/null
+++ b/assets/css/admin.css
@@ -0,0 +1,451 @@
+/* WP Digital Download - Admin Styles */
+
+/* Product Metaboxes */
+.wpdd-metabox-content {
+ padding: 10px 0;
+}
+
+.wpdd-metabox-content p {
+ margin-bottom: 15px;
+}
+
+.wpdd-metabox-content label {
+ display: block;
+ margin-bottom: 5px;
+ font-weight: 600;
+}
+
+.wpdd-metabox-content input[type="text"],
+.wpdd-metabox-content input[type="number"],
+.wpdd-metabox-content input[type="email"] {
+ width: 100%;
+ padding: 8px;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+}
+
+.wpdd-metabox-content input[type="checkbox"] {
+ margin-right: 8px;
+}
+
+.wpdd-metabox-content .description {
+ font-style: italic;
+ color: #666;
+ font-size: 12px;
+ margin-top: 5px;
+}
+
+/* Price fields toggle */
+#wpdd_is_free:checked ~ .wpdd-price-field {
+ opacity: 0.5;
+ pointer-events: none;
+}
+
+/* Files Metabox */
+.wpdd-files-container {
+ padding: 10px 0;
+}
+
+#wpdd-files-list {
+ margin-bottom: 20px;
+}
+
+.wpdd-file-item {
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ margin-bottom: 15px;
+ background: #f9f9f9;
+}
+
+.wpdd-file-header {
+ display: flex;
+ align-items: center;
+ padding: 10px 15px;
+ background: #f1f1f1;
+ border-bottom: 1px solid #ddd;
+}
+
+.wpdd-file-handle {
+ cursor: move;
+ margin-right: 10px;
+ color: #666;
+}
+
+.wpdd-file-header input[type="text"] {
+ flex: 1;
+ margin-right: 10px;
+ padding: 5px 8px;
+ border: 1px solid #ddd;
+ border-radius: 3px;
+}
+
+.wpdd-file-content {
+ padding: 15px;
+}
+
+.wpdd-file-url {
+ display: flex;
+ gap: 10px;
+ align-items: center;
+}
+
+.wpdd-file-url-input {
+ flex: 1;
+ padding: 8px;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+}
+
+.wpdd-upload-file,
+.wpdd-remove-file {
+ white-space: nowrap;
+}
+
+.wpdd-remove-file {
+ background: #dc3545;
+ color: white;
+ border: none;
+ padding: 5px 10px;
+ border-radius: 3px;
+ cursor: pointer;
+}
+
+.wpdd-remove-file:hover {
+ background: #c82333;
+}
+
+#wpdd-add-file {
+ background: #0073aa;
+ color: white;
+ border: none;
+ padding: 10px 20px;
+ border-radius: 4px;
+ cursor: pointer;
+}
+
+#wpdd-add-file:hover {
+ background: #005a87;
+}
+
+/* Settings Grid */
+.wpdd-settings-grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 30px;
+}
+
+.wpdd-setting-group {
+ background: #f9f9f9;
+ padding: 20px;
+ border-radius: 6px;
+ border: 1px solid #e1e5e9;
+}
+
+.wpdd-setting-group h4 {
+ margin: 0 0 15px 0;
+ color: #23282d;
+ font-size: 14px;
+ font-weight: 600;
+}
+
+.wpdd-setting-group p {
+ margin-bottom: 15px;
+}
+
+.wpdd-setting-group label {
+ display: block;
+ margin-bottom: 5px;
+ font-weight: 500;
+ font-size: 13px;
+}
+
+.wpdd-setting-group input[type="text"],
+.wpdd-setting-group input[type="number"] {
+ width: 100%;
+ padding: 6px 8px;
+ border: 1px solid #ddd;
+ border-radius: 3px;
+ font-size: 13px;
+}
+
+.wpdd-setting-group .description {
+ font-size: 12px;
+ color: #666;
+ font-style: italic;
+ margin-top: 5px;
+}
+
+@media (max-width: 782px) {
+ .wpdd-settings-grid {
+ grid-template-columns: 1fr;
+ }
+}
+
+/* Stats Metabox */
+.wpdd-stats p {
+ margin-bottom: 8px;
+ font-size: 13px;
+}
+
+.wpdd-stats strong {
+ color: #23282d;
+}
+
+/* Product List Columns */
+.column-wpdd_price,
+.column-wpdd_sales,
+.column-wpdd_revenue,
+.column-wpdd_files {
+ width: 10%;
+}
+
+/* File Upload Progress */
+.wpdd-upload-progress {
+ width: 100%;
+ height: 20px;
+ background: #f1f1f1;
+ border-radius: 10px;
+ overflow: hidden;
+ margin: 10px 0;
+}
+
+.wpdd-upload-progress-bar {
+ height: 100%;
+ background: #0073aa;
+ transition: width 0.3s;
+ border-radius: 10px;
+}
+
+/* Drag and Drop Sorting */
+.wpdd-file-item.ui-sortable-helper {
+ box-shadow: 0 4px 8px rgba(0,0,0,0.2);
+ transform: rotate(2deg);
+}
+
+.wpdd-file-item.ui-sortable-placeholder {
+ border: 2px dashed #0073aa;
+ background: transparent;
+}
+
+/* Admin Dashboard Widgets */
+.wpdd-sales-summary .wpdd-stats-grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 15px;
+ margin-bottom: 20px;
+}
+
+.wpdd-sales-summary .wpdd-stat {
+ text-align: center;
+ padding: 10px;
+ background: #f0f0f1;
+ border-radius: 4px;
+}
+
+.wpdd-sales-summary .wpdd-stat-value {
+ display: block;
+ font-size: 24px;
+ font-weight: 600;
+ color: #2271b1;
+}
+
+.wpdd-sales-summary .wpdd-stat-label {
+ display: block;
+ font-size: 12px;
+ color: #646970;
+ margin-top: 5px;
+}
+
+/* Orders Page */
+.wpdd-status {
+ padding: 3px 8px;
+ border-radius: 3px;
+ font-size: 12px;
+ font-weight: 600;
+}
+
+.wpdd-status-completed {
+ background: #d4edda;
+ color: #155724;
+}
+
+.wpdd-status-pending {
+ background: #fff3cd;
+ color: #856404;
+}
+
+.wpdd-status-failed {
+ background: #f8d7da;
+ color: #721c24;
+}
+
+/* Reports Page */
+.wpdd-date-filter {
+ margin: 20px 0;
+}
+
+.wpdd-stats-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ gap: 20px;
+ margin: 30px 0;
+}
+
+.wpdd-stat-box {
+ background: white;
+ padding: 20px;
+ border: 1px solid #ccd0d4;
+ border-radius: 4px;
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
+}
+
+.wpdd-stat-box h3 {
+ margin: 0 0 10px 0;
+ color: #23282d;
+ font-size: 14px;
+ font-weight: 600;
+}
+
+.wpdd-stat-value {
+ font-size: 32px;
+ font-weight: 600;
+ color: #2271b1;
+ margin: 0;
+ line-height: 1.2;
+}
+
+.wpdd-reports-tables {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 30px;
+ margin-top: 30px;
+}
+
+.wpdd-report-section h2 {
+ margin-bottom: 15px;
+ font-size: 18px;
+}
+
+@media (max-width: 1200px) {
+ .wpdd-reports-tables {
+ grid-template-columns: 1fr;
+ }
+}
+
+/* Settings Page */
+.wpdd-settings-sidebar {
+ float: right;
+ width: 300px;
+ margin-left: 20px;
+}
+
+.wpdd-settings-box {
+ background: white;
+ border: 1px solid #ccd0d4;
+ padding: 20px;
+ margin-bottom: 20px;
+ border-radius: 4px;
+ box-shadow: 0 1px 3px rgba(0,0,0,0.05);
+}
+
+.wpdd-settings-box h3 {
+ margin-top: 0;
+ font-size: 16px;
+}
+
+.wpdd-settings-box code {
+ background: #f1f1f1;
+ padding: 2px 5px;
+ font-size: 12px;
+ border-radius: 3px;
+}
+
+.wpdd-settings-box ul {
+ margin: 0;
+ padding-left: 20px;
+}
+
+.wpdd-settings-box li {
+ margin-bottom: 8px;
+ font-size: 13px;
+}
+
+.wpdd-status-good {
+ color: #46b450;
+}
+
+.wpdd-status-warning {
+ color: #ffb900;
+}
+
+.wpdd-status-error {
+ color: #dc3232;
+}
+
+#wpforms-settings .form-table {
+ max-width: calc(100% - 340px);
+}
+
+@media (max-width: 1200px) {
+ .wpdd-settings-sidebar {
+ float: none;
+ width: 100%;
+ margin-left: 0;
+ margin-top: 30px;
+ }
+
+ #wpforms-settings .form-table {
+ max-width: 100%;
+ }
+}
+
+/* Responsive adjustments */
+@media (max-width: 782px) {
+ .wpdd-file-header {
+ flex-direction: column;
+ align-items: stretch;
+ gap: 10px;
+ }
+
+ .wpdd-file-url {
+ flex-direction: column;
+ gap: 10px;
+ }
+
+ .wpdd-settings-sidebar {
+ width: 100%;
+ margin-left: 0;
+ margin-top: 20px;
+ float: none;
+ }
+
+ .wpdd-stats-grid {
+ grid-template-columns: 1fr;
+ }
+}
+
+/* Loading states */
+.wpdd-loading {
+ position: relative;
+ pointer-events: none;
+ opacity: 0.6;
+}
+
+.wpdd-loading::after {
+ content: '';
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 20px;
+ height: 20px;
+ margin: -10px 0 0 -10px;
+ border: 2px solid #ccc;
+ border-top: 2px solid #0073aa;
+ border-radius: 50%;
+ animation: wpdd-spin 1s linear infinite;
+}
+
+@keyframes wpdd-spin {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
\ No newline at end of file
diff --git a/assets/css/frontend.css b/assets/css/frontend.css
new file mode 100644
index 0000000..173a1ea
--- /dev/null
+++ b/assets/css/frontend.css
@@ -0,0 +1,469 @@
+/* WP Digital Download - Frontend Styles */
+
+/* Shop Grid */
+.wpdd-shop-container {
+ max-width: 1200px;
+ margin: 0 auto;
+}
+
+.wpdd-shop-filters {
+ background: #f8f9fa;
+ padding: 20px;
+ border-radius: 8px;
+ margin-bottom: 30px;
+}
+
+.wpdd-filter-form {
+ display: flex;
+ gap: 15px;
+ align-items: center;
+ flex-wrap: wrap;
+}
+
+.wpdd-filter-form input[type="text"],
+.wpdd-filter-form select {
+ padding: 8px 12px;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ min-width: 150px;
+}
+
+.wpdd-filter-submit {
+ padding: 8px 16px;
+ background: #0073aa;
+ color: white;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ transition: background 0.3s;
+}
+
+.wpdd-filter-submit:hover {
+ background: #005a87;
+}
+
+.wpdd-products-grid {
+ display: grid;
+ gap: 30px;
+ margin-bottom: 40px;
+}
+
+.wpdd-columns-1 { grid-template-columns: 1fr; }
+.wpdd-columns-2 { grid-template-columns: repeat(2, 1fr); }
+.wpdd-columns-3 { grid-template-columns: repeat(3, 1fr); }
+.wpdd-columns-4 { grid-template-columns: repeat(4, 1fr); }
+
+@media (max-width: 768px) {
+ .wpdd-columns-2,
+ .wpdd-columns-3,
+ .wpdd-columns-4 {
+ grid-template-columns: 1fr;
+ }
+}
+
+@media (min-width: 769px) and (max-width: 1024px) {
+ .wpdd-columns-3,
+ .wpdd-columns-4 {
+ grid-template-columns: repeat(2, 1fr);
+ }
+}
+
+/* Product Cards */
+.wpdd-product-card {
+ background: white;
+ border: 1px solid #e1e5e9;
+ border-radius: 8px;
+ overflow: hidden;
+ transition: transform 0.3s, box-shadow 0.3s;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+}
+
+.wpdd-product-card:hover {
+ transform: translateY(-5px);
+ box-shadow: 0 8px 25px rgba(0,0,0,0.1);
+}
+
+.wpdd-product-image {
+ position: relative;
+ overflow: hidden;
+ height: 200px;
+}
+
+.wpdd-product-image img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ transition: transform 0.3s;
+}
+
+.wpdd-product-card:hover .wpdd-product-image img {
+ transform: scale(1.05);
+}
+
+.wpdd-product-info {
+ padding: 20px;
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+}
+
+.wpdd-product-title {
+ margin: 0 0 10px 0;
+ font-size: 18px;
+ line-height: 1.4;
+}
+
+.wpdd-product-title a {
+ color: #333;
+ text-decoration: none;
+ transition: color 0.3s;
+}
+
+.wpdd-product-title a:hover {
+ color: #0073aa;
+}
+
+.wpdd-product-meta {
+ color: #666;
+ font-size: 14px;
+ margin-bottom: 10px;
+}
+
+.wpdd-product-creator {
+ font-style: italic;
+}
+
+.wpdd-product-excerpt {
+ color: #555;
+ font-size: 14px;
+ line-height: 1.5;
+ margin-bottom: 15px;
+ flex: 1;
+}
+
+.wpdd-product-price {
+ margin-bottom: 15px;
+ font-weight: bold;
+}
+
+.wpdd-price-free {
+ color: #28a745;
+ font-size: 18px;
+}
+
+.wpdd-price-regular {
+ color: #333;
+ font-size: 18px;
+}
+
+.wpdd-price-sale {
+ color: #dc3545;
+ font-size: 18px;
+}
+
+.wpdd-price-strike {
+ text-decoration: line-through;
+ color: #999;
+ font-size: 14px;
+}
+
+.wpdd-product-actions {
+ margin-top: auto;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+
+.wpdd-product-actions .wpdd-btn {
+ width: 100%;
+ justify-content: center;
+}
+
+/* Buttons */
+.wpdd-btn {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 10px 20px;
+ border: none;
+ border-radius: 4px;
+ text-decoration: none;
+ text-align: center;
+ cursor: pointer;
+ transition: all 0.3s;
+ font-size: 14px;
+ line-height: 1.4;
+ box-sizing: border-box;
+}
+
+.wpdd-btn-primary {
+ background: #0073aa;
+ color: white;
+}
+
+.wpdd-btn-primary:hover {
+ background: #005a87;
+ color: white;
+}
+
+.wpdd-btn-view {
+ background: #f8f9fa;
+ color: #333;
+ border: 1px solid #dee2e6;
+}
+
+.wpdd-btn-view:hover {
+ background: #e9ecef;
+ color: #333;
+}
+
+.wpdd-btn-buy {
+ background: #28a745;
+ color: white;
+}
+
+.wpdd-btn-buy:hover {
+ background: #218838;
+ color: white;
+}
+
+.wpdd-owned-product {
+ background: #17a2b8 !important;
+ color: white !important;
+ padding: 10px 30px !important;
+}
+
+.wpdd-owned-product:hover {
+ background: #138496 !important;
+ color: white !important;
+}
+
+.wpdd-btn-view:hover {
+ background: #e9ecef;
+ color: #333;
+}
+
+.wpdd-btn-download {
+ background: #28a745;
+ color: white;
+}
+
+.wpdd-btn-download:hover {
+ background: #218838;
+ color: white;
+}
+
+.wpdd-btn-large {
+ padding: 15px 30px;
+ font-size: 16px;
+}
+
+/* Customer Purchases */
+.wpdd-customer-purchases {
+ max-width: 1000px;
+ margin: 0 auto;
+}
+
+.wpdd-purchases-table {
+ width: 100%;
+ border-collapse: collapse;
+ margin-bottom: 30px;
+ background: white;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+}
+
+.wpdd-purchases-table th,
+.wpdd-purchases-table td {
+ padding: 15px;
+ text-align: left;
+ border-bottom: 1px solid #e1e5e9;
+}
+
+.wpdd-purchases-table th {
+ background: #f8f9fa;
+ font-weight: 600;
+ color: #495057;
+}
+
+.wpdd-purchases-table tr:hover {
+ background: #f8f9fa;
+}
+
+.wpdd-download-expired {
+ color: #dc3545;
+ font-style: italic;
+}
+
+/* Checkout */
+.wpdd-checkout {
+ max-width: 600px;
+ margin: 0 auto;
+}
+
+.wpdd-checkout-product {
+ background: #f8f9fa;
+ padding: 20px;
+ border-radius: 8px;
+ margin-bottom: 30px;
+ text-align: center;
+}
+
+.wpdd-checkout-product h3 {
+ margin: 0 0 15px 0;
+}
+
+.wpdd-checkout-product img {
+ max-width: 150px;
+ height: auto;
+ border-radius: 4px;
+ margin-bottom: 15px;
+}
+
+.wpdd-checkout-price {
+ font-size: 24px;
+ font-weight: bold;
+ color: #0073aa;
+}
+
+.wpdd-checkout-section {
+ background: white;
+ padding: 25px;
+ border: 1px solid #e1e5e9;
+ border-radius: 8px;
+ margin-bottom: 20px;
+}
+
+.wpdd-checkout-section h4 {
+ margin: 0 0 20px 0;
+ padding-bottom: 10px;
+ border-bottom: 1px solid #e1e5e9;
+}
+
+.wpdd-checkout-section p {
+ margin-bottom: 15px;
+}
+
+.wpdd-checkout-section label {
+ display: block;
+ margin-bottom: 5px;
+ font-weight: 500;
+}
+
+.wpdd-checkout-section input[type="text"],
+.wpdd-checkout-section input[type="email"] {
+ width: 100%;
+ padding: 10px;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ font-size: 16px;
+}
+
+.wpdd-checkout-section input[type="checkbox"] {
+ margin-right: 8px;
+}
+
+/* Thank You Page */
+.wpdd-thank-you {
+ max-width: 600px;
+ margin: 0 auto;
+ text-align: center;
+}
+
+.wpdd-order-details {
+ background: white;
+ padding: 30px;
+ border-radius: 8px;
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
+}
+
+.wpdd-order-info {
+ background: #f8f9fa;
+ padding: 20px;
+ border-radius: 6px;
+ margin: 20px 0;
+ text-align: left;
+}
+
+.wpdd-download-section {
+ margin: 30px 0;
+}
+
+.wpdd-download-section h3 {
+ color: #28a745;
+ margin-bottom: 15px;
+}
+
+/* Pagination */
+.wpdd-pagination {
+ text-align: center;
+ margin: 40px 0;
+}
+
+.wpdd-pagination .page-numbers {
+ display: inline-block;
+ padding: 8px 12px;
+ margin: 0 4px;
+ text-decoration: none;
+ border: 1px solid #dee2e6;
+ color: #495057;
+ border-radius: 4px;
+ transition: all 0.3s;
+}
+
+.wpdd-pagination .page-numbers:hover,
+.wpdd-pagination .page-numbers.current {
+ background: #0073aa;
+ color: white;
+ border-color: #0073aa;
+}
+
+/* Responsive */
+@media (max-width: 768px) {
+ .wpdd-shop-filters .wpdd-filter-form {
+ flex-direction: column;
+ align-items: stretch;
+ }
+
+ .wpdd-filter-form input,
+ .wpdd-filter-form select {
+ min-width: auto;
+ width: 100%;
+ }
+
+ .wpdd-purchases-table {
+ font-size: 14px;
+ }
+
+ .wpdd-purchases-table th,
+ .wpdd-purchases-table td {
+ padding: 10px;
+ }
+
+ .wpdd-checkout-section {
+ padding: 20px;
+ }
+}
+
+/* No products message */
+.wpdd-no-products {
+ text-align: center;
+ color: #666;
+ font-size: 18px;
+ margin: 50px 0;
+}
+
+/* Login required message */
+.wpdd-login-required {
+ background: #fff3cd;
+ color: #856404;
+ padding: 15px;
+ border-radius: 4px;
+ border: 1px solid #ffeaa7;
+}
+
+.wpdd-login-required a {
+ color: #856404;
+ font-weight: bold;
+}
\ No newline at end of file
diff --git a/assets/js/admin.js b/assets/js/admin.js
new file mode 100644
index 0000000..f7a610f
--- /dev/null
+++ b/assets/js/admin.js
@@ -0,0 +1,411 @@
+jQuery(document).ready(function($) {
+ 'use strict';
+
+ /**
+ * Main WPDD Admin Object
+ */
+ window.WPDD_Admin = {
+
+ init: function() {
+ this.initFileManager();
+ this.initPriceToggle();
+ this.initFormValidation();
+ },
+
+ initFileManager: function() {
+ var fileIndex = $('#wpdd-files-list .wpdd-file-item').length;
+
+ // Add new file
+ $('#wpdd-add-file').on('click', function(e) {
+ e.preventDefault();
+
+ var template = $('#wpdd-file-template').html();
+ template = template.replace(/INDEX/g, fileIndex);
+
+ var $newFile = $(template);
+ $newFile.attr('data-index', fileIndex);
+
+ $('#wpdd-files-list').append($newFile);
+ fileIndex++;
+
+ WPDD_Admin.updateFileIndices();
+ });
+
+ // Remove file
+ $(document).on('click', '.wpdd-remove-file', function(e) {
+ e.preventDefault();
+
+ if (confirm('Are you sure you want to remove this file?')) {
+ $(this).closest('.wpdd-file-item').remove();
+ WPDD_Admin.updateFileIndices();
+ }
+ });
+
+ // Upload file
+ $(document).on('click', '.wpdd-upload-file', function(e) {
+ e.preventDefault();
+
+ var $button = $(this);
+ var $container = $button.closest('.wpdd-file-item');
+ var $urlInput = $container.find('.wpdd-file-url-input');
+ var $idInput = $container.find('.wpdd-file-id');
+ var $nameInput = $container.find('input[name*="[name]"]');
+
+ // Create file input element
+ var $fileInput = $(' ');
+
+ $fileInput.on('change', function(event) {
+ var file = event.target.files[0];
+ if (!file) return;
+
+ // Show loading state
+ $button.prop('disabled', true).text('Uploading...');
+
+ var formData = new FormData();
+ formData.append('action', 'wpdd_upload_protected_file');
+ formData.append('file', file);
+ formData.append('nonce', wpdd_admin_nonce);
+
+ $.ajax({
+ url: ajaxurl,
+ type: 'POST',
+ data: formData,
+ processData: false,
+ contentType: false,
+ success: function(response) {
+ if (response.success) {
+ $urlInput.val(response.data.protected_url);
+ $idInput.val(response.data.file_id);
+
+ if (!$nameInput.val()) {
+ $nameInput.val(file.name);
+ }
+
+ WPDD_Admin.showAdminNotice('File uploaded to protected directory', 'success');
+ } else {
+ WPDD_Admin.showAdminNotice(response.data || 'Upload failed', 'error');
+ }
+
+ $button.prop('disabled', false).text('Upload File');
+ },
+ error: function() {
+ WPDD_Admin.showAdminNotice('Upload failed', 'error');
+ $button.prop('disabled', false).text('Upload File');
+ }
+ });
+
+ // Clean up
+ $fileInput.remove();
+ });
+
+ // Trigger file selection
+ $('body').append($fileInput);
+ $fileInput.trigger('click');
+ });
+
+ // Make files sortable
+ if ($.fn.sortable) {
+ $('#wpdd-files-list').sortable({
+ handle: '.wpdd-file-handle',
+ placeholder: 'wpdd-file-placeholder',
+ update: function() {
+ WPDD_Admin.updateFileIndices();
+ }
+ });
+ }
+ },
+
+ moveToProtectedDirectory: function(attachmentId, $container) {
+ $.ajax({
+ url: ajaxurl,
+ type: 'POST',
+ data: {
+ action: 'wpdd_move_to_protected',
+ attachment_id: attachmentId,
+ nonce: wpdd_admin_nonce
+ },
+ success: function(response) {
+ if (response.success && response.data.protected_url) {
+ $container.find('.wpdd-file-url-input').val(response.data.protected_url);
+ }
+ }
+ });
+ },
+
+ updateFileIndices: function() {
+ $('#wpdd-files-list .wpdd-file-item').each(function(index) {
+ var $item = $(this);
+ $item.attr('data-index', index);
+
+ // Update input names
+ $item.find('input').each(function() {
+ var name = $(this).attr('name');
+ if (name) {
+ name = name.replace(/\[\d+\]/, '[' + index + ']');
+ $(this).attr('name', name);
+ }
+ });
+ });
+ },
+
+ initPriceToggle: function() {
+ $('#wpdd_is_free').on('change', function() {
+ var $priceFields = $('.wpdd-price-field');
+ if ($(this).is(':checked')) {
+ $priceFields.slideUp();
+ } else {
+ $priceFields.slideDown();
+ }
+ }).trigger('change');
+ },
+
+ initFormValidation: function() {
+ // Validate PayPal settings
+ $('input[name="wpdd_paypal_client_id"], input[name="wpdd_paypal_secret"]').on('blur', function() {
+ var $field = $(this);
+ var value = $field.val().trim();
+
+ if (value && value.length < 10) {
+ $field.addClass('error');
+ WPDD_Admin.showAdminNotice('Invalid PayPal credential format', 'error');
+ } else {
+ $field.removeClass('error');
+ }
+ });
+
+ // Validate email fields
+ $('input[type="email"]').on('blur', function() {
+ var $field = $(this);
+ var value = $field.val().trim();
+ var emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+
+ if (value && !emailRegex.test(value)) {
+ $field.addClass('error');
+ } else {
+ $field.removeClass('error');
+ }
+ });
+
+ // Validate number fields
+ $('input[type="number"]').on('input', function() {
+ var $field = $(this);
+ var value = parseFloat($field.val());
+ var min = parseFloat($field.attr('min'));
+ var max = parseFloat($field.attr('max'));
+
+ if (isNaN(value) || (min !== undefined && value < min) || (max !== undefined && value > max)) {
+ $field.addClass('error');
+ } else {
+ $field.removeClass('error');
+ }
+ });
+ },
+
+ showAdminNotice: function(message, type) {
+ type = type || 'info';
+
+ var notice = $('' +
+ '
' + message + '
' +
+ '
' +
+ '
');
+
+ $('.wrap h1').first().after(notice);
+
+ // Handle dismiss
+ notice.find('.notice-dismiss').on('click', function() {
+ notice.fadeOut(function() {
+ notice.remove();
+ });
+ });
+
+ // Auto-dismiss after 5 seconds for non-error notices
+ if (type !== 'error') {
+ setTimeout(function() {
+ notice.fadeOut(function() {
+ notice.remove();
+ });
+ }, 5000);
+ }
+ },
+
+ initReportsCharts: function() {
+ // Placeholder for future chart implementation
+ // Could integrate Chart.js or similar library
+ },
+
+ initBulkActions: function() {
+ // Handle bulk actions for orders, customers, etc.
+ $('.bulkactions select').on('change', function() {
+ var action = $(this).val();
+ var $button = $(this).siblings('input[type="submit"]');
+
+ if (action) {
+ $button.prop('disabled', false);
+ } else {
+ $button.prop('disabled', true);
+ }
+ });
+ },
+
+ initQuickEdit: function() {
+ // Quick edit functionality for products
+ $('.editinline').on('click', function() {
+ var $row = $(this).closest('tr');
+ var productId = $row.find('.check-column input').val();
+
+ // Populate quick edit fields with current values
+ setTimeout(function() {
+ var $quickEdit = $('.inline-edit-row');
+
+ // Pre-fill price from the displayed value
+ var price = $row.find('.column-wpdd_price').text().replace('$', '');
+ if (price !== 'Free') {
+ $quickEdit.find('input[name="_wpdd_price"]').val(price);
+ }
+
+ }, 100);
+ });
+ },
+
+ initFilePreview: function() {
+ // Show file preview/details when hovering over file names
+ $(document).on('mouseenter', '.wpdd-file-url-input', function() {
+ var url = $(this).val();
+ if (url) {
+ var filename = url.split('/').pop();
+ var extension = filename.split('.').pop().toLowerCase();
+ var fileType = WPDD_Admin.getFileType(extension);
+
+ $(this).attr('title', 'File: ' + filename + ' (Type: ' + fileType + ')');
+ }
+ });
+ },
+
+ getFileType: function(extension) {
+ var types = {
+ 'pdf': 'PDF Document',
+ 'doc': 'Word Document',
+ 'docx': 'Word Document',
+ 'zip': 'Archive',
+ 'rar': 'Archive',
+ 'jpg': 'Image',
+ 'jpeg': 'Image',
+ 'png': 'Image',
+ 'gif': 'Image',
+ 'mp3': 'Audio',
+ 'wav': 'Audio',
+ 'mp4': 'Video',
+ 'avi': 'Video'
+ };
+
+ return types[extension] || 'File';
+ },
+
+ initColorPicker: function() {
+ // Initialize color picker for watermark settings
+ if ($.fn.wpColorPicker) {
+ $('.wpdd-color-picker').wpColorPicker();
+ }
+ },
+
+ initTabs: function() {
+ // Settings page tabs
+ $('.wpdd-settings-tabs').on('click', 'a', function(e) {
+ e.preventDefault();
+
+ var $tab = $(this);
+ var target = $tab.attr('href');
+
+ // Update active tab
+ $tab.siblings().removeClass('nav-tab-active');
+ $tab.addClass('nav-tab-active');
+
+ // Show/hide content
+ $('.wpdd-settings-content').hide();
+ $(target).show();
+ });
+ }
+ };
+
+ // Initialize admin functionality
+ WPDD_Admin.init();
+
+ // Add admin-specific styles
+ $('
+ `;
+ $('head').append(styles);
+ }
+});
\ No newline at end of file
diff --git a/assets/js/paypal.js b/assets/js/paypal.js
new file mode 100644
index 0000000..c68f354
--- /dev/null
+++ b/assets/js/paypal.js
@@ -0,0 +1,346 @@
+jQuery(document).ready(function($) {
+ 'use strict';
+
+ /**
+ * PayPal Integration Object
+ */
+ window.WPDD_PayPal = {
+
+ init: function() {
+ if (typeof paypal !== 'undefined') {
+ this.renderPayPalButton();
+ }
+ },
+
+ renderPayPalButton: function() {
+ var $container = $('#wpdd-paypal-button');
+
+ if (!$container.length) {
+ return;
+ }
+
+ var $form = $container.closest('form');
+
+ paypal.Buttons({
+ createOrder: function(data, actions) {
+ return WPDD_PayPal.createOrder($form);
+ },
+
+ onApprove: function(data, actions) {
+ return WPDD_PayPal.captureOrder(data.orderID);
+ },
+
+ onError: function(err) {
+ console.error('PayPal Error:', err);
+ WPDD_PayPal.showError('Payment processing failed. Please try again.');
+ },
+
+ onCancel: function(data) {
+ WPDD_PayPal.showNotice('Payment was cancelled.', 'info');
+ }
+
+ }).render('#wpdd-paypal-button');
+ },
+
+ createOrder: function($form) {
+ return new Promise(function(resolve, reject) {
+ var formData = $form.serialize();
+
+ $.ajax({
+ url: wpdd_paypal.ajax_url,
+ type: 'POST',
+ data: formData + '&action=wpdd_create_paypal_order&nonce=' + wpdd_paypal.nonce,
+ success: function(response) {
+ if (response.success) {
+ resolve(response.data.orderID);
+ } else {
+ reject(new Error(response.data || 'Failed to create PayPal order'));
+ }
+ },
+ error: function(xhr, status, error) {
+ reject(new Error('Network error: ' + error));
+ }
+ });
+ });
+ },
+
+ captureOrder: function(orderID) {
+ return new Promise(function(resolve, reject) {
+ $.ajax({
+ url: wpdd_paypal.ajax_url,
+ type: 'POST',
+ data: {
+ action: 'wpdd_capture_paypal_order',
+ orderID: orderID,
+ nonce: wpdd_paypal.nonce
+ },
+ success: function(response) {
+ if (response.success) {
+ // Redirect to thank you page
+ window.location.href = response.data.redirect_url;
+ } else {
+ reject(new Error(response.data || 'Failed to capture payment'));
+ }
+ },
+ error: function(xhr, status, error) {
+ reject(new Error('Network error: ' + error));
+ }
+ });
+ });
+ },
+
+ showError: function(message) {
+ this.showNotice(message, 'error');
+ },
+
+ showNotice: function(message, type) {
+ type = type || 'info';
+
+ // Remove existing notices
+ $('.wpdd-paypal-notice').remove();
+
+ var notice = $('' +
+ '
' + message + '
' +
+ '
× ' +
+ '
');
+
+ $('#wpdd-paypal-button').before(notice);
+
+ // Auto-dismiss after 5 seconds
+ setTimeout(function() {
+ notice.fadeOut(function() {
+ notice.remove();
+ });
+ }, 5000);
+
+ // Manual dismiss
+ notice.find('.wpdd-notice-dismiss').on('click', function() {
+ notice.fadeOut(function() {
+ notice.remove();
+ });
+ });
+ },
+
+ validateForm: function($form) {
+ var isValid = true;
+ var errors = [];
+
+ // Check required fields
+ $form.find('input[required]').each(function() {
+ var $field = $(this);
+ var value = $field.val().trim();
+ var fieldName = $field.attr('name') || $field.attr('id');
+
+ if (!value) {
+ isValid = false;
+ errors.push(fieldName + ' is required');
+ $field.addClass('error');
+ } else {
+ $field.removeClass('error');
+ }
+ });
+
+ // Validate email format
+ $form.find('input[type="email"]').each(function() {
+ var $field = $(this);
+ var value = $field.val().trim();
+ var emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+
+ if (value && !emailRegex.test(value)) {
+ isValid = false;
+ errors.push('Please enter a valid email address');
+ $field.addClass('error');
+ }
+ });
+
+ if (!isValid) {
+ this.showError(errors.join(' '));
+ }
+
+ return isValid;
+ },
+
+ showLoadingState: function() {
+ $('#wpdd-paypal-button').addClass('wpdd-loading');
+ },
+
+ hideLoadingState: function() {
+ $('#wpdd-paypal-button').removeClass('wpdd-loading');
+ },
+
+ disableForm: function($form) {
+ $form.find('input, select, button').prop('disabled', true);
+ },
+
+ enableForm: function($form) {
+ $form.find('input, select, button').prop('disabled', false);
+ }
+ };
+
+ // Initialize PayPal integration after object definition
+ WPDD_PayPal.init();
+
+ // Add PayPal-specific styles
+ $('
+ roles)) {
+ $purchases_page = get_option('wpdd_purchases_page_id');
+ if ($purchases_page) {
+ return get_permalink($purchases_page);
+ }
+ }
+
+ return $redirect_to;
+ }
+
+ public static function add_customer_fields($user) {
+ if (!in_array('wpdd_customer', $user->roles)) {
+ return;
+ }
+
+ global $wpdb;
+
+ $total_purchases = $wpdb->get_var($wpdb->prepare(
+ "SELECT COUNT(*) FROM {$wpdb->prefix}wpdd_orders
+ WHERE customer_id = %d AND status = 'completed'",
+ $user->ID
+ ));
+
+ $total_spent = $wpdb->get_var($wpdb->prepare(
+ "SELECT SUM(amount) FROM {$wpdb->prefix}wpdd_orders
+ WHERE customer_id = %d AND status = 'completed'",
+ $user->ID
+ ));
+ ?>
+
+
+ get_results($wpdb->prepare(
+ "SELECT o.*, p.post_title as product_name
+ FROM {$wpdb->prefix}wpdd_orders o
+ LEFT JOIN {$wpdb->posts} p ON o.product_id = p.ID
+ WHERE o.customer_id = %d
+ AND o.status = 'completed'
+ ORDER BY o.purchase_date DESC",
+ $customer_id
+ ));
+ }
+
+ public static function can_download_product($customer_id, $product_id) {
+ global $wpdb;
+
+ $order = $wpdb->get_row($wpdb->prepare(
+ "SELECT * FROM {$wpdb->prefix}wpdd_orders
+ WHERE customer_id = %d
+ AND product_id = %d
+ AND status = 'completed'
+ ORDER BY purchase_date DESC
+ LIMIT 1",
+ $customer_id,
+ $product_id
+ ));
+
+ if (!$order) {
+ return false;
+ }
+
+ $download_limit = get_post_meta($product_id, '_wpdd_download_limit', true);
+ $download_expiry = get_post_meta($product_id, '_wpdd_download_expiry', true);
+
+ if ($download_expiry > 0) {
+ $expiry_date = date('Y-m-d H:i:s', strtotime($order->purchase_date . ' + ' . $download_expiry . ' days'));
+ if (current_time('mysql') > $expiry_date) {
+ return false;
+ }
+ }
+
+ if ($download_limit > 0 && $order->download_count >= $download_limit) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Block wp-admin access for customers
+ */
+ public static function restrict_admin_access() {
+ $current_user = wp_get_current_user();
+
+ // Only block for wpdd_customer role, allow creators and admins
+ if (in_array('wpdd_customer', $current_user->roles) && !current_user_can('manage_options')) {
+ // Allow AJAX requests
+ if (defined('DOING_AJAX') && DOING_AJAX) {
+ return;
+ }
+
+ // Redirect to purchases page
+ $purchases_page = get_option('wpdd_purchases_page_id');
+ $redirect_url = $purchases_page ? get_permalink($purchases_page) : home_url();
+ wp_redirect($redirect_url);
+ exit;
+ }
+ }
+
+ /**
+ * Add frontend customer scripts and functionality
+ */
+ public static function add_customer_scripts() {
+ if (is_user_logged_in()) {
+ $current_user = wp_get_current_user();
+
+ // Only for customers
+ if (in_array('wpdd_customer', $current_user->roles)) {
+ ?>
+
+ get_row($wpdb->prepare(
+ "SELECT * FROM {$wpdb->prefix}wpdd_download_links WHERE id = %d",
+ $download_link_id
+ ));
+
+ if (!$download_link) {
+ wp_die(__('Invalid download link.', 'wp-digital-download'));
+ }
+
+ $order_id = $download_link->order_id;
+
+ // Check by email if guest
+ if (isset($_GET['customer_email']) && isset($_GET['key'])) {
+ $email = sanitize_email($_GET['customer_email']);
+ $key = sanitize_text_field($_GET['key']);
+
+ // Verify the key
+ $expected_key = substr(md5($email . AUTH_KEY), 0, 10);
+ if ($key !== $expected_key) {
+ wp_die(__('Invalid access key.', 'wp-digital-download'));
+ }
+
+ $order = $wpdb->get_row($wpdb->prepare(
+ "SELECT * FROM {$wpdb->prefix}wpdd_orders
+ WHERE id = %d AND customer_email = %s AND status = 'completed'",
+ $order_id,
+ $email
+ ));
+ } elseif (is_user_logged_in()) {
+ $current_user = wp_get_current_user();
+
+ $order = $wpdb->get_row($wpdb->prepare(
+ "SELECT * FROM {$wpdb->prefix}wpdd_orders
+ WHERE id = %d AND (customer_id = %d OR customer_email = %s) AND status = 'completed'",
+ $order_id,
+ $current_user->ID,
+ $current_user->user_email
+ ));
+ } else {
+ // For unregistered users, try to look up order by order number from URL if available
+ if (isset($_GET['order_id'])) {
+ $order_number = sanitize_text_field($_GET['order_id']);
+
+ // Look up order by order ID and verify it matches the order number
+ $order = $wpdb->get_row($wpdb->prepare(
+ "SELECT * FROM {$wpdb->prefix}wpdd_orders
+ WHERE id = %d AND order_number = %s AND status = 'completed'",
+ $order_id,
+ $order_number
+ ));
+
+ if (current_user_can('manage_options')) {
+ error_log('WPDD Debug: Guest order lookup - Order ID: ' . $order_id . ', Order Number: ' . $order_number . ', Found: ' . ($order ? 'Yes' : 'No'));
+ }
+
+ if (!$order) {
+ wp_die(__('Invalid order or order not found.', 'wp-digital-download'));
+ }
+ } else {
+ // Debug: Show what parameters we have
+ $debug_info = '';
+ if (current_user_can('manage_options')) {
+ $debug_info = ' Debug info: ';
+ $debug_info .= 'GET params: ' . print_r($_GET, true);
+ $debug_info .= ' User logged in: ' . (is_user_logged_in() ? 'Yes' : 'No');
+ }
+ wp_die(__('You must be logged in to download this product or provide a valid order reference.', 'wp-digital-download') . $debug_info);
+ }
+ }
+
+ if (!$order) {
+ wp_die(__('Invalid order or you do not have permission to download this product.', 'wp-digital-download'));
+ }
+
+ self::process_download($order);
+ }
+
+ private static function process_download_by_token() {
+ $token = sanitize_text_field($_GET['wpdd_download_token']);
+
+ global $wpdb;
+
+ $download_link = $wpdb->get_row($wpdb->prepare(
+ "SELECT dl.*, o.*
+ FROM {$wpdb->prefix}wpdd_download_links dl
+ INNER JOIN {$wpdb->prefix}wpdd_orders o ON dl.order_id = o.id
+ WHERE dl.token = %s",
+ $token
+ ));
+
+ if (!$download_link) {
+ wp_die(__('Invalid download link.', 'wp-digital-download'));
+ }
+
+ if ($download_link->expires_at < current_time('mysql')) {
+ // Check if user still has downloads remaining
+ if ($download_link->max_downloads > 0 && $download_link->download_count >= $download_link->max_downloads) {
+ wp_die(__('This download link has expired and you have no downloads remaining.', 'wp-digital-download'));
+ }
+
+ // Only send refresh email if this appears to be a real user attempt (not automated)
+ // Check if the customer is unregistered (has no user account)
+ $is_unregistered_customer = ($download_link->customer_id == 0);
+
+ if ($is_unregistered_customer) {
+ // Generate new token and send refresh email only for unregistered customers
+ $new_token = self::refresh_download_token($download_link->order_id, $download_link->customer_email);
+
+ if ($new_token) {
+ wp_die(sprintf(
+ __('Your download link has expired. A new download link has been sent to %s. Please check your email and try again.', 'wp-digital-download'),
+ esc_html($download_link->customer_email)
+ ));
+ } else {
+ wp_die(__('This download link has expired and could not be refreshed. Please contact support.', 'wp-digital-download'));
+ }
+ } else {
+ // For registered users, just show expired message (they can log in to get new links)
+ wp_die(__('This download link has expired. Please log in to your account to get a new download link.', 'wp-digital-download'));
+ }
+ }
+
+ if ($download_link->max_downloads > 0 && $download_link->download_count >= $download_link->max_downloads) {
+ wp_die(__('Download limit exceeded.', 'wp-digital-download'));
+ }
+
+ $wpdb->update(
+ $wpdb->prefix . 'wpdd_download_links',
+ array('download_count' => $download_link->download_count + 1),
+ array('id' => $download_link->id),
+ array('%d'),
+ array('%d')
+ );
+
+ self::process_download($download_link);
+ }
+
+ /**
+ * Refresh an expired download token and send new link via email
+ */
+ private static function refresh_download_token($order_id, $customer_email) {
+ global $wpdb;
+
+ // Generate new token with extended expiry (72 hours as suggested)
+ $new_token = wp_hash(uniqid() . $order_id . time());
+ $new_expires_at = date('Y-m-d H:i:s', strtotime('+72 hours'));
+
+ // Update the existing download link with new token and expiry
+ $updated = $wpdb->update(
+ $wpdb->prefix . 'wpdd_download_links',
+ array(
+ 'token' => $new_token,
+ 'expires_at' => $new_expires_at,
+ 'refreshed_at' => current_time('mysql')
+ ),
+ array('order_id' => $order_id),
+ array('%s', '%s', '%s'),
+ array('%d')
+ );
+
+ if (!$updated) {
+ return false;
+ }
+
+ // Send refresh email
+ self::send_refresh_email($order_id, $new_token, $customer_email);
+
+ return $new_token;
+ }
+
+ /**
+ * Send refresh email with new download link
+ */
+ private static function send_refresh_email($order_id, $token, $customer_email) {
+ global $wpdb;
+
+ // Get order details
+ $order = $wpdb->get_row($wpdb->prepare(
+ "SELECT o.*, p.post_title as product_name
+ FROM {$wpdb->prefix}wpdd_orders o
+ LEFT JOIN {$wpdb->posts} p ON o.product_id = p.ID
+ WHERE o.id = %d",
+ $order_id
+ ));
+
+ if (!$order) {
+ return false;
+ }
+
+ $download_url = add_query_arg(array(
+ 'wpdd_download_token' => $token
+ ), home_url());
+
+ $subject = sprintf(__('New Download Link for %s', 'wp-digital-download'), $order->product_name);
+
+ $message = sprintf(
+ __("Hello %s,\n\nYour download link for \"%s\" has expired, so we've generated a new one for you.\n\nThis new link is valid for 72 hours and can be used for your remaining downloads.\n\nDownload Link: %s\n\nOrder Number: %s\nPurchase Date: %s\n\nIf you have any issues, please contact our support team.\n\nBest regards,\n%s", 'wp-digital-download'),
+ $order->customer_name,
+ $order->product_name,
+ $download_url,
+ $order->order_number,
+ date_i18n(get_option('date_format'), strtotime($order->purchase_date)),
+ get_bloginfo('name')
+ );
+
+ $headers = array('Content-Type: text/plain; charset=UTF-8');
+
+ return wp_mail($customer_email, $subject, $message, $headers);
+ }
+
+ /**
+ * Create download token for orders that don't have one (legacy orders)
+ */
+ public static function ensure_download_token($order_id) {
+ global $wpdb;
+
+ // Check if token already exists
+ $existing_token = $wpdb->get_var($wpdb->prepare(
+ "SELECT token FROM {$wpdb->prefix}wpdd_download_links WHERE order_id = %d",
+ $order_id
+ ));
+
+ if ($existing_token) {
+ return $existing_token;
+ }
+
+ // Create new token for legacy order
+ $token = wp_hash(uniqid() . $order_id . time());
+ $expires_at = date('Y-m-d H:i:s', strtotime('+7 days'));
+
+ $wpdb->insert(
+ $wpdb->prefix . 'wpdd_download_links',
+ array(
+ 'order_id' => $order_id,
+ 'token' => $token,
+ 'expires_at' => $expires_at,
+ 'max_downloads' => 5,
+ 'created_at' => current_time('mysql')
+ ),
+ array('%d', '%s', '%s', '%d', '%s')
+ );
+
+ return $token;
+ }
+
+ private static function process_download($order) {
+ $product_id = $order->product_id;
+ $files = get_post_meta($product_id, '_wpdd_files', true);
+
+ // Debug output for admins
+ if (current_user_can('manage_options') && empty($files)) {
+ wp_die(sprintf(__('Debug: No files found for product ID %d. Files data: %s', 'wp-digital-download'),
+ $product_id, '' . print_r($files, true) . ' '));
+ }
+
+ if (empty($files)) {
+ wp_die(__('No files available for download.', 'wp-digital-download'));
+ }
+
+ // Debug output for admins
+ if (current_user_can('manage_options')) {
+ error_log('WPDD Debug: Files for product ' . $product_id . ': ' . print_r($files, true));
+ }
+
+ $download_limit = get_post_meta($product_id, '_wpdd_download_limit', true);
+ $download_expiry = get_post_meta($product_id, '_wpdd_download_expiry', true);
+
+ if ($download_expiry > 0) {
+ $expiry_date = date('Y-m-d H:i:s', strtotime($order->purchase_date . ' + ' . $download_expiry . ' days'));
+ if (current_time('mysql') > $expiry_date) {
+ wp_die(__('Your download period has expired.', 'wp-digital-download'));
+ }
+ }
+
+ if ($download_limit > 0 && $order->download_count >= $download_limit) {
+ wp_die(__('You have reached the download limit for this product.', 'wp-digital-download'));
+ }
+
+ global $wpdb;
+ $wpdb->update(
+ $wpdb->prefix . 'wpdd_orders',
+ array('download_count' => $order->download_count + 1),
+ array('id' => $order->id),
+ array('%d'),
+ array('%d')
+ );
+
+ $file_index = isset($_GET['file']) ? intval($_GET['file']) : 0;
+
+ // Handle array structure - files might be indexed or not
+ $file_list = array_values($files); // Reindex to ensure numeric keys
+
+ if (count($file_list) > 1 && !isset($_GET['file'])) {
+ self::show_file_selection($file_list, $order);
+ exit;
+ }
+
+ if (!isset($file_list[$file_index])) {
+ // Debug output for admins
+ if (current_user_can('manage_options')) {
+ wp_die(sprintf(__('Debug: File index %d not found. Available files: %s', 'wp-digital-download'),
+ $file_index, '' . print_r($file_list, true) . ' '));
+ }
+ wp_die(__('File not found.', 'wp-digital-download'));
+ }
+
+ $file = $file_list[$file_index];
+
+ self::log_download($order, $product_id, $file['id'] ?? $file_index);
+
+ // Debug for admins
+ if (current_user_can('manage_options')) {
+ error_log('WPDD Debug: Processing file - ID: ' . ($file['id'] ?? 'none') . ', URL: ' . ($file['url'] ?? 'none'));
+ }
+
+ // Check if this is a protected file
+ if (isset($file['id']) && strpos($file['id'], 'wpdd_') === 0) {
+ // This is a protected file, get its metadata
+ $file_meta = get_option('wpdd_protected_file_' . $file['id']);
+
+ if (current_user_can('manage_options')) {
+ error_log('WPDD Debug: Protected file detected - ' . $file['id']);
+ error_log('WPDD Debug: File meta exists: ' . ($file_meta ? 'Yes' : 'No'));
+ if ($file_meta) {
+ error_log('WPDD Debug: File path exists: ' . (file_exists($file_meta['file_path']) ? 'Yes' : 'No'));
+ }
+ }
+
+ if ($file_meta && file_exists($file_meta['file_path'])) {
+ $enable_watermark = get_post_meta($product_id, '_wpdd_enable_watermark', true);
+
+ if ($enable_watermark && self::is_watermarkable($file_meta['file_path'])) {
+ $watermarked_file = WPDD_Watermark::apply_watermark($file_meta['file_path'], $order);
+ if ($watermarked_file) {
+ self::deliver_protected_file($watermarked_file, true);
+ } else {
+ self::deliver_protected_file($file_meta['file_path']);
+ }
+ } else {
+ self::deliver_protected_file($file_meta['file_path']);
+ }
+ return;
+ }
+ }
+
+ // Check if URL contains protected download parameter (alternative check)
+ if (isset($file['url']) && strpos($file['url'], 'wpdd_protected_download=') !== false) {
+ // Extract file_id from URL
+ if (preg_match('/wpdd_protected_download=([^&]+)/', $file['url'], $matches)) {
+ $file_id = $matches[1];
+ $file_meta = get_option('wpdd_protected_file_' . $file_id);
+
+ if (current_user_can('manage_options')) {
+ error_log('WPDD Debug: Protected URL detected - ' . $file_id);
+ }
+
+ if ($file_meta && file_exists($file_meta['file_path'])) {
+ $enable_watermark = get_post_meta($product_id, '_wpdd_enable_watermark', true);
+
+ if ($enable_watermark && self::is_watermarkable($file_meta['file_path'])) {
+ $watermarked_file = WPDD_Watermark::apply_watermark($file_meta['file_path'], $order);
+ if ($watermarked_file) {
+ self::deliver_protected_file($watermarked_file, true);
+ } else {
+ self::deliver_protected_file($file_meta['file_path']);
+ }
+ } else {
+ self::deliver_protected_file($file_meta['file_path']);
+ }
+ return;
+ }
+ }
+ }
+
+ // Regular file handling (backward compatibility)
+ $enable_watermark = get_post_meta($product_id, '_wpdd_enable_watermark', true);
+
+ if ($enable_watermark && self::is_watermarkable($file['url'])) {
+ $watermarked_file = WPDD_Watermark::apply_watermark($file['url'], $order);
+ if ($watermarked_file) {
+ self::deliver_file($watermarked_file, $file['name'], true);
+ } else {
+ self::deliver_file($file['url'], $file['name']);
+ }
+ } else {
+ self::deliver_file($file['url'], $file['name']);
+ }
+ }
+
+ private static function show_file_selection($files, $order) {
+ ?>
+
+ >
+
+
+
+
+
+
+
+
+
+ $file) : ?>
+
+
+
+
+
+
+
+
+
+
+
+ Original: %s', 'wp-digital-download'),
+ $file_path, $original_path));
+ }
+ wp_die(__('File not found. Please contact the administrator.', 'wp-digital-download'));
+ }
+
+ $file_size = filesize($file_path);
+ $file_type = wp_check_filetype($file_path);
+
+ if (empty($file_name)) {
+ $file_name = basename($file_path);
+ }
+
+ nocache_headers();
+
+ header('Content-Type: ' . ($file_type['type'] ?: 'application/octet-stream'));
+ header('Content-Disposition: attachment; filename="' . $file_name . '"');
+ header('Content-Length: ' . $file_size);
+ header('Content-Transfer-Encoding: binary');
+
+ if (ob_get_level()) {
+ ob_end_clean();
+ }
+
+ readfile($file_path);
+
+ if ($is_temp && file_exists($file_path)) {
+ unlink($file_path);
+ }
+
+ exit;
+ }
+
+ private static function log_download($order, $product_id, $file_id) {
+ global $wpdb;
+
+ $wpdb->insert(
+ $wpdb->prefix . 'wpdd_downloads',
+ array(
+ 'order_id' => $order->id,
+ 'product_id' => $product_id,
+ 'customer_id' => $order->customer_id,
+ 'file_id' => $file_id,
+ 'download_date' => current_time('mysql'),
+ 'ip_address' => $_SERVER['REMOTE_ADDR'],
+ 'user_agent' => $_SERVER['HTTP_USER_AGENT']
+ ),
+ array('%d', '%d', '%d', '%s', '%s', '%s', '%s')
+ );
+ }
+
+ private static function is_watermarkable($file_url) {
+ $supported_types = array('jpg', 'jpeg', 'png', 'gif', 'pdf');
+ $file_extension = strtolower(pathinfo($file_url, PATHINFO_EXTENSION));
+ return in_array($file_extension, $supported_types);
+ }
+}
\ No newline at end of file
diff --git a/includes/class-wpdd-file-protection.php b/includes/class-wpdd-file-protection.php
new file mode 100644
index 0000000..9cbd777
--- /dev/null
+++ b/includes/class-wpdd-file-protection.php
@@ -0,0 +1,180 @@
+\n";
+ $new_rules .= "RewriteRule ^" . $protected_path . "/.*$ - [F,L]\n";
+ $new_rules .= "\n\n";
+
+ return $new_rules . $rules;
+ }
+
+ public static function add_download_endpoint() {
+ add_rewrite_endpoint('wpdd-download', EP_ROOT);
+ }
+
+ public static function move_to_protected_directory($attachment_id) {
+ $upload_dir = wp_upload_dir();
+ $protected_dir = trailingslashit($upload_dir['basedir']) . WPDD_UPLOADS_DIR;
+
+ if (!file_exists($protected_dir)) {
+ wp_mkdir_p($protected_dir);
+ self::create_protection_files($protected_dir);
+ }
+
+ $file_path = get_attached_file($attachment_id);
+
+ if (!file_exists($file_path)) {
+ return false;
+ }
+
+ $filename = basename($file_path);
+ $unique_filename = wp_unique_filename($protected_dir, $filename);
+ $new_path = trailingslashit($protected_dir) . $unique_filename;
+
+ if (copy($file_path, $new_path)) {
+ $protected_url = trailingslashit($upload_dir['baseurl']) . WPDD_UPLOADS_DIR . '/' . $unique_filename;
+
+ update_post_meta($attachment_id, '_wpdd_protected_file', $new_path);
+ update_post_meta($attachment_id, '_wpdd_protected_url', $protected_url);
+
+ return array(
+ 'path' => $new_path,
+ 'url' => $protected_url
+ );
+ }
+
+ return false;
+ }
+
+ public static function create_protection_files($directory) {
+ $htaccess_content = "Options -Indexes\n";
+ $htaccess_content .= "deny from all\n";
+
+ $htaccess_file = trailingslashit($directory) . '.htaccess';
+ if (!file_exists($htaccess_file)) {
+ file_put_contents($htaccess_file, $htaccess_content);
+ }
+
+ $index_content = " $file_path,
+ 'order_id' => $order_id,
+ 'expiry' => $expiry
+ ), $expiry_hours * 3600);
+
+ return add_query_arg(array(
+ 'wpdd_secure_download' => $token
+ ), home_url());
+ }
+
+ public static function handle_secure_download() {
+ if (!isset($_GET['wpdd_secure_download'])) {
+ return;
+ }
+
+ $token = sanitize_text_field($_GET['wpdd_secure_download']);
+ $data = get_transient('wpdd_download_' . $token);
+
+ if (!$data) {
+ wp_die(__('Invalid or expired download link.', 'wp-digital-download'));
+ }
+
+ if ($data['expiry'] < time()) {
+ delete_transient('wpdd_download_' . $token);
+ wp_die(__('This download link has expired.', 'wp-digital-download'));
+ }
+
+ if (!file_exists($data['file_path'])) {
+ wp_die(__('File not found.', 'wp-digital-download'));
+ }
+
+ delete_transient('wpdd_download_' . $token);
+
+ self::serve_download($data['file_path']);
+ }
+
+ private static function serve_download($file_path) {
+ $file_name = basename($file_path);
+ $file_size = filesize($file_path);
+ $file_type = wp_check_filetype($file_path);
+
+ nocache_headers();
+
+ header('Content-Type: ' . ($file_type['type'] ?: 'application/octet-stream'));
+ header('Content-Disposition: attachment; filename="' . $file_name . '"');
+ header('Content-Length: ' . $file_size);
+ header('Content-Transfer-Encoding: binary');
+
+ if (ob_get_level()) {
+ ob_end_clean();
+ }
+
+ readfile($file_path);
+ exit;
+ }
+}
\ No newline at end of file
diff --git a/includes/class-wpdd-install.php b/includes/class-wpdd-install.php
new file mode 100644
index 0000000..19302f5
--- /dev/null
+++ b/includes/class-wpdd-install.php
@@ -0,0 +1,215 @@
+get_charset_collate();
+
+ $sql = array();
+
+ $sql[] = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wpdd_orders (
+ id bigint(20) NOT NULL AUTO_INCREMENT,
+ order_number varchar(50) NOT NULL,
+ product_id bigint(20) NOT NULL,
+ customer_id bigint(20) NOT NULL,
+ creator_id bigint(20) NOT NULL,
+ status varchar(20) NOT NULL DEFAULT 'pending',
+ payment_method varchar(50) DEFAULT NULL,
+ transaction_id varchar(100) DEFAULT NULL,
+ amount decimal(10,2) NOT NULL,
+ currency varchar(10) NOT NULL DEFAULT 'USD',
+ customer_email varchar(100) NOT NULL,
+ customer_name varchar(100) DEFAULT NULL,
+ purchase_date datetime DEFAULT CURRENT_TIMESTAMP,
+ download_count int(11) DEFAULT 0,
+ notes text DEFAULT NULL,
+ PRIMARY KEY (id),
+ KEY order_number (order_number),
+ KEY product_id (product_id),
+ KEY customer_id (customer_id),
+ KEY creator_id (creator_id),
+ KEY status (status)
+ ) $charset_collate;";
+
+ $sql[] = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wpdd_downloads (
+ id bigint(20) NOT NULL AUTO_INCREMENT,
+ order_id bigint(20) NOT NULL,
+ product_id bigint(20) NOT NULL,
+ customer_id bigint(20) NOT NULL,
+ file_id varchar(100) NOT NULL,
+ download_date datetime DEFAULT CURRENT_TIMESTAMP,
+ ip_address varchar(45) DEFAULT NULL,
+ user_agent text DEFAULT NULL,
+ PRIMARY KEY (id),
+ KEY order_id (order_id),
+ KEY product_id (product_id),
+ KEY customer_id (customer_id)
+ ) $charset_collate;";
+
+ $sql[] = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wpdd_download_links (
+ id bigint(20) NOT NULL AUTO_INCREMENT,
+ order_id bigint(20) NOT NULL,
+ token varchar(64) NOT NULL,
+ expires_at datetime NOT NULL,
+ download_count int(11) DEFAULT 0,
+ max_downloads int(11) DEFAULT 5,
+ created_at datetime DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (id),
+ UNIQUE KEY token (token),
+ KEY order_id (order_id)
+ ) $charset_collate;";
+
+ $sql[] = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wpdd_creator_earnings (
+ id bigint(20) NOT NULL AUTO_INCREMENT,
+ creator_id bigint(20) NOT NULL,
+ order_id bigint(20) NOT NULL,
+ product_id bigint(20) NOT NULL,
+ sale_amount decimal(10,2) NOT NULL,
+ commission_rate decimal(5,2) NOT NULL,
+ creator_earning decimal(10,2) NOT NULL,
+ created_at datetime DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (id),
+ KEY creator_id (creator_id),
+ KEY order_id (order_id),
+ KEY product_id (product_id)
+ ) $charset_collate;";
+
+ $sql[] = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wpdd_payouts (
+ id bigint(20) NOT NULL AUTO_INCREMENT,
+ creator_id bigint(20) NOT NULL,
+ amount decimal(10,2) NOT NULL,
+ currency varchar(10) NOT NULL,
+ paypal_email varchar(100) NOT NULL,
+ transaction_id varchar(100) DEFAULT NULL,
+ status varchar(20) NOT NULL DEFAULT 'pending',
+ payout_method varchar(20) NOT NULL DEFAULT 'manual',
+ notes text DEFAULT NULL,
+ processed_by bigint(20) DEFAULT NULL,
+ processed_at datetime DEFAULT NULL,
+ created_at datetime DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (id),
+ KEY creator_id (creator_id),
+ KEY status (status),
+ KEY transaction_id (transaction_id)
+ ) $charset_collate;";
+
+ require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
+
+ foreach ($sql as $query) {
+ dbDelta($query);
+ }
+
+ update_option('wpdd_db_version', WPDD_VERSION);
+ }
+
+ private static function create_pages() {
+ $pages = array(
+ 'shop' => array(
+ 'title' => __('Shop', 'wp-digital-download'),
+ 'content' => '[wpdd_shop]',
+ 'option' => 'wpdd_shop_page_id'
+ ),
+ 'my-purchases' => array(
+ 'title' => __('My Purchases', 'wp-digital-download'),
+ 'content' => '[wpdd_customer_purchases]',
+ 'option' => 'wpdd_purchases_page_id'
+ ),
+ 'checkout' => array(
+ 'title' => __('Checkout', 'wp-digital-download'),
+ 'content' => '[wpdd_checkout]',
+ 'option' => 'wpdd_checkout_page_id'
+ ),
+ 'thank-you' => array(
+ 'title' => __('Thank You', 'wp-digital-download'),
+ 'content' => '[wpdd_thank_you]',
+ 'option' => 'wpdd_thank_you_page_id'
+ )
+ );
+
+ foreach ($pages as $slug => $page) {
+ // Check if page already exists
+ $existing_page_id = get_option($page['option']);
+ if ($existing_page_id && get_post($existing_page_id)) {
+ continue; // Page already exists, skip creation
+ }
+
+ // Check if a page with this slug already exists
+ $existing_page = get_page_by_path($slug);
+ if ($existing_page) {
+ update_option($page['option'], $existing_page->ID);
+ continue;
+ }
+
+ // Create the page
+ $page_id = wp_insert_post(array(
+ 'post_title' => $page['title'],
+ 'post_content' => $page['content'],
+ 'post_status' => 'publish',
+ 'post_type' => 'page',
+ 'post_name' => $slug
+ ));
+
+ if ($page_id && !is_wp_error($page_id)) {
+ update_option($page['option'], $page_id);
+ }
+ }
+ }
+
+ private static function create_upload_protection() {
+ $upload_dir = wp_upload_dir();
+ $protection_dir = trailingslashit($upload_dir['basedir']) . WPDD_UPLOADS_DIR;
+
+ if (!file_exists($protection_dir)) {
+ wp_mkdir_p($protection_dir);
+ }
+
+ $htaccess_content = "Options -Indexes\n";
+ $htaccess_content .= "deny from all\n";
+
+ $htaccess_file = trailingslashit($protection_dir) . '.htaccess';
+
+ if (!file_exists($htaccess_file)) {
+ file_put_contents($htaccess_file, $htaccess_content);
+ }
+
+ $index_content = "ID, '_wpdd_price', true);
+ $is_free = get_post_meta($post->ID, '_wpdd_is_free', true);
+ $sale_price = get_post_meta($post->ID, '_wpdd_sale_price', true);
+ ?>
+
+ ID, '_wpdd_files', true);
+ if (!is_array($files)) {
+ $files = array();
+ }
+ ?>
+
+ ID, '_wpdd_download_limit', true);
+ $download_expiry = get_post_meta($post->ID, '_wpdd_download_expiry', true);
+ $enable_watermark = get_post_meta($post->ID, '_wpdd_enable_watermark', true);
+ $watermark_text = get_post_meta($post->ID, '_wpdd_watermark_text', true);
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ get_var($wpdb->prepare(
+ "SELECT COUNT(*) FROM {$wpdb->prefix}wpdd_orders
+ WHERE product_id = %d AND status = 'completed'",
+ $post->ID
+ ));
+
+ $total_revenue = $wpdb->get_var($wpdb->prepare(
+ "SELECT SUM(amount) FROM {$wpdb->prefix}wpdd_orders
+ WHERE product_id = %d AND status = 'completed'",
+ $post->ID
+ ));
+
+ $total_downloads = $wpdb->get_var($wpdb->prepare(
+ "SELECT COUNT(*) FROM {$wpdb->prefix}wpdd_downloads
+ WHERE product_id = %d",
+ $post->ID
+ ));
+ ?>
+
+
+
+
+
+
+
+ $
+
+
+
+
+
+
+ sanitize_text_field($file['id']),
+ 'name' => sanitize_text_field($file['name']),
+ 'url' => esc_url_raw($file['url'])
+ );
+ }
+ }
+ update_post_meta($post_id, '_wpdd_files', $files);
+ }
+
+ if (isset($_POST['wpdd_download_limit'])) {
+ update_post_meta($post_id, '_wpdd_download_limit',
+ intval($_POST['wpdd_download_limit']));
+ }
+
+ if (isset($_POST['wpdd_download_expiry'])) {
+ update_post_meta($post_id, '_wpdd_download_expiry',
+ intval($_POST['wpdd_download_expiry']));
+ }
+
+ update_post_meta($post_id, '_wpdd_enable_watermark',
+ isset($_POST['wpdd_enable_watermark']) ? '1' : '0');
+
+ if (isset($_POST['wpdd_watermark_text'])) {
+ update_post_meta($post_id, '_wpdd_watermark_text',
+ sanitize_text_field($_POST['wpdd_watermark_text']));
+ }
+ }
+}
\ No newline at end of file
diff --git a/includes/class-wpdd-orders.php b/includes/class-wpdd-orders.php
new file mode 100644
index 0000000..12ff13a
--- /dev/null
+++ b/includes/class-wpdd-orders.php
@@ -0,0 +1,318 @@
+post_type !== 'wpdd_product') {
+ return false;
+ }
+
+ $price = get_post_meta($product_id, '_wpdd_price', true);
+ $sale_price = get_post_meta($product_id, '_wpdd_sale_price', true);
+ $is_free = get_post_meta($product_id, '_wpdd_is_free', true);
+
+ $amount = $is_free ? 0 : (($sale_price && $sale_price < $price) ? $sale_price : $price);
+
+ $order_number = 'WPDD-' . strtoupper(uniqid());
+
+ $customer_id = 0;
+ if (is_user_logged_in()) {
+ $current_user = wp_get_current_user();
+ $customer_id = $current_user->ID;
+ $customer_email = $current_user->user_email;
+ $customer_name = $current_user->display_name;
+ } else {
+ $customer_email = $customer_data['email'];
+ $customer_name = $customer_data['name'];
+ }
+
+ $result = $wpdb->insert(
+ $wpdb->prefix . 'wpdd_orders',
+ array(
+ 'order_number' => $order_number,
+ 'product_id' => $product_id,
+ 'customer_id' => $customer_id,
+ 'creator_id' => $product->post_author,
+ 'status' => ($payment_method === 'free' || $amount == 0) ? 'completed' : 'pending',
+ 'payment_method' => $payment_method,
+ 'amount' => $amount,
+ 'currency' => 'USD',
+ 'customer_email' => $customer_email,
+ 'customer_name' => $customer_name,
+ 'purchase_date' => current_time('mysql')
+ ),
+ array('%s', '%d', '%d', '%d', '%s', '%s', '%f', '%s', '%s', '%s', '%s')
+ );
+
+ if ($result) {
+ $order_id = $wpdb->insert_id;
+
+ if ($payment_method === 'free' || $amount == 0) {
+ self::complete_order($order_id);
+ }
+
+ return $order_id;
+ }
+
+ return false;
+ }
+
+ public static function complete_order($order_id, $transaction_id = null) {
+ global $wpdb;
+
+ $order = self::get_order($order_id);
+ if (!$order) {
+ return false;
+ }
+
+ $update_data = array(
+ 'status' => 'completed'
+ );
+
+ if ($transaction_id) {
+ $update_data['transaction_id'] = $transaction_id;
+ }
+
+ $result = $wpdb->update(
+ $wpdb->prefix . 'wpdd_orders',
+ $update_data,
+ array('id' => $order_id),
+ array('%s', '%s'),
+ array('%d')
+ );
+
+ if ($result) {
+ self::generate_download_link($order_id);
+
+ self::send_order_emails($order_id);
+
+ update_post_meta(
+ $order->product_id,
+ '_wpdd_sales_count',
+ intval(get_post_meta($order->product_id, '_wpdd_sales_count', true)) + 1
+ );
+
+ do_action('wpdd_order_completed', $order_id);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ public static function get_order($order_id) {
+ global $wpdb;
+
+ if (is_numeric($order_id)) {
+ return $wpdb->get_row($wpdb->prepare(
+ "SELECT * FROM {$wpdb->prefix}wpdd_orders WHERE id = %d",
+ $order_id
+ ));
+ } else {
+ return $wpdb->get_row($wpdb->prepare(
+ "SELECT * FROM {$wpdb->prefix}wpdd_orders WHERE order_number = %s",
+ $order_id
+ ));
+ }
+ }
+
+ public static function get_orders($args = array()) {
+ global $wpdb;
+
+ $defaults = array(
+ 'status' => '',
+ 'customer_id' => 0,
+ 'creator_id' => 0,
+ 'product_id' => 0,
+ 'limit' => 20,
+ 'offset' => 0,
+ 'orderby' => 'purchase_date',
+ 'order' => 'DESC'
+ );
+
+ $args = wp_parse_args($args, $defaults);
+
+ $where = array('1=1');
+
+ if ($args['status']) {
+ $where[] = $wpdb->prepare("status = %s", $args['status']);
+ }
+
+ if ($args['customer_id']) {
+ $where[] = $wpdb->prepare("customer_id = %d", $args['customer_id']);
+ }
+
+ if ($args['creator_id']) {
+ $where[] = $wpdb->prepare("creator_id = %d", $args['creator_id']);
+ }
+
+ if ($args['product_id']) {
+ $where[] = $wpdb->prepare("product_id = %d", $args['product_id']);
+ }
+
+ $where_clause = implode(' AND ', $where);
+
+ $query = $wpdb->prepare(
+ "SELECT o.*, p.post_title as product_name,
+ u.display_name as customer_display_name,
+ c.display_name as creator_display_name
+ FROM {$wpdb->prefix}wpdd_orders o
+ LEFT JOIN {$wpdb->posts} p ON o.product_id = p.ID
+ LEFT JOIN {$wpdb->users} u ON o.customer_id = u.ID
+ LEFT JOIN {$wpdb->users} c ON o.creator_id = c.ID
+ WHERE {$where_clause}
+ ORDER BY {$args['orderby']} {$args['order']}
+ LIMIT %d OFFSET %d",
+ $args['limit'],
+ $args['offset']
+ );
+
+ return $wpdb->get_results($query);
+ }
+
+ private static function generate_download_link($order_id) {
+ global $wpdb;
+
+ $token = wp_hash(uniqid() . $order_id . time());
+ $expires_at = date('Y-m-d H:i:s', strtotime('+7 days'));
+
+ $wpdb->insert(
+ $wpdb->prefix . 'wpdd_download_links',
+ array(
+ 'order_id' => $order_id,
+ 'token' => $token,
+ 'expires_at' => $expires_at,
+ 'max_downloads' => 5,
+ 'created_at' => current_time('mysql')
+ ),
+ array('%d', '%s', '%s', '%d', '%s')
+ );
+
+ return $token;
+ }
+
+ private static function send_order_emails($order_id) {
+ $order = self::get_order($order_id);
+ if (!$order) {
+ return;
+ }
+
+ self::send_customer_email($order);
+
+ self::send_creator_email($order);
+
+ self::send_admin_email($order);
+ }
+
+ private static function send_customer_email($order) {
+ global $wpdb;
+
+ $product = get_post($order->product_id);
+ $download_link = $wpdb->get_var($wpdb->prepare(
+ "SELECT token FROM {$wpdb->prefix}wpdd_download_links
+ WHERE order_id = %d ORDER BY id DESC LIMIT 1",
+ $order->id
+ ));
+
+ $download_url = add_query_arg(array(
+ 'wpdd_download_token' => $download_link
+ ), home_url());
+
+ $subject = sprintf(
+ __('Your purchase of %s from %s', 'wp-digital-download'),
+ $product->post_title,
+ get_bloginfo('name')
+ );
+
+ $message = sprintf(
+ __("Hi %s,\n\nThank you for your purchase!\n\n", 'wp-digital-download'),
+ $order->customer_name
+ );
+ $message .= sprintf(__("Order Number: %s\n", 'wp-digital-download'), $order->order_number);
+ $message .= sprintf(__("Product: %s\n", 'wp-digital-download'), $product->post_title);
+
+ if ($order->amount > 0) {
+ $message .= sprintf(__("Amount: $%s\n", 'wp-digital-download'), number_format($order->amount, 2));
+ }
+
+ $message .= "\n" . __("Download your product here:\n", 'wp-digital-download');
+ $message .= $download_url . "\n\n";
+ $message .= __("This download link will expire in 7 days.\n\n", 'wp-digital-download');
+
+ if ($order->customer_id) {
+ $purchases_url = get_permalink(get_option('wpdd_purchases_page_id'));
+ $message .= sprintf(
+ __("You can also access your downloads anytime from your account:\n%s\n\n", 'wp-digital-download'),
+ $purchases_url
+ );
+ }
+
+ $message .= sprintf(__("Best regards,\n%s", 'wp-digital-download'), get_bloginfo('name'));
+
+ wp_mail($order->customer_email, $subject, $message);
+ }
+
+ private static function send_creator_email($order) {
+ $creator = get_userdata($order->creator_id);
+ if (!$creator) {
+ return;
+ }
+
+ $product = get_post($order->product_id);
+
+ $subject = sprintf(
+ __('New sale: %s', 'wp-digital-download'),
+ $product->post_title
+ );
+
+ $message = sprintf(
+ __("Hi %s,\n\nYou have a new sale!\n\n", 'wp-digital-download'),
+ $creator->display_name
+ );
+ $message .= sprintf(__("Product: %s\n", 'wp-digital-download'), $product->post_title);
+ $message .= sprintf(__("Customer: %s\n", 'wp-digital-download'), $order->customer_name);
+ $message .= sprintf(__("Amount: $%s\n", 'wp-digital-download'), number_format($order->amount, 2));
+ $message .= sprintf(__("Order Number: %s\n", 'wp-digital-download'), $order->order_number);
+ $message .= "\n" . sprintf(
+ __("View your sales dashboard:\n%s\n", 'wp-digital-download'),
+ admin_url()
+ );
+
+ wp_mail($creator->user_email, $subject, $message);
+ }
+
+ private static function send_admin_email($order) {
+ $admin_email = get_option('wpdd_admin_email', get_option('admin_email'));
+
+ if (!$admin_email) {
+ return;
+ }
+
+ $product = get_post($order->product_id);
+
+ $subject = sprintf(
+ __('[%s] New Digital Download Sale', 'wp-digital-download'),
+ get_bloginfo('name')
+ );
+
+ $message = __("A new digital download sale has been completed.\n\n", 'wp-digital-download');
+ $message .= sprintf(__("Order Number: %s\n", 'wp-digital-download'), $order->order_number);
+ $message .= sprintf(__("Product: %s\n", 'wp-digital-download'), $product->post_title);
+ $message .= sprintf(__("Customer: %s (%s)\n", 'wp-digital-download'), $order->customer_name, $order->customer_email);
+ $message .= sprintf(__("Amount: $%s\n", 'wp-digital-download'), number_format($order->amount, 2));
+ $message .= sprintf(__("Payment Method: %s\n", 'wp-digital-download'), $order->payment_method);
+
+ if ($order->transaction_id) {
+ $message .= sprintf(__("Transaction ID: %s\n", 'wp-digital-download'), $order->transaction_id);
+ }
+
+ wp_mail($admin_email, $subject, $message);
+ }
+}
\ No newline at end of file
diff --git a/includes/class-wpdd-paypal-payouts.php b/includes/class-wpdd-paypal-payouts.php
new file mode 100644
index 0000000..68eba2f
--- /dev/null
+++ b/includes/class-wpdd-paypal-payouts.php
@@ -0,0 +1,231 @@
+balance) >= $threshold && !empty($creator->paypal_email)) {
+ WPDD_Admin_Payouts::create_payout($creator->ID, 'automatic');
+ }
+ }
+ }
+
+ public static function process_payout($payout_id) {
+ global $wpdb;
+
+ // Get payout details
+ $payout = $wpdb->get_row($wpdb->prepare(
+ "SELECT * FROM {$wpdb->prefix}wpdd_payouts WHERE id = %d",
+ $payout_id
+ ));
+
+ if (!$payout) {
+ return array('success' => false, 'error' => 'Payout not found');
+ }
+
+ // Get PayPal credentials
+ $mode = get_option('wpdd_paypal_mode', 'sandbox');
+ $client_id = get_option('wpdd_paypal_client_id');
+ $secret = get_option('wpdd_paypal_secret');
+
+ if (empty($client_id) || empty($secret)) {
+ return array('success' => false, 'error' => 'PayPal credentials not configured');
+ }
+
+ // Get access token
+ $token_result = self::get_access_token($client_id, $secret, $mode);
+
+ if (!$token_result['success']) {
+ return array('success' => false, 'error' => $token_result['error']);
+ }
+
+ $access_token = $token_result['token'];
+
+ // Create payout batch
+ $batch_result = self::create_payout_batch($payout, $access_token, $mode);
+
+ if ($batch_result['success']) {
+ return array(
+ 'success' => true,
+ 'transaction_id' => $batch_result['batch_id']
+ );
+ } else {
+ return array(
+ 'success' => false,
+ 'error' => $batch_result['error']
+ );
+ }
+ }
+
+ private static function get_access_token($client_id, $secret, $mode) {
+ $base_url = $mode === 'sandbox'
+ ? 'https://api-m.sandbox.paypal.com'
+ : 'https://api-m.paypal.com';
+
+ $response = wp_remote_post(
+ $base_url . '/v1/oauth2/token',
+ array(
+ 'headers' => array(
+ 'Authorization' => 'Basic ' . base64_encode($client_id . ':' . $secret),
+ 'Content-Type' => 'application/x-www-form-urlencoded'
+ ),
+ 'body' => 'grant_type=client_credentials',
+ 'timeout' => 30
+ )
+ );
+
+ if (is_wp_error($response)) {
+ return array('success' => false, 'error' => $response->get_error_message());
+ }
+
+ $body = json_decode(wp_remote_retrieve_body($response), true);
+
+ if (isset($body['access_token'])) {
+ return array('success' => true, 'token' => $body['access_token']);
+ } else {
+ $error = isset($body['error_description']) ? $body['error_description'] : 'Failed to get access token';
+ return array('success' => false, 'error' => $error);
+ }
+ }
+
+ private static function create_payout_batch($payout, $access_token, $mode) {
+ $base_url = $mode === 'sandbox'
+ ? 'https://api-m.sandbox.paypal.com'
+ : 'https://api-m.paypal.com';
+
+ $batch_id = 'WPDD_' . $payout->id . '_' . time();
+
+ $payout_data = array(
+ 'sender_batch_header' => array(
+ 'sender_batch_id' => $batch_id,
+ 'email_subject' => 'You have received a payout!',
+ 'email_message' => 'You have received a payout from ' . get_bloginfo('name')
+ ),
+ 'items' => array(
+ array(
+ 'recipient_type' => 'EMAIL',
+ 'amount' => array(
+ 'value' => number_format($payout->amount, 2, '.', ''),
+ 'currency' => $payout->currency
+ ),
+ 'receiver' => $payout->paypal_email,
+ 'note' => 'Payout for your sales on ' . get_bloginfo('name'),
+ 'sender_item_id' => 'payout_' . $payout->id
+ )
+ )
+ );
+
+ $response = wp_remote_post(
+ $base_url . '/v1/payments/payouts',
+ array(
+ 'headers' => array(
+ 'Authorization' => 'Bearer ' . $access_token,
+ 'Content-Type' => 'application/json'
+ ),
+ 'body' => json_encode($payout_data),
+ 'timeout' => 30
+ )
+ );
+
+ if (is_wp_error($response)) {
+ return array('success' => false, 'error' => $response->get_error_message());
+ }
+
+ $response_code = wp_remote_retrieve_response_code($response);
+ $body = json_decode(wp_remote_retrieve_body($response), true);
+
+ if ($response_code === 201 && isset($body['batch_header']['payout_batch_id'])) {
+ return array(
+ 'success' => true,
+ 'batch_id' => $body['batch_header']['payout_batch_id']
+ );
+ } else {
+ $error = 'Failed to create payout batch';
+ if (isset($body['message'])) {
+ $error = $body['message'];
+ } elseif (isset($body['error_description'])) {
+ $error = $body['error_description'];
+ }
+
+ return array('success' => false, 'error' => $error);
+ }
+ }
+
+ public static function check_batch_status($batch_id, $mode = null) {
+ if (!$mode) {
+ $mode = get_option('wpdd_paypal_mode', 'sandbox');
+ }
+
+ $client_id = get_option('wpdd_paypal_client_id');
+ $secret = get_option('wpdd_paypal_secret');
+
+ if (empty($client_id) || empty($secret)) {
+ return array('success' => false, 'error' => 'PayPal credentials not configured');
+ }
+
+ // Get access token
+ $token_result = self::get_access_token($client_id, $secret, $mode);
+
+ if (!$token_result['success']) {
+ return array('success' => false, 'error' => $token_result['error']);
+ }
+
+ $access_token = $token_result['token'];
+
+ $base_url = $mode === 'sandbox'
+ ? 'https://api-m.sandbox.paypal.com'
+ : 'https://api-m.paypal.com';
+
+ $response = wp_remote_get(
+ $base_url . '/v1/payments/payouts/' . $batch_id,
+ array(
+ 'headers' => array(
+ 'Authorization' => 'Bearer ' . $access_token,
+ 'Content-Type' => 'application/json'
+ ),
+ 'timeout' => 30
+ )
+ );
+
+ if (is_wp_error($response)) {
+ return array('success' => false, 'error' => $response->get_error_message());
+ }
+
+ $body = json_decode(wp_remote_retrieve_body($response), true);
+
+ if (isset($body['batch_header'])) {
+ return array(
+ 'success' => true,
+ 'status' => $body['batch_header']['batch_status'],
+ 'data' => $body
+ );
+ } else {
+ return array('success' => false, 'error' => 'Failed to get batch status');
+ }
+ }
+
+ public static function deactivate() {
+ wp_clear_scheduled_hook('wpdd_process_automatic_payouts');
+ }
+}
\ No newline at end of file
diff --git a/includes/class-wpdd-paypal.php b/includes/class-wpdd-paypal.php
new file mode 100644
index 0000000..a7327cf
--- /dev/null
+++ b/includes/class-wpdd-paypal.php
@@ -0,0 +1,302 @@
+ admin_url('admin-ajax.php'),
+ 'nonce' => wp_create_nonce('wpdd-paypal-nonce'),
+ 'mode' => $mode
+ ));
+ }
+
+ public static function create_order() {
+ check_ajax_referer('wpdd-paypal-nonce', 'nonce');
+
+ $product_id = isset($_POST['product_id']) ? intval($_POST['product_id']) : 0;
+
+ if (!$product_id) {
+ wp_send_json_error('Invalid product');
+ }
+
+ $product = get_post($product_id);
+
+ if (!$product || $product->post_type !== 'wpdd_product') {
+ wp_send_json_error('Product not found');
+ }
+
+ $price = get_post_meta($product_id, '_wpdd_price', true);
+ $sale_price = get_post_meta($product_id, '_wpdd_sale_price', true);
+ $final_price = ($sale_price && $sale_price < $price) ? $sale_price : $price;
+
+ $order_data = array(
+ 'intent' => 'CAPTURE',
+ 'purchase_units' => array(
+ array(
+ 'reference_id' => 'wpdd_' . $product_id . '_' . time(),
+ 'description' => substr($product->post_title, 0, 127),
+ 'amount' => array(
+ 'currency_code' => 'USD',
+ 'value' => number_format($final_price, 2, '.', '')
+ )
+ )
+ ),
+ 'application_context' => array(
+ 'brand_name' => get_bloginfo('name'),
+ 'return_url' => add_query_arg('wpdd_paypal_return', '1', get_permalink(get_option('wpdd_thank_you_page_id'))),
+ 'cancel_url' => add_query_arg('wpdd_paypal_cancel', '1', get_permalink(get_option('wpdd_checkout_page_id')))
+ )
+ );
+
+ $paypal_order = self::api_request('/v2/checkout/orders', $order_data, 'POST');
+
+ if (isset($paypal_order['id'])) {
+ $_SESSION['wpdd_paypal_order_' . $paypal_order['id']] = array(
+ 'product_id' => $product_id,
+ 'amount' => $final_price,
+ 'customer_email' => sanitize_email($_POST['customer_email'] ?? ''),
+ 'customer_name' => sanitize_text_field($_POST['customer_name'] ?? '')
+ );
+
+ wp_send_json_success(array('orderID' => $paypal_order['id']));
+ } else {
+ wp_send_json_error('Failed to create PayPal order');
+ }
+ }
+
+ public static function capture_order() {
+ check_ajax_referer('wpdd-paypal-nonce', 'nonce');
+
+ $paypal_order_id = isset($_POST['orderID']) ? sanitize_text_field($_POST['orderID']) : '';
+
+ if (!$paypal_order_id) {
+ wp_send_json_error('Invalid order ID');
+ }
+
+ $capture_response = self::api_request('/v2/checkout/orders/' . $paypal_order_id . '/capture', array(), 'POST');
+
+ if (isset($capture_response['status']) && $capture_response['status'] === 'COMPLETED') {
+ $session_data = $_SESSION['wpdd_paypal_order_' . $paypal_order_id] ?? array();
+
+ // Add error logging for debugging session issues
+ if (empty($session_data)) {
+ error_log('WPDD PayPal Error: No session data found for PayPal order ' . $paypal_order_id);
+ error_log('WPDD PayPal Debug: Session ID: ' . session_id());
+ error_log('WPDD PayPal Debug: Available sessions: ' . print_r($_SESSION ?? array(), true));
+ wp_send_json_error('Session data not found - order cannot be processed');
+ return;
+ }
+
+ $order_number = 'WPDD-' . strtoupper(uniqid());
+
+ global $wpdb;
+
+ $customer_id = 0;
+ $customer_email = $session_data['customer_email'];
+ $customer_name = $session_data['customer_name'];
+
+ if (is_user_logged_in()) {
+ $current_user = wp_get_current_user();
+ $customer_id = $current_user->ID;
+ $customer_email = $current_user->user_email;
+ $customer_name = $current_user->display_name;
+ } elseif (!empty($_POST['create_account']) && !empty($customer_email)) {
+ $username = strstr($customer_email, '@', true) . '_' . wp_rand(1000, 9999);
+ $password = wp_generate_password();
+
+ $user_id = wp_create_user($username, $password, $customer_email);
+
+ if (!is_wp_error($user_id)) {
+ $customer_id = $user_id;
+ wp_update_user(array(
+ 'ID' => $user_id,
+ 'display_name' => $customer_name,
+ 'first_name' => $customer_name
+ ));
+
+ $user = new WP_User($user_id);
+ $user->set_role('wpdd_customer');
+
+ wp_new_user_notification($user_id, null, 'both');
+ }
+ }
+
+ $product = get_post($session_data['product_id']);
+
+ $wpdb->insert(
+ $wpdb->prefix . 'wpdd_orders',
+ array(
+ 'order_number' => $order_number,
+ 'product_id' => $session_data['product_id'],
+ 'customer_id' => $customer_id,
+ 'creator_id' => $product->post_author,
+ 'status' => 'completed',
+ 'payment_method' => 'paypal',
+ 'transaction_id' => $capture_response['id'],
+ 'amount' => $session_data['amount'],
+ 'currency' => 'USD',
+ 'customer_email' => $customer_email,
+ 'customer_name' => $customer_name,
+ 'purchase_date' => current_time('mysql')
+ ),
+ array('%s', '%d', '%d', '%d', '%s', '%s', '%s', '%f', '%s', '%s', '%s', '%s')
+ );
+
+ $order_id = $wpdb->insert_id;
+
+ self::generate_download_link($order_id);
+
+ self::send_purchase_email($order_id);
+
+ update_post_meta($session_data['product_id'], '_wpdd_sales_count',
+ intval(get_post_meta($session_data['product_id'], '_wpdd_sales_count', true)) + 1);
+
+ unset($_SESSION['wpdd_paypal_order_' . $paypal_order_id]);
+
+ wp_send_json_success(array(
+ 'redirect_url' => add_query_arg(
+ 'order_id',
+ $order_number,
+ get_permalink(get_option('wpdd_thank_you_page_id'))
+ )
+ ));
+ } else {
+ wp_send_json_error('Payment capture failed');
+ }
+ }
+
+ private static function api_request($endpoint, $data = array(), $method = 'GET') {
+ $mode = get_option('wpdd_paypal_mode', 'sandbox');
+ $client_id = get_option('wpdd_paypal_client_id');
+ $secret = get_option('wpdd_paypal_secret');
+
+ if (!$client_id || !$secret) {
+ return false;
+ }
+
+ $base_url = $mode === 'live'
+ ? 'https://api.paypal.com'
+ : 'https://api.sandbox.paypal.com';
+
+ $auth = base64_encode($client_id . ':' . $secret);
+
+ $args = array(
+ 'method' => $method,
+ 'headers' => array(
+ 'Authorization' => 'Basic ' . $auth,
+ 'Content-Type' => 'application/json'
+ ),
+ 'timeout' => 30
+ );
+
+ if (!empty($data)) {
+ $args['body'] = json_encode($data);
+ }
+
+ $response = wp_remote_request($base_url . $endpoint, $args);
+
+ if (is_wp_error($response)) {
+ return false;
+ }
+
+ $body = wp_remote_retrieve_body($response);
+ return json_decode($body, true);
+ }
+
+ private static function generate_download_link($order_id) {
+ global $wpdb;
+
+ $token = wp_hash(uniqid() . $order_id . time());
+ $expires_at = date('Y-m-d H:i:s', strtotime('+7 days'));
+
+ $wpdb->insert(
+ $wpdb->prefix . 'wpdd_download_links',
+ array(
+ 'order_id' => $order_id,
+ 'token' => $token,
+ 'expires_at' => $expires_at,
+ 'max_downloads' => 5,
+ 'created_at' => current_time('mysql')
+ ),
+ array('%d', '%s', '%s', '%d', '%s')
+ );
+
+ return $token;
+ }
+
+ private static function send_purchase_email($order_id) {
+ global $wpdb;
+
+ $order = $wpdb->get_row($wpdb->prepare(
+ "SELECT o.*, p.post_title as product_name, dl.token
+ FROM {$wpdb->prefix}wpdd_orders o
+ LEFT JOIN {$wpdb->posts} p ON o.product_id = p.ID
+ LEFT JOIN {$wpdb->prefix}wpdd_download_links dl ON o.id = dl.order_id
+ WHERE o.id = %d",
+ $order_id
+ ));
+
+ if (!$order) {
+ return;
+ }
+
+ $download_url = add_query_arg(array(
+ 'wpdd_download_token' => $order->token
+ ), home_url());
+
+ $subject = sprintf(__('Your Purchase from %s', 'wp-digital-download'), get_bloginfo('name'));
+
+ $message = sprintf(
+ __("Hi %s,\n\nThank you for your purchase!\n\n", 'wp-digital-download'),
+ $order->customer_name
+ );
+ $message .= sprintf(__("Order Number: %s\n", 'wp-digital-download'), $order->order_number);
+ $message .= sprintf(__("Product: %s\n", 'wp-digital-download'), $order->product_name);
+ $message .= sprintf(__("Amount: $%s\n\n", 'wp-digital-download'), number_format($order->amount, 2));
+ $message .= __("Download your product here:\n", 'wp-digital-download');
+ $message .= $download_url . "\n\n";
+ $message .= __("This download link will expire in 7 days.\n\n", 'wp-digital-download');
+ $message .= sprintf(__("Best regards,\n%s", 'wp-digital-download'), get_bloginfo('name'));
+
+ wp_mail($order->customer_email, $subject, $message);
+ }
+}
\ No newline at end of file
diff --git a/includes/class-wpdd-post-types.php b/includes/class-wpdd-post-types.php
new file mode 100644
index 0000000..83f308f
--- /dev/null
+++ b/includes/class-wpdd-post-types.php
@@ -0,0 +1,142 @@
+ __('Products', 'wp-digital-download'),
+ 'singular_name' => __('Product', 'wp-digital-download'),
+ 'menu_name' => __('Digital Products', 'wp-digital-download'),
+ 'add_new' => __('Add New', 'wp-digital-download'),
+ 'add_new_item' => __('Add New Product', 'wp-digital-download'),
+ 'edit_item' => __('Edit Product', 'wp-digital-download'),
+ 'new_item' => __('New Product', 'wp-digital-download'),
+ 'view_item' => __('View Product', 'wp-digital-download'),
+ 'view_items' => __('View Products', 'wp-digital-download'),
+ 'search_items' => __('Search Products', 'wp-digital-download'),
+ 'not_found' => __('No products found', 'wp-digital-download'),
+ 'not_found_in_trash' => __('No products found in Trash', 'wp-digital-download'),
+ 'all_items' => __('All Products', 'wp-digital-download'),
+ 'archives' => __('Product Archives', 'wp-digital-download'),
+ 'attributes' => __('Product Attributes', 'wp-digital-download'),
+ 'insert_into_item' => __('Insert into product', 'wp-digital-download'),
+ 'uploaded_to_this_item' => __('Uploaded to this product', 'wp-digital-download'),
+ 'featured_image' => __('Product Image', 'wp-digital-download'),
+ 'set_featured_image' => __('Set product image', 'wp-digital-download'),
+ 'remove_featured_image' => __('Remove product image', 'wp-digital-download'),
+ 'use_featured_image' => __('Use as product image', 'wp-digital-download'),
+ );
+
+ $args = array(
+ 'labels' => $labels,
+ 'public' => true,
+ 'publicly_queryable' => true,
+ 'show_ui' => true,
+ 'show_in_menu' => true,
+ 'query_var' => true,
+ 'rewrite' => array('slug' => 'product'),
+ 'capability_type' => 'post',
+ 'capabilities' => array(
+ 'edit_post' => 'edit_wpdd_product',
+ 'read_post' => 'read_wpdd_product',
+ 'delete_post' => 'delete_wpdd_product',
+ 'edit_posts' => 'edit_wpdd_products',
+ 'edit_others_posts' => 'edit_others_wpdd_products',
+ 'publish_posts' => 'publish_wpdd_products',
+ 'read_private_posts' => 'read_private_wpdd_products',
+ 'delete_posts' => 'delete_wpdd_products',
+ 'delete_private_posts' => 'delete_private_wpdd_products',
+ 'delete_published_posts' => 'delete_published_wpdd_products',
+ 'delete_others_posts' => 'delete_others_wpdd_products',
+ 'edit_private_posts' => 'edit_private_wpdd_products',
+ 'edit_published_posts' => 'edit_published_wpdd_products',
+ ),
+ 'map_meta_cap' => true,
+ 'has_archive' => true,
+ 'hierarchical' => false,
+ 'menu_position' => 25,
+ 'menu_icon' => 'dashicons-download',
+ 'supports' => array('title', 'editor', 'thumbnail', 'excerpt', 'author'),
+ 'show_in_rest' => true,
+ );
+
+ register_post_type('wpdd_product', $args);
+ }
+
+ public static function register_taxonomies() {
+ $labels = array(
+ 'name' => __('Product Categories', 'wp-digital-download'),
+ 'singular_name' => __('Product Category', 'wp-digital-download'),
+ 'search_items' => __('Search Categories', 'wp-digital-download'),
+ 'all_items' => __('All Categories', 'wp-digital-download'),
+ 'parent_item' => __('Parent Category', 'wp-digital-download'),
+ 'parent_item_colon' => __('Parent Category:', 'wp-digital-download'),
+ 'edit_item' => __('Edit Category', 'wp-digital-download'),
+ 'update_item' => __('Update Category', 'wp-digital-download'),
+ 'add_new_item' => __('Add New Category', 'wp-digital-download'),
+ 'new_item_name' => __('New Category Name', 'wp-digital-download'),
+ 'menu_name' => __('Categories', 'wp-digital-download'),
+ );
+
+ $args = array(
+ 'labels' => $labels,
+ 'hierarchical' => true,
+ 'public' => true,
+ 'show_ui' => true,
+ 'show_admin_column' => true,
+ 'query_var' => true,
+ 'rewrite' => array('slug' => 'product-category'),
+ 'show_in_rest' => true,
+ );
+
+ register_taxonomy('wpdd_product_category', 'wpdd_product', $args);
+
+ $tag_labels = array(
+ 'name' => __('Product Tags', 'wp-digital-download'),
+ 'singular_name' => __('Product Tag', 'wp-digital-download'),
+ 'search_items' => __('Search Tags', 'wp-digital-download'),
+ 'popular_items' => __('Popular Tags', 'wp-digital-download'),
+ 'all_items' => __('All Tags', 'wp-digital-download'),
+ 'edit_item' => __('Edit Tag', 'wp-digital-download'),
+ 'update_item' => __('Update Tag', 'wp-digital-download'),
+ 'add_new_item' => __('Add New Tag', 'wp-digital-download'),
+ 'new_item_name' => __('New Tag Name', 'wp-digital-download'),
+ 'separate_items_with_commas' => __('Separate tags with commas', 'wp-digital-download'),
+ 'add_or_remove_items' => __('Add or remove tags', 'wp-digital-download'),
+ 'choose_from_most_used' => __('Choose from the most used tags', 'wp-digital-download'),
+ 'menu_name' => __('Tags', 'wp-digital-download'),
+ );
+
+ $tag_args = array(
+ 'labels' => $tag_labels,
+ 'hierarchical' => false,
+ 'public' => true,
+ 'show_ui' => true,
+ 'show_admin_column' => true,
+ 'query_var' => true,
+ 'rewrite' => array('slug' => 'product-tag'),
+ 'show_in_rest' => true,
+ );
+
+ register_taxonomy('wpdd_product_tag', 'wpdd_product', $tag_args);
+ }
+
+ public static function product_permalink($permalink, $post) {
+ if ($post->post_type !== 'wpdd_product') {
+ return $permalink;
+ }
+
+ return $permalink;
+ }
+}
\ No newline at end of file
diff --git a/includes/class-wpdd-roles.php b/includes/class-wpdd-roles.php
new file mode 100644
index 0000000..280d26d
--- /dev/null
+++ b/includes/class-wpdd-roles.php
@@ -0,0 +1,116 @@
+ true,
+ 'wpdd_view_purchases' => true,
+ 'wpdd_download_products' => true,
+ )
+ );
+ }
+
+ private static function create_creator_role() {
+ add_role(
+ 'wpdd_creator',
+ __('Digital Creator', 'wp-digital-download'),
+ array(
+ 'read' => true,
+ 'upload_files' => true,
+ 'edit_posts' => false,
+ 'delete_posts' => false,
+ 'publish_posts' => false,
+
+ 'edit_wpdd_products' => true,
+ 'edit_published_wpdd_products' => true,
+ 'publish_wpdd_products' => true,
+ 'delete_wpdd_products' => true,
+ 'delete_published_wpdd_products' => true,
+ 'edit_private_wpdd_products' => true,
+ 'delete_private_wpdd_products' => true,
+
+ 'wpdd_view_own_sales' => true,
+ 'wpdd_manage_own_products' => true,
+ 'wpdd_upload_product_files' => true,
+ 'wpdd_view_reports' => true,
+ )
+ );
+ }
+
+ private static function add_admin_capabilities() {
+ $role = get_role('administrator');
+
+ if ($role) {
+ $role->add_cap('edit_wpdd_products');
+ $role->add_cap('edit_others_wpdd_products');
+ $role->add_cap('edit_published_wpdd_products');
+ $role->add_cap('publish_wpdd_products');
+ $role->add_cap('delete_wpdd_products');
+ $role->add_cap('delete_others_wpdd_products');
+ $role->add_cap('delete_published_wpdd_products');
+ $role->add_cap('edit_private_wpdd_products');
+ $role->add_cap('delete_private_wpdd_products');
+
+ $role->add_cap('wpdd_manage_settings');
+ $role->add_cap('wpdd_view_all_sales');
+ $role->add_cap('wpdd_manage_all_products');
+ $role->add_cap('wpdd_view_reports');
+ $role->add_cap('wpdd_manage_orders');
+ }
+ }
+
+ public static function remove_roles() {
+ remove_role('wpdd_customer');
+ remove_role('wpdd_creator');
+
+ $role = get_role('administrator');
+ if ($role) {
+ $caps = array(
+ 'edit_wpdd_products',
+ 'edit_others_wpdd_products',
+ 'edit_published_wpdd_products',
+ 'publish_wpdd_products',
+ 'delete_wpdd_products',
+ 'delete_others_wpdd_products',
+ 'delete_published_wpdd_products',
+ 'edit_private_wpdd_products',
+ 'delete_private_wpdd_products',
+ 'wpdd_manage_settings',
+ 'wpdd_view_all_sales',
+ 'wpdd_manage_all_products',
+ 'wpdd_view_reports',
+ 'wpdd_manage_orders'
+ );
+
+ foreach ($caps as $cap) {
+ $role->remove_cap($cap);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/includes/class-wpdd-shortcodes.php b/includes/class-wpdd-shortcodes.php
new file mode 100644
index 0000000..a6f559c
--- /dev/null
+++ b/includes/class-wpdd-shortcodes.php
@@ -0,0 +1,1167 @@
+ 3,
+ 'per_page' => 12,
+ 'category' => '',
+ 'orderby' => 'date',
+ 'order' => 'DESC',
+ 'show_filters' => 'yes'
+ ), $atts);
+
+ ob_start();
+
+ $paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
+
+ $args = array(
+ 'post_type' => 'wpdd_product',
+ 'posts_per_page' => $atts['per_page'],
+ 'paged' => $paged,
+ 'orderby' => $atts['orderby'],
+ 'order' => $atts['order'],
+ 'post_status' => 'publish'
+ );
+
+ if (!empty($atts['category'])) {
+ $args['tax_query'] = array(
+ array(
+ 'taxonomy' => 'wpdd_product_category',
+ 'field' => 'slug',
+ 'terms' => explode(',', $atts['category'])
+ )
+ );
+ }
+
+ // Handle search
+ $search_performed = false;
+ if (isset($_GET['wpdd_search']) && !empty($_GET['wpdd_search'])) {
+ $search_term = sanitize_text_field($_GET['wpdd_search']);
+ $search_performed = true;
+
+ // Bypass WordPress search and do direct query
+ global $wpdb;
+ $search_like = '%' . $wpdb->esc_like($search_term) . '%';
+
+ $matching_ids = $wpdb->get_col($wpdb->prepare(
+ "SELECT ID FROM {$wpdb->posts}
+ WHERE post_type = 'wpdd_product'
+ AND post_status = 'publish'
+ AND (post_title LIKE %s OR post_content LIKE %s)",
+ $search_like,
+ $search_like
+ ));
+
+ // Add debug output
+ if (current_user_can('manage_options')) {
+ echo "";
+ }
+
+ if (!empty($matching_ids)) {
+ // Found matching products
+ $args['post__in'] = $matching_ids;
+ // Only set orderby to post__in if no sort parameter is provided
+ if (!isset($_GET['wpdd_sort']) || empty($_GET['wpdd_sort'])) {
+ $args['orderby'] = 'post__in';
+ }
+ } else {
+ // No matches - force empty result
+ $args['post__in'] = array(-1); // Use -1 instead of 0
+ }
+ }
+
+ if (isset($_GET['wpdd_category']) && !empty($_GET['wpdd_category'])) {
+ $args['tax_query'] = array(
+ array(
+ 'taxonomy' => 'wpdd_product_category',
+ 'field' => 'slug',
+ 'terms' => sanitize_text_field($_GET['wpdd_category'])
+ )
+ );
+ }
+
+ // Handle creator filter
+ if (isset($_GET['wpdd_creator']) && !empty($_GET['wpdd_creator'])) {
+ $args['author'] = intval($_GET['wpdd_creator']);
+ }
+
+ if (isset($_GET['wpdd_sort']) && !empty($_GET['wpdd_sort'])) {
+ switch ($_GET['wpdd_sort']) {
+ case 'price_low':
+ $args['meta_key'] = '_wpdd_price';
+ $args['orderby'] = 'meta_value_num';
+ $args['order'] = 'ASC';
+ break;
+ case 'price_high':
+ $args['meta_key'] = '_wpdd_price';
+ $args['orderby'] = 'meta_value_num';
+ $args['order'] = 'DESC';
+ break;
+ case 'newest':
+ $args['orderby'] = 'date';
+ $args['order'] = 'DESC';
+ break;
+ case 'popular':
+ $args['meta_key'] = '_wpdd_sales_count';
+ $args['orderby'] = 'meta_value_num';
+ $args['order'] = 'DESC';
+ break;
+ }
+ }
+
+ $query = new WP_Query($args);
+
+ // Debug output for admins
+ if (current_user_can('manage_options') && isset($_GET['debug']) && $_GET['debug'] == '1') {
+ echo '';
+ echo '
Debug Info (Admin Only) ';
+ echo 'GET Parameters: ' . print_r($_GET, true) . ' ';
+ echo 'Query Args: ' . print_r($args, true) . ' ';
+ echo 'Found Posts: ' . $query->found_posts . ' ';
+ echo '';
+ }
+ ?>
+
+
+
+
+
+ $value) {
+ if (!in_array($key, array('wpdd_search', 'wpdd_category', 'wpdd_creator', 'wpdd_sort', 'paged'))) {
+ if (is_array($value)) {
+ foreach ($value as $sub_value) {
+ echo ' ';
+ }
+ } else {
+ echo ' ';
+ }
+ }
+ }
+ ?>
+
+
+
+
+
+ 'wpdd_product_category',
+ 'hide_empty' => true
+ ));
+
+ if (!is_wp_error($categories) && !empty($categories)) {
+ foreach ($categories as $category) {
+ printf(
+ '%s ',
+ esc_attr($category->slug),
+ selected(isset($_GET['wpdd_category']) && $_GET['wpdd_category'] == $category->slug, true, false),
+ esc_html($category->name)
+ );
+ }
+ }
+ ?>
+
+
+
+
+ >
+
+
+ >
+
+
+ >
+
+
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+ have_posts()) : ?>
+
+ have_posts()) : $query->the_post(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ post_author);
+ ?>
+
+
+
+
+
+
+
+
+
+ post_excerpt ?: $product->post_content, 20); ?>
+
+
+
+
+
+
+ $
+ $
+
+ $
+
+
+
+
+
+
+ ' . __('Invalid access key.', 'wp-digital-download') . '';
+ }
+
+ $orders = $wpdb->get_results($wpdb->prepare(
+ "SELECT o.*, p.post_title as product_name
+ FROM {$wpdb->prefix}wpdd_orders o
+ LEFT JOIN {$wpdb->posts} p ON o.product_id = p.ID
+ WHERE o.customer_email = %s
+ AND o.status = 'completed'
+ ORDER BY o.purchase_date DESC",
+ $email
+ ));
+
+ } elseif (is_user_logged_in()) {
+ $current_user = wp_get_current_user();
+
+ // Get orders by user ID or email (to include guest purchases before account creation)
+ $orders = $wpdb->get_results($wpdb->prepare(
+ "SELECT o.*, p.post_title as product_name
+ FROM {$wpdb->prefix}wpdd_orders o
+ LEFT JOIN {$wpdb->posts} p ON o.product_id = p.ID
+ WHERE (o.customer_id = %d OR o.customer_email = %s)
+ AND o.status = 'completed'
+ ORDER BY o.purchase_date DESC",
+ $current_user->ID,
+ $current_user->user_email
+ ));
+
+ } else {
+ return '' .
+ sprintf(
+ __('Please login to view your purchases.', 'wp-digital-download'),
+ wp_login_url(get_permalink())
+ ) . '
';
+ }
+
+ ob_start();
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ product_id, '_wpdd_download_limit', true);
+ $download_expiry = get_post_meta($order->product_id, '_wpdd_download_expiry', true);
+ $is_expired = false;
+
+ if ($download_expiry > 0) {
+ $expiry_date = date('Y-m-d H:i:s', strtotime($order->purchase_date . ' + ' . $download_expiry . ' days'));
+ $is_expired = current_time('mysql') > $expiry_date;
+ }
+
+ $can_download = !$is_expired && ($download_limit == 0 || $order->download_count < $download_limit);
+ ?>
+
+ order_number); ?>
+
+
+ product_name); ?>
+
+
+ purchase_date)); ?>
+ $amount, 2); ?>
+
+ 0) {
+ echo sprintf('%d / %d', $order->download_count, $download_limit);
+ } else {
+ echo $order->download_count;
+ }
+ ?>
+
+
+
+ $order->id);
+
+ // For guest access, include email and key
+ if (isset($_GET['customer_email']) && isset($_GET['key'])) {
+ $download_args['customer_email'] = $_GET['customer_email'];
+ $download_args['key'] = $_GET['key'];
+ }
+
+ $download_url = wp_nonce_url(
+ add_query_arg($download_args),
+ 'wpdd_download_' . $order->id
+ );
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ' . __('No product selected for checkout.', 'wp-digital-download') . '';
+ }
+
+ $product_id = intval($_GET['product_id']);
+ $product = get_post($product_id);
+
+ if (!$product || $product->post_type !== 'wpdd_product') {
+ return '' . __('Invalid product.', 'wp-digital-download') . '
';
+ }
+
+ $price = get_post_meta($product_id, '_wpdd_price', true);
+ $sale_price = get_post_meta($product_id, '_wpdd_sale_price', true);
+ $is_free = get_post_meta($product_id, '_wpdd_is_free', true);
+ $final_price = ($sale_price && $sale_price < $price) ? $sale_price : $price;
+
+ ob_start();
+ ?>
+
+
+
+
+
post_title); ?>
+
+
+
+
+
+
+
+ $
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ' . __('Invalid order.', 'wp-digital-download') . '';
+ }
+
+ global $wpdb;
+ $order_id = sanitize_text_field($_GET['order_id']);
+
+ // Get order details along with download token
+ $order = $wpdb->get_row($wpdb->prepare(
+ "SELECT o.*, p.post_title as product_name, dl.token as download_token
+ FROM {$wpdb->prefix}wpdd_orders o
+ LEFT JOIN {$wpdb->posts} p ON o.product_id = p.ID
+ LEFT JOIN {$wpdb->prefix}wpdd_download_links dl ON o.id = dl.order_id
+ WHERE o.order_number = %s",
+ $order_id
+ ));
+
+ if (!$order) {
+ return '' . __('Order not found.', 'wp-digital-download') . '
';
+ }
+
+ ob_start();
+ ?>
+
+
+
+
+
+
+
+
order_number); ?>
+
product_name); ?>
+
$amount, 2); ?>
+
+
+
+
+
+
+ download_token) {
+ $download_url = add_query_arg(array(
+ 'wpdd_download_token' => $order->download_token
+ ), home_url());
+ } else {
+ // For legacy orders without tokens, create one now
+ if (class_exists('WPDD_Download_Handler')) {
+ $token = WPDD_Download_Handler::ensure_download_token($order->id);
+ if ($token) {
+ $download_url = add_query_arg(array(
+ 'wpdd_download_token' => $token
+ ), home_url());
+ } else {
+ // Still fallback to old method if token creation fails
+ $download_args = array('wpdd_download' => $order->id);
+
+ // For guest users, include email and authentication key
+ if (!is_user_logged_in()) {
+ $download_args['customer_email'] = $order->customer_email;
+ $download_args['key'] = substr(md5($order->customer_email . AUTH_KEY), 0, 10);
+ }
+
+ $download_url = wp_nonce_url(
+ add_query_arg($download_args),
+ 'wpdd_download_' . $order->id
+ );
+ }
+ } else {
+ // Fallback if WPDD_Download_Handler class not found
+ $download_args = array('wpdd_download' => $order->id);
+
+ // For guest users, include email and authentication key
+ if (!is_user_logged_in()) {
+ $download_args['customer_email'] = $order->customer_email;
+ $download_args['key'] = substr(md5($order->customer_email . AUTH_KEY), 0, 10);
+ }
+
+ $download_url = wp_nonce_url(
+ add_query_arg($download_args),
+ 'wpdd_download_' . $order->id
+ );
+ }
+ }
+ ?>
+
+
+
+
+
+
+
+
+ purchase history.', 'wp-digital-download'),
+ get_permalink(get_option('wpdd_purchases_page_id'))
+ ); ?>
+
+
+
+
+ 0
+ ), $atts);
+
+ if (!$atts['id']) {
+ return '';
+ }
+
+ ob_start();
+ self::render_product_card($atts['id']);
+ return ob_get_clean();
+ }
+
+ public static function buy_button_shortcode($atts) {
+ $atts = shortcode_atts(array(
+ 'id' => get_the_ID(),
+ 'text' => __('Buy Now', 'wp-digital-download'),
+ 'class' => 'wpdd-buy-button'
+ ), $atts);
+
+ if (!$atts['id']) {
+ return '';
+ }
+
+ return self::render_buy_button($atts['id'], $atts['text'], $atts['class']);
+ }
+
+ public static function render_buy_button($product_id, $text = '', $class = '') {
+ global $wpdb;
+
+ // Check if user already owns this product
+ $user_owns_product = false;
+ $existing_order = null;
+
+ if (is_user_logged_in()) {
+ $current_user = wp_get_current_user();
+ $existing_order = $wpdb->get_row($wpdb->prepare(
+ "SELECT * FROM {$wpdb->prefix}wpdd_orders
+ WHERE product_id = %d
+ AND (customer_id = %d OR customer_email = %s)
+ AND status = 'completed'
+ ORDER BY purchase_date DESC
+ LIMIT 1",
+ $product_id,
+ $current_user->ID,
+ $current_user->user_email
+ ));
+
+ if ($existing_order) {
+ $user_owns_product = true;
+ }
+ }
+
+ // If user already owns the product, show download button
+ if ($user_owns_product) {
+ $download_url = wp_nonce_url(
+ add_query_arg(array('wpdd_download' => $existing_order->id)),
+ 'wpdd_download_' . $existing_order->id
+ );
+
+ return sprintf(
+ '%s ',
+ esc_url($download_url),
+ esc_attr($class ?: 'wpdd-download-button'),
+ esc_html(__('Download (Already Purchased)', 'wp-digital-download'))
+ );
+ }
+
+ // Regular buy button for products not owned
+ $price = get_post_meta($product_id, '_wpdd_price', true);
+ $sale_price = get_post_meta($product_id, '_wpdd_sale_price', true);
+ $is_free = get_post_meta($product_id, '_wpdd_is_free', true);
+
+ $actual_price = ($sale_price && $sale_price < $price) ? $sale_price : $price;
+
+ if (!$text) {
+ if ($is_free) {
+ $text = __('Download Free', 'wp-digital-download');
+ } else {
+ $text = sprintf(__('Buy Now - $%s', 'wp-digital-download'), number_format($actual_price, 2));
+ }
+ }
+
+ $checkout_url = add_query_arg(array(
+ 'wpdd_action' => 'add_to_cart',
+ 'product_id' => $product_id,
+ 'nonce' => wp_create_nonce('wpdd_add_to_cart')
+ ), get_permalink(get_option('wpdd_checkout_page_id')));
+
+ $button_html = sprintf(
+ '%s ',
+ esc_url($checkout_url),
+ esc_attr($class ?: 'wpdd-buy-button'),
+ esc_attr($product_id),
+ esc_attr($actual_price),
+ esc_html($text)
+ );
+
+ return $button_html;
+ }
+
+ public static function add_buy_button_to_product($content) {
+ if (!is_singular('wpdd_product')) {
+ return $content;
+ }
+
+ global $post;
+
+ // Get product details
+ $price = get_post_meta($post->ID, '_wpdd_price', true);
+ $sale_price = get_post_meta($post->ID, '_wpdd_sale_price', true);
+ $is_free = get_post_meta($post->ID, '_wpdd_is_free', true);
+ $files = get_post_meta($post->ID, '_wpdd_files', true);
+
+ // Build product info box
+ $product_info = '';
+
+ // Left section with meta
+ $product_info .= '
'; // close meta
+
+ // Right section with buy button
+ $product_info .= '
';
+
+ // Buy button (price is included in button text)
+ $product_info .= '
';
+ $product_info .= self::render_buy_button($post->ID);
+ $product_info .= '
';
+
+ $product_info .= '
'; // close purchase section
+ $product_info .= '
'; // close product info box
+
+ // Add CSS for the product info box
+ $product_info .= '';
+
+ // Add the product info box before the content
+ return $product_info . $content;
+ }
+
+ public static function creator_dashboard_shortcode($atts) {
+ if (!is_user_logged_in()) {
+ return '' . __('Please log in to view your creator dashboard.', 'wp-digital-download') . '
';
+ }
+
+ $user_id = get_current_user_id();
+
+ // Check if user is a creator
+ if (!WPDD_Creator::is_creator($user_id)) {
+ return '' . __('This dashboard is only available for creators.', 'wp-digital-download') . '
';
+ }
+
+ global $wpdb;
+
+ // Get creator stats
+ $total_earnings = WPDD_Creator::get_creator_total_earnings($user_id);
+ $net_earnings = WPDD_Creator::get_creator_net_earnings($user_id);
+ $current_balance = WPDD_Creator::get_creator_balance($user_id);
+ $commission_rate = floatval(get_option('wpdd_commission_rate', 0));
+ $currency = get_option('wpdd_currency', 'USD');
+ $paypal_email = get_user_meta($user_id, 'wpdd_paypal_email', true);
+
+ // Get recent sales
+ $recent_sales = $wpdb->get_results($wpdb->prepare(
+ "SELECT o.*, p.post_title as product_name,
+ (o.total * %f / 100) as platform_fee,
+ (o.total * (100 - %f) / 100) as creator_earning
+ FROM {$wpdb->prefix}wpdd_orders o
+ INNER JOIN {$wpdb->posts} p ON o.product_id = p.ID
+ WHERE p.post_author = %d
+ AND o.status = 'completed'
+ ORDER BY o.purchase_date DESC
+ LIMIT 20",
+ $commission_rate,
+ $commission_rate,
+ $user_id
+ ));
+
+ // Get payout history
+ $payouts = $wpdb->get_results($wpdb->prepare(
+ "SELECT * FROM {$wpdb->prefix}wpdd_payouts
+ WHERE creator_id = %d
+ ORDER BY created_at DESC
+ LIMIT 10",
+ $user_id
+ ));
+
+ ob_start();
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0) {
+ echo '
' . sprintf(__('Automatic payouts are triggered when balance reaches %s', 'wp-digital-download'), wpdd_format_price($threshold, $currency)) . '
';
+ }
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ purchase_date))); ?>
+ product_name); ?>
+ customer_name); ?>
+ total, $currency); ?>
+ platform_fee, $currency); ?>
+ creator_earning, $currency); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ created_at))); ?>
+ amount, $payout->currency); ?>
+
+
+ status)); ?>
+
+
+ transaction_id ?: '-'); ?>
+ payout_method)); ?>
+
+
+
+
+
+
+
+
+
+ product_id;
+ $watermark_text = get_post_meta($product_id, '_wpdd_watermark_text', true);
+
+ if (empty($watermark_text)) {
+ $watermark_text = '{customer_email}';
+ }
+
+ $watermark_text = self::parse_watermark_placeholders($watermark_text, $order);
+
+ switch ($file_extension) {
+ case 'jpg':
+ case 'jpeg':
+ case 'png':
+ case 'gif':
+ return self::watermark_image($file_path, $watermark_text);
+ case 'pdf':
+ return self::watermark_pdf($file_path, $watermark_text);
+ default:
+ return false;
+ }
+ }
+
+ private static function parse_watermark_placeholders($text, $order) {
+ $customer = get_userdata($order->customer_id);
+
+ $replacements = array(
+ '{customer_name}' => $order->customer_name,
+ '{customer_email}' => $order->customer_email,
+ '{order_id}' => $order->order_number,
+ '{date}' => date_i18n(get_option('date_format')),
+ '{site_name}' => get_bloginfo('name')
+ );
+
+ return str_replace(array_keys($replacements), array_values($replacements), $text);
+ }
+
+ private static function watermark_image($file_path, $watermark_text) {
+ if (!function_exists('imagecreatefrompng')) {
+ return false;
+ }
+
+ $file_info = pathinfo($file_path);
+ $extension = strtolower($file_info['extension']);
+
+ switch ($extension) {
+ case 'jpg':
+ case 'jpeg':
+ $image = imagecreatefromjpeg($file_path);
+ break;
+ case 'png':
+ $image = imagecreatefrompng($file_path);
+ break;
+ case 'gif':
+ $image = imagecreatefromgif($file_path);
+ break;
+ default:
+ return false;
+ }
+
+ if (!$image) {
+ return false;
+ }
+
+ $width = imagesx($image);
+ $height = imagesy($image);
+
+ $font_size = max(10, min(30, $width / 40));
+ $font_file = WPDD_PLUGIN_PATH . 'assets/fonts/arial.ttf';
+
+ if (!file_exists($font_file)) {
+ $font = 5;
+ $text_width = imagefontwidth($font) * strlen($watermark_text);
+ $text_height = imagefontheight($font);
+ } else {
+ $bbox = imagettfbbox($font_size, 0, $font_file, $watermark_text);
+ $text_width = $bbox[2] - $bbox[0];
+ $text_height = $bbox[1] - $bbox[7];
+ }
+
+ $x = ($width - $text_width) / 2;
+ $y = $height - 50;
+
+ $text_color = imagecolorallocatealpha($image, 255, 255, 255, 30);
+ $shadow_color = imagecolorallocatealpha($image, 0, 0, 0, 50);
+
+ if (file_exists($font_file)) {
+ imagettftext($image, $font_size, 0, $x + 2, $y + 2, $shadow_color, $font_file, $watermark_text);
+ imagettftext($image, $font_size, 0, $x, $y, $text_color, $font_file, $watermark_text);
+ } else {
+ imagestring($image, $font, $x + 2, $y + 2, $watermark_text, $shadow_color);
+ imagestring($image, $font, $x, $y, $watermark_text, $text_color);
+ }
+
+ $temp_dir = get_temp_dir();
+ $temp_file = $temp_dir . 'wpdd_watermark_' . uniqid() . '.' . $extension;
+
+ switch ($extension) {
+ case 'jpg':
+ case 'jpeg':
+ imagejpeg($image, $temp_file, 90);
+ break;
+ case 'png':
+ imagepng($image, $temp_file, 9);
+ break;
+ case 'gif':
+ imagegif($image, $temp_file);
+ break;
+ }
+
+ imagedestroy($image);
+
+ return $temp_file;
+ }
+
+ private static function watermark_pdf($file_path, $watermark_text) {
+ if (!class_exists('TCPDF') && !class_exists('FPDF')) {
+ return self::watermark_pdf_basic($file_path, $watermark_text);
+ }
+
+ if (class_exists('TCPDF')) {
+ return self::watermark_pdf_tcpdf($file_path, $watermark_text);
+ }
+
+ return false;
+ }
+
+ private static function watermark_pdf_basic($file_path, $watermark_text) {
+ $temp_dir = get_temp_dir();
+ $temp_file = $temp_dir . 'wpdd_watermark_' . uniqid() . '.pdf';
+
+ if (copy($file_path, $temp_file)) {
+ return $temp_file;
+ }
+
+ return false;
+ }
+
+ private static function watermark_pdf_tcpdf($file_path, $watermark_text) {
+ require_once ABSPATH . 'wp-admin/includes/class-pclzip.php';
+
+ $pdf = new TCPDF();
+ $pdf->SetProtection(array('print'), '', null, 0, null);
+
+ $pagecount = $pdf->setSourceFile($file_path);
+
+ for ($i = 1; $i <= $pagecount; $i++) {
+ $tplidx = $pdf->importPage($i);
+ $pdf->AddPage();
+ $pdf->useTemplate($tplidx);
+
+ $pdf->SetFont('helvetica', '', 12);
+ $pdf->SetTextColor(200, 200, 200);
+ $pdf->SetAlpha(0.5);
+
+ $pdf->StartTransform();
+ $pdf->Rotate(45, $pdf->getPageWidth() / 2, $pdf->getPageHeight() / 2);
+ $pdf->Text(
+ $pdf->getPageWidth() / 2 - 50,
+ $pdf->getPageHeight() / 2,
+ $watermark_text
+ );
+ $pdf->StopTransform();
+
+ $pdf->SetAlpha(1);
+ }
+
+ $temp_dir = get_temp_dir();
+ $temp_file = $temp_dir . 'wpdd_watermark_' . uniqid() . '.pdf';
+
+ $pdf->Output($temp_file, 'F');
+
+ return $temp_file;
+ }
+
+ public static function add_text_watermark($content, $watermark_text) {
+ $watermark_html = sprintf(
+ '%s
',
+ esc_html($watermark_text)
+ );
+
+ return $watermark_html . $content;
+ }
+
+ public static function get_watermark_settings() {
+ return array(
+ 'enabled' => get_option('wpdd_watermark_enabled', false),
+ 'text' => get_option('wpdd_watermark_text', '{customer_email}'),
+ 'position' => get_option('wpdd_watermark_position', 'center'),
+ 'opacity' => get_option('wpdd_watermark_opacity', 30),
+ 'font_size' => get_option('wpdd_watermark_font_size', 'auto'),
+ 'color' => get_option('wpdd_watermark_color', '#ffffff')
+ );
+ }
+
+ public static function preview_watermark($file_type = 'image') {
+ $settings = self::get_watermark_settings();
+ $preview_text = str_replace(
+ array('{customer_name}', '{customer_email}', '{order_id}', '{date}', '{site_name}'),
+ array('John Doe', 'john@example.com', 'WPDD-123456', date_i18n(get_option('date_format')), get_bloginfo('name')),
+ $settings['text']
+ );
+
+ if ($file_type === 'image') {
+ $width = 600;
+ $height = 400;
+
+ $image = imagecreatetruecolor($width, $height);
+ $bg_color = imagecolorallocate($image, 240, 240, 240);
+ imagefill($image, 0, 0, $bg_color);
+
+ $text_color = imagecolorallocatealpha($image, 100, 100, 100, 50);
+
+ $font_size = 20;
+ $x = ($width - (strlen($preview_text) * 10)) / 2;
+ $y = $height / 2;
+
+ imagestring($image, 5, $x, $y, $preview_text, $text_color);
+
+ header('Content-Type: image/png');
+ imagepng($image);
+ imagedestroy($image);
+ }
+ }
+}
\ No newline at end of file
diff --git a/node_modules/.bin/playwright b/node_modules/.bin/playwright
new file mode 120000
index 0000000..c30d07f
--- /dev/null
+++ b/node_modules/.bin/playwright
@@ -0,0 +1 @@
+../@playwright/test/cli.js
\ No newline at end of file
diff --git a/node_modules/.bin/playwright-core b/node_modules/.bin/playwright-core
new file mode 120000
index 0000000..08d6c28
--- /dev/null
+++ b/node_modules/.bin/playwright-core
@@ -0,0 +1 @@
+../playwright-core/cli.js
\ No newline at end of file
diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json
new file mode 100644
index 0000000..bda2b34
--- /dev/null
+++ b/node_modules/.package-lock.json
@@ -0,0 +1,72 @@
+{
+ "name": "wp-digital-download-tests",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "node_modules/@playwright/test": {
+ "version": "1.55.0",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.55.0.tgz",
+ "integrity": "sha512-04IXzPwHrW69XusN/SIdDdKZBzMfOT9UNT/YiJit/xpy2VuAoB8NHc8Aplb96zsWDddLnbkPL3TsmrS04ZU2xQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "playwright": "1.55.0"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "ideallyInert": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/playwright": {
+ "version": "1.55.0",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.55.0.tgz",
+ "integrity": "sha512-sdCWStblvV1YU909Xqx0DhOjPZE4/5lJsIS84IfN9dAZfcl/CIZ5O8l3o0j7hPMjDvqoTF8ZUcc+i/GL5erstA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "playwright-core": "1.55.0"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.2"
+ }
+ },
+ "node_modules/playwright-core": {
+ "version": "1.55.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.0.tgz",
+ "integrity": "sha512-GvZs4vU3U5ro2nZpeiwyb0zuFaqb9sUiAJuyrWpcGouD8y9/HLgGbNRjIph7zU9D3hnPaisMl9zG9CgFi/biIg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "playwright-core": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ }
+ }
+}
diff --git a/node_modules/@playwright/test/LICENSE b/node_modules/@playwright/test/LICENSE
new file mode 100644
index 0000000..df11237
--- /dev/null
+++ b/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/node_modules/@playwright/test/NOTICE b/node_modules/@playwright/test/NOTICE
new file mode 100644
index 0000000..814ec16
--- /dev/null
+++ b/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/node_modules/@playwright/test/README.md b/node_modules/@playwright/test/README.md
new file mode 100644
index 0000000..b3441ab
--- /dev/null
+++ b/node_modules/@playwright/test/README.md
@@ -0,0 +1,168 @@
+# 🎭 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 Testing and Automation. It allows testing [Chromium](https://www.chromium.org/Home), [Firefox](https://www.mozilla.org/en-US/firefox/new/) and [WebKit](https://webkit.org/) with a single API. Playwright is built to enable cross-browser web automation that is **ever-green**, **capable**, **reliable** and **fast**.
+
+| | Linux | macOS | Windows |
+| :--- | :---: | :---: | :---: |
+| Chromium 140.0.7339.16 | :white_check_mark: | :white_check_mark: | :white_check_mark: |
+| WebKit 26.0 | :white_check_mark: | :white_check_mark: | :white_check_mark: |
+| Firefox 141.0 | :white_check_mark: | :white_check_mark: | :white_check_mark: |
+
+Headless execution is supported for all browsers on all platforms. Check out [system requirements](https://playwright.dev/docs/intro#system-requirements) for details.
+
+Looking for Playwright for [Python](https://playwright.dev/python/docs/intro), [.NET](https://playwright.dev/dotnet/docs/intro), or [Java](https://playwright.dev/java/docs/intro)?
+
+## Installation
+
+Playwright has its own test runner for end-to-end tests, we call it Playwright Test.
+
+### Using init command
+
+The easiest way to get started with Playwright Test is to run the init command.
+
+```Shell
+# Run from your project's root directory
+npm init playwright@latest
+# Or create a new project
+npm init playwright@latest new-project
+```
+
+This will create a configuration file, optionally add examples, a GitHub Action workflow and a first test example.spec.ts. You can now jump directly to writing assertions section.
+
+### Manually
+
+Add dependency and install browsers.
+
+```Shell
+npm i -D @playwright/test
+# install supported browsers
+npx playwright install
+```
+
+You can optionally install only selected browsers, see [install browsers](https://playwright.dev/docs/cli#install-browsers) for more details. Or you can install no browsers at all and use existing [browser channels](https://playwright.dev/docs/browsers).
+
+* [Getting started](https://playwright.dev/docs/intro)
+* [API reference](https://playwright.dev/docs/api/class-playwright)
+
+## Capabilities
+
+### Resilient • No flaky tests
+
+**Auto-wait**. Playwright waits for elements to be actionable prior to performing actions. It also has a rich set of introspection events. The combination of the two eliminates the need for artificial timeouts - a primary cause of flaky tests.
+
+**Web-first assertions**. Playwright assertions are created specifically for the dynamic web. Checks are automatically retried until the necessary conditions are met.
+
+**Tracing**. Configure test retry strategy, capture execution trace, videos and screenshots to eliminate flakes.
+
+### No trade-offs • No limits
+
+Browsers run web content belonging to different origins in different processes. Playwright is aligned with the architecture of the modern browsers and runs tests out-of-process. This makes Playwright free of the typical in-process test runner limitations.
+
+**Multiple everything**. Test scenarios that span multiple tabs, multiple origins and multiple users. Create scenarios with different contexts for different users and run them against your server, all in one test.
+
+**Trusted events**. Hover elements, interact with dynamic controls and produce trusted events. Playwright uses real browser input pipeline indistinguishable from the real user.
+
+Test frames, pierce Shadow DOM. Playwright selectors pierce shadow DOM and allow entering frames seamlessly.
+
+### Full isolation • Fast execution
+
+**Browser contexts**. Playwright creates a browser context for each test. Browser context is equivalent to a brand new browser profile. This delivers full test isolation with zero overhead. Creating a new browser context only takes a handful of milliseconds.
+
+**Log in once**. Save the authentication state of the context and reuse it in all the tests. This bypasses repetitive log-in operations in each test, yet delivers full isolation of independent tests.
+
+### Powerful Tooling
+
+**[Codegen](https://playwright.dev/docs/codegen)**. Generate tests by recording your actions. Save them into any language.
+
+**[Playwright inspector](https://playwright.dev/docs/inspector)**. Inspect page, generate selectors, step through the test execution, see click points and explore execution logs.
+
+**[Trace Viewer](https://playwright.dev/docs/trace-viewer)**. Capture all the information to investigate the test failure. Playwright trace contains test execution screencast, live DOM snapshots, action explorer, test source and many more.
+
+Looking for Playwright for [TypeScript](https://playwright.dev/docs/intro), [JavaScript](https://playwright.dev/docs/intro), [Python](https://playwright.dev/python/docs/intro), [.NET](https://playwright.dev/dotnet/docs/intro), or [Java](https://playwright.dev/java/docs/intro)?
+
+## Examples
+
+To learn how to run these Playwright Test examples, check out our [getting started docs](https://playwright.dev/docs/intro).
+
+#### Page screenshot
+
+This code snippet navigates to Playwright homepage and saves a screenshot.
+
+```TypeScript
+import { test } from '@playwright/test';
+
+test('Page Screenshot', async ({ page }) => {
+ await page.goto('https://playwright.dev/');
+ await page.screenshot({ path: `example.png` });
+});
+```
+
+#### Mobile and geolocation
+
+This snippet emulates Mobile Safari on a device at given geolocation, navigates to maps.google.com, performs the action and takes a screenshot.
+
+```TypeScript
+import { test, devices } from '@playwright/test';
+
+test.use({
+ ...devices['iPhone 13 Pro'],
+ locale: 'en-US',
+ geolocation: { longitude: 12.492507, latitude: 41.889938 },
+ permissions: ['geolocation'],
+})
+
+test('Mobile and geolocation', async ({ page }) => {
+ await page.goto('https://maps.google.com');
+ await page.getByText('Your location').click();
+ await page.waitForRequest(/.*preview\/pwa/);
+ await page.screenshot({ path: 'colosseum-iphone.png' });
+});
+```
+
+#### Evaluate in browser context
+
+This code snippet navigates to example.com, and executes a script in the page context.
+
+```TypeScript
+import { test } from '@playwright/test';
+
+test('Evaluate in browser context', async ({ page }) => {
+ await page.goto('https://www.example.com/');
+ const dimensions = await page.evaluate(() => {
+ return {
+ width: document.documentElement.clientWidth,
+ height: document.documentElement.clientHeight,
+ deviceScaleFactor: window.devicePixelRatio
+ }
+ });
+ console.log(dimensions);
+});
+```
+
+#### Intercept network requests
+
+This code snippet sets up request routing for a page to log all network requests.
+
+```TypeScript
+import { test } from '@playwright/test';
+
+test('Intercept network requests', async ({ page }) => {
+ // Log and continue all network requests
+ await page.route('**', route => {
+ console.log(route.request().url());
+ route.continue();
+ });
+ await page.goto('http://todomvc.com');
+});
+```
+
+## Resources
+
+* [Documentation](https://playwright.dev)
+* [API reference](https://playwright.dev/docs/api/class-playwright/)
+* [Contribution guide](CONTRIBUTING.md)
+* [Changelog](https://github.com/microsoft/playwright/releases)
diff --git a/node_modules/@playwright/test/cli.js b/node_modules/@playwright/test/cli.js
new file mode 100755
index 0000000..e42facb
--- /dev/null
+++ b/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/node_modules/@playwright/test/index.d.ts b/node_modules/@playwright/test/index.d.ts
new file mode 100644
index 0000000..8d99c91
--- /dev/null
+++ b/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/node_modules/@playwright/test/index.js b/node_modules/@playwright/test/index.js
new file mode 100644
index 0000000..8536f06
--- /dev/null
+++ b/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/node_modules/@playwright/test/index.mjs b/node_modules/@playwright/test/index.mjs
new file mode 100644
index 0000000..8d99c91
--- /dev/null
+++ b/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/node_modules/@playwright/test/package.json b/node_modules/@playwright/test/package.json
new file mode 100644
index 0000000..14bef61
--- /dev/null
+++ b/node_modules/@playwright/test/package.json
@@ -0,0 +1,35 @@
+{
+ "name": "@playwright/test",
+ "version": "1.55.0",
+ "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.55.0"
+ }
+}
diff --git a/node_modules/@playwright/test/reporter.d.ts b/node_modules/@playwright/test/reporter.d.ts
new file mode 100644
index 0000000..806d13f
--- /dev/null
+++ b/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/node_modules/@playwright/test/reporter.js b/node_modules/@playwright/test/reporter.js
new file mode 100644
index 0000000..485e880
--- /dev/null
+++ b/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/node_modules/@playwright/test/reporter.mjs b/node_modules/@playwright/test/reporter.mjs
new file mode 100644
index 0000000..485e880
--- /dev/null
+++ b/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/node_modules/playwright-core/LICENSE b/node_modules/playwright-core/LICENSE
new file mode 100644
index 0000000..df11237
--- /dev/null
+++ b/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/node_modules/playwright-core/NOTICE b/node_modules/playwright-core/NOTICE
new file mode 100644
index 0000000..814ec16
--- /dev/null
+++ b/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/node_modules/playwright-core/README.md b/node_modules/playwright-core/README.md
new file mode 100644
index 0000000..422b373
--- /dev/null
+++ b/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/node_modules/playwright-core/ThirdPartyNotices.txt b/node_modules/playwright-core/ThirdPartyNotices.txt
new file mode 100644
index 0000000..57cf609
--- /dev/null
+++ b/node_modules/playwright-core/ThirdPartyNotices.txt
@@ -0,0 +1,1502 @@
+microsoft/playwright-core
+
+THIRD-PARTY SOFTWARE NOTICES AND INFORMATION
+
+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.
+
+- agent-base@6.0.2 (https://github.com/TooTallNate/node-agent-base)
+- balanced-match@1.0.2 (https://github.com/juliangruber/balanced-match)
+- brace-expansion@1.1.12 (https://github.com/juliangruber/brace-expansion)
+- buffer-crc32@0.2.13 (https://github.com/brianloveswords/buffer-crc32)
+- codemirror@5.65.18 (https://github.com/codemirror/CodeMirror)
+- colors@1.4.0 (https://github.com/Marak/colors.js)
+- commander@13.1.0 (https://github.com/tj/commander.js)
+- concat-map@0.0.1 (https://github.com/substack/node-concat-map)
+- debug@4.3.4 (https://github.com/debug-js/debug)
+- debug@4.4.0 (https://github.com/debug-js/debug)
+- define-lazy-prop@2.0.0 (https://github.com/sindresorhus/define-lazy-prop)
+- diff@7.0.0 (https://github.com/kpdecker/jsdiff)
+- dotenv@16.4.5 (https://github.com/motdotla/dotenv)
+- end-of-stream@1.4.4 (https://github.com/mafintosh/end-of-stream)
+- get-stream@5.2.0 (https://github.com/sindresorhus/get-stream)
+- graceful-fs@4.2.10 (https://github.com/isaacs/node-graceful-fs)
+- https-proxy-agent@5.0.1 (https://github.com/TooTallNate/node-https-proxy-agent)
+- ip-address@9.0.5 (https://github.com/beaugunderson/ip-address)
+- is-docker@2.2.1 (https://github.com/sindresorhus/is-docker)
+- is-wsl@2.2.0 (https://github.com/sindresorhus/is-wsl)
+- jpeg-js@0.4.4 (https://github.com/eugeneware/jpeg-js)
+- jsbn@1.1.0 (https://github.com/andyperlitch/jsbn)
+- mime@3.0.0 (https://github.com/broofa/mime)
+- minimatch@3.1.2 (https://github.com/isaacs/minimatch)
+- ms@2.1.2 (https://github.com/zeit/ms)
+- ms@2.1.3 (https://github.com/vercel/ms)
+- once@1.4.0 (https://github.com/isaacs/once)
+- open@8.4.0 (https://github.com/sindresorhus/open)
+- pend@1.2.0 (https://github.com/andrewrk/node-pend)
+- pngjs@6.0.0 (https://github.com/lukeapage/pngjs)
+- progress@2.0.3 (https://github.com/visionmedia/node-progress)
+- proxy-from-env@1.1.0 (https://github.com/Rob--W/proxy-from-env)
+- pump@3.0.2 (https://github.com/mafintosh/pump)
+- retry@0.12.0 (https://github.com/tim-kos/node-retry)
+- signal-exit@3.0.7 (https://github.com/tapjs/signal-exit)
+- smart-buffer@4.2.0 (https://github.com/JoshGlazebrook/smart-buffer)
+- socks-proxy-agent@6.1.1 (https://github.com/TooTallNate/node-socks-proxy-agent)
+- socks@2.8.3 (https://github.com/JoshGlazebrook/socks)
+- sprintf-js@1.1.3 (https://github.com/alexei/sprintf.js)
+- wrappy@1.0.2 (https://github.com/npm/wrappy)
+- ws@8.17.1 (https://github.com/websockets/ws)
+- yaml@2.6.0 (https://github.com/eemeli/yaml)
+- yauzl@3.2.0 (https://github.com/thejoshwolfe/yauzl)
+- yazl@2.5.1 (https://github.com/thejoshwolfe/yazl)
+
+%% agent-base@6.0.2 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+agent-base
+==========
+### Turn a function into an [`http.Agent`][http.Agent] instance
+[](https://github.com/TooTallNate/node-agent-base/actions?workflow=Node+CI)
+
+This module provides an `http.Agent` generator. That is, you pass it an async
+callback function, and it returns a new `http.Agent` instance that will invoke the
+given callback function when sending outbound HTTP requests.
+
+#### Some subclasses:
+
+Here's some more interesting uses of `agent-base`.
+Send a pull request to list yours!
+
+ * [`http-proxy-agent`][http-proxy-agent]: An HTTP(s) proxy `http.Agent` implementation for HTTP endpoints
+ * [`https-proxy-agent`][https-proxy-agent]: An HTTP(s) proxy `http.Agent` implementation for HTTPS endpoints
+ * [`pac-proxy-agent`][pac-proxy-agent]: A PAC file proxy `http.Agent` implementation for HTTP and HTTPS
+ * [`socks-proxy-agent`][socks-proxy-agent]: A SOCKS proxy `http.Agent` implementation for HTTP and HTTPS
+
+
+Installation
+------------
+
+Install with `npm`:
+
+``` bash
+$ npm install agent-base
+```
+
+
+Example
+-------
+
+Here's a minimal example that creates a new `net.Socket` connection to the server
+for every HTTP request (i.e. the equivalent of `agent: false` option):
+
+```js
+var net = require('net');
+var tls = require('tls');
+var url = require('url');
+var http = require('http');
+var agent = require('agent-base');
+
+var endpoint = 'http://nodejs.org/api/';
+var parsed = url.parse(endpoint);
+
+// This is the important part!
+parsed.agent = agent(function (req, opts) {
+ var socket;
+ // `secureEndpoint` is true when using the https module
+ if (opts.secureEndpoint) {
+ socket = tls.connect(opts);
+ } else {
+ socket = net.connect(opts);
+ }
+ return socket;
+});
+
+// Everything else works just like normal...
+http.get(parsed, function (res) {
+ console.log('"response" event!', res.headers);
+ res.pipe(process.stdout);
+});
+```
+
+Returning a Promise or using an `async` function is also supported:
+
+```js
+agent(async function (req, opts) {
+ await sleep(1000);
+ // etc…
+});
+```
+
+Return another `http.Agent` instance to "pass through" the responsibility
+for that HTTP request to that agent:
+
+```js
+agent(function (req, opts) {
+ return opts.secureEndpoint ? https.globalAgent : http.globalAgent;
+});
+```
+
+
+API
+---
+
+## Agent(Function callback[, Object options]) → [http.Agent][]
+
+Creates a base `http.Agent` that will execute the callback function `callback`
+for every HTTP request that it is used as the `agent` for. The callback function
+is responsible for creating a `stream.Duplex` instance of some kind that will be
+used as the underlying socket in the HTTP request.
+
+The `options` object accepts the following properties:
+
+ * `timeout` - Number - Timeout for the `callback()` function in milliseconds. Defaults to Infinity (optional).
+
+The callback function should have the following signature:
+
+### callback(http.ClientRequest req, Object options, Function cb) → undefined
+
+The ClientRequest `req` can be accessed to read request headers and
+and the path, etc. The `options` object contains the options passed
+to the `http.request()`/`https.request()` function call, and is formatted
+to be directly passed to `net.connect()`/`tls.connect()`, or however
+else you want a Socket to be created. Pass the created socket to
+the callback function `cb` once created, and the HTTP request will
+continue to proceed.
+
+If the `https` module is used to invoke the HTTP request, then the
+`secureEndpoint` property on `options` _will be set to `true`_.
+
+
+License
+-------
+
+(The MIT License)
+
+Copyright (c) 2013 Nathan Rajlich <nathan@tootallnate.net>
+
+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.
+
+[http-proxy-agent]: https://github.com/TooTallNate/node-http-proxy-agent
+[https-proxy-agent]: https://github.com/TooTallNate/node-https-proxy-agent
+[pac-proxy-agent]: https://github.com/TooTallNate/node-pac-proxy-agent
+[socks-proxy-agent]: https://github.com/TooTallNate/node-socks-proxy-agent
+[http.Agent]: https://nodejs.org/api/http.html#http_class_http_agent
+=========================================
+END OF agent-base@6.0.2 AND INFORMATION
+
+%% balanced-match@1.0.2 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+(MIT)
+
+Copyright (c) 2013 Julian Gruber <julian@juliangruber.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 balanced-match@1.0.2 AND INFORMATION
+
+%% brace-expansion@1.1.12 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+MIT License
+
+Copyright (c) 2013 Julian Gruber
+
+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 brace-expansion@1.1.12 AND INFORMATION
+
+%% buffer-crc32@0.2.13 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+The MIT License
+
+Copyright (c) 2013 Brian J. Brennan
+
+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 buffer-crc32@0.2.13 AND INFORMATION
+
+%% codemirror@5.65.18 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+MIT License
+
+Copyright (C) 2017 by Marijn Haverbeke and others
+
+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 codemirror@5.65.18 AND INFORMATION
+
+%% colors@1.4.0 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+MIT License
+
+Original Library
+ - Copyright (c) Marak Squires
+
+Additional Functionality
+ - Copyright (c) Sindre Sorhus (sindresorhus.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 colors@1.4.0 AND INFORMATION
+
+%% commander@13.1.0 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+(The MIT License)
+
+Copyright (c) 2011 TJ Holowaychuk
+
+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 commander@13.1.0 AND INFORMATION
+
+%% concat-map@0.0.1 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+This software is released under the MIT license:
+
+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 concat-map@0.0.1 AND INFORMATION
+
+%% debug@4.3.4 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+(The MIT License)
+
+Copyright (c) 2014-2017 TJ Holowaychuk
+Copyright (c) 2018-2021 Josh Junon
+
+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 debug@4.3.4 AND INFORMATION
+
+%% debug@4.4.0 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+(The MIT License)
+
+Copyright (c) 2014-2017 TJ Holowaychuk
+Copyright (c) 2018-2021 Josh Junon
+
+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 debug@4.4.0 AND INFORMATION
+
+%% define-lazy-prop@2.0.0 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+MIT License
+
+Copyright (c) Sindre Sorhus (sindresorhus.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 define-lazy-prop@2.0.0 AND INFORMATION
+
+%% diff@7.0.0 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+BSD 3-Clause License
+
+Copyright (c) 2009-2015, Kevin Decker
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+=========================================
+END OF diff@7.0.0 AND INFORMATION
+
+%% dotenv@16.4.5 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+Copyright (c) 2015, Scott Motte
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+=========================================
+END OF dotenv@16.4.5 AND INFORMATION
+
+%% end-of-stream@1.4.4 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+The MIT License (MIT)
+
+Copyright (c) 2014 Mathias Buus
+
+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 end-of-stream@1.4.4 AND INFORMATION
+
+%% get-stream@5.2.0 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+MIT License
+
+Copyright (c) Sindre Sorhus (https://sindresorhus.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 get-stream@5.2.0 AND INFORMATION
+
+%% graceful-fs@4.2.10 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+The ISC License
+
+Copyright (c) 2011-2022 Isaac Z. Schlueter, Ben Noordhuis, and Contributors
+
+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 graceful-fs@4.2.10 AND INFORMATION
+
+%% https-proxy-agent@5.0.1 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+https-proxy-agent
+================
+### An HTTP(s) proxy `http.Agent` implementation for HTTPS
+[](https://github.com/TooTallNate/node-https-proxy-agent/actions?workflow=Node+CI)
+
+This module provides an `http.Agent` implementation that connects to a specified
+HTTP or HTTPS proxy server, and can be used with the built-in `https` module.
+
+Specifically, this `Agent` implementation connects to an intermediary "proxy"
+server and issues the [CONNECT HTTP method][CONNECT], which tells the proxy to
+open a direct TCP connection to the destination server.
+
+Since this agent implements the CONNECT HTTP method, it also works with other
+protocols that use this method when connecting over proxies (i.e. WebSockets).
+See the "Examples" section below for more.
+
+
+Installation
+------------
+
+Install with `npm`:
+
+``` bash
+$ npm install https-proxy-agent
+```
+
+
+Examples
+--------
+
+#### `https` module example
+
+``` js
+var url = require('url');
+var https = require('https');
+var HttpsProxyAgent = require('https-proxy-agent');
+
+// HTTP/HTTPS proxy to connect to
+var proxy = process.env.http_proxy || 'http://168.63.76.32:3128';
+console.log('using proxy server %j', proxy);
+
+// HTTPS endpoint for the proxy to connect to
+var endpoint = process.argv[2] || 'https://graph.facebook.com/tootallnate';
+console.log('attempting to GET %j', endpoint);
+var options = url.parse(endpoint);
+
+// create an instance of the `HttpsProxyAgent` class with the proxy server information
+var agent = new HttpsProxyAgent(proxy);
+options.agent = agent;
+
+https.get(options, function (res) {
+ console.log('"response" event!', res.headers);
+ res.pipe(process.stdout);
+});
+```
+
+#### `ws` WebSocket connection example
+
+``` js
+var url = require('url');
+var WebSocket = require('ws');
+var HttpsProxyAgent = require('https-proxy-agent');
+
+// HTTP/HTTPS proxy to connect to
+var proxy = process.env.http_proxy || 'http://168.63.76.32:3128';
+console.log('using proxy server %j', proxy);
+
+// WebSocket endpoint for the proxy to connect to
+var endpoint = process.argv[2] || 'ws://echo.websocket.org';
+var parsed = url.parse(endpoint);
+console.log('attempting to connect to WebSocket %j', endpoint);
+
+// create an instance of the `HttpsProxyAgent` class with the proxy server information
+var options = url.parse(proxy);
+
+var agent = new HttpsProxyAgent(options);
+
+// finally, initiate the WebSocket connection
+var socket = new WebSocket(endpoint, { agent: agent });
+
+socket.on('open', function () {
+ console.log('"open" event!');
+ socket.send('hello world');
+});
+
+socket.on('message', function (data, flags) {
+ console.log('"message" event! %j %j', data, flags);
+ socket.close();
+});
+```
+
+API
+---
+
+### new HttpsProxyAgent(Object options)
+
+The `HttpsProxyAgent` class implements an `http.Agent` subclass that connects
+to the specified "HTTP(s) proxy server" in order to proxy HTTPS and/or WebSocket
+requests. This is achieved by using the [HTTP `CONNECT` method][CONNECT].
+
+The `options` argument may either be a string URI of the proxy server to use, or an
+"options" object with more specific properties:
+
+ * `host` - String - Proxy host to connect to (may use `hostname` as well). Required.
+ * `port` - Number - Proxy port to connect to. Required.
+ * `protocol` - String - If `https:`, then use TLS to connect to the proxy.
+ * `headers` - Object - Additional HTTP headers to be sent on the HTTP CONNECT method.
+ * Any other options given are passed to the `net.connect()`/`tls.connect()` functions.
+
+
+License
+-------
+
+(The MIT License)
+
+Copyright (c) 2013 Nathan Rajlich <nathan@tootallnate.net>
+
+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.
+
+[CONNECT]: http://en.wikipedia.org/wiki/HTTP_tunnel#HTTP_CONNECT_Tunneling
+=========================================
+END OF https-proxy-agent@5.0.1 AND INFORMATION
+
+%% ip-address@9.0.5 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+Copyright (C) 2011 by Beau Gunderson
+
+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 ip-address@9.0.5 AND INFORMATION
+
+%% is-docker@2.2.1 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+MIT License
+
+Copyright (c) Sindre Sorhus (https://sindresorhus.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-docker@2.2.1 AND INFORMATION
+
+%% is-wsl@2.2.0 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+MIT License
+
+Copyright (c) Sindre Sorhus (sindresorhus.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-wsl@2.2.0 AND INFORMATION
+
+%% jpeg-js@0.4.4 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+Copyright (c) 2014, Eugene Ware
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Neither the name of Eugene Ware nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY EUGENE WARE ''AS IS'' AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL EUGENE WARE BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+=========================================
+END OF jpeg-js@0.4.4 AND INFORMATION
+
+%% jsbn@1.1.0 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+Licensing
+---------
+
+This software is covered under the following copyright:
+
+/*
+ * Copyright (c) 2003-2005 Tom Wu
+ * All Rights Reserved.
+ *
+ * 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" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF
+ * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * In addition, the following condition applies:
+ *
+ * All redistributions must retain an intact copy of this copyright notice
+ * and disclaimer.
+ */
+
+Address all questions regarding this license to:
+
+ Tom Wu
+ tjw@cs.Stanford.EDU
+=========================================
+END OF jsbn@1.1.0 AND INFORMATION
+
+%% mime@3.0.0 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+The MIT License (MIT)
+
+Copyright (c) 2010 Benjamin Thomas, Robert Kieffer
+
+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 mime@3.0.0 AND INFORMATION
+
+%% minimatch@3.1.2 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+The ISC License
+
+Copyright (c) Isaac Z. Schlueter and Contributors
+
+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 minimatch@3.1.2 AND INFORMATION
+
+%% ms@2.1.2 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+The MIT License (MIT)
+
+Copyright (c) 2016 Zeit, Inc.
+
+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 ms@2.1.2 AND INFORMATION
+
+%% ms@2.1.3 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+The MIT License (MIT)
+
+Copyright (c) 2020 Vercel, Inc.
+
+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 ms@2.1.3 AND INFORMATION
+
+%% once@1.4.0 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+The ISC License
+
+Copyright (c) Isaac Z. Schlueter and Contributors
+
+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 once@1.4.0 AND INFORMATION
+
+%% open@8.4.0 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+MIT License
+
+Copyright (c) Sindre Sorhus (https://sindresorhus.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 open@8.4.0 AND INFORMATION
+
+%% pend@1.2.0 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+The MIT License (Expat)
+
+Copyright (c) 2014 Andrew Kelley
+
+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 pend@1.2.0 AND INFORMATION
+
+%% pngjs@6.0.0 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+pngjs2 original work Copyright (c) 2015 Luke Page & Original Contributors
+pngjs derived work Copyright (c) 2012 Kuba Niegowski
+
+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 pngjs@6.0.0 AND INFORMATION
+
+%% progress@2.0.3 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+(The MIT License)
+
+Copyright (c) 2017 TJ Holowaychuk
+
+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 progress@2.0.3 AND INFORMATION
+
+%% proxy-from-env@1.1.0 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+The MIT License
+
+Copyright (C) 2016-2018 Rob Wu
+
+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 proxy-from-env@1.1.0 AND INFORMATION
+
+%% pump@3.0.2 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+The MIT License (MIT)
+
+Copyright (c) 2014 Mathias Buus
+
+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 pump@3.0.2 AND INFORMATION
+
+%% retry@0.12.0 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+Copyright (c) 2011:
+Tim Koschützki (tim@debuggable.com)
+Felix Geisendörfer (felix@debuggable.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 retry@0.12.0 AND INFORMATION
+
+%% signal-exit@3.0.7 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+The ISC License
+
+Copyright (c) 2015, Contributors
+
+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 signal-exit@3.0.7 AND INFORMATION
+
+%% smart-buffer@4.2.0 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+The MIT License (MIT)
+
+Copyright (c) 2013-2017 Josh Glazebrook
+
+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 smart-buffer@4.2.0 AND INFORMATION
+
+%% socks-proxy-agent@6.1.1 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+socks-proxy-agent
+================
+### A SOCKS proxy `http.Agent` implementation for HTTP and HTTPS
+[](https://github.com/TooTallNate/node-socks-proxy-agent/actions?workflow=Node+CI)
+
+This module provides an `http.Agent` implementation that connects to a
+specified SOCKS proxy server, and can be used with the built-in `http`
+and `https` modules.
+
+It can also be used in conjunction with the `ws` module to establish a WebSocket
+connection over a SOCKS proxy. See the "Examples" section below.
+
+Installation
+------------
+
+Install with `npm`:
+
+``` bash
+$ npm install socks-proxy-agent
+```
+
+
+Examples
+--------
+
+#### TypeScript example
+
+```ts
+import https from 'https';
+import { SocksProxyAgent } from 'socks-proxy-agent';
+
+const info = {
+ host: 'br41.nordvpn.com',
+ userId: 'your-name@gmail.com',
+ password: 'abcdef12345124'
+};
+const agent = new SocksProxyAgent(info);
+
+https.get('https://jsonip.org', { agent }, (res) => {
+ console.log(res.headers);
+ res.pipe(process.stdout);
+});
+```
+
+#### `http` module example
+
+```js
+var url = require('url');
+var http = require('http');
+var SocksProxyAgent = require('socks-proxy-agent');
+
+// SOCKS proxy to connect to
+var proxy = process.env.socks_proxy || 'socks://127.0.0.1:1080';
+console.log('using proxy server %j', proxy);
+
+// HTTP endpoint for the proxy to connect to
+var endpoint = process.argv[2] || 'http://nodejs.org/api/';
+console.log('attempting to GET %j', endpoint);
+var opts = url.parse(endpoint);
+
+// create an instance of the `SocksProxyAgent` class with the proxy server information
+var agent = new SocksProxyAgent(proxy);
+opts.agent = agent;
+
+http.get(opts, function (res) {
+ console.log('"response" event!', res.headers);
+ res.pipe(process.stdout);
+});
+```
+
+#### `https` module example
+
+```js
+var url = require('url');
+var https = require('https');
+var SocksProxyAgent = require('socks-proxy-agent');
+
+// SOCKS proxy to connect to
+var proxy = process.env.socks_proxy || 'socks://127.0.0.1:1080';
+console.log('using proxy server %j', proxy);
+
+// HTTP endpoint for the proxy to connect to
+var endpoint = process.argv[2] || 'https://encrypted.google.com/';
+console.log('attempting to GET %j', endpoint);
+var opts = url.parse(endpoint);
+
+// create an instance of the `SocksProxyAgent` class with the proxy server information
+var agent = new SocksProxyAgent(proxy);
+opts.agent = agent;
+
+https.get(opts, function (res) {
+ console.log('"response" event!', res.headers);
+ res.pipe(process.stdout);
+});
+```
+
+#### `ws` WebSocket connection example
+
+``` js
+var WebSocket = require('ws');
+var SocksProxyAgent = require('socks-proxy-agent');
+
+// SOCKS proxy to connect to
+var proxy = process.env.socks_proxy || 'socks://127.0.0.1:1080';
+console.log('using proxy server %j', proxy);
+
+// WebSocket endpoint for the proxy to connect to
+var endpoint = process.argv[2] || 'ws://echo.websocket.org';
+console.log('attempting to connect to WebSocket %j', endpoint);
+
+// create an instance of the `SocksProxyAgent` class with the proxy server information
+var agent = new SocksProxyAgent(proxy);
+
+// initiate the WebSocket connection
+var socket = new WebSocket(endpoint, { agent: agent });
+
+socket.on('open', function () {
+ console.log('"open" event!');
+ socket.send('hello world');
+});
+
+socket.on('message', function (data, flags) {
+ console.log('"message" event! %j %j', data, flags);
+ socket.close();
+});
+```
+
+License
+-------
+
+(The MIT License)
+
+Copyright (c) 2013 Nathan Rajlich <nathan@tootallnate.net>
+
+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 socks-proxy-agent@6.1.1 AND INFORMATION
+
+%% socks@2.8.3 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+The MIT License (MIT)
+
+Copyright (c) 2013 Josh Glazebrook
+
+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 socks@2.8.3 AND INFORMATION
+
+%% sprintf-js@1.1.3 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+Copyright (c) 2007-present, Alexandru Mărășteanu
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+* Neither the name of this software nor the names of its contributors may be
+ used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+=========================================
+END OF sprintf-js@1.1.3 AND INFORMATION
+
+%% wrappy@1.0.2 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+The ISC License
+
+Copyright (c) Isaac Z. Schlueter and Contributors
+
+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 wrappy@1.0.2 AND INFORMATION
+
+%% ws@8.17.1 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+Copyright (c) 2011 Einar Otto Stangvik
+Copyright (c) 2013 Arnout Kazemier and contributors
+Copyright (c) 2016 Luigi Pinca and contributors
+
+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 ws@8.17.1 AND INFORMATION
+
+%% yaml@2.6.0 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+Copyright Eemeli Aro
+
+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 yaml@2.6.0 AND INFORMATION
+
+%% yauzl@3.2.0 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+The MIT License (MIT)
+
+Copyright (c) 2014 Josh Wolfe
+
+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 yauzl@3.2.0 AND INFORMATION
+
+%% yazl@2.5.1 NOTICES AND INFORMATION BEGIN HERE
+=========================================
+The MIT License (MIT)
+
+Copyright (c) 2014 Josh Wolfe
+
+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 yazl@2.5.1 AND INFORMATION
+
+SUMMARY BEGIN HERE
+=========================================
+Total Packages: 44
+=========================================
+END OF SUMMARY
\ No newline at end of file
diff --git a/node_modules/playwright-core/bin/install_media_pack.ps1 b/node_modules/playwright-core/bin/install_media_pack.ps1
new file mode 100644
index 0000000..6170754
--- /dev/null
+++ b/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/node_modules/playwright-core/bin/reinstall_chrome_beta_linux.sh b/node_modules/playwright-core/bin/reinstall_chrome_beta_linux.sh
new file mode 100755
index 0000000..0451bda
--- /dev/null
+++ b/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 -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/node_modules/playwright-core/bin/reinstall_chrome_beta_mac.sh b/node_modules/playwright-core/bin/reinstall_chrome_beta_mac.sh
new file mode 100755
index 0000000..c563c81
--- /dev/null
+++ b/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 --retry 3 -o ./googlechromebeta.dmg -k 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/node_modules/playwright-core/bin/reinstall_chrome_beta_win.ps1 b/node_modules/playwright-core/bin/reinstall_chrome_beta_win.ps1
new file mode 100644
index 0000000..3fbe551
--- /dev/null
+++ b/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/node_modules/playwright-core/bin/reinstall_chrome_stable_linux.sh b/node_modules/playwright-core/bin/reinstall_chrome_stable_linux.sh
new file mode 100755
index 0000000..78f1d41
--- /dev/null
+++ b/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 -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/node_modules/playwright-core/bin/reinstall_chrome_stable_mac.sh b/node_modules/playwright-core/bin/reinstall_chrome_stable_mac.sh
new file mode 100755
index 0000000..035fa86
--- /dev/null
+++ b/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 --retry 3 -o ./googlechrome.dmg -k 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/node_modules/playwright-core/bin/reinstall_chrome_stable_win.ps1 b/node_modules/playwright-core/bin/reinstall_chrome_stable_win.ps1
new file mode 100644
index 0000000..7ca2dba
--- /dev/null
+++ b/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/node_modules/playwright-core/bin/reinstall_msedge_beta_linux.sh b/node_modules/playwright-core/bin/reinstall_msedge_beta_linux.sh
new file mode 100755
index 0000000..a1531a9
--- /dev/null
+++ b/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 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/node_modules/playwright-core/bin/reinstall_msedge_beta_mac.sh b/node_modules/playwright-core/bin/reinstall_msedge_beta_mac.sh
new file mode 100755
index 0000000..c03bb02
--- /dev/null
+++ b/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 --retry 3 -o ./msedge_beta.pkg -k "$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/node_modules/playwright-core/bin/reinstall_msedge_beta_win.ps1 b/node_modules/playwright-core/bin/reinstall_msedge_beta_win.ps1
new file mode 100644
index 0000000..cce0d0b
--- /dev/null
+++ b/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/node_modules/playwright-core/bin/reinstall_msedge_dev_linux.sh b/node_modules/playwright-core/bin/reinstall_msedge_dev_linux.sh
new file mode 100755
index 0000000..7fde34e
--- /dev/null
+++ b/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 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/node_modules/playwright-core/bin/reinstall_msedge_dev_mac.sh b/node_modules/playwright-core/bin/reinstall_msedge_dev_mac.sh
new file mode 100755
index 0000000..9b664da
--- /dev/null
+++ b/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 --retry 3 -o ./msedge_dev.pkg -k "$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/node_modules/playwright-core/bin/reinstall_msedge_dev_win.ps1 b/node_modules/playwright-core/bin/reinstall_msedge_dev_win.ps1
new file mode 100644
index 0000000..22e6db8
--- /dev/null
+++ b/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/node_modules/playwright-core/bin/reinstall_msedge_stable_linux.sh b/node_modules/playwright-core/bin/reinstall_msedge_stable_linux.sh
new file mode 100755
index 0000000..4acb1db
--- /dev/null
+++ b/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 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/node_modules/playwright-core/bin/reinstall_msedge_stable_mac.sh b/node_modules/playwright-core/bin/reinstall_msedge_stable_mac.sh
new file mode 100755
index 0000000..7a72703
--- /dev/null
+++ b/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 --retry 3 -o ./msedge_stable.pkg -k "$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/node_modules/playwright-core/bin/reinstall_msedge_stable_win.ps1 b/node_modules/playwright-core/bin/reinstall_msedge_stable_win.ps1
new file mode 100644
index 0000000..31fdf51
--- /dev/null
+++ b/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/node_modules/playwright-core/browsers.json b/node_modules/playwright-core/browsers.json
new file mode 100644
index 0000000..169663e
--- /dev/null
+++ b/node_modules/playwright-core/browsers.json
@@ -0,0 +1,80 @@
+{
+ "comment": "Do not edit this file, use utils/roll_browser.js",
+ "browsers": [
+ {
+ "name": "chromium",
+ "revision": "1187",
+ "installByDefault": true,
+ "browserVersion": "140.0.7339.16"
+ },
+ {
+ "name": "chromium-headless-shell",
+ "revision": "1187",
+ "installByDefault": true,
+ "browserVersion": "140.0.7339.16"
+ },
+ {
+ "name": "chromium-tip-of-tree",
+ "revision": "1357",
+ "installByDefault": false,
+ "browserVersion": "141.0.7342.0"
+ },
+ {
+ "name": "chromium-tip-of-tree-headless-shell",
+ "revision": "1357",
+ "installByDefault": false,
+ "browserVersion": "141.0.7342.0"
+ },
+ {
+ "name": "firefox",
+ "revision": "1490",
+ "installByDefault": true,
+ "browserVersion": "141.0"
+ },
+ {
+ "name": "firefox-beta",
+ "revision": "1485",
+ "installByDefault": false,
+ "browserVersion": "142.0b4"
+ },
+ {
+ "name": "webkit",
+ "revision": "2203",
+ "installByDefault": true,
+ "revisionOverrides": {
+ "debian11-x64": "2105",
+ "debian11-arm64": "2105",
+ "mac10.14": "1446",
+ "mac10.15": "1616",
+ "mac11": "1816",
+ "mac11-arm64": "1816",
+ "mac12": "2009",
+ "mac12-arm64": "2009",
+ "mac13": "2140",
+ "mac13-arm64": "2140",
+ "ubuntu20.04-x64": "2092",
+ "ubuntu20.04-arm64": "2092"
+ },
+ "browserVersion": "26.0"
+ },
+ {
+ "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/node_modules/playwright-core/cli.js b/node_modules/playwright-core/cli.js
new file mode 100755
index 0000000..fb309ea
--- /dev/null
+++ b/node_modules/playwright-core/cli.js
@@ -0,0 +1,18 @@
+#!/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('./lib/cli/programWithTestStub');
+program.parse(process.argv);
diff --git a/node_modules/playwright-core/index.d.ts b/node_modules/playwright-core/index.d.ts
new file mode 100644
index 0000000..97c1493
--- /dev/null
+++ b/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/node_modules/playwright-core/index.js b/node_modules/playwright-core/index.js
new file mode 100644
index 0000000..d4991d0
--- /dev/null
+++ b/node_modules/playwright-core/index.js
@@ -0,0 +1,32 @@
+/**
+ * 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 minimumMajorNodeVersion = 18;
+const currentNodeVersion = process.versions.node;
+const semver = currentNodeVersion.split('.');
+const [major] = [+semver[0]];
+
+if (major < minimumMajorNodeVersion) {
+ console.error(
+ 'You are running Node.js ' +
+ currentNodeVersion +
+ '.\n' +
+ `Playwright requires Node.js ${minimumMajorNodeVersion} or higher. \n` +
+ 'Please update your version of Node.js.'
+ );
+ process.exit(1);
+}
+
+module.exports = require('./lib/inprocess');
diff --git a/node_modules/playwright-core/index.mjs b/node_modules/playwright-core/index.mjs
new file mode 100644
index 0000000..3b3c75b
--- /dev/null
+++ b/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/node_modules/playwright-core/lib/androidServerImpl.js b/node_modules/playwright-core/lib/androidServerImpl.js
new file mode 100644
index 0000000..568548b
--- /dev/null
+++ b/node_modules/playwright-core/lib/androidServerImpl.js
@@ -0,0 +1,65 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var androidServerImpl_exports = {};
+__export(androidServerImpl_exports, {
+ AndroidServerLauncherImpl: () => AndroidServerLauncherImpl
+});
+module.exports = __toCommonJS(androidServerImpl_exports);
+var import_playwrightServer = require("./remote/playwrightServer");
+var import_playwright = require("./server/playwright");
+var import_crypto = require("./server/utils/crypto");
+var import_utilsBundle = require("./utilsBundle");
+var import_progress = require("./server/progress");
+class AndroidServerLauncherImpl {
+ async launchServer(options = {}) {
+ const playwright = (0, import_playwright.createPlaywright)({ sdkLanguage: "javascript", isServer: true });
+ const controller = new import_progress.ProgressController();
+ let devices = await controller.run((progress) => playwright.android.devices(progress, {
+ 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 path = options.wsPath ? options.wsPath.startsWith("/") ? options.wsPath : `/${options.wsPath}` : `/${(0, import_crypto.createGuid)()}`;
+ const server = new import_playwrightServer.PlaywrightServer({ mode: "launchServer", path, maxConnections: 1, preLaunchedAndroidDevice: device });
+ const wsEndpoint = await server.listen(options.port, options.host);
+ const browserServer = new import_utilsBundle.ws.EventEmitter();
+ browserServer.wsEndpoint = () => wsEndpoint;
+ browserServer.close = () => device.close();
+ browserServer.kill = () => device.close();
+ device.on("close", () => {
+ server.close();
+ browserServer.emit("close");
+ });
+ return browserServer;
+ }
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ AndroidServerLauncherImpl
+});
diff --git a/node_modules/playwright-core/lib/browserServerImpl.js b/node_modules/playwright-core/lib/browserServerImpl.js
new file mode 100644
index 0000000..5e97b05
--- /dev/null
+++ b/node_modules/playwright-core/lib/browserServerImpl.js
@@ -0,0 +1,123 @@
+"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 browserServerImpl_exports = {};
+__export(browserServerImpl_exports, {
+ BrowserServerLauncherImpl: () => BrowserServerLauncherImpl
+});
+module.exports = __toCommonJS(browserServerImpl_exports);
+var import_playwrightServer = require("./remote/playwrightServer");
+var import_helper = require("./server/helper");
+var import_playwright = require("./server/playwright");
+var import_crypto = require("./server/utils/crypto");
+var import_debug = require("./server/utils/debug");
+var import_stackTrace = require("./utils/isomorphic/stackTrace");
+var import_time = require("./utils/isomorphic/time");
+var import_utilsBundle = require("./utilsBundle");
+var validatorPrimitives = __toESM(require("./protocol/validatorPrimitives"));
+var import_progress = require("./server/progress");
+class BrowserServerLauncherImpl {
+ constructor(browserName) {
+ this._browserName = browserName;
+ }
+ async launchServer(options = {}) {
+ const playwright = (0, import_playwright.createPlaywright)({ sdkLanguage: "javascript", isServer: true });
+ const metadata = { id: "", startTime: 0, endTime: 0, type: "Internal", method: "", params: {}, log: [], internal: true };
+ const validatorContext = {
+ tChannelImpl: (names, arg, path) => {
+ throw new validatorPrimitives.ValidationError(`${path}: channels are not expected in launchServer`);
+ },
+ binary: "buffer",
+ isUnderTest: import_debug.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 ?? import_time.DEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT
+ };
+ let browser;
+ try {
+ const controller = new import_progress.ProgressController(metadata);
+ browser = await controller.run(async (progress) => {
+ if (options._userDataDir !== void 0) {
+ const validator = validatorPrimitives.scheme["BrowserTypeLaunchPersistentContextParams"];
+ launchOptions = validator({ ...launchOptions, userDataDir: options._userDataDir }, "", validatorContext);
+ const context = await playwright[this._browserName].launchPersistentContext(progress, options._userDataDir, launchOptions);
+ return context._browser;
+ } else {
+ const validator = validatorPrimitives.scheme["BrowserTypeLaunchParams"];
+ launchOptions = validator(launchOptions, "", validatorContext);
+ return await playwright[this._browserName].launch(progress, launchOptions, toProtocolLogger(options.logger));
+ }
+ });
+ } catch (e) {
+ const log = import_helper.helper.formatBrowserLogs(metadata.log);
+ (0, import_stackTrace.rewriteErrorMessage)(e, `${e.message} Failed to launch browser.${log}`);
+ throw e;
+ }
+ return this.launchServerOnExistingBrowser(browser, options);
+ }
+ async launchServerOnExistingBrowser(browser, options) {
+ const path = options.wsPath ? options.wsPath.startsWith("/") ? options.wsPath : `/${options.wsPath}` : `/${(0, import_crypto.createGuid)()}`;
+ const server = new import_playwrightServer.PlaywrightServer({ mode: options._sharedBrowser ? "launchServerShared" : "launchServer", path, maxConnections: Infinity, preLaunchedBrowser: browser, debugController: options._debugController });
+ const wsEndpoint = await server.listen(options.port, options.host);
+ const browserServer = new import_utilsBundle.ws.EventEmitter();
+ 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;
+ }
+}
+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 result = [];
+ for (const name in env) {
+ if (!Object.is(env[name], void 0))
+ result.push({ name, value: String(env[name]) });
+ }
+ return result;
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ BrowserServerLauncherImpl
+});
diff --git a/node_modules/playwright-core/lib/cli/driver.js b/node_modules/playwright-core/lib/cli/driver.js
new file mode 100644
index 0000000..a389e15
--- /dev/null
+++ b/node_modules/playwright-core/lib/cli/driver.js
@@ -0,0 +1,97 @@
+"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 driver_exports = {};
+__export(driver_exports, {
+ launchBrowserServer: () => launchBrowserServer,
+ printApiJson: () => printApiJson,
+ runDriver: () => runDriver,
+ runServer: () => runServer
+});
+module.exports = __toCommonJS(driver_exports);
+var import_fs = __toESM(require("fs"));
+var playwright = __toESM(require("../.."));
+var import_pipeTransport = require("../server/utils/pipeTransport");
+var import_playwrightServer = require("../remote/playwrightServer");
+var import_server = require("../server");
+var import_processLauncher = require("../server/utils/processLauncher");
+function printApiJson() {
+ console.log(JSON.stringify(require("../../api.json")));
+}
+function runDriver() {
+ const dispatcherConnection = new import_server.DispatcherConnection();
+ new import_server.RootDispatcher(dispatcherConnection, async (rootScope, { sdkLanguage }) => {
+ const playwright2 = (0, import_server.createPlaywright)({ sdkLanguage });
+ return new import_server.PlaywrightDispatcher(rootScope, playwright2);
+ });
+ const transport = new import_pipeTransport.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, value) => {
+ if (typeof value === "string")
+ return value.toWellFormed();
+ return value;
+ } : void 0;
+ dispatcherConnection.onmessage = (message) => transport.send(JSON.stringify(message, replacer));
+ transport.onclose = () => {
+ dispatcherConnection.onmessage = () => {
+ };
+ (0, import_processLauncher.gracefullyProcessExitDoNotHang)(0);
+ };
+ process.on("SIGINT", () => {
+ });
+}
+async function runServer(options) {
+ const {
+ port,
+ host,
+ path = "/",
+ maxConnections = Infinity,
+ extension
+ } = options;
+ const server = new import_playwrightServer.PlaywrightServer({ mode: extension ? "extension" : "default", path, maxConnections });
+ const wsEndpoint = await server.listen(port, host);
+ process.on("exit", () => server.close().catch(console.error));
+ console.log("Listening on " + wsEndpoint);
+ process.stdin.on("close", () => (0, import_processLauncher.gracefullyProcessExitDoNotHang)(0));
+}
+async function launchBrowserServer(browserName, configFile) {
+ let options = {};
+ if (configFile)
+ options = JSON.parse(import_fs.default.readFileSync(configFile).toString());
+ const browserType = playwright[browserName];
+ const server = await browserType.launchServer(options);
+ console.log(server.wsEndpoint());
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ launchBrowserServer,
+ printApiJson,
+ runDriver,
+ runServer
+});
diff --git a/node_modules/playwright-core/lib/cli/program.js b/node_modules/playwright-core/lib/cli/program.js
new file mode 100644
index 0000000..2c2ba5c
--- /dev/null
+++ b/node_modules/playwright-core/lib/cli/program.js
@@ -0,0 +1,633 @@
+"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 program_exports = {};
+__export(program_exports, {
+ program: () => import_utilsBundle2.program
+});
+module.exports = __toCommonJS(program_exports);
+var import_fs = __toESM(require("fs"));
+var import_os = __toESM(require("os"));
+var import_path = __toESM(require("path"));
+var playwright = __toESM(require("../.."));
+var import_driver = require("./driver");
+var import_server = require("../server");
+var import_utils = require("../utils");
+var import_traceViewer = require("../server/trace/viewer/traceViewer");
+var import_utils2 = require("../utils");
+var import_ascii = require("../server/utils/ascii");
+var import_utilsBundle = require("../utilsBundle");
+var import_utilsBundle2 = require("../utilsBundle");
+const packageJSON = require("../../package.json");
+import_utilsBundle.program.version("Version " + (process.env.PW_CLI_DISPLAY_VERSION || packageJSON.version)).name(buildBasePlaywrightCLICommand(process.env.PW_LANG_NAME));
+import_utilsBundle.program.command("mark-docker-image [dockerImageNameTemplate]", { hidden: true }).description("mark docker image").allowUnknownOption(true).action(function(dockerImageNameTemplate) {
+ (0, import_utils2.assert)(dockerImageNameTemplate, "dockerImageNameTemplate is required");
+ (0, import_server.writeDockerVersion)(dockerImageNameTemplate).catch(logErrorAndExit);
+});
+commandWithOpenOptions("open [url]", "open page in browser specified via -b, --browser", []).action(function(url, options) {
+ open(options, url).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, java, java-junit`, codegenId()],
+ ["--test-id-attribute ", "use the specified attribute to generate data test ID selectors"]
+ ]
+).action(async function(url, options) {
+ await codegen(options, url);
+}).addHelpText("afterAll", `
+Examples:
+
+ $ codegen
+ $ codegen --target=python
+ $ codegen -b webkit https://example.com`);
+function suggestedBrowsersToInstall() {
+ return import_server.registry.executables().filter((e) => e.installType !== "none" && e.type !== "tool").map((e) => e.name).join(", ");
+}
+function defaultBrowsersToInstall(options) {
+ let executables = import_server.registry.defaultExecutables();
+ if (options.noShell)
+ executables = executables.filter((e) => e.name !== "chromium-headless-shell");
+ if (options.onlyShell)
+ executables = executables.filter((e) => e.name !== "chromium");
+ return executables;
+}
+function checkBrowsersToInstall(args, options) {
+ if (options.noShell && options.onlyShell)
+ throw new Error(`Only one of --no-shell and --only-shell can be specified`);
+ const faultyArguments = [];
+ const executables = [];
+ const handleArgument = (arg) => {
+ const executable = import_server.registry.findExecutable(arg);
+ if (!executable || executable.installType === "none")
+ faultyArguments.push(arg);
+ else
+ executables.push(executable);
+ if (executable?.browserName === "chromium")
+ executables.push(import_server.registry.findExecutable("ffmpeg"));
+ };
+ for (const arg of args) {
+ if (arg === "chromium") {
+ if (!options.onlyShell)
+ handleArgument("chromium");
+ if (!options.noShell)
+ handleArgument("chromium-headless-shell");
+ } else {
+ handleArgument(arg);
+ }
+ }
+ if (process.platform === "win32")
+ executables.push(import_server.registry.findExecutable("winldd"));
+ if (faultyArguments.length)
+ throw new Error(`Invalid installation targets: ${faultyArguments.map((name) => `'${name}'`).join(", ")}. Expecting one of: ${suggestedBrowsersToInstall()}`);
+ return executables;
+}
+function printInstalledBrowsers(browsers2) {
+ const browserPaths = /* @__PURE__ */ new Set();
+ for (const browser of browsers2)
+ 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 browsers2)
+ references.add(browser.referenceDir);
+ for (const reference of [...references].sort())
+ console.log(` ${reference}`);
+}
+function printGroupedByPlaywrightVersion(browsers2) {
+ const dirToVersion = /* @__PURE__ */ new Map();
+ for (const browser of browsers2) {
+ if (dirToVersion.has(browser.referenceDir))
+ continue;
+ const packageJSON2 = require(import_path.default.join(browser.referenceDir, "package.json"));
+ const version = packageJSON2.version;
+ dirToVersion.set(browser.referenceDir, version);
+ }
+ const groupedByPlaywrightMinorVersion = /* @__PURE__ */ new Map();
+ for (const browser of browsers2) {
+ const version = dirToVersion.get(browser.referenceDir);
+ let entries = groupedByPlaywrightMinorVersion.get(version);
+ if (!entries) {
+ entries = [];
+ groupedByPlaywrightMinorVersion.set(version, 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 version of sortedVersions) {
+ console.log(`
+Playwright version: ${version}`);
+ printInstalledBrowsers(groupedByPlaywrightMinorVersion.get(version));
+ }
+}
+import_utilsBundle.program.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 stable browser channels").option("--only-shell", "only install headless shell when installing chromium").option("--no-shell", "do not install chromium headless shell").action(async function(args, options) {
+ if (options.shell === false)
+ options.noShell = true;
+ if ((0, import_utils.isLikelyNpxGlobal)()) {
+ console.error((0, import_ascii.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));
+ }
+ try {
+ const hasNoArguments = !args.length;
+ const executables = hasNoArguments ? defaultBrowsersToInstall(options) : checkBrowsersToInstall(args, options);
+ if (options.withDeps)
+ await import_server.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) {
+ const version = executable.browserVersion ? `version ` + executable.browserVersion : "";
+ console.log(`browser: ${executable.name}${version ? " " + version : ""}`);
+ console.log(` Install location: ${executable.directory ?? ""}`);
+ if (executable.downloadURLs?.length) {
+ const [url, ...fallbacks] = executable.downloadURLs;
+ console.log(` Download url: ${url}`);
+ for (let i = 0; i < fallbacks.length; ++i)
+ console.log(` Download fallback ${i + 1}: ${fallbacks[i]}`);
+ }
+ console.log(``);
+ }
+ } else if (options.list) {
+ const browsers2 = await import_server.registry.listInstalledBrowsers();
+ printGroupedByPlaywrightVersion(browsers2);
+ } else {
+ const forceReinstall = hasNoArguments ? false : !!options.force;
+ await import_server.registry.install(executables, forceReinstall);
+ await import_server.registry.validateHostRequirementsForExecutablesIfNeeded(executables, process.env.PW_LANG_NAME || "javascript").catch((e) => {
+ e.name = "Playwright Host validation warning";
+ console.error(e);
+ });
+ }
+ } catch (e) {
+ console.log(`Failed to install browsers
+${e}`);
+ (0, import_utils.gracefullyProcessExitDoNotHang)(1);
+ }
+}).addHelpText("afterAll", `
+
+Examples:
+ - $ install
+ Install default browsers.
+
+ - $ install chrome firefox
+ Install custom browsers, supports ${suggestedBrowsersToInstall()}.`);
+import_utilsBundle.program.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) => {
+ delete process.env.PLAYWRIGHT_SKIP_BROWSER_GC;
+ await import_server.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.`);
+ }
+ }).catch(logErrorAndExit);
+});
+import_utilsBundle.program.command("install-deps [browser...]").description("install dependencies necessary to run browsers (will ask for sudo permissions)").option("--dry-run", "Do not execute installation commands, only print them").action(async function(args, options) {
+ try {
+ if (!args.length)
+ await import_server.registry.installDeps(defaultBrowsersToInstall({}), !!options.dryRun);
+ else
+ await import_server.registry.installDeps(checkBrowsersToInstall(args, {}), !!options.dryRun);
+ } catch (e) {
+ console.log(`Failed to install browser dependencies
+${e}`);
+ (0, import_utils.gracefullyProcessExitDoNotHang)(1);
+ }
+}).addHelpText("afterAll", `
+Examples:
+ - $ install-deps
+ Install dependencies for default browsers.
+
+ - $ install-deps chrome firefox
+ Install dependencies for specific browsers, supports ${suggestedBrowsersToInstall()}.`);
+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 } of browsers) {
+ commandWithOpenOptions(`${alias} [url]`, `open page in ${name}`, []).action(function(url, options) {
+ open({ ...options, browser: type }, url).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(function(url, filename, command) {
+ screenshot(command, command, url, 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(function(url, filename, options) {
+ pdf(options, options, url, filename).catch(logErrorAndExit);
+}).addHelpText("afterAll", `
+Examples:
+
+ $ pdf https://example.com example.pdf`);
+import_utilsBundle.program.command("run-driver", { hidden: true }).action(function(options) {
+ (0, import_driver.runDriver)();
+});
+import_utilsBundle.program.command("run-server").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"').action(function(options) {
+ (0, import_driver.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
+ }).catch(logErrorAndExit);
+});
+import_utilsBundle.program.command("print-api-json", { hidden: true }).action(function(options) {
+ (0, import_driver.printApiJson)();
+});
+import_utilsBundle.program.command("launch-server", { hidden: true }).requiredOption("--browser ", 'Browser name, one of "chromium", "firefox" or "webkit"').option("--config ", "JSON file with launchServer options").action(function(options) {
+ (0, import_driver.launchBrowserServer)(options.browser, options.config);
+});
+import_utilsBundle.program.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(function(traces, 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)
+ (0, import_traceViewer.runTraceInBrowser)(traces, openOptions).catch(logErrorAndExit);
+ else
+ (0, import_traceViewer.runTraceViewerApp)(traces, options.browser, openOptions, true).catch(logErrorAndExit);
+}).addHelpText("afterAll", `
+Examples:
+
+ $ show-trace https://example.com/trace.zip`);
+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_os.default.platform() === "darwin" ? 2 : 1;
+ if (browserType.name() === "webkit" && process.platform === "linux") {
+ delete contextOptions.hasTouch;
+ delete contextOptions.isMobile;
+ }
+ if (contextOptions.isMobile && browserType.name() === "firefox")
+ contextOptions.isMobile = void 0;
+ 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_path.default.resolve(process.cwd(), options.saveHar), mode: "minimal" };
+ if (options.saveHarGlob)
+ contextOptions.recordHar.urlFilter = options.saveHarGlob;
+ contextOptions.serviceWorkers = "block";
+ }
+ let browser;
+ let context;
+ if (options.userDataDir) {
+ context = await browserType.launchPersistentContext(options.userDataDir, { ...launchOptions, ...contextOptions });
+ browser = context.browser();
+ } else {
+ browser = await browserType.launch(launchOptions);
+ context = await browser.newContext(contextOptions);
+ }
+ let closingBrowser = false;
+ async function closeBrowser() {
+ if (closingBrowser)
+ return;
+ closingBrowser = true;
+ if (options.saveStorage)
+ await context.storageState({ path: options.saveStorage }).catch((e) => null);
+ if (options.saveHar)
+ await context.close();
+ await browser.close();
+ }
+ context.on("page", (page) => {
+ page.on("dialog", () => {
+ });
+ page.on("close", () => {
+ const hasPage = browser.contexts().some((context2) => context2.pages().length > 0);
+ if (hasPage)
+ return;
+ closeBrowser().catch(() => {
+ });
+ });
+ });
+ process.on("SIGINT", async () => {
+ await closeBrowser();
+ (0, import_utils.gracefullyProcessExitDoNotHang)(130);
+ });
+ const timeout = options.timeout ? parseInt(options.timeout, 10) : 0;
+ context.setDefaultTimeout(timeout);
+ context.setDefaultNavigationTimeout(timeout);
+ delete launchOptions.headless;
+ delete launchOptions.executablePath;
+ delete launchOptions.handleSIGINT;
+ delete contextOptions.deviceScaleFactor;
+ return { browser, browserName: browserType.name(), context, contextOptions, launchOptions, closeBrowser };
+}
+async function openPage(context, url) {
+ let page = context.pages()[0];
+ if (!page)
+ page = await context.newPage();
+ if (url) {
+ if (import_fs.default.existsSync(url))
+ url = "file://" + import_path.default.resolve(url);
+ else if (!url.startsWith("http") && !url.startsWith("file://") && !url.startsWith("about:") && !url.startsWith("data:"))
+ url = "http://" + url;
+ await page.goto(url);
+ }
+ return page;
+}
+async function open(options, url) {
+ const { context } = await launchContext(options, { headless: !!process.env.PWTEST_CLI_HEADLESS, executablePath: process.env.PWTEST_CLI_EXECUTABLE_PATH });
+ await openPage(context, url);
+}
+async function codegen(options, url) {
+ const { target: language, output: outputFile, testIdAttribute: testIdAttributeName } = options;
+ const tracesDir = import_path.default.join(import_os.default.tmpdir(), `playwright-recorder-trace-${Date.now()}`);
+ const { context, browser, launchOptions, contextOptions, closeBrowser } = await launchContext(options, {
+ headless: !!process.env.PWTEST_CLI_HEADLESS,
+ executablePath: process.env.PWTEST_CLI_EXECUTABLE_PATH,
+ tracesDir
+ });
+ const donePromise = new import_utils.ManualPromise();
+ maybeSetupTestHooks(browser, closeBrowser, donePromise);
+ import_utilsBundle.dotenv.config({ path: "playwright.env" });
+ await context._enableRecorder({
+ language,
+ launchOptions,
+ contextOptions,
+ device: options.device,
+ saveStorage: options.saveStorage,
+ mode: "recording",
+ testIdAttributeName,
+ outputFile: outputFile ? import_path.default.resolve(outputFile) : void 0,
+ handleSIGINT: false
+ });
+ await openPage(context, url);
+ donePromise.resolve();
+}
+async function maybeSetupTestHooks(browser, closeBrowser, donePromise) {
+ if (!process.env.PWTEST_CLI_IS_UNDER_TEST)
+ return;
+ const logs = [];
+ require("playwright-core/lib/utilsBundle").debug.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");
+ (0, import_utils.gracefullyProcessExitDoNotHang)(1);
+ }
+ });
+ const close = async () => {
+ await donePromise;
+ await closeBrowser();
+ };
+ if (process.env.PWTEST_CLI_EXIT_AFTER_TIMEOUT) {
+ setTimeout(close, +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();
+ close();
+ }
+ });
+}
+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 screenshot(options, captureOptions, url, path2) {
+ const { context } = await launchContext(options, { headless: true });
+ console.log("Navigating to " + url);
+ const page = await openPage(context, url);
+ await waitForPage(page, captureOptions);
+ console.log("Capturing screenshot into " + path2);
+ await page.screenshot({ path: path2, fullPage: !!captureOptions.fullPage });
+ await page.close();
+}
+async function pdf(options, captureOptions, url, path2) {
+ if (options.browser !== "chromium")
+ throw new Error("PDF creation is only working with Chromium");
+ const { context } = await launchContext({ ...options, browser: "chromium" }, { headless: true });
+ console.log("Navigating to " + url);
+ const page = await openPage(context, url);
+ await waitForPage(page, captureOptions);
+ console.log("Saving as pdf into " + path2);
+ await page.pdf({ path: path2, 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;
+ import_utilsBundle.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"');
+}
+function logErrorAndExit(e) {
+ if (process.env.PWDEBUGIMPL)
+ console.error(e);
+ else
+ console.error(e.name + ": " + e.message);
+ (0, import_utils.gracefullyProcessExitDoNotHang)(1);
+}
+function codegenId() {
+ return process.env.PW_LANG_NAME || "playwright-test";
+}
+function commandWithOpenOptions(command, description, options) {
+ let result = import_utilsBundle.program.command(command).description(description);
+ for (const option of options)
+ result = result.option(option[0], ...option.slice(1));
+ return result.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 = (0, import_utils2.getPackageManagerExecCommand)();
+ return `${packageManagerCommand} playwright`;
+ }
+ }
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ program
+});
diff --git a/node_modules/playwright-core/lib/cli/programWithTestStub.js b/node_modules/playwright-core/lib/cli/programWithTestStub.js
new file mode 100644
index 0000000..6c4e47c
--- /dev/null
+++ b/node_modules/playwright-core/lib/cli/programWithTestStub.js
@@ -0,0 +1,74 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var programWithTestStub_exports = {};
+__export(programWithTestStub_exports, {
+ program: () => import_program2.program
+});
+module.exports = __toCommonJS(programWithTestStub_exports);
+var import_processLauncher = require("../server/utils/processLauncher");
+var import_utils = require("../utils");
+var import_program = require("./program");
+var import_program2 = require("./program");
+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 = (0, import_utils.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");
+ }
+}
+const kExternalPlaywrightTestCommands = [
+ ["test", "Run tests with Playwright Test."],
+ ["show-report", "Show Playwright Test HTML report."],
+ ["merge-reports", "Merge Playwright Test Blob reports"]
+];
+function addExternalPlaywrightTestCommands() {
+ for (const [command, description] of kExternalPlaywrightTestCommands) {
+ const playwrightTest = import_program.program.command(command).allowUnknownOption(true).allowExcessArguments(true);
+ playwrightTest.description(`${description} Available in @playwright/test package.`);
+ playwrightTest.action(async () => {
+ printPlaywrightTestError(command);
+ (0, import_processLauncher.gracefullyProcessExitDoNotHang)(1);
+ });
+ }
+}
+if (!process.env.PW_LANG_NAME)
+ addExternalPlaywrightTestCommands();
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ program
+});
diff --git a/node_modules/playwright-core/lib/client/accessibility.js b/node_modules/playwright-core/lib/client/accessibility.js
new file mode 100644
index 0000000..7901891
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/accessibility.js
@@ -0,0 +1,49 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var accessibility_exports = {};
+__export(accessibility_exports, {
+ Accessibility: () => Accessibility
+});
+module.exports = __toCommonJS(accessibility_exports);
+function axNodeFromProtocol(axNode) {
+ const result = {
+ ...axNode,
+ value: axNode.valueNumber !== void 0 ? axNode.valueNumber : axNode.valueString,
+ checked: axNode.checked === "checked" ? true : axNode.checked === "unchecked" ? false : axNode.checked,
+ pressed: axNode.pressed === "pressed" ? true : axNode.pressed === "released" ? false : axNode.pressed,
+ children: axNode.children ? axNode.children.map(axNodeFromProtocol) : void 0
+ };
+ delete result.valueNumber;
+ delete result.valueString;
+ return result;
+}
+class Accessibility {
+ constructor(channel) {
+ this._channel = channel;
+ }
+ async snapshot(options = {}) {
+ const root = options.root ? options.root._elementChannel : void 0;
+ const result = await this._channel.accessibilitySnapshot({ interestingOnly: options.interestingOnly, root });
+ return result.rootAXNode ? axNodeFromProtocol(result.rootAXNode) : null;
+ }
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ Accessibility
+});
diff --git a/node_modules/playwright-core/lib/client/android.js b/node_modules/playwright-core/lib/client/android.js
new file mode 100644
index 0000000..750b6be
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/android.js
@@ -0,0 +1,361 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var android_exports = {};
+__export(android_exports, {
+ Android: () => Android,
+ AndroidDevice: () => AndroidDevice,
+ AndroidInput: () => AndroidInput,
+ AndroidSocket: () => AndroidSocket,
+ AndroidWebView: () => AndroidWebView
+});
+module.exports = __toCommonJS(android_exports);
+var import_eventEmitter = require("./eventEmitter");
+var import_browserContext = require("./browserContext");
+var import_channelOwner = require("./channelOwner");
+var import_errors = require("./errors");
+var import_events = require("./events");
+var import_waiter = require("./waiter");
+var import_timeoutSettings = require("./timeoutSettings");
+var import_rtti = require("../utils/isomorphic/rtti");
+var import_time = require("../utils/isomorphic/time");
+var import_timeoutRunner = require("../utils/isomorphic/timeoutRunner");
+var import_webSocket = require("./webSocket");
+class Android extends import_channelOwner.ChannelOwner {
+ static from(android) {
+ return android._object;
+ }
+ constructor(parent, type, guid, initializer) {
+ super(parent, type, guid, initializer);
+ this._timeoutSettings = new import_timeoutSettings.TimeoutSettings(this._platform);
+ }
+ setDefaultTimeout(timeout) {
+ this._timeoutSettings.setDefaultTimeout(timeout);
+ }
+ async devices(options = {}) {
+ const { devices } = await this._channel.devices(options);
+ return devices.map((d) => AndroidDevice.from(d));
+ }
+ async launchServer(options = {}) {
+ if (!this._serverLauncher)
+ throw new Error("Launching server is not supported");
+ return await this._serverLauncher.launchServer(options);
+ }
+ async connect(wsEndpoint, options = {}) {
+ return await this._wrapApiCall(async () => {
+ const deadline = options.timeout ? (0, import_time.monotonicTime)() + options.timeout : 0;
+ const headers = { "x-playwright-browser": "android", ...options.headers };
+ const connectParams = { wsEndpoint, headers, slowMo: options.slowMo, timeout: options.timeout || 0 };
+ const connection = await (0, import_webSocket.connectOverWebSocket)(this._connection, connectParams);
+ let device;
+ connection.on("close", () => {
+ device?._didClose();
+ });
+ const result = await (0, import_timeoutRunner.raceAgainstDeadline)(async () => {
+ const playwright = await connection.initializePlaywright();
+ if (!playwright._initializer.preConnectedAndroidDevice) {
+ connection.close();
+ throw new Error("Malformed endpoint. Did you use Android.launchServer method?");
+ }
+ device = AndroidDevice.from(playwright._initializer.preConnectedAndroidDevice);
+ device._shouldCloseConnectionOnClose = true;
+ device.on(import_events.Events.AndroidDevice.Close, () => connection.close());
+ return device;
+ }, deadline);
+ if (!result.timedOut) {
+ return result.result;
+ } else {
+ connection.close();
+ throw new Error(`Timeout ${options.timeout}ms exceeded`);
+ }
+ });
+ }
+}
+class AndroidDevice extends import_channelOwner.ChannelOwner {
+ constructor(parent, type, guid, initializer) {
+ super(parent, type, guid, initializer);
+ this._webViews = /* @__PURE__ */ new Map();
+ this._shouldCloseConnectionOnClose = false;
+ this._android = parent;
+ this.input = new AndroidInput(this);
+ this._timeoutSettings = new import_timeoutSettings.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(import_events.Events.AndroidDevice.WebView, view);
+ }
+ _onWebViewRemoved(socketName) {
+ const view = this._webViews.get(socketName);
+ this._webViews.delete(socketName);
+ if (view)
+ view.emit(import_events.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, text, options = {}) {
+ await this._channel.fill({ androidSelector: toSelectorChannel(selector), text, ...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 ((0, import_errors.isTargetClosedError)(e))
+ return;
+ throw e;
+ }
+ }
+ _didClose() {
+ this.emit(import_events.Events.AndroidDevice.Close, this);
+ }
+ async shell(command) {
+ const { result } = await this._channel.shell({ command });
+ return result;
+ }
+ 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, path, options) {
+ await this._channel.push({ file: await loadFile(this._platform, file), path, mode: options ? options.mode : void 0 });
+ }
+ async launchBrowser(options = {}) {
+ const contextOptions = await (0, import_browserContext.prepareBrowserContextParams)(this._platform, options);
+ const result = await this._channel.launchBrowser(contextOptions);
+ const context = import_browserContext.BrowserContext.from(result.context);
+ const selectors = this._android._playwright.selectors;
+ selectors._contextsForSelectors.add(context);
+ context.once(import_events.Events.BrowserContext.Close, () => selectors._contextsForSelectors.delete(context));
+ await context._initializeHarFromOptions(options.recordHar);
+ return context;
+ }
+ 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 = import_waiter.Waiter.createForEvent(this, event);
+ waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`);
+ if (event !== import_events.Events.AndroidDevice.Close)
+ waiter.rejectOnEvent(this, import_events.Events.AndroidDevice.Close, () => new import_errors.TargetClosedError());
+ const result = await waiter.waitForEvent(this, event, predicate);
+ waiter.dispose();
+ return result;
+ });
+ }
+}
+class AndroidSocket extends import_channelOwner.ChannelOwner {
+ static from(androidDevice) {
+ return androidDevice._object;
+ }
+ constructor(parent, type, guid, initializer) {
+ super(parent, type, guid, initializer);
+ this._channel.on("data", ({ data }) => this.emit(import_events.Events.AndroidSocket.Data, data));
+ this._channel.on("close", () => this.emit(import_events.Events.AndroidSocket.Close));
+ }
+ async write(data) {
+ await this._channel.write({ data });
+ }
+ async close() {
+ await this._channel.close();
+ }
+ async [Symbol.asyncDispose]() {
+ await this.close();
+ }
+}
+async function loadFile(platform, file) {
+ if ((0, import_rtti.isString)(file))
+ return await platform.fs().promises.readFile(file);
+ return file;
+}
+class AndroidInput {
+ constructor(device) {
+ this._device = device;
+ }
+ async type(text) {
+ await this._device._channel.inputType({ text });
+ }
+ 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 });
+ }
+}
+function toSelectorChannel(selector) {
+ const {
+ checkable,
+ checked,
+ clazz,
+ clickable,
+ depth,
+ desc,
+ enabled,
+ focusable,
+ focused,
+ hasChild,
+ hasDescendant,
+ longClickable,
+ pkg,
+ res,
+ scrollable,
+ selected,
+ text
+ } = selector;
+ const toRegex = (value) => {
+ if (value === void 0)
+ return void 0;
+ if ((0, import_rtti.isRegExp)(value))
+ return value.source;
+ return "^" + value.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d") + "$";
+ };
+ return {
+ checkable,
+ checked,
+ clazz: toRegex(clazz),
+ pkg: toRegex(pkg),
+ desc: toRegex(desc),
+ res: toRegex(res),
+ text: toRegex(text),
+ 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
+ };
+}
+class AndroidWebView extends import_eventEmitter.EventEmitter {
+ 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 } = await this._device._channel.connectToWebView({ socketName: this._data.socketName });
+ return import_browserContext.BrowserContext.from(context).pages()[0];
+ }
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ Android,
+ AndroidDevice,
+ AndroidInput,
+ AndroidSocket,
+ AndroidWebView
+});
diff --git a/node_modules/playwright-core/lib/client/api.js b/node_modules/playwright-core/lib/client/api.js
new file mode 100644
index 0000000..eba9e2f
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/api.js
@@ -0,0 +1,137 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var api_exports = {};
+__export(api_exports, {
+ APIRequest: () => import_fetch.APIRequest,
+ APIRequestContext: () => import_fetch.APIRequestContext,
+ APIResponse: () => import_fetch.APIResponse,
+ Accessibility: () => import_accessibility.Accessibility,
+ Android: () => import_android.Android,
+ AndroidDevice: () => import_android.AndroidDevice,
+ AndroidInput: () => import_android.AndroidInput,
+ AndroidSocket: () => import_android.AndroidSocket,
+ AndroidWebView: () => import_android.AndroidWebView,
+ Browser: () => import_browser.Browser,
+ BrowserContext: () => import_browserContext.BrowserContext,
+ BrowserType: () => import_browserType.BrowserType,
+ CDPSession: () => import_cdpSession.CDPSession,
+ Clock: () => import_clock.Clock,
+ ConsoleMessage: () => import_consoleMessage.ConsoleMessage,
+ Coverage: () => import_coverage.Coverage,
+ Dialog: () => import_dialog.Dialog,
+ Download: () => import_download.Download,
+ Electron: () => import_electron.Electron,
+ ElectronApplication: () => import_electron.ElectronApplication,
+ ElementHandle: () => import_elementHandle.ElementHandle,
+ FileChooser: () => import_fileChooser.FileChooser,
+ Frame: () => import_frame.Frame,
+ FrameLocator: () => import_locator.FrameLocator,
+ JSHandle: () => import_jsHandle.JSHandle,
+ Keyboard: () => import_input.Keyboard,
+ Locator: () => import_locator.Locator,
+ Mouse: () => import_input.Mouse,
+ Page: () => import_page.Page,
+ Playwright: () => import_playwright.Playwright,
+ Request: () => import_network.Request,
+ Response: () => import_network.Response,
+ Route: () => import_network.Route,
+ Selectors: () => import_selectors.Selectors,
+ TimeoutError: () => import_errors.TimeoutError,
+ Touchscreen: () => import_input.Touchscreen,
+ Tracing: () => import_tracing.Tracing,
+ Video: () => import_video.Video,
+ WebError: () => import_webError.WebError,
+ WebSocket: () => import_network.WebSocket,
+ WebSocketRoute: () => import_network.WebSocketRoute,
+ Worker: () => import_worker.Worker
+});
+module.exports = __toCommonJS(api_exports);
+var import_accessibility = require("./accessibility");
+var import_android = require("./android");
+var import_browser = require("./browser");
+var import_browserContext = require("./browserContext");
+var import_browserType = require("./browserType");
+var import_clock = require("./clock");
+var import_consoleMessage = require("./consoleMessage");
+var import_coverage = require("./coverage");
+var import_dialog = require("./dialog");
+var import_download = require("./download");
+var import_electron = require("./electron");
+var import_locator = require("./locator");
+var import_elementHandle = require("./elementHandle");
+var import_fileChooser = require("./fileChooser");
+var import_errors = require("./errors");
+var import_frame = require("./frame");
+var import_input = require("./input");
+var import_jsHandle = require("./jsHandle");
+var import_network = require("./network");
+var import_fetch = require("./fetch");
+var import_page = require("./page");
+var import_selectors = require("./selectors");
+var import_tracing = require("./tracing");
+var import_video = require("./video");
+var import_worker = require("./worker");
+var import_cdpSession = require("./cdpSession");
+var import_playwright = require("./playwright");
+var import_webError = require("./webError");
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ APIRequest,
+ APIRequestContext,
+ APIResponse,
+ Accessibility,
+ Android,
+ AndroidDevice,
+ AndroidInput,
+ AndroidSocket,
+ AndroidWebView,
+ Browser,
+ BrowserContext,
+ BrowserType,
+ CDPSession,
+ Clock,
+ ConsoleMessage,
+ Coverage,
+ Dialog,
+ Download,
+ Electron,
+ ElectronApplication,
+ ElementHandle,
+ FileChooser,
+ Frame,
+ FrameLocator,
+ JSHandle,
+ Keyboard,
+ Locator,
+ Mouse,
+ Page,
+ Playwright,
+ Request,
+ Response,
+ Route,
+ Selectors,
+ TimeoutError,
+ Touchscreen,
+ Tracing,
+ Video,
+ WebError,
+ WebSocket,
+ WebSocketRoute,
+ Worker
+});
diff --git a/node_modules/playwright-core/lib/client/artifact.js b/node_modules/playwright-core/lib/client/artifact.js
new file mode 100644
index 0000000..d50f3ea
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/artifact.js
@@ -0,0 +1,79 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var artifact_exports = {};
+__export(artifact_exports, {
+ Artifact: () => Artifact
+});
+module.exports = __toCommonJS(artifact_exports);
+var import_channelOwner = require("./channelOwner");
+var import_stream = require("./stream");
+var import_fileUtils = require("./fileUtils");
+class Artifact extends import_channelOwner.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(path) {
+ if (!this._connection.isRemote()) {
+ await this._channel.saveAs({ path });
+ return;
+ }
+ const result = await this._channel.saveAsStream();
+ const stream = import_stream.Stream.from(result.stream);
+ await (0, import_fileUtils.mkdirIfNeeded)(this._platform, path);
+ await new Promise((resolve, reject) => {
+ stream.stream().pipe(this._platform.fs().createWriteStream(path)).on("finish", resolve).on("error", reject);
+ });
+ }
+ async failure() {
+ return (await this._channel.failure()).error || null;
+ }
+ async createReadStream() {
+ const result = await this._channel.stream();
+ const stream = import_stream.Stream.from(result.stream);
+ return stream.stream();
+ }
+ async readIntoBuffer() {
+ const stream = await this.createReadStream();
+ return await new Promise((resolve, reject) => {
+ const chunks = [];
+ stream.on("data", (chunk) => {
+ chunks.push(chunk);
+ });
+ stream.on("end", () => {
+ resolve(Buffer.concat(chunks));
+ });
+ stream.on("error", reject);
+ });
+ }
+ async cancel() {
+ return await this._channel.cancel();
+ }
+ async delete() {
+ return await this._channel.delete();
+ }
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ Artifact
+});
diff --git a/node_modules/playwright-core/lib/client/browser.js b/node_modules/playwright-core/lib/client/browser.js
new file mode 100644
index 0000000..bca696d
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/browser.js
@@ -0,0 +1,173 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var browser_exports = {};
+__export(browser_exports, {
+ Browser: () => Browser
+});
+module.exports = __toCommonJS(browser_exports);
+var import_artifact = require("./artifact");
+var import_browserContext = require("./browserContext");
+var import_cdpSession = require("./cdpSession");
+var import_channelOwner = require("./channelOwner");
+var import_errors = require("./errors");
+var import_events = require("./events");
+var import_fileUtils = require("./fileUtils");
+class Browser extends import_channelOwner.ChannelOwner {
+ constructor(parent, type, guid, initializer) {
+ super(parent, type, guid, initializer);
+ this._contexts = /* @__PURE__ */ new Set();
+ this._isConnected = true;
+ this._shouldCloseConnectionOnClose = false;
+ this._options = {};
+ this._name = initializer.name;
+ this._channel.on("context", ({ context }) => this._didCreateContext(import_browserContext.BrowserContext.from(context)));
+ this._channel.on("close", () => this._didClose());
+ this._closedPromise = new Promise((f) => this.once(import_events.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 context = [...this._contexts].find((context2) => context2._forReuse);
+ if (!context)
+ return;
+ await this._instrumentation.runBeforeCloseBrowserContext(context);
+ for (const page of context.pages())
+ page._onClose();
+ context._onClose();
+ await this._channel.disconnectFromReusedContext({ reason });
+ }
+ async _innerNewContext(options = {}, forReuse) {
+ options = this._browserType._playwright.selectors._withSelectorOptions({
+ ...this._browserType._playwright._defaultContextOptions,
+ ...options
+ });
+ const contextOptions = await (0, import_browserContext.prepareBrowserContextParams)(this._platform, options);
+ const response = forReuse ? await this._channel.newContextForReuse(contextOptions) : await this._channel.newContext(contextOptions);
+ const context = import_browserContext.BrowserContext.from(response.context);
+ if (forReuse)
+ context._forReuse = true;
+ if (options.logger)
+ context._logger = options.logger;
+ await context._initializeHarFromOptions(options.recordHar);
+ await this._instrumentation.runAfterCreateBrowserContext(context);
+ return context;
+ }
+ _connectToBrowserType(browserType, browserOptions, logger) {
+ this._browserType = browserType;
+ this._options = browserOptions;
+ this._logger = logger;
+ for (const context of this._contexts)
+ this._setupBrowserContext(context);
+ }
+ _didCreateContext(context) {
+ context._browser = this;
+ this._contexts.add(context);
+ if (this._browserType)
+ this._setupBrowserContext(context);
+ }
+ _setupBrowserContext(context) {
+ context._logger = this._logger;
+ context.tracing._tracesDir = this._options.tracesDir;
+ this._browserType._contexts.add(context);
+ this._browserType._playwright.selectors._contextsForSelectors.add(context);
+ context.setDefaultTimeout(this._browserType._playwright._defaultContextTimeout);
+ context.setDefaultNavigationTimeout(this._browserType._playwright._defaultContextNavigationTimeout);
+ }
+ contexts() {
+ return [...this._contexts];
+ }
+ version() {
+ return this._initializer.version;
+ }
+ async newPage(options = {}) {
+ return await this._wrapApiCall(async () => {
+ const context = await this.newContext(options);
+ const page = await context.newPage();
+ page._ownedContext = context;
+ context._ownerPage = page;
+ return page;
+ }, { title: "Create page" });
+ }
+ isConnected() {
+ return this._isConnected;
+ }
+ async newBrowserCDPSession() {
+ return import_cdpSession.CDPSession.from((await this._channel.newBrowserCDPSession()).session);
+ }
+ async _launchServer(options = {}) {
+ const serverLauncher = this._browserType._serverLauncher;
+ const browserImpl = this._connection.toImpl?.(this);
+ if (!serverLauncher || !browserImpl)
+ throw new Error("Launching server is not supported");
+ return await serverLauncher.launchServerOnExistingBrowser(browserImpl, {
+ _sharedBrowser: true,
+ ...options
+ });
+ }
+ async startTracing(page, options = {}) {
+ this._path = options.path;
+ await this._channel.startTracing({ ...options, page: page ? page._channel : void 0 });
+ }
+ async stopTracing() {
+ const artifact = import_artifact.Artifact.from((await this._channel.stopTracing()).artifact);
+ const buffer = await artifact.readIntoBuffer();
+ await artifact.delete();
+ if (this._path) {
+ await (0, import_fileUtils.mkdirIfNeeded)(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 ((0, import_errors.isTargetClosedError)(e))
+ return;
+ throw e;
+ }
+ }
+ _didClose() {
+ this._isConnected = false;
+ this.emit(import_events.Events.Browser.Disconnected, this);
+ }
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ Browser
+});
diff --git a/node_modules/playwright-core/lib/client/browserContext.js b/node_modules/playwright-core/lib/client/browserContext.js
new file mode 100644
index 0000000..2795fbf
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/browserContext.js
@@ -0,0 +1,535 @@
+"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 browserContext_exports = {};
+__export(browserContext_exports, {
+ BrowserContext: () => BrowserContext,
+ prepareBrowserContextParams: () => prepareBrowserContextParams,
+ toClientCertificatesProtocol: () => toClientCertificatesProtocol
+});
+module.exports = __toCommonJS(browserContext_exports);
+var import_artifact = require("./artifact");
+var import_cdpSession = require("./cdpSession");
+var import_channelOwner = require("./channelOwner");
+var import_clientHelper = require("./clientHelper");
+var import_clock = require("./clock");
+var import_consoleMessage = require("./consoleMessage");
+var import_dialog = require("./dialog");
+var import_errors = require("./errors");
+var import_events = require("./events");
+var import_fetch = require("./fetch");
+var import_frame = require("./frame");
+var import_harRouter = require("./harRouter");
+var network = __toESM(require("./network"));
+var import_page = require("./page");
+var import_tracing = require("./tracing");
+var import_waiter = require("./waiter");
+var import_webError = require("./webError");
+var import_worker = require("./worker");
+var import_timeoutSettings = require("./timeoutSettings");
+var import_fileUtils = require("./fileUtils");
+var import_headers = require("../utils/isomorphic/headers");
+var import_urlMatch = require("../utils/isomorphic/urlMatch");
+var import_rtti = require("../utils/isomorphic/rtti");
+var import_stackTrace = require("../utils/isomorphic/stackTrace");
+class BrowserContext extends import_channelOwner.ChannelOwner {
+ constructor(parent, type, guid, initializer) {
+ super(parent, type, 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._backgroundPages = /* @__PURE__ */ new Set();
+ this._serviceWorkers = /* @__PURE__ */ new Set();
+ this._harRecorders = /* @__PURE__ */ new Map();
+ this._closingStatus = "none";
+ this._harRouters = [];
+ this._options = initializer.options;
+ this._timeoutSettings = new import_timeoutSettings.TimeoutSettings(this._platform);
+ this.tracing = import_tracing.Tracing.from(initializer.tracing);
+ this.request = import_fetch.APIRequestContext.from(initializer.requestContext);
+ this.request._timeoutSettings = this._timeoutSettings;
+ this.clock = new import_clock.Clock(this);
+ this._channel.on("bindingCall", ({ binding }) => this._onBinding(import_page.BindingCall.from(binding)));
+ this._channel.on("close", () => this._onClose());
+ this._channel.on("page", ({ page }) => this._onPage(import_page.Page.from(page)));
+ this._channel.on("route", ({ route }) => this._onRoute(network.Route.from(route)));
+ this._channel.on("webSocketRoute", ({ webSocketRoute }) => this._onWebSocketRoute(network.WebSocketRoute.from(webSocketRoute)));
+ this._channel.on("backgroundPage", ({ page }) => {
+ const backgroundPage = import_page.Page.from(page);
+ this._backgroundPages.add(backgroundPage);
+ this.emit(import_events.Events.BrowserContext.BackgroundPage, backgroundPage);
+ });
+ this._channel.on("serviceWorker", ({ worker }) => {
+ const serviceWorker = import_worker.Worker.from(worker);
+ serviceWorker._context = this;
+ this._serviceWorkers.add(serviceWorker);
+ this.emit(import_events.Events.BrowserContext.ServiceWorker, serviceWorker);
+ });
+ this._channel.on("console", (event) => {
+ const consoleMessage = new import_consoleMessage.ConsoleMessage(this._platform, event);
+ this.emit(import_events.Events.BrowserContext.Console, consoleMessage);
+ const page = consoleMessage.page();
+ if (page)
+ page.emit(import_events.Events.Page.Console, consoleMessage);
+ });
+ this._channel.on("pageError", ({ error, page }) => {
+ const pageObject = import_page.Page.from(page);
+ const parsedError = (0, import_errors.parseError)(error);
+ this.emit(import_events.Events.BrowserContext.WebError, new import_webError.WebError(pageObject, parsedError));
+ if (pageObject)
+ pageObject.emit(import_events.Events.Page.PageError, parsedError);
+ });
+ this._channel.on("dialog", ({ dialog }) => {
+ const dialogObject = import_dialog.Dialog.from(dialog);
+ let hasListeners = this.emit(import_events.Events.BrowserContext.Dialog, dialogObject);
+ const page = dialogObject.page();
+ if (page)
+ hasListeners = page.emit(import_events.Events.Page.Dialog, dialogObject) || hasListeners;
+ if (!hasListeners) {
+ if (dialogObject.type() === "beforeunload")
+ dialog.accept({}).catch(() => {
+ });
+ else
+ dialog.dismiss().catch(() => {
+ });
+ }
+ });
+ this._channel.on("request", ({ request, page }) => this._onRequest(network.Request.from(request), import_page.Page.fromNullable(page)));
+ this._channel.on("requestFailed", ({ request, failureText, responseEndTiming, page }) => this._onRequestFailed(network.Request.from(request), responseEndTiming, failureText, import_page.Page.fromNullable(page)));
+ this._channel.on("requestFinished", (params) => this._onRequestFinished(params));
+ this._channel.on("response", ({ response, page }) => this._onResponse(network.Response.from(response), import_page.Page.fromNullable(page)));
+ this._channel.on("recorderEvent", ({ event, data, page, code }) => {
+ if (event === "actionAdded")
+ this._onRecorderEventSink?.actionAdded?.(import_page.Page.from(page), data, code);
+ else if (event === "actionUpdated")
+ this._onRecorderEventSink?.actionUpdated?.(import_page.Page.from(page), data, code);
+ else if (event === "signalAdded")
+ this._onRecorderEventSink?.signalAdded?.(import_page.Page.from(page), data);
+ });
+ this._closedPromise = new Promise((f) => this.once(import_events.Events.BrowserContext.Close, f));
+ this._setEventToSubscriptionMapping(/* @__PURE__ */ new Map([
+ [import_events.Events.BrowserContext.Console, "console"],
+ [import_events.Events.BrowserContext.Dialog, "dialog"],
+ [import_events.Events.BrowserContext.Request, "request"],
+ [import_events.Events.BrowserContext.Response, "response"],
+ [import_events.Events.BrowserContext.RequestFinished, "requestFinished"],
+ [import_events.Events.BrowserContext.RequestFailed, "requestFailed"]
+ ]));
+ }
+ static from(context) {
+ return context._object;
+ }
+ static fromNullable(context) {
+ return context ? BrowserContext.from(context) : null;
+ }
+ async _initializeHarFromOptions(recordHar) {
+ if (!recordHar)
+ return;
+ const defaultContent = recordHar.path.endsWith(".zip") ? "attach" : "embed";
+ await this._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(import_events.Events.BrowserContext.Page, page);
+ if (page._opener && !page._opener.isClosed())
+ page._opener.emit(import_events.Events.Page.Popup, page);
+ }
+ _onRequest(request, page) {
+ this.emit(import_events.Events.BrowserContext.Request, request);
+ if (page)
+ page.emit(import_events.Events.Page.Request, request);
+ }
+ _onResponse(response, page) {
+ this.emit(import_events.Events.BrowserContext.Response, response);
+ if (page)
+ page.emit(import_events.Events.Page.Response, response);
+ }
+ _onRequestFailed(request, responseEndTiming, failureText, page) {
+ request._failureText = failureText || null;
+ request._setResponseEndTiming(responseEndTiming);
+ this.emit(import_events.Events.BrowserContext.RequestFailed, request);
+ if (page)
+ page.emit(import_events.Events.Page.RequestFailed, request);
+ }
+ _onRequestFinished(params) {
+ const { responseEndTiming } = params;
+ const request = network.Request.from(params.request);
+ const response = network.Response.fromNullable(params.response);
+ const page = import_page.Page.fromNullable(params.page);
+ request._setResponseEndTiming(responseEndTiming);
+ this.emit(import_events.Events.BrowserContext.RequestFinished, request);
+ if (page)
+ page.emit(import_events.Events.Page.RequestFinished, request);
+ if (response)
+ response._finishedPromise.resolve(null);
+ }
+ async _onRoute(route) {
+ route._context = this;
+ const page = route.request()._safePage();
+ const routeHandlers = this._routes.slice();
+ for (const routeHandler of routeHandlers) {
+ if (page?._closeWasCalled || this._closingStatus !== "none")
+ return;
+ if (!routeHandler.matches(route.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(route);
+ if (!this._routes.length)
+ this._updateInterceptionPatterns({ internal: true }).catch(() => {
+ });
+ if (handled)
+ return;
+ }
+ await route._innerContinue(
+ true
+ /* isFallback */
+ ).catch(() => {
+ });
+ }
+ async _onWebSocketRoute(webSocketRoute) {
+ const routeHandler = this._webSocketRoutes.find((route) => route.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);
+ }
+ setDefaultNavigationTimeout(timeout) {
+ this._timeoutSettings.setDefaultNavigationTimeout(timeout);
+ }
+ setDefaultTimeout(timeout) {
+ this._timeoutSettings.setDefaultTimeout(timeout);
+ }
+ browser() {
+ return this._browser;
+ }
+ pages() {
+ return [...this._pages];
+ }
+ async newPage() {
+ if (this._ownerPage)
+ throw new Error("Please use browser.newContext()");
+ return import_page.Page.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: (0, import_rtti.isString)(options.name) ? options.name : void 0,
+ nameRegexSource: (0, import_rtti.isRegExp)(options.name) ? options.name.source : void 0,
+ nameRegexFlags: (0, import_rtti.isRegExp)(options.name) ? options.name.flags : void 0,
+ domain: (0, import_rtti.isString)(options.domain) ? options.domain : void 0,
+ domainRegexSource: (0, import_rtti.isRegExp)(options.domain) ? options.domain.source : void 0,
+ domainRegexFlags: (0, import_rtti.isRegExp)(options.domain) ? options.domain.flags : void 0,
+ path: (0, import_rtti.isString)(options.path) ? options.path : void 0,
+ pathRegexSource: (0, import_rtti.isRegExp)(options.path) ? options.path.source : void 0,
+ pathRegexFlags: (0, import_rtti.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) {
+ network.validateHeaders(headers);
+ await this._channel.setExtraHTTPHeaders({ headers: (0, import_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 source = await (0, import_clientHelper.evaluationScript)(this._platform, script, arg);
+ await this._channel.addInitScript({ source });
+ }
+ async exposeBinding(name, callback, options = {}) {
+ await this._channel.exposeBinding({ name, needsHandle: options.handle });
+ this._bindings.set(name, callback);
+ }
+ async exposeFunction(name, callback) {
+ await this._channel.exposeBinding({ name });
+ const binding = (source, ...args) => callback(...args);
+ this._bindings.set(name, binding);
+ }
+ async route(url, handler, options = {}) {
+ this._routes.unshift(new network.RouteHandler(this._platform, this._options.baseURL, url, handler, options.times));
+ await this._updateInterceptionPatterns({ title: "Route requests" });
+ }
+ async routeWebSocket(url, handler) {
+ this._webSocketRoutes.unshift(new network.WebSocketRouteHandler(this._options.baseURL, url, handler));
+ await this._updateWebSocketInterceptionPatterns({ title: "Route WebSockets" });
+ }
+ async _recordIntoHAR(har, page, options = {}) {
+ const { harId } = await this._channel.harStart({
+ page: page?._channel,
+ options: {
+ zip: har.endsWith(".zip"),
+ content: options.updateContent ?? "attach",
+ urlGlob: (0, import_rtti.isString)(options.url) ? options.url : void 0,
+ urlRegexSource: (0, import_rtti.isRegExp)(options.url) ? options.url.source : void 0,
+ urlRegexFlags: (0, import_rtti.isRegExp)(options.url) ? options.url.flags : void 0,
+ mode: options.updateMode ?? "minimal"
+ }
+ });
+ this._harRecorders.set(harId, { path: har, content: options.updateContent ?? "attach" });
+ }
+ 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._recordIntoHAR(har, null, options);
+ return;
+ }
+ const harRouter = await import_harRouter.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(url, handler) {
+ const removed = [];
+ const remaining = [];
+ for (const route of this._routes) {
+ if ((0, import_urlMatch.urlMatchesEqual)(route.url, url) && (!handler || route.handler === handler))
+ removed.push(route);
+ else
+ remaining.push(route);
+ }
+ 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 = network.RouteHandler.prepareInterceptionPatterns(this._routes);
+ await this._wrapApiCall(() => this._channel.setNetworkInterceptionPatterns({ patterns }), options);
+ }
+ async _updateWebSocketInterceptionPatterns(options) {
+ const patterns = network.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 = import_waiter.Waiter.createForEvent(this, event);
+ waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`);
+ if (event !== import_events.Events.BrowserContext.Close)
+ waiter.rejectOnEvent(this, import_events.Events.BrowserContext.Close, () => new import_errors.TargetClosedError(this._effectiveCloseReason()));
+ const result = await waiter.waitForEvent(this, event, predicate);
+ waiter.dispose();
+ return result;
+ });
+ }
+ async storageState(options = {}) {
+ const state = await this._channel.storageState({ indexedDB: options.indexedDB });
+ if (options.path) {
+ await (0, import_fileUtils.mkdirIfNeeded)(this._platform, options.path);
+ await this._platform.fs().promises.writeFile(options.path, JSON.stringify(state, void 0, 2), "utf8");
+ }
+ return state;
+ }
+ backgroundPages() {
+ return [...this._backgroundPages];
+ }
+ serviceWorkers() {
+ return [...this._serviceWorkers];
+ }
+ async newCDPSession(page) {
+ if (!(page instanceof import_page.Page) && !(page instanceof import_frame.Frame))
+ throw new Error("page: expected Page or Frame");
+ const result = await this._channel.newCDPSession(page instanceof import_page.Page ? { page: page._channel } : { frame: page._channel });
+ return import_cdpSession.CDPSession.from(result.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(import_events.Events.BrowserContext.Close, this);
+ }
+ async [Symbol.asyncDispose]() {
+ await this.close();
+ }
+ async close(options = {}) {
+ if (this._closingStatus !== "none")
+ return;
+ this._closeReason = options.reason;
+ this._closingStatus = "closing";
+ await this.request.dispose(options);
+ await this._instrumentation.runBeforeCloseBrowserContext(this);
+ await this._wrapApiCall(async () => {
+ for (const [harId, harParams] of this._harRecorders) {
+ const har = await this._channel.harExport({ harId });
+ const artifact = import_artifact.Artifact.from(har.artifact);
+ const isCompressed = harParams.content === "attach" || harParams.path.endsWith(".zip");
+ const needCompressed = harParams.path.endsWith(".zip");
+ if (isCompressed && !needCompressed) {
+ 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 });
+ } else {
+ await artifact.saveAs(harParams.path);
+ }
+ await artifact.delete();
+ }
+ }, { internal: true });
+ await this._channel.close(options);
+ await this._closedPromise;
+ }
+ async _enableRecorder(params, eventSink) {
+ if (eventSink)
+ this._onRecorderEventSink = eventSink;
+ await this._channel.enableRecorder(params);
+ }
+ async _disableRecorder() {
+ this._onRecorderEventSink = void 0;
+ await this._channel.disableRecorder();
+ }
+}
+async function prepareStorageState(platform, storageState) {
+ if (typeof storageState !== "string")
+ return storageState;
+ try {
+ return JSON.parse(await platform.fs().promises.readFile(storageState, "utf8"));
+ } catch (e) {
+ (0, import_stackTrace.rewriteErrorMessage)(e, `Error reading storage state from ${storageState}:
+` + e.message);
+ throw e;
+ }
+}
+async function prepareBrowserContextParams(platform, options) {
+ if (options.videoSize && !options.videosPath)
+ throw new Error(`"videoSize" option requires "videosPath" to be specified`);
+ if (options.extraHTTPHeaders)
+ network.validateHeaders(options.extraHTTPHeaders);
+ const contextParams = {
+ ...options,
+ viewport: options.viewport === null ? void 0 : options.viewport,
+ noDefaultViewport: options.viewport === null,
+ extraHTTPHeaders: options.extraHTTPHeaders ? (0, import_headers.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 && options.videosPath) {
+ contextParams.recordVideo = {
+ dir: options.videosPath,
+ size: options.videoSize
+ };
+ }
+ 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 (value, path) => {
+ if (value)
+ return value;
+ if (path)
+ return await platform.fs().promises.readFile(path);
+ };
+ 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
+ })));
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ BrowserContext,
+ prepareBrowserContextParams,
+ toClientCertificatesProtocol
+});
diff --git a/node_modules/playwright-core/lib/client/browserType.js b/node_modules/playwright-core/lib/client/browserType.js
new file mode 100644
index 0000000..6ab863f
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/browserType.js
@@ -0,0 +1,184 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var browserType_exports = {};
+__export(browserType_exports, {
+ BrowserType: () => BrowserType
+});
+module.exports = __toCommonJS(browserType_exports);
+var import_browser = require("./browser");
+var import_browserContext = require("./browserContext");
+var import_channelOwner = require("./channelOwner");
+var import_clientHelper = require("./clientHelper");
+var import_events = require("./events");
+var import_assert = require("../utils/isomorphic/assert");
+var import_headers = require("../utils/isomorphic/headers");
+var import_time = require("../utils/isomorphic/time");
+var import_timeoutRunner = require("../utils/isomorphic/timeoutRunner");
+var import_webSocket = require("./webSocket");
+var import_timeoutSettings = require("./timeoutSettings");
+class BrowserType extends import_channelOwner.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 = {}) {
+ (0, import_assert.assert)(!options.userDataDir, "userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead");
+ (0, import_assert.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 ? (0, import_clientHelper.envObjectToArray)(options.env) : void 0,
+ timeout: new import_timeoutSettings.TimeoutSettings(this._platform).launchTimeout(options)
+ };
+ return await this._wrapApiCall(async () => {
+ const browser = import_browser.Browser.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 = {}) {
+ const logger = options.logger || this._playwright._defaultLaunchOptions?.logger;
+ (0, import_assert.assert)(!options.port, "Cannot specify a port without launching as a server.");
+ options = this._playwright.selectors._withSelectorOptions({
+ ...this._playwright._defaultLaunchOptions,
+ ...this._playwright._defaultContextOptions,
+ ...options
+ });
+ const contextParams = await (0, import_browserContext.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 ? (0, import_clientHelper.envObjectToArray)(options.env) : void 0,
+ channel: options.channel,
+ userDataDir: this._platform.path().isAbsolute(userDataDir) || !userDataDir ? userDataDir : this._platform.path().resolve(userDataDir),
+ timeout: new import_timeoutSettings.TimeoutSettings(this._platform).launchTimeout(options)
+ };
+ const context = await this._wrapApiCall(async () => {
+ const result = await this._channel.launchPersistentContext(persistentParams);
+ const browser = import_browser.Browser.from(result.browser);
+ browser._connectToBrowserType(this, options, logger);
+ const context2 = import_browserContext.BrowserContext.from(result.context);
+ await context2._initializeHarFromOptions(options.recordHar);
+ return context2;
+ });
+ await this._instrumentation.runAfterCreateBrowserContext(context);
+ return context;
+ }
+ async connect(optionsOrWsEndpoint, options) {
+ if (typeof optionsOrWsEndpoint === "string")
+ return await this._connect({ ...options, wsEndpoint: optionsOrWsEndpoint });
+ (0, import_assert.assert)(optionsOrWsEndpoint.wsEndpoint, "options.wsEndpoint is required");
+ return await this._connect(optionsOrWsEndpoint);
+ }
+ async _connect(params) {
+ const logger = params.logger;
+ return await this._wrapApiCall(async () => {
+ const deadline = params.timeout ? (0, import_time.monotonicTime)() + params.timeout : 0;
+ const headers = { "x-playwright-browser": this.name(), ...params.headers };
+ const connectParams = {
+ wsEndpoint: params.wsEndpoint,
+ headers,
+ exposeNetwork: params.exposeNetwork ?? params._exposeNetwork,
+ slowMo: params.slowMo,
+ timeout: params.timeout || 0
+ };
+ if (params.__testHookRedirectPortForwarding)
+ connectParams.socksProxyRedirectPortForTest = params.__testHookRedirectPortForwarding;
+ const connection = await (0, import_webSocket.connectOverWebSocket)(this._connection, connectParams);
+ let browser;
+ connection.on("close", () => {
+ for (const context of browser?.contexts() || []) {
+ for (const page of context.pages())
+ page._onClose();
+ context._onClose();
+ }
+ setTimeout(() => browser?._didClose(), 0);
+ });
+ const result = await (0, import_timeoutRunner.raceAgainstDeadline)(async () => {
+ if (params.__testHookBeforeCreateBrowser)
+ await params.__testHookBeforeCreateBrowser();
+ const playwright = await connection.initializePlaywright();
+ if (!playwright._initializer.preLaunchedBrowser) {
+ connection.close();
+ throw new Error("Malformed endpoint. Did you use BrowserType.launchServer method?");
+ }
+ playwright.selectors = this._playwright.selectors;
+ browser = import_browser.Browser.from(playwright._initializer.preLaunchedBrowser);
+ browser._connectToBrowserType(this, {}, logger);
+ browser._shouldCloseConnectionOnClose = true;
+ browser.on(import_events.Events.Browser.Disconnected, () => connection.close());
+ return browser;
+ }, deadline);
+ if (!result.timedOut) {
+ return result.result;
+ } else {
+ connection.close();
+ throw new Error(`Timeout ${params.timeout}ms exceeded`);
+ }
+ });
+ }
+ async connectOverCDP(endpointURLOrOptions, options) {
+ if (typeof endpointURLOrOptions === "string")
+ return await this._connectOverCDP(endpointURLOrOptions, options);
+ const endpointURL = "endpointURL" in endpointURLOrOptions ? endpointURLOrOptions.endpointURL : endpointURLOrOptions.wsEndpoint;
+ (0, import_assert.assert)(endpointURL, "Cannot connect over CDP without wsEndpoint.");
+ return await this.connectOverCDP(endpointURL, endpointURLOrOptions);
+ }
+ async _connectOverCDP(endpointURL, params = {}) {
+ if (this.name() !== "chromium")
+ throw new Error("Connecting over CDP is only supported in Chromium.");
+ const headers = params.headers ? (0, import_headers.headersObjectToArray)(params.headers) : void 0;
+ const result = await this._channel.connectOverCDP({
+ endpointURL,
+ headers,
+ slowMo: params.slowMo,
+ timeout: new import_timeoutSettings.TimeoutSettings(this._platform).timeout(params)
+ });
+ const browser = import_browser.Browser.from(result.browser);
+ browser._connectToBrowserType(this, {}, params.logger);
+ if (result.defaultContext)
+ await this._instrumentation.runAfterCreateBrowserContext(import_browserContext.BrowserContext.from(result.defaultContext));
+ return browser;
+ }
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ BrowserType
+});
diff --git a/node_modules/playwright-core/lib/client/cdpSession.js b/node_modules/playwright-core/lib/client/cdpSession.js
new file mode 100644
index 0000000..f23a7db
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/cdpSession.js
@@ -0,0 +1,51 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var cdpSession_exports = {};
+__export(cdpSession_exports, {
+ CDPSession: () => CDPSession
+});
+module.exports = __toCommonJS(cdpSession_exports);
+var import_channelOwner = require("./channelOwner");
+class CDPSession extends import_channelOwner.ChannelOwner {
+ static from(cdpSession) {
+ return cdpSession._object;
+ }
+ constructor(parent, type, guid, initializer) {
+ super(parent, type, guid, initializer);
+ this._channel.on("event", ({ method, params }) => {
+ this.emit(method, params);
+ });
+ this.on = super.on;
+ this.addListener = super.addListener;
+ this.off = super.removeListener;
+ this.removeListener = super.removeListener;
+ this.once = super.once;
+ }
+ async send(method, params) {
+ const result = await this._channel.send({ method, params });
+ return result.result;
+ }
+ async detach() {
+ return await this._channel.detach();
+ }
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ CDPSession
+});
diff --git a/node_modules/playwright-core/lib/client/channelOwner.js b/node_modules/playwright-core/lib/client/channelOwner.js
new file mode 100644
index 0000000..6cae061
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/channelOwner.js
@@ -0,0 +1,201 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var channelOwner_exports = {};
+__export(channelOwner_exports, {
+ ChannelOwner: () => ChannelOwner
+});
+module.exports = __toCommonJS(channelOwner_exports);
+var import_eventEmitter = require("./eventEmitter");
+var import_validator = require("../protocol/validator");
+var import_protocolMetainfo = require("../utils/isomorphic/protocolMetainfo");
+var import_clientStackTrace = require("./clientStackTrace");
+var import_stackTrace = require("../utils/isomorphic/stackTrace");
+class ChannelOwner extends import_eventEmitter.EventEmitter {
+ constructor(parent, type, 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 = type;
+ 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 import_eventEmitter.EventEmitter(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 = (0, import_validator.maybeFindValidator)(this._type, prop, "Params");
+ const { internal } = import_protocolMetainfo.methodMetainfo.get(this._type + "." + prop) || {};
+ if (validator) {
+ return async (params) => {
+ return await this._wrapApiCall(async (apiZone) => {
+ const validatedParams = validator(params, "", this._validatorToWireContext());
+ if (!apiZone.internal && !apiZone.reported) {
+ apiZone.reported = true;
+ this._instrumentation.onApiCallBegin(apiZone, { type: this._type, method: prop, params });
+ 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 = (0, import_clientStackTrace.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 result = 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 result;
+ } 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" + (0, import_stackTrace.stringifyStackFrames)(stackTrace.frames).join("\n") + innerError;
+ if (stackFrames.trim())
+ e.stack = e.message + stackFrames;
+ else
+ e.stack = "";
+ if (!options?.internal) {
+ const recoveryHandlers = [];
+ apiZone.error = e;
+ this._instrumentation.onApiCallRecovery(apiZone, e, recoveryHandlers);
+ for (const handler of recoveryHandlers) {
+ const recoverResult = await handler();
+ if (recoverResult.status === "recovered")
+ return recoverResult.value;
+ }
+ logApiCall(this._platform, logger, `<= ${apiZone.apiName} failed`);
+ this._instrumentation.onApiCallEnd(apiZone);
+ }
+ throw e;
+ }
+ }
+ toJSON() {
+ return {
+ _type: this._type,
+ _guid: this._guid
+ };
+ }
+}
+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, path, context) {
+ if (arg._object instanceof ChannelOwner && (names === "*" || names.includes(arg._object._type)))
+ return { guid: arg._object._guid };
+ throw new import_validator.ValidationError(`${path}: expected channel ${names.toString()}`);
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ ChannelOwner
+});
diff --git a/node_modules/playwright-core/lib/client/clientHelper.js b/node_modules/playwright-core/lib/client/clientHelper.js
new file mode 100644
index 0000000..fc215ee
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/clientHelper.js
@@ -0,0 +1,64 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var clientHelper_exports = {};
+__export(clientHelper_exports, {
+ addSourceUrlToScript: () => addSourceUrlToScript,
+ envObjectToArray: () => envObjectToArray,
+ evaluationScript: () => evaluationScript
+});
+module.exports = __toCommonJS(clientHelper_exports);
+var import_rtti = require("../utils/isomorphic/rtti");
+function envObjectToArray(env) {
+ const result = [];
+ for (const name in env) {
+ if (!Object.is(env[name], void 0))
+ result.push({ name, value: String(env[name]) });
+ }
+ return result;
+}
+async function evaluationScript(platform, fun, arg, addSourceUrl = true) {
+ if (typeof fun === "function") {
+ const source = fun.toString();
+ const argString = Object.is(arg, void 0) ? "undefined" : JSON.stringify(arg);
+ return `(${source})(${argString})`;
+ }
+ if (arg !== void 0)
+ throw new Error("Cannot evaluate a string with arguments");
+ if ((0, import_rtti.isString)(fun))
+ return fun;
+ if (fun.content !== void 0)
+ return fun.content;
+ if (fun.path !== void 0) {
+ let source = await platform.fs().promises.readFile(fun.path, "utf8");
+ if (addSourceUrl)
+ source = addSourceUrlToScript(source, fun.path);
+ return source;
+ }
+ throw new Error("Either path or content property must be present");
+}
+function addSourceUrlToScript(source, path) {
+ return `${source}
+//# sourceURL=${path.replace(/\n/g, "")}`;
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ addSourceUrlToScript,
+ envObjectToArray,
+ evaluationScript
+});
diff --git a/node_modules/playwright-core/lib/client/clientInstrumentation.js b/node_modules/playwright-core/lib/client/clientInstrumentation.js
new file mode 100644
index 0000000..7140061
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/clientInstrumentation.js
@@ -0,0 +1,55 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var clientInstrumentation_exports = {};
+__export(clientInstrumentation_exports, {
+ createInstrumentation: () => createInstrumentation
+});
+module.exports = __toCommonJS(clientInstrumentation_exports);
+function createInstrumentation() {
+ 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 (...params) => {
+ for (const listener of listeners)
+ await listener[prop]?.(...params);
+ };
+ }
+ if (prop.startsWith("on")) {
+ return (...params) => {
+ for (const listener of listeners)
+ listener[prop]?.(...params);
+ };
+ }
+ return obj[prop];
+ }
+ });
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ createInstrumentation
+});
diff --git a/node_modules/playwright-core/lib/client/clientStackTrace.js b/node_modules/playwright-core/lib/client/clientStackTrace.js
new file mode 100644
index 0000000..973fc1a
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/clientStackTrace.js
@@ -0,0 +1,69 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var clientStackTrace_exports = {};
+__export(clientStackTrace_exports, {
+ captureLibraryStackTrace: () => captureLibraryStackTrace
+});
+module.exports = __toCommonJS(clientStackTrace_exports);
+var import_stackTrace = require("../utils/isomorphic/stackTrace");
+function captureLibraryStackTrace(platform) {
+ const stack = (0, import_stackTrace.captureRawStack)();
+ let parsedFrames = stack.map((line) => {
+ const frame = (0, import_stackTrace.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])(.*)/);
+ if (!match)
+ return name;
+ return match[1].toLowerCase() + match[2];
+ }
+ 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
+ };
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ captureLibraryStackTrace
+});
diff --git a/node_modules/playwright-core/lib/client/clock.js b/node_modules/playwright-core/lib/client/clock.js
new file mode 100644
index 0000000..71faaf9
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/clock.js
@@ -0,0 +1,68 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var clock_exports = {};
+__export(clock_exports, {
+ Clock: () => Clock
+});
+module.exports = __toCommonJS(clock_exports);
+class Clock {
+ constructor(browserContext) {
+ this._browserContext = browserContext;
+ }
+ async install(options = {}) {
+ await this._browserContext._channel.clockInstall(options.time !== void 0 ? parseTime(options.time) : {});
+ }
+ async fastForward(ticks) {
+ await this._browserContext._channel.clockFastForward(parseTicks(ticks));
+ }
+ async pauseAt(time) {
+ await this._browserContext._channel.clockPauseAt(parseTime(time));
+ }
+ async resume() {
+ await this._browserContext._channel.clockResume({});
+ }
+ async runFor(ticks) {
+ await this._browserContext._channel.clockRunFor(parseTicks(ticks));
+ }
+ async setFixedTime(time) {
+ await this._browserContext._channel.clockSetFixedTime(parseTime(time));
+ }
+ async setSystemTime(time) {
+ await this._browserContext._channel.clockSetSystemTime(parseTime(time));
+ }
+}
+function parseTime(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 parseTicks(ticks) {
+ return {
+ ticksNumber: typeof ticks === "number" ? ticks : void 0,
+ ticksString: typeof ticks === "string" ? ticks : void 0
+ };
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ Clock
+});
diff --git a/node_modules/playwright-core/lib/client/connection.js b/node_modules/playwright-core/lib/client/connection.js
new file mode 100644
index 0000000..7898e39
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/connection.js
@@ -0,0 +1,314 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var connection_exports = {};
+__export(connection_exports, {
+ Connection: () => Connection
+});
+module.exports = __toCommonJS(connection_exports);
+var import_eventEmitter = require("./eventEmitter");
+var import_android = require("./android");
+var import_artifact = require("./artifact");
+var import_browser = require("./browser");
+var import_browserContext = require("./browserContext");
+var import_browserType = require("./browserType");
+var import_cdpSession = require("./cdpSession");
+var import_channelOwner = require("./channelOwner");
+var import_clientInstrumentation = require("./clientInstrumentation");
+var import_dialog = require("./dialog");
+var import_electron = require("./electron");
+var import_elementHandle = require("./elementHandle");
+var import_errors = require("./errors");
+var import_fetch = require("./fetch");
+var import_frame = require("./frame");
+var import_jsHandle = require("./jsHandle");
+var import_jsonPipe = require("./jsonPipe");
+var import_localUtils = require("./localUtils");
+var import_network = require("./network");
+var import_page = require("./page");
+var import_playwright = require("./playwright");
+var import_stream = require("./stream");
+var import_tracing = require("./tracing");
+var import_worker = require("./worker");
+var import_writableStream = require("./writableStream");
+var import_validator = require("../protocol/validator");
+var import_stackTrace = require("../utils/isomorphic/stackTrace");
+class Root extends import_channelOwner.ChannelOwner {
+ constructor(connection) {
+ super(connection, "Root", "", {});
+ }
+ async initialize() {
+ return import_playwright.Playwright.from((await this._channel.initialize({
+ sdkLanguage: "javascript"
+ })).playwright);
+ }
+}
+class DummyChannelOwner extends import_channelOwner.ChannelOwner {
+}
+class Connection extends import_eventEmitter.EventEmitter {
+ 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._instrumentation = instrumentation || (0, import_clientInstrumentation.createInstrumentation)();
+ this._localUtils = localUtils;
+ this._rootObject = new Root(this);
+ this.headers = headers;
+ }
+ 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, params, options) {
+ 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 type = object._type;
+ const id = ++this._lastId;
+ const message = { id, guid, method, params };
+ if (this._platform.isLogEnabled("channel")) {
+ this._platform.log("channel", "SEND> " + JSON.stringify(message));
+ }
+ const location = 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, internal: options.internal, stepId: options.stepId };
+ if (this._tracingCount && options.frames && type !== "LocalUtils")
+ this._localUtils?.addStackToTracingNoReply({ callData: { stack: options.frames ?? [], id } }).catch(() => {
+ });
+ this._platform.zones.empty.run(() => this.onmessage({ ...message, metadata }));
+ return await new Promise((resolve, reject) => this._callbacks.set(id, { resolve, reject, title: options.title, type, 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, result, error, log } = message;
+ if (id) {
+ if (this._platform.isLogEnabled("channel"))
+ this._platform.log("channel", " !!l))
+ return "";
+ return `
+Call log:
+${platform.colors.dim(log.join("\n"))}
+`;
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ Connection
+});
diff --git a/node_modules/playwright-core/lib/client/consoleMessage.js b/node_modules/playwright-core/lib/client/consoleMessage.js
new file mode 100644
index 0000000..03e17ab
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/consoleMessage.js
@@ -0,0 +1,55 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var consoleMessage_exports = {};
+__export(consoleMessage_exports, {
+ ConsoleMessage: () => ConsoleMessage
+});
+module.exports = __toCommonJS(consoleMessage_exports);
+var import_jsHandle = require("./jsHandle");
+var import_page = require("./page");
+class ConsoleMessage {
+ constructor(platform, event) {
+ this._page = "page" in event && event.page ? import_page.Page.from(event.page) : null;
+ this._event = event;
+ if (platform.inspectCustom)
+ this[platform.inspectCustom] = () => this._inspect();
+ }
+ page() {
+ return this._page;
+ }
+ type() {
+ return this._event.type;
+ }
+ text() {
+ return this._event.text;
+ }
+ args() {
+ return this._event.args.map(import_jsHandle.JSHandle.from);
+ }
+ location() {
+ return this._event.location;
+ }
+ _inspect() {
+ return this.text();
+ }
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ ConsoleMessage
+});
diff --git a/node_modules/playwright-core/lib/client/coverage.js b/node_modules/playwright-core/lib/client/coverage.js
new file mode 100644
index 0000000..737c042
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/coverage.js
@@ -0,0 +1,44 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var coverage_exports = {};
+__export(coverage_exports, {
+ Coverage: () => Coverage
+});
+module.exports = __toCommonJS(coverage_exports);
+class Coverage {
+ 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;
+ }
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ Coverage
+});
diff --git a/node_modules/playwright-core/lib/client/dialog.js b/node_modules/playwright-core/lib/client/dialog.js
new file mode 100644
index 0000000..ef1541a
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/dialog.js
@@ -0,0 +1,56 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var dialog_exports = {};
+__export(dialog_exports, {
+ Dialog: () => Dialog
+});
+module.exports = __toCommonJS(dialog_exports);
+var import_channelOwner = require("./channelOwner");
+var import_page = require("./page");
+class Dialog extends import_channelOwner.ChannelOwner {
+ static from(dialog) {
+ return dialog._object;
+ }
+ constructor(parent, type, guid, initializer) {
+ super(parent, type, guid, initializer);
+ this._page = import_page.Page.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() {
+ await this._channel.dismiss();
+ }
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ Dialog
+});
diff --git a/node_modules/playwright-core/lib/client/download.js b/node_modules/playwright-core/lib/client/download.js
new file mode 100644
index 0000000..1c02e25
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/download.js
@@ -0,0 +1,62 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var download_exports = {};
+__export(download_exports, {
+ Download: () => Download
+});
+module.exports = __toCommonJS(download_exports);
+class Download {
+ constructor(page, url, suggestedFilename, artifact) {
+ this._page = page;
+ this._url = url;
+ 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(path) {
+ return await this._artifact.saveAs(path);
+ }
+ 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();
+ }
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ Download
+});
diff --git a/node_modules/playwright-core/lib/client/electron.js b/node_modules/playwright-core/lib/client/electron.js
new file mode 100644
index 0000000..2ef9958
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/electron.js
@@ -0,0 +1,138 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var electron_exports = {};
+__export(electron_exports, {
+ Electron: () => Electron,
+ ElectronApplication: () => ElectronApplication
+});
+module.exports = __toCommonJS(electron_exports);
+var import_browserContext = require("./browserContext");
+var import_channelOwner = require("./channelOwner");
+var import_clientHelper = require("./clientHelper");
+var import_consoleMessage = require("./consoleMessage");
+var import_errors = require("./errors");
+var import_events = require("./events");
+var import_jsHandle = require("./jsHandle");
+var import_waiter = require("./waiter");
+var import_timeoutSettings = require("./timeoutSettings");
+class Electron extends import_channelOwner.ChannelOwner {
+ static from(electron) {
+ return electron._object;
+ }
+ constructor(parent, type, guid, initializer) {
+ super(parent, type, guid, initializer);
+ }
+ async launch(options = {}) {
+ options = this._playwright.selectors._withSelectorOptions(options);
+ const params = {
+ ...await (0, import_browserContext.prepareBrowserContextParams)(this._platform, options),
+ env: (0, import_clientHelper.envObjectToArray)(options.env ? options.env : this._platform.env),
+ tracesDir: options.tracesDir,
+ timeout: new import_timeoutSettings.TimeoutSettings(this._platform).launchTimeout(options)
+ };
+ const app = ElectronApplication.from((await this._channel.launch(params)).electronApplication);
+ this._playwright.selectors._contextsForSelectors.add(app._context);
+ app.once(import_events.Events.ElectronApplication.Close, () => this._playwright.selectors._contextsForSelectors.delete(app._context));
+ await app._context._initializeHarFromOptions(options.recordHar);
+ app._context.tracing._tracesDir = options.tracesDir;
+ return app;
+ }
+}
+class ElectronApplication extends import_channelOwner.ChannelOwner {
+ constructor(parent, type, guid, initializer) {
+ super(parent, type, guid, initializer);
+ this._windows = /* @__PURE__ */ new Set();
+ this._timeoutSettings = new import_timeoutSettings.TimeoutSettings(this._platform);
+ this._context = import_browserContext.BrowserContext.from(initializer.context);
+ for (const page of this._context._pages)
+ this._onPage(page);
+ this._context.on(import_events.Events.BrowserContext.Page, (page) => this._onPage(page));
+ this._channel.on("close", () => {
+ this.emit(import_events.Events.ElectronApplication.Close);
+ });
+ this._channel.on("console", (event) => this.emit(import_events.Events.ElectronApplication.Console, new import_consoleMessage.ConsoleMessage(this._platform, event)));
+ this._setEventToSubscriptionMapping(/* @__PURE__ */ new Map([
+ [import_events.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(import_events.Events.ElectronApplication.Window, page);
+ page.once(import_events.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 ((0, import_errors.isTargetClosedError)(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 = import_waiter.Waiter.createForEvent(this, event);
+ waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`);
+ if (event !== import_events.Events.ElectronApplication.Close)
+ waiter.rejectOnEvent(this, import_events.Events.ElectronApplication.Close, () => new import_errors.TargetClosedError());
+ const result = await waiter.waitForEvent(this, event, predicate);
+ waiter.dispose();
+ return result;
+ });
+ }
+ async browserWindow(page) {
+ const result = await this._channel.browserWindow({ page: page._channel });
+ return import_jsHandle.JSHandle.from(result.handle);
+ }
+ async evaluate(pageFunction, arg) {
+ const result = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: (0, import_jsHandle.serializeArgument)(arg) });
+ return (0, import_jsHandle.parseResult)(result.value);
+ }
+ async evaluateHandle(pageFunction, arg) {
+ const result = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: (0, import_jsHandle.serializeArgument)(arg) });
+ return import_jsHandle.JSHandle.from(result.handle);
+ }
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ Electron,
+ ElectronApplication
+});
diff --git a/node_modules/playwright-core/lib/client/elementHandle.js b/node_modules/playwright-core/lib/client/elementHandle.js
new file mode 100644
index 0000000..1db5163
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/elementHandle.js
@@ -0,0 +1,281 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var elementHandle_exports = {};
+__export(elementHandle_exports, {
+ ElementHandle: () => ElementHandle,
+ convertInputFiles: () => convertInputFiles,
+ convertSelectOptionValues: () => convertSelectOptionValues,
+ determineScreenshotType: () => determineScreenshotType
+});
+module.exports = __toCommonJS(elementHandle_exports);
+var import_frame = require("./frame");
+var import_jsHandle = require("./jsHandle");
+var import_assert = require("../utils/isomorphic/assert");
+var import_fileUtils = require("./fileUtils");
+var import_rtti = require("../utils/isomorphic/rtti");
+var import_writableStream = require("./writableStream");
+var import_mimeType = require("../utils/isomorphic/mimeType");
+class ElementHandle extends import_jsHandle.JSHandle {
+ static from(handle) {
+ return handle._object;
+ }
+ static fromNullable(handle) {
+ return handle ? ElementHandle.from(handle) : null;
+ }
+ constructor(parent, type, guid, initializer) {
+ super(parent, type, guid, initializer);
+ this._frame = parent;
+ this._elementChannel = this._channel;
+ }
+ asElement() {
+ return this;
+ }
+ async ownerFrame() {
+ return import_frame.Frame.fromNullable((await this._elementChannel.ownerFrame()).frame);
+ }
+ async contentFrame() {
+ return import_frame.Frame.fromNullable((await this._elementChannel.contentFrame()).frame);
+ }
+ async getAttribute(name) {
+ const value = (await this._elementChannel.getAttribute({ name })).value;
+ return value === void 0 ? null : value;
+ }
+ async inputValue() {
+ return (await this._elementChannel.inputValue()).value;
+ }
+ async textContent() {
+ const value = (await this._elementChannel.textContent()).value;
+ return value === void 0 ? null : value;
+ }
+ 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(type, eventInit = {}) {
+ await this._elementChannel.dispatchEvent({ type, eventInit: (0, import_jsHandle.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 result = await this._elementChannel.selectOption({ ...convertSelectOptionValues(values), ...options, timeout: this._frame._timeout(options) });
+ return result.values;
+ }
+ async fill(value, options = {}) {
+ return await this._elementChannel.fill({ value, ...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(text, options = {}) {
+ await this._elementChannel.type({ text, ...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 value = (await this._elementChannel.boundingBox()).value;
+ return value === void 0 ? null : value;
+ }
+ 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((locator) => ({
+ frame: locator._frame._channel,
+ selector: locator._selector
+ }));
+ }
+ const result = await this._elementChannel.screenshot(copy);
+ if (options.path) {
+ await (0, import_fileUtils.mkdirIfNeeded)(this._platform, options.path);
+ await this._platform.fs().promises.writeFile(options.path, result.binary);
+ }
+ return result.binary;
+ }
+ async $(selector) {
+ return ElementHandle.fromNullable((await this._elementChannel.querySelector({ selector })).element);
+ }
+ async $$(selector) {
+ const result = await this._elementChannel.querySelectorAll({ selector });
+ return result.elements.map((h) => ElementHandle.from(h));
+ }
+ async $eval(selector, pageFunction, arg) {
+ const result = await this._elementChannel.evalOnSelector({ selector, expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: (0, import_jsHandle.serializeArgument)(arg) });
+ return (0, import_jsHandle.parseResult)(result.value);
+ }
+ async $$eval(selector, pageFunction, arg) {
+ const result = await this._elementChannel.evalOnSelectorAll({ selector, expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: (0, import_jsHandle.serializeArgument)(arg) });
+ return (0, import_jsHandle.parseResult)(result.value);
+ }
+ async waitForElementState(state, options = {}) {
+ return await this._elementChannel.waitForElementState({ state, ...options, timeout: this._frame._timeout(options) });
+ }
+ async waitForSelector(selector, options = {}) {
+ const result = await this._elementChannel.waitForSelector({ selector, ...options, timeout: this._frame._timeout(options) });
+ return ElementHandle.fromNullable(result.element);
+ }
+}
+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++)
+ (0, import_assert.assert)(values[i] !== null, `options[${i}]: expected object, got null`);
+ if (values[0] instanceof ElementHandle)
+ return { elements: values.map((v) => v._elementChannel) };
+ if ((0, import_rtti.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) >= import_fileUtils.fileUploadSizeLimit;
+}
+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, context) {
+ 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 (context._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.path, f.name)) : localPaths;
+ const { writableStreams, rootDir } = await context._wrapApiCall(async () => context._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 = import_writableStream.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 = (0, import_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;
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ ElementHandle,
+ convertInputFiles,
+ convertSelectOptionValues,
+ determineScreenshotType
+});
diff --git a/node_modules/playwright-core/lib/client/errors.js b/node_modules/playwright-core/lib/client/errors.js
new file mode 100644
index 0000000..6075efc
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/errors.js
@@ -0,0 +1,77 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var errors_exports = {};
+__export(errors_exports, {
+ TargetClosedError: () => TargetClosedError,
+ TimeoutError: () => TimeoutError,
+ isTargetClosedError: () => isTargetClosedError,
+ parseError: () => parseError,
+ serializeError: () => serializeError
+});
+module.exports = __toCommonJS(errors_exports);
+var import_serializers = require("../protocol/serializers");
+var import_rtti = require("../utils/isomorphic/rtti");
+class TimeoutError extends Error {
+ constructor(message) {
+ super(message);
+ this.name = "TimeoutError";
+ }
+}
+class TargetClosedError extends Error {
+ constructor(cause) {
+ super(cause || "Target page, context or browser has been closed");
+ }
+}
+function isTargetClosedError(error) {
+ return error instanceof TargetClosedError;
+}
+function serializeError(e) {
+ if ((0, import_rtti.isError)(e))
+ return { error: { message: e.message, stack: e.stack, name: e.name } };
+ return { value: (0, import_serializers.serializeValue)(e, (value) => ({ fallThrough: value })) };
+}
+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 (0, import_serializers.parseSerializedValue)(error.value, void 0);
+ }
+ if (error.error.name === "TimeoutError") {
+ const e2 = new TimeoutError(error.error.message);
+ e2.stack = error.error.stack || "";
+ return e2;
+ }
+ if (error.error.name === "TargetClosedError") {
+ const e2 = new TargetClosedError(error.error.message);
+ e2.stack = error.error.stack || "";
+ return e2;
+ }
+ const e = new Error(error.error.message);
+ e.stack = error.error.stack || "";
+ e.name = error.error.name;
+ return e;
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ TargetClosedError,
+ TimeoutError,
+ isTargetClosedError,
+ parseError,
+ serializeError
+});
diff --git a/node_modules/playwright-core/lib/client/eventEmitter.js b/node_modules/playwright-core/lib/client/eventEmitter.js
new file mode 100644
index 0000000..dc72dea
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/eventEmitter.js
@@ -0,0 +1,314 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var eventEmitter_exports = {};
+__export(eventEmitter_exports, {
+ EventEmitter: () => EventEmitter
+});
+module.exports = __toCommonJS(eventEmitter_exports);
+class EventEmitter {
+ 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(type, ...args) {
+ const events = this._events;
+ if (events === void 0)
+ return false;
+ const handler = events?.[type];
+ if (handler === void 0)
+ return false;
+ if (typeof handler === "function") {
+ this._callHandler(type, handler, args);
+ } else {
+ const len = handler.length;
+ const listeners = handler.slice();
+ for (let i = 0; i < len; ++i)
+ this._callHandler(type, listeners[i], args);
+ }
+ return true;
+ }
+ _callHandler(type, handler, args) {
+ const promise = Reflect.apply(handler, this, args);
+ if (!(promise instanceof Promise))
+ return;
+ let set = this._pendingHandlers.get(type);
+ if (!set) {
+ set = /* @__PURE__ */ new Set();
+ this._pendingHandlers.set(type, set);
+ }
+ set.add(promise);
+ promise.catch((e) => {
+ if (this._rejectionHandler)
+ this._rejectionHandler(e);
+ else
+ throw e;
+ }).finally(() => set.delete(promise));
+ }
+ addListener(type, listener) {
+ return this._addListener(type, listener, false);
+ }
+ on(type, listener) {
+ return this._addListener(type, listener, false);
+ }
+ _addListener(type, 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", type, unwrapListener(listener));
+ events = this._events;
+ }
+ existing = events[type];
+ }
+ if (existing === void 0) {
+ existing = events[type] = listener;
+ ++this._eventsCount;
+ } else {
+ if (typeof existing === "function") {
+ existing = events[type] = 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(type) + " listeners added. Use emitter.setMaxListeners() to increase limit");
+ w.name = "MaxListenersExceededWarning";
+ w.emitter = this;
+ w.type = type;
+ w.count = existing.length;
+ if (!this._platform.isUnderTest()) {
+ console.warn(w);
+ }
+ }
+ }
+ return this;
+ }
+ prependListener(type, listener) {
+ return this._addListener(type, listener, true);
+ }
+ once(type, listener) {
+ checkListener(listener);
+ this.on(type, new OnceWrapper(this, type, listener).wrapperFunction);
+ return this;
+ }
+ prependOnceListener(type, listener) {
+ checkListener(listener);
+ this.prependListener(type, new OnceWrapper(this, type, listener).wrapperFunction);
+ return this;
+ }
+ removeListener(type, listener) {
+ checkListener(listener);
+ const events = this._events;
+ if (events === void 0)
+ return this;
+ const list = events[type];
+ 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[type];
+ if (events.removeListener)
+ this.emit("removeListener", type, 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[type] = list[0];
+ if (events.removeListener !== void 0)
+ this.emit("removeListener", type, originalListener || listener);
+ }
+ return this;
+ }
+ off(type, listener) {
+ return this.removeListener(type, listener);
+ }
+ removeAllListeners(type, options) {
+ this._removeAllListeners(type);
+ if (!options)
+ return this;
+ if (options.behavior === "wait") {
+ const errors = [];
+ this._rejectionHandler = (error) => errors.push(error);
+ return this._waitFor(type).then(() => {
+ if (errors.length)
+ throw errors[0];
+ });
+ }
+ if (options.behavior === "ignoreErrors")
+ this._rejectionHandler = () => {
+ };
+ return Promise.resolve();
+ }
+ _removeAllListeners(type) {
+ const events = this._events;
+ if (!events)
+ return;
+ if (!events.removeListener) {
+ if (type === void 0) {
+ this._events = /* @__PURE__ */ Object.create(null);
+ this._eventsCount = 0;
+ } else if (events[type] !== void 0) {
+ if (--this._eventsCount === 0)
+ this._events = /* @__PURE__ */ Object.create(null);
+ else
+ delete events[type];
+ }
+ return;
+ }
+ if (type === 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[type];
+ if (typeof listeners === "function") {
+ this.removeListener(type, listeners);
+ } else if (listeners !== void 0) {
+ for (let i = listeners.length - 1; i >= 0; i--)
+ this.removeListener(type, listeners[i]);
+ }
+ }
+ listeners(type) {
+ return this._listeners(this, type, true);
+ }
+ rawListeners(type) {
+ return this._listeners(this, type, false);
+ }
+ listenerCount(type) {
+ const events = this._events;
+ if (events !== void 0) {
+ const listener = events[type];
+ 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(type) {
+ let promises = [];
+ if (type) {
+ promises = [...this._pendingHandlers.get(type) || []];
+ } else {
+ promises = [];
+ for (const [, pending] of this._pendingHandlers)
+ promises.push(...pending);
+ }
+ await Promise.all(promises);
+ }
+ _listeners(target, type, unwrap) {
+ const events = target._events;
+ if (events === void 0)
+ return [];
+ const listener = events[type];
+ if (listener === void 0)
+ return [];
+ if (typeof listener === "function")
+ return unwrap ? [unwrapListener(listener)] : [listener];
+ return unwrap ? unwrapListeners(listener) : listener.slice();
+ }
+}
+function checkListener(listener) {
+ if (typeof listener !== "function")
+ throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener);
+}
+class OnceWrapper {
+ 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);
+ }
+}
+function unwrapListener(l) {
+ return wrappedListener(l) ?? l;
+}
+function unwrapListeners(arr) {
+ return arr.map((l) => wrappedListener(l) ?? l);
+}
+function wrappedListener(l) {
+ return l.listener;
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ EventEmitter
+});
diff --git a/node_modules/playwright-core/lib/client/events.js b/node_modules/playwright-core/lib/client/events.js
new file mode 100644
index 0000000..154b22d
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/events.js
@@ -0,0 +1,98 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var events_exports = {};
+__export(events_exports, {
+ Events: () => Events
+});
+module.exports = __toCommonJS(events_exports);
+const Events = {
+ AndroidDevice: {
+ WebView: "webview",
+ Close: "close"
+ },
+ AndroidSocket: {
+ Data: "data",
+ Close: "close"
+ },
+ AndroidWebView: {
+ Close: "close"
+ },
+ Browser: {
+ Disconnected: "disconnected"
+ },
+ BrowserContext: {
+ Console: "console",
+ Close: "close",
+ Dialog: "dialog",
+ 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
+ WebError: "weberror",
+ BackgroundPage: "backgroundpage",
+ 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"
+ },
+ ElectronApplication: {
+ Close: "close",
+ Console: "console",
+ Window: "window"
+ }
+};
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ Events
+});
diff --git a/node_modules/playwright-core/lib/client/fetch.js b/node_modules/playwright-core/lib/client/fetch.js
new file mode 100644
index 0000000..d41fcfb
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/fetch.js
@@ -0,0 +1,369 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var fetch_exports = {};
+__export(fetch_exports, {
+ APIRequest: () => APIRequest,
+ APIRequestContext: () => APIRequestContext,
+ APIResponse: () => APIResponse
+});
+module.exports = __toCommonJS(fetch_exports);
+var import_browserContext = require("./browserContext");
+var import_channelOwner = require("./channelOwner");
+var import_errors = require("./errors");
+var import_network = require("./network");
+var import_tracing = require("./tracing");
+var import_assert = require("../utils/isomorphic/assert");
+var import_fileUtils = require("./fileUtils");
+var import_headers = require("../utils/isomorphic/headers");
+var import_rtti = require("../utils/isomorphic/rtti");
+var import_timeoutSettings = require("./timeoutSettings");
+class APIRequest {
+ constructor(playwright) {
+ this._contexts = /* @__PURE__ */ new Set();
+ this._playwright = playwright;
+ }
+ async newContext(options = {}) {
+ options = {
+ ...this._playwright._defaultContextOptions,
+ ...options
+ };
+ const storageState = typeof options.storageState === "string" ? JSON.parse(await this._playwright._platform.fs().promises.readFile(options.storageState, "utf8")) : options.storageState;
+ const context = APIRequestContext.from((await this._playwright._channel.newRequest({
+ ...options,
+ extraHTTPHeaders: options.extraHTTPHeaders ? (0, import_headers.headersObjectToArray)(options.extraHTTPHeaders) : void 0,
+ storageState,
+ tracesDir: this._playwright._defaultLaunchOptions?.tracesDir,
+ // We do not expose tracesDir in the API, so do not allow options to accidentally override it.
+ clientCertificates: await (0, import_browserContext.toClientCertificatesProtocol)(this._playwright._platform, options.clientCertificates)
+ })).request);
+ this._contexts.add(context);
+ context._request = this;
+ context._timeoutSettings.setDefaultTimeout(options.timeout ?? this._playwright._defaultContextTimeout);
+ context._tracing._tracesDir = this._playwright._defaultLaunchOptions?.tracesDir;
+ await context._instrumentation.runAfterCreateRequestContext(context);
+ return context;
+ }
+}
+class APIRequestContext extends import_channelOwner.ChannelOwner {
+ static from(channel) {
+ return channel._object;
+ }
+ constructor(parent, type, guid, initializer) {
+ super(parent, type, guid, initializer);
+ this._tracing = import_tracing.Tracing.from(initializer.tracing);
+ this._timeoutSettings = new import_timeoutSettings.TimeoutSettings(this._platform);
+ }
+ async [Symbol.asyncDispose]() {
+ await this.dispose();
+ }
+ async dispose(options = {}) {
+ this._closeReason = options.reason;
+ await this._instrumentation.runBeforeCloseRequestContext(this);
+ try {
+ await this._channel.dispose(options);
+ } catch (e) {
+ if ((0, import_errors.isTargetClosedError)(e))
+ return;
+ throw e;
+ }
+ this._tracing._resetStackCounter();
+ this._request?._contexts.delete(this);
+ }
+ async delete(url, options) {
+ return await this.fetch(url, {
+ ...options,
+ method: "DELETE"
+ });
+ }
+ async head(url, options) {
+ return await this.fetch(url, {
+ ...options,
+ method: "HEAD"
+ });
+ }
+ async get(url, options) {
+ return await this.fetch(url, {
+ ...options,
+ method: "GET"
+ });
+ }
+ async patch(url, options) {
+ return await this.fetch(url, {
+ ...options,
+ method: "PATCH"
+ });
+ }
+ async post(url, options) {
+ return await this.fetch(url, {
+ ...options,
+ method: "POST"
+ });
+ }
+ async put(url, options) {
+ return await this.fetch(url, {
+ ...options,
+ method: "PUT"
+ });
+ }
+ async fetch(urlOrRequest, options = {}) {
+ const url = (0, import_rtti.isString)(urlOrRequest) ? urlOrRequest : void 0;
+ const request = (0, import_rtti.isString)(urlOrRequest) ? void 0 : urlOrRequest;
+ return await this._innerFetch({ url, request, ...options });
+ }
+ async _innerFetch(options = {}) {
+ return await this._wrapApiCall(async () => {
+ if (this._closeReason)
+ throw new import_errors.TargetClosedError(this._closeReason);
+ (0, import_assert.assert)(options.request || typeof options.url === "string", "First argument must be either URL string or Request");
+ (0, import_assert.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`);
+ (0, import_assert.assert)(options.maxRedirects === void 0 || options.maxRedirects >= 0, `'maxRedirects' must be greater than or equal to '0'`);
+ (0, import_assert.assert)(options.maxRetries === void 0 || options.maxRetries >= 0, `'maxRetries' must be greater than or equal to '0'`);
+ const url = 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 ? (0, import_headers.headersObjectToArray)(headersObj) : void 0;
+ let jsonData;
+ let formData;
+ let multipartData;
+ let postDataBuffer;
+ if (options.data !== void 0) {
+ if ((0, import_rtti.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, value] of options.form.entries()) {
+ if (typeof value !== "string")
+ throw new Error(`Expected string for options.form["${name}"], found File. Please use options.multipart instead.`);
+ formData.push({ name, value });
+ }
+ } else {
+ formData = objectToArray(options.form);
+ }
+ } else if (options.multipart) {
+ multipartData = [];
+ if (globalThis.FormData && options.multipart instanceof FormData) {
+ const form = options.multipart;
+ for (const [name, value] of form.entries()) {
+ if ((0, import_rtti.isString)(value)) {
+ multipartData.push({ name, value });
+ } else {
+ const file = {
+ name: value.name,
+ mimeType: value.type,
+ buffer: Buffer.from(await value.arrayBuffer())
+ };
+ multipartData.push({ name, file });
+ }
+ }
+ } else {
+ for (const [name, value] of Object.entries(options.multipart))
+ multipartData.push(await toFormField(this._platform, name, value));
+ }
+ }
+ 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 result = await this._channel.fetch({
+ url,
+ 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, result.response);
+ });
+ }
+ async storageState(options = {}) {
+ const state = await this._channel.storageState({ indexedDB: options.indexedDB });
+ if (options.path) {
+ await (0, import_fileUtils.mkdirIfNeeded)(this._platform, options.path);
+ await this._platform.fs().promises.writeFile(options.path, JSON.stringify(state, void 0, 2), "utf8");
+ }
+ return state;
+ }
+}
+async function toFormField(platform, name, value) {
+ const typeOfValue = typeof value;
+ if (isFilePayload(value)) {
+ const payload = value;
+ 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(value) };
+ } else {
+ return { name, file: await readStreamToJson(platform, value) };
+ }
+}
+function isJsonParsable(value) {
+ if (typeof value !== "string")
+ return false;
+ try {
+ JSON.parse(value);
+ return true;
+ } catch (e) {
+ if (e instanceof SyntaxError)
+ return false;
+ else
+ throw e;
+ }
+}
+class APIResponse {
+ constructor(context, initializer) {
+ this._request = context;
+ this._initializer = initializer;
+ this._headers = new import_network.RawHeaders(this._initializer.headers);
+ if (context._platform.inspectCustom)
+ this[context._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 body() {
+ return await this._request._wrapApiCall(async () => {
+ try {
+ const result = await this._request._channel.fetchResponseBody({ fetchUid: this._fetchUid() });
+ if (result.binary === void 0)
+ throw new Error("Response has been disposed");
+ return result.binary;
+ } catch (e) {
+ if ((0, import_errors.isTargetClosedError)(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 }) => ` ${name}: ${value}`);
+ return `APIResponse: ${this.status()} ${this.statusText()}
+${headers.join("\n")}`;
+ }
+ _fetchUid() {
+ return this._initializer.fetchUid;
+ }
+ async _fetchLog() {
+ const { log } = await this._request._channel.fetchLog({ fetchUid: this._fetchUid() });
+ return log;
+ }
+}
+function filePayloadToJson(payload) {
+ return {
+ name: payload.name,
+ mimeType: payload.mimeType,
+ buffer: payload.buffer
+ };
+}
+async function readStreamToJson(platform, stream) {
+ const buffer = await new Promise((resolve, reject) => {
+ const chunks = [];
+ stream.on("data", (chunk) => chunks.push(chunk));
+ stream.on("end", () => resolve(Buffer.concat(chunks)));
+ stream.on("error", (err) => reject(err));
+ });
+ const streamPath = Buffer.isBuffer(stream.path) ? stream.path.toString("utf8") : stream.path;
+ return {
+ name: platform.path().basename(streamPath),
+ buffer
+ };
+}
+function isJsonContentType(headers) {
+ if (!headers)
+ return false;
+ for (const { name, value } of headers) {
+ if (name.toLocaleLowerCase() === "content-type")
+ return value === "application/json";
+ }
+ return false;
+}
+function objectToArray(map) {
+ if (!map)
+ return void 0;
+ const result = [];
+ for (const [name, value] of Object.entries(map)) {
+ if (value !== void 0)
+ result.push({ name, value: String(value) });
+ }
+ return result;
+}
+function isFilePayload(value) {
+ return typeof value === "object" && value["name"] && value["mimeType"] && value["buffer"];
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ APIRequest,
+ APIRequestContext,
+ APIResponse
+});
diff --git a/node_modules/playwright-core/lib/client/fileChooser.js b/node_modules/playwright-core/lib/client/fileChooser.js
new file mode 100644
index 0000000..65a0828
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/fileChooser.js
@@ -0,0 +1,46 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var fileChooser_exports = {};
+__export(fileChooser_exports, {
+ FileChooser: () => FileChooser
+});
+module.exports = __toCommonJS(fileChooser_exports);
+class FileChooser {
+ 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);
+ }
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ FileChooser
+});
diff --git a/node_modules/playwright-core/lib/client/fileUtils.js b/node_modules/playwright-core/lib/client/fileUtils.js
new file mode 100644
index 0000000..1c015bc
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/fileUtils.js
@@ -0,0 +1,34 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var fileUtils_exports = {};
+__export(fileUtils_exports, {
+ fileUploadSizeLimit: () => fileUploadSizeLimit,
+ mkdirIfNeeded: () => mkdirIfNeeded
+});
+module.exports = __toCommonJS(fileUtils_exports);
+const fileUploadSizeLimit = 50 * 1024 * 1024;
+async function mkdirIfNeeded(platform, filePath) {
+ await platform.fs().promises.mkdir(platform.path().dirname(filePath), { recursive: true }).catch(() => {
+ });
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ fileUploadSizeLimit,
+ mkdirIfNeeded
+});
diff --git a/node_modules/playwright-core/lib/client/frame.js b/node_modules/playwright-core/lib/client/frame.js
new file mode 100644
index 0000000..3ae21e4
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/frame.js
@@ -0,0 +1,408 @@
+"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 frame_exports = {};
+__export(frame_exports, {
+ Frame: () => Frame,
+ verifyLoadState: () => verifyLoadState
+});
+module.exports = __toCommonJS(frame_exports);
+var import_eventEmitter = require("./eventEmitter");
+var import_channelOwner = require("./channelOwner");
+var import_clientHelper = require("./clientHelper");
+var import_elementHandle = require("./elementHandle");
+var import_events = require("./events");
+var import_jsHandle = require("./jsHandle");
+var import_locator = require("./locator");
+var network = __toESM(require("./network"));
+var import_types = require("./types");
+var import_waiter = require("./waiter");
+var import_assert = require("../utils/isomorphic/assert");
+var import_locatorUtils = require("../utils/isomorphic/locatorUtils");
+var import_urlMatch = require("../utils/isomorphic/urlMatch");
+var import_timeoutSettings = require("./timeoutSettings");
+class Frame extends import_channelOwner.ChannelOwner {
+ constructor(parent, type, guid, initializer) {
+ super(parent, type, guid, initializer);
+ this._parentFrame = null;
+ this._url = "";
+ this._name = "";
+ this._detached = false;
+ this._childFrames = /* @__PURE__ */ new Set();
+ this._eventEmitter = new import_eventEmitter.EventEmitter(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(import_events.Events.Page.Load, this._page);
+ if (!this._parentFrame && event.add === "domcontentloaded" && this._page)
+ this._page.emit(import_events.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(import_events.Events.Page.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 import_timeoutSettings.TimeoutSettings(this._platform);
+ return timeoutSettings.timeout(options || {});
+ }
+ _navigationTimeout(options) {
+ const timeoutSettings = this._page?._timeoutSettings || new import_timeoutSettings.TimeoutSettings(this._platform);
+ return timeoutSettings.navigationTimeout(options || {});
+ }
+ async goto(url, options = {}) {
+ const waitUntil = verifyLoadState("waitUntil", options.waitUntil === void 0 ? "load" : options.waitUntil);
+ return network.Response.fromNullable((await this._channel.goto({ url, ...options, waitUntil, timeout: this._navigationTimeout(options) })).response);
+ }
+ _setupNavigationWaiter(options) {
+ const waiter = new import_waiter.Waiter(this._page, "");
+ if (this._page.isClosed())
+ waiter.rejectImmediately(this._page._closeErrorWithReason());
+ waiter.rejectOnEvent(this._page, import_events.Events.Page.Close, () => this._page._closeErrorWithReason());
+ waiter.rejectOnEvent(this._page, import_events.Events.Page.Crash, new Error("Navigation failed because page crashed!"));
+ waiter.rejectOnEvent(this._page, import_events.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 (0, import_urlMatch.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 request = navigatedEvent.newDocument ? network.Request.fromNullable(navigatedEvent.newDocument.request) : null;
+ const response = request ? await waiter.waitForPromise(request._finalRequest()._internalResponse()) : null;
+ waiter.dispose();
+ return response;
+ }, { 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(url, options = {}) {
+ if ((0, import_urlMatch.urlMatches)(this._page?.context()._options.baseURL, this.url(), url))
+ return await this.waitForLoadState(options.waitUntil, options);
+ await this.waitForNavigation({ url, ...options });
+ }
+ async frameElement() {
+ return import_elementHandle.ElementHandle.from((await this._channel.frameElement()).element);
+ }
+ async evaluateHandle(pageFunction, arg) {
+ (0, import_jsHandle.assertMaxArguments)(arguments.length, 2);
+ const result = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: (0, import_jsHandle.serializeArgument)(arg) });
+ return import_jsHandle.JSHandle.from(result.handle);
+ }
+ async evaluate(pageFunction, arg) {
+ (0, import_jsHandle.assertMaxArguments)(arguments.length, 2);
+ const result = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: (0, import_jsHandle.serializeArgument)(arg) });
+ return (0, import_jsHandle.parseResult)(result.value);
+ }
+ async _evaluateFunction(functionDeclaration) {
+ const result = await this._channel.evaluateExpression({ expression: functionDeclaration, isFunction: true, arg: (0, import_jsHandle.serializeArgument)(void 0) });
+ return (0, import_jsHandle.parseResult)(result.value);
+ }
+ async _evaluateExposeUtilityScript(pageFunction, arg) {
+ (0, import_jsHandle.assertMaxArguments)(arguments.length, 2);
+ const result = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: (0, import_jsHandle.serializeArgument)(arg) });
+ return (0, import_jsHandle.parseResult)(result.value);
+ }
+ async $(selector, options) {
+ const result = await this._channel.querySelector({ selector, ...options });
+ return import_elementHandle.ElementHandle.fromNullable(result.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 result = await this._channel.waitForSelector({ selector, ...options, timeout: this._timeout(options) });
+ return import_elementHandle.ElementHandle.fromNullable(result.element);
+ }
+ async dispatchEvent(selector, type, eventInit, options = {}) {
+ await this._channel.dispatchEvent({ selector, type, eventInit: (0, import_jsHandle.serializeArgument)(eventInit), ...options, timeout: this._timeout(options) });
+ }
+ async $eval(selector, pageFunction, arg) {
+ (0, import_jsHandle.assertMaxArguments)(arguments.length, 3);
+ const result = await this._channel.evalOnSelector({ selector, expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: (0, import_jsHandle.serializeArgument)(arg) });
+ return (0, import_jsHandle.parseResult)(result.value);
+ }
+ async $$eval(selector, pageFunction, arg) {
+ (0, import_jsHandle.assertMaxArguments)(arguments.length, 3);
+ const result = await this._channel.evalOnSelectorAll({ selector, expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: (0, import_jsHandle.serializeArgument)(arg) });
+ return (0, import_jsHandle.parseResult)(result.value);
+ }
+ async $$(selector) {
+ const result = await this._channel.querySelectorAll({ selector });
+ return result.elements.map((e) => import_elementHandle.ElementHandle.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 = (0, import_clientHelper.addSourceUrlToScript)(copy.content, copy.path);
+ }
+ return import_elementHandle.ElementHandle.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 import_elementHandle.ElementHandle.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(source, target, options = {}) {
+ return await this._channel.dragAndDrop({ source, target, ...options, timeout: this._timeout(options) });
+ }
+ async tap(selector, options = {}) {
+ return await this._channel.tap({ selector, ...options, timeout: this._timeout(options) });
+ }
+ async fill(selector, value, options = {}) {
+ return await this._channel.fill({ selector, value, ...options, timeout: this._timeout(options) });
+ }
+ async _highlight(selector) {
+ return await this._channel.highlight({ selector });
+ }
+ locator(selector, options) {
+ return new import_locator.Locator(this, selector, options);
+ }
+ getByTestId(testId) {
+ return this.locator((0, import_locatorUtils.getByTestIdSelector)((0, import_locator.testIdAttributeName)(), testId));
+ }
+ getByAltText(text, options) {
+ return this.locator((0, import_locatorUtils.getByAltTextSelector)(text, options));
+ }
+ getByLabel(text, options) {
+ return this.locator((0, import_locatorUtils.getByLabelSelector)(text, options));
+ }
+ getByPlaceholder(text, options) {
+ return this.locator((0, import_locatorUtils.getByPlaceholderSelector)(text, options));
+ }
+ getByText(text, options) {
+ return this.locator((0, import_locatorUtils.getByTextSelector)(text, options));
+ }
+ getByTitle(text, options) {
+ return this.locator((0, import_locatorUtils.getByTitleSelector)(text, options));
+ }
+ getByRole(role, options = {}) {
+ return this.locator((0, import_locatorUtils.getByRoleSelector)(role, options));
+ }
+ frameLocator(selector) {
+ return new import_locator.FrameLocator(this, selector);
+ }
+ async focus(selector, options = {}) {
+ await this._channel.focus({ selector, ...options, timeout: this._timeout(options) });
+ }
+ async textContent(selector, options = {}) {
+ const value = (await this._channel.textContent({ selector, ...options, timeout: this._timeout(options) })).value;
+ return value === void 0 ? null : value;
+ }
+ 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 value = (await this._channel.getAttribute({ selector, name, ...options, timeout: this._timeout(options) })).value;
+ return value === void 0 ? null : value;
+ }
+ 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, ...(0, import_elementHandle.convertSelectOptionValues)(values), ...options, timeout: this._timeout(options) })).values;
+ }
+ async setInputFiles(selector, files, options = {}) {
+ const converted = await (0, import_elementHandle.convertInputFiles)(this._platform, files, this.page().context());
+ await this._channel.setInputFiles({ selector, ...converted, ...options, timeout: this._timeout(options) });
+ }
+ async type(selector, text, options = {}) {
+ await this._channel.type({ selector, text, ...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")
+ (0, import_assert.assert)(options.polling === "raf", "Unknown polling option: " + options.polling);
+ const result = await this._channel.waitForFunction({
+ ...options,
+ pollingInterval: options.polling === "raf" ? void 0 : options.polling,
+ expression: String(pageFunction),
+ isFunction: typeof pageFunction === "function",
+ arg: (0, import_jsHandle.serializeArgument)(arg),
+ timeout: this._timeout(options)
+ });
+ return import_jsHandle.JSHandle.from(result.handle);
+ }
+ async title() {
+ return (await this._channel.title()).value;
+ }
+ async _expect(expression, options) {
+ const params = { expression, ...options, isNot: !!options.isNot };
+ params.expectedValue = (0, import_jsHandle.serializeArgument)(options.expectedValue);
+ const result = await this._channel.expect(params);
+ if (result.received !== void 0)
+ result.received = (0, import_jsHandle.parseResult)(result.received);
+ return result;
+ }
+}
+function verifyLoadState(name, waitUntil) {
+ if (waitUntil === "networkidle0")
+ waitUntil = "networkidle";
+ if (!import_types.kLifecycleEvents.has(waitUntil))
+ throw new Error(`${name}: expected one of (load|domcontentloaded|networkidle|commit)`);
+ return waitUntil;
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ Frame,
+ verifyLoadState
+});
diff --git a/node_modules/playwright-core/lib/client/harRouter.js b/node_modules/playwright-core/lib/client/harRouter.js
new file mode 100644
index 0000000..76cfbfc
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/harRouter.js
@@ -0,0 +1,87 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var harRouter_exports = {};
+__export(harRouter_exports, {
+ HarRouter: () => HarRouter
+});
+module.exports = __toCommonJS(harRouter_exports);
+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(route) {
+ const request = route.request();
+ const response = await this._localUtils.harLookup({
+ harId: this._harId,
+ url: request.url(),
+ method: request.method(),
+ headers: await request.headersArray(),
+ postData: request.postDataBuffer() || void 0,
+ isNavigationRequest: request.isNavigationRequest()
+ });
+ if (response.action === "redirect") {
+ route._platform.log("api", `HAR: ${route.request().url()} redirected to ${response.redirectURL}`);
+ await route._redirectNavigationRequest(response.redirectURL);
+ return;
+ }
+ if (response.action === "fulfill") {
+ if (response.status === -1)
+ return;
+ await route.fulfill({
+ status: response.status,
+ headers: Object.fromEntries(response.headers.map((h) => [h.name, h.value])),
+ body: response.body
+ });
+ return;
+ }
+ if (response.action === "error")
+ route._platform.log("api", "HAR: " + response.message);
+ if (this._notFoundAction === "abort") {
+ await route.abort();
+ return;
+ }
+ await route.fallback();
+ }
+ async addContextRoute(context) {
+ await context.route(this._options.urlMatch || "**/*", (route) => this._handle(route));
+ }
+ async addPageRoute(page) {
+ await page.route(this._options.urlMatch || "**/*", (route) => this._handle(route));
+ }
+ async [Symbol.asyncDispose]() {
+ await this.dispose();
+ }
+ dispose() {
+ this._localUtils.harClose({ harId: this._harId }).catch(() => {
+ });
+ }
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ HarRouter
+});
diff --git a/node_modules/playwright-core/lib/client/input.js b/node_modules/playwright-core/lib/client/input.js
new file mode 100644
index 0000000..c2a9bbc
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/input.js
@@ -0,0 +1,84 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var input_exports = {};
+__export(input_exports, {
+ Keyboard: () => Keyboard,
+ Mouse: () => Mouse,
+ Touchscreen: () => Touchscreen
+});
+module.exports = __toCommonJS(input_exports);
+class Keyboard {
+ 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(text) {
+ await this._page._channel.keyboardInsertText({ text });
+ }
+ async type(text, options = {}) {
+ await this._page._channel.keyboardType({ text, ...options });
+ }
+ async press(key, options = {}) {
+ await this._page._channel.keyboardPress({ key, ...options });
+ }
+}
+class Mouse {
+ 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 });
+ }
+}
+class Touchscreen {
+ constructor(page) {
+ this._page = page;
+ }
+ async tap(x, y) {
+ await this._page._channel.touchscreenTap({ x, y });
+ }
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ Keyboard,
+ Mouse,
+ Touchscreen
+});
diff --git a/node_modules/playwright-core/lib/client/jsHandle.js b/node_modules/playwright-core/lib/client/jsHandle.js
new file mode 100644
index 0000000..d0582d6
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/jsHandle.js
@@ -0,0 +1,109 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var jsHandle_exports = {};
+__export(jsHandle_exports, {
+ JSHandle: () => JSHandle,
+ assertMaxArguments: () => assertMaxArguments,
+ parseResult: () => parseResult,
+ serializeArgument: () => serializeArgument
+});
+module.exports = __toCommonJS(jsHandle_exports);
+var import_channelOwner = require("./channelOwner");
+var import_errors = require("./errors");
+var import_serializers = require("../protocol/serializers");
+class JSHandle extends import_channelOwner.ChannelOwner {
+ static from(handle) {
+ return handle._object;
+ }
+ constructor(parent, type, guid, initializer) {
+ super(parent, type, guid, initializer);
+ this._preview = this._initializer.preview;
+ this._channel.on("previewUpdated", ({ preview }) => this._preview = preview);
+ }
+ async evaluate(pageFunction, arg) {
+ const result = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: serializeArgument(arg) });
+ return parseResult(result.value);
+ }
+ async _evaluateFunction(functionDeclaration) {
+ const result = await this._channel.evaluateExpression({ expression: functionDeclaration, isFunction: true, arg: serializeArgument(void 0) });
+ return parseResult(result.value);
+ }
+ async evaluateHandle(pageFunction, arg) {
+ const result = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: serializeArgument(arg) });
+ return JSHandle.from(result.handle);
+ }
+ async getProperty(propertyName) {
+ const result = await this._channel.getProperty({ name: propertyName });
+ return JSHandle.from(result.handle);
+ }
+ async getProperties() {
+ const map = /* @__PURE__ */ new Map();
+ for (const { name, value } of (await this._channel.getPropertyList()).properties)
+ map.set(name, JSHandle.from(value));
+ 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 ((0, import_errors.isTargetClosedError)(e))
+ return;
+ throw e;
+ }
+ }
+ toString() {
+ return this._preview;
+ }
+}
+function serializeArgument(arg) {
+ const handles = [];
+ const pushHandle = (channel) => {
+ handles.push(channel);
+ return handles.length - 1;
+ };
+ const value = (0, import_serializers.serializeValue)(arg, (value2) => {
+ if (value2 instanceof JSHandle)
+ return { h: pushHandle(value2._channel) };
+ return { fallThrough: value2 };
+ });
+ return { value, handles };
+}
+function parseResult(value) {
+ return (0, import_serializers.parseSerializedValue)(value, 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.");
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ JSHandle,
+ assertMaxArguments,
+ parseResult,
+ serializeArgument
+});
diff --git a/node_modules/playwright-core/lib/client/jsonPipe.js b/node_modules/playwright-core/lib/client/jsonPipe.js
new file mode 100644
index 0000000..2117a06
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/jsonPipe.js
@@ -0,0 +1,39 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var jsonPipe_exports = {};
+__export(jsonPipe_exports, {
+ JsonPipe: () => JsonPipe
+});
+module.exports = __toCommonJS(jsonPipe_exports);
+var import_channelOwner = require("./channelOwner");
+class JsonPipe extends import_channelOwner.ChannelOwner {
+ static from(jsonPipe) {
+ return jsonPipe._object;
+ }
+ constructor(parent, type, guid, initializer) {
+ super(parent, type, guid, initializer);
+ }
+ channel() {
+ return this._channel;
+ }
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ JsonPipe
+});
diff --git a/node_modules/playwright-core/lib/client/localUtils.js b/node_modules/playwright-core/lib/client/localUtils.js
new file mode 100644
index 0000000..921ad29
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/localUtils.js
@@ -0,0 +1,60 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var localUtils_exports = {};
+__export(localUtils_exports, {
+ LocalUtils: () => LocalUtils
+});
+module.exports = __toCommonJS(localUtils_exports);
+var import_channelOwner = require("./channelOwner");
+class LocalUtils extends import_channelOwner.ChannelOwner {
+ constructor(parent, type, guid, initializer) {
+ super(parent, type, guid, initializer);
+ this.devices = {};
+ for (const { name, descriptor } of initializer.deviceDescriptors)
+ this.devices[name] = descriptor;
+ }
+ async zip(params) {
+ return await this._channel.zip(params);
+ }
+ async harOpen(params) {
+ return await this._channel.harOpen(params);
+ }
+ async harLookup(params) {
+ return await this._channel.harLookup(params);
+ }
+ async harClose(params) {
+ return await this._channel.harClose(params);
+ }
+ async harUnzip(params) {
+ return await this._channel.harUnzip(params);
+ }
+ async tracingStarted(params) {
+ return await this._channel.tracingStarted(params);
+ }
+ async traceDiscarded(params) {
+ return await this._channel.traceDiscarded(params);
+ }
+ async addStackToTracingNoReply(params) {
+ return await this._channel.addStackToTracingNoReply(params);
+ }
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ LocalUtils
+});
diff --git a/node_modules/playwright-core/lib/client/locator.js b/node_modules/playwright-core/lib/client/locator.js
new file mode 100644
index 0000000..97b84ea
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/locator.js
@@ -0,0 +1,366 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var locator_exports = {};
+__export(locator_exports, {
+ FrameLocator: () => FrameLocator,
+ Locator: () => Locator,
+ setTestIdAttribute: () => setTestIdAttribute,
+ testIdAttributeName: () => testIdAttributeName
+});
+module.exports = __toCommonJS(locator_exports);
+var import_elementHandle = require("./elementHandle");
+var import_locatorGenerators = require("../utils/isomorphic/locatorGenerators");
+var import_locatorUtils = require("../utils/isomorphic/locatorUtils");
+var import_stringUtils = require("../utils/isomorphic/stringUtils");
+var import_rtti = require("../utils/isomorphic/rtti");
+var import_time = require("../utils/isomorphic/time");
+class Locator {
+ constructor(frame, selector, options) {
+ this._frame = frame;
+ this._selector = selector;
+ if (options?.hasText)
+ this._selector += ` >> internal:has-text=${(0, import_stringUtils.escapeForTextSelector)(options.hasText, false)}`;
+ if (options?.hasNotText)
+ this._selector += ` >> internal:has-not-text=${(0, import_stringUtils.escapeForTextSelector)(options.hasNotText, false)}`;
+ if (options?.has) {
+ const locator = options.has;
+ if (locator._frame !== frame)
+ throw new Error(`Inner "has" locator must belong to the same frame.`);
+ this._selector += ` >> internal:has=` + JSON.stringify(locator._selector);
+ }
+ if (options?.hasNot) {
+ const locator = options.hasNot;
+ if (locator._frame !== frame)
+ throw new Error(`Inner "hasNot" locator must belong to the same frame.`);
+ this._selector += ` >> internal:has-not=` + JSON.stringify(locator._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 ? (0, import_time.monotonicTime)() + timeout : 0;
+ return await this._frame._wrapApiCall(async () => {
+ const result = await this._frame._channel.waitForSelector({ selector: this._selector, strict: true, state: "attached", timeout });
+ const handle = import_elementHandle.ElementHandle.fromNullable(result.element);
+ if (!handle)
+ throw new Error(`Could not resolve ${this._selector} to DOM Element`);
+ try {
+ return await task(handle, deadline ? deadline - (0, import_time.monotonicTime)() : 0);
+ } finally {
+ await handle.dispose();
+ }
+ }, { title: options.title, internal: options.internal });
+ }
+ _equals(locator) {
+ return this._frame === locator._frame && this._selector === locator._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(type, eventInit = {}, options) {
+ return await this._frame.dispatchEvent(this._selector, type, eventInit, { strict: true, ...options });
+ }
+ async dragTo(target, options = {}) {
+ return await this._frame.dragAndDrop(this._selector, target._selector, {
+ strict: true,
+ ...options
+ });
+ }
+ async evaluate(pageFunction, arg, options) {
+ return await this._withElement((h) => h.evaluate(pageFunction, arg), { title: "Evaluate", timeout: options?.timeout });
+ }
+ async _evaluateFunction(functionDeclaration, options) {
+ return await this._withElement((h) => h._evaluateFunction(functionDeclaration), { 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(value, options = {}) {
+ return await this._frame.fill(this._selector, value, { 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() {
+ return await this._frame._highlight(this._selector);
+ }
+ locator(selectorOrLocator, options) {
+ if ((0, import_rtti.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((0, import_locatorUtils.getByTestIdSelector)(testIdAttributeName(), testId));
+ }
+ getByAltText(text, options) {
+ return this.locator((0, import_locatorUtils.getByAltTextSelector)(text, options));
+ }
+ getByLabel(text, options) {
+ return this.locator((0, import_locatorUtils.getByLabelSelector)(text, options));
+ }
+ getByPlaceholder(text, options) {
+ return this.locator((0, import_locatorUtils.getByPlaceholderSelector)(text, options));
+ }
+ getByText(text, options) {
+ return this.locator((0, import_locatorUtils.getByTextSelector)(text, options));
+ }
+ getByTitle(text, options) {
+ return this.locator((0, import_locatorUtils.getByTitleSelector)(text, options));
+ }
+ getByRole(role, options = {}) {
+ return this.locator((0, import_locatorUtils.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));
+ }
+ 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(locator) {
+ if (locator._frame !== this._frame)
+ throw new Error(`Locators must belong to the same frame.`);
+ return new Locator(this._frame, this._selector + ` >> internal:and=` + JSON.stringify(locator._selector));
+ }
+ or(locator) {
+ if (locator._frame !== this._frame)
+ throw new Error(`Locators must belong to the same frame.`);
+ return new Locator(this._frame, this._selector + ` >> internal:or=` + JSON.stringify(locator._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 _resolveSelector() {
+ return await this._frame._channel.resolveSelector({ selector: this._selector });
+ }
+ 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 result = await this._frame._channel.ariaSnapshot({ ...options, selector: this._selector, timeout: this._frame._timeout(options) });
+ return result.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(text, options = {}) {
+ return await this._frame.type(this._selector, text, { strict: true, ...options });
+ }
+ async pressSequentially(text, options = {}) {
+ return await this.type(text, 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(expression, options) {
+ return this._frame._expect(expression, {
+ ...options,
+ selector: this._selector
+ });
+ }
+ _inspect() {
+ return this.toString();
+ }
+ toString() {
+ return (0, import_locatorGenerators.asLocator)("javascript", this._selector);
+ }
+}
+class FrameLocator {
+ constructor(frame, selector) {
+ this._frame = frame;
+ this._frameSelector = selector;
+ }
+ locator(selectorOrLocator, options) {
+ if ((0, import_rtti.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((0, import_locatorUtils.getByTestIdSelector)(testIdAttributeName(), testId));
+ }
+ getByAltText(text, options) {
+ return this.locator((0, import_locatorUtils.getByAltTextSelector)(text, options));
+ }
+ getByLabel(text, options) {
+ return this.locator((0, import_locatorUtils.getByLabelSelector)(text, options));
+ }
+ getByPlaceholder(text, options) {
+ return this.locator((0, import_locatorUtils.getByPlaceholderSelector)(text, options));
+ }
+ getByText(text, options) {
+ return this.locator((0, import_locatorUtils.getByTextSelector)(text, options));
+ }
+ getByTitle(text, options) {
+ return this.locator((0, import_locatorUtils.getByTitleSelector)(text, options));
+ }
+ getByRole(role, options = {}) {
+ return this.locator((0, import_locatorUtils.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}`);
+ }
+}
+let _testIdAttributeName = "data-testid";
+function testIdAttributeName() {
+ return _testIdAttributeName;
+}
+function setTestIdAttribute(attributeName) {
+ _testIdAttributeName = attributeName;
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ FrameLocator,
+ Locator,
+ setTestIdAttribute,
+ testIdAttributeName
+});
diff --git a/node_modules/playwright-core/lib/client/network.js b/node_modules/playwright-core/lib/client/network.js
new file mode 100644
index 0000000..dacdf74
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/network.js
@@ -0,0 +1,744 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var network_exports = {};
+__export(network_exports, {
+ RawHeaders: () => RawHeaders,
+ Request: () => Request,
+ Response: () => Response,
+ Route: () => Route,
+ RouteHandler: () => RouteHandler,
+ WebSocket: () => WebSocket,
+ WebSocketRoute: () => WebSocketRoute,
+ WebSocketRouteHandler: () => WebSocketRouteHandler,
+ validateHeaders: () => validateHeaders
+});
+module.exports = __toCommonJS(network_exports);
+var import_channelOwner = require("./channelOwner");
+var import_errors = require("./errors");
+var import_events = require("./events");
+var import_fetch = require("./fetch");
+var import_frame = require("./frame");
+var import_waiter = require("./waiter");
+var import_worker = require("./worker");
+var import_assert = require("../utils/isomorphic/assert");
+var import_headers = require("../utils/isomorphic/headers");
+var import_urlMatch = require("../utils/isomorphic/urlMatch");
+var import_manualPromise = require("../utils/isomorphic/manualPromise");
+var import_multimap = require("../utils/isomorphic/multimap");
+var import_rtti = require("../utils/isomorphic/rtti");
+var import_stackTrace = require("../utils/isomorphic/stackTrace");
+var import_mimeType = require("../utils/isomorphic/mimeType");
+class Request extends import_channelOwner.ChannelOwner {
+ constructor(parent, type, guid, initializer) {
+ super(parent, type, guid, initializer);
+ this._redirectedFrom = null;
+ this._redirectedTo = null;
+ this._failureText = 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(request) {
+ return request._object;
+ }
+ static fromNullable(request) {
+ return request ? Request.from(request) : 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 Response.fromNullable((await this._channel.response()).response);
+ }
+ async _internalResponse() {
+ return Response.fromNullable((await this._channel.response()).response);
+ }
+ frame() {
+ if (!this._initializer.frame) {
+ (0, import_assert.assert)(this.serviceWorker());
+ throw new Error("Service Worker requests do not have an associated frame.");
+ }
+ const frame = import_frame.Frame.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 import_frame.Frame.fromNullable(this._initializer.frame)?._page || null;
+ }
+ serviceWorker() {
+ return this._initializer.serviceWorker ? import_worker.Worker.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 response = await this.response();
+ if (!response)
+ throw new Error("Unable to fetch sizes for failed request");
+ return (await response._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 ((0, import_rtti.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 import_manualPromise.LongStandingScope();
+ }
+}
+class Route extends import_channelOwner.ChannelOwner {
+ constructor(parent, type, guid, initializer) {
+ super(parent, type, guid, initializer);
+ this._handlingPromise = null;
+ this._didThrow = false;
+ }
+ static from(route) {
+ return route._object;
+ }
+ request() {
+ return Request.from(this._initializer.request);
+ }
+ async _raceWithTargetClose(promise) {
+ return await this.request()._targetClosedScope().safeRace(promise);
+ }
+ async _startHandling() {
+ this._handlingPromise = new import_manualPromise.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(url) {
+ await this._handleRoute(async () => {
+ await this._raceWithTargetClose(this._channel.redirectNavigationRequest({ url }));
+ });
+ }
+ 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) {
+ (0, import_assert.assert)(options.body === void 0, "Can specify either body or json parameters");
+ body = JSON.stringify(options.json);
+ }
+ if (options.response instanceof import_fetch.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 ((0, import_rtti.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"] = (0, import_mimeType.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: (0, import_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 ? (0, import_headers.headersObjectToArray)(options.headers) : void 0,
+ postData: options.postDataBuffer,
+ isFallback
+ }));
+ }
+}
+class WebSocketRoute extends import_channelOwner.ChannelOwner {
+ constructor(parent, type, guid, initializer) {
+ super(parent, type, 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;
+ },
+ close: async (options = {}) => {
+ await this._channel.closeServer({ ...options, wasClean: true }).catch(() => {
+ });
+ },
+ send: (message) => {
+ if ((0, import_rtti.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(route) {
+ return route._object;
+ }
+ url() {
+ return this._initializer.url;
+ }
+ 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 ((0, import_rtti.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(() => {
+ });
+ }
+}
+class WebSocketRouteHandler {
+ constructor(baseURL, url, handler) {
+ this._baseURL = baseURL;
+ this.url = url;
+ this.handler = handler;
+ }
+ static prepareInterceptionPatterns(handlers) {
+ const patterns = [];
+ let all = false;
+ for (const handler of handlers) {
+ if ((0, import_rtti.isString)(handler.url))
+ patterns.push({ glob: handler.url });
+ else if ((0, import_rtti.isRegExp)(handler.url))
+ patterns.push({ regexSource: handler.url.source, regexFlags: handler.url.flags });
+ else
+ all = true;
+ }
+ if (all)
+ return [{ glob: "**/*" }];
+ return patterns;
+ }
+ matches(wsURL) {
+ return (0, import_urlMatch.urlMatches)(this._baseURL, wsURL, this.url, true);
+ }
+ async handle(webSocketRoute) {
+ const handler = this.handler;
+ await handler(webSocketRoute);
+ await webSocketRoute._afterHandle();
+ }
+}
+class Response extends import_channelOwner.ChannelOwner {
+ constructor(parent, type, guid, initializer) {
+ super(parent, type, guid, initializer);
+ this._finishedPromise = new import_manualPromise.ManualPromise();
+ this._provisionalHeaders = new RawHeaders(initializer.headers);
+ this._request = Request.from(this._initializer.request);
+ Object.assign(this._request._timing, this._initializer.timing);
+ }
+ static from(response) {
+ return response._object;
+ }
+ static fromNullable(response) {
+ return response ? Response.from(response) : 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;
+ }
+}
+class WebSocket extends import_channelOwner.ChannelOwner {
+ static from(webSocket) {
+ return webSocket._object;
+ }
+ constructor(parent, type, guid, initializer) {
+ super(parent, type, guid, initializer);
+ this._isClosed = false;
+ this._page = parent;
+ this._channel.on("frameSent", (event) => {
+ if (event.opcode === 1)
+ this.emit(import_events.Events.WebSocket.FrameSent, { payload: event.data });
+ else if (event.opcode === 2)
+ this.emit(import_events.Events.WebSocket.FrameSent, { payload: Buffer.from(event.data, "base64") });
+ });
+ this._channel.on("frameReceived", (event) => {
+ if (event.opcode === 1)
+ this.emit(import_events.Events.WebSocket.FrameReceived, { payload: event.data });
+ else if (event.opcode === 2)
+ this.emit(import_events.Events.WebSocket.FrameReceived, { payload: Buffer.from(event.data, "base64") });
+ });
+ this._channel.on("socketError", ({ error }) => this.emit(import_events.Events.WebSocket.Error, error));
+ this._channel.on("close", () => {
+ this._isClosed = true;
+ this.emit(import_events.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 = import_waiter.Waiter.createForEvent(this, event);
+ waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`);
+ if (event !== import_events.Events.WebSocket.Error)
+ waiter.rejectOnEvent(this, import_events.Events.WebSocket.Error, new Error("Socket error"));
+ if (event !== import_events.Events.WebSocket.Close)
+ waiter.rejectOnEvent(this, import_events.Events.WebSocket.Close, new Error("Socket closed"));
+ waiter.rejectOnEvent(this._page, import_events.Events.Page.Close, () => this._page._closeErrorWithReason());
+ const result = await waiter.waitForEvent(this, event, predicate);
+ waiter.dispose();
+ return result;
+ });
+ }
+}
+function validateHeaders(headers) {
+ for (const key of Object.keys(headers)) {
+ const value = headers[key];
+ if (!Object.is(value, void 0) && !(0, import_rtti.isString)(value))
+ throw new Error(`Expected value of header "${key}" to be String, but "${typeof value}" is found.`);
+ }
+}
+class RouteHandler {
+ constructor(platform, baseURL, url, 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 = url;
+ this.handler = handler;
+ this._savedZone = platform.zones.current().pop();
+ }
+ static prepareInterceptionPatterns(handlers) {
+ const patterns = [];
+ let all = false;
+ for (const handler of handlers) {
+ if ((0, import_rtti.isString)(handler.url))
+ patterns.push({ glob: handler.url });
+ else if ((0, import_rtti.isRegExp)(handler.url))
+ patterns.push({ regexSource: handler.url.source, regexFlags: handler.url.flags });
+ else
+ all = true;
+ }
+ if (all)
+ return [{ glob: "**/*" }];
+ return patterns;
+ }
+ matches(requestURL) {
+ return (0, import_urlMatch.urlMatches)(this._baseURL, requestURL, this.url);
+ }
+ async handle(route) {
+ return await this._savedZone.run(async () => this._handleImpl(route));
+ }
+ async _handleImpl(route) {
+ const handlerInvocation = { complete: new import_manualPromise.ManualPromise(), route };
+ this._activeInvocations.add(handlerInvocation);
+ try {
+ return await this._handleInternal(route);
+ } catch (e) {
+ if (this._ignoreException)
+ return false;
+ if ((0, import_errors.isTargetClosedError)(e)) {
+ (0, import_stackTrace.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(route) {
+ ++this.handledCount;
+ const handledPromise = route._startHandling();
+ const handler = this.handler;
+ const [handled] = await Promise.all([
+ handledPromise,
+ handler(route, route.request())
+ ]);
+ return handled;
+ }
+ willExpire() {
+ return this.handledCount + 1 >= this._times;
+ }
+}
+class RawHeaders {
+ constructor(headers) {
+ this._headersMap = new import_multimap.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, value]) => ({
+ name,
+ value
+ })).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 result = {};
+ for (const name of this._headersMap.keys())
+ result[name] = this.get(name);
+ return result;
+ }
+ headersArray() {
+ return this._headersArray;
+ }
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ RawHeaders,
+ Request,
+ Response,
+ Route,
+ RouteHandler,
+ WebSocket,
+ WebSocketRoute,
+ WebSocketRouteHandler,
+ validateHeaders
+});
diff --git a/node_modules/playwright-core/lib/client/page.js b/node_modules/playwright-core/lib/client/page.js
new file mode 100644
index 0000000..24ccf7b
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/page.js
@@ -0,0 +1,709 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var page_exports = {};
+__export(page_exports, {
+ BindingCall: () => BindingCall,
+ Page: () => Page
+});
+module.exports = __toCommonJS(page_exports);
+var import_accessibility = require("./accessibility");
+var import_artifact = require("./artifact");
+var import_channelOwner = require("./channelOwner");
+var import_clientHelper = require("./clientHelper");
+var import_coverage = require("./coverage");
+var import_download = require("./download");
+var import_elementHandle = require("./elementHandle");
+var import_errors = require("./errors");
+var import_events = require("./events");
+var import_fileChooser = require("./fileChooser");
+var import_frame = require("./frame");
+var import_harRouter = require("./harRouter");
+var import_input = require("./input");
+var import_jsHandle = require("./jsHandle");
+var import_network = require("./network");
+var import_video = require("./video");
+var import_waiter = require("./waiter");
+var import_worker = require("./worker");
+var import_timeoutSettings = require("./timeoutSettings");
+var import_assert = require("../utils/isomorphic/assert");
+var import_fileUtils = require("./fileUtils");
+var import_headers = require("../utils/isomorphic/headers");
+var import_stringUtils = require("../utils/isomorphic/stringUtils");
+var import_urlMatch = require("../utils/isomorphic/urlMatch");
+var import_manualPromise = require("../utils/isomorphic/manualPromise");
+var import_rtti = require("../utils/isomorphic/rtti");
+class Page extends import_channelOwner.ChannelOwner {
+ constructor(parent, type, guid, initializer) {
+ super(parent, type, guid, initializer);
+ this._frames = /* @__PURE__ */ new Set();
+ this._workers = /* @__PURE__ */ new Set();
+ this._closed = false;
+ this._closedOrCrashedScope = new import_manualPromise.LongStandingScope();
+ this._routes = [];
+ this._webSocketRoutes = [];
+ this._bindings = /* @__PURE__ */ new Map();
+ this._video = null;
+ this._closeWasCalled = false;
+ this._harRouters = [];
+ this._locatorHandlers = /* @__PURE__ */ new Map();
+ this._browserContext = parent;
+ this._timeoutSettings = new import_timeoutSettings.TimeoutSettings(this._platform, this._browserContext._timeoutSettings);
+ this.accessibility = new import_accessibility.Accessibility(this._channel);
+ this.keyboard = new import_input.Keyboard(this);
+ this.mouse = new import_input.Mouse(this);
+ this.request = this._browserContext.request;
+ this.touchscreen = new import_input.Touchscreen(this);
+ this.clock = this._browserContext.clock;
+ this._mainFrame = import_frame.Frame.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._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, suggestedFilename, artifact }) => {
+ const artifactObject = import_artifact.Artifact.from(artifact);
+ this.emit(import_events.Events.Page.Download, new import_download.Download(this, url, suggestedFilename, artifactObject));
+ });
+ this._channel.on("fileChooser", ({ element, isMultiple }) => this.emit(import_events.Events.Page.FileChooser, new import_fileChooser.FileChooser(this, import_elementHandle.ElementHandle.from(element), isMultiple)));
+ this._channel.on("frameAttached", ({ frame }) => this._onFrameAttached(import_frame.Frame.from(frame)));
+ this._channel.on("frameDetached", ({ frame }) => this._onFrameDetached(import_frame.Frame.from(frame)));
+ this._channel.on("locatorHandlerTriggered", ({ uid }) => this._onLocatorHandlerTriggered(uid));
+ this._channel.on("route", ({ route }) => this._onRoute(import_network.Route.from(route)));
+ this._channel.on("webSocketRoute", ({ webSocketRoute }) => this._onWebSocketRoute(import_network.WebSocketRoute.from(webSocketRoute)));
+ this._channel.on("video", ({ artifact }) => {
+ const artifactObject = import_artifact.Artifact.from(artifact);
+ this._forceVideo()._artifactReady(artifactObject);
+ });
+ this._channel.on("viewportSizeChanged", ({ viewportSize }) => this._viewportSize = viewportSize);
+ this._channel.on("webSocket", ({ webSocket }) => this.emit(import_events.Events.Page.WebSocket, import_network.WebSocket.from(webSocket)));
+ this._channel.on("worker", ({ worker }) => this._onWorker(import_worker.Worker.from(worker)));
+ this.coverage = new import_coverage.Coverage(this._channel);
+ this.once(import_events.Events.Page.Close, () => this._closedOrCrashedScope.close(this._closeErrorWithReason()));
+ this.once(import_events.Events.Page.Crash, () => this._closedOrCrashedScope.close(new import_errors.TargetClosedError()));
+ this._setEventToSubscriptionMapping(/* @__PURE__ */ new Map([
+ [import_events.Events.Page.Console, "console"],
+ [import_events.Events.Page.Dialog, "dialog"],
+ [import_events.Events.Page.Request, "request"],
+ [import_events.Events.Page.Response, "response"],
+ [import_events.Events.Page.RequestFinished, "requestFinished"],
+ [import_events.Events.Page.RequestFailed, "requestFailed"],
+ [import_events.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(import_events.Events.Page.FrameAttached, frame);
+ }
+ _onFrameDetached(frame) {
+ this._frames.delete(frame);
+ frame._detached = true;
+ if (frame._parentFrame)
+ frame._parentFrame._childFrames.delete(frame);
+ this.emit(import_events.Events.Page.FrameDetached, frame);
+ }
+ async _onRoute(route) {
+ route._context = this.context();
+ const routeHandlers = this._routes.slice();
+ for (const routeHandler of routeHandlers) {
+ if (this._closeWasCalled || this._browserContext._closingStatus !== "none")
+ return;
+ if (!routeHandler.matches(route.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(route);
+ if (!this._routes.length)
+ this._updateInterceptionPatterns({ internal: true }).catch(() => {
+ });
+ if (handled)
+ return;
+ }
+ await this._browserContext._onRoute(route);
+ }
+ async _onWebSocketRoute(webSocketRoute) {
+ const routeHandler = this._webSocketRoutes.find((route) => route.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(import_events.Events.Page.Worker, worker);
+ }
+ _onClose() {
+ this._closed = true;
+ this._browserContext._pages.delete(this);
+ this._browserContext._backgroundPages.delete(this);
+ this._disposeHarRouters();
+ this.emit(import_events.Events.Page.Close, this);
+ }
+ _onCrash() {
+ this.emit(import_events.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 = (0, import_rtti.isString)(frameSelector) ? frameSelector : frameSelector.name;
+ const url = (0, import_rtti.isObject)(frameSelector) ? frameSelector.url : void 0;
+ (0, import_assert.assert)(name || url, "Either name or url matcher should be specified");
+ return this.frames().find((f) => {
+ if (name)
+ return f.name() === name;
+ return (0, import_urlMatch.urlMatches)(this._browserContext._options.baseURL, f.url(), url);
+ }) || null;
+ }
+ frames() {
+ return [...this._frames];
+ }
+ setDefaultNavigationTimeout(timeout) {
+ this._timeoutSettings.setDefaultNavigationTimeout(timeout);
+ }
+ setDefaultTimeout(timeout) {
+ this._timeoutSettings.setDefaultTimeout(timeout);
+ }
+ _forceVideo() {
+ if (!this._video)
+ this._video = new import_video.Video(this, this._connection);
+ return this._video;
+ }
+ video() {
+ if (!this._browserContext._options.recordVideo)
+ return null;
+ return this._forceVideo();
+ }
+ async $(selector, options) {
+ return await this._mainFrame.$(selector, options);
+ }
+ async waitForSelector(selector, options) {
+ return await this._mainFrame.waitForSelector(selector, options);
+ }
+ async dispatchEvent(selector, type, eventInit, options) {
+ return await this._mainFrame.dispatchEvent(selector, type, eventInit, options);
+ }
+ async evaluateHandle(pageFunction, arg) {
+ (0, import_jsHandle.assertMaxArguments)(arguments.length, 2);
+ return await this._mainFrame.evaluateHandle(pageFunction, arg);
+ }
+ async $eval(selector, pageFunction, arg) {
+ (0, import_jsHandle.assertMaxArguments)(arguments.length, 3);
+ return await this._mainFrame.$eval(selector, pageFunction, arg);
+ }
+ async $$eval(selector, pageFunction, arg) {
+ (0, import_jsHandle.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) {
+ await this._channel.exposeBinding({ name });
+ const binding = (source, ...args) => callback(...args);
+ this._bindings.set(name, binding);
+ }
+ async exposeBinding(name, callback, options = {}) {
+ await this._channel.exposeBinding({ name, needsHandle: options.handle });
+ this._bindings.set(name, callback);
+ }
+ async setExtraHTTPHeaders(headers) {
+ (0, import_network.validateHeaders)(headers);
+ await this._channel.setExtraHTTPHeaders({ headers: (0, import_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(url, options) {
+ return await this._mainFrame.goto(url, options);
+ }
+ async reload(options = {}) {
+ const waitUntil = (0, import_frame.verifyLoadState)("waitUntil", options.waitUntil === void 0 ? "load" : options.waitUntil);
+ return import_network.Response.fromNullable((await this._channel.reload({ ...options, waitUntil, timeout: this._timeoutSettings.navigationTimeout(options) })).response);
+ }
+ async addLocatorHandler(locator, handler, options = {}) {
+ if (locator._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: locator._selector, noWaitAfter: options.noWaitAfter });
+ this._locatorHandlers.set(uid, { locator, 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(locator) {
+ for (const [uid, data] of this._locatorHandlers) {
+ if (data.locator._equals(locator)) {
+ 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(url, options) {
+ return await this._mainFrame.waitForURL(url, options);
+ }
+ async waitForRequest(urlOrPredicate, options = {}) {
+ const predicate = async (request) => {
+ if ((0, import_rtti.isString)(urlOrPredicate) || (0, import_rtti.isRegExp)(urlOrPredicate))
+ return (0, import_urlMatch.urlMatches)(this._browserContext._options.baseURL, request.url(), urlOrPredicate);
+ return await urlOrPredicate(request);
+ };
+ const trimmedUrl = trimUrl(urlOrPredicate);
+ const logLine = trimmedUrl ? `waiting for request ${trimmedUrl}` : void 0;
+ return await this._waitForEvent(import_events.Events.Page.Request, { predicate, timeout: options.timeout }, logLine);
+ }
+ async waitForResponse(urlOrPredicate, options = {}) {
+ const predicate = async (response) => {
+ if ((0, import_rtti.isString)(urlOrPredicate) || (0, import_rtti.isRegExp)(urlOrPredicate))
+ return (0, import_urlMatch.urlMatches)(this._browserContext._options.baseURL, response.url(), urlOrPredicate);
+ return await urlOrPredicate(response);
+ };
+ const trimmedUrl = trimUrl(urlOrPredicate);
+ const logLine = trimmedUrl ? `waiting for response ${trimmedUrl}` : void 0;
+ return await this._waitForEvent(import_events.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 import_errors.TargetClosedError(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 = import_waiter.Waiter.createForEvent(this, event);
+ if (logLine)
+ waiter.log(logLine);
+ waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`);
+ if (event !== import_events.Events.Page.Crash)
+ waiter.rejectOnEvent(this, import_events.Events.Page.Crash, new Error("Page crashed"));
+ if (event !== import_events.Events.Page.Close)
+ waiter.rejectOnEvent(this, import_events.Events.Page.Close, () => this._closeErrorWithReason());
+ const result = await waiter.waitForEvent(this, event, predicate);
+ waiter.dispose();
+ return result;
+ });
+ }
+ async goBack(options = {}) {
+ const waitUntil = (0, import_frame.verifyLoadState)("waitUntil", options.waitUntil === void 0 ? "load" : options.waitUntil);
+ return import_network.Response.fromNullable((await this._channel.goBack({ ...options, waitUntil, timeout: this._timeoutSettings.navigationTimeout(options) })).response);
+ }
+ async goForward(options = {}) {
+ const waitUntil = (0, import_frame.verifyLoadState)("waitUntil", options.waitUntil === void 0 ? "load" : options.waitUntil);
+ return import_network.Response.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) {
+ (0, import_jsHandle.assertMaxArguments)(arguments.length, 2);
+ return await this._mainFrame.evaluate(pageFunction, arg);
+ }
+ async _evaluateFunction(functionDeclaration) {
+ return this._mainFrame._evaluateFunction(functionDeclaration);
+ }
+ async addInitScript(script, arg) {
+ const source = await (0, import_clientHelper.evaluationScript)(this._platform, script, arg);
+ await this._channel.addInitScript({ source });
+ }
+ async route(url, handler, options = {}) {
+ this._routes.unshift(new import_network.RouteHandler(this._platform, this._browserContext._options.baseURL, url, handler, options.times));
+ await this._updateInterceptionPatterns({ title: "Route requests" });
+ }
+ 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._recordIntoHAR(har, this, options);
+ return;
+ }
+ const harRouter = await import_harRouter.HarRouter.create(localUtils, har, options.notFound || "abort", { urlMatch: options.url });
+ this._harRouters.push(harRouter);
+ await harRouter.addPageRoute(this);
+ }
+ async routeWebSocket(url, handler) {
+ this._webSocketRoutes.unshift(new import_network.WebSocketRouteHandler(this._browserContext._options.baseURL, url, 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(url, handler) {
+ const removed = [];
+ const remaining = [];
+ for (const route of this._routes) {
+ if ((0, import_urlMatch.urlMatchesEqual)(route.url, url) && (!handler || route.handler === handler))
+ removed.push(route);
+ else
+ remaining.push(route);
+ }
+ 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 = import_network.RouteHandler.prepareInterceptionPatterns(this._routes);
+ await this._wrapApiCall(() => this._channel.setNetworkInterceptionPatterns({ patterns }), options);
+ }
+ async _updateWebSocketInterceptionPatterns(options) {
+ const patterns = import_network.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 = (0, import_elementHandle.determineScreenshotType)(options);
+ if (mask) {
+ copy.mask = mask.map((locator) => ({
+ frame: locator._frame._channel,
+ selector: locator._selector
+ }));
+ }
+ const result = await this._channel.screenshot(copy);
+ if (options.path) {
+ await (0, import_fileUtils.mkdirIfNeeded)(this._platform, options.path);
+ await this._platform.fs().promises.writeFile(options.path, result.binary);
+ }
+ return result.binary;
+ }
+ async _expectScreenshot(options) {
+ const mask = options?.mask ? options?.mask.map((locator2) => ({
+ frame: locator2._frame._channel,
+ selector: locator2._selector
+ })) : void 0;
+ const locator = options.locator ? {
+ frame: options.locator._frame._channel,
+ selector: options.locator._selector
+ } : void 0;
+ return await this._channel.expectScreenshot({
+ ...options,
+ isNot: !!options.isNot,
+ locator,
+ mask
+ });
+ }
+ 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;
+ this._closeWasCalled = true;
+ try {
+ if (this._ownedContext)
+ await this._ownedContext.close();
+ else
+ await this._channel.close(options);
+ } catch (e) {
+ if ((0, import_errors.isTargetClosedError)(e) && !options.runBeforeUnload)
+ return;
+ throw e;
+ }
+ }
+ isClosed() {
+ return this._closed;
+ }
+ async click(selector, options) {
+ return await this._mainFrame.click(selector, options);
+ }
+ async dragAndDrop(source, target, options) {
+ return await this._mainFrame.dragAndDrop(source, 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, value, options) {
+ return await this._mainFrame.fill(selector, value, options);
+ }
+ locator(selector, options) {
+ return this.mainFrame().locator(selector, options);
+ }
+ getByTestId(testId) {
+ return this.mainFrame().getByTestId(testId);
+ }
+ getByAltText(text, options) {
+ return this.mainFrame().getByAltText(text, options);
+ }
+ getByLabel(text, options) {
+ return this.mainFrame().getByLabel(text, options);
+ }
+ getByPlaceholder(text, options) {
+ return this.mainFrame().getByPlaceholder(text, options);
+ }
+ getByText(text, options) {
+ return this.mainFrame().getByText(text, options);
+ }
+ getByTitle(text, options) {
+ return this.mainFrame().getByTitle(text, 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, text, options) {
+ return await this._mainFrame.type(selector, text, 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);
+ }
+ 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 result = 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, result.pdf);
+ }
+ return result.pdf;
+ }
+ async _snapshotForAI(options = {}) {
+ const result = await this._channel.snapshotForAI({ timeout: this._timeoutSettings.timeout(options) });
+ return result.snapshot;
+ }
+}
+class BindingCall extends import_channelOwner.ChannelOwner {
+ static from(channel) {
+ return channel._object;
+ }
+ constructor(parent, type, guid, initializer) {
+ super(parent, type, guid, initializer);
+ }
+ async call(func) {
+ try {
+ const frame = import_frame.Frame.from(this._initializer.frame);
+ const source = {
+ context: frame._page.context(),
+ page: frame._page,
+ frame
+ };
+ let result;
+ if (this._initializer.handle)
+ result = await func(source, import_jsHandle.JSHandle.from(this._initializer.handle));
+ else
+ result = await func(source, ...this._initializer.args.map(import_jsHandle.parseResult));
+ this._channel.resolve({ result: (0, import_jsHandle.serializeArgument)(result) }).catch(() => {
+ });
+ } catch (e) {
+ this._channel.reject({ error: (0, import_errors.serializeError)(e) }).catch(() => {
+ });
+ }
+ }
+}
+function trimUrl(param) {
+ if ((0, import_rtti.isRegExp)(param))
+ return `/${(0, import_stringUtils.trimStringWithEllipsis)(param.source, 50)}/${param.flags}`;
+ if ((0, import_rtti.isString)(param))
+ return `"${(0, import_stringUtils.trimStringWithEllipsis)(param, 50)}"`;
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ BindingCall,
+ Page
+});
diff --git a/node_modules/playwright-core/lib/client/platform.js b/node_modules/playwright-core/lib/client/platform.js
new file mode 100644
index 0000000..6bce4f8
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/platform.js
@@ -0,0 +1,74 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var platform_exports = {};
+__export(platform_exports, {
+ emptyPlatform: () => emptyPlatform
+});
+module.exports = __toCommonJS(platform_exports);
+var import_colors = require("../utils/isomorphic/colors");
+const noopZone = {
+ push: () => noopZone,
+ pop: () => noopZone,
+ run: (func) => func(),
+ data: () => void 0
+};
+const emptyPlatform = {
+ name: "empty",
+ boxedStackPrefixes: () => [],
+ calculateSha1: async () => {
+ throw new Error("Not implemented");
+ },
+ colors: import_colors.webColors,
+ createGuid: () => {
+ throw new Error("Not implemented");
+ },
+ defaultMaxListeners: () => 10,
+ env: {},
+ fs: () => {
+ throw new Error("Not implemented");
+ },
+ inspectCustom: void 0,
+ isDebugMode: () => false,
+ isJSDebuggerAttached: () => false,
+ isLogEnabled(name) {
+ return false;
+ },
+ isUnderTest: () => false,
+ log(name, message) {
+ },
+ path: () => {
+ throw new Error("Function not implemented.");
+ },
+ pathSeparator: "/",
+ showInternalStackFrames: () => false,
+ streamFile(path, writable) {
+ throw new Error("Streams are not available");
+ },
+ streamReadable: (channel) => {
+ throw new Error("Streams are not available");
+ },
+ streamWritable: (channel) => {
+ throw new Error("Streams are not available");
+ },
+ zones: { empty: noopZone, current: () => noopZone }
+};
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ emptyPlatform
+});
diff --git a/node_modules/playwright-core/lib/client/playwright.js b/node_modules/playwright-core/lib/client/playwright.js
new file mode 100644
index 0000000..0ae8c95
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/playwright.js
@@ -0,0 +1,75 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var playwright_exports = {};
+__export(playwright_exports, {
+ Playwright: () => Playwright
+});
+module.exports = __toCommonJS(playwright_exports);
+var import_android = require("./android");
+var import_browser = require("./browser");
+var import_browserType = require("./browserType");
+var import_channelOwner = require("./channelOwner");
+var import_electron = require("./electron");
+var import_errors = require("./errors");
+var import_fetch = require("./fetch");
+var import_selectors = require("./selectors");
+class Playwright extends import_channelOwner.ChannelOwner {
+ constructor(parent, type, guid, initializer) {
+ super(parent, type, guid, initializer);
+ this.request = new import_fetch.APIRequest(this);
+ this.chromium = import_browserType.BrowserType.from(initializer.chromium);
+ this.chromium._playwright = this;
+ this.firefox = import_browserType.BrowserType.from(initializer.firefox);
+ this.firefox._playwright = this;
+ this.webkit = import_browserType.BrowserType.from(initializer.webkit);
+ this.webkit._playwright = this;
+ this._android = import_android.Android.from(initializer.android);
+ this._android._playwright = this;
+ this._electron = import_electron.Electron.from(initializer.electron);
+ this._electron._playwright = this;
+ this._bidiChromium = import_browserType.BrowserType.from(initializer._bidiChromium);
+ this._bidiChromium._playwright = this;
+ this._bidiFirefox = import_browserType.BrowserType.from(initializer._bidiFirefox);
+ this._bidiFirefox._playwright = this;
+ this.devices = this._connection.localUtils()?.devices ?? {};
+ this.selectors = new import_selectors.Selectors(this._connection._platform);
+ this.errors = { TimeoutError: import_errors.TimeoutError };
+ }
+ static from(channel) {
+ return channel._object;
+ }
+ _browserTypes() {
+ return [this.chromium, this.firefox, this.webkit, this._bidiChromium, this._bidiFirefox];
+ }
+ _preLaunchedBrowser() {
+ const browser = import_browser.Browser.from(this._initializer.preLaunchedBrowser);
+ browser._connectToBrowserType(this[browser._name], {}, void 0);
+ return browser;
+ }
+ _allContexts() {
+ return this._browserTypes().flatMap((type) => [...type._contexts]);
+ }
+ _allPages() {
+ return this._allContexts().flatMap((context) => context.pages());
+ }
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ Playwright
+});
diff --git a/node_modules/playwright-core/lib/client/selectors.js b/node_modules/playwright-core/lib/client/selectors.js
new file mode 100644
index 0000000..d831be9
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/selectors.js
@@ -0,0 +1,55 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var selectors_exports = {};
+__export(selectors_exports, {
+ Selectors: () => Selectors
+});
+module.exports = __toCommonJS(selectors_exports);
+var import_clientHelper = require("./clientHelper");
+var import_locator = require("./locator");
+class Selectors {
+ 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 source = await (0, import_clientHelper.evaluationScript)(this._platform, script, void 0, false);
+ const selectorEngine = { ...options, name, source };
+ for (const context of this._contextsForSelectors)
+ await context._channel.registerSelectorEngine({ selectorEngine });
+ this._selectorEngines.push(selectorEngine);
+ }
+ setTestIdAttribute(attributeName) {
+ this._testIdAttributeName = attributeName;
+ (0, import_locator.setTestIdAttribute)(attributeName);
+ for (const context of this._contextsForSelectors)
+ context._channel.setTestIdAttributeName({ testIdAttributeName: attributeName }).catch(() => {
+ });
+ }
+ _withSelectorOptions(options) {
+ return { ...options, selectorEngines: this._selectorEngines, testIdAttributeName: this._testIdAttributeName };
+ }
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ Selectors
+});
diff --git a/node_modules/playwright-core/lib/client/stream.js b/node_modules/playwright-core/lib/client/stream.js
new file mode 100644
index 0000000..ffcd068
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/stream.js
@@ -0,0 +1,39 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var stream_exports = {};
+__export(stream_exports, {
+ Stream: () => Stream
+});
+module.exports = __toCommonJS(stream_exports);
+var import_channelOwner = require("./channelOwner");
+class Stream extends import_channelOwner.ChannelOwner {
+ static from(Stream2) {
+ return Stream2._object;
+ }
+ constructor(parent, type, guid, initializer) {
+ super(parent, type, guid, initializer);
+ }
+ stream() {
+ return this._platform.streamReadable(this._channel);
+ }
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ Stream
+});
diff --git a/node_modules/playwright-core/lib/client/timeoutSettings.js b/node_modules/playwright-core/lib/client/timeoutSettings.js
new file mode 100644
index 0000000..0563633
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/timeoutSettings.js
@@ -0,0 +1,79 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var timeoutSettings_exports = {};
+__export(timeoutSettings_exports, {
+ TimeoutSettings: () => TimeoutSettings
+});
+module.exports = __toCommonJS(timeoutSettings_exports);
+var import_time = require("../utils/isomorphic/time");
+class TimeoutSettings {
+ 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 import_time.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 import_time.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 import_time.DEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT;
+ }
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ TimeoutSettings
+});
diff --git a/node_modules/playwright-core/lib/client/tracing.js b/node_modules/playwright-core/lib/client/tracing.js
new file mode 100644
index 0000000..de3df73
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/tracing.js
@@ -0,0 +1,117 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var tracing_exports = {};
+__export(tracing_exports, {
+ Tracing: () => Tracing
+});
+module.exports = __toCommonJS(tracing_exports);
+var import_artifact = require("./artifact");
+var import_channelOwner = require("./channelOwner");
+class Tracing extends import_channelOwner.ChannelOwner {
+ constructor(parent, type, guid, initializer) {
+ super(parent, type, guid, initializer);
+ this._includeSources = false;
+ this._isTracing = false;
+ }
+ static from(channel) {
+ return channel._object;
+ }
+ async start(options = {}) {
+ await this._wrapApiCall(async () => {
+ this._includeSources = !!options.sources;
+ 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);
+ });
+ }
+ async startChunk(options = {}) {
+ await this._wrapApiCall(async () => {
+ const { traceName } = await this._channel.tracingStartChunk(options);
+ await this._startCollectingStacks(traceName);
+ });
+ }
+ async group(name, options = {}) {
+ await this._channel.tracingGroup({ name, location: options.location });
+ }
+ async groupEnd() {
+ await this._channel.tracingGroupEnd();
+ }
+ async _startCollectingStacks(traceName) {
+ if (!this._isTracing) {
+ this._isTracing = true;
+ this._connection.setIsTracing(true);
+ }
+ const result = await this._connection.localUtils()?.tracingStarted({ tracesDir: this._tracesDir, traceName });
+ this._stacksId = result?.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 _doStopChunk(filePath) {
+ this._resetStackCounter();
+ 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 result2 = await this._channel.tracingStopChunk({ mode: "entries" });
+ await localUtils.zip({ zipFile: filePath, entries: result2.entries, mode: "write", stacksId: this._stacksId, includeSources: this._includeSources });
+ return;
+ }
+ const result = await this._channel.tracingStopChunk({ mode: "archive" });
+ if (!result.artifact) {
+ if (this._stacksId)
+ await localUtils.traceDiscarded({ stacksId: this._stacksId });
+ return;
+ }
+ const artifact = import_artifact.Artifact.from(result.artifact);
+ await artifact.saveAs(filePath);
+ await artifact.delete();
+ await localUtils.zip({ zipFile: filePath, entries: [], mode: "append", stacksId: this._stacksId, includeSources: this._includeSources });
+ }
+ _resetStackCounter() {
+ if (this._isTracing) {
+ this._isTracing = false;
+ this._connection.setIsTracing(false);
+ }
+ }
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ Tracing
+});
diff --git a/node_modules/playwright-core/lib/client/types.js b/node_modules/playwright-core/lib/client/types.js
new file mode 100644
index 0000000..56e8c53
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/types.js
@@ -0,0 +1,28 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var types_exports = {};
+__export(types_exports, {
+ kLifecycleEvents: () => kLifecycleEvents
+});
+module.exports = __toCommonJS(types_exports);
+const kLifecycleEvents = /* @__PURE__ */ new Set(["load", "domcontentloaded", "networkidle", "commit"]);
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ kLifecycleEvents
+});
diff --git a/node_modules/playwright-core/lib/client/video.js b/node_modules/playwright-core/lib/client/video.js
new file mode 100644
index 0000000..ddc8eb7
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/video.js
@@ -0,0 +1,59 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var video_exports = {};
+__export(video_exports, {
+ Video: () => Video
+});
+module.exports = __toCommonJS(video_exports);
+var import_manualPromise = require("../utils/isomorphic/manualPromise");
+class Video {
+ constructor(page, connection) {
+ this._artifact = null;
+ this._artifactReadyPromise = new import_manualPromise.ManualPromise();
+ this._isRemote = false;
+ this._isRemote = connection.isRemote();
+ this._artifact = page._closedOrCrashedScope.safeRace(this._artifactReadyPromise);
+ }
+ _artifactReady(artifact) {
+ this._artifactReadyPromise.resolve(artifact);
+ }
+ async path() {
+ if (this._isRemote)
+ throw new Error(`Path is not available when connecting remotely. Use saveAs() to save a local copy.`);
+ const artifact = await this._artifact;
+ if (!artifact)
+ throw new Error("Page did not produce any video frames");
+ return artifact._initializer.absolutePath;
+ }
+ async saveAs(path) {
+ const artifact = await this._artifact;
+ if (!artifact)
+ throw new Error("Page did not produce any video frames");
+ return await artifact.saveAs(path);
+ }
+ async delete() {
+ const artifact = await this._artifact;
+ if (artifact)
+ await artifact.delete();
+ }
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ Video
+});
diff --git a/node_modules/playwright-core/lib/client/waiter.js b/node_modules/playwright-core/lib/client/waiter.js
new file mode 100644
index 0000000..75eac08
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/waiter.js
@@ -0,0 +1,142 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var waiter_exports = {};
+__export(waiter_exports, {
+ Waiter: () => Waiter
+});
+module.exports = __toCommonJS(waiter_exports);
+var import_errors = require("./errors");
+var import_stackTrace = require("../utils/isomorphic/stackTrace");
+class Waiter {
+ constructor(channelOwner, event) {
+ this._failures = [];
+ this._logs = [];
+ this._waitId = channelOwner._platform.createGuid();
+ this._channelOwner = channelOwner;
+ this._savedZone = channelOwner._platform.zones.current().pop();
+ this._channelOwner._channel.waitForEventInfo({ info: { waitId: this._waitId, phase: "before", event } }).catch(() => {
+ });
+ this._dispose = [
+ () => this._channelOwner._wrapApiCall(async () => {
+ await this._channelOwner._channel.waitForEventInfo({ info: { waitId: this._waitId, phase: "after", error: this._error } });
+ }, { internal: true }).catch(() => {
+ })
+ ];
+ }
+ static createForEvent(channelOwner, event) {
+ return new Waiter(channelOwner, event);
+ }
+ 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 import_errors.TimeoutError(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 result = await Promise.race([promise, ...this._failures]);
+ if (dispose)
+ dispose();
+ return result;
+ } catch (e) {
+ if (dispose)
+ dispose();
+ this._error = e.message;
+ this.dispose();
+ (0, import_stackTrace.rewriteErrorMessage)(e, e.message + formatLogRecording(this._logs));
+ throw e;
+ }
+ }
+ log(s) {
+ this._logs.push(s);
+ this._channelOwner._wrapApiCall(async () => {
+ await this._channelOwner._channel.waitForEventInfo({ info: { waitId: this._waitId, phase: "log", message: s } });
+ }, { internal: true }).catch(() => {
+ });
+ }
+ _rejectOn(promise, dispose) {
+ this._failures.push(promise);
+ if (dispose)
+ this._dispose.push(dispose);
+ }
+}
+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(log) {
+ if (!log.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)}
+${log.join("\n")}
+${"=".repeat(headerLength)}`;
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ Waiter
+});
diff --git a/node_modules/playwright-core/lib/client/webError.js b/node_modules/playwright-core/lib/client/webError.js
new file mode 100644
index 0000000..f0eae68
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/webError.js
@@ -0,0 +1,39 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var webError_exports = {};
+__export(webError_exports, {
+ WebError: () => WebError
+});
+module.exports = __toCommonJS(webError_exports);
+class WebError {
+ constructor(page, error) {
+ this._page = page;
+ this._error = error;
+ }
+ page() {
+ return this._page;
+ }
+ error() {
+ return this._error;
+ }
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ WebError
+});
diff --git a/node_modules/playwright-core/lib/client/webSocket.js b/node_modules/playwright-core/lib/client/webSocket.js
new file mode 100644
index 0000000..4f7e47c
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/webSocket.js
@@ -0,0 +1,93 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var webSocket_exports = {};
+__export(webSocket_exports, {
+ connectOverWebSocket: () => connectOverWebSocket
+});
+module.exports = __toCommonJS(webSocket_exports);
+var import_connection = require("./connection");
+async function connectOverWebSocket(parentConnection, params) {
+ const localUtils = parentConnection.localUtils();
+ const transport = localUtils ? new JsonPipeTransport(localUtils) : new WebSocketTransport();
+ const connectHeaders = await transport.connect(params);
+ const connection = new import_connection.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;
+}
+class JsonPipeTransport {
+ constructor(owner) {
+ this._owner = owner;
+ }
+ async connect(params) {
+ const { pipe, headers: connectHeaders } = await this._owner._channel.connect(params);
+ 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(() => {
+ });
+ }
+}
+class WebSocketTransport {
+ async connect(params) {
+ this._ws = new window.WebSocket(params.wsEndpoint);
+ 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();
+ }
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ connectOverWebSocket
+});
diff --git a/node_modules/playwright-core/lib/client/worker.js b/node_modules/playwright-core/lib/client/worker.js
new file mode 100644
index 0000000..09f20e5
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/worker.js
@@ -0,0 +1,63 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var worker_exports = {};
+__export(worker_exports, {
+ Worker: () => Worker
+});
+module.exports = __toCommonJS(worker_exports);
+var import_channelOwner = require("./channelOwner");
+var import_errors = require("./errors");
+var import_events = require("./events");
+var import_jsHandle = require("./jsHandle");
+var import_manualPromise = require("../utils/isomorphic/manualPromise");
+class Worker extends import_channelOwner.ChannelOwner {
+ constructor(parent, type, guid, initializer) {
+ super(parent, type, guid, initializer);
+ // Set for service workers.
+ this._closedScope = new import_manualPromise.LongStandingScope();
+ this._channel.on("close", () => {
+ if (this._page)
+ this._page._workers.delete(this);
+ if (this._context)
+ this._context._serviceWorkers.delete(this);
+ this.emit(import_events.Events.Worker.Close, this);
+ });
+ this.once(import_events.Events.Worker.Close, () => this._closedScope.close(this._page?._closeErrorWithReason() || new import_errors.TargetClosedError()));
+ }
+ static from(worker) {
+ return worker._object;
+ }
+ url() {
+ return this._initializer.url;
+ }
+ async evaluate(pageFunction, arg) {
+ (0, import_jsHandle.assertMaxArguments)(arguments.length, 2);
+ const result = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: (0, import_jsHandle.serializeArgument)(arg) });
+ return (0, import_jsHandle.parseResult)(result.value);
+ }
+ async evaluateHandle(pageFunction, arg) {
+ (0, import_jsHandle.assertMaxArguments)(arguments.length, 2);
+ const result = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: (0, import_jsHandle.serializeArgument)(arg) });
+ return import_jsHandle.JSHandle.from(result.handle);
+ }
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ Worker
+});
diff --git a/node_modules/playwright-core/lib/client/writableStream.js b/node_modules/playwright-core/lib/client/writableStream.js
new file mode 100644
index 0000000..09aed99
--- /dev/null
+++ b/node_modules/playwright-core/lib/client/writableStream.js
@@ -0,0 +1,39 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var writableStream_exports = {};
+__export(writableStream_exports, {
+ WritableStream: () => WritableStream
+});
+module.exports = __toCommonJS(writableStream_exports);
+var import_channelOwner = require("./channelOwner");
+class WritableStream extends import_channelOwner.ChannelOwner {
+ static from(Stream) {
+ return Stream._object;
+ }
+ constructor(parent, type, guid, initializer) {
+ super(parent, type, guid, initializer);
+ }
+ stream() {
+ return this._platform.streamWritable(this._channel);
+ }
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ WritableStream
+});
diff --git a/node_modules/playwright-core/lib/generated/bindingsControllerSource.js b/node_modules/playwright-core/lib/generated/bindingsControllerSource.js
new file mode 100644
index 0000000..14dcf85
--- /dev/null
+++ b/node_modules/playwright-core/lib/generated/bindingsControllerSource.js
@@ -0,0 +1,28 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var bindingsControllerSource_exports = {};
+__export(bindingsControllerSource_exports, {
+ source: () => source
+});
+module.exports = __toCommonJS(bindingsControllerSource_exports);
+const 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/bindingsController.ts\nvar bindingsController_exports = {};\n__export(bindingsController_exports, {\n BindingsController: () => BindingsController\n});\nmodule.exports = __toCommonJS(bindingsController_exports);\n\n// packages/playwright-core/src/utils/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}\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 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 // eslint-disable-next-line no-restricted-globals\n constructor(global, globalBindingName) {\n this._bindings = /* @__PURE__ */ new Map();\n this._global = global;\n this._globalBindingName = globalBindingName;\n }\n addBinding(bindingName, needsHandle) {\n const data = {\n callbacks: /* @__PURE__ */ new Map(),\n lastSeq: 0,\n handles: /* @__PURE__ */ new Map(),\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 if (needsHandle && args.slice(1).some((arg) => arg !== void 0))\n throw new Error(`exposeBindingHandle supports a single argument, ${args.length} received`);\n const seq = ++data.lastSeq;\n const promise = new Promise((resolve, reject) => data.callbacks.set(seq, { resolve, reject }));\n let payload;\n if (needsHandle) {\n data.handles.set(seq, args[0]);\n payload = { name: bindingName, seq };\n } else {\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 payload = { name: bindingName, seq, serializedArgs };\n }\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 takeBindingHandle(arg) {\n const handles = this._bindings.get(arg.name).handles;\n const handle = handles.get(arg.seq);\n handles.delete(arg.seq);\n return handle;\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';
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ source
+});
diff --git a/node_modules/playwright-core/lib/generated/clockSource.js b/node_modules/playwright-core/lib/generated/clockSource.js
new file mode 100644
index 0000000..5837ac5
--- /dev/null
+++ b/node_modules/playwright-core/lib/generated/clockSource.js
@@ -0,0 +1,28 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var clockSource_exports = {};
+__export(clockSource_exports, {
+ source: () => source
+});
+module.exports = __toCommonJS(clockSource_exports);
+const 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._innerSetTime(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 _innerSetFixedTime(time) {\n this._innerSetTime(time);\n this._now.isFixedTime = true;\n }\n _advanceNow(to) {\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._runTo(shiftTicks(this._now.ticks, ticks));\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 this._innerPause();\n const toConsume = time - this._now.time;\n await this._innerFastForwardTo(shiftTicks(this._now.ticks, toConsume));\n return toConsume;\n }\n _innerPause() {\n this._realTime = void 0;\n this._updateRealTimeTimer();\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 (!this._realTime) {\n (_a = this._currentRealTimeTimer) == null ? void 0 : _a.dispose();\n this._currentRealTimeTimer = void 0;\n return;\n }\n const firstTimer = this._firstTimer();\n const callAt = Math.min(firstTimer ? firstTimer.callAt : this._now.ticks + maxTimeout, this._now.ticks + 100);\n if (this._currentRealTimeTimer && this._currentRealTimeTimer.callAt < callAt)\n return;\n if (this._currentRealTimeTimer) {\n this._currentRealTimeTimer.dispose();\n this._currentRealTimeTimer = void 0;\n }\n this._currentRealTimeTimer = {\n callAt,\n dispose: this._embedder.setTimeout(() => {\n this._currentRealTimeTimer = void 0;\n this._syncRealTime();\n void this._runTo(this._now.ticks).catch((e) => console.error(e)).then(() => this._updateRealTimeTimer());\n }, callAt - this._now.ticks)\n };\n }\n async fastForward(ticks) {\n this._replayLogOnce();\n await this._innerFastForwardTo(shiftTicks(this._now.ticks, ticks | 0));\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 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._innerSetTime(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._innerPause();\n this._innerSetTime(asWallTime(param));\n } else if (type === "resume") {\n this._innerResume();\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 && lastLogTime > 0)\n this._advanceNow(shiftTicks(this._now.ticks, this._embedder.dateNow() - lastLogTime));\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 };\n const bound = { ...raw };\n for (const key of Object.keys(bound)) {\n if (key !== "Date" && 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) {\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 };\n}\nfunction getClearHandler(type) {\n if (type === "IdleCallback" || type === "AnimationFrame")\n return `cancel${type}`;\n return `clear${type}`;\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\n result[key] = () => {\n };\n }\n return result;\n}\nfunction createClock(globalObject) {\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);\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);\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 === "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) {\n const builtins = platformOriginals(globalObject).bound;\n const { clock: controller } = install(globalObject);\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';
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+ source
+});
diff --git a/node_modules/playwright-core/lib/generated/injectedScriptSource.js b/node_modules/playwright-core/lib/generated/injectedScriptSource.js
new file mode 100644
index 0000000..52ec557
--- /dev/null
+++ b/node_modules/playwright-core/lib/generated/injectedScriptSource.js
@@ -0,0 +1,28 @@
+"use strict";
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var injectedScriptSource_exports = {};
+__export(injectedScriptSource_exports, {
+ source: () => source
+});
+module.exports = __toCommonJS(injectedScriptSource_exports);
+const 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/injectedScript.ts\nvar injectedScript_exports = {};\n__export(injectedScript_exports, {\n InjectedScript: () => InjectedScript\n});\nmodule.exports = __toCommonJS(injectedScript_exports);\n\n// packages/playwright-core/src/utils/isomorphic/ariaSnapshot.ts\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: valueOrRegex(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)] = valueOrRegex(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: valueOrRegex(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 valueOrRegex(value) {\n return value.startsWith("/") && value.endsWith("/") && value.length > 1 ? { pattern: value.slice(1, -1) } : normalizeWhitespace(value);\n}\nvar KeyParser = class _KeyParser {\n static parse(text, options, errors) {\n try {\n return new _KeyParser(text.value, options)._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, options) {\n this._input = input;\n this._pos = 0;\n this._length = input.length;\n this._options = options;\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 === "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};\n\n// packages/playwright-core/src/utils/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), c2 = next(2), 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/playwright-core/src/utils/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/playwright-core/src/utils/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/playwright-core/src/utils/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}\n\n// packages/playwright-core/src/utils/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 options.exact = attr.caseSensitive;\n options.name = 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 (options.exact)\n attrs.push(`exact: true`);\n }\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 (options.exact)\n attrs.push(`exact=True`);\n }\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 (options.exact)\n attrs.push(`.setExact(true)`);\n }\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 (options.exact)\n attrs.push(`Exact = true`);\n }\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/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 return element.ownerDocument && element.ownerDocument.defaultView ? element.ownerDocument.defaultView.getComputedStyle(element, pseudo) : void 0;\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 box(element) {\n const style = getElementComputedStyle(element);\n if (!style)\n return { visible: true };\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, style };\n if (child.nodeType === 3 && isVisibleTextNode(child))\n return { visible: true, style };\n }\n return { visible: false, style };\n }\n if (!isElementStyleVisibilityVisible(element, style))\n return { style, visible: false };\n const rect = element.getBoundingClientRect();\n return { rect, style, visible: rect.width > 0 && rect.height > 0 };\n}\nfunction isElementVisible(element) {\n return box(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 if (element instanceof HTMLFormElement)\n return "FORM";\n return element.tagName.toUpperCase();\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 if (e.getAttribute("scope") === "col")\n return "columnheader";\n if (e.getAttribute("scope") === "row")\n return "rowheader";\n const table = closestCrossShadow(e, "table");\n const role = table ? getExplicitAriaRole(table) : "";\n return role === "grid" || role === "treegrid" ? "gridcell" : "cell";\n },\n "THEAD": () => "rowgroup",\n "TIME": () => "time",\n "TR": () => "row",\n "UL": () => "list"\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 belongsToDisplayNoneOrAriaHiddenOrNonSlottedOrInert(element);\n}\nfunction belongsToDisplayNoneOrAriaHiddenOrNonSlottedOrInert(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 || element.getAttribute("inert") !== null;\n }\n if (!hidden) {\n const parent = parentElementOrShadowHost(element);\n if (parent)\n hidden = belongsToDisplayNoneOrAriaHiddenOrNonSlottedOrInert(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 && style.display !== "none" && style.visibility !== "hidden") {\n content = parseCSSContentPropertyAsString(element, style.content, !!pseudo);\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}\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 cachesCounter = 0;\nfunction beginAriaCaches() {\n ++cachesCounter;\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 (!--cachesCounter) {\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}\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/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/ariaSnapshot.ts\nvar lastRef = 0;\nfunction toInternalOptions(options) {\n if (options.mode === "ai") {\n return {\n visibility: "ariaOrVisible",\n refs: "interactable",\n refPrefix: options.refPrefix,\n includeGenericRole: true,\n renderActive: true,\n renderCursorPointer: true\n };\n }\n if (options.mode === "autoexpect") {\n return { visibility: "ariaAndVisible", refs: "none" };\n }\n if (options.mode === "codegen") {\n return { visibility: "aria", refs: "none", renderStringsAsRegex: true };\n }\n return { visibility: "aria", refs: "none" };\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: [], element: rootElement, props: {}, box: box(rootElement), receivesPointerEvents: true },\n elements: /* @__PURE__ */ new Map(),\n refs: /* @__PURE__ */ new Map()\n };\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 }\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 }\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 let ariaRef;\n ariaRef = ariaNode.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 ariaNode.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 element,\n box: box(element),\n receivesPointerEvents: true,\n active\n };\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 result = {\n role,\n name,\n children: [],\n props: {},\n element,\n box: box(element),\n receivesPointerEvents: receivesPointerEvents2,\n active\n };\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 (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" && 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 matchesText(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 matchesTextNode(text, template) {\n return matchesText(text, template.text);\n}\nfunction matchesName(text, template) {\n return matchesText(text, template.name);\n}\nfunction matchesExpectAriaTemplate(rootElement, template) {\n const snapshot = generateAriaTree(rootElement, { mode: "expect" });\n const matches = matchesNodeDeep(snapshot.root, template, false, false);\n return {\n matches,\n received: {\n raw: renderAriaTree(snapshot, { mode: "expect" }),\n regex: renderAriaTree(snapshot, { mode: "codegen" })\n }\n };\n}\nfunction getAllElementsMatchingExpectAriaTemplate(rootElement, template) {\n const root = generateAriaTree(rootElement, { mode: "expect" }).root;\n const matches = matchesNodeDeep(root, template, true, false);\n return matches.map((n) => n.element);\n}\nfunction matchesNode(node, template, isDeepEqual) {\n var _a;\n if (typeof node === "string" && template.kind === "text")\n return matchesTextNode(node, template);\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.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 (!matchesName(node.name, template))\n return false;\n if (!matchesText(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 renderAriaTree(ariaSnapshot, publicOptions) {\n const options = toInternalOptions(publicOptions);\n const lines = [];\n const includeText = options.renderStringsAsRegex ? textContributesInfo : () => true;\n const renderString = options.renderStringsAsRegex ? convertToBestGuessRegex : (str) => str;\n const visit = (ariaNode2, parentAriaNode, indent) => {\n if (typeof ariaNode2 === "string") {\n if (parentAriaNode && !includeText(parentAriaNode, ariaNode2))\n return;\n const text = yamlEscapeValueIfNeeded(renderString(ariaNode2));\n if (text)\n lines.push(indent + "- text: " + text);\n return;\n }\n let key = ariaNode2.role;\n if (ariaNode2.name && ariaNode2.name.length <= 900) {\n const name = renderString(ariaNode2.name);\n if (name) {\n const stringifiedName = name.startsWith("/") && name.endsWith("/") ? name : JSON.stringify(name);\n key += " " + stringifiedName;\n }\n }\n if (ariaNode2.checked === "mixed")\n key += ` [checked=mixed]`;\n if (ariaNode2.checked === true)\n key += ` [checked]`;\n if (ariaNode2.disabled)\n key += ` [disabled]`;\n if (ariaNode2.expanded)\n key += ` [expanded]`;\n if (ariaNode2.active && options.renderActive)\n key += ` [active]`;\n if (ariaNode2.level)\n key += ` [level=${ariaNode2.level}]`;\n if (ariaNode2.pressed === "mixed")\n key += ` [pressed=mixed]`;\n if (ariaNode2.pressed === true)\n key += ` [pressed]`;\n if (ariaNode2.selected === true)\n key += ` [selected]`;\n if (ariaNode2.ref) {\n key += ` [ref=${ariaNode2.ref}]`;\n if (options.renderCursorPointer && hasPointerCursor(ariaNode2))\n key += " [cursor=pointer]";\n }\n const escapedKey = indent + "- " + yamlEscapeKeyIfNeeded(key);\n const hasProps = !!Object.keys(ariaNode2.props).length;\n if (!ariaNode2.children.length && !hasProps) {\n lines.push(escapedKey);\n } else if (ariaNode2.children.length === 1 && typeof ariaNode2.children[0] === "string" && !hasProps) {\n const text = includeText(ariaNode2, ariaNode2.children[0]) ? renderString(ariaNode2.children[0]) : null;\n if (text)\n lines.push(escapedKey + ": " + yamlEscapeValueIfNeeded(text));\n else\n lines.push(escapedKey);\n } else {\n lines.push(escapedKey + ":");\n for (const [name, value] of Object.entries(ariaNode2.props))\n lines.push(indent + " - /" + name + ": " + yamlEscapeValueIfNeeded(value));\n for (const child of ariaNode2.children || [])\n visit(child, ariaNode2, indent + " ");\n }\n };\n const ariaNode = ariaSnapshot.root;\n if (ariaNode.role === "fragment") {\n for (const child of ariaNode.children || [])\n visit(child, ariaNode, "");\n } else {\n visit(ariaNode, null, "");\n }\n return lines.join("\\n");\n}\nfunction convertToBestGuessRegex(text) {\n const dynamicContent = [\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 if (node.name.length > text.length)\n return false;\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}\nfunction hasPointerCursor(ariaNode) {\n var _a;\n return ((_a = ariaNode.box.style) == null ? void 0 : _a.cursor) === "pointer";\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}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-line.selectable:hover{background-color:#f2f2f2;overflow:hidden}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;width:400px;height:150px;z-index:10;font-size:13px}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-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.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}\\n";\n\n// packages/injected/src/highlight.ts\nvar Highlight = class {\n constructor(injectedScript) {\n this._renderedEntries = [];\n this._language = "javascript";\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.style.position = "fixed";\n this._glassPaneElement.style.top = "0";\n this._glassPaneElement.style.right = "0";\n this._glassPaneElement.style.bottom = "0";\n this._glassPaneElement.style.left = "0";\n this._glassPaneElement.style.zIndex = "2147483647";\n this._glassPaneElement.style.pointerEvents = "none";\n this._glassPaneElement.style.display = "flex";\n this._glassPaneElement.style.backgroundColor = "transparent";\n for (const eventName of ["click", "auxclick", "dragstart", "input", "keydown", "keyup", "pointerdown", "pointerup", "mousedown", "mouseup", "mouseleave", "focus", "scroll"]) {\n this._glassPaneElement.addEventListener(eventName, (e) => {\n e.stopPropagation();\n e.stopImmediatePropagation();\n });\n }\n this._actionPointElement = document.createElement("x-pw-action-point");\n this._actionPointElement.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 }\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 }\n setLanguage(language) {\n this._language = language;\n }\n runHighlightOnRaf(selector) {\n if (this._rafRequest)\n this._injectedScript.utils.builtins.cancelAnimationFrame(this._rafRequest);\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 this.updateHighlight(elements.map((element, index) => {\n const suffix = elements.length > 1 ? ` [${index + 1} of ${elements.length}]` : "";\n return { element, color, tooltipText: locator + suffix };\n }));\n this._rafRequest = this._injectedScript.utils.builtins.requestAnimationFrame(() => this.runHighlightOnRaf(selector));\n }\n uninstall() {\n if (this._rafRequest)\n this._injectedScript.utils.builtins.cancelAnimationFrame(this._rafRequest);\n this._glassPaneElement.remove();\n }\n showActionPoint(x, y) {\n this._actionPointElement.style.top = y + "px";\n this._actionPointElement.style.left = x + "px";\n this._actionPointElement.hidden = false;\n }\n hideActionPoint() {\n this._actionPointElement.hidden = true;\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, color: entry.color, tooltipElement, highlightElement });\n }\n for (const entry of this._renderedEntries) {\n 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 box2 = entry.box;\n entry.highlightElement.style.backgroundColor = entry.color;\n entry.highlightElement.style.left = box2.x + "px";\n entry.highlightElement.style.top = box2.y + "px";\n entry.highlightElement.style.width = box2.width + "px";\n entry.highlightElement.style.height = box2.height + "px";\n entry.highlightElement.style.display = "block";\n if (this._isUnderTest)\n console.error("Highlight box for test: " + JSON.stringify({ x: box2.x, y: box2.y, width: box2.width, height: box2.height }));\n }\n }\n firstBox() {\n var _a;\n return (_a = this._renderedEntries[0]) == null ? void 0 : _a.box;\n }\n tooltipPosition(box2, 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 = box2.left;\n if (anchorLeft + tooltipWidth > totalWidth - 5)\n anchorLeft = totalWidth - tooltipWidth - 5;\n let anchorTop = box2.bottom + 5;\n if (anchorTop + tooltipHeight > totalHeight - 5) {\n if (box2.top > tooltipHeight + 5) {\n anchorTop = box2.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 const oldBox = this._renderedEntries[i].box;\n if (!oldBox)\n return false;\n const box2 = entries[i].element.getBoundingClientRect();\n if (box2.top !== oldBox.top || box2.right !== oldBox.right || box2.bottom !== oldBox.bottom || box2.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};\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 box2 = 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(box2, 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 matchesComponentAttribute(obj, attr) {\n for (const token of attr.jsonPath) {\n if (obj !== void 0 && obj !== null)\n obj = obj[token];\n }\n return matchesAttributePart(obj, attr);\n}\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/reactSelectorEngine.ts\nfunction getFunctionComponentName(component) {\n return component.displayName || component.name || "Anonymous";\n}\nfunction getComponentName(reactElement) {\n if (reactElement.type) {\n switch (typeof reactElement.type) {\n case "function":\n return getFunctionComponentName(reactElement.type);\n case "string":\n return reactElement.type;\n case "object":\n return reactElement.type.displayName || (reactElement.type.render ? getFunctionComponentName(reactElement.type.render) : "");\n }\n }\n if (reactElement._currentElement) {\n const elementType = reactElement._currentElement.type;\n if (typeof elementType === "string")\n return elementType;\n if (typeof elementType === "function")\n return elementType.displayName || elementType.name || "Anonymous";\n }\n return "";\n}\nfunction getComponentKey(reactElement) {\n var _a, _b;\n return (_b = reactElement.key) != null ? _b : (_a = reactElement._currentElement) == null ? void 0 : _a.key;\n}\nfunction getChildren(reactElement) {\n if (reactElement.child) {\n const children = [];\n for (let child = reactElement.child; child; child = child.sibling)\n children.push(child);\n return children;\n }\n if (!reactElement._currentElement)\n return [];\n const isKnownElement = (reactElement2) => {\n var _a;\n const elementType = (_a = reactElement2._currentElement) == null ? void 0 : _a.type;\n return typeof elementType === "function" || typeof elementType === "string";\n };\n if (reactElement._renderedComponent) {\n const child = reactElement._renderedComponent;\n return isKnownElement(child) ? [child] : [];\n }\n if (reactElement._renderedChildren)\n return [...Object.values(reactElement._renderedChildren)].filter(isKnownElement);\n return [];\n}\nfunction getProps(reactElement) {\n var _a;\n const props = (\n // React 16+\n reactElement.memoizedProps || // React 15\n ((_a = reactElement._currentElement) == null ? void 0 : _a.props)\n );\n if (!props || typeof props === "string")\n return props;\n const result = { ...props };\n delete result.children;\n return result;\n}\nfunction buildComponentsTree(reactElement) {\n var _a;\n const treeNode = {\n key: getComponentKey(reactElement),\n name: getComponentName(reactElement),\n children: getChildren(reactElement).map(buildComponentsTree),\n rootElements: [],\n props: getProps(reactElement)\n };\n const rootElement = (\n // React 16+\n // @see https://github.com/baruchvlz/resq/blob/5c15a5e04d3f7174087248f5a158c3d6dcc1ec72/src/utils.js#L29\n reactElement.stateNode || // React 15\n reactElement._hostNode || ((_a = reactElement._renderedComponent) == null ? void 0 : _a._hostNode)\n );\n if (rootElement instanceof Element) {\n treeNode.rootElements.push(rootElement);\n } else {\n for (const child of treeNode.children)\n treeNode.rootElements.push(...child.rootElements);\n }\n return treeNode;\n}\nfunction filterComponentsTree(treeNode, searchFn, result = []) {\n if (searchFn(treeNode))\n result.push(treeNode);\n for (const child of treeNode.children)\n filterComponentsTree(child, searchFn, result);\n return result;\n}\nfunction findReactRoots(root, roots = []) {\n const document = root.ownerDocument || root;\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);\n do {\n const node = walker.currentNode;\n const reactNode = node;\n const rootKey = Object.keys(reactNode).find((key) => key.startsWith("__reactContainer") && reactNode[key] !== null);\n if (rootKey) {\n roots.push(reactNode[rootKey].stateNode.current);\n } else {\n const legacyRootKey = "_reactRootContainer";\n if (reactNode.hasOwnProperty(legacyRootKey) && reactNode[legacyRootKey] !== null) {\n roots.push(reactNode[legacyRootKey]._internalRoot.current);\n }\n }\n if (node instanceof Element && node.hasAttribute("data-reactroot")) {\n for (const key of Object.keys(node)) {\n if (key.startsWith("__reactInternalInstance") || key.startsWith("__reactFiber"))\n roots.push(node[key]);\n }\n }\n const shadowRoot = node instanceof Element ? node.shadowRoot : null;\n if (shadowRoot)\n findReactRoots(shadowRoot, roots);\n } while (walker.nextNode());\n return roots;\n}\nvar createReactEngine = () => ({\n queryAll(scope, selector) {\n const { name, attributes } = parseAttributeSelector(selector, false);\n const reactRoots = findReactRoots(scope.ownerDocument || scope);\n const trees = reactRoots.map((reactRoot) => buildComponentsTree(reactRoot));\n const treeNodes = trees.map((tree) => filterComponentsTree(tree, (treeNode) => {\n var _a;\n const props = (_a = treeNode.props) != null ? _a : {};\n if (treeNode.key !== void 0)\n props.key = treeNode.key;\n if (name && treeNode.name !== name)\n return false;\n if (treeNode.rootElements.some((domNode) => !isInsideScope(scope, domNode)))\n return false;\n for (const attr of attributes) {\n if (!matchesComponentAttribute(props, attr))\n return false;\n }\n return true;\n })).flat();\n const allRootElements = /* @__PURE__ */ new Set();\n for (const treeNode of treeNodes) {\n for (const domNode of treeNode.rootElements)\n allRootElements.add(domNode);\n }\n return [...allRootElements];\n }\n});\n\n// packages/injected/src/roleSelectorEngine.ts\nvar kSupportedAttributes = ["selected", "checked", "pressed", "expanded", "level", "disabled", "name", "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.exact = 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.exact && options.nameOp === "=")\n options.nameOp = "*=";\n if (!matchesAttributePart(accessibleName, { name: "", jsonPath: [], op: options.nameOp || "=", value: options.name, caseSensitive: !!options.exact }))\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 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 endAriaCaches();\n injectedScript._evaluator.end();\n }\n}\nfunction filterRegexTokens(textCandidates) {\n return textCandidates.filter((c) => c[0].selector[0] !== "/");\n}\nfunction generateSelectorFor(cache, injectedScript, targetElement, options) {\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 const calculate = (element, allowText) => {\n var _a;\n const allowNthMatch = element === targetElement;\n let textCandidates = allowText ? buildTextCandidates(injectedScript, element, element === targetElement) : [];\n if (element !== targetElement) {\n textCandidates = filterRegexTokens(textCandidates);\n }\n const noTextCandidates = buildNoTextCandidates(injectedScript, element, options).filter((token) => !options.omitInternalEngines || !token.engine.startsWith("internal:")).map((token) => [token]);\n let result = chooseFirstSelector(injectedScript, (_a = options.root) != null ? _a : targetElement.ownerDocument, element, [...textCandidates, ...noTextCandidates], allowNthMatch);\n textCandidates = filterRegexTokens(textCandidates);\n const checkWithText = (textCandidatesToUse) => {\n const allowParentText = allowText && !textCandidatesToUse.length;\n const candidates = [...textCandidatesToUse, ...noTextCandidates].filter((c) => {\n if (!result)\n return true;\n return combineScores(c) < combineScores(result);\n });\n let bestPossibleInParent = candidates[0];\n if (!bestPossibleInParent)\n return;\n for (let parent = parentElementOrShadowHost(element); parent && parent !== options.root; parent = parentElementOrShadowHost(parent)) {\n const parentTokens = calculateCached(parent, allowParentText);\n if (!parentTokens)\n continue;\n if (result && combineScores([...parentTokens, ...bestPossibleInParent]) >= combineScores(result))\n continue;\n bestPossibleInParent = chooseFirstSelector(injectedScript, parent, element, candidates, allowNthMatch);\n if (!bestPossibleInParent)\n return;\n const combined = [...parentTokens, ...bestPossibleInParent];\n if (!result || combineScores(combined) < combineScores(result))\n result = combined;\n }\n };\n checkWithText(textCandidates);\n if (element === targetElement && textCandidates.length)\n checkWithText([]);\n return result;\n };\n const calculateCached = (element, allowText) => {\n const map = allowText ? cache.allowText : cache.disallowText;\n let value = map.get(element);\n if (value === void 0) {\n value = calculate(element, allowText);\n map.set(element, value);\n }\n return value;\n };\n return calculate(targetElement, !options.noText);\n}\nfunction buildNoTextCandidates(injectedScript, element, options) {\n const candidates = [];\n {\n for (const attr of ["data-testid", "data-test-id", "data-test"]) {\n if (attr !== options.testIdAttributeName && 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 if (element.getAttribute(options.testIdAttributeName))\n candidates.push({ engine: "css", selector: `[${options.testIdAttributeName}=${quoteCSSAttributeValue(element.getAttribute(options.testIdAttributeName))}]`, score: kTestIdScore });\n penalizeScoreForLength([candidates]);\n return candidates;\n }\n if (element.getAttribute(options.testIdAttributeName))\n candidates.push({ engine: "internal:testid", selector: `[${options.testIdAttributeName}=${escapeForAttributeSelector(element.getAttribute(options.testIdAttributeName), true)}]`, score: kTestIdScore });\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 (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) {\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 } else {\n const roleToken = { engine: "internal:role", selector: `${ariaRole}`, score: kRoleWithoutNameScore };\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 (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 chooseFirstSelector(injectedScript, scope, targetElement, selectors, allowNthMatch) {\n const joined = selectors.map((tokens) => ({ tokens, score: combineScores(tokens) }));\n joined.sort((a, b) => a.score - b.score);\n let bestWithIndex = null;\n for (const { tokens } of joined) {\n const parsedSelector = injectedScript.parseSelector(joinTokens(tokens));\n const result = injectedScript.querySelectorAll(parsedSelector, scope);\n if (result[0] === targetElement && result.length === 1) {\n return tokens;\n }\n const index = result.indexOf(targetElement);\n if (!allowNthMatch || bestWithIndex || index === -1 || result.length > 5)\n continue;\n const nth = { engine: "nth", selector: String(index), score: kNthScore };\n bestWithIndex = [...tokens, nth];\n }\n return bestWithIndex;\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/vueSelectorEngine.ts\nfunction basename(filename, ext) {\n const normalized = filename.replace(/^[a-zA-Z]:/, "").replace(/\\\\/g, "/");\n let result = normalized.substring(normalized.lastIndexOf("/") + 1);\n if (ext && result.endsWith(ext))\n result = result.substring(0, result.length - ext.length);\n return result;\n}\nfunction toUpper(_, c) {\n return c ? c.toUpperCase() : "";\n}\nvar classifyRE = /(?:^|[-_/])(\\w)/g;\nvar classify = (str) => {\n return str && str.replace(classifyRE, toUpper);\n};\nfunction buildComponentsTreeVue3(instance) {\n function getComponentTypeName(options) {\n const name = options.name || options._componentTag || options.__playwright_guessedName;\n if (name)\n return name;\n const file = options.__file;\n if (file)\n return classify(basename(file, ".vue"));\n }\n function saveComponentName(instance2, key) {\n instance2.type.__playwright_guessedName = key;\n return key;\n }\n function getInstanceName(instance2) {\n var _a, _b, _c, _d;\n const name = getComponentTypeName(instance2.type || {});\n if (name)\n return name;\n if (instance2.root === instance2)\n return "Root";\n for (const key in (_b = (_a = instance2.parent) == null ? void 0 : _a.type) == null ? void 0 : _b.components) {\n if (((_c = instance2.parent) == null ? void 0 : _c.type.components[key]) === instance2.type)\n return saveComponentName(instance2, key);\n }\n for (const key in (_d = instance2.appContext) == null ? void 0 : _d.components) {\n if (instance2.appContext.components[key] === instance2.type)\n return saveComponentName(instance2, key);\n }\n return "Anonymous Component";\n }\n function isBeingDestroyed(instance2) {\n return instance2._isBeingDestroyed || instance2.isUnmounted;\n }\n function isFragment(instance2) {\n return instance2.subTree.type.toString() === "Symbol(Fragment)";\n }\n function getInternalInstanceChildren(subTree) {\n const list = [];\n if (subTree.component)\n list.push(subTree.component);\n if (subTree.suspense)\n list.push(...getInternalInstanceChildren(subTree.suspense.activeBranch));\n if (Array.isArray(subTree.children)) {\n subTree.children.forEach((childSubTree) => {\n if (childSubTree.component)\n list.push(childSubTree.component);\n else\n list.push(...getInternalInstanceChildren(childSubTree));\n });\n }\n return list.filter((child) => {\n var _a;\n return !isBeingDestroyed(child) && !((_a = child.type.devtools) == null ? void 0 : _a.hide);\n });\n }\n function getRootElementsFromComponentInstance(instance2) {\n if (isFragment(instance2))\n return getFragmentRootElements(instance2.subTree);\n return [instance2.subTree.el];\n }\n function getFragmentRootElements(vnode) {\n if (!vnode.children)\n return [];\n const list = [];\n for (let i = 0, l = vnode.children.length; i < l; i++) {\n const childVnode = vnode.children[i];\n if (childVnode.component)\n list.push(...getRootElementsFromComponentInstance(childVnode.component));\n else if (childVnode.el)\n list.push(childVnode.el);\n }\n return list;\n }\n function buildComponentsTree2(instance2) {\n return {\n name: getInstanceName(instance2),\n children: getInternalInstanceChildren(instance2.subTree).map(buildComponentsTree2),\n rootElements: getRootElementsFromComponentInstance(instance2),\n props: instance2.props\n };\n }\n return buildComponentsTree2(instance);\n}\nfunction buildComponentsTreeVue2(instance) {\n function getComponentName2(options) {\n const name = options.displayName || options.name || options._componentTag;\n if (name)\n return name;\n const file = options.__file;\n if (file)\n return classify(basename(file, ".vue"));\n }\n function getInstanceName(instance2) {\n const name = getComponentName2(instance2.$options || instance2.fnOptions || {});\n if (name)\n return name;\n return instance2.$root === instance2 ? "Root" : "Anonymous Component";\n }\n function getInternalInstanceChildren(instance2) {\n if (instance2.$children)\n return instance2.$children;\n if (Array.isArray(instance2.subTree.children))\n return instance2.subTree.children.filter((vnode) => !!vnode.component).map((vnode) => vnode.component);\n return [];\n }\n function buildComponentsTree2(instance2) {\n return {\n name: getInstanceName(instance2),\n children: getInternalInstanceChildren(instance2).map(buildComponentsTree2),\n rootElements: [instance2.$el],\n props: instance2._props\n };\n }\n return buildComponentsTree2(instance);\n}\nfunction filterComponentsTree2(treeNode, searchFn, result = []) {\n if (searchFn(treeNode))\n result.push(treeNode);\n for (const child of treeNode.children)\n filterComponentsTree2(child, searchFn, result);\n return result;\n}\nfunction findVueRoots(root, roots = []) {\n const document = root.ownerDocument || root;\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);\n const vue2Roots = /* @__PURE__ */ new Set();\n do {\n const node = walker.currentNode;\n if (node.__vue__)\n vue2Roots.add(node.__vue__.$root);\n if (node.__vue_app__ && node._vnode && node._vnode.component)\n roots.push({ root: node._vnode.component, version: 3 });\n const shadowRoot = node instanceof Element ? node.shadowRoot : null;\n if (shadowRoot)\n findVueRoots(shadowRoot, roots);\n } while (walker.nextNode());\n for (const vue2root of vue2Roots) {\n roots.push({\n version: 2,\n root: vue2root\n });\n }\n return roots;\n}\nvar createVueEngine = () => ({\n queryAll(scope, selector) {\n const document = scope.ownerDocument || scope;\n const { name, attributes } = parseAttributeSelector(selector, false);\n const vueRoots = findVueRoots(document);\n const trees = vueRoots.map((vueRoot) => vueRoot.version === 3 ? buildComponentsTreeVue3(vueRoot.root) : buildComponentsTreeVue2(vueRoot.root));\n const treeNodes = trees.map((tree) => filterComponentsTree2(tree, (treeNode) => {\n if (name && treeNode.name !== name)\n return false;\n if (treeNode.rootElements.some((rootElement) => !isInsideScope(scope, rootElement)))\n return false;\n for (const attr of attributes) {\n if (!matchesComponentAttribute(treeNode.props, attr))\n return false;\n }\n return true;\n })).flat();\n const allRootElements = /* @__PURE__ */ new Set();\n for (const treeNode of treeNodes) {\n for (const rootElement of treeNode.rootElements)\n allRootElements.add(rootElement);\n }\n return [...allRootElements];\n }\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/playwright-core/src/utils/isomorphic/locatorUtils.ts\nfunction getByAttributeTextSelector(attrName, text, options) {\n return `internal:attr=[${attrName}=${escapeForAttributeSelector(text, (options == null ? void 0 : options.exact) || false)}]`;\n}\nfunction getByTestIdSelector(testIdAttributeName, testId) {\n return `internal:testid=[${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.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/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) => {\n return this._injectedScript.ariaSnapshot(element || this._injectedScript.document.body, { mode: "expect" });\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/playwright-core/src/utils/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}\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 }\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 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 // eslint-disable-next-line no-restricted-globals\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 };\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 // eslint-disable-next-line no-restricted-globals\n constructor(window, options) {\n this._testIdAttributeNameForStrictErrorAndConsoleCodegen = "data-testid";\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 // 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("_react", createReactEngine());\n this._engines.set("_vue", createVueEngine());\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._createNamedAttributeEngine());\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 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 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 if (node.nodeType !== Node.ELEMENT_NODE)\n throw this.createStacklessError("Can only capture aria snapshot of Element nodes.");\n this._lastAriaSnapshot = generateAriaTree(node, options);\n return renderAriaTree(this._lastAriaSnapshot, options);\n }\n ariaSnapshotForRecorder() {\n const tree = generateAriaTree(this.document.body, { mode: "ai" });\n const 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, value, caseSensitive } = parsed.attributes[0];\n const lowerCaseValue = caseSensitive ? null : value.toLowerCase();\n let matcher;\n if (value instanceof RegExp)\n matcher = (s) => !!s.match(value);\n else if (caseSensitive)\n matcher = (s) => s === value;\n else\n matcher = (s) => s.toLowerCase().includes(lowerCaseValue);\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 _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._lastAriaSnapshot) == 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 return {\n matches: need === checked,\n received: checked ? "checked" : "unchecked"\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 filter = (optionToSelect) => {\n if (optionToSelect instanceof Node)\n return option === optionToSelect;\n let matches = true;\n if (optionToSelect.valueOrLabel !== void 0)\n matches = matches && (optionToSelect.valueOrLabel === option.value || optionToSelect.valueOrLabel === option.label);\n if (optionToSelect.value !== void 0)\n matches = matches && optionToSelect.value === option.value;\n if (optionToSelect.label !== void 0)\n matches = matches && optionToSelect.label === option.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 (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 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 element.focus();\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 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 = 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