[
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)\nand this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).\n\n**Note:** PuPHPeteer is heavily based on [Rialto](https://github.com/nesk/rialto). For a complete overview of the changes, you might want to check out [Rialto's changelog](https://github.com/nesk/rialto/blob/master/CHANGELOG.md) too.\n\n## [Unreleased]\n_In progress…_\n\n## [2.0.0] - 2020-12-01\n### Added\n- Support Puppeteer v5.5\n- Support PHP 8\n- Add documentation on all resources to provide autocompletion in IDEs\n\n### Removed\n- Drop support for PHP 7.1 and 7.2\n\n## [1.6.0] - 2019-07-01\n### Added\n- Support Puppeteer v1.18\n\n## [1.5.0] - 2019-03-17\n### Added\n- Support Puppeteer v1.13\n- Make the `ElementHandle` resource extend the `JSHandle` one\n\n### Fixed\n- Add missing `Accessibility` resource\n\n## [1.4.1] - 2018-11-27\n### Added\n- Support Puppeteer v1.10\n\n## [1.4.0] - 2018-09-22\n### Added\n- Support Puppeteer v1.8\n\n### Changed\n- Detect resource types by using the constructor name\n\n### Fixed\n- Logs of initial pages are now retrieved\n\n## [1.3.0] - 2018-08-20\n### Added\n- Add a `log_browser_console` option to log the output of Browser's console methods (`console.log`, `console.debug`, `console.table`, etc…) to the PHP logger\n- Support Puppeteer v1.7\n\n## [1.2.0] - 2018-07-25\n### Added\n- Support Puppeteer v1.6\n\n### Changed\n- Upgrade to Rialto v1.1\n\n## [1.1.0] - 2018-06-12\n### Added\n- Support Puppeteer v1.5\n- Add aliases for evaluation methods to the `ElementHandle` resource\n- Support new Puppeteer v1.5 resources:\n    - `BrowserContext`\n    - `Worker`\n\n### Fixed\n- Fix Travis tests\n\n## [1.0.0] - 2018-06-05\n### Changed\n- Change PHP's vendor name from `extractr-io` to `nesk`\n- Change NPM's scope name from `@extractr-io` to `@nesk`\n- Upgrade to Rialto v1\n\n## [0.2.2] - 2018-04-20\n### Added\n- Support Puppeteer v1.3\n- Test missing Puppeteer resources: `ConsoleMessage` and `Dialog`\n- Show a warning in logs if Puppeteer's version doesn't match requirements\n\n## [0.2.1] - 2018-04-09\n### Changed\n- Update Rialto version requirements\n\n## [0.2.0] - 2018-02-19\n### Added\n- Support new Puppeteer v1.1 resources:\n    - `BrowserFetcher`\n    - `CDPSession`\n    - `Coverage`\n    - `SecurityDetails`\n- Test Puppeteer resources\n- Support PHPUnit v7\n- Add Travis integration\n\n### Changed\n- Lock Puppeteer's version to v1.1 to avoid issues with forward compatibility\n\n## 0.1.0 - 2018-01-29\nFirst release\n\n\n[Unreleased]: https://github.com/nesk/puphpeteer/compare/2.0.0...HEAD\n[2.0.0]: https://github.com/nesk/puphpeteer/compare/1.6.0...2.0.0\n[1.6.0]: https://github.com/nesk/puphpeteer/compare/1.5.0...1.6.0\n[1.5.0]: https://github.com/nesk/puphpeteer/compare/1.4.1...1.5.0\n[1.4.1]: https://github.com/nesk/puphpeteer/compare/1.4.0...1.4.1\n[1.4.0]: https://github.com/nesk/puphpeteer/compare/1.3.0...1.4.0\n[1.3.0]: https://github.com/nesk/puphpeteer/compare/1.2.0...1.3.0\n[1.2.0]: https://github.com/nesk/puphpeteer/compare/1.1.0...1.2.0\n[1.1.0]: https://github.com/nesk/puphpeteer/compare/1.0.0...1.1.0\n[1.0.0]: https://github.com/nesk/puphpeteer/compare/0.2.2...1.0.0\n[0.2.2]: https://github.com/nesk/puphpeteer/compare/0.2.1...0.2.2\n[0.2.1]: https://github.com/nesk/puphpeteer/compare/0.2.0...0.2.1\n[0.2.0]: https://github.com/nesk/puphpeteer/compare/0.1.0...0.2.0\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) Johann Pardanaud\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# PuPHPeteer\n\n<img src=\"https://user-images.githubusercontent.com/817508/100672192-dd258500-3361-11eb-845f-e8b5109752e4.png\" style=\"max-width:100%;\" width=\"190px\" align=\"right\">\n\n[![PHP Version](https://img.shields.io/packagist/php-v/zoon/puphpeteer.svg?style=flat-square)](http://php.net/)\n[![Composer Version](https://img.shields.io/packagist/v/zoon/puphpeteer.svg?style=flat-square&label=Composer)](https://packagist.org/packages/zoon/puphpeteer)\n\nA [Puppeteer](https://github.com/GoogleChrome/puppeteer/) bridge for PHP, supporting the entire API. Based on [Rialto](https://github.com/zoonru/rialto/), a package to manage Node resources from PHP.\n\nHere are some examples [borrowed from Puppeteer's documentation](https://github.com/GoogleChrome/puppeteer/blob/master/README.md#usage) and adapted to PHP's syntax:\n\n**Example** - navigating to https://example.com and saving a screenshot as *example.png*:\n\n```php\nuse Nesk\\Puphpeteer\\Puppeteer;\n\n$puppeteer = new Puppeteer;\n$browser = $puppeteer->launch();\n\n$page = $browser->newPage();\n$page->goto('https://example.com');\n$page->screenshot(['path' => 'example.png']);\n\n$browser->close();\n```\n\n**Example** - evaluate a script in the context of the page:\n\n```php\nuse Nesk\\Puphpeteer\\Puppeteer;\nuse Nesk\\Rialto\\Data\\JsFunction;\n\n$puppeteer = new Puppeteer;\n\n$browser = $puppeteer->launch();\n$page = $browser->newPage();\n$page->goto('https://example.com');\n\n// Get the \"viewport\" of the page, as reported by the page.\n$dimensions = $page->evaluate(JsFunction::createWithBody(\"\n    return {\n        width: document.documentElement.clientWidth,\n        height: document.documentElement.clientHeight,\n        deviceScaleFactor: window.devicePixelRatio\n    };\n\"));\n\nprintf('Dimensions: %s', print_r($dimensions, true));\n\n$browser->close();\n```\n\n## Requirements and installation\n\nThis package requires PHP >= 7.3 and Node >= 8.\n\nInstall it with these two command lines:\n\n```shell\ncomposer require zoon/puphpeteer\nnpm install git+https://git@github.com/zoonru/puphpeteer.git#zoon\n```\n\n## Use with browserless\n\n```shell\ndocker run --rm  -p 3000:3000 ghcr.io/browserless/chrome\n```\n\n```php\n$puppeteer = new Nesk\\Puphpeteer\\Puppeteer;\n\n$options = [\n    'headless' => false,\n    'stealth'=> true,\n    'timeout'=> 5000,\n    'args' => [\n        '--window-size=1366,768',\n    ],\n];\n\n$browser = $puppeteer->connect(['browserWSEndpoint' => 'ws://127.0.0.1:3000/chrome?launch='.urlencode(json_encode($options, JSON_UNESCAPED_UNICODE))]);\n$page = $browser->newPage();\n$page->goto('https://www.example.com');\n$page->screenshot(['path' => 'example.png']);\n$browser->close();\n```\n\n## Notable differences between PuPHPeteer and Puppeteer\n\n### Puppeteer's class must be instantiated\n\nInstead of requiring Puppeteer:\n\n```js\nconst puppeteer = require('puppeteer');\n```\n\nYou have to instantiate the `Puppeteer` class:\n\n```php\n$puppeteer = new Puppeteer;\n```\n\nThis will create a new Node process controlled by PHP.\n\nYou can also pass some options to the constructor, see [Rialto's documentation](https://github.com/nesk/rialto/blob/master/docs/api.md#options). PuPHPeteer also extends these options:\n\n```php\n[\n    // Logs the output of Browser's console methods (console.log, console.debug, etc...) to the PHP logger\n    'log_browser_console' => false,\n]\n```\n\n<details>\n<summary><strong>⏱ Want to use some timeouts higher than 30 seconds in Puppeteer's API?</strong></summary> <br>\n\nIf you use some timeouts higher than 30 seconds, you will have to set a higher value for the `read_timeout` option (default: `35`):\n\n```php\n$puppeteer = new Puppeteer([\n    'read_timeout' => 65, // In seconds\n]);\n\n$puppeteer->launch()->newPage()->goto($url, [\n    'timeout' => 60000, // In milliseconds\n]);\n```\n</details>\n\n### No need to use the `await` keyword\n\nWith PuPHPeteer, every method call or property getting/setting is synchronous.\n\n### Some methods have been aliased\n\nThe following methods have been aliased because PHP doesn't support the `$` character in method names:\n\n- `$` => `querySelector`\n- `$$` => `querySelectorAll`\n- `$eval` => `querySelectorEval`\n- `$$eval` => `querySelectorAllEval`\n\nUse these aliases just like you would have used the original methods:\n\n```php\n$divs = $page->querySelectorAll('div');\n// Runs the `//h2` as the XPath expression.\n$xpath = $page->querySelectorAll('::-p-xpath(//h2)');\n// div element that has Checkout as the inner text.\n$text = $page->querySelector('div ::-p-text(Checkout)');\n```\n\n### Evaluated functions must be created with `JsFunction`\n\nFunctions evaluated in the context of the page must be written [with the `JsFunction` class](https://github.com/nesk/rialto/blob/master/docs/api.md#javascript-functions), the body of these functions must be written in JavaScript instead of PHP.\n\n```php\nuse Nesk\\Rialto\\Data\\JsFunction;\n\n$pageFunction = JsFunction::createWithParameters(['element'])\n    ->body(\"return element.textContent\");\n```\n\n### Exceptions must be caught with `->tryCatch`\n\nIf an error occurs in Node, a `Node\\FatalException` will be thrown and the process closed, you will have to create a new instance of `Puppeteer`.\n\nTo avoid that, you can ask Node to catch these errors by prepending your instruction with `->tryCatch`:\n\n```php\nuse Nesk\\Rialto\\Exceptions\\Node;\n\ntry {\n    $page->tryCatch->goto('invalid_url');\n} catch (Node\\Exception $exception) {\n    // Handle the exception...\n}\n```\n\nInstead, a `Node\\Exception` will be thrown, the Node process will stay alive and usable.\n\n### Puppeteer plugins\n\nPuppeteer-extra and puppeteer-extra-plugin-stealth plugins already added in npm requirements.\n\nTo use them, override js inclusion with js_extra option\n```php\n    $puppeteer = new Puppeteer([\n        'js_extra' => /** @lang JavaScript */ \"\n            const puppeteer = require('puppeteer-extra');\n            const StealthPlugin = require('puppeteer-extra-plugin-stealth');\n            puppeteer.use(StealthPlugin());\n            instruction.setDefaultResource(puppeteer);\n        \"\n    ]);\n```\n\n## License\n\nThe MIT License (MIT). Please see [License File](LICENSE) for more information.\n\n## Logo attribution\n\nPuPHPeteer's logo is composed of:\n\n- [Puppet](https://thenounproject.com/search/?q=puppet&i=52120) by Luis Prado from [the Noun Project](http://thenounproject.com/).\n- [Elephant](https://thenounproject.com/search/?q=elephant&i=954119) by Lluisa Iborra from [the Noun Project](http://thenounproject.com/).\n\nThanks to [Laravel News](https://laravel-news.com/) for picking the icons and colors of the logo.\n"
  },
  {
    "path": "bin/console",
    "content": "#!/usr/bin/env php\n<?php\n\nrequire_once __DIR__.'/../vendor/autoload.php';\n\nuse Symfony\\Component\\Console\\Application;\nuse Nesk\\Puphpeteer\\Command\\GenerateDocumentationCommand;\n\n(new Application())\n    ->add(new GenerateDocumentationCommand)\n    ->getApplication()\n    ->run();\n"
  },
  {
    "path": "composer.json",
    "content": "{\n  \"name\": \"zoon/puphpeteer\",\n  \"description\": \"A Puppeteer bridge for PHP, supporting the entire API.\",\n  \"keywords\": [\n    \"php\",\n    \"puppeteer\",\n    \"headless-chrome\",\n    \"testing\",\n    \"web\",\n    \"developer-tools\",\n    \"automation\"\n  ],\n  \"type\": \"library\",\n  \"license\": \"MIT\",\n  \"authors\": [\n    {\n      \"name\": \"Johann Pardanaud\",\n      \"email\": \"pardanaud.j@gmail.com\"\n    }\n  ],\n  \"require\": {\n    \"php\": \"^8.1\",\n    \"ext-json\": \"*\",\n    \"composer/semver\": \"^3.0\",\n    \"psr/log\": \"^3.0\",\n    \"zoon/rialto\": \"^1.6\"\n  },\n  \"require-dev\": {\n    \"friendsofphp/php-cs-fixer\": \"^3.2\",\n    \"monolog/monolog\": \"^3.0\",\n    \"phpstan/phpstan\": \"^2.0\",\n    \"phpunit/phpunit\": \"^11\",\n    \"symfony/console\": \"^7\",\n    \"symfony/filesystem\": \"^7\",\n    \"symfony/process\": \"^7\",\n    \"symfony/var-dumper\": \"^7\"\n  },\n  \"autoload\": {\n    \"psr-4\": {\n      \"Nesk\\\\Puphpeteer\\\\\": \"src/\"\n    }\n  },\n  \"autoload-dev\": {\n    \"psr-4\": {\n      \"Nesk\\\\Puphpeteer\\\\Tests\\\\\": \"tests/\"\n    }\n  },\n  \"scripts\": {\n    \"post-install-cmd\": \"npm install\",\n    \"test\": \"./vendor/bin/phpunit\",\n    \"update-docs\": \"php bin/console doc:generate\",\n    \"stan\": \"vendor/bin/phpstan analyze src\",\n    \"format\": \"vendor/bin/php-cs-fixer fix src\"\n  },\n  \"config\": {\n    \"sort-packages\": true\n  }\n}\n"
  },
  {
    "path": "examples/01_page_open.php",
    "content": "<?php\n\nrequire __DIR__ . '/../vendor/autoload.php';\n\nuse Nesk\\Puphpeteer\\Puppeteer;\nuse Nesk\\Rialto\\Data\\JsFunction;\n\n$puppeteer = new Puppeteer;\n\n$browser = $puppeteer->launch();\n$page = $browser->newPage();\n$page->goto('https://example.com');\n\n// Get the \"viewport\" of the page, as reported by the page.\n$dimensions = $page->evaluate(JsFunction::createWithBody(/** @lang JavaScript */\"\n    return {\n        width: document.documentElement.clientWidth,\n        height: document.documentElement.clientHeight,\n        deviceScaleFactor: window.devicePixelRatio\n    };\n\"));\n\nprintf('Dimensions: %s', print_r($dimensions, true));\n\n$browser->close();"
  },
  {
    "path": "examples/02_page_screenshot.php",
    "content": "<?php\n\nrequire __DIR__ . '/../vendor/autoload.php';\n\nuse Nesk\\Puphpeteer\\Puppeteer;\n\n$puppeteer = new Puppeteer;\n$browser = $puppeteer->launch();\n\n$page = $browser->newPage();\n$page->setViewport(['width' => 1366, 'height' => 768]);\n$page->goto('https://example.com');\n$page->screenshot(['path' => 'example.png']);\n\n$browser->close();"
  },
  {
    "path": "examples/03_browserless.php",
    "content": "<?php\n\nrequire __DIR__ . '/../vendor/autoload.php';\n\nuse Nesk\\Puphpeteer\\Puppeteer;\nuse Nesk\\Rialto\\Data\\JsFunction;\nuse Symfony\\Component\\Console\\Output\\ConsoleOutput;\n\n// TIMEOUT=-1 dont work properly\n$dockerId = `docker run --rm -d -p 3000:3000 --platform=linux/amd64 -e \"TOKEN=abc\" -e \"TIMEOUT=-1\" --name=chrome ghcr.io/browserless/chrome:v2.39.0`;\n\necho \"Started browserless with id $dockerId\" . PHP_EOL;\nsleep(2);\n\ntry {\n    $puppeteer = new Puppeteer([\n        'js_extra' => /** @lang JavaScript */ \"\n            const puppeteer = require('puppeteer-extra');\n            const StealthPlugin = require('puppeteer-extra-plugin-stealth');\n            puppeteer.use(StealthPlugin());\n            instruction.setDefaultResource(puppeteer);\n        \",\n        'idle_timeout' => 90,\n        'read_timeout' => 120,\n        'log_browser_console' => true,\n        'log_node_console' => true,\n        'logger' => new Symfony\\Component\\Console\\Logger\\ConsoleLogger(new ConsoleOutput(\\Symfony\\Component\\Console\\Output\\OutputInterface::VERBOSITY_VERY_VERBOSE)),\n    ]);\n\n    $options = [\n        'timeout' => 120000,\n        'token' => 'abc',\n        'launch' => json_encode([\n            'headless' => false,\n            'stealth'=> true,\n            'blockAds' => false,\n            'timeout'=> 600_000,\n            'args' => [\n                '--window-size=1366,768',\n                '--lang=ru-RU',\n                '--incognito',\n                '--0',\n            ],\n        ], JSON_UNESCAPED_UNICODE),\n    ];\n\n    $browser = $puppeteer->connect([\n        'browserWSEndpoint' => sprintf(\n            'ws://127.0.0.1:3000/chrome?%s',\n            http_build_query($options)\n        ),\n        'ignoreHTTPSErrors' => true,\n        'ignoreDefaultArgs' => true,\n    ]);\n    $page = $browser->newPage();\n    $page->setViewport(['width' => 1366, 'height' => 768]);\n    $page->goto('https://bot.sannysoft.com', ['timeout' => 10_000, 'waitUntil' => 'domcontentloaded']);\n    $page->waitForNetworkIdle(['idleTime' => 1_000, 'concurrency' => 1]);\n\n\n    // Get the \"viewport\" of the page, as reported by the page.\n    $dimensions = $page->evaluate(JsFunction::createWithBody(/** @lang JavaScript */\"\n        return {\n            width: document.documentElement.clientWidth,\n            height: document.documentElement.clientHeight,\n            deviceScaleFactor: window.devicePixelRatio\n        };\n    \"));\n\n    printf('Dimensions: %s', print_r($dimensions, true));\n\n    $page->screenshot(['path' => 'example_browserless.png', \"fullPage\" => true]);\n\n    $browser->close();\n} finally {\n    `docker stop $dockerId`;\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"@zoon/puphpeteer\",\n  \"version\": \"2.4.3\",\n  \"description\": \"A Puppeteer bridge for PHP, supporting the entire API.\",\n  \"keywords\": [\n    \"php\",\n    \"puppeteer\",\n    \"headless-chrome\",\n    \"testing\",\n    \"web\",\n    \"developer-tools\",\n    \"automation\"\n  ],\n  \"author\": {\n    \"name\": \"Johann Pardanaud\",\n    \"email\": \"pardanaud.j@gmail.com\",\n    \"url\": \"https://johann.pardanaud.com/\"\n  },\n  \"license\": \"MIT\",\n  \"repository\": \"git+https://git@github.com/zoonru/puphpeteer.git#zoon\",\n  \"engines\": {\n    \"node\": \">=9.0.0\"\n  },\n  \"dependencies\": {\n    \"@zoon/rialto\": \"git+https://git@github.com/zoonru/rialto.git#zoon\",\n    \"puppeteer\": \"24.36.1\",\n    \"puppeteer-extra\": \"^3.3.6\",\n    \"puppeteer-extra-plugin-stealth\": \"^2.11.2\"\n  },\n  \"devDependencies\": {\n    \"@types/yargs\": \"^15.0.10\",\n    \"typescript\": \"^5\",\n    \"yargs\": \"^16.1.1\"\n  }\n}\n"
  },
  {
    "path": "phpstan.neon",
    "content": "parameters:\n    level: 4\n    fileExtensions:\n        - php\n"
  },
  {
    "path": "phpunit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:noNamespaceSchemaLocation=\"https://schema.phpunit.de/9.3/phpunit.xsd\"\n         bootstrap=\"vendor/autoload.php\"\n         colors=\"true\">\n  <testsuite name=\"default\">\n    <directory suffix=\"Test.php\">tests</directory>\n  </testsuite>\n</phpunit>\n"
  },
  {
    "path": "src/Command/GenerateDocumentationCommand.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Command;\n\nuse Nesk\\Puphpeteer\\Puppeteer;\nuse Symfony\\Component\\Console\\Command\\Command;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfony\\Component\\Console\\Input\\InputOption;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\nuse Symfony\\Component\\Console\\Style\\SymfonyStyle;\nuse Symfony\\Component\\Process\\Process;\n\nfinal class GenerateDocumentationCommand extends Command\n{\n    private const DOC_FILE_NAME = 'doc-generator';\n\n    private const BUILD_DIR = __DIR__.'/../../.build';\n\n    private const NODE_MODULES_DIR = __DIR__.'/../../node_modules';\n\n    private const RESOURCES_DIR = __DIR__.'/../Resources';\n\n    private const RESOURCES_NAMESPACE = 'Nesk\\\\Puphpeteer\\\\Resources';\n\n    private const DOC_FORMAT_PHP = 'php';\n\n    private const DOC_FORMAT_PHPSTAN = 'phpstan';\n\n    private const DOC_FORMATS = [self::DOC_FORMAT_PHP, self::DOC_FORMAT_PHPSTAN];\n\n    public function getName(): ?string\n    {\n        return 'doc:generate';\n    }\n\n    protected function configure(): void\n    {\n        $this->addOption(\n            'puppeteerPath',\n            null,\n            InputOption::VALUE_OPTIONAL,\n            'The path where Puppeteer is installed.',\n            self::NODE_MODULES_DIR.'/puppeteer-core'\n        );\n    }\n\n    /**\n     * Builds the documentation generator from TypeScript to JavaScript.\n     */\n    private static function buildDocumentationGenerator(): void\n    {\n        self::rmdirRecursive(self::BUILD_DIR);\n        $process = new Process([\n            self::NODE_MODULES_DIR.'/.bin/tsc',\n            '--outDir',\n            self::BUILD_DIR,\n            __DIR__.'/../../src/'.self::DOC_FILE_NAME.'.ts',\n        ]);\n        $process->run();\n    }\n\n    /**\n     * Gets the documentation from the TypeScript documentation generator.\n     */\n    private static function getDocumentation(string $puppeteerPath, array $resourceNames): array\n    {\n        self::buildDocumentationGenerator();\n\n        $files = \\glob($puppeteerPath . '/lib/esm/puppeteer/{common,node,api,cdp}/*.d.ts', GLOB_BRACE);\n        if ($files === false) {\n            $error = \\error_get_last();\n            throw new \\ErrorException($error['message'] ?? 'An error occurred', 0, $error['type'] ?? 1);\n        }\n\n        $result = [];\n        foreach (self::DOC_FORMATS as $format) {\n            $process = new Process(\n                array_merge(\n                    ['node', self::BUILD_DIR.'/'.self::DOC_FILE_NAME.'.js', $format],\n                    $files,\n                    ['--resources-namespace', self::RESOURCES_NAMESPACE, '--resources'],\n                    $resourceNames\n                )\n            );\n            $process->mustRun();\n\n            echo $process->getErrorOutput().\\PHP_EOL;\n\n            $data = \\json_decode($process->getOutput(), true);\n            if (JSON_ERROR_NONE !== \\json_last_error()) {\n                throw new \\JsonException(json_last_error_msg(), json_last_error());\n            }\n\n            foreach ($data as &$class) {\n                $result[$class['name']]['name'] = $class['name'];\n                $result[$class['name']][$format] = [\n                    'properties' => $class['properties'],\n                    'getters' => $class['getters'],\n                    'methods' => $class['methods'],\n                ];\n            }\n        }\n\n        return $result;\n    }\n\n    private static function getResourceNames(): array\n    {\n        return array_map(static function (string $filePath): string {\n            return explode('.', basename($filePath))[0];\n        }, glob(self::RESOURCES_DIR.'/*'));\n    }\n\n    private static function generatePhpDocWithDocumentation(array $classDocumentation): ?string\n    {\n        $properties = array_map(function (string $property): string {\n            return \"\\n * @property {$property}\";\n        }, $classDocumentation[self::DOC_FORMAT_PHP]['properties']);\n        $properties = implode('', $properties);\n\n        $getters = array_map(function (string $getter): string {\n            return \"\\n * @property-read {$getter}\";\n        }, $classDocumentation[self::DOC_FORMAT_PHP]['getters']);\n        $getters = implode('', $getters);\n\n        $methods = '';\n        foreach ($classDocumentation[self::DOC_FORMAT_PHP]['methods'] as $pos => $method) {\n            $methods .= \"\\n * @method {$method}\";\n\n            $phpStanMethod = $classDocumentation[self::DOC_FORMAT_PHPSTAN]['methods'][$pos];\n            // phpStorm works incorrectly if @phpstan-method is used.\n            // Using non-standard method-extended phpDoc:\n            $methods .= \"\\n * @method-extended {$phpStanMethod}\";\n        }\n\n        if ('' !== $properties || '' !== $getters || '' !== $methods) {\n            return \"/**{$properties}{$getters}{$methods}\\n */\";\n        }\n\n        return null;\n    }\n\n    /**\n     * Writes the doc comment in the PHP class.\n     */\n    private static function writePhpDoc(string $className, string $phpDoc): void\n    {\n        $reflectionClass = new \\ReflectionClass($className);\n\n        if (! $reflectionClass) {\n            return;\n        }\n\n        $fileName = $reflectionClass->getFileName();\n\n        $contents = file_get_contents($fileName);\n\n        // If there already is a doc comment, replace it.\n        if ($doc = $reflectionClass->getDocComment()) {\n            $newContents = str_replace($doc, $phpDoc, $contents);\n        } else {\n            $startLine = $reflectionClass->getStartLine();\n\n            $lines = explode(\"\\n\", $contents);\n\n            $before = \\array_slice($lines, 0, $startLine - 1);\n            $after = \\array_slice($lines, $startLine - 1);\n\n            $newContents = implode(\"\\n\", array_merge($before, explode(\"\\n\", $phpDoc), $after));\n        }\n\n        file_put_contents($fileName, $newContents);\n    }\n\n    /**\n     * Executes the current command.\n     */\n    protected function execute(InputInterface $input, OutputInterface $output): int\n    {\n        $io = new SymfonyStyle($input, $output);\n\n        $resourceNames = self::getResourceNames();\n        $documentation = self::getDocumentation($input->getOption('puppeteerPath'), $resourceNames);\n\n        foreach ($resourceNames as $resourceName) {\n            $classDocumentation = $documentation[$resourceName] ?? null;\n\n            if (null !== $classDocumentation) {\n                $phpDoc = self::generatePhpDocWithDocumentation($classDocumentation);\n                if (null !== $phpDoc) {\n                    $resourceClass = self::RESOURCES_NAMESPACE.'\\\\'.$resourceName;\n                    self::writePhpDoc($resourceClass, $phpDoc);\n                }\n            }\n        }\n\n        // Handle the specific Puppeteer class\n        $classDocumentation = array_replace_recursive($documentation['Puppeteer'], $documentation['PuppeteerNode']);\n        unset($documentation['Puppeteer'], $documentation['PuppeteerNode'], $resourceNames[array_search('Puppeteer', $resourceNames, true)]);\n\n        if (null !== $classDocumentation) {\n            $phpDoc = self::generatePhpDocWithDocumentation($classDocumentation);\n            if (null !== $phpDoc) {\n                self::writePhpDoc(Puppeteer::class, $phpDoc);\n            }\n        }\n\n        $missingResources = array_diff(array_keys($documentation), $resourceNames);\n        foreach ($missingResources as $resource) {\n            $io->warning(\"The {$resource} class in Puppeteer doesn't have any equivalent in PuPHPeteer.\");\n        }\n\n        $inexistantResources = array_diff($resourceNames, array_keys($documentation));\n        foreach ($inexistantResources as $resource) {\n            $io->error(\"The {$resource} resource doesn't have any equivalent in Puppeteer.\");\n        }\n\n        return 0;\n    }\n\n    private static function rmdirRecursive(string $dir): bool\n    {\n        $files = scandir($dir);\n        if (! \\is_array($files)) {\n            return false;\n        }\n        $files = array_diff($files, ['.', '..']);\n        foreach ($files as $file) {\n            (is_dir(\"{$dir}/{$file}\")) ? self::rmdirRecursive(\"{$dir}/{$file}\") : unlink(\"{$dir}/{$file}\");\n        }\n\n        return rmdir($dir);\n    }\n}\n"
  },
  {
    "path": "src/Puppeteer.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer;\n\nuse Composer\\Semver\\Semver;\nuse Exception;\nuse Nesk\\Rialto\\AbstractEntryPoint;\nuse Psr\\Log\\LoggerInterface;\nuse Symfony\\Component\\Process\\Process;\n\n/**\n * @property string $defaultBrowserRevision\n * @property mixed $configuration\n * @property-read string $browserVersion\n * @property-read string|null $defaultDownloadPath\n * @property-read mixed $lastLaunchedBrowser\n * @property-read mixed $defaultBrowser\n * @property-read string $product\n * @method \\Nesk\\Puphpeteer\\Resources\\Browser connect(array $options)\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Browser connect(array<string, mixed> $options)\n * @method \\Nesk\\Puphpeteer\\Resources\\Browser launch(array $options = [])\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Browser launch(array<string, mixed> $options = null)\n * @method string executablePath()\n * @method-extended string executablePath()\n * @method string[] defaultArgs(array $options = [])\n * @method-extended string[] defaultArgs(array<string, mixed> $options = null)\n * @method void trimCache()\n * @method-extended void trimCache()\n */\nclass Puppeteer extends AbstractEntryPoint\n{\n    /**\n     * Default options.\n     *\n     * @var array\n     */\n    protected $options = [\n        'read_timeout' => 30,\n\n        // Logs the output of Browser's console methods (console.log, console.debug, etc...) to the PHP logger\n        'log_browser_console' => false,\n\n        // Custom js to load puppeteer-extra plugins\n        'js_extra' => '',\n    ];\n\n    /**\n     * Instantiate Puppeteer's entry point.\n     */\n    public function __construct(array $userOptions = [])\n    {\n        if (! empty($userOptions['logger']) && $userOptions['logger'] instanceof LoggerInterface) {\n            $this->checkPuppeteerVersion($userOptions['executable_path'] ?? 'node', $userOptions['logger']);\n        }\n\n        parent::__construct(\n            __DIR__.'/PuppeteerConnectionDelegate.js',\n            new PuppeteerProcessDelegate(),\n            $this->options,\n            $userOptions\n        );\n    }\n\n    private function checkPuppeteerVersion(string $nodePath, LoggerInterface $logger): void\n    {\n        $currentVersion = $this->currentPuppeteerVersion($nodePath);\n        $acceptedVersions = $this->acceptedPuppeteerVersion();\n\n        if (null === $currentVersion) {\n            $logger->warning(\"Puppeteer doesn't seem to be installed.\");\n\n            return;\n        }\n\n        if (! Semver::satisfies($currentVersion, $acceptedVersions)) {\n            $logger->warning(\n                \"The installed version of Puppeteer (v{$currentVersion}) doesn't match the requirements\"\n                .\" ({$acceptedVersions}), you may encounter issues.\"\n            );\n        }\n    }\n\n    private function currentPuppeteerVersion(string $nodePath): ?string\n    {\n        $process = new Process([$nodePath, __DIR__.'/get-puppeteer-version.js']);\n        $process->mustRun();\n\n        return json_decode($process->getOutput(), true, 10, JSON_THROW_ON_ERROR);\n    }\n\n    private function acceptedPuppeteerVersion(): string\n    {\n        $npmManifestPath = __DIR__.'/../package.json';\n        $contents = file_get_contents($npmManifestPath) ?: throw new Exception('Cant load file');\n        $npmManifest = json_decode($contents, false, 10, JSON_THROW_ON_ERROR);\n\n        return $npmManifest->dependencies->puppeteer;\n    }\n}\n"
  },
  {
    "path": "src/PuppeteerConnectionDelegate.js",
    "content": "\"use strict\";\n\nconst { ConnectionDelegate } = require(\"@zoon/rialto\"),\n  Logger = require(\"@zoon/rialto/src/node-process/Logger\"),\n  ConsoleInterceptor = require(\"@zoon/rialto/src/node-process/NodeInterceptors/ConsoleInterceptor\"),\n  StandardStreamsInterceptor = require(\"@zoon/rialto/src/node-process/NodeInterceptors/StandardStreamsInterceptor\");\n\n/**\n * Handle the requests of a connection to control Puppeteer.\n */\nclass PuppeteerConnectionDelegate extends ConnectionDelegate {\n  /**\n   * Constructor.\n   *\n   * @param  {Object} options\n   */\n  constructor(options) {\n    super(options);\n\n    this.browsers = new Set();\n\n    this.addSignalEventListeners();\n  }\n\n  /**\n   * @inheritdoc\n   */\n  async handleInstruction(instruction, responseHandler, errorHandler) {\n    if (this.options.js_extra) {\n      eval(this.options.js_extra);\n    } else {\n      const puppeteer = require(\"puppeteer\");\n      instruction.setDefaultResource(puppeteer);\n    }\n\n    let value = null;\n\n    try {\n      value = await instruction.execute();\n    } catch (error) {\n      if (instruction.shouldCatchErrors()) {\n        return errorHandler(error);\n      }\n\n      throw error;\n    }\n\n    if (this.isInstanceOf(value, \"Browser\")) {\n      this.browsers.add(value);\n\n      if (this.options.log_browser_console === true) {\n        const initialPages = await value.pages();\n        initialPages.forEach((page) =>\n          page.on(\"console\", this.logConsoleMessage)\n        );\n      }\n    }\n\n    if (\n      this.options.log_browser_console === true &&\n      this.isInstanceOf(value, \"Page\")\n    ) {\n      value.on(\"console\", this.logConsoleMessage);\n    }\n\n    if (this.isInstanceOf(value, Uint8Array.name)) {\n      value = Buffer.from(value)\n    }\n\n    responseHandler(value);\n  }\n\n  /**\n   * Checks if a value is an instance of a class. The check must be done with the `[object].constructor.name`\n   * property because relying on Puppeteer's constructors isn't viable since the exports aren't constrained by semver.\n   *\n   * @protected\n   * @param  {*} value\n   * @param  {string} className\n   *\n   * @see {@link https://github.com/GoogleChrome/puppeteer/issues/3067|Puppeteer's issue about semver on exports}\n   */\n  isInstanceOf(value, className) {\n    const nonObjectValues = [undefined, null];\n\n    return (\n      !nonObjectValues.includes(value) &&\n      !nonObjectValues.includes(value.constructor) &&\n      (\n          value.constructor.name === className\n          || value.constructor.name === \"Cdp\" + className\n          || value.constructor.name === \"CDP\" + className\n      )\n    );\n  }\n\n  /**\n   * Log the console message.\n   *\n   * @param  {ConsoleMessage} consoleMessage\n   */\n  async logConsoleMessage(consoleMessage) {\n    const type = consoleMessage.type();\n\n    if (!ConsoleInterceptor.typeIsSupported(type)) {\n      return;\n    }\n\n    const level = ConsoleInterceptor.getLevelFromType(type);\n    const args = await Promise.all(\n      consoleMessage.args().map((arg) => arg.jsonValue())\n    );\n\n    StandardStreamsInterceptor.startInterceptingStrings((message) => {\n      Logger.log(\"Browser\", level, ConsoleInterceptor.formatMessage(message));\n    });\n\n    ConsoleInterceptor.originalConsole[type](...args);\n\n    StandardStreamsInterceptor.stopInterceptingStrings();\n  }\n\n  /**\n   * Listen for process signal events.\n   *\n   * @protected\n   */\n  addSignalEventListeners() {\n    for (let eventName of [\"SIGINT\", \"SIGTERM\", \"SIGHUP\"]) {\n      process.on(eventName, () => {\n        this.closeAllBrowsers();\n        process.exit();\n      });\n    }\n  }\n\n  /**\n   * Close all the browser instances when the process exits.\n   *\n   * Calling this method before exiting Node is mandatory since Puppeteer doesn't seem to handle that properly.\n   *\n   * @protected\n   */\n  closeAllBrowsers() {\n    for (let browser of this.browsers.values()) {\n      browser.close();\n    }\n  }\n}\n\nmodule.exports = PuppeteerConnectionDelegate;\n"
  },
  {
    "path": "src/PuppeteerProcessDelegate.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer;\n\nuse Nesk\\Rialto\\Interfaces\\ShouldHandleProcessDelegation;\nuse Nesk\\Rialto\\Traits\\UsesBasicResourceAsDefault;\n\nclass PuppeteerProcessDelegate implements ShouldHandleProcessDelegation\n{\n    use UsesBasicResourceAsDefault;\n\n    public function resourceFromOriginalClassName(string $className): ?string\n    {\n        $class = \"Nesk\\\\Puphpeteer\\\\Resources\\\\{$className}\";\n\n        if (class_exists($class)) {\n            return $class;\n        }\n\n        $classWithoutCDP = 'Nesk\\\\Puphpeteer\\\\Resources\\\\'. preg_replace('/^Cdp/i', '', $className);\n\n        if (class_exists($classWithoutCDP)) {\n            return $classWithoutCDP;\n        }\n\n        return null;\n    }\n}\n"
  },
  {
    "path": "src/Resources/Accessibility.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @method mixed|null snapshot(array $options = [])\n * @method-extended mixed|null snapshot(array<string, mixed> $options = null)\n */\nclass Accessibility extends BasicResource\n{\n}\n"
  },
  {
    "path": "src/Resources/Browser.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\n/**\n * @property-read bool $connected\n * @property-read mixed $protocol\n * @property-read mixed $debugInfo\n * @method mixed|null process()\n * @method-extended mixed|null process()\n * @method \\Nesk\\Puphpeteer\\Resources\\BrowserContext createBrowserContext(array $options = [])\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\BrowserContext createBrowserContext(array<string, mixed> $options = null)\n * @method \\Nesk\\Puphpeteer\\Resources\\BrowserContext[] browserContexts()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\BrowserContext[] browserContexts()\n * @method \\Nesk\\Puphpeteer\\Resources\\BrowserContext defaultBrowserContext()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\BrowserContext defaultBrowserContext()\n * @method string wsEndpoint()\n * @method-extended string wsEndpoint()\n * @method \\Nesk\\Puphpeteer\\Resources\\Page newPage()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Page newPage()\n * @method \\Nesk\\Puphpeteer\\Resources\\Target[] targets()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Target[] targets()\n * @method \\Nesk\\Puphpeteer\\Resources\\Target target()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Target target()\n * @method \\Nesk\\Puphpeteer\\Resources\\Target waitForTarget(\\Nesk\\Rialto\\Data\\JsFunction $predicate, array $options = [])\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Target waitForTarget(callable(\\Nesk\\Puphpeteer\\Resources\\Target $x): bool|Promise|bool[]|\\Nesk\\Rialto\\Data\\JsFunction $predicate, array<string, mixed> $options = null)\n * @method \\Nesk\\Puphpeteer\\Resources\\Page[] pages()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Page[] pages()\n * @method string version()\n * @method-extended string version()\n * @method string userAgent()\n * @method-extended string userAgent()\n * @method void close()\n * @method-extended void close()\n * @method void disconnect()\n * @method-extended void disconnect()\n * @method mixed[] cookies()\n * @method-extended mixed[] cookies()\n * @method void setCookie(mixed ...$cookies)\n * @method-extended void setCookie(mixed ...$cookies)\n * @method void deleteCookie(mixed ...$cookies)\n * @method-extended void deleteCookie(mixed ...$cookies)\n * @method void deleteMatchingCookies(mixed ...$filters)\n * @method-extended void deleteMatchingCookies(mixed ...$filters)\n * @method string installExtension(string $path)\n * @method-extended string installExtension(string $path)\n * @method void uninstallExtension(string $id)\n * @method-extended void uninstallExtension(string $id)\n * @method bool isConnected()\n * @method-extended bool isConnected()\n * @method bool isNetworkEnabled()\n * @method-extended bool isNetworkEnabled()\n */\nclass Browser extends EventEmitter\n{\n}\n"
  },
  {
    "path": "src/Resources/BrowserContext.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\n/**\n * @property-read bool $closed\n * @property-read string|null $id\n * @method \\Nesk\\Puphpeteer\\Resources\\Target[] targets()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Target[] targets()\n * @method mixed startScreenshot()\n * @method-extended mixed startScreenshot()\n * @method mixed|null waitForScreenshotOperations()\n * @method-extended mixed|null waitForScreenshotOperations()\n * @method \\Nesk\\Puphpeteer\\Resources\\Target waitForTarget(\\Nesk\\Rialto\\Data\\JsFunction $predicate, array $options = [])\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Target waitForTarget(callable(\\Nesk\\Puphpeteer\\Resources\\Target $x): bool|Promise|bool[]|\\Nesk\\Rialto\\Data\\JsFunction $predicate, array<string, mixed> $options = null)\n * @method \\Nesk\\Puphpeteer\\Resources\\Page[] pages()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Page[] pages()\n * @method void overridePermissions(string $origin, mixed[] $permissions)\n * @method-extended void overridePermissions(string $origin, mixed[] $permissions)\n * @method void clearPermissionOverrides()\n * @method-extended void clearPermissionOverrides()\n * @method \\Nesk\\Puphpeteer\\Resources\\Page newPage()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Page newPage()\n * @method \\Nesk\\Puphpeteer\\Resources\\Browser browser()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Browser browser()\n * @method void close()\n * @method-extended void close()\n * @method mixed[] cookies()\n * @method-extended mixed[] cookies()\n * @method void setCookie(mixed ...$cookies)\n * @method-extended void setCookie(mixed ...$cookies)\n * @method void deleteCookie(mixed ...$cookies)\n * @method-extended void deleteCookie(mixed ...$cookies)\n * @method void deleteMatchingCookies(mixed ...$filters)\n * @method-extended void deleteMatchingCookies(mixed ...$filters)\n */\nclass BrowserContext extends EventEmitter\n{\n}\n"
  },
  {
    "path": "src/Resources/BrowserLauncher.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\n/**\n * @property mixed $puppeteer\n * @property-read mixed $browser\n * @method \\Nesk\\Puphpeteer\\Resources\\Browser launch(array $options = [])\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Browser launch(array<string, mixed> $options = null)\n * @method string executablePath(mixed $channel = null, bool $validatePath = null)\n * @method-extended string executablePath(mixed $channel = null, bool $validatePath = null)\n * @method string[] defaultArgs(array $object)\n * @method-extended string[] defaultArgs(array<string, mixed> $object)\n * @method string resolveExecutablePath(bool|'shell' $headless = null, bool $validatePath = null)\n * @method-extended string resolveExecutablePath(bool|'shell' $headless = null, bool $validatePath = null)\n */\nclass BrowserLauncher\n{\n}\n"
  },
  {
    "path": "src/Resources/BrowserWebSocketTransport.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\n/**\n * @property \\Nesk\\Rialto\\Data\\JsFunction $onmessage\n * @property \\Nesk\\Rialto\\Data\\JsFunction $onclose\n * @method void send(string $message)\n * @method-extended void send(string $message)\n * @method void close()\n * @method-extended void close()\n */\nclass BrowserWebSocketTransport\n{\n}\n"
  },
  {
    "path": "src/Resources/Buffer.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n\n/**\n * @method \\string toString(?string $encoding = \"utf8\", ?int $start = null, ?int $end = null)\n */\nclass Buffer extends BasicResource\n{\n}\n"
  },
  {
    "path": "src/Resources/CDPSession.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\n/**\n * @property-read bool $detached\n * @method mixed|null connection()\n * @method-extended mixed|null connection()\n * @method \\Nesk\\Puphpeteer\\Resources\\CDPSession|null parentSession()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\CDPSession|null parentSession()\n * @method mixed send(mixed $method, mixed $params = null, array $options = [])\n * @method-extended mixed send(mixed $method, mixed $params = null, array<string, mixed> $options = null)\n * @method void detach()\n * @method-extended void detach()\n * @method string id()\n * @method-extended string id()\n */\nclass CDPSession extends EventEmitter\n{\n}\n"
  },
  {
    "path": "src/Resources/ConsoleMessage.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @method mixed type()\n * @method-extended mixed type()\n * @method string text()\n * @method-extended string text()\n * @method \\Nesk\\Puphpeteer\\Resources\\JSHandle[] args()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\JSHandle[] args()\n * @method mixed location()\n * @method-extended mixed location()\n * @method mixed[] stackTrace()\n * @method-extended mixed[] stackTrace()\n */\nclass ConsoleMessage extends BasicResource\n{\n}\n"
  },
  {
    "path": "src/Resources/Coverage.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @method void updateClient(\\Nesk\\Puphpeteer\\Resources\\CDPSession $client)\n * @method-extended void updateClient(\\Nesk\\Puphpeteer\\Resources\\CDPSession $client)\n * @method void startJSCoverage(array $options = [])\n * @method-extended void startJSCoverage(array<string, mixed> $options = null)\n * @method mixed[] stopJSCoverage()\n * @method-extended mixed[] stopJSCoverage()\n * @method void startCSSCoverage(array $options = [])\n * @method-extended void startCSSCoverage(array<string, mixed> $options = null)\n * @method mixed[] stopCSSCoverage()\n * @method-extended mixed[] stopCSSCoverage()\n */\nclass Coverage extends BasicResource\n{\n}\n"
  },
  {
    "path": "src/Resources/DevToolsTarget.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nclass DevToolsTarget extends Target\n{\n}\n"
  },
  {
    "path": "src/Resources/Dialog.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @method mixed type()\n * @method-extended mixed type()\n * @method string message()\n * @method-extended string message()\n * @method string defaultValue()\n * @method-extended string defaultValue()\n * @method void accept(string $promptText = null)\n * @method-extended void accept(string $promptText = null)\n * @method void dismiss()\n * @method-extended void dismiss()\n */\nclass Dialog extends BasicResource\n{\n}\n"
  },
  {
    "path": "src/Resources/ElementHandle.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Puphpeteer\\Traits\\AliasesEvaluationMethods;\nuse Nesk\\Puphpeteer\\Traits\\AliasesSelectionMethods;\n\n/**\n * @property mixed $isolatedHandle\n * @property-read string|null $id\n * @property-read bool $disposed\n * @property-read \\Nesk\\Puphpeteer\\Resources\\Frame $frame\n * @method mixed getProperty(mixed $propertyName)\n * @method-extended mixed getProperty(mixed $propertyName)\n * @method array|string[]|\\Nesk\\Puphpeteer\\Resources\\JSHandle[] getProperties()\n * @method-extended array|string[]|\\Nesk\\Puphpeteer\\Resources\\JSHandle[] getProperties()\n * @method mixed evaluate(\\Nesk\\Rialto\\Data\\JsFunction $pageFunction, mixed ...$args)\n * @method-extended mixed evaluate(callable|\\Nesk\\Rialto\\Data\\JsFunction $pageFunction, mixed ...$args)\n * @method mixed evaluateHandle(\\Nesk\\Rialto\\Data\\JsFunction $pageFunction, mixed ...$args)\n * @method-extended mixed evaluateHandle(callable|\\Nesk\\Rialto\\Data\\JsFunction $pageFunction, mixed ...$args)\n * @method mixed jsonValue()\n * @method-extended mixed jsonValue()\n * @method string toString()\n * @method-extended string toString()\n * @method mixed remoteObject()\n * @method-extended mixed remoteObject()\n * @method void dispose()\n * @method-extended void dispose()\n * @method \\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] asElement()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] asElement()\n * @method \\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[]|null waitForSelector(mixed $selector, array $options = [])\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[]|null waitForSelector(mixed $selector, array<string, mixed> $options = null)\n * @method bool isVisible()\n * @method-extended bool isVisible()\n * @method bool isHidden()\n * @method-extended bool isHidden()\n * @method mixed toElement(mixed $tagName)\n * @method-extended mixed toElement(mixed $tagName)\n * @method \\Nesk\\Puphpeteer\\Resources\\Frame|null contentFrame()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Frame|null contentFrame()\n * @method mixed clickablePoint(mixed $offset = null)\n * @method-extended mixed clickablePoint(mixed $offset = null)\n * @method void hover(\\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $selector)\n * @method-extended void hover(\\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $selector)\n * @method void click(\\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $selector, mixed $options = null)\n * @method-extended void click(\\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $selector, mixed $options = null)\n * @method mixed|null drag(\\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $selector, mixed|\\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $target)\n * @method-extended mixed|null drag(\\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $selector, mixed|\\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $target)\n * @method void dragEnter(\\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $selector, mixed $data = null)\n * @method-extended void dragEnter(\\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $selector, mixed $data = null)\n * @method void dragOver(\\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $selector, mixed $data = null)\n * @method-extended void dragOver(\\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $selector, mixed $data = null)\n * @method void drop(\\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $selector, mixed $data = null)\n * @method-extended void drop(\\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $selector, mixed $data = null)\n * @method void dragAndDrop(\\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $selector, \\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $target, array $options = [])\n * @method-extended void dragAndDrop(\\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $selector, \\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $target, array{ $delay: float } $options = null)\n * @method string[] select(string ...$values)\n * @method-extended string[] select(string ...$values)\n * @method void uploadFile(\\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $selector, string ...$paths)\n * @method-extended void uploadFile(\\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $selector, string ...$paths)\n * @method mixed queryAXTree(string $name = null, string $role = null)\n * @method-extended mixed queryAXTree(string $name = null, string $role = null)\n * @method void tap(\\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $selector)\n * @method-extended void tap(\\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $selector)\n * @method mixed touchStart(\\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $selector)\n * @method-extended mixed touchStart(\\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $selector)\n * @method void touchMove(\\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $selector, mixed $touch = null)\n * @method-extended void touchMove(\\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $selector, mixed $touch = null)\n * @method void touchEnd(\\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $selector)\n * @method-extended void touchEnd(\\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $selector)\n * @method void focus()\n * @method-extended void focus()\n * @method void type(string $text, mixed $options = null)\n * @method-extended void type(string $text, mixed $options = null)\n * @method void press(mixed $key, mixed $options = null)\n * @method-extended void press(mixed $key, mixed $options = null)\n * @method mixed|null boundingBox()\n * @method-extended mixed|null boundingBox()\n * @method mixed|null boxModel()\n * @method-extended mixed|null boxModel()\n * @method \\Nesk\\Puphpeteer\\Resources\\Uint8Array screenshot(mixed $options = null)\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Uint8Array screenshot(mixed $options = null)\n * @method bool isIntersectingViewport(\\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $selector, array $options = [])\n * @method-extended bool isIntersectingViewport(\\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $selector, array{ $threshold: float } $options = null)\n * @method void scrollIntoView(\\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $selector)\n * @method-extended void scrollIntoView(\\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $selector)\n * @method mixed asLocator(\\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $selector)\n * @method-extended mixed asLocator(\\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] $selector)\n * @method void autofill(mixed $data)\n * @method-extended void autofill(mixed $data)\n * @method float backendNodeId()\n * @method-extended float backendNodeId()\n */\nclass ElementHandle extends JSHandle\n{\n    use AliasesEvaluationMethods;\n    use AliasesSelectionMethods;\n}\n"
  },
  {
    "path": "src/Resources/EventEmitter.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @method mixed on(mixed $type, mixed $handler)\n * @method-extended mixed on(mixed $type, mixed $handler)\n * @method mixed off(mixed $type, mixed $handler = null)\n * @method-extended mixed off(mixed $type, mixed $handler = null)\n * @method bool emit(mixed $type, mixed $event)\n * @method-extended bool emit(mixed $type, mixed $event)\n * @method mixed once(mixed $type, mixed $handler)\n * @method-extended mixed once(mixed $type, mixed $handler)\n * @method float listenerCount(mixed $type)\n * @method-extended float listenerCount(mixed $type)\n * @method mixed removeAllListeners(mixed $type = null)\n * @method-extended mixed removeAllListeners(mixed $type = null)\n */\nclass EventEmitter extends BasicResource\n{\n}\n"
  },
  {
    "path": "src/Resources/FileChooser.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @method bool isMultiple()\n * @method-extended bool isMultiple()\n * @method void accept(string[] $paths)\n * @method-extended void accept(string[] $paths)\n * @method void cancel()\n * @method-extended void cancel()\n */\nclass FileChooser extends BasicResource\n{\n}\n"
  },
  {
    "path": "src/Resources/Frame.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Puphpeteer\\Traits\\AliasesEvaluationMethods;\nuse Nesk\\Puphpeteer\\Traits\\AliasesSelectionMethods;\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @property-read \\Nesk\\Puphpeteer\\Resources\\CDPSession $client\n * @property-read \\Nesk\\Puphpeteer\\Resources\\Accessibility $accessibility\n * @property-read bool $detached\n * @property-read bool $disposed\n * @method \\Nesk\\Puphpeteer\\Resources\\Page page()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Page page()\n * @method \\Nesk\\Puphpeteer\\Resources\\HTTPResponse|null goto(string $url, array $options = [])\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\HTTPResponse|null goto(string $url, array<string, mixed> $options = null)\n * @method \\Nesk\\Puphpeteer\\Resources\\HTTPResponse|null waitForNavigation(array $options = [])\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\HTTPResponse|null waitForNavigation(array<string, mixed> $options = null)\n * @method \\Nesk\\Puphpeteer\\Resources\\Realm mainRealm()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Realm mainRealm()\n * @method \\Nesk\\Puphpeteer\\Resources\\Realm isolatedRealm()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Realm isolatedRealm()\n * @method void clearDocumentHandle()\n * @method-extended void clearDocumentHandle()\n * @method mixed|null frameElement()\n * @method-extended mixed|null frameElement()\n * @method mixed evaluateHandle(\\Nesk\\Rialto\\Data\\JsFunction $pageFunction, mixed ...$args)\n * @method-extended mixed evaluateHandle(callable|\\Nesk\\Rialto\\Data\\JsFunction $pageFunction, mixed ...$args)\n * @method mixed evaluate(\\Nesk\\Rialto\\Data\\JsFunction $pageFunction, mixed ...$args)\n * @method-extended mixed evaluate(callable|\\Nesk\\Rialto\\Data\\JsFunction $pageFunction, mixed ...$args)\n * @method mixed locator(\\Nesk\\Rialto\\Data\\JsFunction $func)\n * @method-extended mixed locator(callable(): mixed|\\Nesk\\Rialto\\Data\\JsFunction $func)\n * @method \\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[]|null waitForSelector(mixed $selector, array $options = [])\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[]|null waitForSelector(mixed $selector, array<string, mixed> $options = null)\n * @method mixed waitForFunction(\\Nesk\\Rialto\\Data\\JsFunction $pageFunction, array $options = [], mixed ...$args)\n * @method-extended mixed waitForFunction(callable|\\Nesk\\Rialto\\Data\\JsFunction $pageFunction, array<string, mixed> $options = null, mixed ...$args)\n * @method string content()\n * @method-extended string content()\n * @method void setContent(string $html, array $options = [])\n * @method-extended void setContent(string $html, array<string, mixed> $options = null)\n * @method void setFrameContent(string $content)\n * @method-extended void setFrameContent(string $content)\n * @method string name()\n * @method-extended string name()\n * @method string url()\n * @method-extended string url()\n * @method \\Nesk\\Puphpeteer\\Resources\\Frame|null parentFrame()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Frame|null parentFrame()\n * @method \\Nesk\\Puphpeteer\\Resources\\Frame[] childFrames()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Frame[] childFrames()\n * @method bool isDetached()\n * @method-extended bool isDetached()\n * @method \\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] addScriptTag(array $options)\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] addScriptTag(array<string, mixed> $options)\n * @method \\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] addStyleTag(array $options)\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] addStyleTag(array<string, mixed> $options)\n * @method void click(string $selector, mixed $options = null)\n * @method-extended void click(string $selector, mixed $options = null)\n * @method void focus(string $selector)\n * @method-extended void focus(string $selector)\n * @method void hover(string $selector)\n * @method-extended void hover(string $selector)\n * @method string[] select(string $selector, string ...$values)\n * @method-extended string[] select(string $selector, string ...$values)\n * @method void tap(string $selector)\n * @method-extended void tap(string $selector)\n * @method void type(string $selector, string $text, mixed $options = null)\n * @method-extended void type(string $selector, string $text, mixed $options = null)\n * @method string title()\n * @method-extended string title()\n * @method mixed waitForDevicePrompt(array $options = [])\n * @method-extended mixed waitForDevicePrompt(array<string, mixed> $options = null)\n */\nclass Frame extends BasicResource\n{\n    use AliasesEvaluationMethods;\n    use AliasesSelectionMethods;\n}\n"
  },
  {
    "path": "src/Resources/HTTPRequest.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @property-read string $id\n * @property-read \\Nesk\\Puphpeteer\\Resources\\CDPSession $client\n * @method string url()\n * @method-extended string url()\n * @method mixed continueRequestOverrides()\n * @method-extended mixed continueRequestOverrides()\n * @method mixed|null responseForRequest()\n * @method-extended mixed|null responseForRequest()\n * @method mixed|null abortErrorReason()\n * @method-extended mixed|null abortErrorReason()\n * @method mixed interceptResolutionState()\n * @method-extended mixed interceptResolutionState()\n * @method bool isInterceptResolutionHandled()\n * @method-extended bool isInterceptResolutionHandled()\n * @method void enqueueInterceptAction(\\Nesk\\Rialto\\Data\\JsFunction $pendingHandler)\n * @method-extended void enqueueInterceptAction(callable(): null|mixed|\\Nesk\\Rialto\\Data\\JsFunction $pendingHandler)\n * @method void finalizeInterceptions()\n * @method-extended void finalizeInterceptions()\n * @method mixed resourceType()\n * @method-extended mixed resourceType()\n * @method string method()\n * @method-extended string method()\n * @method string|null postData()\n * @method-extended string|null postData()\n * @method bool hasPostData()\n * @method-extended bool hasPostData()\n * @method string|null fetchPostData()\n * @method-extended string|null fetchPostData()\n * @method array|string[]|string[] headers()\n * @method-extended array|string[]|string[] headers()\n * @method \\Nesk\\Puphpeteer\\Resources\\HTTPResponse|null response()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\HTTPResponse|null response()\n * @method \\Nesk\\Puphpeteer\\Resources\\Frame|null frame()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Frame|null frame()\n * @method bool isNavigationRequest()\n * @method-extended bool isNavigationRequest()\n * @method mixed|null initiator()\n * @method-extended mixed|null initiator()\n * @method \\Nesk\\Puphpeteer\\Resources\\HTTPRequest[] redirectChain()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\HTTPRequest[] redirectChain()\n * @method array|null failure()\n * @method-extended array{ $errorText: string }|null failure()\n * @method void continue(mixed $overrides = null, float $priority = null)\n * @method-extended void continue(mixed $overrides = null, float $priority = null)\n * @method void respond(mixed $response, float $priority = null)\n * @method-extended void respond(mixed $response, float $priority = null)\n * @method void abort(mixed $errorCode = null, float $priority = null)\n * @method-extended void abort(mixed $errorCode = null, float $priority = null)\n */\nclass HTTPRequest extends BasicResource\n{\n}\n"
  },
  {
    "path": "src/Resources/HTTPResponse.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @method mixed remoteAddress()\n * @method-extended mixed remoteAddress()\n * @method string url()\n * @method-extended string url()\n * @method bool ok()\n * @method-extended bool ok()\n * @method float status()\n * @method-extended float status()\n * @method string statusText()\n * @method-extended string statusText()\n * @method array|string[]|string[] headers()\n * @method-extended array|string[]|string[] headers()\n * @method \\Nesk\\Puphpeteer\\Resources\\SecurityDetails|null securityDetails()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\SecurityDetails|null securityDetails()\n * @method mixed|null timing()\n * @method-extended mixed|null timing()\n * @method \\Nesk\\Puphpeteer\\Resources\\Uint8Array content()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Uint8Array content()\n * @method \\Nesk\\Puphpeteer\\Resources\\Buffer buffer()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Buffer buffer()\n * @method string text()\n * @method-extended string text()\n * @method mixed json()\n * @method-extended mixed json()\n * @method \\Nesk\\Puphpeteer\\Resources\\HTTPRequest request()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\HTTPRequest request()\n * @method bool fromCache()\n * @method-extended bool fromCache()\n * @method bool fromServiceWorker()\n * @method-extended bool fromServiceWorker()\n * @method \\Nesk\\Puphpeteer\\Resources\\Frame|null frame()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Frame|null frame()\n */\nclass HTTPResponse extends BasicResource\n{\n}\n"
  },
  {
    "path": "src/Resources/JSHandle.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @property \\Nesk\\Rialto\\Data\\JsFunction $move\n * @property-read \\Nesk\\Puphpeteer\\Resources\\Realm $realm\n * @property-read bool $disposed\n * @property-read string|null $id\n * @method mixed evaluate(\\Nesk\\Rialto\\Data\\JsFunction $pageFunction, mixed ...$args)\n * @method-extended mixed evaluate(callable|\\Nesk\\Rialto\\Data\\JsFunction $pageFunction, mixed ...$args)\n * @method mixed evaluateHandle(\\Nesk\\Rialto\\Data\\JsFunction $pageFunction, mixed ...$args)\n * @method-extended mixed evaluateHandle(callable|\\Nesk\\Rialto\\Data\\JsFunction $pageFunction, mixed ...$args)\n * @method \\Nesk\\Puphpeteer\\Resources\\JSHandle|mixed[] getProperty(string $propertyName)\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\JSHandle|mixed[] getProperty(string $propertyName)\n * @method array|string[]|\\Nesk\\Puphpeteer\\Resources\\JSHandle[] getProperties()\n * @method-extended array|string[]|\\Nesk\\Puphpeteer\\Resources\\JSHandle[] getProperties()\n * @method mixed jsonValue()\n * @method-extended mixed jsonValue()\n * @method \\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[]|null asElement()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[]|null asElement()\n * @method void dispose()\n * @method-extended void dispose()\n * @method string toString()\n * @method-extended string toString()\n * @method mixed remoteObject()\n * @method-extended mixed remoteObject()\n */\nclass JSHandle extends BasicResource\n{\n}\n"
  },
  {
    "path": "src/Resources/Keyboard.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @method void down(mixed $key, mixed $options = null)\n * @method-extended void down(mixed $key, mixed $options = null)\n * @method void up(mixed $key)\n * @method-extended void up(mixed $key)\n * @method void sendCharacter(string $char)\n * @method-extended void sendCharacter(string $char)\n * @method void type(string $text, mixed $options = null)\n * @method-extended void type(string $text, mixed $options = null)\n * @method void press(mixed $key, mixed $options = null)\n * @method-extended void press(mixed $key, mixed $options = null)\n */\nclass Keyboard extends BasicResource\n{\n}\n"
  },
  {
    "path": "src/Resources/Mouse.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @method void reset()\n * @method-extended void reset()\n * @method void move(float $x, float $y, mixed $options = null)\n * @method-extended void move(float $x, float $y, mixed $options = null)\n * @method void down(mixed $options = null)\n * @method-extended void down(mixed $options = null)\n * @method void up(mixed $options = null)\n * @method-extended void up(mixed $options = null)\n * @method void click(float $x, float $y, mixed $options = null)\n * @method-extended void click(float $x, float $y, mixed $options = null)\n * @method void wheel(mixed $options = null)\n * @method-extended void wheel(mixed $options = null)\n * @method mixed drag(mixed $start, mixed $target)\n * @method-extended mixed drag(mixed $start, mixed $target)\n * @method void dragEnter(mixed $target, mixed $data)\n * @method-extended void dragEnter(mixed $target, mixed $data)\n * @method void dragOver(mixed $target, mixed $data)\n * @method-extended void dragOver(mixed $target, mixed $data)\n * @method void drop(mixed $target, mixed $data)\n * @method-extended void drop(mixed $target, mixed $data)\n * @method void dragAndDrop(mixed $start, mixed $target, array $options = [])\n * @method-extended void dragAndDrop(mixed $start, mixed $target, array{ $delay: float } $options = null)\n */\nclass Mouse extends BasicResource\n{\n}\n"
  },
  {
    "path": "src/Resources/OtherTarget.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nclass OtherTarget extends Target\n{\n}\n"
  },
  {
    "path": "src/Resources/Page.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Puphpeteer\\Traits\\AliasesEvaluationMethods;\nuse Nesk\\Puphpeteer\\Traits\\AliasesSelectionMethods;\n\n/**\n * @property-read \\Nesk\\Puphpeteer\\Resources\\Keyboard $keyboard\n * @property-read \\Nesk\\Puphpeteer\\Resources\\Touchscreen $touchscreen\n * @property-read \\Nesk\\Puphpeteer\\Resources\\Coverage $coverage\n * @property-read \\Nesk\\Puphpeteer\\Resources\\Tracing $tracing\n * @property-read \\Nesk\\Puphpeteer\\Resources\\Accessibility $accessibility\n * @property-read \\Nesk\\Puphpeteer\\Resources\\Mouse $mouse\n * @method bool isServiceWorkerBypassed()\n * @method-extended bool isServiceWorkerBypassed()\n * @method bool isDragInterceptionEnabled()\n * @method-extended bool isDragInterceptionEnabled()\n * @method bool isJavaScriptEnabled()\n * @method-extended bool isJavaScriptEnabled()\n * @method mixed on(mixed $type, \\Nesk\\Rialto\\Data\\JsFunction $handler)\n * @method-extended mixed on(mixed $type, callable(mixed $event): void|\\Nesk\\Rialto\\Data\\JsFunction $handler)\n * @method mixed off(mixed $type, \\Nesk\\Rialto\\Data\\JsFunction $handler)\n * @method-extended mixed off(mixed $type, callable(mixed $event): void|\\Nesk\\Rialto\\Data\\JsFunction $handler)\n * @method \\Nesk\\Puphpeteer\\Resources\\FileChooser waitForFileChooser(array $options = [])\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\FileChooser waitForFileChooser(array<string, mixed> $options = null)\n * @method void setGeolocation(array $options)\n * @method-extended void setGeolocation(array<string, mixed> $options)\n * @method \\Nesk\\Puphpeteer\\Resources\\Target target()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Target target()\n * @method \\Nesk\\Puphpeteer\\Resources\\Browser browser()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Browser browser()\n * @method \\Nesk\\Puphpeteer\\Resources\\BrowserContext browserContext()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\BrowserContext browserContext()\n * @method \\Nesk\\Puphpeteer\\Resources\\Frame mainFrame()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Frame mainFrame()\n * @method \\Nesk\\Puphpeteer\\Resources\\CDPSession createCDPSession()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\CDPSession createCDPSession()\n * @method \\Nesk\\Puphpeteer\\Resources\\Frame[] frames()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Frame[] frames()\n * @method \\Nesk\\Puphpeteer\\Resources\\WebWorker[] workers()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\WebWorker[] workers()\n * @method void setRequestInterception(bool $value)\n * @method-extended void setRequestInterception(bool $value)\n * @method void setBypassServiceWorker(bool $bypass)\n * @method-extended void setBypassServiceWorker(bool $bypass)\n * @method void setDragInterception(bool $enabled)\n * @method-extended void setDragInterception(bool $enabled)\n * @method void setOfflineMode(bool $enabled)\n * @method-extended void setOfflineMode(bool $enabled)\n * @method void emulateNetworkConditions(mixed|null $networkConditions)\n * @method-extended void emulateNetworkConditions(mixed|null $networkConditions)\n * @method void setDefaultNavigationTimeout(float $timeout)\n * @method-extended void setDefaultNavigationTimeout(float $timeout)\n * @method void setDefaultTimeout(float $timeout)\n * @method-extended void setDefaultTimeout(float $timeout)\n * @method float getDefaultTimeout()\n * @method-extended float getDefaultTimeout()\n * @method float getDefaultNavigationTimeout()\n * @method-extended float getDefaultNavigationTimeout()\n * @method mixed locator(\\Nesk\\Rialto\\Data\\JsFunction $func)\n * @method-extended mixed locator(callable(): mixed|\\Nesk\\Rialto\\Data\\JsFunction $func)\n * @method mixed locatorRace(mixed $locators)\n * @method-extended mixed locatorRace(mixed $locators)\n * @method mixed evaluateHandle(\\Nesk\\Rialto\\Data\\JsFunction $pageFunction, mixed ...$args)\n * @method-extended mixed evaluateHandle(callable|\\Nesk\\Rialto\\Data\\JsFunction $pageFunction, mixed ...$args)\n * @method \\Nesk\\Puphpeteer\\Resources\\JSHandle|mixed[][] queryObjects(\\Nesk\\Puphpeteer\\Resources\\JSHandle|mixed[] $prototypeHandle)\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\JSHandle|mixed[][] queryObjects(\\Nesk\\Puphpeteer\\Resources\\JSHandle|mixed[] $prototypeHandle)\n * @method mixed[] cookies(string ...$urls)\n * @method-extended mixed[] cookies(string ...$urls)\n * @method void deleteCookie(mixed ...$cookies)\n * @method-extended void deleteCookie(mixed ...$cookies)\n * @method void setCookie(mixed ...$cookies)\n * @method-extended void setCookie(mixed ...$cookies)\n * @method \\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] addScriptTag(array $options)\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] addScriptTag(array<string, mixed> $options)\n * @method \\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] addStyleTag(array $options)\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[] addStyleTag(array<string, mixed> $options)\n * @method void exposeFunction(string $name, \\Nesk\\Rialto\\Data\\JsFunction|array $pptrFunction)\n * @method-extended void exposeFunction(string $name, callable|\\Nesk\\Rialto\\Data\\JsFunction|array{ $default: callable|\\Nesk\\Rialto\\Data\\JsFunction } $pptrFunction)\n * @method void removeExposedFunction(string $name)\n * @method-extended void removeExposedFunction(string $name)\n * @method void authenticate(mixed|null $credentials)\n * @method-extended void authenticate(mixed|null $credentials)\n * @method void setExtraHTTPHeaders(array|string[]|string[] $headers)\n * @method-extended void setExtraHTTPHeaders(array|string[]|string[] $headers)\n * @method void setUserAgent(array $options)\n * @method-extended void setUserAgent(array{ $userAgent: string, $userAgentMetadata: mixed, $platform: string } $options)\n * @method mixed metrics()\n * @method-extended mixed metrics()\n * @method string url()\n * @method-extended string url()\n * @method string content()\n * @method-extended string content()\n * @method void setContent(string $html, array $options = [])\n * @method-extended void setContent(string $html, array<string, mixed> $options = null)\n * @method \\Nesk\\Puphpeteer\\Resources\\HTTPResponse|null goto(string $url, array $options = [])\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\HTTPResponse|null goto(string $url, array<string, mixed> $options = null)\n * @method \\Nesk\\Puphpeteer\\Resources\\HTTPResponse|null reload(array $options = [])\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\HTTPResponse|null reload(array<string, mixed> $options = null)\n * @method \\Nesk\\Puphpeteer\\Resources\\HTTPResponse|null waitForNavigation(array $options = [])\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\HTTPResponse|null waitForNavigation(array<string, mixed> $options = null)\n * @method \\Nesk\\Puphpeteer\\Resources\\HTTPRequest waitForRequest(string|mixed $urlOrPredicate, array $options = [])\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\HTTPRequest waitForRequest(string|mixed $urlOrPredicate, array<string, mixed> $options = null)\n * @method \\Nesk\\Puphpeteer\\Resources\\HTTPResponse waitForResponse(string|mixed $urlOrPredicate, array $options = [])\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\HTTPResponse waitForResponse(string|mixed $urlOrPredicate, array<string, mixed> $options = null)\n * @method void waitForNetworkIdle(array $options = [])\n * @method-extended void waitForNetworkIdle(array<string, mixed> $options = null)\n * @method \\Nesk\\Puphpeteer\\Resources\\Frame waitForFrame(string|\\Nesk\\Rialto\\Data\\JsFunction $urlOrPredicate, array $options = [])\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Frame waitForFrame(string|callable(callable(\\Nesk\\Puphpeteer\\Resources\\Frame $frame): mixed|\\Nesk\\Rialto\\Data\\JsFunction): |\\Nesk\\Rialto\\Data\\JsFunction $urlOrPredicate, array<string, mixed> $options = null)\n * @method \\Nesk\\Puphpeteer\\Resources\\HTTPResponse|null goBack(array $options = [])\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\HTTPResponse|null goBack(array<string, mixed> $options = null)\n * @method \\Nesk\\Puphpeteer\\Resources\\HTTPResponse|null goForward(array $options = [])\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\HTTPResponse|null goForward(array<string, mixed> $options = null)\n * @method void bringToFront()\n * @method-extended void bringToFront()\n * @method void emulate(mixed $device)\n * @method-extended void emulate(mixed $device)\n * @method void setJavaScriptEnabled(bool $enabled)\n * @method-extended void setJavaScriptEnabled(bool $enabled)\n * @method void setBypassCSP(bool $enabled)\n * @method-extended void setBypassCSP(bool $enabled)\n * @method void emulateMediaType(string $type = null)\n * @method-extended void emulateMediaType(string $type = null)\n * @method void emulateCPUThrottling(float|null $factor)\n * @method-extended void emulateCPUThrottling(float|null $factor)\n * @method void emulateMediaFeatures(mixed[] $features = null)\n * @method-extended void emulateMediaFeatures(mixed[] $features = null)\n * @method void emulateTimezone(string $timezoneId = null)\n * @method-extended void emulateTimezone(string $timezoneId = null)\n * @method void emulateIdleState(array $overrides = [])\n * @method-extended void emulateIdleState(array{ $isUserActive: bool, $isScreenUnlocked: bool } $overrides = null)\n * @method void emulateVisionDeficiency(mixed $type = null)\n * @method-extended void emulateVisionDeficiency(mixed $type = null)\n * @method void setViewport(mixed|null $viewport)\n * @method-extended void setViewport(mixed|null $viewport)\n * @method mixed|null viewport()\n * @method-extended mixed|null viewport()\n * @method mixed evaluate(\\Nesk\\Rialto\\Data\\JsFunction $pageFunction, mixed ...$args)\n * @method-extended mixed evaluate(callable|\\Nesk\\Rialto\\Data\\JsFunction $pageFunction, mixed ...$args)\n * @method mixed evaluateOnNewDocument(\\Nesk\\Rialto\\Data\\JsFunction $pageFunction, mixed ...$args)\n * @method-extended mixed evaluateOnNewDocument(callable|\\Nesk\\Rialto\\Data\\JsFunction $pageFunction, mixed ...$args)\n * @method void removeScriptToEvaluateOnNewDocument(string $identifier)\n * @method-extended void removeScriptToEvaluateOnNewDocument(string $identifier)\n * @method void setCacheEnabled(bool $enabled = null)\n * @method-extended void setCacheEnabled(bool $enabled = null)\n * @method \\Nesk\\Puphpeteer\\Resources\\ScreenRecorder screencast(mixed $options = null)\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\ScreenRecorder screencast(mixed $options = null)\n * @method \\Nesk\\Puphpeteer\\Resources\\Uint8Array screenshot(mixed $options = null)\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Uint8Array screenshot(mixed $options = null)\n * @method mixed createPDFStream(array $options = [])\n * @method-extended mixed createPDFStream(array<string, mixed> $options = null)\n * @method \\Nesk\\Puphpeteer\\Resources\\Uint8Array pdf(array $options = [])\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Uint8Array pdf(array<string, mixed> $options = null)\n * @method string title()\n * @method-extended string title()\n * @method void close(array $options = [])\n * @method-extended void close(array{ $runBeforeUnload: bool } $options = null)\n * @method bool isClosed()\n * @method-extended bool isClosed()\n * @method void click(string $selector, mixed $options = null)\n * @method-extended void click(string $selector, mixed $options = null)\n * @method void focus(string $selector)\n * @method-extended void focus(string $selector)\n * @method void hover(string $selector)\n * @method-extended void hover(string $selector)\n * @method string[] select(string $selector, string ...$values)\n * @method-extended string[] select(string $selector, string ...$values)\n * @method void tap(string $selector)\n * @method-extended void tap(string $selector)\n * @method void type(string $selector, string $text, mixed $options = null)\n * @method-extended void type(string $selector, string $text, mixed $options = null)\n * @method \\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[]|null waitForSelector(mixed $selector, array $options = [])\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[]|null waitForSelector(mixed $selector, array<string, mixed> $options = null)\n * @method mixed waitForFunction(\\Nesk\\Rialto\\Data\\JsFunction $pageFunction, array $options = [], mixed ...$args)\n * @method-extended mixed waitForFunction(callable|\\Nesk\\Rialto\\Data\\JsFunction $pageFunction, array<string, mixed> $options = null, mixed ...$args)\n * @method mixed waitForDevicePrompt(array $options = [])\n * @method-extended mixed waitForDevicePrompt(array<string, mixed> $options = null)\n * @method void resize(array $params)\n * @method-extended void resize(array{ $contentWidth: float, $contentHeight: float } $params)\n */\nclass Page extends EventEmitter\n{\n    use AliasesEvaluationMethods;\n    use AliasesSelectionMethods;\n}\n"
  },
  {
    "path": "src/Resources/PageTarget.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\n/**\n * @method \\Nesk\\Puphpeteer\\Resources\\Page|null page()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Page|null page()\n */\nclass PageTarget extends Target\n{\n}\n"
  },
  {
    "path": "src/Resources/Realm.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\n/**\n * @property \\Nesk\\Puphpeteer\\Resources\\TaskManager $taskManager\n * @property-read mixed $environment\n * @property-read bool $disposed\n * @method mixed adoptHandle(mixed $handle)\n * @method-extended mixed adoptHandle(mixed $handle)\n * @method mixed transferHandle(mixed $handle)\n * @method-extended mixed transferHandle(mixed $handle)\n * @method mixed evaluateHandle(\\Nesk\\Rialto\\Data\\JsFunction $pageFunction, mixed ...$args)\n * @method-extended mixed evaluateHandle(callable|\\Nesk\\Rialto\\Data\\JsFunction $pageFunction, mixed ...$args)\n * @method mixed evaluate(\\Nesk\\Rialto\\Data\\JsFunction $pageFunction, mixed ...$args)\n * @method-extended mixed evaluate(callable|\\Nesk\\Rialto\\Data\\JsFunction $pageFunction, mixed ...$args)\n * @method mixed waitForFunction(\\Nesk\\Rialto\\Data\\JsFunction $pageFunction, array $options = [], mixed ...$args)\n * @method-extended mixed waitForFunction(callable|\\Nesk\\Rialto\\Data\\JsFunction $pageFunction, array{ $polling: 'raf'|'mutation'|float, $timeout: float, $root: \\Nesk\\Puphpeteer\\Resources\\ElementHandle|mixed[], $signal: mixed } $options = null, mixed ...$args)\n * @method \\Nesk\\Puphpeteer\\Resources\\JSHandle|mixed[] adoptBackendNode(float $backendNodeId = null)\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\JSHandle|mixed[] adoptBackendNode(float $backendNodeId = null)\n * @method void dispose()\n * @method-extended void dispose()\n */\nclass Realm\n{\n}\n"
  },
  {
    "path": "src/Resources/ScreenRecorder.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\n/**\n * @method void stop()\n * @method-extended void stop()\n */\nclass ScreenRecorder\n{\n}\n"
  },
  {
    "path": "src/Resources/SecurityDetails.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @method string issuer()\n * @method-extended string issuer()\n * @method float validFrom()\n * @method-extended float validFrom()\n * @method float validTo()\n * @method-extended float validTo()\n * @method string protocol()\n * @method-extended string protocol()\n * @method string subjectName()\n * @method-extended string subjectName()\n * @method string[] subjectAlternativeNames()\n * @method-extended string[] subjectAlternativeNames()\n */\nclass SecurityDetails extends BasicResource\n{\n}\n"
  },
  {
    "path": "src/Resources/Target.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @method \\Nesk\\Puphpeteer\\Resources\\WebWorker|null worker()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\WebWorker|null worker()\n * @method \\Nesk\\Puphpeteer\\Resources\\Page|null page()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Page|null page()\n * @method \\Nesk\\Puphpeteer\\Resources\\Page asPage()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Page asPage()\n * @method string url()\n * @method-extended string url()\n * @method \\Nesk\\Puphpeteer\\Resources\\CDPSession createCDPSession()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\CDPSession createCDPSession()\n * @method mixed type()\n * @method-extended mixed type()\n * @method \\Nesk\\Puphpeteer\\Resources\\Browser browser()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Browser browser()\n * @method \\Nesk\\Puphpeteer\\Resources\\BrowserContext browserContext()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\BrowserContext browserContext()\n * @method \\Nesk\\Puphpeteer\\Resources\\Target|null opener()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Target|null opener()\n */\nclass Target extends BasicResource\n{\n}\n"
  },
  {
    "path": "src/Resources/TaskManager.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\n/**\n * @method void add(\\Nesk\\Puphpeteer\\Resources\\WaitTask|mixed[] $task)\n * @method-extended void add(\\Nesk\\Puphpeteer\\Resources\\WaitTask|mixed[] $task)\n * @method void delete(\\Nesk\\Puphpeteer\\Resources\\WaitTask|mixed[] $task)\n * @method-extended void delete(\\Nesk\\Puphpeteer\\Resources\\WaitTask|mixed[] $task)\n * @method void terminateAll(mixed $error = null)\n * @method-extended void terminateAll(mixed $error = null)\n * @method void rerunAll()\n * @method-extended void rerunAll()\n */\nclass TaskManager\n{\n}\n"
  },
  {
    "path": "src/Resources/TimeoutError.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\nclass TimeoutError extends BasicResource\n{\n}\n"
  },
  {
    "path": "src/Resources/TimeoutSettings.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\n/**\n * @method void setDefaultTimeout(float $timeout)\n * @method-extended void setDefaultTimeout(float $timeout)\n * @method void setDefaultNavigationTimeout(float $timeout)\n * @method-extended void setDefaultNavigationTimeout(float $timeout)\n * @method float navigationTimeout()\n * @method-extended float navigationTimeout()\n * @method float timeout()\n * @method-extended float timeout()\n */\nclass TimeoutSettings\n{\n}\n"
  },
  {
    "path": "src/Resources/Touchscreen.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @property mixed $idGenerator\n * @property mixed[] $touches\n * @method void removeHandle(mixed $handle)\n * @method-extended void removeHandle(mixed $handle)\n * @method void tap(float $x, float $y)\n * @method-extended void tap(float $x, float $y)\n * @method mixed touchStart(float $x, float $y)\n * @method-extended mixed touchStart(float $x, float $y)\n * @method void touchMove(float $x, float $y)\n * @method-extended void touchMove(float $x, float $y)\n * @method void touchEnd()\n * @method-extended void touchEnd()\n */\nclass Touchscreen extends BasicResource\n{\n}\n"
  },
  {
    "path": "src/Resources/Tracing.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @method void updateClient(\\Nesk\\Puphpeteer\\Resources\\CDPSession $client)\n * @method-extended void updateClient(\\Nesk\\Puphpeteer\\Resources\\CDPSession $client)\n * @method void start(array $options = [])\n * @method-extended void start(array<string, mixed> $options = null)\n * @method \\Nesk\\Puphpeteer\\Resources\\Uint8Array|null stop()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Uint8Array|null stop()\n */\nclass Tracing extends BasicResource\n{\n}\n"
  },
  {
    "path": "src/Resources/Uint8Array.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n\n/**\n * Uint8Array automatically converted to Buffer to support easy base64 convertation\n */\nclass Uint8Array extends Buffer\n{\n}\n"
  },
  {
    "path": "src/Resources/WaitTask.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\n/**\n * @property-read Promise|mixed[] $result\n * @method void rerun()\n * @method-extended void rerun()\n * @method void terminate(mixed $error = null)\n * @method-extended void terminate(mixed $error = null)\n * @method mixed|null getBadError(mixed $error)\n * @method-extended mixed|null getBadError(mixed $error)\n */\nclass WaitTask\n{\n}\n"
  },
  {
    "path": "src/Resources/WebWorker.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @property \\Nesk\\Puphpeteer\\Resources\\TimeoutSettings $timeoutSettings\n * @property-read \\Nesk\\Puphpeteer\\Resources\\CDPSession $client\n * @method \\Nesk\\Puphpeteer\\Resources\\Realm mainRealm()\n * @method-extended \\Nesk\\Puphpeteer\\Resources\\Realm mainRealm()\n * @method string url()\n * @method-extended string url()\n * @method mixed evaluate(mixed|string $func, mixed ...$args)\n * @method-extended mixed evaluate(mixed|string $func, mixed ...$args)\n * @method mixed evaluateHandle(mixed|string $func, mixed ...$args)\n * @method-extended mixed evaluateHandle(mixed|string $func, mixed ...$args)\n * @method void close()\n * @method-extended void close()\n */\nclass WebWorker extends BasicResource\n{\n}\n"
  },
  {
    "path": "src/Resources/WorkerTarget.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\n/**\n * @method mixed|null worker()\n * @method-extended mixed|null worker()\n */\nclass WorkerTarget extends Target\n{\n}\n"
  },
  {
    "path": "src/Traits/AliasesEvaluationMethods.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Traits;\n\nuse Nesk\\Puphpeteer\\Resources\\JSHandle;\nuse Nesk\\Rialto\\Data\\JsFunction;\n\n/**\n * @method null|array|bool|float|int|string querySelectorEval(string $selector, JsFunction $pageFunction, null|array|bool|float|int|JSHandle|string ...$args)\n * @method null|array|bool|float|int|string querySelectorAllEval(string $selector, JsFunction $pageFunction, null|array|bool|float|int|JSHandle|string ...$args)\n */\ntrait AliasesEvaluationMethods\n{\n    public function querySelectorEval(...$arguments)\n    {\n        return $this->__call('$eval', $arguments);\n    }\n\n    public function querySelectorAllEval(...$arguments)\n    {\n        return $this->__call('$$eval', $arguments);\n    }\n}\n"
  },
  {
    "path": "src/Traits/AliasesSelectionMethods.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Traits;\n\nuse Nesk\\Puphpeteer\\Resources\\ElementHandle;\n\n/**\n * @method null|ElementHandle querySelector(string $selector)\n * @method ElementHandle[]    querySelectorAll(string $selector)\n */\ntrait AliasesSelectionMethods\n{\n    public function querySelector(...$arguments)\n    {\n        return $this->__call('$', $arguments);\n    }\n\n    public function querySelectorAll(...$arguments)\n    {\n        return $this->__call('$$', $arguments);\n    }\n}\n"
  },
  {
    "path": "src/doc-generator.ts",
    "content": "import * as ts from 'typescript';\nconst yargs = require('yargs/yargs');\nconst { hideBin } = require('yargs/helpers');\nconst callbackClass = '\\\\Nesk\\\\Rialto\\\\Data\\\\JsFunction';\n\ntype ObjectMemberAsJson = { [key: string]: string; }\n\ntype ObjectMembersAsJson = {\n    properties: ObjectMemberAsJson,\n    getters: ObjectMemberAsJson,\n    methods: ObjectMemberAsJson,\n}\n\ntype ClassAsJson = { name: string } & ObjectMembersAsJson\ntype MemberContext = 'class'|'literal'\ntype TypeContext = 'methodReturn'\n\nclass TypeNotSupportedError extends Error {\n    constructor(message?: string) {\n        super('This type is currently not supported: ' + message);\n    }\n}\n\ninterface SupportChecker {\n    supportsMethodName(methodName: string): boolean;\n}\n\nclass JsSupportChecker {\n    supportsMethodName(methodName: string): boolean {\n        return true;\n    }\n}\n\nclass PhpSupportChecker {\n    supportsMethodName(methodName: string): boolean {\n        return !methodName.includes('$');\n    }\n}\n\ninterface DocumentationFormatter {\n    formatProperty(name: string, type: string, context: MemberContext): string\n    formatGetter(name: string, type: string): string\n    formatAnonymousFunction(parameters: string, returnType: string): string\n    formatFunction(name: string, parameters: string, returnType: string): string\n    formatParameter(name: string, type: string, isVariadic: boolean, isOptional: boolean): string\n    formatTypeAny(): string\n    formatTypeUnknown(): string\n    formatTypeVoid(): string\n    formatTypeUndefined(): string\n    formatTypeNull(): string\n    formatTypeBoolean(): string\n    formatTypeNumber(): string\n    formatTypeString(): string\n    formatTypeReference(type: string): string\n    formatGeneric(parentType: string, argumentTypes: string[], context?: TypeContext): string\n    formatQualifiedName(left: string, right: string): string\n    formatIndexedAccessType(object: string, index: string): string\n    formatLiteralType(value: string): string\n    formatUnion(types: string[]): string\n    formatIntersection(types: string[]): string\n    formatObject(members: string[]): string\n    formatArray(type: string): string\n}\n\nclass JsDocumentationFormatter implements DocumentationFormatter {\n    formatProperty(name: string, type: string, context: MemberContext): string {\n        return `${name}: ${type}`;\n    }\n\n    formatGetter(name: string, type: string): string {\n        return `${name}: ${type}`;\n    }\n\n    formatAnonymousFunction(parameters: string, returnType: string): string {\n        return `(${parameters}) => ${returnType}`;\n    }\n\n    formatFunction(name: string, parameters: string, returnType: string): string {\n        return `${name}(${parameters}): ${returnType}`;\n    }\n\n    formatParameter(name: string, type: string, isVariadic: boolean, isOptional: boolean): string {\n        return `${isVariadic ? '...' : ''}${name}${isOptional ? '?' : ''}: ${type}`;\n    }\n\n    formatTypeAny(): string {\n        return 'any';\n    }\n\n    formatTypeUnknown(): string {\n        return 'unknown';\n    }\n\n    formatTypeVoid(): string {\n        return 'void';\n    }\n\n    formatTypeUndefined(): string {\n        return 'undefined';\n    }\n\n    formatTypeNull(): string {\n        return 'null';\n    }\n\n    formatTypeBoolean(): string {\n        return 'boolean';\n    }\n\n    formatTypeNumber(): string {\n        return 'number';\n    }\n\n    formatTypeString(): string {\n        return 'string';\n    }\n\n    formatTypeReference(type: string): string {\n        return type;\n    }\n\n    formatGeneric(parentType: string, argumentTypes: string[], context?: TypeContext): string {\n        return `${parentType}<${argumentTypes.join(', ')}>`;\n    }\n\n    formatQualifiedName(left: string, right: string): string {\n        return `${left}.${right}`;\n    }\n\n    formatIndexedAccessType(object: string, index: string): string {\n        return `${object}[${index}]`;\n    }\n\n    formatLiteralType(value: string): string {\n        return `'${value}'`;\n    }\n\n    formatUnion(types: string[]): string {\n        return types.join(' | ');\n    }\n\n    formatIntersection(types: string[]): string {\n        return types.join(' & ');\n    }\n\n    formatObject(members: string[]): string {\n        return `{ ${members.join(', ')} }`;\n    }\n\n    formatArray(type: string): string {\n        return `${type}[]`;\n    }\n}\n\nclass PhpDocumentationFormatter implements DocumentationFormatter {\n    static readonly allowedJsClasses = ['Promise', 'Record', 'Map'];\n\n    constructor(\n        protected readonly resourcesNamespace: string,\n        protected readonly resources: string[],\n    ) {}\n\n    formatProperty(name: string, type: string, context: MemberContext): string {\n        return context === 'class'\n            ? `${type} \\$${name}`\n            : `\\$${name}: ${type}`;\n    }\n\n    formatGetter(name: string, type: string): string {\n        return `${type} \\$${name}`;\n    }\n\n    formatAnonymousFunction(parameters: string, returnType: string): string {\n        return callbackClass;\n    }\n\n    formatFunction(name: string, parameters: string, returnType: string): string {\n        return `${returnType} ${name}(${parameters})`;\n    }\n\n    formatParameter(name: string, type: string, isVariadic: boolean, isOptional: boolean): string {\n        if (name === 'this') {\n            name = 'selector'\n        }\n        if (isVariadic && type.endsWith('[]')) {\n            type = type.slice(0, -2);\n        }\n\n        let defaultValue;\n\n        switch (type) {\n            case 'array' :\n                defaultValue = isOptional ? ' = []' : '';\n                break;\n            default:\n                defaultValue = isOptional ? ' = null' : '';\n                break;\n        }\n\n        return `${type} ${isVariadic ? '...' : ''}\\$${name}${defaultValue}`;\n    }\n\n    formatTypeAny(): string {\n        return 'mixed';\n    }\n\n    formatTypeUnknown(): string {\n        return 'mixed';\n    }\n\n    formatTypeVoid(): string {\n        return 'void';\n    }\n\n    formatTypeUndefined(): string {\n        return 'null';\n    }\n\n    formatTypeNull(): string {\n        return 'null';\n    }\n\n    formatTypeBoolean(): string {\n        return 'bool';\n    }\n\n    formatTypeNumber(): string {\n        return 'float';\n    }\n\n    formatTypeString(): string {\n        return 'string';\n    }\n\n    formatTypeReference(type: string): string {\n        // Allow some specific JS classes to be used in phpDoc\n        if (PhpDocumentationFormatter.allowedJsClasses.includes(type)) {\n            return type;\n        }\n\n        // Prefix PHP resources with their namespace\n        if (this.resources.includes(type)) {\n            return this.resourcesNamespace ? `\\\\${this.resourcesNamespace}\\\\${type}` : type;\n        }\n\n        // If the type ends with \"options\" then convert it to an associative array\n        if (/options$/i.test(type)) {\n            return 'array';\n        }\n\n        // Types ending with \"Fn\" are always callables or strings\n        if (type.endsWith('Fn')) {\n            return this.formatUnion([callbackClass, 'string']);\n        }\n\n        if (type === 'Function') {\n            return callbackClass;\n        }\n\n        if (type === 'PuppeteerLifeCycleEvent') {\n            return 'string';\n        }\n\n        if (type === 'Serializable') {\n            return this.formatUnion(['int', 'float', 'string', 'bool', 'null', 'array']);\n        }\n\n        if (type === 'SerializableOrJSHandle') {\n            return this.formatUnion([this.formatTypeReference('Serializable'), this.formatTypeReference('JSHandle')]);\n        }\n\n        if (type === 'HandleType') {\n            return this.formatUnion([this.formatTypeReference('JSHandle'), this.formatTypeReference('ElementHandle')]);\n        }\n\n        return 'mixed';\n    }\n\n    formatGeneric(parentType: string, argumentTypes: string[], context?: TypeContext): string {\n        // Avoid generics with \"mixed\" as parent type\n        if (parentType === 'mixed') {\n            return 'mixed';\n        }\n\n        // Unwrap promises for method return types\n        if (context === 'methodReturn' && parentType === 'Promise' && argumentTypes.length === 1) {\n            return argumentTypes[0];\n        }\n\n        // Transform Record and Map types to associative arrays\n        if (['Record', 'Map'].includes(parentType) && argumentTypes.length === 2) {\n            parentType = 'array';\n        }\n\n        return `${parentType}|${argumentTypes.join('[]|')}[]`;\n    }\n\n    formatQualifiedName(left: string, right: string): string {\n        return `mixed`;\n    }\n\n    formatIndexedAccessType(object: string, index: string): string {\n        return `mixed`;\n    }\n\n    formatLiteralType(value: string): string {\n        return `'${value}'`;\n    }\n\n    private prepareUnionOrIntersectionTypes(types: string[]): string[] {\n        // Replace \"void\" type by \"null\"\n        types = types.map(type => type === 'void' ? 'null' : type)\n\n        // Remove duplicates\n        const uniqueTypes = new Set(types);\n        return Array.from(uniqueTypes.values());\n    }\n\n    formatUnion(types: string[]): string {\n        const result = this.prepareUnionOrIntersectionTypes(types).join('|');\n\n        // Convert enums to string type\n        if (/^('\\w+'\\|)*'\\w+'$/.test(result)) {\n            return 'string';\n        }\n\n        return result;\n    }\n\n    formatIntersection(types: string[]): string {\n        return this.prepareUnionOrIntersectionTypes(types).join('&');\n    }\n\n    formatObject(members: string[]): string {\n        return `array`;\n    }\n\n    formatArray(type: string): string {\n        return `${type}[]`;\n    }\n}\n\nclass PhpStanDocumentationFormatter extends PhpDocumentationFormatter {\n    formatAnonymousFunction(parameters: string, returnType: string): string {\n        return `callable(${parameters}): ${returnType}|` + callbackClass;\n    }\n\n    formatTypeReference(type: string): string {\n        // Allow some specific JS classes to be used in phpDoc\n        if (PhpDocumentationFormatter.allowedJsClasses.includes(type)) {\n            return type;\n        }\n\n        // Prefix PHP resources with their namespace\n        if (this.resources.includes(type)) {\n            return this.resourcesNamespace ? `\\\\${this.resourcesNamespace}\\\\${type}` : type;\n        }\n\n        // If the type ends with \"options\" then convert it to an associative array\n        if (/options$/i.test(type)) {\n            return 'array<string, mixed>';\n        }\n\n        // Types ending with \"Fn\" are always callables or strings\n        if (type.endsWith('Fn')) {\n            return this.formatUnion([callbackClass, 'callable', 'string']);\n        }\n\n        if (type === 'Function') {\n            return this.formatUnion(['callable', callbackClass]);\n        }\n\n        if (type === 'PuppeteerLifeCycleEvent') {\n            return 'string';\n        }\n\n        if (type === 'Serializable') {\n            return this.formatUnion(['int', 'float', 'string', 'bool', 'null', 'array']);\n        }\n\n        if (type === 'SerializableOrJSHandle') {\n            return this.formatUnion([this.formatTypeReference('Serializable'), this.formatTypeReference('JSHandle')]);\n        }\n\n        if (type === 'HandleType') {\n            return this.formatUnion([this.formatTypeReference('JSHandle'), this.formatTypeReference('ElementHandle')]);\n        }\n\n        return 'mixed';\n    }\n\n    formatObject(members: string[]): string {\n        return `array{ ${members.join(', ')} }`;\n    }\n}\n\nclass DocumentationGenerator {\n    constructor(\n        private readonly supportChecker: SupportChecker,\n        private readonly formatter: DocumentationFormatter,\n    ) {}\n\n    private hasModifierForNode(\n        node: ts.Node & { modifiers?: ts.Modifier[] },\n        modifier: ts.KeywordSyntaxKind,\n    ): boolean {\n        if (!node.modifiers) {\n            return false;\n        }\n\n        return node.modifiers.some((node) => node.kind === modifier);\n    }\n\n    private isNodeAccessible(node: ts.Node): boolean {\n        // @ts-ignore\n        if (node.name && (this.getNamedDeclarationAsString(node).startsWith('_') || this.getNamedDeclarationAsString(node).startsWith('#'))) {\n            return false;\n        }\n\n        return (\n            this.hasModifierForNode(node, ts.SyntaxKind.PublicKeyword) ||\n            (!this.hasModifierForNode(node, ts.SyntaxKind.ProtectedKeyword) &&\n            !this.hasModifierForNode(node, ts.SyntaxKind.PrivateKeyword))\n        );\n    }\n\n    private isNodeStatic(node: ts.Node): boolean {\n        return this.hasModifierForNode(node, ts.SyntaxKind.StaticKeyword);\n    }\n\n    public getClassDeclarationAsJson(node: ts.ClassDeclaration): ClassAsJson {\n        return Object.assign(\n            { name: this.getNamedDeclarationAsString(node) },\n            this.getMembersAsJson(node.members, 'class'),\n        );\n    }\n\n    private getMembersAsJson(members: ts.NodeArray<ts.NamedDeclaration>, context: MemberContext): ObjectMembersAsJson {\n        const json: ObjectMembersAsJson = {\n            properties: {},\n            getters: {},\n            methods: {},\n        };\n\n        for (const member of members) {\n            if (!this.isNodeAccessible(member) || this.isNodeStatic(member)) {\n                continue;\n            }\n\n            const name = member.name ? this.getNamedDeclarationAsString(member) : null;\n\n            if (ts.isPropertySignature(member) || ts.isPropertyDeclaration(member)) {\n                json.properties[name] = this.getPropertySignatureOrDeclarationAsString(member, context);\n            } else if (ts.isGetAccessorDeclaration(member)) {\n                json.getters[name] = this.getGetAccessorDeclarationAsString(member);\n            } else if (ts.isMethodDeclaration(member)) {\n                if (!this.supportChecker.supportsMethodName(name)) {\n                    continue;\n                }\n                json.methods[name] = this.getSignatureDeclarationBaseAsString(member);\n            }\n        }\n\n        return json;\n    }\n\n    private getPropertySignatureOrDeclarationAsString(\n        node: ts.PropertySignature | ts.PropertyDeclaration,\n        context: MemberContext\n    ): string {\n        const type = this.getTypeNodeAsString(node.type);\n        const name = this.getNamedDeclarationAsString(node);\n        return this.formatter.formatProperty(name, type, context);\n    }\n\n    private getGetAccessorDeclarationAsString(\n        node: ts.GetAccessorDeclaration\n    ): string {\n        const type = this.getTypeNodeAsString(node.type);\n        const name = this.getNamedDeclarationAsString(node);\n        return this.formatter.formatGetter(name, type);\n    }\n\n    private getSignatureDeclarationBaseAsString(\n        node: ts.SignatureDeclarationBase\n    ): string {\n        const name = node.name && this.getNamedDeclarationAsString(node);\n        const parameters = node.parameters\n            .map(parameter => this.getParameterDeclarationAsString(parameter))\n            .join(', ');\n\n        const returnType = this.getTypeNodeAsString(node.type, name ? 'methodReturn' : undefined);\n\n        return name\n            ? this.formatter.formatFunction(name, parameters, returnType)\n            : this.formatter.formatAnonymousFunction(parameters, returnType);\n    }\n\n    private getEmptyFunctionSignatureAsString(\n        node: ts.ParenthesizedTypeNode\n    ): string {\n        return this.formatter.formatAnonymousFunction(this.getTypeNodeAsString(node.type), '');\n    }\n\n    private getParameterDeclarationAsString(node: ts.ParameterDeclaration): string {\n        const name = this.getNamedDeclarationAsString(node);\n        let type = this.getTypeNodeAsString(node.type);\n        const isVariadic = node.dotDotDotToken !== undefined;\n        const isOptional = node.questionToken !== undefined;\n\n        //fix missing argument type in evaluate* methods.\n        if (name.includes('Function') && type.includes('mixed')) {\n            type = this.formatter.formatTypeReference('Function');\n        }\n\n        return this.formatter.formatParameter(name, type, isVariadic, isOptional);\n    }\n\n    private getTypeNodeAsString(node: ts.TypeNode, context?: TypeContext): string {\n        if (!node) {\n            return '';\n        }\n        if (node.kind === ts.SyntaxKind.AnyKeyword) {\n            return this.formatter.formatTypeAny();\n        } else if (node.kind === ts.SyntaxKind.UnknownKeyword) {\n            return this.formatter.formatTypeUnknown();\n        } else if (node.kind === ts.SyntaxKind.VoidKeyword) {\n            return this.formatter.formatTypeVoid();\n        } else if (node.kind === ts.SyntaxKind.UndefinedKeyword) {\n            return this.formatter.formatTypeUndefined();\n        } else if (node.kind === ts.SyntaxKind.NullKeyword) {\n            return this.formatter.formatTypeNull();\n        } else if (node.kind === ts.SyntaxKind.BooleanKeyword) {\n            return this.formatter.formatTypeBoolean();\n        } else if (node.kind === ts.SyntaxKind.NumberKeyword) {\n            return this.formatter.formatTypeNumber();\n        } else if (node.kind === ts.SyntaxKind.StringKeyword) {\n            return this.formatter.formatTypeString();\n        } else if (ts.isTypeReferenceNode(node)) {\n            return this.getTypeReferenceNodeAsString(node, context);\n        } else if (ts.isIndexedAccessTypeNode(node)) {\n            return this.getIndexedAccessTypeNodeAsString(node);\n        } else if (ts.isLiteralTypeNode(node)) {\n            return this.getLiteralTypeNodeAsString(node);\n        } else if (ts.isUnionTypeNode(node)) {\n            return this.getUnionTypeNodeAsString(node, context);\n        } else if (ts.isIntersectionTypeNode(node)) {\n            return this.getIntersectionTypeNodeAsString(node, context);\n        } else if (ts.isTypeLiteralNode(node)) {\n            return this.getTypeLiteralNodeAsString(node);\n        } else if (ts.isArrayTypeNode(node)) {\n            return this.getArrayTypeNodeAsString(node, context);\n        } else if (ts.isFunctionTypeNode(node)) {\n            return this.getSignatureDeclarationBaseAsString(node);\n        } else if (ts.isParenthesizedTypeNode(node)) {\n            return this.getEmptyFunctionSignatureAsString(node);\n        } else {\n            console.error('Unknown type: ' + ts.SyntaxKind[node.kind])\n            return this.formatter.formatTypeAny();\n        }\n    }\n\n    private getTypeReferenceNodeAsString(node: ts.TypeReferenceNode, context?: TypeContext): string {\n        return this.getGenericTypeReferenceNodeAsString(node, context) || this.getSimpleTypeReferenceNodeAsString(node);\n    }\n\n    private getGenericTypeReferenceNodeAsString(node: ts.TypeReferenceNode, context?: TypeContext): string | null {\n        if (!node.typeArguments || node.typeArguments.length === 0) {\n            return null;\n        }\n\n        const parentType = this.getSimpleTypeReferenceNodeAsString(node);\n        const argumentTypes = node.typeArguments.map((node) => this.getTypeNodeAsString(node));\n        return this.formatter.formatGeneric(parentType, argumentTypes, context);\n    }\n\n    private getSimpleTypeReferenceNodeAsString(node: ts.TypeReferenceNode): string {\n        return ts.isIdentifier(node.typeName)\n            ? this.formatter.formatTypeReference(this.getIdentifierAsString(node.typeName))\n            : this.getQualifiedNameAsString(node.typeName);\n    }\n\n    private getQualifiedNameAsString(node: ts.QualifiedName): string {\n        const right = this.getIdentifierAsString(node.right);\n        const left = ts.isIdentifier(node.left)\n            ? this.getIdentifierAsString(node.left)\n            : this.getQualifiedNameAsString(node.left);\n\n        return this.formatter.formatQualifiedName(left, right);\n    }\n\n    private getIndexedAccessTypeNodeAsString(\n        node: ts.IndexedAccessTypeNode\n    ): string {\n        const object = this.getTypeNodeAsString(node.objectType);\n        const index = this.getTypeNodeAsString(node.indexType);\n        return this.formatter.formatIndexedAccessType(object, index);\n    }\n\n    private getLiteralTypeNodeAsString(node: ts.LiteralTypeNode): string {\n        if (node.literal.kind === ts.SyntaxKind.NullKeyword) {\n            return this.formatter.formatTypeNull();\n        } else if (node.literal.kind === ts.SyntaxKind.BooleanKeyword) {\n            return this.formatter.formatTypeBoolean();\n        } else if (ts.isLiteralExpression(node.literal)) {\n            return this.formatter.formatLiteralType(node.literal.text);\n        }\n        throw new TypeNotSupportedError();\n    }\n\n    private getUnionTypeNodeAsString(node: ts.UnionTypeNode, context?: TypeContext): string {\n        const types = node.types.map(typeNode => this.getTypeNodeAsString(typeNode, context));\n        return this.formatter.formatUnion(types);\n    }\n\n    private getIntersectionTypeNodeAsString(node: ts.IntersectionTypeNode, context?: TypeContext): string {\n        const types = node.types.map(typeNode => this.getTypeNodeAsString(typeNode, context));\n        return this.formatter.formatIntersection(types);\n    }\n\n    private getTypeLiteralNodeAsString(node: ts.TypeLiteralNode): string {\n        const members = this.getMembersAsJson(node.members, 'literal');\n        const stringMembers = Object.values(members).map(Object.values);\n        const flattenMembers = stringMembers.reduce((acc, val) => acc.concat(val), []);\n        return this.formatter.formatObject(flattenMembers);\n    }\n\n    private getArrayTypeNodeAsString(node: ts.ArrayTypeNode, context?: TypeContext): string {\n        const type = this.getTypeNodeAsString(node.elementType, context);\n        return this.formatter.formatArray(type);\n    }\n\n    private getNamedDeclarationAsString(node: ts.NamedDeclaration): string {\n        if (!ts.isIdentifier(node.name) && !ts.isPrivateIdentifier(node.name)) {\n            console.warn('Unknown type: ' + ts.SyntaxKind[node.kind]);\n            return '#';\n        }\n        return this.getIdentifierAsString(node.name);\n    }\n\n    private getIdentifierAsString(node: ts.Identifier|ts.PrivateIdentifier): string {\n        return String(node.escapedText);\n    }\n}\n\nconst { argv } = yargs(hideBin(process.argv))\n    .command('$0 <language> <definition-files...>')\n    .option('resources-namespace', { type: 'string', default: '' })\n    .option('resources', { type: 'array', default: [] })\n    .option('pretty', { type: 'boolean', default: false })\n\nlet supportChecker, formatter;\nswitch (argv.language.toUpperCase()) {\n    case 'JS':\n        supportChecker = new JsSupportChecker();\n        formatter = new JsDocumentationFormatter();\n        break;\n    case 'PHP':\n        supportChecker = new PhpSupportChecker();\n        formatter = new PhpDocumentationFormatter(argv.resourcesNamespace, argv.resources);\n        break;\n    case 'PHPSTAN':\n        supportChecker = new PhpSupportChecker();\n        formatter = new PhpStanDocumentationFormatter(argv.resourcesNamespace, argv.resources);\n        break;\n    default:\n        console.error(`Unsupported \"${argv.language}\" language.`);\n        process.exit(1);\n}\n\nconst docGenerator = new DocumentationGenerator(supportChecker, formatter);\nconst program = ts.createProgram(argv.definitionFiles, {});\nconst classes = {};\n\nfor (const fileName of argv.definitionFiles) {\n    const sourceFile = program.getSourceFile(fileName);\n\n    ts.forEachChild(sourceFile, node => {\n        if (ts.isClassDeclaration(node)) {\n            const classAsJson = docGenerator.getClassDeclarationAsJson(node);\n            classes[classAsJson.name] = classAsJson;\n        }\n    });\n}\n\nprocess.stdout.write(JSON.stringify(classes, null, argv.pretty ? 2 : null));\n"
  },
  {
    "path": "src/get-puppeteer-version.js",
    "content": "'use strict';\n\nfunction output(value) {\n    process.stdout.write(JSON.stringify(value));\n}\n\ntry {\n    const manifest = require('puppeteer/package.json');\n    output(manifest.version);\n} catch (exception) {\n    output(null);\n}\n"
  },
  {
    "path": "tests/DownloadTest.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Tests;\n\nuse Nesk\\Puphpeteer\\Puppeteer;\nuse Nesk\\Rialto\\Data\\JsFunction;\nuse PHPUnit\\Framework\\ExpectationFailedException;\nuse Nesk\\Puphpeteer\\Resources\\ElementHandle;\n\nclass DownloadTest extends TestCase\n{\n    public function setUp(): void\n    {\n        parent::setUp();\n\n        // Serve the content of the \"/resources\"-folder to test these.\n        $this->serveResources();\n\n        // Launch the browser to run tests on.\n        $this->launchBrowser();\n    }\n\n    /**\n     * Downloads an image and checks string length.\n     *\n     */\n    public function testDownloadImage()\n    {\n        // Download the image\n        $page = $this->browser\n            ->newPage()\n            ->goto($this->url . '/puphpeteer-logo.png');\n\n        $base64 = $page->buffer()->toString('base64');\n        $imageString = base64_decode($base64);\n\n        // Get the reference image from resources\n        $reference = file_get_contents('tests/resources/puphpeteer-logo.png');\n\n        $this->assertTrue(\n            mb_strlen($reference) === mb_strlen($imageString),\n            'Image is not the same length after download.'\n        );\n    }\n\n    public function testDownloadPdf()\n    {\n        $page = $this->browser->newPage();\n        $page->goto($this->url);\n\n        $base64 = $page->pdf()->toString('base64');\n        $pdfBytes = base64_decode($base64);\n        $reference = file_get_contents('tests/resources/example.pdf');\n\n        $this->assertTrue(\n            mb_strlen($reference) === mb_strlen($pdfBytes),\n            'Pdf of the main page is not the same length after generation.'\n        );\n    }\n\n    /**\n     * Downloads an image and checks string length.\n     *\n     * @test\n     */\n    // public function download_large_image()\n    // {\n    //     // Download the image\n    //     $page = $this->browser\n    //         ->newPage()\n    //         ->goto($this->url . '/denys-barabanov-jKcFmXCfaQ8-unsplash.jpg');\n\n    //     $base64 = $page->buffer()->toString('base64');\n    //     $imageString = base64_decode($base64);\n\n    //     // Get the reference image from resources\n    //     $reference = file_get_contents('tests/resources/denys-barabanov-jKcFmXCfaQ8-unsplash.jpg');\n\n    //     $this->assertTrue(\n    //         mb_strlen($reference) === mb_strlen($imageString),\n    //         'Large image is not the same length after download.'\n    //     );\n    // }\n}\n"
  },
  {
    "path": "tests/PuphpeteerTest.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Tests;\n\nuse Nesk\\Puphpeteer\\Puppeteer;\nuse Nesk\\Puphpeteer\\Resources\\Frame;\nuse Nesk\\Puphpeteer\\Resources\\Page;\nuse Nesk\\Rialto\\Data\\JsFunction;\nuse PHPUnit\\Framework\\ExpectationFailedException;\nuse Nesk\\Puphpeteer\\Resources\\ElementHandle;\nuse Psr\\Log\\LoggerInterface;\n\nclass PuphpeteerTest extends TestCase\n{\n    public function setUp(): void\n    {\n        parent::setUp();\n\n        // Serve the content of the \"/resources\"-folder to test these.\n        $this->serveResources();\n\n        // Launch the browser to run tests on.\n        $this->launchBrowser();\n    }\n\n    /** @test */\n    public function can_browse_website()\n    {\n        $response = $this->browser->newPage()->goto($this->url);\n\n        $this->assertTrue($response->ok(), 'Failed asserting that the response is successful.');\n    }\n\n    /**\n     * @test\n     */\n    public function can_use_method_aliases()\n    {\n        $page = $this->browser->newPage();\n\n        $page->goto($this->url);\n\n        $select = function(Page | Frame | ElementHandle $resource) {\n            $elements = [\n                $resource->querySelector('h1'),\n                $resource->querySelectorAll('h1')[0],\n                $resource->querySelectorAll('::-p-xpath(/html/body/h1)')[0],\n            ];\n\n            $this->assertContainsOnlyInstancesOf(ElementHandle::class, $elements);\n        };\n\n        $evaluate = function(Page | Frame | ElementHandle $resource) {\n            $strings = [\n                $resource->querySelectorEval('h1', JsFunction::createWithBody('return \"Hello World!\";')),\n                $resource->querySelectorAllEval('h1', JsFunction::createWithBody('return \"Hello World!\";')),\n            ];\n\n            foreach ($strings as $string) {\n                $this->assertEquals('Hello World!', $string);\n            }\n        };\n\n        // Test method aliases for Page, Frame and ElementHandle classes\n        $resources = [$page, $page->mainFrame(), $page->querySelector('body')];\n        foreach ($resources as $resource) {\n            $select($resource);\n            $evaluate($resource);\n        }\n    }\n\n    /** @test */\n    public function can_evaluate_a_selection()\n    {\n        $page = $this->browser->newPage();\n\n        $page->goto($this->url);\n\n        $title = $page->querySelectorEval('h1', JsFunction::createWithParameters(['node'])\n            ->body('return node.textContent;'));\n\n        $titleCount = $page->querySelectorAllEval('h1', JsFunction::createWithParameters(['nodes'])\n            ->body('return nodes.length;'));\n\n        $this->assertEquals('Example Page', $title);\n        $this->assertEquals(1, $titleCount);\n    }\n\n    /** @test */\n    public function can_intercept_requests()\n    {\n        $page = $this->browser->newPage();\n\n        $page->setRequestInterception(true);\n\n        $page->on('request', JsFunction::createWithParameters(['request'])\n            ->body('request.resourceType() === \"stylesheet\" ? request.abort() : request.continue()'));\n\n        $page->goto($this->url);\n\n        $backgroundColor = $page->querySelectorEval('h1', JsFunction::createWithParameters(['node'])\n            ->body('return getComputedStyle(node).textTransform'));\n\n        $this->assertNotEquals('lowercase', $backgroundColor);\n    }\n\n    /**\n     * @test\n     * @dataProvider resourceProvider\n     * @dontPopulateProperties browser\n     */\n    public function check_all_resources_are_supported(string $name)\n    {\n        $incompleteTest = false;\n        $resourceInstantiator = new ResourceInstantiator($this->browserOptions, $this->url);\n        $resource = $resourceInstantiator->{$name}(new Puppeteer, $this->browserOptions);\n\n        if ($resource instanceof UntestableResource) {\n            $incompleteTest = true;\n        } else if ($resource instanceof RiskyResource) {\n            if (!empty($resource->exception())) {\n                $incompleteTest = true;\n            } else {\n                try {\n                    $this->assertInstanceOf(\"Nesk\\\\Puphpeteer\\\\Resources\\\\$name\", $resource->value());\n                } catch (ExpectationFailedException $exception) {\n                    $incompleteTest = true;\n                }\n            }\n        } else {\n            $this->assertInstanceOf(\"\\\\Nesk\\\\Puphpeteer\\\\Resources\\\\$name\", $resource, json_encode($resource));\n        }\n\n        if (!$incompleteTest) return;\n\n        $reason = \"The \\\"$name\\\" resource has not been tested properly, probably\"\n            .\" for a good reason but you might want to have a look: \\n\\n    \";\n\n        if ($resource instanceof UntestableResource) {\n            $reason .= \"\\e[33mMarked as untestable.\\e[0m\";\n        } else {\n            if (!empty($exception = $resource->exception())) {\n                $reason .= \"\\e[31mMarked as risky because of a Node error: {$exception->getMessage()}\\e[0m\";\n            } else {\n                $value = print_r($resource->value(), true);\n                $reason .= \"\\e[31mMarked as risky because of an unexpected value: $value\\e[0m\";\n            }\n        }\n\n        $this->markTestIncomplete($reason);\n    }\n\n    public static function resourceProvider(): \\Generator\n    {\n        $resourceNames = (new ResourceInstantiator([], ''))->getResourceNames();\n\n        foreach ($resourceNames as $name) {\n            yield [$name];\n        }\n    }\n\n    private function createBrowserLogger(callable $onBrowserLog): LoggerInterface\n    {\n        $logger = $this->createMock(LoggerInterface::class);\n        $logger->expects(self::atLeastOnce())\n            ->method('log')\n            ->willReturnCallback(function (string $level, string $message) use ($onBrowserLog) {\n                if (\\strpos($message, \"Received a Browser log:\") === 0) {\n                    $onBrowserLog();\n                }\n\n                return null;\n            });\n\n        return $logger;\n    }\n\n    /**\n     * @test\n     * @dontPopulateProperties browser\n     */\n    public function browser_console_calls_are_logged_if_enabled()\n    {\n        $browserLogOccured = false;\n        $logger = $this->createBrowserLogger(function () use (&$browserLogOccured) {\n            $browserLogOccured = true;\n        });\n\n        $puppeteer = new Puppeteer([\n            'log_browser_console' => true,\n            'logger' => $logger,\n        ]);\n\n        $this->browser = $puppeteer->launch($this->browserOptions);\n        $this->browser->pages()[0]->goto($this->url);\n\n        static::assertTrue($browserLogOccured);\n    }\n\n    /**\n     * @test\n     * @dontPopulateProperties browser\n     */\n    public function browser_console_calls_are_not_logged_if_disabled()\n    {\n        $browserLogOccured = false;\n        $logger = $this->createBrowserLogger(function () use (&$browserLogOccured) {\n            $browserLogOccured = true;\n        });\n\n        $puppeteer = new Puppeteer([\n            'log_browser_console' => false,\n            'logger' => $logger,\n        ]);\n\n        $this->browser = $puppeteer->launch($this->browserOptions);\n        $this->browser->pages()[0]->goto($this->url);\n\n        static::assertFalse($browserLogOccured);\n    }\n}\n"
  },
  {
    "path": "tests/ResourceInstantiator.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Tests;\n\nuse Nesk\\Puphpeteer\\Puppeteer;\nuse Nesk\\Puphpeteer\\Resources\\Browser;\nuse Nesk\\Puphpeteer\\Resources\\Frame;\nuse Nesk\\Puphpeteer\\Resources\\HTTPResponse;\nuse Nesk\\Puphpeteer\\Resources\\Page;\nuse Nesk\\Rialto\\Data\\JsFunction;\n\n/**\n * @method Page Page(Puppeteer $puppeteer)\n * @method Browser Browser(Puppeteer $puppeteer)\n * @method Frame Frame(Puppeteer $puppeteer)\n * @method HTTPResponse HttpResponse(Puppeteer $puppeteer)\n */\nclass ResourceInstantiator\n{\n    protected $resources = [];\n\n    public function __construct(\n        public array $browserOptions,\n        public string $url\n     ) {\n\n        $this->resources = [\n            'Accessibility' => function (Puppeteer $puppeteer) {\n                return $this->Page($puppeteer)->accessibility;\n            },\n            'Browser' => function (Puppeteer $puppeteer): Browser {\n                return $puppeteer->launch($this->browserOptions);\n            },\n            'BrowserContext' => function (Puppeteer $puppeteer) {\n                return $this->Browser($puppeteer)->createBrowserContext();\n            },\n            'CDPSession' => function (Puppeteer $puppeteer) {\n                return $this->Target($puppeteer)->createCDPSession();\n            },\n            'ConsoleMessage' => function () {\n                return new UntestableResource;\n            },\n            'Coverage' => function ($puppeteer) {\n                return $this->Page($puppeteer)->coverage;\n            },\n            'Dialog' => function () {\n                return new UntestableResource;\n            },\n            'ElementHandle' => function ($puppeteer) {\n                return $this->Page($puppeteer)->querySelector('body');\n            },\n            'EventEmitter' => function (Puppeteer $puppeteer) {\n                return $puppeteer->launch($this->browserOptions);\n            },\n            'FileChooser' => function () {\n                return new UntestableResource;\n            },\n            'Frame' => function (Puppeteer $puppeteer) {\n                return $this->Page($puppeteer)->mainFrame();\n            },\n            'HTTPRequest' => function (Puppeteer $puppeteer) {\n                return $this->HTTPResponse($puppeteer)->request();\n            },\n            'HTTPResponse' => function (Puppeteer $puppeteer): HTTPResponse {\n                return $this->Page($puppeteer)->goto($this->url);\n            },\n            'JSHandle' => function (Puppeteer $puppeteer) {\n                return $this->Page($puppeteer)->evaluateHandle(JsFunction::createWithBody('window'));\n            },\n            'Keyboard' => function (Puppeteer $puppeteer) {\n                return $this->Page($puppeteer)->keyboard;\n            },\n            'Mouse' => function (Puppeteer $puppeteer) {\n                return $this->Page($puppeteer)->mouse;\n            },\n            'Page' => function (Puppeteer $puppeteer): Page  {\n                return $this->Browser($puppeteer)->newPage();\n            },\n            'SecurityDetails' => function (Puppeteer $puppeteer) {\n                return new RiskyResource(function () use ($puppeteer) {\n                    return $this->Page($puppeteer)->goto('https://example.com')->securityDetails();\n                });\n            },\n            'Target' => function (Puppeteer $puppeteer) {\n                return $this->Page($puppeteer)->target();\n            },\n            'TimeoutError' => function () {\n                return new UntestableResource;\n            },\n            'Touchscreen' => function (Puppeteer $puppeteer) {\n                return $this->Page($puppeteer)->touchscreen;\n            },\n            'Tracing' => function (Puppeteer $puppeteer) {\n                return $this->Page($puppeteer)->tracing;\n            },\n            'WebWorker' => function (Puppeteer $puppeteer) {\n                $page = $this->Page($puppeteer);\n                $page->goto($this->url, ['waitUntil' => 'networkidle0']);\n                return $page->workers()[0];\n            },\n        ];\n    }\n\n    public function getResourceNames(): array\n    {\n        return array_keys($this->resources);\n    }\n\n    public function __call(string $name, array $arguments)\n    {\n        if (!isset($this->resources[$name])) {\n            throw new \\InvalidArgumentException(\"The $name resource is not supported.\");\n        }\n\n        return $this->resources[$name](...$arguments);\n    }\n}\n"
  },
  {
    "path": "tests/RiskyResource.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Tests;\n\nuse Nesk\\Rialto\\Exceptions\\Node\\FatalException as NodeFatalException;\n\nclass RiskyResource\n{\n    protected $value = null;\n    protected $exception = null;\n\n    public function __construct(callable $resourceRetriever) {\n        try {\n            $this->value = $resourceRetriever();\n        } catch (NodeFatalException $exception) {\n            $this->exception = $exception;\n        }\n    }\n\n    public function value() {\n        return $this->value;\n    }\n\n    public function exception(): ?NodeFatalException {\n        return $this->exception;\n    }\n}\n"
  },
  {
    "path": "tests/TestCase.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Tests;\n\nuse Nesk\\Puphpeteer\\Puppeteer;\nuse Monolog\\Logger;\nuse Nesk\\Puphpeteer\\Resources\\Browser;\nuse ReflectionClass;\nuse Psr\\Log\\LogLevel;\nuse Symfony\\Component\\Process\\Process;\nuse PHPUnit\\Framework\\Constraint\\Callback;\nuse PHPUnit\\Framework\\TestCase as BaseTestCase;\n\nclass TestCase extends BaseTestCase\n{\n    private $dontPopulateProperties = [];\n    protected string $host;\n    protected array $browserOptions;\n    protected string $url;\n    protected string $serverDir;\n    protected Process $servingProcess;\n    protected Browser $browser;\n\n    public function setUp(): void\n    {\n        parent::setUp();\n\n        $methodName = explode(' ', $this->name())[0] ?? '';\n        $testMethod = new \\ReflectionMethod($this, $methodName);\n        $docComment = $testMethod->getDocComment();\n\n        if (preg_match('/@dontPopulateProperties (.*)/', $docComment, $matches)) {\n            $this->dontPopulateProperties = array_values(array_filter(explode(' ', $matches[1])));\n        }\n    }\n\n    /**\n     * Stops the browser and local server\n     */\n    public function tearDown(): void\n    {\n        // Close the browser.\n        if (isset($this->browser)) {\n            $this->browser->close();\n        }\n\n        // Shutdown the local server\n        if (isset($this->servingProcess)) {\n            $this->servingProcess->stop(0);\n        }\n    }\n\n    /**\n     * Serves the resources folder locally on port 8089\n     */\n    protected function serveResources(): void\n    {\n        // Spin up a local server to deliver the resources.\n        $this->host = '127.0.0.1:8089';\n        $this->url = \"http://{$this->host}\";\n        $this->serverDir = __DIR__.'/resources';\n\n        $this->servingProcess = new Process(['php', '-S', $this->host, '-t', $this->serverDir]);\n        $this->servingProcess->start();\n    }\n\n    /**\n     * Launches the PuPHPeteer-controlled browser\n     */\n    protected function launchBrowser(): void\n    {\n        /**\n         * Chrome doesn't support Linux sandbox on many CI environments\n         *\n         * @see: https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md#chrome-headless-fails-due-to-sandbox-issues\n         */\n        $this->browserOptions = [\n            'args' => ['--no-sandbox', '--disable-setuid-sandbox'],\n            'headless' => true,\n        ];\n\n        if ($this->canPopulateProperty('browser')) {\n            $this->browser = (new Puppeteer)->launch($this->browserOptions);\n        }\n    }\n\n    public function canPopulateProperty(string $propertyName): bool\n    {\n        return !in_array($propertyName, $this->dontPopulateProperties);\n    }\n\n    public function isLogLevel(): Callback {\n        $psrLogLevels = (new ReflectionClass(LogLevel::class))->getConstants();\n        $monologLevels = (new ReflectionClass(Logger::class))->getConstants();\n        $monologLevels = array_intersect_key($monologLevels, $psrLogLevels);\n\n        return $this->callback(function ($level) use ($psrLogLevels, $monologLevels) {\n            if (is_string($level)) {\n                return in_array($level, $psrLogLevels, true);\n            } else if (is_int($level)) {\n                return in_array($level, $monologLevels, true);\n            }\n\n            return false;\n        });\n    }\n}\n"
  },
  {
    "path": "tests/UntestableResource.php",
    "content": "<?php\n\nnamespace Nesk\\Puphpeteer\\Tests;\n\nclass UntestableResource\n{\n    //\n}\n"
  },
  {
    "path": "tests/resources/index.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <title>Document</title>\n\n        <link rel=\"stylesheet\" href=\"./stylesheet.css\">\n    </head>\n\n    <body>\n        <h1>Example Page</h1>\n\n        <script>\n            console.log('Starting worker...');\n            new Worker('worker.js');\n            console.log('Worker started!');\n        </script>\n    </body>\n</html>\n"
  },
  {
    "path": "tests/resources/stylesheet.css",
    "content": "h1 {\n    text-transform: lowercase;\n}\n"
  },
  {
    "path": "tests/resources/worker.js",
    "content": "// There's nothing to do, just wait.\n"
  }
]