<!--
  url: https://browserscale.cloud/docs/api-reference/ts
  title: TypeScript SDK Reference
  description: Complete reference for the browserscale-ts TypeScript package: promise-based methods, single opts argument per call, full type definitions, and runnable examples.
-->

# TypeScript SDK Reference

Auto-generated from TSDoc. All methods return a `Promise`; the resolved value type is shown below.

## CloudBrowser

CloudBrowser is the SDK-side handle for an active browserscale browser session.

One CloudBrowser corresponds to exactly one browser context, which always has at least one page. The session is implicitly bound to its primary page server-side — the proto's page_id field is currently ignored server-side, so the SDK never sets it.

Construct via rentBrowser() / createWebSocketBrowser() — never directly.

### `clearCookies(): void`

*method on `CloudBrowser`*

Deletes every cookie in the browser context.

**Returns:** `void`

**Throws:**
- `UNKNOWN_ERROR` — the cookies could not be cleared

```ts
await browser.clearCookies();
```

### `clearStorage(origin: string | undefined): void`

*method on `CloudBrowser`*

Deletes localStorage in the browser context.

**Parameters:**
- `origin` (`string | undefined`) — if set, only this origin's storage is deleted (e.g. "https://example.com"); omit to delete all origins

**Returns:** `void`

**Throws:**
- `UNKNOWN_ERROR` — the storage could not be cleared

```ts
// Wipe one origin.
await browser.clearStorage("https://example.com");

// Wipe everything.
await browser.clearStorage();
```

### `click(target: Locator, opts: ClickOpts | undefined): ElementResult`

*method on `CloudBrowser`*

Triggers a single left mouse click on the given target.

The browser scrolls the element into view if needed, moves the cursor along a human-like path, then dispatches a full mouseDown+mouseUp at a randomized point inside the element's bounding rect.

For right-click, double-click, press/release-only, or to override the target frame, pass a `ClickOpts` object as the second argument.

**Parameters:**
- `target` (`Locator`) — locator describing what to click; at is also valid
- `opts` (`ClickOpts | undefined`) — optional click customization; see ClickOpts

**Returns:** `ElementResult` — ElementResult with success, resolved frameId, backendNodeId, post-scroll isVisible, element bounds, and the root-viewport (rootX, rootY) where the click landed

**Throws:**
- `CLICK_FAILED` — the click could not be dispatched
- `ELEMENT_NOT_FOUND` — no element matched the locator
- `FRAME_NOT_FOUND` — the requested frame does not exist
- `INVALID_LOCATOR` — target is empty or has multiple targets set
- `PAGE_NOT_ALIVE` — the page has been closed
- `TIMEOUT` — the operation exceeded the server-side timeout

```ts
await browser.click(css("button.submit"));
```

```ts
// Right double-click on a context menu trigger.
await browser.click(css("li.menu"), { button: "right", clickCount: 2 });
```

### `dragBy(target: Locator, offsetX: number, offsetY: number): DragResult`

*method on `CloudBrowser`*

Picks up the target and drops it at an offset relative to the pickup point.

The browser presses the left mouse button at a pickup point inside the element, drags along a human-like path to (pickupX+offsetX, pickupY+offsetY), then releases. at is not a valid target — drag needs a real element.

**Parameters:**
- `target` (`Locator`) — locator describing the element to pick up
- `offsetX` (`number`) — horizontal distance to drag, in CSS pixels
- `offsetY` (`number`) — vertical distance to drag, in CSS pixels

**Returns:** `DragResult` — DragResult with the resolved frameId, backendNodeId and the final cursor position (rootX, rootY) where the drop happened

**Throws:**
- `UNKNOWN_ERROR` — the drag could not be performed

```ts
await browser.dragBy(css(".slider .handle"), 120, 0);
```

### `dragTo(target: Locator, absoluteX: number, absoluteY: number): DragResult`

*method on `CloudBrowser`*

Picks up the target and drops it at absolute root-viewport coordinates.

Same gesture as dragBy, but the drop destination is in page coordinates rather than relative to the pickup point.

**Parameters:**
- `target` (`Locator`) — locator describing the element to pick up
- `absoluteX` (`number`) — horizontal drop coordinate in the root viewport
- `absoluteY` (`number`) — vertical drop coordinate in the root viewport

**Returns:** `DragResult` — DragResult with the resolved frameId, backendNodeId and the final cursor position (rootX, rootY) where the drop happened

**Throws:**
- `UNKNOWN_ERROR` — the drag could not be performed

```ts
await browser.dragTo(css(".card"), 800, 400);
```

### `evaluate(expression: string): EvaluateResult<T>`

*method on `CloudBrowser`*

Runs a JavaScript expression in the page's main frame.

The expression's return value is JSON-serialized server-side and parsed eagerly into `.value`. When the expression returns a DOM element the `.value` is null and the element metadata (backendNodeId, isVisible, bounds) is populated instead — use node in subsequent calls to act on it.

The generic T is a TypeScript hint only — there is no runtime validation that the JS expression actually returned that type.

**Parameters:**
- `expression` (`string`) — JavaScript expression evaluated in the main frame

**Returns:** `EvaluateResult<T>` — EvaluateResult with either value (non-Element) or element metadata (Element)

**Throws:**
- `UNKNOWN_ERROR` — the expression threw or could not be compiled

```ts
const res = await browser.evaluate<string>("document.title");
console.log(res.value);
```

### `evaluateInFrame(frameId: string, expression: string): EvaluateResult<T>`

*method on `CloudBrowser`*
*inherits from `CloudBrowser.evaluate`*

Runs a JavaScript expression in the given frame.

Same semantics as evaluate but targets a specific frame instead of the main frame. Useful for evaluating inside OOPIFs (out- of-process iframes) found via getPages. ALL_FRAMES is not supported here.

**Parameters:**
- `frameId` (`string`) — id of the frame to evaluate in
- `expression` (`string`) — JavaScript expression evaluated in the main frame

**Returns:** `EvaluateResult<T>` — EvaluateResult with either value (non-Element) or element metadata (Element)

**Throws:**
- `UNKNOWN_ERROR` — the expression threw or could not be compiled

```ts
const pages = await browser.getPages();
const iframeId = pages[0].frameTree.children[0].frameId;
await browser.evaluateInFrame(iframeId, "location.href");
```

### `fill(target: Locator, text: string, opts: FillOpts | undefined): ElementResult`

*method on `CloudBrowser`*

Clicks the target and types text into it, appending to any existing content.

The browser scrolls the element into view, moves the cursor along a human-like path, clicks to focus, then types the text character- by-character with QWERTZ keyboard simulation and human-like timing.

To overwrite the field instead of appending, pass `{ clearFirst: true }`.

at is not a valid target — fill requires an actual element.

**Parameters:**
- `target` (`Locator`) — locator describing the input element
- `text` (`string`) — text to type into the element
- `opts` (`FillOpts | undefined`) — optional fill customization; see FillOpts

**Returns:** `ElementResult` — ElementResult with success, resolved frameId, backendNodeId and the root-viewport (rootX, rootY) where the element was clicked

**Throws:**
- `ELEMENT_NOT_FOUND` — no element matched the locator
- `FILL_FAILED` — the input could not be filled
- `FRAME_NOT_FOUND` — the requested frame does not exist
- `INVALID_LOCATOR` — target is empty or has multiple targets set
- `PAGE_NOT_ALIVE` — the page has been closed
- `TIMEOUT` — the operation exceeded the server-side timeout

```ts
await browser.fill(css("input[name=email]"), "user@example.com");
```

```ts
// Wipe the field first, then type fresh content.
await browser.fill(css("input[name=email]"), "user@example.com", { clearFirst: true });
```

### `getApiKey(): string`

*method on `CloudBrowser`*

Returns the API key used to rent this session.

**Returns:** `string`

### `getCookies(): CookieParam[]`

*method on `CloudBrowser`*

Returns all cookies currently stored in this session's browser context.

**Returns:** `CookieParam[]` — CookieParam[], one per cookie in the context

**Throws:**
- `UNKNOWN_ERROR` — the cookies could not be read

```ts
const cookies = await browser.getCookies();
for (const c of cookies) console.log(c.name, "=", c.value);
```

### `getDOM(frameId: string, opts: GetDOMOpts | undefined): DOMResult`

*method on `CloudBrowser`*

Returns the DOM in CDP DOM.Node shape for the requested frame.

The shape matches Chrome DevTools' Protocol DOM.Node — useful for piping into agent loops or visualizers that already speak CDP. For a much smaller agent-oriented payload, prefer getObservation instead. The cheap polling endpoint is getDOMHash.

**Parameters:**
- `frameId` (`string`) — id of the frame to dump; empty string targets the main frame
- `opts` (`GetDOMOpts | undefined`) — optional `depth`: -1 full tree (default), 0 root only, N root + N descendant levels

**Returns:** `DOMResult` — DOMResult with the JSON string in `.dom` (the `.hash` field is populated by getDOMHash, not by this call)

**Throws:**
- `UNKNOWN_ERROR` — the DOM could not be retrieved

```ts
const { dom } = await browser.getDOM();
```

### `getDOMHash(frameId: string): string`

*method on `CloudBrowser`*

Returns sha256:8 of the full-tree DOM JSON for cheap polling-based change detection.

Computing a hash is much cheaper than transferring the full tree — pair this with getDOM only when the hash differs from your last snapshot.

**Parameters:**
- `frameId` (`string`) — id of the frame to hash; empty string targets the main frame

**Returns:** `string` — 16-char hex string (the first 8 bytes of sha256 of the DOM JSON)

**Throws:**
- `UNKNOWN_ERROR` — the hash could not be computed

```ts
const hash = await browser.getDOMHash();
if (hash !== lastHash) {
// DOM changed → re-fetch
}
```

### `getFingerprint(): string`

*method on `CloudBrowser`*

Returns the browser fingerprint id in use for this session.

**Returns:** `string`

### `getObservation(opts: GetObservationOpts | undefined): ObservationResult`

*method on `CloudBrowser`*

Returns a compact, agent-friendly description of every interactable element currently visible on the page, together with a truncated view of the surrounding text.

Intended as input for LLM/agent loops where a full DOM dump would be too large; the server filters down to elements that are actually visible and interactable.

**Parameters:**
- `opts` (`GetObservationOpts | undefined`) — optional caps: `maxElementsPerFrame`, `maxTextLength`; omit either to use the server default

**Returns:** `ObservationResult` — ObservationResult with both a human-readable `text` rendering and a `json` payload of the structured observation

**Throws:**
- `UNKNOWN_ERROR` — the observation could not be produced

```ts
const obs = await browser.getObservation({ maxElementsPerFrame: 200 });
console.log(obs.text);
```

### `getPages(): PageInfo[]`

*method on `CloudBrowser`*

Returns all open pages (tabs and popups) for this session's browser context.

Each PageInfo carries the page's URL, title, viewport and a full nested frame tree (out-of-process iframes are children of the page's main frame).

**Returns:** `PageInfo[]` — PageInfo[] for every page currently open in the context

**Throws:**
- `UNKNOWN_ERROR` — the pages could not be enumerated

```ts
const pages = await browser.getPages();
for (const p of pages) console.log(p.url, p.title);
```

### `getSelection(): string`

*method on `CloudBrowser`*

Returns the current text selection.

Walks every frame and returns the first non-empty selection found — useful for "copy what the user highlighted" flows. Returns an empty string when nothing is selected anywhere.

**Returns:** `string` — the selected text, or `""` when nothing is selected

**Throws:**
- `UNKNOWN_ERROR` — the selection could not be read

```ts
const sel = await browser.getSelection();
console.log("user selected:", sel);
```

### `getSessionId(): string`

*method on `CloudBrowser`*

Returns the unique server-assigned id for this browser session.

**Returns:** `string`

### `getStorage(origin: string | undefined): StorageOriginEntry[]`

*method on `CloudBrowser`*

Returns the localStorage contents of this session's browser context, grouped by origin.

The storage database is read directly in the browser process, so no page needs to be open. Only first-party localStorage is included — sessionStorage is per-tab and not covered.

**Parameters:**
- `origin` (`string | undefined`) — if set, only this origin is returned (e.g. "https://example.com"); omit to get all origins

**Returns:** `StorageOriginEntry[]` — StorageOriginEntry[], one per origin with localStorage data

**Throws:**
- `UNKNOWN_ERROR` — the storage could not be read

```ts
const storage = await browser.getStorage();
for (const e of storage) {
for (const { key, value } of e.items) console.log(e.origin, key, "=", value);
}
```

### `highlightNode(backendNodeId: number, frameId: string): void`

*method on `CloudBrowser`*

Paints a debug overlay over the node identified by backendNodeId.

Useful for visual debugging of agent flows — the overlay stays until the next call. Pass backendNodeId <= 0 to clear any current highlights.

**Parameters:**
- `backendNodeId` (`number`) — id of the node to highlight, or <= 0 to clear
- `frameId` (`string`) — id of the frame the node lives in; empty string targets the main frame

**Returns:** `void`

**Throws:**
- `UNKNOWN_ERROR` — the highlight could not be applied

```ts
await browser.highlightNode(res.backendNodeId, res.frameId);
```

### `insertText(text: string): void`

*method on `CloudBrowser`*

Pastes text at the current caret using IME-style input.

No individual key events are dispatched; the entire string is committed at once via Input.insertText. Whatever element currently has focus receives the text. Use click or fill first if you need a specific element to be focused.

**Parameters:**
- `text` (`string`) — the text to insert at the caret

**Returns:** `void`

**Throws:**
- `UNKNOWN_ERROR` — the text could not be inserted

```ts
await browser.insertText("hello world");
```

### `inspectAtPosition(x: number, y: number): InspectResult`

*method on `CloudBrowser`*

Hit-tests at the viewport-relative (x, y) and returns the topmost element under that point.

Mirrors what the live-UI overlay does on hover. Elements with pointer-events:none are skipped — the result is the actual click target, not the visually-topmost node. A `backendNodeId === 0` in the result means nothing was found.

**Parameters:**
- `x` (`number`) — viewport-relative x in CSS pixels
- `y` (`number`) — viewport-relative y in CSS pixels

**Returns:** `InspectResult` — InspectResult with the resolved backendNodeId, frameId, tag name, trimmed textContent, visibility and bounds

**Throws:**
- `UNKNOWN_ERROR` — the hit-test failed

```ts
const r = await browser.inspectAtPosition(200, 300);
console.log(r.tagName, r.textContent);
```

### `loadHTML(url: string, html: string, opts: LoadHTMLOpts | undefined): void`

*method on `CloudBrowser`*

Serves a synthetic response for the next navigation to url.

Registers a one-shot interceptor that intercepts the next request to url and replies with the supplied html and headers instead of going to the network. Useful for snapshotted pages, test fixtures, and offline replays. Pair with navigate to trigger the load.

**Parameters:**
- `url` (`string`) — URL pattern that, when navigated to, returns the html
- `html` (`string`) — response body to serve
- `opts` (`LoadHTMLOpts | undefined`) — optional headers and statusCode (default 200)

**Returns:** `void`

**Throws:**
- `UNKNOWN_ERROR` — the interceptor could not be installed

```ts
await browser.loadHTML("https://example.com", "<h1>hi</h1>");
await browser.navigate("https://example.com");
```

### `modifyRequest(urlPattern: string, opts: {
      body?: string;
      modifications?: HeaderModification[];
      timeoutMs?: number;
    } | undefined): InterceptedRequest | null`

*method on `CloudBrowser`*

Waits for the next request whose URL matches urlPattern, applies the supplied header modifications (and optional body replacement), then forwards the modified request.

One-shot: consumes the first matching request. Each modification is a plain HeaderModification object literal.

**Parameters:**
- `urlPattern` (`string`) — URL wildcard to wait for
- `opts` (`{
      body?: string;
      modifications?: HeaderModification[];
      timeoutMs?: number;
    } | undefined`) — optional `body` (replacement request body), `modifications` (header changes), and `timeoutMs` (per-call timeout)

**Returns:** `InterceptedRequest | null` — InterceptedRequest carrying the method/URL/headers/body that were actually sent on the wire after modifications were applied; null when no request payload was reported

**Throws:**
- `UNKNOWN_ERROR` — no matching request appeared within the timeout

```ts
const req = await browser.modifyRequest("*\/api/me", {
modifications: [
  { action: "add", name: "X-Trace", value: "abc123" },
  { action: "remove", name: "Cookie" },
],
timeoutMs: 5000,
});
console.log("forwarded headers:", req?.headers);
```

### `moveTo(target: Locator): ElementResult`

*method on `CloudBrowser`*

Moves the mouse cursor over the given target.

The browser scrolls the target into view first if necessary, then animates the cursor along a human-like path to the element's center (or to the viewport coordinate when target is at).

**Parameters:**
- `target` (`Locator`) — locator describing where to move; at is also valid

**Returns:** `ElementResult` — ElementResult with the resolved frameId, backendNodeId, post-scroll isVisible, element bounds and the root-viewport (rootX, rootY) where the cursor ended up

**Throws:**
- `UNKNOWN_ERROR` — the move could not be completed

```ts
await browser.moveTo(css("nav .menu"));
```

### `navigate(url: string, opts: NavigateOpts | undefined): NavigateResult`

*method on `CloudBrowser`*

Navigates the page to url.

Returns once the primary main-frame navigation commits (the response is received and a new document is selected), before DOMContentLoaded or load fire. Cross-origin redirects are followed.

**Parameters:**
- `url` (`string`) — destination URL
- `opts` (`NavigateOpts | undefined`) — optional navigation customization (e.g. timeoutMs)

**Returns:** `NavigateResult` — NavigateResult with the final resolved URL and the frameId of the main frame after navigation

**Throws:**
- `UNKNOWN_ERROR` — the navigation failed or timed out

```ts
await browser.navigate("https://example.com");
```

### `pressKey(key: string, opts: { code?: string; modifiers?: number; location?: number } | undefined): void`

*method on `CloudBrowser`*

Fires a single key-down event.

Only the keydown half is dispatched — pair with releaseKey for a full press cycle. The event targets whichever element currently has focus.

**Parameters:**
- `key` (`string`) — DOM KeyboardEvent.key value (e.g. `"Enter"`, `"a"`, `"ArrowLeft"`)
- `opts` (`{ code?: string; modifiers?: number; location?: number } | undefined`) — optional key customization: `code` (DOM KeyboardEvent.code, e.g. `"KeyA"`), `modifiers` (bit-flag: Alt=1, Ctrl=2, Meta=4, Shift=8), `location` (0=standard, 1=left, 2=right, 3=numpad)

**Returns:** `void`

**Throws:**
- `UNKNOWN_ERROR` — the event could not be dispatched

```ts
// Ctrl+A
await browser.pressKey("a", { code: "KeyA", modifiers: 2 });
await browser.releaseKey("a", { code: "KeyA", modifiers: 2 });
```

### `readCanvas(target: Locator, opts: ReadCanvasOpts | undefined): ReadCanvasResult`

*method on `CloudBrowser`*

Reads the pixels of a <canvas> element directly in the renderer, bypassing the origin-clean (tainted) security check and without executing any page JavaScript — so cross-origin/tainted canvases (common in captchas) read fine where a normal `toDataURL` / `getImageData` would throw a SecurityError.

at is not a valid target — a real <canvas> element is required.

**Parameters:**
- `target` (`Locator`) — locator for the <canvas>; css, js or node
- `opts` (`ReadCanvasOpts | undefined`) — optional `format`, `quality`, sub-rectangle and frame override; see ReadCanvasOpts

**Returns:** `ReadCanvasResult` — ReadCanvasResult with the base64 image in `dataBase64`, the canvas `width`/`height`, resolved `frameId`/`backendNodeId` and the `originClean` flag

**Throws:**
- `ELEMENT_NOT_FOUND` — no element matched the locator
- `FRAME_NOT_FOUND` — the requested frame does not exist
- `INVALID_LOCATOR` — target is empty, uses at(x,y), or has multiple targets
- `PAGE_NOT_ALIVE` — the page has been closed
- `TIMEOUT` — the operation exceeded the server-side timeout

```ts
const res = await browser.readCanvas(css("#game canvas"));
await fs.writeFile("canvas.png", Buffer.from(res.dataBase64, "base64"));
```

```ts
// Read the left half as JPEG at quality 80.
const res = await browser.readCanvas(css("canvas"), {
format: "jpeg", quality: 80, sw: 150, sh: 300,
});
```

### `releaseKey(key: string, opts: { code?: string; modifiers?: number; location?: number } | undefined): void`

*method on `CloudBrowser`*
*inherits from `CloudBrowser.pressKey`*

Fires a single key-up event.

Mirror of pressKey. Same parameter semantics; use this to close a press cycle that was started with pressKey.

**Parameters:**
- `key` (`string`) — DOM KeyboardEvent.key value (e.g. `"Enter"`, `"a"`, `"ArrowLeft"`)
- `opts` (`{ code?: string; modifiers?: number; location?: number } | undefined`) — optional key customization: `code` (DOM KeyboardEvent.code, e.g. `"KeyA"`), `modifiers` (bit-flag: Alt=1, Ctrl=2, Meta=4, Shift=8), `location` (0=standard, 1=left, 2=right, 3=numpad)

**Returns:** `void`

**Throws:**
- `UNKNOWN_ERROR` — the event could not be dispatched

```ts
await browser.pressKey("Shift", { code: "ShiftLeft", location: 1 });
await browser.releaseKey("Shift", { code: "ShiftLeft", location: 1 });
```

### `screenshot(opts: ScreenshotOpts | undefined): ScreenshotResult`

*method on `CloudBrowser`*

Captures a single image of the page's current frame and returns it as base64-encoded image bytes.

The capture uses a one-shot surface copy (the same mechanism as CDP Page.captureScreenshot), so it is independent of any active live stream and works with both GPU (hardware) and software compositing.

**Parameters:**
- `opts` (`ScreenshotOpts | undefined`) — optional `format` ("png" default, "jpeg", "webp") and `quality` (0-100, for jpeg/webp only); omit to use the server defaults

**Returns:** `ScreenshotResult` — ScreenshotResult with the base64 image in `dataBase64` and the physical pixel `width`/`height`

**Throws:**
- `UNKNOWN_ERROR` — the screenshot could not be captured

```ts
const shot = await browser.screenshot({ format: "png" });
await fs.writeFile("page.png", Buffer.from(shot.dataBase64, "base64"));
```

### `scrollTo(target: Locator): ElementResult`

*method on `CloudBrowser`*

Scrolls the given element into view.

Whatever scroll container is closest to the element does the scrolling — nested scroll containers and out-of-process iframe chains are walked automatically. at is not a valid target here; scrolling needs a real element.

**Parameters:**
- `target` (`Locator`) — locator describing the element to bring into view; at is rejected

**Returns:** `ElementResult` — ElementResult with the resolved frameId, backendNodeId, post-scroll isVisible and the element's bounds after the scroll

**Throws:**
- `UNKNOWN_ERROR` — the element could not be scrolled into view

```ts
await browser.scrollTo(css("#footer"));
```

### `selectByIndex(target: Locator, index: number, opts: SelectOpts | undefined): SelectOptionResult`

*method on `CloudBrowser`*

Picks the `<option>` at the zero-based index inside the targeted `<select>` element.

Sets the option as selected on the targeted `<select>`, then fires the standard input + change events (unless suppressed via `opts.fireEvents = false`).

at is not a valid target — select requires an actual `<select>` element.

**Parameters:**
- `target` (`Locator`) — locator describing the `<select>` element
- `index` (`number`) — zero-based option index
- `opts` (`SelectOpts | undefined`) — optional select customization; see SelectOpts

**Returns:** `SelectOptionResult` — SelectOptionResult with the resolved selectedIndex, selectedValue and selectedText after the change

**Throws:**
- `ELEMENT_NOT_FOUND` — no element matched the locator
- `FRAME_NOT_FOUND` — the requested frame does not exist
- `INVALID_LOCATOR` — target is empty or has multiple targets set
- `PAGE_NOT_ALIVE` — the page has been closed
- `SELECT_FAILED` — the option could not be selected (out of range, or element is not a `<select>`)
- `TIMEOUT` — the operation exceeded the server-side timeout

```ts
await browser.selectByIndex(css("select#country"), 2);
```

```ts
// Pick the option silently, no input/change events.
await browser.selectByIndex(css("select#hidden"), 0, { fireEvents: false });
```

### `selectByText(target: Locator, text: string, opts: SelectOpts | undefined): SelectOptionResult`

*method on `CloudBrowser`*
*inherits from `CloudBrowser.selectByIndex`*

Picks the `<option>` whose visible (trimmed) text matches the given string exactly.

Sets the option as selected on the targeted `<select>`, then fires the standard input + change events (unless suppressed via `opts.fireEvents = false`).

at is not a valid target — select requires an actual `<select>` element.

**Parameters:**
- `target` (`Locator`) — locator describing the `<select>` element
- `text` (`string`) — the visible option text to match
- `opts` (`SelectOpts | undefined`) — optional select customization; see SelectOpts

**Returns:** `SelectOptionResult` — SelectOptionResult with the resolved selectedIndex, selectedValue and selectedText after the change

**Throws:**
- `ELEMENT_NOT_FOUND` — no element matched the locator
- `FRAME_NOT_FOUND` — the requested frame does not exist
- `INVALID_LOCATOR` — target is empty or has multiple targets set
- `PAGE_NOT_ALIVE` — the page has been closed
- `SELECT_FAILED` — the option could not be selected (out of range, or element is not a `<select>`)
- `TIMEOUT` — the operation exceeded the server-side timeout

```ts
await browser.selectByText(css("select#country"), "Germany");
```

### `selectByValue(target: Locator, value: string, opts: SelectOpts | undefined): SelectOptionResult`

*method on `CloudBrowser`*
*inherits from `CloudBrowser.selectByIndex`*

Picks the `<option>` whose `value` attribute matches the given string exactly.

Sets the option as selected on the targeted `<select>`, then fires the standard input + change events (unless suppressed via `opts.fireEvents = false`).

at is not a valid target — select requires an actual `<select>` element.

**Parameters:**
- `target` (`Locator`) — locator describing the `<select>` element
- `value` (`string`) — the `value` attribute to match
- `opts` (`SelectOpts | undefined`) — optional select customization; see SelectOpts

**Returns:** `SelectOptionResult` — SelectOptionResult with the resolved selectedIndex, selectedValue and selectedText after the change

**Throws:**
- `ELEMENT_NOT_FOUND` — no element matched the locator
- `FRAME_NOT_FOUND` — the requested frame does not exist
- `INVALID_LOCATOR` — target is empty or has multiple targets set
- `PAGE_NOT_ALIVE` — the page has been closed
- `SELECT_FAILED` — the option could not be selected (out of range, or element is not a `<select>`)
- `TIMEOUT` — the operation exceeded the server-side timeout

```ts
await browser.selectByValue(css("select#country"), "DE");
```

### `setBlockList(patterns: string[]): void`

*method on `CloudBrowser`*

Replaces the session's URL blocklist.

Any request whose URL matches one of the supplied patterns is blocked before it leaves the browser. Patterns are simple URL wildcards (`*` matches any character span). Pass an empty array to clear the blocklist and let everything through.

**Parameters:**
- `patterns` (`string[]`) — URL wildcards to block; empty array clears the list

**Returns:** `void`

**Throws:**
- `UNKNOWN_ERROR` — the blocklist could not be applied

```ts
await browser.setBlockList([
"*.doubleclick.net/*",
"*googletagmanager.com*",
]);
```

### `setCookies(cookies: CookieParam[]): void`

*method on `CloudBrowser`*

Writes the supplied cookies into the browser context.

Existing cookies with the same (name, domain, path) tuple are overwritten. Pass an empty array for a no-op.

**Parameters:**
- `cookies` (`CookieParam[]`) — cookies to write; empty array is a no-op

**Returns:** `void`

**Throws:**
- `UNKNOWN_ERROR` — the cookies could not be written

```ts
await browser.setCookies([
{ name: "auth", value: "tok", domain: "example.com", path: "/" },
]);
```

### `setProxy(proxyHost: string, proxyPort: number, proxyUsername: string, proxyPassword: string): void`

*method on `CloudBrowser`*

Changes the runtime proxy for this session.

Takes effect for new requests immediately; in-flight requests keep their original routing. Pass an empty proxyHost to clear the proxy and route directly.

**Parameters:**
- `proxyHost` (`string`) — upstream proxy host; empty disables the proxy
- `proxyPort` (`number`) — upstream proxy port; ignored when proxyHost is empty
- `proxyUsername` (`string`) — proxy auth user; empty for unauthenticated proxies
- `proxyPassword` (`string`) — proxy auth password; empty for unauthenticated proxies

**Returns:** `void`

**Throws:**
- `UNKNOWN_ERROR` — the proxy could not be applied

```ts
await browser.setProxy("proxy.example.com", 8080, "user", "pass");
```

### `setStaticPaths(blobName: string, patterns: string[]): void`

*method on `CloudBrowser`*

Configures the session to serve cached static responses for requests matching the given patterns from blobName.

Useful for replaying frozen page assets (HTML/JS/CSS/images) without hitting the origin every time. The cache backend itself is configured server-side. Pass an empty patterns array to disable caching for this session.

**Parameters:**
- `blobName` (`string`) — server-side identifier of the snapshot to serve from
- `patterns` (`string[]`) — URL wildcards to redirect to the cache; empty disables

**Returns:** `void`

**Throws:**
- `UNKNOWN_ERROR` — the static paths could not be configured

```ts
await browser.setStaticPaths("snap-2026-05", ["*.example.com/*"]);
```

### `setStorage(storage: StorageOriginEntry[]): void`

*method on `CloudBrowser`*

Writes localStorage entries into the browser context, grouped by origin.

Accepts the same structure getStorage() returns, so a dump can be fed back verbatim. Existing keys are overwritten. Works without any open page; pages that are already open will not observe the writes until they reload.

**Parameters:**
- `storage` (`StorageOriginEntry[]`) — entries to write, grouped by origin

**Returns:** `void`

**Throws:**
- `UNKNOWN_ERROR` — the storage could not be written

```ts
await browser.setStorage([
{
  origin: "https://example.com",
  items: [
    { key: "token", value: "abc123" },
    { key: "theme", value: "dark" },
  ],
},
]);
```

### `solveCaptcha(opts: { timeoutMs?: number; retryAmount?: number } | undefined): string`

*method on `CloudBrowser`*

Detects and solves the first supported bot-challenge it finds anywhere on the page.

Detection covers the common challenge types you run into in the wild. The challenge is completed in-page server-side (the resulting token / bypass cookies are wired into the page automatically), so callers can ignore the returned string.

**Parameters:**
- `opts` (`{ timeoutMs?: number; retryAmount?: number } | undefined`) — optional `timeoutMs` (how long to wait for a captcha to appear; omit for server default 60s) and `retryAmount` (failures tolerated before giving up)

**Returns:** `string` — empty string on success — the solution is applied server-side

**Throws:**
- `UNKNOWN_ERROR` — no captcha appeared within timeoutMs, or the detected captcha could not be solved within retryAmount attempts

```ts
await browser.solveCaptcha({ retryAmount: 2 });
```

### `wait(condition: Locator, opts: WaitOpts | undefined): WaitResult`

*method on `CloudBrowser`*
*inherits from `CloudBrowser.waitAny`*

Blocks until the given locator matches.

Shortcut for `waitAny(condition, opts)`. See waitAny for timeout handling, per-locator visible/steady defaults and the list of locators that are not valid wait conditions.

**Parameters:**
- `condition` (`Locator`) — the single locator to wait for
- `opts` (`WaitOpts | undefined`) — optional wait customization (timeoutMs)

**Returns:** `WaitResult` — WaitResult for the first matching condition

**Throws:**
- `UNKNOWN_ERROR` — the wait timed out or a condition was invalid

```ts
await browser.wait(css(".success"));
```

### `waitAny(conditions: Locator[], opts: WaitOpts | undefined): WaitResult`

*method on `CloudBrowser`*

Blocks until any of the supplied locators matches.

When several conditions are supplied, the first one to match wins; the others are abandoned. The returned WaitResult's `.index` points to the entry in `conditions` that matched.

Defaults applied automatically: - timeout: 30000 ms — override via `opts.timeoutMs` - per-locator visible/steady: `true` / `500` for css() and js() locators. For js() expressions returning a non-Element value both flags are no-ops. Override with `.visible(false)` / `.steady(ms)` on individual locators.

node and at are not valid wait conditions — they only make sense as action targets — and throw at send time.

**Parameters:**
- `conditions` (`Locator[]`) — one or more Locators to wait for; must be non-empty
- `opts` (`WaitOpts | undefined`) — optional wait customization (timeoutMs)

**Returns:** `WaitResult` — WaitResult for the first matching condition

**Throws:**
- `UNKNOWN_ERROR` — the wait timed out or a condition was invalid

```ts
const r = await browser.waitAny(
[css(".success"), js("window.__ready === true")],
{ timeoutMs: 5000 },
);
console.log("matched index:", r.index);
```

### `waitForAnyRequest(patterns: RequestPattern[], opts: { timeoutMs?: number } | undefined): { index: number; request: InterceptedRequest | null }`

*method on `CloudBrowser`*

Blocks until the next request whose URL matches one of the supplied patterns is observed.

Returns the matched pattern's index and the captured request. When `patternsi.abort` is true the request is dropped with an empty 200 response instead of being sent to the network.

**Parameters:**
- `patterns` (`RequestPattern[]`) — one or more URL patterns (with optional abort flags)
- `opts` (`{ timeoutMs?: number } | undefined`) — optional `timeoutMs`; omit to use the server default

**Returns:** `{ index: number; request: InterceptedRequest | null }` — object with `index` (matched pattern index) and `request` (the captured method/URL/headers/body; null if intercepted with no body)

**Throws:**
- `UNKNOWN_ERROR` — the wait timed out or no patterns were supplied

```ts
const { index, request } = await browser.waitForAnyRequest(
[{ url: "*\/api/login" }],
{ timeoutMs: 5000 },
);
console.log(index, request?.method, request?.url);
```

### `waitForAnyResponse(patterns: RequestPattern[], opts: { timeoutMs?: number } | undefined): { index: number; response: InterceptedResponse | null }`

*method on `CloudBrowser`*
*inherits from `CloudBrowser.waitForAnyRequest`*

Blocks until the next response whose URL matches one of the supplied patterns is observed.

Same shape as waitForAnyRequest but on the response phase. When `patternsi.abort` is true the page receives an empty 200 instead of the real response.

**Parameters:**
- `patterns` (`RequestPattern[]`) — one or more URL patterns (with optional abort flags)
- `opts` (`{ timeoutMs?: number } | undefined`) — optional `timeoutMs`; omit to use the server default

**Returns:** `{ index: number; response: InterceptedResponse | null }` — object with `index` (matched pattern index) and `response` (the captured status/headers/body; null if no body was returned)

**Throws:**
- `UNKNOWN_ERROR` — the wait timed out or no patterns were supplied

```ts
const { index, response } = await browser.waitForAnyResponse(
[{ url: "*\/api/login" }],
{ timeoutMs: 5000 },
);
console.log(index, response?.statusCode);
```

## Locator

Locator is the universal "what element / what condition" type. It is used both as a wait condition (passed to wait()/waitAny()) and as a target for element actions (passed to click(), fill(), …).

Not every field is meaningful in every context: - selector / jsExpression  → both wait and actions - backendNodeId            → actions only (wait rejects it) - visible / steadyMs       → wait only (silently ignored by actions) - x / y                    → actions only (wait rejects it) - frameId                  → both, may be overridden by call-level opts.inFrame

Use the css() / js() / node() / at() constructors instead of building this class by hand.

Modifiers (visible, steady, inFrame, inAllFrames) are immutable — they return a new Locator and leave the original untouched, so it's safe to share a base locator across calls.

### `inAllFrames(): Locator`

*method on `Locator`*

Scopes this Locator to every frame.

Equivalent to `.inFrame(AllFrames)`. Use this when an element might appear inside any of several frames and you do not want to enumerate them.

**Returns:** `Locator` — a new Locator scoped to all frames

```ts
await browser.wait(css("button.consent").inAllFrames());
```

### `inFrame(frameId: string): Locator`

*method on `Locator`*

Scopes this Locator to a specific frameId.

Use the frameId from a previous result or CloudBrowser.getPages to target elements inside a known iframe.

**Parameters:**
- `frameId` (`string`) — id of the frame to scope to

**Returns:** `Locator` — a new Locator scoped to that frame

```ts
const pages = await browser.getPages();
const iframeId = pages[0].frameTree.children[0].frameId;
await browser.click(css("button").inFrame(iframeId));
```

### `steady(ms: number): Locator`

*method on `Locator`*

Requires the element to keep a stable position and size for at least `ms` milliseconds before the wait matches.

Pass 0 to disable the default `DefaultSteadyMs` (500). Has no effect for js() expressions that return a non-Element value, nor when used as an action target.

**Parameters:**
- `ms` (`number`) — steady-state duration in milliseconds; 0 disables

**Returns:** `Locator` — a new Locator with the override applied

```ts
await browser.wait(css(".banner").steady(0));
```

### `visible(v: boolean): Locator`

*method on `Locator`*

Enforces or disables the visibility check for this Locator's wait condition.

Pass `false` to opt out of the default `DefaultVisible` (true). Has no effect when used as an action target — actions never check visibility before dispatching.

**Parameters:**
- `v` (`boolean`) — true to require visibility, false to skip the check

**Returns:** `Locator` — a new Locator with the override applied

```ts
await browser.wait(css("#hidden").visible(false));
```

## BrowserConfig

Configuration for renting a browser session. Create with the required parameters, then chain optional setters.

### `withCountryCode(countryCode: string): BrowserConfig`

*method on `BrowserConfig`*

Sets the geo-IP country code for the rented session.

Drives both the assigned exit-IP region and the locale defaults (Accept-Language, timezone fallback) when those are not overridden separately.

**Parameters:**
- `countryCode` (`string`) — ISO-3166 country code (e.g. "DE", "US")

**Returns:** `BrowserConfig` — this BrowserConfig for chaining

```ts
new BrowserConfig(apiKey, 600, "", 0, "", "").withCountryCode("DE");
```

### `withFingerprint(fingerprint: string): BrowserConfig`

*method on `BrowserConfig`*

Pins a specific browser fingerprint id for the session.

When omitted the server picks a fingerprint based on the country code. Pass a known id (e.g. one returned by a previous rental) to keep fingerprints stable across sessions.

**Parameters:**
- `fingerprint` (`string`) — server-side fingerprint id

**Returns:** `BrowserConfig` — this BrowserConfig for chaining

```ts
new BrowserConfig(apiKey, 600, "", 0, "", "").withFingerprint("fp_abc123");
```

### `withTimezone(timezone: string): BrowserConfig`

*method on `BrowserConfig`*

Sets the IANA timezone for the rented session.

**Parameters:**
- `timezone` (`string`) — IANA timezone (e.g. "Europe/Berlin")

**Returns:** `BrowserConfig` — this BrowserConfig for chaining

```ts
new BrowserConfig(apiKey, 600, "", 0, "", "").withTimezone("Europe/Berlin");
```

## Functions

### `at(x: number, y: number): Locator`

*function*

at targets viewport coordinates instead of an element.

Useful for clicking inside a canvas, hovering decorative regions, or dispatching events at synthetic positions. Action-only — using it in wait() throws at send time. Note that only click and moveTo accept at; scrollTo, drag, fill and select all require a real element.

**Parameters:**
- `x` (`number`) — viewport-relative x in CSS pixels
- `y` (`number`) — viewport-relative y in CSS pixels

**Returns:** `Locator` — Locator usable only as an action target

```ts
// Click at canvas-relative coordinates.
await browser.click(at(120, 240));
```

### `createWebSocketBrowser(wsUrl: string, sessionId: string, apiKey: string, fingerprint: string): CloudBrowser`

*function*

Attaches a CloudBrowser to an existing session over a raw WebSocket transport.

Use this from a browser context: the WebSocket transport framing is defined by WebSocketTransport and is served directly by the browserscale session host. The session must already exist server-side; unlike rentBrowser this does not call the rent API. Closing the returned handle (via CloudBrowser.stopBrowser) only closes the transport — the rental stays alive.

**Parameters:**
- `wsUrl` (`string`) — WebSocket URL (ws:// or wss://) of the session host
- `sessionId` (`string`) — id of the existing session
- `apiKey` (`string`) — API key authorizing access to the session
- `fingerprint` (`string`) — browser fingerprint id; empty if unknown

**Returns:** `CloudBrowser` — CloudBrowser attached to the existing session

```ts
const browser = createWebSocketBrowser(
"wss://session-abc.browserscale.example.com/ws",
sessionId,
apiKey,
);
await browser.navigate("https://example.com");
```

### `css(selector: string): Locator`

*function*

css waits for / targets an element matching the given CSS selector.

When used in CloudBrowser.wait/CloudBrowser.waitAny, the returned Locator carries the SDK defaults `DefaultVisible` (true) and `DefaultSteadyMs` (500). Override per call with `.visible(false)` / `.steady(ms)` (use `.steady(0)` to disable the steady check).

When used as an action target (click, fill, …) the visible/steady fields are ignored — there are no corresponding fields on the action requests.

**Parameters:**
- `selector` (`string`) — CSS selector matching the element

**Returns:** `Locator` — Locator usable as a wait condition or as an action target

```ts
// As a wait condition.
await browser.wait(css("button.submit"));
// As an action target.
await browser.click(css("button.submit"));
```

### `js(expression: string): Locator`

*function*

js waits for / targets the result of a JavaScript expression.

Same wait defaults as css (`DefaultVisible`=true, `DefaultSteadyMs`=500); these only apply when the expression returns a DOM Element. For non-Element truthy values (boolean, string, number, plain object) both fields are no-ops and the condition matches as soon as the value is truthy. Use `.visible(false)` / `.steady(0)` to opt out.

**Parameters:**
- `expression` (`string`) — JavaScript expression evaluated in the target frame

**Returns:** `Locator` — Locator usable as a wait condition or as an action target

```ts
await browser.wait(js("window.__ready === true"));
```

### `node(backendNodeId: number): Locator`

*function*

node targets an element by its DevTools backendNodeId.

Use this when you already have a backendNodeId from a previous result (e.g. a wait or evaluate result) and want to act on the exact same element without re-resolving by selector. Action-only — using it in wait() throws at send time.

**Parameters:**
- `backendNodeId` (`number`) — DevTools backendNodeId of the target element

**Returns:** `Locator` — Locator usable only as an action target

```ts
const r = await browser.click(css("button.open"));
await browser.click(node(r.backendNodeId));
```

### `rentBrowser(config: BrowserConfig): CloudBrowser`

*function*

Rents a new browser session and returns a connected handle.

Calls the browserscale rent endpoint with the supplied BrowserConfig, opens a gRPC connection to the assigned session host, and returns a ready-to-use CloudBrowser. Closing the returned handle (via CloudBrowser.stopBrowser) also releases the rental.

**Parameters:**
- `config` (`BrowserConfig`) — rental parameters

**Returns:** `CloudBrowser` — CloudBrowser ready to drive the rented session

**Throws:**
- `UNKNOWN_ERROR` — the rent API rejected the request or the gRPC connection could not be established

```ts
const cfg = new BrowserConfig("sk_…", 600, "", 0, "", "");
const browser = await rentBrowser(cfg);
try {
await browser.navigate("https://example.com");
} finally {
await browser.stopBrowser();
}
```

### `setApiEndpoint(endpoint: string): void`

*function*

Overrides the HTTP rent/stop endpoint.

Defaults to `https://api.browserscale.cloud`. Call this before any rentBrowser / stopBrowser call if you need to point at a private browserscale deployment.

**Parameters:**
- `endpoint` (`string`) — base URL of the rent/stop service, with no trailing slash

**Returns:** `void`

```ts
setApiEndpoint("https://browserscale.internal.example.com");
```

### `stopBrowser(apiKey: string, sessionId: string): void`

*function*

Releases a session without needing a CloudBrowser handle.

Useful when a session id was persisted across processes and the rental outlived the original handle. Only calls the rent stop endpoint; there is no gRPC connection to close in this form.

**Parameters:**
- `apiKey` (`string`) — API key the session was rented with
- `sessionId` (`string`) — id of the session to release

**Returns:** `void`

**Throws:**
- `UNKNOWN_ERROR` — the stop API rejected the request

```ts
await stopBrowser(apiKey, sessionId);
```

## Types

### `BrowserScaleError`

*class*

BrowserScaleError is the single error type thrown by all SDK methods. It wraps either: - a client-side validation failure (bad locator, missing patterns, …) - a server-side gRPC error (Connect's ConnectError, available as `cause`)

Catch it with `instanceof BrowserScaleError`:

try { await browser.click(css("#btn")); } catch (e) { if (e instanceof BrowserScaleError) { ... } }

### `Button`

*type alias*

`type Button = "left" | "right" | "middle"`

Mouse button used by CloudBrowser.click.

### `ClickAction`

*type alias*

`type ClickAction = "click" | "press" | "release"`

Mouse phase performed by CloudBrowser.click.

### `ClickOpts`

*interface*

Optional customization for CloudBrowser.click. All fields are optional; missing or zero values mean "use the server default".

**Fields:**
- `inFrame` (`string`) *(optional)* — Override the locator's frame. Omit to use the locator's own Locator.inFrame (or the main frame). Pass a specific `frameId` or AllFrames to search elsewhere.
- `button` (`Button`) *(optional)* — Mouse button. Default `"left"`.
- `clickCount` (`number`) *(optional)* — `1` = single click (default), `2` = double-click.
- `action` (`ClickAction`) *(optional)* — `"click"` (default) performs a full mouseDown+mouseUp. `"press"` only dispatches mouseDown, `"release"` only mouseUp at the current cursor position.

### `CookieParam`

*interface*

CookieParam is one entry returned by getCookies() or passed to setCookies().

**Fields:**
- `name` (`string`)
- `value` (`string`)
- `url` (`string`) *(optional)*
- `domain` (`string`)
- `path` (`string`)
- `secure` (`boolean`) *(optional)*
- `httpOnly` (`boolean`) *(optional)*
- `sameSite` (`string`) *(optional)*
- `expires` (`number`) *(optional)*
- `priority` (`string`) *(optional)*
- `sourceScheme` (`string`) *(optional)*
- `sourcePort` (`number`) *(optional)*
- `partitionKey` (`CookiePartitionKey`) *(optional)*

### `CookiePartitionKey`

*interface*

Partition metadata for partitioned cookies (CHIPS).

**Fields:**
- `topLevelSite` (`string`)
- `hasCrossSiteAncestor` (`boolean`)

### `DOMResult`

*interface*

DOMResult is the full-tree DOM snapshot returned by CloudBrowser.getDOM, plus its sha256:8 hash for cheap change detection.

**Fields:**
- `hash` (`string`)
- `dom` (`string`)

### `DragResult`

*interface*

DragResult is the outcome of a CloudBrowser.drag gesture: the resolved source element and the start/end coordinates of the performed drag.

**Fields:**
- `success` (`boolean`)
- `frameId` (`string`)
- `backendNodeId` (`number`)
- `startX` (`number`)
- `startY` (`number`)
- `endX` (`number`)
- `endY` (`number`)

### `ElementResult`

*interface*

ElementResult is the outcome of an element interaction such as CloudBrowser.click, CloudBrowser.fill or CloudBrowser.scrollTo: the resolved element plus the root-relative coordinates the action was performed at.

**Fields:**
- `success` (`boolean`)
- `frameId` (`string`)
- `backendNodeId` (`number`)
- `isVisible` (`boolean`)
- `bounds` (`Rect`)
- `rootX` (`number`)
- `rootY` (`number`)

### `EvaluateResult`

*interface*

EvaluateResult carries the outcome of a JS evaluate call.

If the expression returned a DOM element, backendNodeId/isVisible/bounds are populated and value is null. Otherwise value holds the parsed JSON value (string/number/boolean/array/object/null). On parse failure value falls back to the raw server string so the caller is never empty-handed.

The optional generic T types the .value field for convenience — this is purely a TS hint, not a runtime guarantee.

**Fields:**
- `value` (`T`)
- `backendNodeId` (`number`)
- `isVisible` (`boolean`)
- `bounds` (`Rect`)

### `FillOpts`

*interface*

Optional customization for CloudBrowser.fill. All fields are optional; missing values mean "use the server default".

**Fields:**
- `inFrame` (`string`) *(optional)* — Override the locator's frame. Omit to use the locator's own Locator.inFrame (or the main frame). Pass a specific `frameId` or AllFrames to search elsewhere.
- `clearFirst` (`boolean`) *(optional)* — `true` wipes the field's existing content with Ctrl+A, Delete before typing. Default (`false`) appends to whatever is already in the field.

### `FrameInfo`

*interface*

FrameInfo describes a single frame within a page's frame tree.

**Fields:**
- `frameId` (`string`)
- `url` (`string`)
- `isOOPIF` (`boolean`)
- `hasJSContext` (`boolean`)
- `isLoading` (`boolean`)
- `isVisible` (`boolean`)
- `absoluteRect` (`Rect`)
- `relativeRect` (`Rect`)
- `children` (`FrameInfo[]`)

### `GetDOMOpts`

*interface*

Depth used by getDOM(). 0 = whole tree (default), >0 = limited depth.

**Fields:**
- `depth` (`number`) *(optional)*

### `GetObservationOpts`

*interface*

Optional customization for CloudBrowser.getObservation.

**Fields:**
- `maxElementsPerFrame` (`number`) *(optional)* — Default 200.
- `maxTextLength` (`number`) *(optional)* — Default 200.

### `Header`

*interface*

Header is a single HTTP header (name/value pair) on an intercepted request or response.

**Fields:**
- `name` (`string`)
- `value` (`string`)

### `HeaderModification`

*interface*

HeaderModification is one entry passed to CloudBrowser.modifyRequest. Write it as a plain object literal.

**Fields:**
- `action` (`HeaderModificationAction`) — `"add"` inserts a new header, `"edit"` replaces an existing header's value, `"remove"` drops the header.
- `name` (`string`) — Header name the action applies to.
- `value` (`string`) *(optional)* — Header value for add/edit; ignored for remove.
- `before` (`string`) *(optional)* — Positions an `"add"` immediately before the named existing header; otherwise the header is appended at the end. Ignored for edit/remove.
- `after` (`string`) *(optional)* — Positions an `"add"` immediately after the named existing header. Mirror of `before`; ignored for edit/remove.

### `HeaderModificationAction`

*type alias*

`type HeaderModificationAction = "add" | "edit" | "remove"`

Action verb for a HeaderModification.

### `InspectResult`

*interface*

InspectResult describes the topmost element hit at viewport-relative (x, y). backendNodeId === 0 means nothing was found at that position.

**Fields:**
- `backendNodeId` (`number`)
- `frameId` (`string`)
- `tagName` (`string`)
- `textContent` (`string`)
- `isVisible` (`boolean`)
- `bounds` (`Rect`)

### `InterceptedRequest`

*interface*

InterceptedRequest describes an outgoing request captured by CloudBrowser.waitForAnyRequest.

**Fields:**
- `method` (`string`)
- `url` (`string`)
- `headers` (`Header[]`)
- `body` (`string`)
- `resourceType` (`string`)

### `InterceptedResponse`

*interface*

InterceptedResponse describes a network response captured by CloudBrowser.waitForAnyResponse.

**Fields:**
- `url` (`string`)
- `statusCode` (`number`)
- `headers` (`Header[]`)
- `body` (`string`)

### `LoadHTMLOpts`

*interface*

Optional customization for CloudBrowser.loadHTML.

**Fields:**
- `headers` (`{ name: string; value: string }[]`) *(optional)* — Extra headers to attach to the synthetic response.
- `statusCode` (`number`) *(optional)* — Default 200.

### `NavigateOpts`

*interface*

Optional customization for CloudBrowser.navigate.

**Fields:**
- `timeoutMs` (`number`) *(optional)* — Default 30 000 ms.
- `waitUntil` (`WaitUntil`) *(optional)* — Default "load".

### `NavigateResult`

*interface*

NavigateResult reports where a CloudBrowser.navigate call ended up after redirects.

**Fields:**
- `frameId` (`string`)
- `url` (`string`)

### `ObservationResult`

*interface*

ObservationResult is the compact page snapshot returned by CloudBrowser.getObservation — the visible, interactive elements rendered as prompt-friendly text and as JSON.

**Fields:**
- `text` (`string`)
- `json` (`string`)

### `PageInfo`

*interface*

PageInfo describes an open page (tab or popup) inside a browser context.

**Fields:**
- `pageId` (`string`)
- `browserContextId` (`string`)
- `url` (`string`)
- `title` (`string`)
- `viewport` (`Rect`)
- `frameTree` (`FrameInfo`)

### `ReadCanvasOpts`

*interface*

Optional customization for CloudBrowser.readCanvas.

**Fields:**
- `inFrame` (`string`) *(optional)* — Override the locator's frame. Omit to use the locator's own Locator.inFrame (or the main frame). Pass a specific `frameId` or AllFrames to search elsewhere.
- `format` (`"png" | "jpeg" | "webp" | "rgba"`) *(optional)* — Output encoding: `"png"` (default), `"jpeg"`, `"webp"`, or `"rgba"` for the raw unpremultiplied RGBA pixel buffer.
- `quality` (`number`) *(optional)* — Encode quality 0-100 for "jpeg"/"webp" (ignored otherwise). Default 90.
- `sx` (`number`) *(optional)* — Optional sub-rectangle in canvas pixels (mirrors `getImageData(sx, sy, sw, sh)`). The full canvas is read when `sw`/`sh` are omitted or <= 0.
- `sy` (`number`) *(optional)*
- `sw` (`number`) *(optional)*
- `sh` (`number`) *(optional)*

### `ReadCanvasResult`

*interface*

ReadCanvasResult is the pixel readback of a <canvas>, returned by CloudBrowser.readCanvas. `dataBase64` holds the encoded image bytes (PNG by default) or the raw RGBA buffer when `format` is `"rgba"`. `originClean` reports whether the canvas was untainted (informational — the read succeeds either way).

**Fields:**
- `success` (`boolean`)
- `frameId` (`string`)
- `backendNodeId` (`number`)
- `dataBase64` (`string`)
- `width` (`number`)
- `height` (`number`)
- `originClean` (`boolean`)

### `Rect`

*interface*

Rect describes a position and size in CSS pixels.

**Fields:**
- `x` (`number`)
- `y` (`number`)
- `width` (`number`)
- `height` (`number`)

### `RentResponse`

*interface*

RentResponse is the result of renting a browser via the REST API.

**Fields:**
- `sessionId` (`string`)
- `grpcUrl` (`string`)
- `countryCode` (`string`)
- `timezone` (`string`)
- `acceptLanguage` (`string`)
- `fingerprint` (`string`)

### `RequestPattern`

*interface*

RequestPattern matches a URL pattern in waitForAnyRequest/Response. Set abort to true to drop the request with an empty 200 response instead of letting it through to the network.

**Fields:**
- `url` (`string`)
- `abort` (`boolean`) *(optional)* — Default false.

### `ScreenshotOpts`

*interface*

Optional customization for CloudBrowser.screenshot.

**Fields:**
- `format` (`"png" | "jpeg" | "webp"`) *(optional)* — Image format: "png" (default), "jpeg", or "webp".
- `quality` (`number`) *(optional)* — Encode quality 0-100 for "jpeg"/"webp" (ignored for "png"). Default 90.

### `ScreenshotResult`

*interface*

ScreenshotResult is a single captured image of the page, returned by CloudBrowser.screenshot. `dataBase64` holds the encoded image bytes (PNG by default); `width` and `height` are in physical pixels.

**Fields:**
- `dataBase64` (`string`)
- `width` (`number`)
- `height` (`number`)

### `SelectOptionResult`

*interface*

SelectOptionResult reports which option a selectByXxx call ended up selecting.

**Fields:**
- `selectedIndex` (`number`)
- `selectedValue` (`string`)
- `selectedText` (`string`)

### `SelectOpts`

*interface*

Optional customization for CloudBrowser.selectByIndex, CloudBrowser.selectByValue and CloudBrowser.selectByText.

**Fields:**
- `inFrame` (`string`) *(optional)* — Override the locator's frame. Omit to use the locator's own Locator.inFrame (or the main frame). Pass a specific `frameId` or AllFrames to search elsewhere.
- `fireEvents` (`boolean`) *(optional)* — `false` suppresses change/input events. Default (`true`) fires the standard events after the selection.

### `StorageItem`

*interface*

StorageItem is a single localStorage key/value pair.

**Fields:**
- `key` (`string`)
- `value` (`string`)

### `StorageOriginEntry`

*interface*

StorageOriginEntry groups the localStorage entries of one origin (e.g. "https://example.com"). getStorage() returns these and setStorage() accepts the same shape, so a dump can be fed back verbatim.

**Fields:**
- `origin` (`string`)
- `items` (`StorageItem[]`)

### `WaitOpts`

*interface*

Optional customization for CloudBrowser.wait / CloudBrowser.waitForAny.

**Fields:**
- `timeoutMs` (`number`) *(optional)* — Default `DefaultWaitTimeoutMs` (30 000 ms).

### `WaitResult`

*interface*

WaitResult is the outcome of a CloudBrowser.wait / CloudBrowser.waitForAny call: which condition matched (index, in argument order) and where the matched element lives.

**Fields:**
- `index` (`number`)
- `frameId` (`string`)
- `backendNodeId` (`number`)
- `isVisible` (`boolean`)
- `bounds` (`Rect`)

### `WaitUntil`

*type alias*

`type WaitUntil = "load" | "domcontentloaded" | "networkidle"`

Lifecycle event CloudBrowser.navigate waits for before returning.
