From b5e10060a064de9a9454fa90e461f09154094742 Mon Sep 17 00:00:00 2001 From: Matic Zavadlal Date: Fri, 22 Aug 2025 14:46:23 +0100 Subject: [PATCH 1/2] feat: NextJS Utils --- README.md | 59 +---------------------- package.json | 6 ++- src/lib/nextjs/hooks/useBrowserUse.ts | 37 ++++++++++++++ src/lib/nextjs/server/utils.ts | 69 +++++++++++++++++++++++++++ yarn.lock | 29 +++++++++++ 5 files changed, 142 insertions(+), 58 deletions(-) create mode 100644 src/lib/nextjs/hooks/useBrowserUse.ts create mode 100644 src/lib/nextjs/server/utils.ts diff --git a/README.md b/README.md index 2214c04..673e169 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,8 @@ export async function POST(req: Request) { } ``` +## Advanced Usage + ## Handling errors When the library is unable to connect to the API, @@ -197,8 +199,6 @@ On timeout, an `APIConnectionTimeoutError` is thrown. Note that requests which time out will be [retried twice by default](#retries). -## Advanced Usage - ### Accessing raw Response data (e.g., headers) The "raw" `Response` returned by `fetch()` can be accessed through the `.asResponse()` method on the `APIPromise` type that all methods return. @@ -277,49 +277,6 @@ const client = new BrowserUse({ }); ``` -### Making custom/undocumented requests - -This library is typed for convenient access to the documented API. If you need to access undocumented -endpoints, params, or response properties, the library can still be used. - -#### Undocumented endpoints - -To make requests to undocumented endpoints, you can use `client.get`, `client.post`, and other HTTP verbs. -Options on the client, such as retries, will be respected when making these requests. - -```ts -await client.post('/some/path', { - body: { some_prop: 'foo' }, - query: { some_query_arg: 'bar' }, -}); -``` - -#### Undocumented request params - -To make requests using undocumented parameters, you may use `// @ts-expect-error` on the undocumented -parameter. This library doesn't validate at runtime that the request matches the type, so any extra values you -send will be sent as-is. - -```ts -client.tasks.create({ - // ... - // @ts-expect-error baz is not yet public - baz: 'undocumented option', -}); -``` - -For requests with the `GET` verb, any extra params will be in the query, all other requests will send the -extra param in the body. - -If you want to explicitly send an extra argument, you can do so with the `query`, `body`, and `headers` request -options. - -#### Undocumented response properties - -To access undocumented response properties, you may access the response object with `// @ts-expect-error` on -the response object, or cast the response object to the requisite type. Like the request params, we do not -validate or strip extra properties from the response from the API. - ### Customizing the fetch client By default, this library expects a global `fetch` function is defined. @@ -401,18 +358,6 @@ const client = new BrowserUse({ ## Frequently Asked Questions -## Semantic versioning - -This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions: - -1. Changes that only affect static types, without breaking runtime behavior. -2. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_ -3. Changes that we do not expect to impact the vast majority of users in practice. - -We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience. - -We are keen for your feedback; please open an [issue](https://www.github.com/browser-use/browser-use-node/issues) with questions, bugs, or suggestions. - ## Requirements TypeScript >= 4.9 is supported. diff --git a/package.json b/package.json index 9bf8019..655a7bc 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "@swc/jest": "^0.2.29", "@types/jest": "^29.4.0", "@types/node": "^24.3.0", + "@types/react": "^19.1.10", "@typescript-eslint/eslint-plugin": "8.31.1", "@typescript-eslint/parser": "8.31.1", "commander": "^14.0.0", @@ -51,6 +52,8 @@ "jest": "^29.4.0", "prettier": "^3.0.0", "publint": "^0.2.12", + "react": "^19.1.1", + "react-dom": "^19.1.1", "ts-jest": "^29.1.0", "ts-node": "^10.5.0", "tsc-multi": "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.9/tsc-multi.tgz", @@ -61,7 +64,8 @@ "zod": "^4.0.17" }, "peerDependencies": { - "zod": "^4.0.17" + "react": "^18 || ^19", + "zod": "^4" }, "exports": { ".": { diff --git a/src/lib/nextjs/hooks/useBrowserUse.ts b/src/lib/nextjs/hooks/useBrowserUse.ts new file mode 100644 index 0000000..1f9ab84 --- /dev/null +++ b/src/lib/nextjs/hooks/useBrowserUse.ts @@ -0,0 +1,37 @@ +import { useEffect, useState } from 'react'; +import type { ZodType } from 'zod'; + +import type { BrowserUseEvent } from '../server/utils'; +import { TaskViewWithSchema } from '../../parse'; + +/** + * A hook to stream Browser Use updates to the client. + */ +export function useBrowserUse(route: string): TaskViewWithSchema | null { + const [status, setStatus] = useState | null>(null); + + useEffect(() => { + const es = new EventSource(route); + + es.addEventListener('status', (e) => { + if (e instanceof MessageEvent) { + const msg = JSON.parse(e.data) as BrowserUseEvent; + + setStatus(msg.data); + + if (msg.data.status === 'finished') { + es.close(); + } + } else { + console.error('Event is not a MessageEvent', e); + } + }); + + es.addEventListener('end', () => es.close()); + es.addEventListener('error', () => es.close()); + + return () => es.close(); + }, [route]); + + return status; +} diff --git a/src/lib/nextjs/server/utils.ts b/src/lib/nextjs/server/utils.ts new file mode 100644 index 0000000..6c8959c --- /dev/null +++ b/src/lib/nextjs/server/utils.ts @@ -0,0 +1,69 @@ +import { TaskViewWithSchema } from '../../parse'; +import { ZodType } from 'zod'; + +export type BrowserUseEvent = { + event: 'status'; + data: TaskViewWithSchema; +}; + +/** + * Convert an async generator to a stream. + * + * @param gen - The async generator to convert to a stream. + * @returns A stream of the async generator. + */ +export function gtos( + gen: AsyncGenerator<{ + event: 'status'; + data: TaskViewWithSchema; + }>, + opts?: { + /** + * Called when an event is emitted. + */ + onEvent?: (event: TaskViewWithSchema) => void; + + /** + * Called when the task is finished. + */ + onFinished?: (event: TaskViewWithSchema) => void; + }, +): ReadableStream> { + const enc = new TextEncoder(); + + const stream = new ReadableStream({ + async start(controller) { + // open the SSE stream quickly + controller.enqueue(enc.encode(': connected\n\n')); + + try { + for await (const msg of gen) { + opts?.onEvent?.(msg.data); + + const data: BrowserUseEvent = { + event: msg.event, + data: msg.data, + }; + + const encoded = JSON.stringify(data); + + const payload = `event: ${msg.event}\ndata: ${encoded}\n\n`; + + controller.enqueue(enc.encode(payload)); + + if (msg.data.status === 'finished') { + opts?.onFinished?.(msg.data); + } + } + + controller.enqueue(enc.encode('event: end\ndata: {}\n\n')); + } catch (e) { + controller.enqueue(enc.encode(`event: error\ndata: ${JSON.stringify({ message: String(e) })}\n\n`)); + } finally { + controller.close(); + } + }, + }); + + return stream; +} diff --git a/yarn.lock b/yarn.lock index 0551ce4..12eb306 100644 --- a/yarn.lock +++ b/yarn.lock @@ -991,6 +991,13 @@ dependencies: undici-types "~7.10.0" +"@types/react@^19.1.10": + version "19.1.10" + resolved "https://registry.yarnpkg.com/@types/react/-/react-19.1.10.tgz#a05015952ef328e1b85579c839a71304b07d21d9" + integrity sha512-EhBeSYX0Y6ye8pNebpKrwFJq7BoQ8J5SO6NlvNwwHjSj6adXJViPQrKlsyPw7hLBLvckEMO1yxeGdR82YBBlDg== + dependencies: + csstype "^3.0.2" + "@types/stack-utils@^2.0.0": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" @@ -1475,6 +1482,11 @@ cross-spawn@^7.0.3, cross-spawn@^7.0.6: shebang-command "^2.0.0" which "^2.0.1" +csstype@^3.0.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" + integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== + debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.7: version "4.4.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" @@ -2984,11 +2996,23 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +react-dom@^19.1.1: + version "19.1.1" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.1.1.tgz#2daa9ff7f3ae384aeb30e76d5ee38c046dc89893" + integrity sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw== + dependencies: + scheduler "^0.26.0" + react-is@^18.0.0: version "18.3.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== +react@^19.1.1: + version "19.1.1" + resolved "https://registry.yarnpkg.com/react/-/react-19.1.1.tgz#06d9149ec5e083a67f9a1e39ce97b06a03b644af" + integrity sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ== + readable-stream@^3.4.0: version "3.6.2" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" @@ -3063,6 +3087,11 @@ safe-buffer@~5.2.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +scheduler@^0.26.0: + version "0.26.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.26.0.tgz#4ce8a8c2a2095f13ea11bf9a445be50c555d6337" + integrity sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA== + semver@^6.3.0, semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" From bd3ba9092f9f4088d5344c9b522ba07d4e040a2b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 22 Aug 2025 13:46:39 +0000 Subject: [PATCH 2/2] release: 1.2.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ package.json | 2 +- src/version.ts | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index d719a72..c3f1463 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.1.3" + ".": "1.2.0" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cd3e1f..c9b4c4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.2.0 (2025-08-22) + +Full Changelog: [v1.1.3...v1.2.0](https://github.com/browser-use/browser-use-node/compare/v1.1.3...v1.2.0) + +### Features + +* NextJS Utils ([b5e1006](https://github.com/browser-use/browser-use-node/commit/b5e10060a064de9a9454fa90e461f09154094742)) + ## 1.1.3 (2025-08-22) Full Changelog: [v1.1.2...v1.1.3](https://github.com/browser-use/browser-use-node/compare/v1.1.2...v1.1.3) diff --git a/package.json b/package.json index 655a7bc..ee4d086 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "browser-use-sdk", - "version": "1.1.3", + "version": "1.2.0", "description": "The official TypeScript library for the Browser Use API", "author": "Browser Use ", "types": "dist/index.d.ts", diff --git a/src/version.ts b/src/version.ts index 3965836..54c8a47 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const VERSION = '1.1.3'; // x-release-please-version +export const VERSION = '1.2.0'; // x-release-please-version