@solarisdk/browser
v0.5.4
Published
Official client SDK for Solari Browser.
Maintainers
Readme
@solarisdk/browser
Official client SDK for Solari Browser — managed, stealth-hardened browsers as an API.
Feature coverage across all five languages is on the SDK feature matrix.
Install
npm install @solarisdk/browserQuickstart
import { Solari } from "@solarisdk/browser";
const solari = new Solari({
apiKey: process.env.SOLARI_API_KEY!,
// region: "us-west", // default — additional regions coming soon
});
const browser = await solari.launch({ stealth: true });
const page = await browser.newPage();
await page.goto("https://example.com");
console.log(await page.title());
await browser.close();Or with explicit resource management (compiled TypeScript 5.2+, or uncompiled on Node 24+; Node 22 rejects the syntax):
await using browser = await solari.launch({ stealth: true });
const page = await browser.newPage();
await page.goto("https://example.com");Sessions
Drive a session yourself instead of using launch():
const session = await solari.sessions.create({ stealth: true });
// connect via Playwright wire protocol
import { chromium } from "patchright-core";
const browser = await chromium.connect(session.wsEndpoint);
// ...
await browser.close();
await solari.sessions.releaseAndWait(session.id);Or via raw CDP (Puppeteer, browser-use, playwright-core):
const session = await solari.sessions.create();
const browser = await chromium.connectOverCDP(session.cdpEndpoint);Get and list sessions
const view = await solari.sessions.get(session.id); // status, kind, userMetadata, endpoints while active
let page = await solari.sessions.list({ limit: 50, metadata: { job: "nightly-42" } });
// page.sessions, page.nextCursor (absent on the last page)list never returns the websocket endpoints (they are bearer capabilities): call get
for a single session's. Pass page.nextCursor as cursor for the next page.
Every REST error carries the gateway's code when it sends one, so you can branch on
it whichever call failed: e.code === "ReplayPending" (a replay still uploading; wait and
call again), "SessionNotFound", "InvalidSessionId", "PlanLimitExceeded" and so on.
Timeout, keep-alive and metadata
const session = await solari.sessions.create({
sessionTimeoutMs: 600_000, // end earlier than the plan maximum, in ms (at least 60 000)
keepAlive: true, // keep it if the client drops: held until sessionTimeoutMs after creation (1 h if unset) or release
userMetadata: { job: "nightly-42" }, // stored with the session
});Every duration option is in milliseconds. sessionTimeoutMs is rounded up to whole
seconds for the gateway (1001 ms is 2 s); the gateway refuses anything under 60 s;
zero or unset leaves the plan maximum, and a negative, NaN, Infinity or
fractional value throws before any request. launch() accepts the same options.
Edit a profile
solari.profiles.edit(id) opens the profile editor, where a person signs in once and the result is saved into the profile. It returns a handle; give its url to whoever should sign in, or open it yourself.
const editor = await solari.profiles.edit("prof_123", { reason: "Sign in to Acme" });
console.log(editor.url); // open this in a browser
await editor.waitForSave({ timeoutMs: 10 * 60_000 }); // the person clicks Save in the editor page
// or drive it from code once they are done:
await editor.saveAndClose(); // save and close the editor
await editor.exit(); // close it WITHOUT saving
console.log(await editor.status());The link works for 30 minutes and the editor starts when the person clicks "Open editor" on the page. After that the editor stops by itself and these calls fail with a clear 404 (link expired) or 409 (editor not running). There is no save-and-keep-editing: saveAndClose() saves and closes.
Regions
Solari runs in us-west (N. California) today, with additional regions
coming soon. Sessions, replays, and profile storage are colocated with
the region you pick.
const solari = new Solari({ apiKey, region: "us-west" });For staging or self-hosted gateways, pass baseUrl instead of region.
Managed proxies
Proxy bandwidth is billed per GB, and pages heavy in video and images can cost tens of megabytes
per session. blockHeavy (off by default) skips them:
await solari.launch({
stealth: true,
proxy: "us",
blockHeavy: true, // video/audio + images over 100 KB; or { fonts: true, imageMaxBytes: 50_000 }
});Blocked requests fail inside the browser, so nothing is fetched and nothing leaves through a different route; the page sees an ordinary failed load and the blocked image or video does not appear. Images that declare no size, and requests made by service workers or cross-origin iframes, are not covered. Replays still play: they load media from the original site.
await solari.launch({
stealth: true,
proxy: { country: "us", tier: "mobile" },
});For targets whose stealth posture is unknown a priori, proxy: "smart"
runs an escalation ladder (rotating residential → direct → mobile → fresh
residential → residential GB) and swaps the egress in-place when a block
page is detected mid-session — no client-side retry required.
await solari.launch({ stealth: true, proxy: "smart" });See the Solari docs for the full proxy, stealth, captcha, and profile reference.
Errors
import { SolariError } from "@solarisdk/browser";
try {
await solari.sessions.create({ stealth: true });
} catch (err) {
if (err instanceof SolariError && err.code === "FeatureRequiresPlan") {
// ...
}
}License
MIT
