{"id":14155187,"url":"https://github.com/lino-levan/astral","last_synced_at":"2025-04-12T04:51:19.977Z","repository":{"id":188180294,"uuid":"670803275","full_name":"lino-levan/astral","owner":"lino-levan","description":"A high-level puppeteer/playwright-like library for Deno","archived":false,"fork":false,"pushed_at":"2025-02-01T17:44:33.000Z","size":7352,"stargazers_count":303,"open_issues_count":36,"forks_count":23,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-04-03T07:11:52.902Z","etag":null,"topics":["astral","deno","playwright","puppeteer"],"latest_commit_sha":null,"homepage":"https://jsr.io/@astral/astral","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lino-levan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-07-25T21:56:07.000Z","updated_at":"2025-04-01T11:54:19.000Z","dependencies_parsed_at":null,"dependency_job_id":"e50a8f70-e0a8-4579-a90e-7f237c7c86f3","html_url":"https://github.com/lino-levan/astral","commit_stats":{"total_commits":182,"total_committers":9,"mean_commits":20.22222222222222,"dds":0.3571428571428571,"last_synced_commit":"7e91588e342561a2e5a3b515e7b3cedf6683d02b"},"previous_names":["lino-levan/astral"],"tags_count":28,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lino-levan%2Fastral","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lino-levan%2Fastral/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lino-levan%2Fastral/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lino-levan%2Fastral/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lino-levan","download_url":"https://codeload.github.com/lino-levan/astral/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248519473,"owners_count":21117757,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["astral","deno","playwright","puppeteer"],"created_at":"2024-08-17T08:02:25.561Z","updated_at":"2025-04-12T04:51:19.953Z","avatar_url":"https://github.com/lino-levan.png","language":"TypeScript","funding_links":[],"categories":["deno","TypeScript"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n    \u003cimg src=\"./docs/static/astral.png\"/\u003e\n\u003c/p\u003e\n\nAstral is a high-level puppeteer/playwright-like library that allows for control\nover a web browser (primarily for automation and testing). It is written from\nscratch with Deno in mind.\n\n## Usage\n\nTake a screenshot of a website.\n\n```ts\n// Import Astral\nimport { launch } from \"jsr:@astral/astral\";\n\n// Launch the browser\nconst browser = await launch();\n\n// Open a new page\nconst page = await browser.newPage(\"https://deno.land\");\n\n// Take a screenshot of the page and save that to disk\nconst screenshot = await page.screenshot();\nDeno.writeFileSync(\"screenshot.png\", screenshot);\n\n// Close the browser\nawait browser.close();\n```\n\nYou can use the evaluate function to run code in the context of the browser.\n\n```ts\n// Import Astral\nimport { launch } from \"jsr:@astral/astral\";\n\n// Launch the browser\nconst browser = await launch();\n\n// Open a new page\nconst page = await browser.newPage(\"https://deno.land\");\n\n// Run code in the context of the browser\nconst value = await page.evaluate(() =\u003e {\n  return document.body.innerHTML;\n});\nconsole.log(value);\n\n// Run code with args\nconst result = await page.evaluate((x, y) =\u003e {\n  return `The result of adding ${x}+${y} = ${x + y}`;\n}, {\n  args: [10, 15],\n});\nconsole.log(result);\n\n// Close the browser\nawait browser.close();\n```\n\nYou can navigate to a page and interact with it.\n\n```ts\n// Import Astral\nimport { launch } from \"jsr:@astral/astral\";\n\n// Launch browser in headfull mode\nconst browser = await launch({ headless: false });\n\n// Open the webpage\nconst page = await browser.newPage(\"https://deno.land\");\n\n// Click the search button\nconst button = await page.$(\"button\");\nawait button!.click();\n\n// Type in the search input\nconst input = await page.$(\"#search-input\");\nawait input!.type(\"pyro\", { delay: 1000 });\n\n// Wait for the search results to come back\nawait page.waitForNetworkIdle({ idleConnections: 0, idleTime: 1000 });\n\n// Click the 'pyro' link\nconst xLink = await page.$(\"a.justify-between:nth-child(1)\");\nawait Promise.all([\n  page.waitForNavigation(),\n  xLink!.click(),\n]);\n\n// Click the link to 'pyro.deno.dev'\nconst dLink = await page.$(\n  \".markdown-body \u003e p:nth-child(8) \u003e a:nth-child(1)\",\n);\nawait Promise.all([\n  page.waitForNavigation(),\n  dLink!.click(),\n]);\n\n// Close browser\nawait browser.close();\n```\n\nTODO: Document the locator API.\n\n## Advanced Usage\n\nIf you already have a browser process running somewhere else or you're using a\nservice that provides remote browsers for automation (such as\n[browserless.io](https://www.browserless.io/)), it is possible to directly\nconnect to its endpoint rather than spawning a new process.\n\n```ts\n// Import Astral\nimport { connect } from \"jsr:@astral/astral\";\n\n// Connect to remote endpoint\nconst browser = await connect({\n  wsEndpoint: \"wss://remote-browser-endpoint.example.com\",\n});\n\n// Do stuff\nconst page = await browser.newPage(\"http://example.com\");\nconsole.log(await page.evaluate(() =\u003e document.title));\n\n// Close connection\nawait browser.close();\n```\n\nIf you'd like to instead re-use a browser that you already launched, astral\nexposes the WebSocket endpoint through `browser.wsEndpoint()`.\n\n```ts\n// Spawn a browser process\nconst browser = await launch();\n\n// Connect to first browser instead\nconst anotherBrowser = await connect({ wsEndpoint: browser.wsEndpoint() });\n```\n\n### Page authenticate\n\n[authenticate example code](https://github.com/lino-levan/astral/blob/main/examples/authenticate.ts):\n\n```ts\n// Open a new page\nconst page = await browser.newPage();\n\n// Provide credentials for HTTP authentication.\nconst url = \"https://postman-echo.com/basic-auth\";\nawait page.authenticate({ username: \"postman\", password: \"password\" });\nawait page.goto(url, { waitUntil: \"networkidle2\" });\n```\n\n## BYOB - Bring Your Own Browser\n\nEssentially the process is as simple as running a chromium-like binary with the\nfollowing flags:\n\n```\nchromium --remote-debugging-port=1337 \\\n--headless=new \\\n--no-first-run \\\n--password-store=basic \\\n--use-mock-keychain \\\n--hide-scrollbars\n```\n\nTechnically, only the first flag is necessary, though I've found that these\nflags generally get the best result. Once your browser process is running,\nconnecting to it is as simple as\n\n```typescript\n// Import Astral\nimport { connect } from \"jsr:@astral/astral\";\n\n// Connect to remote endpoint\nconst browser = await connect({\n  wsEndpoint: \"\u003cWS-ENDPOINT\u003e\",\n  headless: false,\n});\n\nconsole.log(browser.wsEndpoint());\n\n// Do stuff\nconst page = await browser.newPage(\"http://example.com\");\nconsole.log(await page.evaluate(() =\u003e document.title));\n\n// Close connection\nawait browser.close();\n```\n\n## FAQ\n\n### Launch FAQ\n\n#### \"No usable sandbox!\" with user namespace cloning enabled\n\n\u003e Ubuntu 23.10+ (or possibly other Linux distros in the future) ship an AppArmor\n\u003e profile that applies to Chrome stable binaries installed at\n\u003e /opt/google/chrome/chrome (the default installation path). This policy is\n\u003e stored at /etc/apparmor.d/chrome. This AppArmor policy prevents Chrome for\n\u003e Testing binaries downloaded by Puppeteer from using user namespaces resulting\n\u003e in the No usable sandbox! error when trying to launch the browser. For\n\u003e workarounds, see\n\u003e https://chromium.googlesource.com/chromium/src/+/main/docs/security/apparmor-userns-restrictions.md\n\nThe following command removes AppArmor restrictions on user namespaces, allowing\nPuppeteer to launch Chrome without the \"No usable sandbox!\" error (see\n[puppeteer#13196](https://github.com/puppeteer/puppeteer/pull/13196)):\n\n    echo 0 | sudo tee /proc/sys/kernel/apparmor_restrict_unprivileged_userns\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flino-levan%2Fastral","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flino-levan%2Fastral","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flino-levan%2Fastral/lists"}