diff --git a/.github/workflows/api-reference.yml b/.github/workflows/api-reference.yml index 46d08ee..d4006ea 100644 --- a/.github/workflows/api-reference.yml +++ b/.github/workflows/api-reference.yml @@ -24,10 +24,10 @@ jobs: name: Build and publish to npmjs.com runs-on: ubuntu-latest steps: - - name: Setup Node.js 20 + - name: Setup Node.js 24 uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 24 - name: Checkout source code uses: actions/checkout@v4 - uses: pnpm/action-setup@v3 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index b1d3ce9..0c2e830 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -9,10 +9,10 @@ jobs: name: Build and publish to npmjs.com runs-on: ubuntu-latest steps: - - name: Setup Node.js 20 + - name: Setup Node.js 24 uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 24 - name: Checkout source code uses: actions/checkout@v4 - name: Install pnpm diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0365126..e54c76c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,8 +13,8 @@ jobs: fail-fast: false matrix: node-version: - - "20" - - "18" + - "24" + - "22" os: - ubuntu-latest # - macos-latest diff --git a/.github/workflows/version-bump.yml b/.github/workflows/version-bump.yml index afc6d06..d7cd795 100644 --- a/.github/workflows/version-bump.yml +++ b/.github/workflows/version-bump.yml @@ -27,7 +27,7 @@ jobs: - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 24 - name: Compare version against base branch env: diff --git a/CHANGELOG.md b/CHANGELOG.md index afb1bfb..ee1ba45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## [5.0.0] + +- **Breaking**: Require Node.js 22 or later. `engines.node` is now `>=22` (was `>=18`), because Node.js 18 and 20 have reached end of life. There is no runtime change in this release, but installing it on Node.js 18 or 20 fails under package managers that enforce `engines` (Yarn 1, or npm and pnpm with `engine-strict`) + ## [4.42.0] - Add Text-to-Speech: `client.tts.synthesize(request)` returns one text's complete audio (`TextToSpeechResponse`: `audio` plus the response headers as `requestId`, `region`, `model`, `voiceId`, `encoding`, `sampleRate`, `billedCharacters`, `audioDurationMs`, ...), `client.tts.stream(request)` returns it as the API generates it (`TextToSpeechStream`, an async iterable over a web `ReadableStream`), and `client.tts.session(params)` opens a WebSocket `TextToSpeechSession` for voice agents: `connect()`, `generate()` / `flush()` / `audio()`, `speak()`, `cancel()`, `keepAlive()`, `close()`, with `on("audio" | "flushDone" | "wordBoundaries" | "cancelled" | "termination" | "error" | "close", ...)`. Requests take the API's fields (`text`, `voice_id`, `model_id`, `language`, `sample_rate`, `encoding`, `container`, `bit_rate`); failures throw `TextToSpeechError` (`status`, `errorCode`, `param`, `requestId`, `retryable`, `retryAfter`, `errors`, `closeCode`, `body`). New client option `ttsBaseUrl` (default `https://tts.us.assemblyai.com`); `client.tts.createTemporaryToken()` mints a token for browser sessions. Text sent while `connect()` is pending is held and sent the moment the socket opens, so it need not wait for `Begin` diff --git a/CLAUDE.md b/CLAUDE.md index 7084cc3..5e17ec5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -465,7 +465,7 @@ Gotchas: `stream()` gives up a body that sends no audio for `readTimeout` ms (30 - **Never expose API keys client-side** — use temporary auth tokens for browser streaming - **Sync timeout is a total deadline** — `transcribe()`, `transcribeLive()` and `openLive()` all default to a 180 s deadline spanning the whole request (upload plus transcription), and the sync API still caps audio at 120 s - **Dictation timeout is a total 300 s deadline** — `client.dictation` spans the upload, the transcription and the LLM pass in one deadline; the service caps audio at 120 s and accepts WAV or raw 16-bit PCM only -- **Node >= 18 required** +- **Node >= 22 required** - **Only runtime dependency**: ws (WebSocket library) - **Multi-runtime support**: Works in Node.js, Deno, Bun, Cloudflare Workers, and browsers diff --git a/README.md b/README.md index 27b3db0..3d4cbc8 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,8 @@ pnpm add assemblyai bun add assemblyai ``` +On Node.js, the SDK requires version 22 or later. On Node.js 18 or 20, install version 4 instead: `npm install assemblyai@4`. + Then, import the `assemblyai` module and create an AssemblyAI object with your API key: ```js diff --git a/docs/compat.md b/docs/compat.md index 0f32ced..90750ea 100644 --- a/docs/compat.md +++ b/docs/compat.md @@ -5,8 +5,8 @@ such as the browser, Deno, Bun, Cloudflare Workers, etc. ## Node.js compatibility -The SDK supports Node.js 18, 20, and 21. -If you do use an older version of Node.js like version 16, you'll need to polyfill `fetch`. +The SDK supports Node.js 22 and later. +If you're still on Node.js 18 or 20, use version 4 of the SDK. ## Browser compatibility diff --git a/package.json b/package.json index f4507f2..1d5979e 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { "name": "assemblyai", - "version": "4.42.0", + "version": "5.0.0", "description": "The AssemblyAI JavaScript SDK provides an easy-to-use interface for interacting with the AssemblyAI API, which supports async and real-time transcription and text-to-speech.", "engines": { - "node": ">=18" + "node": ">=22" }, "exports": { ".": { @@ -133,7 +133,7 @@ "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^12.1.0", "@types/jest": "^29.5.13", - "@types/node": "^18.19.53", + "@types/node": "^22.20.5", "@types/websocket": "^1.0.10", "@types/ws": "^8.5.12", "@typescript-eslint/eslint-plugin": "^7.18.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 13ecbe7..0b2a0ab 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -40,8 +40,8 @@ devDependencies: specifier: ^29.5.13 version: 29.5.13 '@types/node': - specifier: ^18.19.53 - version: 18.19.55 + specifier: ^22.20.5 + version: 22.20.5 '@types/websocket': specifier: ^1.0.10 version: 1.0.10 @@ -62,10 +62,10 @@ devDependencies: version: 0.3.0 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@18.19.55) + version: 29.7.0(@types/node@22.20.5) jest-cli: specifier: ^29.7.0 - version: 29.7.0(@types/node@18.19.55) + version: 29.7.0(@types/node@22.20.5) jest-fetch-mock: specifier: ^3.0.3 version: 3.0.3 @@ -1921,7 +1921,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 18.19.55 + '@types/node': 22.20.5 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -1942,14 +1942,14 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 18.19.55 + '@types/node': 22.20.5 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@18.19.55) + jest-config: 29.7.0(@types/node@22.20.5) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -1977,7 +1977,7 @@ packages: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 18.19.55 + '@types/node': 22.20.5 jest-mock: 29.7.0 dev: true @@ -2004,7 +2004,7 @@ packages: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 18.19.55 + '@types/node': 22.20.5 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -2037,7 +2037,7 @@ packages: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.25 - '@types/node': 18.19.55 + '@types/node': 22.20.5 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit: 0.1.2 @@ -2125,7 +2125,7 @@ packages: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 18.19.55 + '@types/node': 22.20.5 '@types/yargs': 17.0.32 chalk: 4.1.2 dev: true @@ -2318,6 +2318,7 @@ packages: resolution: {integrity: sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==} cpu: [arm] os: [linux] + libc: [glibc] requiresBuild: true dev: true optional: true @@ -2326,6 +2327,7 @@ packages: resolution: {integrity: sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==} cpu: [arm] os: [linux] + libc: [musl] requiresBuild: true dev: true optional: true @@ -2334,6 +2336,7 @@ packages: resolution: {integrity: sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==} cpu: [arm64] os: [linux] + libc: [glibc] requiresBuild: true dev: true optional: true @@ -2342,6 +2345,7 @@ packages: resolution: {integrity: sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==} cpu: [arm64] os: [linux] + libc: [musl] requiresBuild: true dev: true optional: true @@ -2350,6 +2354,7 @@ packages: resolution: {integrity: sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==} cpu: [ppc64] os: [linux] + libc: [glibc] requiresBuild: true dev: true optional: true @@ -2358,6 +2363,7 @@ packages: resolution: {integrity: sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==} cpu: [riscv64] os: [linux] + libc: [glibc] requiresBuild: true dev: true optional: true @@ -2366,6 +2372,7 @@ packages: resolution: {integrity: sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==} cpu: [s390x] os: [linux] + libc: [glibc] requiresBuild: true dev: true optional: true @@ -2374,6 +2381,7 @@ packages: resolution: {integrity: sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==} cpu: [x64] os: [linux] + libc: [glibc] requiresBuild: true dev: true optional: true @@ -2382,6 +2390,7 @@ packages: resolution: {integrity: sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==} cpu: [x64] os: [linux] + libc: [musl] requiresBuild: true dev: true optional: true @@ -2503,7 +2512,7 @@ packages: /@types/graceful-fs@4.1.9: resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} dependencies: - '@types/node': 18.19.55 + '@types/node': 22.20.5 dev: true /@types/hast@3.0.4: @@ -2541,10 +2550,10 @@ packages: '@types/unist': 3.0.3 dev: true - /@types/node@18.19.55: - resolution: {integrity: sha512-zzw5Vw52205Zr/nmErSEkN5FLqXPuKX/k5d1D7RKHATGqU7y6YfX9QxZraUzUrFGqH6XzOzG196BC35ltJC4Cw==} + /@types/node@22.20.5: + resolution: {integrity: sha512-U2+DNr+wSjpsTS/wZGYHq7GcwfuSmKiKvoPvK22zwTlRhU91yOniN4qRR5KhIjvif7ysw/dz/hKmfDH0Ris4aA==} dependencies: - undici-types: 5.26.5 + undici-types: 6.21.0 /@types/resolve@1.20.2: resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} @@ -2561,13 +2570,13 @@ packages: /@types/websocket@1.0.10: resolution: {integrity: sha512-svjGZvPB7EzuYS94cI7a+qhwgGU1y89wUgjT6E2wVUfmAGIvRfT7obBvRtnhXCSsoMdlG4gBFGE7MfkIXZLoww==} dependencies: - '@types/node': 18.19.55 + '@types/node': 22.20.5 dev: true /@types/ws@8.5.12: resolution: {integrity: sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==} dependencies: - '@types/node': 18.19.55 + '@types/node': 22.20.5 /@types/yargs-parser@21.0.3: resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} @@ -2710,7 +2719,7 @@ packages: globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.3 - semver: 7.6.0 + semver: 7.6.3 ts-api-utils: 1.3.0(typescript@5.4.5) typescript: 5.4.5 transitivePeerDependencies: @@ -3156,7 +3165,7 @@ packages: browserslist: 4.24.0 dev: true - /create-jest@29.7.0(@types/node@18.19.55): + /create-jest@29.7.0(@types/node@22.20.5): resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -3165,7 +3174,7 @@ packages: chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@18.19.55) + jest-config: 29.7.0(@types/node@22.20.5) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -3929,7 +3938,7 @@ packages: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 18.19.55 + '@types/node': 22.20.5 chalk: 4.1.2 co: 4.6.0 dedent: 1.5.1 @@ -3950,7 +3959,7 @@ packages: - supports-color dev: true - /jest-cli@29.7.0(@types/node@18.19.55): + /jest-cli@29.7.0(@types/node@22.20.5): resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -3964,10 +3973,10 @@ packages: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@18.19.55) + create-jest: 29.7.0(@types/node@22.20.5) exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0(@types/node@18.19.55) + jest-config: 29.7.0(@types/node@22.20.5) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -3978,7 +3987,7 @@ packages: - ts-node dev: true - /jest-config@29.7.0(@types/node@18.19.55): + /jest-config@29.7.0(@types/node@22.20.5): resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -3993,7 +4002,7 @@ packages: '@babel/core': 7.24.0 '@jest/test-sequencer': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 18.19.55 + '@types/node': 22.20.5 babel-jest: 29.7.0(@babel/core@7.24.0) chalk: 4.1.2 ci-info: 3.9.0 @@ -4053,7 +4062,7 @@ packages: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 18.19.55 + '@types/node': 22.20.5 jest-mock: 29.7.0 jest-util: 29.7.0 dev: true @@ -4078,7 +4087,7 @@ packages: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 18.19.55 + '@types/node': 22.20.5 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -4139,7 +4148,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 18.19.55 + '@types/node': 22.20.5 jest-util: 29.7.0 dev: true @@ -4194,7 +4203,7 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 18.19.55 + '@types/node': 22.20.5 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -4225,7 +4234,7 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 18.19.55 + '@types/node': 22.20.5 chalk: 4.1.2 cjs-module-lexer: 1.2.3 collect-v8-coverage: 1.0.2 @@ -4277,7 +4286,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 18.19.55 + '@types/node': 22.20.5 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -4302,7 +4311,7 @@ packages: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 18.19.55 + '@types/node': 22.20.5 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -4321,13 +4330,13 @@ packages: resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@types/node': 18.19.55 + '@types/node': 22.20.5 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true - /jest@29.7.0(@types/node@18.19.55): + /jest@29.7.0(@types/node@22.20.5): resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -4340,7 +4349,7 @@ packages: '@jest/core': 29.7.0 '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@18.19.55) + jest-cli: 29.7.0(@types/node@22.20.5) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -5421,7 +5430,7 @@ packages: bs-logger: 0.2.6 ejs: 3.1.10 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@18.19.55) + jest: 29.7.0(@types/node@22.20.5) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -5490,8 +5499,8 @@ packages: resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} dev: true - /undici-types@5.26.5: - resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + /undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} /undici@5.28.4: resolution: {integrity: sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==} diff --git a/src/utils/live.ts b/src/utils/live.ts index 08e78f5..fff285a 100644 --- a/src/utils/live.ts +++ b/src/utils/live.ts @@ -20,8 +20,8 @@ const encoder = new TextEncoder(); /** * Generates a multipart boundary. Random alphanumerics behind a fixed prefix, - * as `FormData` implementations do; `Math.random` rather than Web Crypto - * because `globalThis.crypto` is flagged on Node 18, which is still supported. + * as `FormData` implementations do. `Math.random` is enough for a boundary, + * and unlike Web Crypto it needs no polyfill on React Native. * @returns The boundary token, without the leading dashes. */ export function multipartBoundary(): string { @@ -559,7 +559,8 @@ export function resolveFormat( /** * A signal that aborts when `timeoutMs` elapses or when `external` aborts, - * whichever comes first. `AbortSignal.any` would do this but needs Node 20. + * whichever comes first. `AbortSignal.any` would do this, but browsers only + * have it since Chrome 116 and Safari 17.4. * @param timeoutMs - The deadline in milliseconds. * @param external - An optional caller-supplied signal. * @returns The combined signal. diff --git a/tests/unit/utils.test.ts b/tests/unit/utils.test.ts index 306bce2..893bc59 100644 --- a/tests/unit/utils.test.ts +++ b/tests/unit/utils.test.ts @@ -58,7 +58,9 @@ describe("utils", () => { const userAgent = buildUserAgent({}); expect(userAgent).toContain("AssemblyAI/1.0 ("); expect(userAgent).toContain("sdk=JavaScript/__SDK_VERSION__"); - expect(userAgent).toContain("runtime_env=Node/"); + // Node 21+ sets `navigator.userAgent` to "Node.js/", which prefixes + // the UA in place of a `runtime_env` entry. + expect(userAgent).toMatch(/^Node\.js\/\d+ /); }); it("should build a user-agent with extras", () => { @@ -67,7 +69,7 @@ describe("utils", () => { }); expect(userAgent).toContain("AssemblyAI/1.0 ("); expect(userAgent).toContain("sdk=JavaScript/__SDK_VERSION__"); - expect(userAgent).toContain("runtime_env=Node/"); + expect(userAgent).toMatch(/^Node\.js\/\d+ /); expect(userAgent).toContain("integration=Zapier/1.0.0"); }); @@ -89,7 +91,7 @@ describe("utils", () => { }); expect(userAgent).toContain("AssemblyAI/1.0 ("); expect(userAgent).toContain("sdk=TS/1.0"); - expect(userAgent).toContain("runtime_env=Node/"); + expect(userAgent).toMatch(/^Node\.js\/\d+ /); expect(userAgent).not.toContain("os="); });