@anam-ai/js-sdk
Version:
Client side JavaScript SDK for Anam AI
211 lines • 8.58 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SignallingClient = void 0;
const constants_1 = require("../lib/constants");
const types_1 = require("../types");
const DEFAULT_HEARTBEAT_INTERVAL_SECONDS = 5;
const DEFAULT_WS_RECONNECTION_ATTEMPTS = 5;
class SignallingClient {
constructor(sessionId, options, publicEventEmitter, internalEventEmitter) {
var _a;
this.stopSignal = false;
this.sendingBuffer = [];
this.wsConnectionAttempts = 0;
this.socket = null;
this.heartBeatIntervalRef = null;
this.publicEventEmitter = publicEventEmitter;
this.internalEventEmitter = internalEventEmitter;
if (!sessionId) {
throw new Error('Signalling Client: sessionId is required');
}
this.sessionId = sessionId;
const { heartbeatIntervalSeconds, maxWsReconnectionAttempts, url } = options;
this.heartbeatIntervalSeconds =
heartbeatIntervalSeconds || DEFAULT_HEARTBEAT_INTERVAL_SECONDS;
this.maxWsReconnectionAttempts =
maxWsReconnectionAttempts || DEFAULT_WS_RECONNECTION_ATTEMPTS;
if (!url.baseUrl) {
throw new Error('Signalling Client: baseUrl is required');
}
const httpProtocol = url.protocol || 'https';
const initUrl = `${httpProtocol}://${url.baseUrl}`;
this.url = new URL(initUrl);
this.url.protocol = url.protocol === 'http' ? 'ws:' : 'wss:';
if (url.port) {
this.url.port = url.port;
}
this.url.pathname = (_a = url.signallingPath) !== null && _a !== void 0 ? _a : '/ws';
this.url.searchParams.append('session_id', sessionId);
}
stop() {
this.stopSignal = true;
this.closeSocket();
}
connect() {
this.socket = new WebSocket(this.url.href);
this.socket.onopen = this.onOpen.bind(this);
this.socket.onclose = this.onClose.bind(this);
this.socket.onerror = this.onError.bind(this);
return this.socket;
}
sendOffer(localDescription) {
return __awaiter(this, void 0, void 0, function* () {
const offerMessagePayload = {
connectionDescription: localDescription,
userUid: this.sessionId, // TODO: this should be renamed to session Id on the server
};
const offerMessage = {
actionType: types_1.SignalMessageAction.OFFER,
sessionId: this.sessionId,
payload: offerMessagePayload,
};
this.sendSignalMessage(offerMessage);
});
}
sendIceCandidate(candidate) {
return __awaiter(this, void 0, void 0, function* () {
const iceCandidateMessage = {
actionType: types_1.SignalMessageAction.ICE_CANDIDATE,
sessionId: this.sessionId,
payload: candidate.toJSON(),
};
this.sendSignalMessage(iceCandidateMessage);
});
}
sendSignalMessage(message) {
var _a;
if (((_a = this.socket) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN) {
try {
this.socket.send(JSON.stringify(message));
}
catch (error) {
console.error('SignallingClient - sendSignalMessage: error sending message', error);
}
}
else {
this.sendingBuffer.push(message);
}
}
sendTalkMessage(payload) {
return __awaiter(this, void 0, void 0, function* () {
const chatMessage = {
actionType: types_1.SignalMessageAction.TALK_STREAM_INPUT,
sessionId: this.sessionId,
payload: payload,
};
this.sendSignalMessage(chatMessage);
});
}
closeSocket() {
if (this.socket) {
this.socket.close();
this.socket = null;
}
if (this.heartBeatIntervalRef) {
clearInterval(this.heartBeatIntervalRef);
this.heartBeatIntervalRef = null;
}
}
onOpen() {
return __awaiter(this, void 0, void 0, function* () {
if (!this.socket) {
throw new Error('SignallingClient - onOpen: socket is null');
}
try {
this.wsConnectionAttempts = 0;
this.flushSendingBuffer();
this.socket.onmessage = this.onMessage.bind(this);
this.startSendingHeartBeats();
this.internalEventEmitter.emit(types_1.InternalEvent.WEB_SOCKET_OPEN);
}
catch (e) {
console.error('SignallingClient - onOpen: error in onOpen', e);
this.publicEventEmitter.emit(types_1.AnamEvent.CONNECTION_CLOSED, constants_1.CONNECTION_CLOSED_CODE_SIGNALLING_CLIENT_CONNECTION_FAILURE);
}
});
}
onClose() {
return __awaiter(this, void 0, void 0, function* () {
this.wsConnectionAttempts += 1;
if (this.stopSignal) {
return;
}
if (this.wsConnectionAttempts <= this.maxWsReconnectionAttempts) {
this.socket = null;
setTimeout(() => {
this.connect();
}, 100 * this.wsConnectionAttempts);
}
else {
if (this.heartBeatIntervalRef) {
clearInterval(this.heartBeatIntervalRef);
this.heartBeatIntervalRef = null;
}
this.publicEventEmitter.emit(types_1.AnamEvent.CONNECTION_CLOSED, constants_1.CONNECTION_CLOSED_CODE_SIGNALLING_CLIENT_CONNECTION_FAILURE);
}
});
}
onError(event) {
if (this.stopSignal) {
return;
}
console.error('SignallingClient - onError: ', event);
}
flushSendingBuffer() {
const newBuffer = [];
if (this.sendingBuffer.length > 0) {
this.sendingBuffer.forEach((message) => {
var _a;
if (((_a = this.socket) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN) {
this.socket.send(JSON.stringify(message));
}
else {
newBuffer.push(message);
}
});
}
this.sendingBuffer = newBuffer;
}
onMessage(event) {
return __awaiter(this, void 0, void 0, function* () {
const message = JSON.parse(event.data);
this.internalEventEmitter.emit(types_1.InternalEvent.SIGNAL_MESSAGE_RECEIVED, message);
});
}
startSendingHeartBeats() {
if (!this.socket) {
throw new Error('SignallingClient - startSendingHeartBeats: socket is null');
}
if (this.heartBeatIntervalRef) {
console.warn('SignallingClient - startSendingHeartBeats: heartbeat interval already set');
}
// send a heartbeat message every heartbeatIntervalSeconds
const heartbeatInterval = this.heartbeatIntervalSeconds * 1000;
const heartbeatMessage = {
actionType: types_1.SignalMessageAction.HEARTBEAT,
sessionId: this.sessionId,
payload: '',
};
const heartbeatMessageJson = JSON.stringify(heartbeatMessage);
this.heartBeatIntervalRef = setInterval(() => {
var _a;
if (this.stopSignal) {
return;
}
if (((_a = this.socket) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN) {
this.socket.send(heartbeatMessageJson);
}
}, heartbeatInterval);
}
}
exports.SignallingClient = SignallingClient;
//# sourceMappingURL=SignallingClient.js.map