Solari

Browsers

@solarisdk/browser: classes Solari and BrowserSession. See the TypeScript SDK hub for install and configuration.

npm install @solarisdk/browser

Contents

Solari

The browser client. Creates sessions and manages profiles.

Properties

  • sessions SessionsResource: session create/release/replay calls.
  • profiles ProfilesResource: stored browser profile CRUD.

Constructors

new Solari()

new Solari(opts: SolariOptions): Solari

Creates a client. Does not open any connection.

Parameters:

Returns: Solari

Throws: SolariError if apiKey is empty or region is unknown.

Example:

import { Solari } from "@solarisdk/browser";

const solari = new Solari({ apiKey: process.env.SOLARI_API_KEY! });

Methods

launch()

launch(options?: LaunchOptions): Promise<BrowserSession>

Creates a session and connects to it. Returns a BrowserSession wrapping a real Playwright Browser.

Parameters:

Returns: Promise<BrowserSession>

Throws: the last connect error once attempts are spent; SolariError with code BrowserUnhealthy if the probe fails.

Example:

const browser = await solari.launch({ stealth: true, retries: 2 });
const page = await browser.newPage();
await page.goto("https://example.com");
await browser.close();
retries only re-runs launch
Each retry calls sessions.create() again and sleeps 100ms × attempt, and only for a BrowserUnhealthy probe failure or a transient connect error. Anything else throws on the first attempt. A failure from sessions.create() itself is not retried here; it propagates immediately under its own HTTP retry policy. probe defaults to true when retries > 0.

close()

close(): Promise<void>

Stops the client’s local proxy. Call once you are done with the client. It does not release live sessions.

Returns: Promise<void>

Example:

await browser.close();  // release the session first
await solari.close();   // then shut the client down

request()

request(method: string, path: string, body?: unknown): Promise<Response>

Low-level authenticated call against the API, with the client’s retry and timeout policy applied. An escape hatch for endpoints the SDK does not wrap.

Parameters:

  • method string: HTTP verb.
  • path string: path appended to baseUrl, e.g. /sessions.
  • body? unknown: JSON-serialized when present.

Returns: Promise<Response>: the raw fetch response. Non-2xx is returned, not thrown.

Throws: SolariError only when all attempts are exhausted.

Example:

const res = await solari.request("GET", "/profiles");
console.log(res.status, await res.json());
Retry policy
Only 502, 503, and 504 (and transport errors) are retried, with a fixed backoffMs sleep, not exponential. maxAttempts: 2 means one retry. timeoutMs is per attempt. 429 is not retried.

sessions.create()

sessions.create(options?: CreateSessionOptions): Promise<Session>

Acquires a browser session without connecting. Use when you want to drive the endpoints yourself; otherwise use launch().

Parameters:

  • options? CreateSessionOptions: profileId, recording, stealth, captcha, webBotAuth, proxy.

Returns: Promise<Session>

Throws: SolariError carrying status and the server code (e.g. FeatureRequiresPlan, ConcurrencyLimitExceeded).

Example:

const session = await solari.sessions.create({ stealth: true, proxy: "us" });
const browser = await chromium.connect(session.wsEndpoint);
captcha and proxy require stealth
captcha: true and any proxy request are rejected unless stealth: true is also set.

sessions.release()

sessions.release(id: string): void

Fire-and-forget release. Returns immediately; failures are logged, not thrown. Use releaseAndWait() when you need confirmation.

Parameters:

  • id string: session id.

Returns: void

Example:

solari.sessions.release(session.id);

sessions.releaseAndWait()

sessions.releaseAndWait(id: string): Promise<void>

Releases a session and waits for the server to confirm.

Parameters:

  • id string: session id.

Returns: Promise<void>

Throws: SolariError on any error status except a bare 404, including a 404 carrying code: "InvalidSessionId".

Example:

await solari.sessions.releaseAndWait(session.id);
// the replay is available ~1-3s later
Not every 404 is success
The gateway acks 204 for any authentic session id, even one already gone, so a 404 does not mean “already released”. A 404 tagged InvalidSessionId means the id was refused (malformed, forged, or another org’s) and nothing was released; that throws. A bare 404 with no code is tolerated for older gateways.

sessions.getReplayUrl()

sessions.getReplayUrl(id: string): Promise<ReplayUrl>

Returns a presigned URL for a recorded session’s replay. Requires recording: true at create time.

Parameters:

  • id string: session id.

Returns: Promise<ReplayUrl>: url, expiresInSeconds, contentEncoding.

Throws: SolariError if the replay is not ready or does not exist.

Example:

const { url, expiresInSeconds } = await solari.sessions.getReplayUrl(id);

sessions.downloadReplay()

sessions.downloadReplay(id: string): Promise<Uint8Array>

Resolves the replay URL and downloads the NDJSON bytes in one call.

Parameters:

  • id string: session id.

Returns: Promise<Uint8Array>: gzipped NDJSON.

Throws: SolariError if the download fails.

Example:

import { writeFile } from "node:fs/promises";

const bytes = await solari.sessions.downloadReplay(id);
await writeFile("replay.ndjson.gz", bytes);

profiles.create()

profiles.create(opts: { name: string }): Promise<Profile>

Creates an empty profile. Attach it to a session with profileId to persist cookies and localStorage.

Parameters:

  • opts.name string: human-facing name.

Returns: Promise<Profile>

Throws: SolariError with the server code on failure.

Example:

const profile = await solari.profiles.create({ name: "logged-in" });
const browser = await solari.launch({ profileId: profile.id });

profiles.list()

profiles.list(): Promise<Profile[]>

Lists every profile on the account.

Returns: Promise<Profile[]>

Example:

for (const p of await solari.profiles.list()) console.log(p.id, p.name);

profiles.save()

profiles.save(id: string, storageState: StorageState): Promise<{ version: number; sizeBytes: number }>

Overwrites a profile with the given storage state. Pair it with Playwright’s context.storageState() to snapshot a logged-in session.

Parameters:

  • id string: profile id.
  • storageState StorageState: cookies + origins.

Returns: Promise<{ version: number; sizeBytes: number }>

Throws: SolariError on a non-2xx response.

Example:

const state = await browser.contexts()[0].storageState();
await solari.profiles.save(profile.id, state);

profiles.delete()

profiles.delete(id: string): Promise<void>

Deletes a profile. Idempotent, so a 404 resolves successfully.

Parameters:

  • id string: profile id.

Returns: Promise<void>

Example:

await solari.profiles.delete(profile.id);

BrowserSession

A live browser and its session. Returned by launch(); not constructed directly.

Properties

  • session Session: the raw session record.
  • id string: session id.
  • expiresAt string: ISO 8601 UTC deadline; the session auto-releases then.
  • proxy ResolvedProxyConfig | undefined: resolved proxy confirmation (country, tier, timezone — no credentials), when one was requested.
  • wsEndpoint string: Playwright wire-protocol endpoint (loopback-wrapped).
  • cdpEndpoint string: raw CDP endpoint (loopback-wrapped).
  • raw Browser: the underlying Playwright Browser, for APIs this wrapper does not surface.
Endpoints are loopback URLs
wsEndpoint and cdpEndpoint point at a LocalProxy on 127.0.0.1 that the TypeScript SDK runs in-process. They are only reachable from this process and only while the client is alive. Do not persist them or hand them to another machine. Other language SDKs return the upstream URL directly.

Methods

isConnected()

isConnected(): boolean

true while the browser connection is alive and close() has not been called.

Returns: boolean

Example:

if (!browser.isConnected()) throw new Error("session dropped");

version()

version(): string

The browser version string.

Returns: string, e.g. "Chromium/130.0.6723.31".

Example:

console.log(browser.version());

contexts()

contexts(): BrowserContext[]

All open contexts. Sessions ship with a default context at contexts()[0]. Use it rather than opening a new one.

Returns: BrowserContext[]

Example:

const ctx = browser.contexts()[0];
const page = await ctx.newPage();

newContext()

newContext(options?: BrowserContextOptions): Promise<BrowserContext>

Opens a fresh context. Most callers want contexts()[0] instead. A new context does not inherit the session’s profile or stealth setup.

Parameters:

  • options? BrowserContextOptions: Playwright context options.

Returns: Promise<BrowserContext>

Example:

const ctx = await browser.newContext({ locale: "en-GB" });

newPage()

newPage(): Promise<Page>

Opens a fresh page in a new context.

Returns: Promise<Page>

Example:

const page = await browser.newPage();
await page.goto("https://example.com");

close()

close(): Promise<void>

Closes the browser and releases the session, waiting for confirmation. Idempotent.

Returns: Promise<void>

Throws: the browser-close error if one occurred, otherwise the release error.

Example:

try {
  const page = await browser.newPage();
  await page.goto("https://example.com");
} finally {
  await browser.close();
}

[Symbol.asyncDispose]()

[Symbol.asyncDispose](): Promise<void>

Calls close(). Enables await using on Node 22+.

Returns: Promise<void>

Example:

await using browser = await solari.launch({ stealth: true });
const page = await browser.newPage();
await page.goto("https://example.com");
// released automatically at scope exit

Types

SolariOptions

  • apiKey string: required.
  • region? SolariRegion: "us-west" (default). Ignored when baseUrl is set.
  • baseUrl? string: override the resolved region URL.
  • maxAttempts? number: default 2 (one retry).
  • backoffMs? number: default 500, fixed.
  • timeoutMs? number: default 90_000, per attempt.

CreateSessionOptions

  • profileId? string: attach a stored profile.
  • recording? boolean: record the session. Off by default.
  • stealth? boolean: enable the runtime stealth shim. Off by default.
  • captcha? boolean: managed captcha solving. Requires stealth.
  • webBotAuth? boolean: sign outbound requests for Cloudflare Web Bot Auth. Independent of stealth; inert unless Web Bot Auth is enabled for your account.
  • proxy? string | ProxyRequest | "off" | "smart": managed egress. Requires stealth.

LaunchOptions

Extends CreateSessionOptions.

  • retries? number: extra re-launch attempts. Default 0.
  • probe? boolean: probe the browser before returning. Defaults to true when retries > 0.
  • probeTimeoutMs? number: probe cap. Default 2000.

Session

  • id string: session id.
  • wsEndpoint string: loopback Playwright endpoint.
  • cdpEndpoint string: loopback CDP endpoint.
  • expiresAt string: ISO 8601 UTC deadline.
  • storageState? StorageState | null: tri-state. Absent = no profile attached; null = profile exists but is empty; an object = the profile’s state.
  • proxy? ResolvedProxyConfig: present only when a managed proxy was requested.

StorageState

  • cookies? Array: each with name, value, and optional domain, path, expires, httpOnly, secure, sameSite.
  • origins? Array: each with origin and optional localStorage entries.

ProxyRequest

  • country? string: ISO-3166-1 alpha-2, lowercase. Default "us".
  • tier? "residential" | "static" | "mobile": default "residential" (rotating).
  • asn? string: pin egress to an ASN.
  • session? string: sticky-session id (alnum + dash, ≤32 chars).
  • sessionDuration? number: sticky lifetime in minutes (1 to 30, default 10). Only with session.
  • state? string: US-only geo narrowing.
  • city? string: US-only city pin.
  • static? boolean: deprecated; use tier: "static".

ResolvedProxyConfig

Confirmation only. It carries no credentials by design — egress is applied server-side, so you never dial the proxy yourself and never receive its address or account.

  • timezoneId string: timezone matching the egress country. Pass it to Playwright’s newContext as timezoneId if you build your own context and want Intl/Date to line up.
  • country string: egress country actually resolved (lowercase ISO code).
  • tier? "residential" | "static" | "mobile": the tier that actually served the session. A mobile request can degrade to residential, so compare against what you asked for.

Profile

  • id string: profile id.
  • name string: human-facing name.

ReplayUrl

  • url string: presigned download URL.
  • expiresInSeconds number: URL lifetime.
  • contentEncoding string: e.g. "gzip".

SolariError

Extends Error. Every failure the browser SDK throws.

  • status? number: HTTP status, when the error came from a response.
  • cause? unknown: the underlying error.
  • code? SolariErrorCode | string: FeatureRequiresPlan, ConcurrencyLimitExceeded, PlanLimitExceeded, BrowserUnhealthy, or InvalidSessionId. Unknown codes pass through as plain strings.
import { SolariError } from "@solarisdk/browser";

try {
  await solari.launch({ stealth: true, captcha: true });
} catch (e) {
  if (e instanceof SolariError && e.code === "FeatureRequiresPlan") {
    console.error("upgrade required:", e.status);
  }
}