Skip to content

Commit

Permalink
Merge pull request #48 from AssemblyAI/E07417BDFEA3614F5967B1520F8B2F61
Browse files Browse the repository at this point in the history
Sync from internal repo (2024/05/03)
  • Loading branch information
Swimburger authored May 3, 2024
2 parents c6a4520 + 5e9873f commit 3d8bd1f
Show file tree
Hide file tree
Showing 21 changed files with 303 additions and 76 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changelog

## [4.4.2]

### Changed

- Caching is disabled for all HTTP request made by the SDK
- 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.

## [4.4.1] - 2024-04-16

### Changed
Expand Down
18 changes: 7 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "assemblyai",
"version": "4.4.1",
"version": "4.4.2",
"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"
Expand All @@ -17,7 +17,7 @@
"default": "./dist/deno.mjs"
},
"workerd": "./dist/index.mjs",
"browser": "./dist/index.mjs",
"browser": "./dist/browser.mjs",
"node": {
"types": "./dist/index.d.ts",
"import": "./dist/node.mjs",
Expand All @@ -40,14 +40,10 @@
"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"
}
"#websocket": {
"browser": "./src/polyfills/websocket/browser.ts",
"node": "./src/polyfills/websocket/default.ts",
"default": "./src/polyfills/websocket/default.ts"
}
},
"type": "commonjs",
Expand All @@ -70,7 +66,7 @@
"clean": "rimraf dist/* && rimraf temp/* && rimraf temp-docs/*",
"lint": "eslint -c .eslintrc.json '{src,tests}/**/*.{js,ts}' && publint && tsc --noEmit -p tsconfig.json",
"test": "pnpm run test:unit && pnpm run test:integration",
"test:unit": "jest --config jest.unit.config.js",
"test:unit": "jest --config jest.unit.config.js --testTimeout 1000",
"test:integration": "jest --config jest.integration.config.js --testTimeout 360000",
"format": "prettier '**/*' --write",
"generate:types": "tsx ./scripts/generate-types.ts && prettier 'src/types/*.generated.ts' --write",
Expand Down
20 changes: 16 additions & 4 deletions rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ module.exports = [
// so we compile to es2015 for maximum compatibility.
ts({ compilerOptions: { target: "ES2015" } }),
],
external: ["#ws"],
external: ["ws"],
output: [
{
file: `./dist/index.mjs`,
Expand All @@ -38,7 +38,7 @@ module.exports = [
{
input: "src/index.ts",
plugins: [ts({ compilerOptions: { customConditions: ["node"] } })],
external: ["fs", "stream", "stream/web", "#ws"],
external: ["fs", "stream", "stream/web", "ws"],
output: [
{
file: `./dist/node.mjs`,
Expand All @@ -55,7 +55,7 @@ module.exports = [
{
input: "src/index.ts",
plugins: [ts({ compilerOptions: { customConditions: ["deno"] } })],
external: ["#ws"],
external: ["ws"],
output: [
{
file: `./dist/deno.mjs`,
Expand All @@ -67,7 +67,7 @@ module.exports = [
{
input: "src/index.ts",
plugins: [ts({ compilerOptions: { customConditions: ["bun"] } })],
external: ["#ws"],
external: ["ws"],
output: [
{
file: `./dist/bun.mjs`,
Expand All @@ -76,6 +76,18 @@ module.exports = [
},
],
},
// Browser ESM build
{
input: "src/index.ts",
plugins: [ts({ compilerOptions: { customConditions: ["browser"] } })],
output: [
{
file: `./dist/browser.mjs`,
format: "es",
exports: "named",
},
],
},
// Browser UMD build to reference directly in the browser.
{
...umdConfig,
Expand Down
18 changes: 18 additions & 0 deletions src/polyfills/websocket/browser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { PolyfillWebSocketFactory, PolyfillWebSocket } from ".";
export { PolyfillWebSocket } from ".";

const PolyfillWebSocket =
WebSocket ?? global?.WebSocket ?? window?.WebSocket ?? self?.WebSocket;

export const factory: PolyfillWebSocketFactory = (
url: string,
params?: unknown,
) => {
if (params) {
return new PolyfillWebSocket(
url,
params as string | string[],
) as unknown as PolyfillWebSocket;
}
return new PolyfillWebSocket(url) as unknown as PolyfillWebSocket;
};
8 changes: 8 additions & 0 deletions src/polyfills/websocket/default.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import ws from "ws";
import { PolyfillWebSocket, PolyfillWebSocketFactory } from ".";
export { PolyfillWebSocket } from ".";

export const factory: PolyfillWebSocketFactory = (
url: string,
params?: unknown,
) => new ws(url, params as ws.ClientOptions) as unknown as PolyfillWebSocket;
41 changes: 41 additions & 0 deletions src/polyfills/websocket/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import ws, { Event, ErrorEvent, CloseEvent, MessageEvent } from "ws";

export type PolyfillWebSocket = {
OPEN: typeof ws.OPEN;
binaryType: string;
onopen: ((event: Event) => void) | null;
onerror: ((event: ErrorEvent) => void) | null;
onclose: ((event: CloseEvent) => void) | null;
onmessage: ((event: MessageEvent) => void) | null;
readonly readyState:
| typeof ws.CONNECTING
| typeof ws.OPEN
| typeof ws.CLOSING
| typeof ws.CLOSED;
removeAllListeners?: () => void;
send(
data:
| string
| number
| Buffer
| DataView
| ArrayBufferView
| Uint8Array
| ArrayBuffer
| SharedArrayBuffer
| readonly unknown[]
| readonly number[]
| { valueOf(): ArrayBuffer }
| { valueOf(): SharedArrayBuffer }
| { valueOf(): Uint8Array }
| { valueOf(): readonly number[] }
| { valueOf(): string }
| { [Symbol.toPrimitive](hint: string): string },
): unknown;
close(): unknown;
};

export type PolyfillWebSocketFactory = (
url: string,
params?: unknown,
) => PolyfillWebSocket;
15 changes: 0 additions & 15 deletions src/polyfills/ws/browser.mjs

This file was deleted.

1 change: 0 additions & 1 deletion src/polyfills/ws/index.cjs

This file was deleted.

2 changes: 0 additions & 2 deletions src/polyfills/ws/index.d.ts

This file was deleted.

2 changes: 0 additions & 2 deletions src/polyfills/ws/index.mjs

This file was deleted.

1 change: 1 addition & 0 deletions src/services/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export abstract class BaseService {
"Content-Type": "application/json",
...init.headers,
};
init.cache = "no-store";
if (!input.startsWith("http")) input = this.params.baseUrl + input;

const response = await fetch(input, init);
Expand Down
21 changes: 19 additions & 2 deletions src/services/files/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@ export class FileService extends BaseService {
*/
async upload(input: FileUploadParams): Promise<string> {
let fileData: FileUploadData;
if (typeof input === "string") fileData = await readFile(input);
else fileData = input;
if (typeof input === "string") {
if (input.startsWith("data:")) {
fileData = dataUrlToBlob(input);
} else {
fileData = await readFile(input);
}
} else fileData = input;

const data = await this.fetchJson<UploadedFile>("/v2/upload", {
method: "POST",
Expand All @@ -24,3 +29,15 @@ export class FileService extends BaseService {
return data.upload_url;
}
}

function dataUrlToBlob(dataUrl: string) {
const arr = dataUrl.split(",");
const mime = arr[0].match(/:(.*?);/)![1];
const bstr = atob(arr[1]);
let n = bstr.length;
const u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], { type: mime });
}
27 changes: 15 additions & 12 deletions src/services/realtime/service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { WritableStream } from "#streams";
import WebSocket from "#ws";
import {
PolyfillWebSocket,
factory as polyfillWebSocketFactory,
} from "#websocket";
import { ErrorEvent, MessageEvent, CloseEvent } from "ws";
import {
RealtimeEvents,
Expand Down Expand Up @@ -52,7 +55,7 @@ export class RealtimeTranscriber {
private endUtteranceSilenceThreshold?: number;
private disablePartialTranscripts?: boolean;

private socket?: WebSocket;
private socket?: PolyfillWebSocket;
private listeners: RealtimeListeners = {};
private sessionTerminatedResolve?: () => void;

Expand Down Expand Up @@ -136,15 +139,15 @@ export class RealtimeTranscriber {
const url = this.connectionUrl();

if (this.token) {
this.socket = new WebSocket(url.toString());
this.socket = polyfillWebSocketFactory(url.toString());
} else {
this.socket = new WebSocket(url.toString(), {
this.socket = polyfillWebSocketFactory(url.toString(), {
headers: { Authorization: this.apiKey },
});
}
this.socket.binaryType = "arraybuffer";
this.socket!.binaryType = "arraybuffer";

this.socket.onopen = () => {
this.socket!.onopen = () => {
if (
this.endUtteranceSilenceThreshold === undefined ||
this.endUtteranceSilenceThreshold === null
Expand All @@ -156,7 +159,7 @@ export class RealtimeTranscriber {
);
};

this.socket.onclose = ({ code, reason }: CloseEvent) => {
this.socket!.onclose = ({ code, reason }: CloseEvent) => {
if (!reason) {
if (code in RealtimeErrorType) {
reason = RealtimeErrorMessages[code as RealtimeErrorType];
Expand All @@ -165,12 +168,12 @@ export class RealtimeTranscriber {
this.listeners.close?.(code, reason);
};

this.socket.onerror = (event: ErrorEvent) => {
this.socket!.onerror = (event: ErrorEvent) => {
if (event.error) this.listeners.error?.(event.error as Error);
else this.listeners.error?.(new Error(event.message));
};

this.socket.onmessage = ({ data }: MessageEvent) => {
this.socket!.onmessage = ({ data }: MessageEvent) => {
const message = JSON.parse(data.toString()) as RealtimeMessage;
if ("error" in message) {
this.listeners.error?.(new RealtimeError(message.error));
Expand Down Expand Up @@ -242,15 +245,15 @@ export class RealtimeTranscriber {
}

private send(data: BufferLike) {
if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
if (!this.socket || this.socket.readyState !== this.socket.OPEN) {
throw new Error("Socket is not open for communication");
}
this.socket.send(data);
}

async close(waitForSessionTermination = true) {
if (this.socket) {
if (this.socket.readyState === WebSocket.OPEN) {
if (this.socket.readyState === this.socket.OPEN) {
if (waitForSessionTermination) {
const sessionTerminatedPromise = new Promise<void>((resolve) => {
this.sessionTerminatedResolve = resolve;
Expand All @@ -261,7 +264,7 @@ export class RealtimeTranscriber {
this.socket.send(terminateSessionMessage);
}
}
if ("removeAllListeners" in this.socket) this.socket.removeAllListeners();
if (this.socket?.removeAllListeners) this.socket.removeAllListeners();
this.socket.close();
}

Expand Down
8 changes: 6 additions & 2 deletions src/services/transcripts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,12 @@ export class TranscriptService extends BaseService {
// audio is local path, upload local file
audioUrl = await this.files.upload(path);
} else {
// audio is not a local path, assume it's a URL
audioUrl = audio;
if (audio.startsWith("data:")) {
audioUrl = await this.files.upload(audio);
} else {
// audio is not a local path, and not a data-URI, assume it's a normal URL
audioUrl = audio;
}
}
} else {
// audio is of uploadable type
Expand Down
Loading

0 comments on commit 3d8bd1f

Please sign in to comment.