From 2249e142def97e1509ea0ca4b188ef99ff40ed23 Mon Sep 17 00:00:00 2001 From: AssemblyAI Date: Thu, 7 Dec 2023 17:21:15 -0500 Subject: [PATCH] Project import generated by Copybara. GitOrigin-RevId: 9efd0278b7a8cd44f1bd3ea10beb4bcc400ed53b --- .prettierrc.json | 3 ++ CHANGELOG.md | 3 +- docs/compat.md | 7 ++- jest.config.js | 8 +++ package.json | 78 +++++++++++++++++++++------- rollup.config.js | 87 ++++++++++++++++++++------------ scripts/kitchensink.ts | 4 +- src/browser/fs.ts | 8 --- src/polyfills/fs/bun.ts | 8 +++ src/polyfills/fs/deno.ts | 9 ++++ src/polyfills/fs/index.ts | 8 +++ src/polyfills/fs/node.ts | 7 +++ src/polyfills/streams/index.ts | 6 +++ src/polyfills/streams/node.ts | 1 + src/polyfills/ws/browser.mjs | 15 ++++++ src/polyfills/ws/index.cjs | 1 + src/polyfills/ws/index.d.ts | 2 + src/polyfills/ws/index.mjs | 2 + src/services/files/index.ts | 4 +- src/services/realtime/service.ts | 8 +-- tsconfig.json | 9 ++-- tsconfig.scripts.json | 7 +++ tsconfig.test.json | 7 ++- 23 files changed, 216 insertions(+), 76 deletions(-) create mode 100644 .prettierrc.json create mode 100644 jest.config.js delete mode 100644 src/browser/fs.ts create mode 100644 src/polyfills/fs/bun.ts create mode 100644 src/polyfills/fs/deno.ts create mode 100644 src/polyfills/fs/index.ts create mode 100644 src/polyfills/fs/node.ts create mode 100644 src/polyfills/streams/index.ts create mode 100644 src/polyfills/streams/node.ts create mode 100644 src/polyfills/ws/browser.mjs create mode 100644 src/polyfills/ws/index.cjs create mode 100644 src/polyfills/ws/index.d.ts create mode 100644 src/polyfills/ws/index.mjs create mode 100644 tsconfig.scripts.json diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..732e220 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,3 @@ +{ + "semi": true +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f5258d..3835a51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,8 @@ ### Added -- Add `browser` and `browser:min` exports, at `dist/index.umd.js` and `dist/index.umd.min.js`. These exports are browser compatible versions of the SDK, with a few limitations. You can't use the file system and you have to use a temporary auth token with the real-time transcriber. +- Add `node`, `deno`, `bun`, `browser`, and `workerd` (Cloudflare Workers) exports to package.json. These exports are compatible versions of the SDK, with a few limitations in some cases. For more details, consult the [SDK Compatibility document](./docs/compat.md). +- Add `dist/assemblyai.umd.js` and `dist/assemblyai.umd.min.js`. You can reference these script files directly in the browser and the SDK will be available at the global `assemblyai` variable. ### Changed diff --git a/docs/compat.md b/docs/compat.md index 970f24a..f952624 100644 --- a/docs/compat.md +++ b/docs/compat.md @@ -3,6 +3,11 @@ The JavaScript SDK is developed for Node.js but is also compatible with other runtimes 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`. + ## Browser compatibility To make the SDK compatible with the browser, the SDK aims to use web standards as much as possible. @@ -40,7 +45,7 @@ However, there are still incompatibilities between Node.js and the browser. }); ``` -- You can't pass local audio file paths to `client.files.upload`, `client.transcripts.transcribe`, and `client.transcripts.submit`. If you do, you'll get the following error: "Function is not supported in this environment.". +- You can't pass local audio file paths to `client.files.upload`, `client.transcripts.transcribe`, and `client.transcripts.submit`. If you do, you'll get the following error: "Interacting with the file system is not supported in this environment.". If you want to transcribe audio files, you must use a public URL, a stream, or a buffer. > [!WARNING] diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..677dcc4 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,8 @@ +module.exports = { + preset: "ts-jest", + testEnvironment: "node", + collectCoverage: true, + modulePathIgnorePatterns: ["/dist"], +}; + +process.env.TESTDATA_DIR = "tests/static"; diff --git a/package.json b/package.json index 0e184c4..1a51edd 100644 --- a/package.json +++ b/package.json @@ -1,37 +1,75 @@ { "name": "assemblyai", - "version": "4.0.0-beta.0", + "version": "4.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, as well as the latest LeMUR models.", + "engines": { + "node": ">=18" + }, "exports": { ".": { "types": "./dist/index.d.ts", + "bun": { + "types": "./dist/index.d.ts", + "default": "./dist/bun.mjs" + }, + "deno": { + "types": "./dist/index.d.ts", + "default": "./dist/deno.mjs" + }, + "workerd": "./dist/index.mjs", + "browser": "./dist/index.mjs", + "node": { + "types": "./dist/index.d.ts", + "import": "./dist/node.mjs", + "require": "./dist/node.cjs" + }, "import": "./dist/index.mjs", "require": "./dist/index.cjs", - "browser": "./dist/index.browser.js", "default": "./dist/index.cjs" }, "./package.json": "./package.json" }, + "imports": { + "#fs": { + "node": "./src/polyfills/fs/node.ts", + "bun": "./src/polyfills/fs/bun.ts", + "deno": "./src/polyfills/fs/deno.ts", + "default": "./src/polyfills/fs/index.ts" + }, + "#streams": { + "node": "./src/polyfills/streams/node.ts", + "default": "./src/polyfills/streams/index.ts" + }, + "#ws": { + "types": "./src/polyfills/ws/index.d.ts", + "browser": "./src/polyfills/ws/browser.mjs", + "default": { + "types": "./src/polyfills/ws/index.d.ts", + "import": "./src/polyfills/ws/index.mjs", + "require": "./src/polyfills/ws/index.cjs" + } + } + }, "type": "commonjs", - "main": "dist/index.cjs", - "module": "dist/index.mjs", - "types": "dist/index.d.ts", - "typings": "dist/index.d.ts", + "main": "./dist/index.cjs", + "require": "./dist/index.cjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "typings": "./dist/index.d.ts", "repository": { "type": "git", "url": "git+https://github.com/AssemblyAI/assemblyai-node-sdk.git" }, "publishConfig": { - "tag": "beta", + "tag": "latest", "access": "public", "registry": "https://registry.npmjs.org/" }, "scripts": { "build": "pnpm clean && pnpm rollup -c", - "clean": "rimraf dist", + "clean": "rimraf dist/*", "lint": "eslint -c .eslintrc.json '{src,tests}/**/*.{js,ts}' && publint", - "test": "pnpm lint && pnpm test:unit", - "test:unit": "jest --config jest.config.rollup.ts", + "test": "jest --config jest.config.js", "format": "prettier '**/*' --write", "generate-types": "tsx ./scripts/generate-types.ts && pnpm format", "copybara:dry-run": "./copybara.sh dry_run --init-history", @@ -47,14 +85,24 @@ "author": "AssemblyAI (https://www.assemblyai.com)", "license": "MIT", "homepage": "https://www.assemblyai.com/docs", + "bugs": { + "url": "https://github.com/AssemblyAI/assemblyai-node-sdk/issues", + "email": "support@assemblyai.com" + }, "files": [ "dist", - "src" + "src", + "package.json", + "README.md", + "CHANGELOG.md", + "docs" ], "devDependencies": { - "@rollup/plugin-alias": "^5.0.1", + "@babel/preset-env": "^7.23.5", + "@babel/preset-typescript": "^7.23.3", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-terser": "^0.4.4", + "@rollup/plugin-typescript": "^11.1.5", "@types/jest": "^29.5.5", "@types/node": "^20.5.7", "@types/websocket": "^1.0.8", @@ -62,12 +110,10 @@ "@typescript-eslint/eslint-plugin": "^6.7.5", "dotenv": "^16.3.1", "eslint": "^8.48.0", - "i": "^0.3.7", "jest": "^29.5.0", "jest-cli": "^29.5.0", "jest-fetch-mock": "^3.0.3", "jest-junit": "^16.0.0", - "jest-mock-extended": "^3.0.4", "jest-websocket-mock": "^2.4.1", "mock-socket": "^9.2.1", "npm": "^9.7.1", @@ -76,15 +122,11 @@ "publint": "^0.2.5", "rimraf": "^5.0.1", "rollup": "^3.25.1", - "rollup-plugin-typescript2": "^0.34.1", "ts-jest": "^29.1.0", - "ts-node": "^10.9.1", "tslib": "^2.5.3", "typescript": "^5.2.2" }, "dependencies": { - "@swimburger/isomorphic-streams": "^1.0.5", - "isomorphic-ws": "^5.0.0", "ws": "^8.13.0" } } diff --git a/rollup.config.js b/rollup.config.js index c34ab3c..403fb8d 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,30 +1,12 @@ -const pkg = require("./package.json"); -const ts = require("rollup-plugin-typescript2"); +const ts = require("@rollup/plugin-typescript"); const terser = require("@rollup/plugin-terser"); -const alias = require("@rollup/plugin-alias"); -const { nodeResolve } = require("@rollup/plugin-node-resolve"); +const nodeResolve = require("@rollup/plugin-node-resolve"); -const cjsFile = pkg.main; -const esmFile = pkg.module; -const browserFile = pkg.exports["."].browser; - -const defaultPlugins = [ - ts({ - tsconfigOverride: { exclude: ["**/*.test.ts"] }, - }), -]; -const defaultConfig = { - plugins: defaultPlugins, - external: ["fs", "isomorphic-ws", "@swimburger/isomorphic-streams"], +const umdConfig = { input: "src/index.ts", -}; - -const browserConfig = { - ...defaultConfig, plugins: [ - ...defaultConfig.plugins, - alias({ - entries: [{ find: "fs", replacement: "./src/browser/fs.ts" }], + ts({ + compilerOptions: { target: "ES2015", customConditions: ["browser"] }, }), nodeResolve({ browser: true }), ], @@ -33,32 +15,70 @@ const browserConfig = { module.exports = [ { - ...defaultConfig, + input: "src/index.ts", + plugins: [ + // we don't know where this will be used, could be browser, could be another runtime + // so we compile to es2015 for maximum compatibility. + ts({ compilerOptions: { target: "ES2015" } }), + ], + external: ["#ws"], + output: [ + { + file: `./dist/index.mjs`, + format: "es", + exports: "named", + }, + { + file: `./dist/index.cjs`, + format: "cjs", + exports: "named", + }, + ], + }, + { + input: "src/index.ts", + plugins: [ts({ compilerOptions: { customConditions: ["node"] } })], + external: ["fs", "stream", "stream/web", "#ws"], output: [ { - file: cjsFile, + file: `./dist/node.mjs`, + format: "es", + exports: "named", + }, + { + file: `./dist/node.cjs`, format: "cjs", exports: "named", }, + ], + }, + { + input: "src/index.ts", + plugins: [ts({ compilerOptions: { customConditions: ["deno"] } })], + external: ["#ws"], + output: [ { - file: esmFile, + file: `./dist/deno.mjs`, format: "es", exports: "named", }, ], }, { - ...browserConfig, + input: "src/index.ts", + plugins: [ts({ compilerOptions: { customConditions: ["bun"] } })], + external: ["#ws"], output: [ { - name: "assemblyai", - file: browserFile, - format: "esm", + file: `./dist/bun.mjs`, + format: "es", + exports: "named", }, ], }, + // Browser UMD build to reference directly in the browser. { - ...browserConfig, + ...umdConfig, output: [ { name: "assemblyai", @@ -67,9 +87,10 @@ module.exports = [ }, ], }, + // Browser UMD minified build to reference directly in the browser. { - ...browserConfig, - plugins: [...browserConfig.plugins, terser()], + ...umdConfig, + plugins: [...umdConfig.plugins, terser()], output: [ { name: "assemblyai", diff --git a/scripts/kitchensink.ts b/scripts/kitchensink.ts index e64b15b..7e8b57e 100644 --- a/scripts/kitchensink.ts +++ b/scripts/kitchensink.ts @@ -133,7 +133,7 @@ const transcribeParams: TranscribeParams = { await getParagraphs(transcript); console.error("Error expected but not thrown."); } catch (error) { - console.log("Error expected:", error.toString()); + console.log("Error expected:", error); await deleteTranscript(transcript); } }); @@ -145,7 +145,7 @@ const transcribeParams: TranscribeParams = { }); console.error("Error expected but not thrown."); } catch (error) { - console.log("Error expected:", error.toString()); + console.log("Error expected:", error); } })(); diff --git a/src/browser/fs.ts b/src/browser/fs.ts deleted file mode 100644 index 9f89f2f..0000000 --- a/src/browser/fs.ts +++ /dev/null @@ -1,8 +0,0 @@ -function throwError() { - throw new Error("Function is not supported in this environment."); -} - -export const createReadStream = throwError; -export default { - createReadStream, -}; diff --git a/src/polyfills/fs/bun.ts b/src/polyfills/fs/bun.ts new file mode 100644 index 0000000..69245da --- /dev/null +++ b/src/polyfills/fs/bun.ts @@ -0,0 +1,8 @@ +declare const Bun: { + file(path: string): BunFile; +}; +declare type BunFile = { + stream(): ReadableStream; +}; + +export const readFile = async (path: string) => Bun.file(path).stream(); diff --git a/src/polyfills/fs/deno.ts b/src/polyfills/fs/deno.ts new file mode 100644 index 0000000..a7bb221 --- /dev/null +++ b/src/polyfills/fs/deno.ts @@ -0,0 +1,9 @@ +declare const Deno: { + open(path: string): Promise; +}; +declare type DenoFile = { + readable: ReadableStream; +}; + +export const readFile = async (path: string) => + (await Deno.open(path)).readable; diff --git a/src/polyfills/fs/index.ts b/src/polyfills/fs/index.ts new file mode 100644 index 0000000..05f32bb --- /dev/null +++ b/src/polyfills/fs/index.ts @@ -0,0 +1,8 @@ +export const readFile = async function ( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + path: string +): Promise> { + throw new Error( + "Interacting with the file system is not supported in this environment." + ); +}; diff --git a/src/polyfills/fs/node.ts b/src/polyfills/fs/node.ts new file mode 100644 index 0000000..22a95aa --- /dev/null +++ b/src/polyfills/fs/node.ts @@ -0,0 +1,7 @@ +import { createReadStream } from "fs"; +import { Readable } from "stream"; + +export const readFile = async (path: string) => + Readable.toWeb( + createReadStream(path) + ) as unknown as ReadableStream; diff --git a/src/polyfills/streams/index.ts b/src/polyfills/streams/index.ts new file mode 100644 index 0000000..c1946bc --- /dev/null +++ b/src/polyfills/streams/index.ts @@ -0,0 +1,6 @@ +export const { WritableStream } = + typeof window !== "undefined" + ? window + : typeof global !== "undefined" + ? global + : globalThis; diff --git a/src/polyfills/streams/node.ts b/src/polyfills/streams/node.ts new file mode 100644 index 0000000..47591d8 --- /dev/null +++ b/src/polyfills/streams/node.ts @@ -0,0 +1 @@ +export { WritableStream } from "stream/web"; diff --git a/src/polyfills/ws/browser.mjs b/src/polyfills/ws/browser.mjs new file mode 100644 index 0000000..eb1754f --- /dev/null +++ b/src/polyfills/ws/browser.mjs @@ -0,0 +1,15 @@ +var ws = null; + +if (typeof WebSocket !== "undefined") { + ws = WebSocket; +} else if (typeof MozWebSocket !== "undefined") { + ws = MozWebSocket; +} else if (typeof global !== "undefined") { + ws = global.WebSocket || global.MozWebSocket; +} else if (typeof window !== "undefined") { + ws = window.WebSocket || window.MozWebSocket; +} else if (typeof self !== "undefined") { + ws = self.WebSocket || self.MozWebSocket; +} + +export default ws; diff --git a/src/polyfills/ws/index.cjs b/src/polyfills/ws/index.cjs new file mode 100644 index 0000000..104c2b1 --- /dev/null +++ b/src/polyfills/ws/index.cjs @@ -0,0 +1 @@ +module.exports = require("ws"); diff --git a/src/polyfills/ws/index.d.ts b/src/polyfills/ws/index.d.ts new file mode 100644 index 0000000..76226dc --- /dev/null +++ b/src/polyfills/ws/index.d.ts @@ -0,0 +1,2 @@ +import ws from "ws"; +export default ws; diff --git a/src/polyfills/ws/index.mjs b/src/polyfills/ws/index.mjs new file mode 100644 index 0000000..76226dc --- /dev/null +++ b/src/polyfills/ws/index.mjs @@ -0,0 +1,2 @@ +import ws from "ws"; +export default ws; diff --git a/src/services/files/index.ts b/src/services/files/index.ts index 3c81fbc..640d698 100644 --- a/src/services/files/index.ts +++ b/src/services/files/index.ts @@ -1,4 +1,4 @@ -import fs from "fs"; +import { readFile } from "#fs"; import { BaseService } from "../base"; import { UploadedFile, FileUploadParams, FileUploadData } from "../.."; @@ -10,7 +10,7 @@ export class FileService extends BaseService { */ async upload(input: FileUploadParams): Promise { let fileData: FileUploadData; - if (typeof input === "string") fileData = fs.createReadStream(input); + if (typeof input === "string") fileData = await readFile(input); else fileData = input; const data = await this.fetchJson("/v2/upload", { diff --git a/src/services/realtime/service.ts b/src/services/realtime/service.ts index db563a9..7d13651 100644 --- a/src/services/realtime/service.ts +++ b/src/services/realtime/service.ts @@ -1,5 +1,5 @@ -import { WritableStream } from "@swimburger/isomorphic-streams"; -import WebSocket from "isomorphic-ws"; +import { WritableStream } from "#streams"; +import WebSocket from "#ws"; import { ErrorEvent, MessageEvent, CloseEvent } from "ws"; import { RealtimeEvents, @@ -33,8 +33,8 @@ export class RealtimeService { this.realtimeUrl = params.realtimeUrl ?? defaultRealtimeUrl; this.sampleRate = params.sampleRate ?? 16_000; this.wordBoost = params.wordBoost; - if ("apiKey" in params) this.apiKey = params.apiKey; - if ("token" in params) this.token = params.token; + if ("apiKey" in params && params.apiKey) this.apiKey = params.apiKey; + if ("token" in params && params.token) this.token = params.token; if (!(this.apiKey || this.token)) { throw new Error("API key or temporary token is required."); diff --git a/tsconfig.json b/tsconfig.json index fae541a..2f66de4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,9 +2,9 @@ "compilerOptions": { "outDir": "./dist", "rootDir": "./src", - "module": "esnext", - "moduleResolution": "node", - "target": "es6", + "module": "ESNext", + "moduleResolution": "Bundler", + "target": "ES2021", "allowJs": false, "declaration": true, "removeComments": false, @@ -13,6 +13,5 @@ "esModuleInterop": true, "allowSyntheticDefaultImports": true }, - "include": ["src"], - "exclude": ["node_modules", "scripts", "**/*.spec.ts"] + "include": ["src"] } diff --git a/tsconfig.scripts.json b/tsconfig.scripts.json new file mode 100644 index 0000000..5dd007e --- /dev/null +++ b/tsconfig.scripts.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": "." + }, + "include": ["src", "scripts"] +} diff --git a/tsconfig.test.json b/tsconfig.test.json index b059196..8eb1e98 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -1,5 +1,8 @@ { "extends": "./tsconfig.json", - "include": ["src/**/*.ts", "tests/**/*.ts"], - "exclude": ["node_modules", "**/*.spec.ts", "**/*.ignore.*"] + "compilerOptions": { + "rootDir": "." + }, + "include": ["src", "tests"], + "exclude": ["node_modules", "scripts"] }