Skip to content

Commit 7d72251

Browse files
Jayko001claude
andcommitted
feat(browser-pools): typed acquire helper distinguishing got / timed-out
Add `acquireBrowserFromPool` returning a discriminated union — `{ status: 'acquired', browser }` on 200 or `{ status: 'timed_out' }` on 204 — so callers can disambiguate the long-poll timeout from a successful lease without inspecting the raw HTTP status. 404 continues to reject with the existing `NotFoundError`. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 2467fd9 commit 7d72251

3 files changed

Lines changed: 73 additions & 0 deletions

File tree

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export { APIPromise } from './core/api-promise';
77
export { Kernel, type ClientOptions } from './client';
88
export { type BrowserFetchInit } from './lib/browser-fetch';
99
export { BrowserRouteCache, type BrowserRoute } from './lib/browser-routing';
10+
export { acquire as acquireBrowserFromPool, type AcquireOutcome } from './lib/browser-pools';
1011
export { PagePromise } from './core/pagination';
1112
export {
1213
KernelError,

src/lib/browser-pools.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import type { Kernel } from '../client';
2+
import type { RequestOptions } from '../internal/request-options';
3+
import type { BrowserPoolAcquireParams, BrowserPoolAcquireResponse } from '../resources/browser-pools';
4+
5+
export type AcquireOutcome =
6+
| { status: 'acquired'; browser: BrowserPoolAcquireResponse }
7+
| { status: 'timed_out' };
8+
9+
/**
10+
* Long-polling acquire that surfaces the HTTP outcome as a typed result.
11+
*
12+
* Resolves to one of:
13+
*
14+
* - `{ status: 'acquired', browser }` — a browser was leased from the pool.
15+
* - `{ status: 'timed_out' }` — the long poll expired without a browser becoming
16+
* available. Retry to keep waiting.
17+
*
18+
* Rejects with `NotFoundError` if the pool does not exist.
19+
*/
20+
export async function acquire(
21+
client: Kernel,
22+
idOrName: string,
23+
body: BrowserPoolAcquireParams = {},
24+
options?: RequestOptions,
25+
): Promise<AcquireOutcome> {
26+
const { data, response } = await client.browserPools.acquire(idOrName, body, options).withResponse();
27+
if (response.status === 204) {
28+
return { status: 'timed_out' };
29+
}
30+
return { status: 'acquired', browser: data };
31+
}

tests/lib/browser-pools.test.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import Kernel, { NotFoundError } from '@onkernel/sdk';
2+
3+
import { acquire, type AcquireOutcome } from '../../src/lib/browser-pools';
4+
5+
const baseBrowser = {
6+
session_id: 'sess-1',
7+
base_url: 'http://browser-session.test/browser/kernel',
8+
cdp_ws_url: 'wss://browser-session.test/browser/cdp?jwt=t',
9+
webdriver_ws_url: 'wss://x',
10+
created_at: '2020-01-01T00:00:00Z',
11+
headless: true,
12+
stealth: false,
13+
timeout_seconds: 60,
14+
};
15+
16+
const clientWith = (fetcher: typeof fetch) =>
17+
new Kernel({ apiKey: 'k', baseURL: 'https://api.example/', fetch: fetcher });
18+
19+
describe('browser pool typed acquire', () => {
20+
test('resolves to acquired on 200', async () => {
21+
const client = clientWith(async () => Response.json(baseBrowser));
22+
const result: AcquireOutcome = await acquire(client, 'my-pool');
23+
expect(result.status).toBe('acquired');
24+
if (result.status === 'acquired') {
25+
expect(result.browser.session_id).toBe('sess-1');
26+
}
27+
});
28+
29+
test('resolves to timed_out on 204', async () => {
30+
const client = clientWith(async () => new Response(null, { status: 204 }));
31+
const result = await acquire(client, 'my-pool');
32+
expect(result.status).toBe('timed_out');
33+
});
34+
35+
test('rejects with NotFoundError on 404', async () => {
36+
const client = clientWith(async () =>
37+
Response.json({ code: 'not_found', message: 'pool not found' }, { status: 404 }),
38+
);
39+
await expect(acquire(client, 'missing')).rejects.toBeInstanceOf(NotFoundError);
40+
});
41+
});

0 commit comments

Comments
 (0)