UNPKG

@corti/dictation-web

Version:
289 lines 19.4 kB
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); }; var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { if (kind === "m") throw new TypeError("Private method is not writable"); if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; }; var _DictationController_instances, _DictationController_cortiClient, _DictationController_webSocket, _DictationController_closeTimeout, _DictationController_callbacks, _DictationController_lastDictationConfig, _DictationController_lastSocketUrl, _DictationController_lastSocketProxy, _DictationController_outboundQueue, _DictationController_socketReady, _DictationController_connectingPromise, _DictationController_connectionGeneration, _DictationController_isConnecting, _DictationController_configHasChanged, _DictationController_doConnect, _DictationController_connectProxy, _DictationController_connectAuth, _DictationController_setupWebSocketHandlers, _DictationController_isSocketOpen, _DictationController_drain; import { CortiClient, CortiWebSocketProxyClient, } from "@corti/sdk"; import { DEFAULT_DICTATION_CONFIG } from "../constants.js"; import { errorEvent } from "../utils/events.js"; export class DictationController { constructor(host) { _DictationController_instances.add(this); _DictationController_cortiClient.set(this, null); _DictationController_webSocket.set(this, null); _DictationController_closeTimeout.set(this, void 0); _DictationController_callbacks.set(this, void 0); _DictationController_lastDictationConfig.set(this, null); _DictationController_lastSocketUrl.set(this, void 0); _DictationController_lastSocketProxy.set(this, void 0); _DictationController_outboundQueue.set(this, []); _DictationController_socketReady.set(this, false); _DictationController_connectingPromise.set(this, null); _DictationController_connectionGeneration.set(this, 0); _DictationController_isConnecting.set(this, false); this.mediaRecorderHandler = (data) => { if (__classPrivateFieldGet(this, _DictationController_socketReady, "f") && __classPrivateFieldGet(this, _DictationController_instances, "m", _DictationController_isSocketOpen).call(this)) { __classPrivateFieldGet(this, _DictationController_webSocket, "f")?.send(data); __classPrivateFieldGet(this, _DictationController_callbacks, "f")?.onNetworkActivity?.("sent", { size: data.size, type: "audio", }); return; } __classPrivateFieldGet(this, _DictationController_outboundQueue, "f").push(data); }; this.host = host; host.addController(this); } hostDisconnected() { this.cleanup(); } async connect(dictationConfig = DEFAULT_DICTATION_CONFIG, callbacks = {}) { // If a connection attempt is already in progress with the same config, reuse it // to avoid opening multiple sockets when connect() is called concurrently. if (__classPrivateFieldGet(this, _DictationController_connectingPromise, "f") && !__classPrivateFieldGet(this, _DictationController_instances, "m", _DictationController_configHasChanged).call(this)) { return __classPrivateFieldGet(this, _DictationController_connectingPromise, "f"); } // #isConnecting must be set synchronously before #doConnect runs, because // #doConnect calls cleanup() which closes the old socket, firing its "close" // event synchronously. Handlers that check isConnecting() need to see true // at that point — before #connectingPromise is even assigned. __classPrivateFieldSet(this, _DictationController_isConnecting, true, "f"); __classPrivateFieldSet(this, _DictationController_connectingPromise, __classPrivateFieldGet(this, _DictationController_instances, "m", _DictationController_doConnect).call(this, dictationConfig, callbacks).finally(() => { __classPrivateFieldSet(this, _DictationController_isConnecting, false, "f"); __classPrivateFieldSet(this, _DictationController_connectingPromise, null, "f"); }), "f"); return __classPrivateFieldGet(this, _DictationController_connectingPromise, "f"); } async pause() { if (__classPrivateFieldGet(this, _DictationController_socketReady, "f") && __classPrivateFieldGet(this, _DictationController_instances, "m", _DictationController_isSocketOpen).call(this)) { __classPrivateFieldGet(this, _DictationController_webSocket, "f")?.send(JSON.stringify({ type: "flush" })); __classPrivateFieldGet(this, _DictationController_callbacks, "f")?.onNetworkActivity?.("sent", { type: "flush" }); return; } __classPrivateFieldGet(this, _DictationController_outboundQueue, "f").push({ type: "flush" }); } isConnectionOpen() { return (__classPrivateFieldGet(this, _DictationController_webSocket, "f") !== null && (__classPrivateFieldGet(this, _DictationController_webSocket, "f").readyState === WebSocket.OPEN || __classPrivateFieldGet(this, _DictationController_webSocket, "f").readyState === WebSocket.CONNECTING)); } isConnecting() { return __classPrivateFieldGet(this, _DictationController_isConnecting, "f"); } async waitForConnection() { await __classPrivateFieldGet(this, _DictationController_connectingPromise, "f"); } async closeConnection(onClose) { await new Promise((resolve, reject) => { const oldSocket = __classPrivateFieldGet(this, _DictationController_webSocket, "f"); __classPrivateFieldSet(this, _DictationController_webSocket, null, "f"); if (!oldSocket || (oldSocket.readyState !== WebSocket.OPEN && oldSocket.readyState !== WebSocket.CONNECTING)) { __classPrivateFieldSet(this, _DictationController_socketReady, false, "f"); resolve(); return; } oldSocket.on("close", (event) => { if (__classPrivateFieldGet(this, _DictationController_closeTimeout, "f")) { clearTimeout(__classPrivateFieldGet(this, _DictationController_closeTimeout, "f")); __classPrivateFieldSet(this, _DictationController_closeTimeout, undefined, "f"); } if (onClose) { onClose(event); } resolve(); }); const wasReady = __classPrivateFieldGet(this, _DictationController_socketReady, "f"); __classPrivateFieldSet(this, _DictationController_socketReady, false, "f"); oldSocket.on("message", (message) => { __classPrivateFieldGet(this, _DictationController_callbacks, "f")?.onNetworkActivity?.("received", message); if (__classPrivateFieldGet(this, _DictationController_callbacks, "f")?.onMessage) { __classPrivateFieldGet(this, _DictationController_callbacks, "f")?.onMessage(message); } // closeConnection() may be called before CONFIG_ACCEPTED arrives (e.g. // openConnection() followed immediately by closeConnection()). We can't // use the outbound queue here because #webSocket is already null, so we // send "end" directly on oldSocket as soon as config is accepted. if (!wasReady && message.type === "CONFIG_ACCEPTED") { oldSocket.sendEnd({ type: "end" }); __classPrivateFieldGet(this, _DictationController_callbacks, "f")?.onNetworkActivity?.("sent", { type: "end" }); return; } if (message.type === "ended") { if (__classPrivateFieldGet(this, _DictationController_closeTimeout, "f")) { clearTimeout(__classPrivateFieldGet(this, _DictationController_closeTimeout, "f")); __classPrivateFieldSet(this, _DictationController_closeTimeout, undefined, "f"); } resolve(); return; } }); if (wasReady) { oldSocket.sendEnd({ type: "end" }); __classPrivateFieldGet(this, _DictationController_callbacks, "f")?.onNetworkActivity?.("sent", { type: "end" }); } __classPrivateFieldSet(this, _DictationController_closeTimeout, window.setTimeout(() => { reject(new Error("Connection close timeout")); if (oldSocket?.readyState === WebSocket.OPEN) { oldSocket.close(); } }, 10000), "f"); }); } cleanup() { var _a; // Incrementing generation invalidates any in-flight #doConnect awaits, // causing them to discard their socket and return "superseded". __classPrivateFieldSet(this, _DictationController_connectionGeneration, (_a = __classPrivateFieldGet(this, _DictationController_connectionGeneration, "f"), _a++, _a), "f"); __classPrivateFieldSet(this, _DictationController_socketReady, false, "f"); if (__classPrivateFieldGet(this, _DictationController_closeTimeout, "f")) { clearTimeout(__classPrivateFieldGet(this, _DictationController_closeTimeout, "f")); __classPrivateFieldSet(this, _DictationController_closeTimeout, undefined, "f"); } if (this.isConnectionOpen()) { __classPrivateFieldGet(this, _DictationController_webSocket, "f")?.close(); } __classPrivateFieldSet(this, _DictationController_webSocket, null, "f"); __classPrivateFieldSet(this, _DictationController_cortiClient, null, "f"); __classPrivateFieldSet(this, _DictationController_lastDictationConfig, null, "f"); __classPrivateFieldSet(this, _DictationController_lastSocketUrl, undefined, "f"); __classPrivateFieldSet(this, _DictationController_lastSocketProxy, undefined, "f"); if (__classPrivateFieldGet(this, _DictationController_outboundQueue, "f").length > 0) { this.host.dispatchEvent(errorEvent(`${__classPrivateFieldGet(this, _DictationController_outboundQueue, "f").length} unsent message(s) were discarded because the configuration changed before the connection was closed`)); } __classPrivateFieldSet(this, _DictationController_outboundQueue, [], "f"); } } _DictationController_cortiClient = new WeakMap(), _DictationController_webSocket = new WeakMap(), _DictationController_closeTimeout = new WeakMap(), _DictationController_callbacks = new WeakMap(), _DictationController_lastDictationConfig = new WeakMap(), _DictationController_lastSocketUrl = new WeakMap(), _DictationController_lastSocketProxy = new WeakMap(), _DictationController_outboundQueue = new WeakMap(), _DictationController_socketReady = new WeakMap(), _DictationController_connectingPromise = new WeakMap(), _DictationController_connectionGeneration = new WeakMap(), _DictationController_isConnecting = new WeakMap(), _DictationController_instances = new WeakSet(), _DictationController_configHasChanged = function _DictationController_configHasChanged() { return (JSON.stringify(this.host._dictationConfig) !== JSON.stringify(__classPrivateFieldGet(this, _DictationController_lastDictationConfig, "f")) || this.host._socketUrl !== __classPrivateFieldGet(this, _DictationController_lastSocketUrl, "f") || JSON.stringify(this.host._socketProxy) !== JSON.stringify(__classPrivateFieldGet(this, _DictationController_lastSocketProxy, "f"))); }, _DictationController_doConnect = async function _DictationController_doConnect(dictationConfig, callbacks) { const newConnection = __classPrivateFieldGet(this, _DictationController_instances, "m", _DictationController_configHasChanged).call(this) || !this.isConnectionOpen(); if (newConnection) { this.cleanup(); __classPrivateFieldSet(this, _DictationController_lastDictationConfig, this.host._dictationConfig || null, "f"); __classPrivateFieldSet(this, _DictationController_lastSocketUrl, this.host._socketUrl, "f"); __classPrivateFieldSet(this, _DictationController_lastSocketProxy, this.host._socketProxy, "f"); const generation = __classPrivateFieldGet(this, _DictationController_connectionGeneration, "f"); const socket = this.host._socketUrl || this.host._socketProxy ? await __classPrivateFieldGet(this, _DictationController_instances, "m", _DictationController_connectProxy).call(this, dictationConfig) : await __classPrivateFieldGet(this, _DictationController_instances, "m", _DictationController_connectAuth).call(this, dictationConfig); // If cleanup() was called while we were awaiting (e.g. config changed), // the generation counter will have advanced — discard this stale socket. if (__classPrivateFieldGet(this, _DictationController_connectionGeneration, "f") !== generation) { socket.close(); return "superseded"; } __classPrivateFieldSet(this, _DictationController_webSocket, socket, "f"); __classPrivateFieldGet(this, _DictationController_callbacks, "f")?.onNetworkActivity?.("sent", { configuration: dictationConfig, type: "config", }); } __classPrivateFieldSet(this, _DictationController_callbacks, callbacks, "f"); __classPrivateFieldGet(this, _DictationController_instances, "m", _DictationController_setupWebSocketHandlers).call(this, callbacks); if (!newConnection && this.isConnectionOpen()) { __classPrivateFieldSet(this, _DictationController_socketReady, true, "f"); __classPrivateFieldGet(this, _DictationController_instances, "m", _DictationController_drain).call(this); } return newConnection; }, _DictationController_connectProxy = async function _DictationController_connectProxy(dictationConfig) { const proxyOptions = this.host._socketProxy || { url: this.host._socketUrl || "", }; if (!proxyOptions.url) { throw new Error("Proxy URL is required when using proxy client"); } return await CortiWebSocketProxyClient.transcribe.connect({ // setting to "false" to have CONFIG_* message in network activity events awaitConfiguration: false, configuration: dictationConfig, proxy: proxyOptions, }); }, _DictationController_connectAuth = async function _DictationController_connectAuth(dictationConfig) { if (!this.host._authConfig && !this.host._accessToken) { throw new Error("Auth configuration or access token is required to connect"); } // Use authConfig if available, otherwise create one from accessToken const auth = this.host._authConfig || { accessToken: this.host._accessToken || "", refreshAccessToken: () => ({ accessToken: this.host._accessToken || "", }), }; __classPrivateFieldSet(this, _DictationController_cortiClient, new CortiClient({ auth, environment: this.host._region, tenantName: this.host._tenantName, }), "f"); return await __classPrivateFieldGet(this, _DictationController_cortiClient, "f").transcribe.connect({ // setting to "false" to have CONFIG_* message in network activity events awaitConfiguration: false, configuration: dictationConfig, }); }, _DictationController_setupWebSocketHandlers = function _DictationController_setupWebSocketHandlers(callbacks) { if (!__classPrivateFieldGet(this, _DictationController_webSocket, "f")) { throw new Error("WebSocket not initialized"); } __classPrivateFieldGet(this, _DictationController_webSocket, "f").on("message", (message) => { if (message.type === "CONFIG_ACCEPTED") { __classPrivateFieldSet(this, _DictationController_socketReady, true, "f"); __classPrivateFieldGet(this, _DictationController_instances, "m", _DictationController_drain).call(this); } callbacks.onNetworkActivity?.("received", message); if (callbacks.onMessage) { callbacks.onMessage(message); } }); __classPrivateFieldGet(this, _DictationController_webSocket, "f").on("error", (event) => { __classPrivateFieldSet(this, _DictationController_socketReady, false, "f"); if (callbacks.onError) { callbacks.onError(event); } }); __classPrivateFieldGet(this, _DictationController_webSocket, "f").on("close", (event) => { __classPrivateFieldSet(this, _DictationController_socketReady, false, "f"); if (callbacks.onClose) { callbacks.onClose(event); } }); }, _DictationController_isSocketOpen = function _DictationController_isSocketOpen() { return (__classPrivateFieldGet(this, _DictationController_webSocket, "f") !== null && __classPrivateFieldGet(this, _DictationController_webSocket, "f").readyState === WebSocket.OPEN); }, _DictationController_drain = function _DictationController_drain() { if (!__classPrivateFieldGet(this, _DictationController_socketReady, "f") || !__classPrivateFieldGet(this, _DictationController_instances, "m", _DictationController_isSocketOpen).call(this) || __classPrivateFieldGet(this, _DictationController_outboundQueue, "f").length === 0) { return; } while (__classPrivateFieldGet(this, _DictationController_outboundQueue, "f").length > 0 && __classPrivateFieldGet(this, _DictationController_instances, "m", _DictationController_isSocketOpen).call(this)) { const item = __classPrivateFieldGet(this, _DictationController_outboundQueue, "f").shift(); if (item === undefined) { break; } if (item instanceof Blob) { __classPrivateFieldGet(this, _DictationController_webSocket, "f").send(item); __classPrivateFieldGet(this, _DictationController_callbacks, "f")?.onNetworkActivity?.("sent", { size: item.size, type: "audio", }); continue; } __classPrivateFieldGet(this, _DictationController_webSocket, "f").send(JSON.stringify(item)); __classPrivateFieldGet(this, _DictationController_callbacks, "f")?.onNetworkActivity?.("sent", { type: item.type, }); } }; //# sourceMappingURL=dictation-controller.js.map