fluxwright 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/MISSING.md CHANGED
@@ -1,15 +1,22 @@
1
- # Playwright methods not in Fluxwright (TypeScript)
1
+ # Playwright features not in Fluxwright
2
2
 
3
- The TS binding covers the same small page API as the Rust crate. Missing on purpose:
3
+ The Node binding covers the same page API as the Rust crate. Some Playwright features are planned, and some are out of scope.
4
4
 
5
- - Firefox / WebKit
6
- - Tracing, HAR, video
7
- - `page.route` beyond resource-type / URL blocking on the job
8
- - Storage state import/export
9
- - `page.emulateMedia`, geolocation, permissions helpers
10
- - Playwright Test runner, fixtures, expect
11
- - `locator.filter`, `nth`, `getByLabel`/`getByPlaceholder`/`getByTestId`, shadow-DOM piercing
5
+ ## Planned (see [ROADMAP.md](https://github.com/harshit-d3v/fluxwright/blob/main/ROADMAP.md))
6
+
7
+ - Saved login state: `storageState` import and export
8
+ - `page.route` to fulfill or modify requests (today: blocking by resource type or URL)
9
+ - Emulation: user agent, locale, timezone, geolocation, permissions, `emulateMedia`
10
+ - `locator.screenshot()`
11
+ - `getByLabel`, `getByPlaceholder`, `getByTestId`, `nth`, `filter`
12
+ - Downloads
13
+ - `connectOverCDP`, to use browsers you already run
14
+ - Python bindings
15
+
16
+ ## Out of scope
17
+
18
+ - Firefox and WebKit
19
+ - Playwright Test: the runner, fixtures and `expect`
20
+ - Tracing, the trace viewer, codegen and video recording
12
21
  - Multiple pages per context
13
- - Download / upload helpers
14
- - WebSocket / worker APIs
15
- - `connectOverCDP` (planned as a later `BrowserSource`)
22
+ - WebSocket and worker APIs
package/README.md CHANGED
@@ -1,8 +1,10 @@
1
1
  # fluxwright
2
2
 
3
- In-process **Chromium fleet** for Node. Lease API (fresh context per `newPage`), not Playwright parity.
3
+ Run hundreds of headless Chrome jobs on one machine, with a Playwright-style API. Each `newPage` is a fresh browser context on a pooled Chrome. The engine queues jobs when every slot is busy, restarts browsers before they bloat, and replaces browsers that crash.
4
4
 
5
- Requires a local Chrome/Chromium binary (`FLUXWRIGHT_CHROMIUM`, `CHROME`, `CHROMIUM`, or a standard install path).
5
+ Docs: https://fluxwright.vercel.app
6
+
7
+ Requires a local Chrome/Chromium binary (`FLUXWRIGHT_CHROMIUM`, `CHROME`, `CHROMIUM`, or a standard install path). For headless jobs, chrome-headless-shell is picked up from `PATH` or Puppeteer's/Playwright's cache and opens pages much faster: `npx @puppeteer/browsers install chrome-headless-shell@stable`.
6
8
 
7
9
  ## Install
8
10
 
@@ -24,11 +26,9 @@ import { chromium } from "fluxwright";
24
26
  const browser = await chromium.launch({ maxBrowsers: 4 });
25
27
  const page = await browser.newPage();
26
28
  await page.goto("https://example.com");
27
- console.log(await page.title());
28
- await page.click("css-selector");
29
- await page.fill("input", "value");
30
- const html = await page.content();
31
- const png = await page.screenshot(); // Buffer
29
+ console.log(await page.getByRole("heading").textContent()); // "Example Domain"
30
+ console.log(await page.evaluate(() => location.hostname));
31
+ const png = await page.screenshot({ fullPage: true }); // Buffer
32
32
  await page.close();
33
33
  await browser.close();
34
34
  ```
@@ -46,13 +46,23 @@ await browser.close();
46
46
  | `page.click(selector)` / `page.fill(selector, value)` | Scrolls into view, waits until enabled, stable, and not covered. Selector: CSS, `text=Foo`, `text="Exact"`, `role=button[name="Save"]` |
47
47
  | `page.getByRole(role, { name?, exact? })` / `page.getByText(text, { exact? })` / `page.locator(selector)` | `Locator` with `click()`, `fill(v)`, `waitFor()`, `textContent()`. Implicit ARIA roles and accessible names; hidden elements never match a role |
48
48
  | `page.frameLocator(iframeSelector)` | Same locators inside an iframe, same- or cross-origin; nest with `.frameLocator()` |
49
- | `page.evaluate(expression)` | `Runtime.evaluate`, JSON. A thrown JS error rejects with its message and stack |
49
+ | `page.evaluate(fn, arg?)` / `page.evaluate(expression)` | Runs a function with a JSON-serializable `arg`, or a string expression, in the page. Awaits promises and returns JSON. As in Playwright, the function is sent as source text, so it can't use variables from Node. A thrown JS error rejects with its message and stack |
50
50
  | `page.screenshot({ fullPage? })` | PNG `Buffer` |
51
51
  | `page.setViewportSize({ width, height })` | CSS-pixel viewport for this page |
52
52
  | `page.waitForSelector(selector)` | Waits until visible (Playwright's default state) |
53
53
  | `page.close()` / `browser.close()` | Dispose context / shut down |
54
54
 
55
- See `MISSING.md` for Playwright methods that will not be added.
55
+ Close pages when a job is done. A page you forget is cleaned up when Node garbage-collects it, which may be much later.
56
+
57
+ See `MISSING.md` for Playwright features that are planned or out of scope.
58
+
59
+ ## Develop
60
+
61
+ ```bash
62
+ npm install
63
+ npm run build # needs Rust
64
+ npm test # smoke test against a real Chrome
65
+ ```
56
66
 
57
67
  ## License
58
68
 
package/addon.d.ts CHANGED
@@ -2,4 +2,16 @@ export * from './index'
2
2
  import { Chromium } from './index'
3
3
  /** Playwright-style export: `chromium.launch(...)`. Same as `Chromium.launch`. */
4
4
  export const chromium: typeof Chromium
5
- export default { chromium }
5
+ declare const fluxwright: { chromium: typeof Chromium }
6
+ export default fluxwright
7
+
8
+ declare module './index' {
9
+ interface Page {
10
+ /**
11
+ * Runs `pageFunction` in the page with `arg` and returns its result, awaiting promises.
12
+ * As in Playwright, the function is sent as source text: it can use `arg` (JSON-serializable)
13
+ * but not variables from Node.
14
+ */
15
+ evaluate<R, Arg = undefined>(pageFunction: (arg: Arg) => R, arg?: Arg): Promise<Awaited<R>>
16
+ }
17
+ }
package/addon.js CHANGED
@@ -1,7 +1,44 @@
1
1
  'use strict'
2
2
 
3
+ const { Script } = require('node:vm')
4
+
5
+ // Playwright-style `page.evaluate(fn, arg)`. As in Playwright, the function is sent to the page as
6
+ // source text, so it can use `arg` but not variables from Node. Strings run as expressions.
7
+ function toExpression(pageFunction, arg) {
8
+ if (typeof pageFunction !== 'function') return pageFunction
9
+ return `(${functionSource(pageFunction)})(${arg === undefined ? '' : JSON.stringify(arg)})`
10
+ }
11
+
12
+ // Object and class methods stringify as `name(x) { ... }`, which is not an expression on its own.
13
+ // Like Playwright, turn them into function expressions (this also covers async and generator methods).
14
+ function functionSource(fn) {
15
+ let source = String(fn)
16
+ if (!parses(source)) {
17
+ source = source.startsWith('async ') ? `async function ${source.slice(6)}` : `function ${source}`
18
+ if (!parses(source)) {
19
+ throw new TypeError(`evaluate: this function can't be sent to the page: ${String(fn).slice(0, 60)}`)
20
+ }
21
+ }
22
+ return source
23
+ }
24
+
25
+ // Compiles only, nothing runs. Unlike `new Function`, this also works under
26
+ // `--disallow-code-generation-from-strings`.
27
+ function parses(source) {
28
+ try {
29
+ new Script(`(${source})`)
30
+ return true
31
+ } catch {
32
+ return false
33
+ }
34
+ }
35
+
3
36
  try {
4
37
  const native = require('./index.js')
38
+ const evaluate = native.Page.prototype.evaluate
39
+ native.Page.prototype.evaluate = async function (pageFunction, arg) {
40
+ return evaluate.call(this, toExpression(pageFunction, arg))
41
+ }
5
42
  module.exports = native
6
43
  module.exports.chromium = native.Chromium
7
44
  module.exports.default = module.exports
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "fluxwright",
3
- "version": "0.2.1",
4
- "description": "In-process Chromium fleet engine (napi-rs). Lease API, not Playwright parity.",
3
+ "version": "0.2.2",
4
+ "description": "Run hundreds of headless Chrome jobs on one machine: pooling, queueing, recycling and crash recovery, with a Playwright-style API.",
5
5
  "main": "addon.js",
6
6
  "types": "addon.d.ts",
7
7
  "repository": {
@@ -9,16 +9,20 @@
9
9
  "url": "git+https://github.com/harshit-d3v/fluxwright.git",
10
10
  "directory": "bindings/node"
11
11
  },
12
- "homepage": "https://github.com/harshit-d3v/fluxwright#readme",
12
+ "homepage": "https://fluxwright.vercel.app",
13
13
  "bugs": {
14
14
  "url": "https://github.com/harshit-d3v/fluxwright/issues"
15
15
  },
16
16
  "keywords": [
17
17
  "chromium",
18
18
  "chrome",
19
- "cdp",
19
+ "headless",
20
+ "playwright",
21
+ "puppeteer",
20
22
  "browser-pool",
21
- "automation",
23
+ "browser-automation",
24
+ "scraping",
25
+ "cdp",
22
26
  "napi-rs"
23
27
  ],
24
28
  "author": "Fluxwright contributors",
@@ -37,6 +41,8 @@
37
41
  ],
38
42
  "scripts": {
39
43
  "build": "napi build --platform --release",
44
+ "test": "node test/smoke.mjs",
45
+ "test:types": "tsc -p test",
40
46
  "build:debug": "napi build --platform",
41
47
  "artifacts": "napi artifacts",
42
48
  "prepublishOnly": "napi prepublish -t npm --skip-gh-release"
@@ -54,12 +60,14 @@
54
60
  "access": "public"
55
61
  },
56
62
  "devDependencies": {
57
- "@napi-rs/cli": "^2.18.4"
63
+ "@napi-rs/cli": "^2.18.4",
64
+ "@types/node": "^22.20.4",
65
+ "typescript": "^5.9.3"
58
66
  },
59
67
  "optionalDependencies": {
60
- "fluxwright-darwin-arm64": "0.2.1",
61
- "fluxwright-darwin-x64": "0.2.1",
62
- "fluxwright-linux-x64-gnu": "0.2.1",
63
- "fluxwright-win32-x64-msvc": "0.2.1"
68
+ "fluxwright-darwin-arm64": "0.2.2",
69
+ "fluxwright-darwin-x64": "0.2.2",
70
+ "fluxwright-linux-x64-gnu": "0.2.2",
71
+ "fluxwright-win32-x64-msvc": "0.2.2"
64
72
  }
65
73
  }