@solarisdk/browserbase-compat 0.1.0 → 0.2.1

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/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ # Changelog
2
+
3
+ ## 0.2.1
4
+ - **Security: the base URL check accepts only the Solari API hosts.** It used to accept any `*.getsolari.com` host, including sandbox preview hosts (`<label>-<port>.preview.getsolari.com`) that serve customer code, so a `baseURL` / `SOLARI_BASE_URL` pointed there would send your `slr_` key to code running in a sandbox. Now only the Solari API hosts and loopback are accepted.
5
+
6
+ ## 0.2.0
7
+ - `timeout`, `keepAlive` and `userMetadata` are sent to Solari (they were warned about and ignored in 0.1.0). `userMetadata` comes back on `create`, `retrieve` and `list`, and `sessions.list({ q })` filters on it.
8
+ - **A base URL that does not point at Solari is now refused.** `baseURL` and `SOLARI_BASE_URL` must be `https` on `getsolari.com` (or a subdomain) or a loopback address; anything else throws `BrowserbaseError` ("Refusing to send a Solari API key to …"). In 0.1.0 such a URL was used as given, which could send your `slr_` key to another host.
9
+ - Errors use Browserbase's class names: `NotFoundError`, `RateLimitError`, `BadRequestError`, `AuthenticationError`, `PermissionDeniedError`, `ConflictError`, `UnprocessableEntityError`, `InternalServerError` (all `APIError` subclasses), `APIConnectionError` and `APIConnectionTimeoutError`. Network failures used to surface as the raw `fetch` error.
10
+ - A saved context's cookies are also present in the browser's default context (`browser.contexts()[0]`).
11
+ - `viewport` at create remains a documented gap (warns; `strict` throws).
12
+ - Added `NOTICE`; README links to docs.getsolari.com.
13
+
14
+ ## 0.1.0
15
+ - First release.
package/COMPAT.md CHANGED
@@ -22,7 +22,7 @@ Keep it private: the session id inside it is the credential.
22
22
  | | |
23
23
  |---|---|
24
24
  | Key | `apiKey` option, else `SOLARI_API_KEY`, else `BROWSERBASE_API_KEY`. **A key that does not start with `slr_` is never used or sent** (it is probably a Browserbase secret): it is skipped with a warning that does not print it, and if no `slr_` key remains the constructor throws. |
25
- | Endpoint | `baseURL` option or `SOLARI_BASE_URL`; default `https://api.getsolari.com`. |
25
+ | Endpoint | `baseURL` option or `SOLARI_BASE_URL`; default `https://api.getsolari.com`. Must be `https` on a Solari API host or loopback (preview and other `*.getsolari.com` hosts are refused: they serve customer code), otherwise the constructor throws `BrowserbaseError` ("Refusing to send a Solari API key to …"): the key is never sent to another host. |
26
26
  | `projectId` | Accepted and ignored: Solari keys belong to an organisation, there are no projects. |
27
27
 
28
28
  ## Works
@@ -35,16 +35,16 @@ Keep it private: the session id inside it is the credential.
35
35
  | `browserSettings.verified`, `advancedStealth` | Solari's stealth browser. |
36
36
  | `browserSettings.solveCaptchas: true` | Solari captcha solving (stealth browser, plan-gated). **Off unless you pass `true`**: Browserbase turns it on by default. |
37
37
  | `browserSettings.recordSession: true` | Session recording. **Off unless you pass `true`**: Browserbase records by default. Read it back with `sessions.recording.retrieve(id)` (rrweb events). |
38
- | `browserSettings.allowedDomains` | Egress allowlist (enterprise plan, stealth browser); a plan error comes back as an `APIError` with `code: "FeatureRequiresPlan"`. |
38
+ | `browserSettings.allowedDomains` | Egress allowlist (Team or Enterprise plan, stealth browser); a plan error comes back as an `APIError` with `code: "FeatureRequiresPlan"`. |
39
39
  | `browserSettings.context: { id, persist }`, `contexts.create/retrieve/delete` | A Context is a Solari **profile**. See [Contexts](#contexts). |
40
40
  | `sessions.debug(id, { expiresIn })` | A view-only live-view link (`debuggerUrl` and `debuggerFullscreenUrl` are the same link; `pages` is empty; `wsUrl` is the CDP URL). |
41
41
 
42
42
  ## Differs (documented, not errors)
43
43
  - **Recording is a DOM-event log, not video.** `sessions.recording.retrieve(id)` returns rrweb events once the replay has been uploaded (a few seconds after release; until then it throws `APIError` with `code: "ReplayPending"`). There is no HLS playlist and no MP4.
44
44
  - **Contexts store cookies and localStorage only** (Playwright `storageState`), not IndexedDB, service workers or autofill data.
45
- - **No choice of operating system or viewport at create time.** Set the viewport from your automation client.
46
- - **`keepAlive`**: a disconnected session is released about 45 s after its socket closes (reconnect with the same `connectUrl` inside that window). Sessions end at your plan's maximum session length.
47
- - **`timeout`, `userMetadata`, `keepAlive: true`, `viewport`, `blockAds: true`, `logSession: true`, `os`** are accepted with a one-line warning and have no effect. Pass `strict: true` to make them throw instead.
45
+ - **No choice of operating system or viewport at create time (known gap).** A `viewport` setting is accepted with a one-line warning and has no effect; set the viewport from your automation client (`page.setViewportSize`). `strict: true` makes it throw instead.
46
+ - **`timeout`, `keepAlive` and `userMetadata` work as in Browserbase.** `timeout` (seconds, at least 60) ends the session at that time, capped at your plan's maximum session length. `keepAlive: true` keeps the session after your client disconnects, until its `timeout` (default 1 hour), the plan maximum, or `sessions.update(..., REQUEST_RELEASE)`. `userMetadata` (up to 50 keys, 8 KB) is stored, returned by `retrieve`/`list`, and filterable with `sessions.list({ q: "user_metadata['key']:'value'" })` (conditions joined with `AND`).
47
+ - **`viewport`, `blockAds: true`, `logSession: true`, `os`** are accepted with a one-line warning and have no effect. Pass `strict: true` to make them throw instead.
48
48
 
49
49
  ## Contexts
50
50
  ```ts
@@ -59,7 +59,7 @@ const s2 = await bb.sessions.create({ browserSettings: { context: { id: ctx.id }
59
59
  const b2 = await chromium.connectOverCDP(s2.connectUrl);
60
60
  const page = await (await b2.newContext({ storageState: s2.storageState })).newPage();
61
61
  ```
62
- Two differences from Browserbase: Solari applies a saved context only to a browser context created *with* it (use `newContext({ storageState })`, not `browser.contexts()[0]`), and `persist: true` saves when you release **through the same client object** (it is remembered in memory).
62
+ Two differences from Browserbase: a saved context's **cookies** are also present in the default browser context (`browser.contexts()[0]`), but localStorage is applied only to a browser context created *with* the state (use `newContext({ storageState })` to get both), and `persist: true` saves when you release **through the same client object** (it is remembered in memory).
63
63
 
64
64
  ## Raises `SolariCompatUnsupported`
65
- Extensions (`extensionId`), the Selenium endpoint (`seleniumRemoteUrl` is always empty), downloads (`sessions.downloads`) and uploads (`sessions.uploads`) APIs, video replays (`sessions.replays`), session logs (`sessions.logs`), external/bring-your-own proxies, per-domain proxy rules (`domainPattern`), custom CA certificates, `ignoreCertificateErrors: true`, custom captcha selectors, `os: "mobile" | "tablet"`, a `region` other than `us-west-2` (or `us-west`), `sessions.list({ q })` (metadata queries), `contexts.update`, and the separate `projects`, `extensions`, `agents`, `fetchAPI` and `search` resources.
65
+ Extensions (`extensionId`), the Selenium endpoint (`seleniumRemoteUrl` is always empty), downloads (`sessions.downloads`) and uploads (`sessions.uploads`) APIs, video replays (`sessions.replays`), session logs (`sessions.logs`), external/bring-your-own proxies, per-domain proxy rules (`domainPattern`), custom CA certificates, `ignoreCertificateErrors: true`, custom captcha selectors, `os: "mobile" | "tablet"`, a `region` other than `us-west-2` (or `us-west`), `contexts.update`, and the separate `projects`, `extensions`, `agents`, `fetchAPI` and `search` resources.
package/NOTICE ADDED
@@ -0,0 +1,10 @@
1
+ Solari Browserbase compatibility SDK
2
+ Copyright (c) 2026 Solari. Licensed under the MIT License (see LICENSE).
3
+
4
+ This package implements a client whose method names and parameters follow the
5
+ publicly documented Browserbase SDK, so that code written for that SDK can run
6
+ against Solari Browser. It contains no Browserbase source code.
7
+
8
+ It is not affiliated with, endorsed by, or sponsored by Browserbase, Inc.
9
+ "Browserbase" is a trademark of its owner and is used only to describe API
10
+ compatibility.
package/README.md CHANGED
@@ -20,7 +20,9 @@ await bb.sessions.update(session.id, { status: "REQUEST_RELEASE" });
20
20
  ```
21
21
 
22
22
  Sessions, contexts, live view and recording are supported; extensions, downloads/uploads APIs, video replays,
23
- bring-your-own proxies and Selenium raise `SolariCompatUnsupported`. See [COMPAT.md](./COMPAT.md) for the full list
23
+ bring-your-own proxies and Selenium raise `SolariCompatUnsupported`. See [docs.getsolari.com/browserbase](https://docs.getsolari.com/browserbase) (and `COMPAT.md` in this package) for the full list
24
24
  and every difference. Requires Node 20+. No Playwright version pin is needed: `connectUrl` is a plain CDP endpoint.
25
25
 
26
- Licence: MIT.
26
+ Licence: MIT. See `NOTICE`.
27
+
28
+ This is an independent project. It is not affiliated with, endorsed by, or sponsored by Browserbase, Inc. "Browserbase" is a trademark of its owner and is used here only to describe API compatibility.
package/dist/client.d.ts CHANGED
@@ -15,6 +15,8 @@ export interface BrowserbaseOptions {
15
15
  /** Test hook. */
16
16
  fetch?: typeof fetch;
17
17
  }
18
+ /** Whether a base URL may receive a Solari key: https on a Solari API host, or loopback. */
19
+ export declare function isSolariBaseUrl(raw: string): boolean;
18
20
  export declare class Browserbase {
19
21
  readonly sessions: Sessions;
20
22
  readonly contexts: Contexts;
package/dist/client.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { gunzipSync } from "node:zlib";
2
- import { APIError, BrowserbaseError, SolariCompatUnsupported } from "./errors.js";
2
+ import { APIError, BrowserbaseError, SolariCompatUnsupported, apiErrorFor, connectionErrorFor } from "./errors.js";
3
3
  import { mapCreateParams, mapStatus } from "./mapping.js";
4
4
  export const DEFAULT_BASE_URL = "https://api.getsolari.com";
5
5
  const REGION = "us-west-2"; // reported for Browserbase-shaped objects; Solari's single region (us-west)
@@ -19,6 +19,42 @@ function resolveApiKey(explicit, warn) {
19
19
  }
20
20
  throw new BrowserbaseError("No Solari API key: pass apiKey or set SOLARI_API_KEY (keys start with slr_)");
21
21
  }
22
+ /** The only Solari hosts that may receive a key (the API gateways). Not every *.getsolari.com host: preview hosts (<label>-<port>.preview.getsolari.com) serve customer code. */
23
+ const SOLARI_API_HOST = /^api(-sta(-gcp)?)?\.getsolari\.com$/;
24
+ /** Whether a base URL may receive a Solari key: https on a Solari API host, or loopback. */
25
+ export function isSolariBaseUrl(raw) {
26
+ let u;
27
+ try {
28
+ u = new URL(raw);
29
+ }
30
+ catch {
31
+ return false;
32
+ }
33
+ if (u.username || u.password)
34
+ return false; // never a URL with credentials in it
35
+ const host = u.hostname.toLowerCase().replace(/\.$/, "");
36
+ if (u.protocol === "https:" && SOLARI_API_HOST.test(host))
37
+ return true;
38
+ const loopback = host === "localhost" || host === "[::1]" || /^127(\.\d{1,3}){3}$/.test(host);
39
+ return loopback && (u.protocol === "http:" || u.protocol === "https:");
40
+ }
41
+ function describeHost(raw) {
42
+ try {
43
+ return new URL(raw).hostname || "unparsable URL";
44
+ }
45
+ catch {
46
+ return "unparsable URL";
47
+ }
48
+ }
49
+ function resolveBaseUrl(explicit) {
50
+ const given = explicit || process.env.SOLARI_BASE_URL;
51
+ if (!given)
52
+ return DEFAULT_BASE_URL;
53
+ if (!isSolariBaseUrl(given)) {
54
+ throw new BrowserbaseError(`Refusing to send a Solari API key to ${describeHost(given)}: it is not a Solari API host or loopback.`);
55
+ }
56
+ return given.replace(/\/+$/, "");
57
+ }
22
58
  function sleep(ms) {
23
59
  return new Promise((r) => setTimeout(r, ms));
24
60
  }
@@ -43,7 +79,7 @@ export class Browserbase {
43
79
  this.warn = opts.onWarning ?? ((m) => console.warn(`[solari-browserbase-compat] ${m}`));
44
80
  // Non-enumerable so console.log(client) / JSON.stringify(client) never print the key.
45
81
  Object.defineProperty(this, "apiKey", { value: resolveApiKey(opts.apiKey, this.warn), enumerable: false });
46
- this.baseURL = (opts.baseURL ?? process.env.SOLARI_BASE_URL ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
82
+ this.baseURL = resolveBaseUrl(opts.baseURL);
47
83
  this.strict = opts.strict === true;
48
84
  this.fetchImpl = opts.fetch ?? fetch;
49
85
  this.maxRetries = opts.maxRetries ?? 2;
@@ -92,12 +128,12 @@ export class Browserbase {
92
128
  catch (e) {
93
129
  clearTimeout(timer);
94
130
  if (!replaySafe || attempt === this.maxRetries)
95
- throw e;
131
+ throw connectionErrorFor(e);
96
132
  lastErr = e;
97
133
  }
98
134
  await sleep(300 * (attempt + 1));
99
135
  }
100
- throw lastErr instanceof Error ? lastErr : new BrowserbaseError("request failed");
136
+ throw connectionErrorFor(lastErr ?? new Error("request failed"));
101
137
  }
102
138
  /** @internal */
103
139
  async json(method, path, body, opts) {
@@ -114,7 +150,7 @@ export class Browserbase {
114
150
  }
115
151
  if (!res.ok) {
116
152
  const o = (parsed ?? {});
117
- throw new APIError(res.status, `Solari ${method} ${path}: ${res.status} ${o.error ?? text.slice(0, 200)}`, o.code, parsed);
153
+ throw apiErrorFor(res.status, `Solari ${method} ${path}: ${res.status} ${o.error ?? text.slice(0, 200)}`, o.code, parsed);
118
154
  }
119
155
  return parsed;
120
156
  }
@@ -157,6 +193,7 @@ function toSession(row, extra = {}) {
157
193
  status,
158
194
  proxyBytes: 0,
159
195
  keepAlive: extra.keepAlive ?? false,
196
+ ...(row.userMetadata ? { userMetadata: row.userMetadata } : {}),
160
197
  ...(extra.contextId ? { contextId: extra.contextId } : {}),
161
198
  region: REGION,
162
199
  };
@@ -190,7 +227,13 @@ class Sessions {
190
227
  if (mapped.persistProfileId)
191
228
  this.c.persist.set(data.sessionId, mapped.persistProfileId);
192
229
  const ctx = params?.browserSettings?.context?.id;
193
- const s = toSession({ id: data.sessionId, status: "active", createdAt: new Date().toISOString(), ...(data.expiresAt ? { expiresAt: data.expiresAt } : {}) }, { keepAlive: params?.keepAlive === true, ...(ctx ? { contextId: ctx } : {}), status: "RUNNING" });
230
+ const s = toSession({
231
+ id: data.sessionId,
232
+ status: "active",
233
+ createdAt: new Date().toISOString(),
234
+ ...(data.expiresAt ? { expiresAt: data.expiresAt } : {}),
235
+ ...(data.userMetadata ? { userMetadata: data.userMetadata } : {}),
236
+ }, { keepAlive: params?.keepAlive === true, ...(ctx ? { contextId: ctx } : {}), status: "RUNNING" });
194
237
  const out = withConnect(s, data.cdpEndpoint);
195
238
  if (ctx) {
196
239
  let state = data.storageState ?? null;
@@ -210,9 +253,7 @@ class Sessions {
210
253
  return withConnect(toSession({ ...row, id: row.id ?? id }), row.cdpEndpoint ?? "");
211
254
  }
212
255
  async list(query) {
213
- if (query?.q)
214
- throw new SolariCompatUnsupported("sessions.list q (userMetadata query)", "metadata is not stored");
215
- const data = await this.c.json("GET", "/sessions");
256
+ const data = await this.c.json("GET", query?.q ? `/sessions?q=${encodeURIComponent(query.q)}` : "/sessions");
216
257
  const all = (data?.sessions ?? []).map((r) => toSession(r));
217
258
  return query?.status ? all.filter((s) => s.status === query.status) : all;
218
259
  }
package/dist/errors.d.ts CHANGED
@@ -9,6 +9,43 @@ export declare class APIError extends BrowserbaseError {
9
9
  readonly body: unknown;
10
10
  constructor(status: number, message: string, code?: string, body?: unknown);
11
11
  }
12
+ /** 400 */
13
+ export declare class BadRequestError extends APIError {
14
+ }
15
+ /** 401 */
16
+ export declare class AuthenticationError extends APIError {
17
+ }
18
+ /** 403 */
19
+ export declare class PermissionDeniedError extends APIError {
20
+ }
21
+ /** 404 */
22
+ export declare class NotFoundError extends APIError {
23
+ }
24
+ /** 409 */
25
+ export declare class ConflictError extends APIError {
26
+ }
27
+ /** 422 */
28
+ export declare class UnprocessableEntityError extends APIError {
29
+ }
30
+ /** 429 */
31
+ export declare class RateLimitError extends APIError {
32
+ }
33
+ /** 5xx */
34
+ export declare class InternalServerError extends APIError {
35
+ }
36
+ /** The request never produced an HTTP response (DNS, refused, reset, ...). Same name as Browserbase's SDK. */
37
+ export declare class APIConnectionError extends BrowserbaseError {
38
+ readonly cause: unknown;
39
+ constructor(message: string, cause?: unknown);
40
+ }
41
+ /** The request timed out. */
42
+ export declare class APIConnectionTimeoutError extends APIConnectionError {
43
+ constructor(message?: string, cause?: unknown);
44
+ }
45
+ /** The APIError subclass for an HTTP status, as Browserbase's SDK raises them. */
46
+ export declare function apiErrorFor(status: number, message: string, code?: string, body?: unknown): APIError;
47
+ /** A fetch failure as a Browserbase-style connection error (abort/timeout vs. everything else). */
48
+ export declare function connectionErrorFor(e: unknown): BrowserbaseError;
12
49
  /** A Browserbase feature Solari Browser does not offer (see COMPAT.md). Never silently ignored when it would change results. */
13
50
  export declare class SolariCompatUnsupported extends BrowserbaseError {
14
51
  readonly feature: string;
package/dist/errors.js CHANGED
@@ -17,6 +17,66 @@ export class APIError extends BrowserbaseError {
17
17
  this.body = body;
18
18
  }
19
19
  }
20
+ /** 400 */
21
+ export class BadRequestError extends APIError {
22
+ }
23
+ /** 401 */
24
+ export class AuthenticationError extends APIError {
25
+ }
26
+ /** 403 */
27
+ export class PermissionDeniedError extends APIError {
28
+ }
29
+ /** 404 */
30
+ export class NotFoundError extends APIError {
31
+ }
32
+ /** 409 */
33
+ export class ConflictError extends APIError {
34
+ }
35
+ /** 422 */
36
+ export class UnprocessableEntityError extends APIError {
37
+ }
38
+ /** 429 */
39
+ export class RateLimitError extends APIError {
40
+ }
41
+ /** 5xx */
42
+ export class InternalServerError extends APIError {
43
+ }
44
+ /** The request never produced an HTTP response (DNS, refused, reset, ...). Same name as Browserbase's SDK. */
45
+ export class APIConnectionError extends BrowserbaseError {
46
+ cause;
47
+ constructor(message, cause) {
48
+ super(message);
49
+ this.cause = cause;
50
+ }
51
+ }
52
+ /** The request timed out. */
53
+ export class APIConnectionTimeoutError extends APIConnectionError {
54
+ constructor(message = "Request timed out.", cause) {
55
+ super(message, cause);
56
+ }
57
+ }
58
+ /** The APIError subclass for an HTTP status, as Browserbase's SDK raises them. */
59
+ export function apiErrorFor(status, message, code, body) {
60
+ const C = status === 400 ? BadRequestError
61
+ : status === 401 ? AuthenticationError
62
+ : status === 403 ? PermissionDeniedError
63
+ : status === 404 ? NotFoundError
64
+ : status === 409 ? ConflictError
65
+ : status === 422 ? UnprocessableEntityError
66
+ : status === 429 ? RateLimitError
67
+ : status >= 500 ? InternalServerError
68
+ : APIError;
69
+ return new C(status, message, code, body);
70
+ }
71
+ /** A fetch failure as a Browserbase-style connection error (abort/timeout vs. everything else). */
72
+ export function connectionErrorFor(e) {
73
+ if (e instanceof BrowserbaseError)
74
+ return e;
75
+ const name = e?.name;
76
+ if (name === "AbortError" || name === "TimeoutError")
77
+ return new APIConnectionTimeoutError("Request timed out.", e);
78
+ return new APIConnectionError(`Connection error: ${e?.message ?? "request failed"}`, e);
79
+ }
20
80
  /** A Browserbase feature Solari Browser does not offer (see COMPAT.md). Never silently ignored when it would change results. */
21
81
  export class SolariCompatUnsupported extends BrowserbaseError {
22
82
  feature;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Browserbase } from "./client.js";
2
2
  export { Browserbase, DEFAULT_BASE_URL } from "./client.js";
3
3
  export type { BrowserbaseOptions, UnsupportedResource } from "./client.js";
4
- export { APIError, BrowserbaseError, SolariCompatUnsupported } from "./errors.js";
4
+ export { APIConnectionError, APIConnectionTimeoutError, APIError, AuthenticationError, BadRequestError, BrowserbaseError, ConflictError, InternalServerError, NotFoundError, PermissionDeniedError, RateLimitError, SolariCompatUnsupported, UnprocessableEntityError, } from "./errors.js";
5
5
  export { mapCreateParams, mapStatus } from "./mapping.js";
6
6
  export type * from "./types.js";
7
7
  export default Browserbase;
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Browserbase } from "./client.js";
2
2
  export { Browserbase, DEFAULT_BASE_URL } from "./client.js";
3
- export { APIError, BrowserbaseError, SolariCompatUnsupported } from "./errors.js";
3
+ export { APIConnectionError, APIConnectionTimeoutError, APIError, AuthenticationError, BadRequestError, BrowserbaseError, ConflictError, InternalServerError, NotFoundError, PermissionDeniedError, RateLimitError, SolariCompatUnsupported, UnprocessableEntityError, } from "./errors.js";
4
4
  export { mapCreateParams, mapStatus } from "./mapping.js";
5
5
  export default Browserbase;
package/dist/mapping.js CHANGED
@@ -62,14 +62,8 @@ export function mapCreateParams(params, strict) {
62
62
  throw new SolariCompatUnsupported(`region ${p.region}`, "Solari Browser runs in one region (us-west)");
63
63
  }
64
64
  // ── soft gaps ────────────────────────────────────────────────────────────────
65
- if (p.timeout !== undefined)
66
- soft("timeout", "sessions end at your plan's maximum session length, not at this value; call sessions.update to release earlier");
67
- if (p.keepAlive === true)
68
- soft("keepAlive", "a disconnected session is released about 45 s after the socket closes; reconnect inside that window or hold the connection");
69
- if (p.userMetadata !== undefined)
70
- soft("userMetadata", "metadata is not stored or queryable");
71
65
  if (bs.viewport !== undefined)
72
- soft("browserSettings.viewport", "set the viewport from your automation client (page.setViewportSize)");
66
+ soft("browserSettings.viewport", "known gap: the viewport cannot be set at create; call page.setViewportSize() from your automation client");
73
67
  if (bs.blockAds === true)
74
68
  soft("browserSettings.blockAds", "ad blocking is not available");
75
69
  if (bs.logSession === true)
@@ -94,6 +88,12 @@ export function mapCreateParams(params, strict) {
94
88
  body.allowedDomains = allowedDomains;
95
89
  if (bs.context?.id)
96
90
  body.profileId = bs.context.id;
91
+ if (p.timeout !== undefined)
92
+ body.timeoutSeconds = p.timeout; // the gateway validates (>= 60) and clamps to the plan maximum
93
+ if (p.keepAlive === true)
94
+ body.keepAlive = true;
95
+ if (p.userMetadata !== undefined)
96
+ body.userMetadata = p.userMetadata;
97
97
  return {
98
98
  body,
99
99
  ...(bs.context?.id && bs.context.persist === true ? { persistProfileId: bs.context.id } : {}),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solarisdk/browserbase-compat",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Drop-in Browserbase-style SDK surface for Solari Browser: sessions, connectUrl (CDP), contexts, live view, replay events.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -12,7 +12,7 @@
12
12
  "import": "./dist/index.js"
13
13
  }
14
14
  },
15
- "files": ["dist", "README.md", "COMPAT.md", "LICENSE"],
15
+ "files": ["dist", "README.md", "COMPAT.md", "CHANGELOG.md", "LICENSE", "NOTICE"],
16
16
  "keywords": ["browserbase", "solari", "browser", "playwright", "puppeteer", "cdp", "compat"],
17
17
  "engines": { "node": ">=20" },
18
18
  "publishConfig": { "access": "public" },