https://github.com/lino-levan/astral
A high-level puppeteer/playwright-like library for Deno
https://github.com/lino-levan/astral
astral deno playwright puppeteer
Last synced: 13 days ago
JSON representation
A high-level puppeteer/playwright-like library for Deno
- Host: GitHub
- URL: https://github.com/lino-levan/astral
- Owner: lino-levan
- License: mit
- Created: 2023-07-25T21:56:07.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2025-02-01T17:44:33.000Z (3 months ago)
- Last Synced: 2025-04-03T07:11:52.902Z (22 days ago)
- Topics: astral, deno, playwright, puppeteer
- Language: TypeScript
- Homepage: https://jsr.io/@astral/astral
- Size: 7.01 MB
- Stars: 303
- Watchers: 4
- Forks: 23
- Open Issues: 36
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
![]()
Astral is a high-level puppeteer/playwright-like library that allows for control
over a web browser (primarily for automation and testing). It is written from
scratch with Deno in mind.## Usage
Take a screenshot of a website.
```ts
// Import Astral
import { launch } from "jsr:@astral/astral";// Launch the browser
const browser = await launch();// Open a new page
const page = await browser.newPage("https://deno.land");// Take a screenshot of the page and save that to disk
const screenshot = await page.screenshot();
Deno.writeFileSync("screenshot.png", screenshot);// Close the browser
await browser.close();
```You can use the evaluate function to run code in the context of the browser.
```ts
// Import Astral
import { launch } from "jsr:@astral/astral";// Launch the browser
const browser = await launch();// Open a new page
const page = await browser.newPage("https://deno.land");// Run code in the context of the browser
const value = await page.evaluate(() => {
return document.body.innerHTML;
});
console.log(value);// Run code with args
const result = await page.evaluate((x, y) => {
return `The result of adding ${x}+${y} = ${x + y}`;
}, {
args: [10, 15],
});
console.log(result);// Close the browser
await browser.close();
```You can navigate to a page and interact with it.
```ts
// Import Astral
import { launch } from "jsr:@astral/astral";// Launch browser in headfull mode
const browser = await launch({ headless: false });// Open the webpage
const page = await browser.newPage("https://deno.land");// Click the search button
const button = await page.$("button");
await button!.click();// Type in the search input
const input = await page.$("#search-input");
await input!.type("pyro", { delay: 1000 });// Wait for the search results to come back
await page.waitForNetworkIdle({ idleConnections: 0, idleTime: 1000 });// Click the 'pyro' link
const xLink = await page.$("a.justify-between:nth-child(1)");
await Promise.all([
page.waitForNavigation(),
xLink!.click(),
]);// Click the link to 'pyro.deno.dev'
const dLink = await page.$(
".markdown-body > p:nth-child(8) > a:nth-child(1)",
);
await Promise.all([
page.waitForNavigation(),
dLink!.click(),
]);// Close browser
await browser.close();
```TODO: Document the locator API.
## Advanced Usage
If you already have a browser process running somewhere else or you're using a
service that provides remote browsers for automation (such as
[browserless.io](https://www.browserless.io/)), it is possible to directly
connect to its endpoint rather than spawning a new process.```ts
// Import Astral
import { connect } from "jsr:@astral/astral";// Connect to remote endpoint
const browser = await connect({
wsEndpoint: "wss://remote-browser-endpoint.example.com",
});// Do stuff
const page = await browser.newPage("http://example.com");
console.log(await page.evaluate(() => document.title));// Close connection
await browser.close();
```If you'd like to instead re-use a browser that you already launched, astral
exposes the WebSocket endpoint through `browser.wsEndpoint()`.```ts
// Spawn a browser process
const browser = await launch();// Connect to first browser instead
const anotherBrowser = await connect({ wsEndpoint: browser.wsEndpoint() });
```### Page authenticate
[authenticate example code](https://github.com/lino-levan/astral/blob/main/examples/authenticate.ts):
```ts
// Open a new page
const page = await browser.newPage();// Provide credentials for HTTP authentication.
const url = "https://postman-echo.com/basic-auth";
await page.authenticate({ username: "postman", password: "password" });
await page.goto(url, { waitUntil: "networkidle2" });
```## BYOB - Bring Your Own Browser
Essentially the process is as simple as running a chromium-like binary with the
following flags:```
chromium --remote-debugging-port=1337 \
--headless=new \
--no-first-run \
--password-store=basic \
--use-mock-keychain \
--hide-scrollbars
```Technically, only the first flag is necessary, though I've found that these
flags generally get the best result. Once your browser process is running,
connecting to it is as simple as```typescript
// Import Astral
import { connect } from "jsr:@astral/astral";// Connect to remote endpoint
const browser = await connect({
wsEndpoint: "",
headless: false,
});console.log(browser.wsEndpoint());
// Do stuff
const page = await browser.newPage("http://example.com");
console.log(await page.evaluate(() => document.title));// Close connection
await browser.close();
```## FAQ
### Launch FAQ
#### "No usable sandbox!" with user namespace cloning enabled
> Ubuntu 23.10+ (or possibly other Linux distros in the future) ship an AppArmor
> profile that applies to Chrome stable binaries installed at
> /opt/google/chrome/chrome (the default installation path). This policy is
> stored at /etc/apparmor.d/chrome. This AppArmor policy prevents Chrome for
> Testing binaries downloaded by Puppeteer from using user namespaces resulting
> in the No usable sandbox! error when trying to launch the browser. For
> workarounds, see
> https://chromium.googlesource.com/chromium/src/+/main/docs/security/apparmor-userns-restrictions.mdThe following command removes AppArmor restrictions on user namespaces, allowing
Puppeteer to launch Chrome without the "No usable sandbox!" error (see
[puppeteer#13196](https://github.com/puppeteer/puppeteer/pull/13196)):echo 0 | sudo tee /proc/sys/kernel/apparmor_restrict_unprivileged_userns