diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c34dbad..535c4a7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,10 +21,10 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/browserbase-node' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Node - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: '20' @@ -43,10 +43,10 @@ jobs: contents: read id-token: write steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Node - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: '20' @@ -61,7 +61,7 @@ jobs: github.repository == 'stainless-sdks/browserbase-node' && !startsWith(github.ref, 'refs/heads/stl/') id: github-oidc - uses: actions/github-script@v8 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: script: core.setOutput('github_token', await core.getIDToken()); @@ -80,10 +80,10 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/browserbase-node' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Node - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: '20' diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml index d2c264b..38c849f 100644 --- a/.github/workflows/publish-npm.yml +++ b/.github/workflows/publish-npm.yml @@ -14,10 +14,10 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Node - uses: actions/setup-node@v3 + uses: actions/setup-node@3235b876344d2a9aa001b8d1453c930bba69e610 # v3.9.1 with: node-version: '20' diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index 144f559..8b31abb 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -12,7 +12,7 @@ jobs: if: github.repository == 'browserbase/sdk-node' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Check release environment run: | diff --git a/.release-please-manifest.json b/.release-please-manifest.json index f393718..a9b8e02 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.10.0" + ".": "2.11.0" } diff --git a/.stats.yml b/.stats.yml index bf21668..4e2d03e 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 21 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase%2Fbrowserbase-921d3c61c7aa06269f74bee63cee993597944f913429caa2aa2e00dd51fab60f.yml -openapi_spec_hash: d35b9613c41bf172fa2b28aceef10b39 -config_hash: cf04ecfb8dad5fbd8b85be25d6e9ec55 +configured_endpoints: 23 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase/browserbase-2118fd938d408dda6ed82d06c48b0785fad91fd54b5397acc3421a49a386c791.yml +openapi_spec_hash: 8e48a39a55a11b128028b47747aea775 +config_hash: 40fbac80e24faaa0dc19e93368bcd821 diff --git a/CHANGELOG.md b/CHANGELOG.md index abb6690..7075cbd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## 2.11.0 (2026-05-13) + +Full Changelog: [v2.10.0...v2.11.0](https://github.com/browserbase/sdk-node/compare/v2.10.0...v2.11.0) + +### Features + +* [CORE-1928][apps/api] Add `PENDING` as a valid session state ([6c1dc59](https://github.com/browserbase/sdk-node/commit/6c1dc59e8ed581d88c26483a84efecd3903f4379)) +* [CORE-1979] [apps/api] Regenerate OpenAPI spec to match current routes ([90f048c](https://github.com/browserbase/sdk-node/commit/90f048c79e02ead952b73d6bd185f6d58fa25076)) +* **api:** add replays ([c1bcc8b](https://github.com/browserbase/sdk-node/commit/c1bcc8b9e6baff112a6672b24dccba0cec683c72)) +* support setting headers via env ([c4bce5e](https://github.com/browserbase/sdk-node/commit/c4bce5e45f6dc0d2f560313ea70fd9bb1ac28fa6)) + + +### Chores + +* **internal:** codegen related update ([e079c39](https://github.com/browserbase/sdk-node/commit/e079c39733d21312ee5d9e11fed35a2b8bba93be)) +* **internal:** more robust bootstrap script ([39adddf](https://github.com/browserbase/sdk-node/commit/39adddf3d60bcf94349cb9103a7f41a1b0bb9f9f)) +* **tests:** bump steady to v0.22.1 ([cd4bf1a](https://github.com/browserbase/sdk-node/commit/cd4bf1aad650b63d38638218919715a790c1c819)) + ## 2.10.0 (2026-04-06) Full Changelog: [v2.9.0...v2.10.0](https://github.com/browserbase/sdk-node/compare/v2.9.0...v2.10.0) diff --git a/api.md b/api.md index eb38395..11f9afd 100644 --- a/api.md +++ b/api.md @@ -114,3 +114,14 @@ Types: Methods: - client.sessions.uploads.create(id, { ...params }) -> UploadCreateResponse + +## Replays + +Types: + +- ReplayRetrieveResponse + +Methods: + +- client.sessions.replays.retrieve(id) -> ReplayRetrieveResponse +- client.sessions.replays.retrievePage(id, pageId) -> Response diff --git a/package-lock.json b/package-lock.json index a719b05..64a24c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@browserbasehq/sdk", - "version": "2.10.0", + "version": "2.11.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@browserbasehq/sdk", - "version": "2.10.0", + "version": "2.11.0", "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", diff --git a/package.json b/package.json index 095c2c4..d836657 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@browserbasehq/sdk", - "version": "2.10.0", + "version": "2.11.0", "description": "The official Node.js library for the Browserbase API", "author": "Browserbase ", "types": "dist/index.d.ts", diff --git a/scripts/bootstrap b/scripts/bootstrap index f68beda..28b5985 100755 --- a/scripts/bootstrap +++ b/scripts/bootstrap @@ -4,7 +4,7 @@ set -e cd "$(dirname "$0")/.." -if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ] && [ -t 0 ]; then +if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "${SKIP_BREW:-}" != "1" ] && [ -t 0 ]; then brew bundle check >/dev/null 2>&1 || { echo -n "==> Install Homebrew dependencies? (y/N): " read -r response diff --git a/scripts/mock b/scripts/mock index 5cd7c15..feebe5e 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.20.2 -- steady --version + npm exec --package=@stdy/cli@0.22.1 -- steady --version - npm exec --package=@stdy/cli@0.20.2 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.22.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.20.2 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.22.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index a9d718c..19b8d0c 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.20.2 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.22.1 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" echo exit 1 diff --git a/src/core.ts b/src/core.ts index 821e4e5..10b9d3f 100644 --- a/src/core.ts +++ b/src/core.ts @@ -1054,10 +1054,10 @@ export const ensurePresent = (value: T | null | undefined): T => { */ export const readEnv = (env: string): string | undefined => { if (typeof process !== 'undefined') { - return process.env?.[env]?.trim() ?? undefined; + return process.env?.[env]?.trim() || undefined; } if (typeof Deno !== 'undefined') { - return Deno.env?.get?.(env)?.trim(); + return Deno.env?.get?.(env)?.trim() || undefined; } return undefined; }; diff --git a/src/index.ts b/src/index.ts index 56c1535..19c7fb5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -139,6 +139,18 @@ export class Browserbase extends Core.APIClient { fetch: options.fetch, }); + const customHeadersEnv = Core.readEnv('BROWSERBASE_CUSTOM_HEADERS'); + if (customHeadersEnv) { + const parsed: Record = {}; + for (const line of customHeadersEnv.split('\n')) { + const colon = line.indexOf(':'); + if (colon >= 0) { + parsed[line.substring(0, colon).trim()] = line.substring(colon + 1).trim(); + } + } + options.defaultHeaders = { ...parsed, ...options.defaultHeaders }; + } + this._options = options; this.apiKey = apiKey; diff --git a/src/resources/sessions/index.ts b/src/resources/sessions/index.ts index 9c9a61e..b3c2a80 100644 --- a/src/resources/sessions/index.ts +++ b/src/resources/sessions/index.ts @@ -3,6 +3,7 @@ export { Downloads } from './downloads'; export { Logs, type SessionLog, type LogListResponse } from './logs'; export { Recording, type SessionRecording, type RecordingRetrieveResponse } from './recording'; +export { Replays, type ReplayRetrieveResponse } from './replays'; export { Sessions, type Session, diff --git a/src/resources/sessions/replays.ts b/src/resources/sessions/replays.ts new file mode 100644 index 0000000..70d1f3b --- /dev/null +++ b/src/resources/sessions/replays.ts @@ -0,0 +1,49 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../resource'; +import * as Core from '../../core'; +import { type Response } from '../../_shims/index'; + +export class Replays extends APIResource { + /** + * Returns page metadata for a session replay, including timing information and the + * URL of each page's HLS playlist. + */ + retrieve(id: string, options?: Core.RequestOptions): Core.APIPromise { + return this._client.get(`/v1/sessions/${id}/replays`, options); + } + + /** + * Returns an HLS VOD media playlist (.m3u8) for a specific page of a session + * replay. + */ + retrievePage(id: string, pageId: string, options?: Core.RequestOptions): Core.APIPromise { + return this._client.get(`/v1/sessions/${id}/replays/${pageId}`, { + ...options, + headers: { Accept: 'application/vnd.apple.mpegurl', ...options?.headers }, + __binaryResponse: true, + }); + } +} + +export interface ReplayRetrieveResponse { + pageCount: number; + + pages: Array; +} + +export namespace ReplayRetrieveResponse { + export interface Page { + endTimeMs: number; + + pageId: string; + + startTimeMs: number; + + url: string; + } +} + +export declare namespace Replays { + export { type ReplayRetrieveResponse as ReplayRetrieveResponse }; +} diff --git a/src/resources/sessions/sessions.ts b/src/resources/sessions/sessions.ts index 183afb7..ac5b37d 100644 --- a/src/resources/sessions/sessions.ts +++ b/src/resources/sessions/sessions.ts @@ -9,6 +9,8 @@ import * as LogsAPI from './logs'; import { LogListResponse, Logs, SessionLog } from './logs'; import * as RecordingAPI from './recording'; import { Recording, RecordingRetrieveResponse, SessionRecording } from './recording'; +import * as ReplaysAPI from './replays'; +import { ReplayRetrieveResponse, Replays } from './replays'; import * as UploadsAPI from './uploads'; import { UploadCreateParams, UploadCreateResponse, Uploads } from './uploads'; @@ -17,6 +19,7 @@ export class Sessions extends APIResource { logs: LogsAPI.Logs = new LogsAPI.Logs(this._client); recording: RecordingAPI.Recording = new RecordingAPI.Recording(this._client); uploads: UploadsAPI.Uploads = new UploadsAPI.Uploads(this._client); + replays: ReplaysAPI.Replays = new ReplaysAPI.Replays(this._client); /** * Create a Session @@ -99,7 +102,7 @@ export interface Session { startedAt: string; - status: 'RUNNING' | 'ERROR' | 'TIMED_OUT' | 'COMPLETED'; + status: 'PENDING' | 'RUNNING' | 'ERROR' | 'TIMED_OUT' | 'COMPLETED'; updatedAt: string; @@ -263,6 +266,12 @@ export namespace SessionCreateParams { */ extensionId?: string; + /** + * Enable or disable ignoring of certificate errors in the browser. Defaults to + * `true`. + */ + ignoreCertificateErrors?: boolean; + /** * Enable or disable session logging. Defaults to `true`. */ @@ -424,13 +433,14 @@ export interface SessionListParams { */ q?: string; - status?: 'RUNNING' | 'ERROR' | 'TIMED_OUT' | 'COMPLETED'; + status?: 'PENDING' | 'RUNNING' | 'ERROR' | 'TIMED_OUT' | 'COMPLETED'; } Sessions.Downloads = Downloads; Sessions.Logs = Logs; Sessions.Recording = Recording; Sessions.Uploads = Uploads; +Sessions.Replays = Replays; export declare namespace Sessions { export { @@ -459,4 +469,6 @@ export declare namespace Sessions { type UploadCreateResponse as UploadCreateResponse, type UploadCreateParams as UploadCreateParams, }; + + export { Replays as Replays, type ReplayRetrieveResponse as ReplayRetrieveResponse }; } diff --git a/src/version.ts b/src/version.ts index 7b16f63..e91ff8d 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const VERSION = '2.10.0'; // x-release-please-version +export const VERSION = '2.11.0'; // x-release-please-version diff --git a/tests/api-resources/sessions/replays.test.ts b/tests/api-resources/sessions/replays.test.ts new file mode 100644 index 0000000..4387aaa --- /dev/null +++ b/tests/api-resources/sessions/replays.test.ts @@ -0,0 +1,40 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Browserbase from '@browserbasehq/sdk'; +import { Response } from 'node-fetch'; + +const client = new Browserbase({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource replays', () => { + test('retrieve', async () => { + const responsePromise = client.sessions.replays.retrieve('182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('retrieve: request options instead of params are passed correctly', async () => { + // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error + await expect( + client.sessions.replays.retrieve('182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e', { + path: '/_stainless_unknown_path', + }), + ).rejects.toThrow(Browserbase.NotFoundError); + }); + + test('retrievePage: request options instead of params are passed correctly', async () => { + // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error + await expect( + client.sessions.replays.retrievePage('182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e', '090', { + path: '/_stainless_unknown_path', + }), + ).rejects.toThrow(Browserbase.NotFoundError); + }); +}); diff --git a/tests/api-resources/sessions/sessions.test.ts b/tests/api-resources/sessions/sessions.test.ts index 765cf69..d13d1be 100644 --- a/tests/api-resources/sessions/sessions.test.ts +++ b/tests/api-resources/sessions/sessions.test.ts @@ -39,6 +39,7 @@ describe('resource sessions', () => { captchaInputSelector: 'captchaInputSelector', context: { id: 'id', persist: true }, extensionId: 'extensionId', + ignoreCertificateErrors: true, logSession: true, os: 'windows', recordSession: true, @@ -126,7 +127,7 @@ describe('resource sessions', () => { test('list: request options and params are passed correctly', async () => { // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error await expect( - client.sessions.list({ q: 'q', status: 'RUNNING' }, { path: '/_stainless_unknown_path' }), + client.sessions.list({ q: 'q', status: 'PENDING' }, { path: '/_stainless_unknown_path' }), ).rejects.toThrow(Browserbase.NotFoundError); });