diff --git a/CHANGELOG.md b/CHANGELOG.md index 92975a2..3d404ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,15 @@ # Changelog -## [4.4.2] +## [4.4.3] - 2024-05-09 + +- Add react-native exports that resolve to the browser version of the library. + +## [4.4.2] - 2024-05-03 ### Changed - Caching is disabled for all HTTP request made by the SDK +- Accept data-URIs in `client.files.upload(dataUri)`, `client.transcripts.submit(audio: dataUri)`, `client.transcripts.transcribe(audio: dataUri)`. - Change how the WebSocket libraries are imported for better compatibility across frameworks and runtimes. The library no longer relies on a internal `#ws` import, and instead compiles the imports into the dist bundles. Browser builds will use the native `WebSocket`, other builds will use the `ws` package. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..19dff87 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,96 @@ +# Contribute + +We welcome contributions from the community. + +## Create issues + +Before you create an issue, check the [existing issues](https://github.com/AssemblyAI/assemblyai-node-sdk/issues) to see if someone's already created a similar one. + +- If you find an existing issue, add any relevant information and upvote the issue using a thumb-up emoji (👍). +- If you don't find an issue, [create an issue](https://github.com/AssemblyAI/assemblyai-node-sdk/issues/new) so we can help you, and discuss solutions and next steps. + +## Submit a PR + +Before submitting a PR, to avoid wasting your valuable time, make sure you are aligned with the maintainers of the repository by discussing the desired changes in a GitHub issue. + +To make changes, follow these steps: + +1. Fork the repository and clone your own fork. +2. [Set up pnpm](https://pnpm.io/installation). +3. Run `pnpm install`. +4. Make your changes. + +- Make the changes themselves. +- Add tests that verify your changes. +- Describe your changes in [CHANGELOG.md](./CHANGELOG.md). + +1. Before committing your changes, run the following scripts: + - `pnpm format` + - `pnpm lint` + - `pnpm test` (see [run integration tests](#run-integration-tests)) + - `pnpm build` +2. If linting and testing passes, commit your changes. +3. Submit your PR. + +## Run integration tests + +The integration tests require an upgraded AssemblyAI account. +The integration tests will use the AssemblyAI API and you'll be charged for your usage. +Reach out to us if you want some credits for running integration tests. + +The integration tests require the following environment variables: + +- `ASSEMBLYAI_API_KEY`: Your AssemblyAI API key +- `TEST_TRANSCRIPT_ID`: The transcript ID of a completed transcript your created +- `TEST_TRANSCRIPT_IDS`: One or more completed transcript IDs, separated by a comma `,` + +You can set these environment variables in your shell, or by creating a _.env_ file with the following format: + +```plaintext +ASSEMBLYAI_API_KEY=... +TEST_TRANSCRIPT_ID=... +TEST_TRANSCRIPT_IDS=... +``` + +## Generate types from OpenAPI and AsyncAPI spec + +1. Configure the location of the OpenAPI and AsyncAPI spec as environment variables: + - `OPENAPI_SPEC`: Path or URL to the AssemblyAI OpenAPI spec + - `ASYNCAPI_SPEC`: Path or URL to the AssemblyAI AsyncAPI spec + +2. Generate the types using `pnpm generate:types`. + + +## Notes about the JavaScript SDK + +### Rollup + +We use Rollup to build the JavaScript SDK. +Our Rollup configuration generates the following versions: +- node.{cjs,mjs}: The Node runtime version using CommonJS and ESModule syntax. +- bun.mjs: The Bun runtime version. +- deno.mjs: The Deno runtime version. +- browser: The browser runtime version that uses the native WebSocket instead of `ws`. + - browser.mjs: Using ESModule syntax, to be used by users using a bundler. + - assemblyai.umd?(.min).js: Using UMD syntax which creates, to be used directly from a script tag. + This script adds the SDK to the global `assemblyai` variable. +- index.cjs: The default version using CommonJS syntax. +- index.mjs: The default version using ESModule syntax. +- index.d.ts: The TypeScript types for the SDK. + +### Package.json exports + +When a user uses the SDK, the users' runtime will automatically choose the runtime-specific version +if defined in the package.json `exports` object, or fall back to the default version. + +For example, Bun will use *bun.mjs* and Cloudflare Workers (workerd) will use *index.mjs*. + +### Package.json imports + +To make the SDK compatible with a variety of runtimes, we have to polyfill certain features. +We're doing this using package.json `imports`, which lets you define runtime-specific imports. +When these imports are TypeScript files, Rollup automatically includes the code in the output for the specified runtime. + +When they are ordinary JavaScript files, Rollup leaves the private import in the output as is. +This works fine in most runtimes, but some runtimes like React Native/Expo do not support this. +Therefore, we should restrict ourselves to using only TypeScript code with package.json imports. diff --git a/README.md b/README.md index 4a7cd05..161379e 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,30 @@ const client = new AssemblyAI({ You can now use the `client` object to interact with the AssemblyAI API. +### Using a CDN + +You can use automatic CDNs like [UNPKG](https://unpkg.com/) to load the library from a script tag. + +- Replace `:version` with the desired version or `latest`. +- Remove `.min` to load the non-minified version. + +```html + +``` + +The script creates a global `assemblyai` variable containing all the services. +Here's how you create a `RealtimeTranscriber` object. + +```js +const { RealtimeTranscriber } = assemblyai; +const transcriber = new RealtimeTranscriber({ + token: "[GENERATE TEMPORARY AUTH TOKEN IN YOUR API]", + ... +}); +``` + +For type support in your IDE, see [Reference types from JavaScript](./docs/reference-types-from-js.md). + ## Speech-To-Text ### Transcribe audio and video files @@ -229,24 +253,25 @@ const rt = client.realtime.transcriber({ > [!WARNING] > Storing your API key in client-facing applications exposes your API key. -> Generate a temporary auth token on the server and pass it to your client. -> _Server code_: +> Generate a temporary auth token on the server and pass it to your client. +> _Server code_: +> > ```typescript > const token = await client.realtime.createTemporaryToken({ expires_in = 60 }); > // TODO: return token to client > ``` > > _Client code_: +> > ```typescript > import { RealtimeTranscriber } from "assemblyai"; > // TODO: implement getToken to retrieve token from server > const token = await getToken(); > const rt = new RealtimeTranscriber({ -> token +> token, > }); > ``` - You can configure the following events. @@ -351,3 +376,7 @@ const response = await client.lemur.purgeRequestData(lemurResponse.request_id); ``` + +## Contributing + +If you want to contribute to the JavaScript SDK, follow the guidelines in [CONTRIBUTING.md](./CONTRIBUTING.md). diff --git a/docs/reference-types-from-js.md b/docs/reference-types-from-js.md new file mode 100644 index 0000000..b760ced --- /dev/null +++ b/docs/reference-types-from-js.md @@ -0,0 +1,27 @@ +# Reference types from JavaScript + +Types are automatically configured in most IDEs when you import the `assemblyai` module using `require` or `import`. +However, if you're using the _assemblyai.umd.js_ or _assemblyai.umd.min.js_ script, +you need to manually reference the types. + +1. Install the `assemblyai` module locally. +2. Create an _assemblyai.d.ts_ file with the following content. + ```typescript + import AssemblyAIModule from "assemblyai"; + declare global { + const assemblyai: typeof AssemblyAIModule; + } + ``` + This will import the TypeScript types from the `assemblyai` module, + and configure them as the global `assemblyai` variable. +3. Reference the _assemblyai.d.ts_ file at the top of your script file. + + ```js + /// + const { RealtimeTranscriber } = assemblyai; + ... + ``` + + Your IDE will load the types specified in the `` tag. + + > [!INFO] > `/// ` tags only work in script files, not script-blocks, and should be at the top of the file. diff --git a/package.json b/package.json index 18b5bc2..f81ca2a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "assemblyai", - "version": "4.4.2", + "version": "4.4.3", "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" @@ -18,6 +18,7 @@ }, "workerd": "./dist/index.mjs", "browser": "./dist/browser.mjs", + "react-native": "./dist/browser.mjs", "node": { "types": "./dist/index.d.ts", "import": "./dist/node.mjs", @@ -47,6 +48,8 @@ } }, "type": "commonjs", + "react-native": "./dist/browser.mjs", + "browser": "./dist/browser.mjs", "main": "./dist/index.cjs", "require": "./dist/index.cjs", "module": "./dist/index.mjs", @@ -68,7 +71,7 @@ "test": "pnpm run test:unit && pnpm run test:integration", "test:unit": "jest --config jest.unit.config.js --testTimeout 1000", "test:integration": "jest --config jest.integration.config.js --testTimeout 360000", - "format": "prettier '**/*' --write", + "format": "prettier {*,**/*} --write --no-error-on-unmatched-pattern", "generate:types": "tsx ./scripts/generate-types.ts && prettier 'src/types/*.generated.ts' --write", "generate:reference": "typedoc", "copybara:dry-run": "./copybara.sh dry_run --init-history", @@ -97,16 +100,16 @@ "docs" ], "devDependencies": { - "@babel/preset-env": "^7.24.0", - "@babel/preset-typescript": "^7.23.3", + "@babel/preset-env": "^7.24.5", + "@babel/preset-typescript": "^7.24.1", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^11.1.6", "@types/jest": "^29.5.12", - "@types/node": "^18.11.9", + "@types/node": "^18.19.32", "@types/websocket": "^1.0.10", "@types/ws": "^8.5.10", - "@typescript-eslint/eslint-plugin": "^7.2.0", + "@typescript-eslint/eslint-plugin": "^7.8.0", "dotenv": "^16.4.5", "eslint": "^8.57.0", "eslint-plugin-tsdoc": "^0.2.17", @@ -120,14 +123,14 @@ "prettier": "^3.2.5", "publint": "^0.2.7", "rimraf": "^5.0.5", - "rollup": "^4.13.0", + "rollup": "^4.17.2", "ts-jest": "^29.1.2", "tslib": "^2.5.3", - "typescript": "^5.4.2", - "typedoc": "^0.25.12", - "typedoc-plugin-extras": "^3.0.0" + "typedoc": "^0.25.13", + "typedoc-plugin-extras": "^3.0.0", + "typescript": "^5.4.5" }, "dependencies": { - "ws": "^8.16.0" + "ws": "^8.17.0" } } diff --git a/scripts/generate-types.ts b/scripts/generate-types.ts index 96f7f19..9c489a7 100644 --- a/scripts/generate-types.ts +++ b/scripts/generate-types.ts @@ -1,7 +1,15 @@ import openapiTS from "openapi-typescript"; import fs from "fs"; +import "dotenv/config"; + +async function generateTypes( + apiSpecPath: string | undefined, + outputPath: string, +) { + if (!apiSpecPath) { + throw new Error("API spec path not provided."); + } -async function generateTypes(apiSpecPath: string, outputPath: string) { const localPath = new URL(apiSpecPath, import.meta.url); let output = await openapiTS(localPath, { alphabetize: true, @@ -90,5 +98,5 @@ type OneOf = T extends [infer Only] ? Only : T extends [infer A fs.writeFileSync(outputPath, output); } -generateTypes("../../spec/openapi.yml", "./src/types/openapi.generated.ts"); -generateTypes("../../spec/asyncapi.yml", "./src/types/asyncapi.generated.ts"); +generateTypes(process.env.OPENAPI_SPEC, "./src/types/openapi.generated.ts"); +generateTypes(process.env.ASYNCAPI_SPEC, "./src/types/asyncapi.generated.ts");