UNPKG

@signalwire/core

Version:
1,633 lines (1,593 loc) 135 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropNames = Object.getOwnPropertyNames; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __reflectGet = Reflect.get; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); var __objRest = (source, exclude) => { var target = {}; for (var prop in source) if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0) target[prop] = source[prop]; if (source != null && __getOwnPropSymbols) for (var prop of __getOwnPropSymbols(source)) { if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop)) target[prop] = source[prop]; } return target; }; var __export = (target, all2) => { for (var name in all2) __defProp(target, name, { get: all2[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default")); var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); var __superGet = (cls, obj, key) => __reflectGet(__getProtoOf(cls), key, obj); var __async = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; // src/index.ts var index_exports = {}; __export(index_exports, { AuthError: () => AuthError, BaseClient: () => BaseClient, BaseComponent: () => BaseComponent, BaseConsumer: () => BaseConsumer, BaseJWTSession: () => BaseJWTSession, BaseSession: () => BaseSession, CapabilityError: () => CapabilityError, Chat: () => chat_exports, ChatMember: () => ChatMember, ChatMessage: () => ChatMessage, DEFAULT_CONNECT_VERSION: () => DEFAULT_CONNECT_VERSION, EventEmitter: () => import_eventemitter3.default, FABRIC_MEMBER_UPDATABLE_PROPS: () => FABRIC_MEMBER_UPDATABLE_PROPS, FABRIC_MEMBER_UPDATED_EVENTS: () => FABRIC_MEMBER_UPDATED_EVENTS, GLOBAL_VIDEO_EVENTS: () => GLOBAL_VIDEO_EVENTS, HttpError: () => HttpError, INTERNAL_FABRIC_MEMBER_UPDATABLE_PROPS: () => INTERNAL_FABRIC_MEMBER_UPDATABLE_PROPS, INTERNAL_FABRIC_MEMBER_UPDATED_EVENTS: () => INTERNAL_FABRIC_MEMBER_UPDATED_EVENTS, INTERNAL_MEMBER_UPDATABLE_PROPS: () => INTERNAL_MEMBER_UPDATABLE_PROPS, INTERNAL_MEMBER_UPDATED_EVENTS: () => INTERNAL_MEMBER_UPDATED_EVENTS, LOCAL_EVENT_PREFIX: () => LOCAL_EVENT_PREFIX, MEMBER_UPDATABLE_PROPS: () => MEMBER_UPDATABLE_PROPS, MEMBER_UPDATED_EVENTS: () => MEMBER_UPDATED_EVENTS, MemberPosition: () => memberPosition_exports, PubSub: () => pubSub_exports, PubSubMessage: () => PubSubMessage, RPCConnect: () => RPCConnect, RPCDisconnectResponse: () => RPCDisconnectResponse, RPCEventAckResponse: () => RPCEventAckResponse, RPCExecute: () => RPCExecute, RPCPing: () => RPCPing, RPCPingResponse: () => RPCPingResponse, RPCReauthenticate: () => RPCReauthenticate, Rooms: () => rooms_exports, SWCloseEvent: () => SWCloseEvent, SYMBOL_CONNECT_ERROR: () => SYMBOL_CONNECT_ERROR, SYMBOL_EXECUTE_CONNECTION_CLOSED: () => SYMBOL_EXECUTE_CONNECTION_CLOSED, SYMBOL_EXECUTE_TIMEOUT: () => SYMBOL_EXECUTE_TIMEOUT, UNIFIED_CONNECT_VERSION: () => UNIFIED_CONNECT_VERSION, VertoAnswer: () => VertoAnswer, VertoAttach: () => VertoAttach, VertoBye: () => VertoBye, VertoInfo: () => VertoInfo, VertoInvite: () => VertoInvite, VertoModify: () => VertoModify, VertoPong: () => VertoPong, VertoResult: () => VertoResult, VertoSubscribe: () => VertoSubscribe, WEBRTC_EVENT_TYPES: () => WEBRTC_EVENT_TYPES, actions: () => actions_exports, asyncRetry: () => asyncRetry, componentActions: () => componentActions, componentReducer: () => componentReducer, componentSelectors: () => componentSelectors_exports, configureStore: () => configureStore2, connect: () => connect, constDelay: () => constDelay, debounce: () => debounce, decreasingDelay: () => decreasingDelay, extendComponent: () => extendComponent, fromSnakeToCamelCase: () => fromSnakeToCamelCase, getEventEmitter: () => getEventEmitter, getLogger: () => getLogger, increasingDelay: () => increasingDelay, initialComponentState: () => initialComponentState, initialSessionState: () => initialSessionState, isConnectRequest: () => isConnectRequest, isGlobalEvent: () => isGlobalEvent, isJSONRPCRequest: () => isJSONRPCRequest, isJSONRPCResponse: () => isJSONRPCResponse, isSATAuth: () => isSATAuth, isVertoInvite: () => isVertoInvite, isWebrtcEventType: () => isWebrtcEventType, makeRPCRequest: () => makeRPCRequest, makeRPCResponse: () => makeRPCResponse, sagaEffects: () => sagaEffects, sagaHelpers: () => sagaHelpers_exports, selectors: () => selectors, sessionActions: () => sessionActions, sessionReducer: () => sessionReducer, setLogger: () => setLogger, stripNamespacePrefix: () => stripNamespacePrefix, testUtils: () => testUtils_exports, timeoutPromise: () => timeoutPromise, toExternalJSON: () => toExternalJSON, toInternalAction: () => toInternalAction, toInternalEventName: () => toInternalEventName, toLocalEvent: () => toLocalEvent, toSnakeCaseKeys: () => toSnakeCaseKeys, toSyntheticEvent: () => toSyntheticEvent, uuid: () => import_uuid.v4, validateEventsToSubscribe: () => validateEventsToSubscribe }); module.exports = __toCommonJS(index_exports); // src/utils/constants.ts var DEFAULT_HOST = "wss://relay.signalwire.com"; var EVENT_NAMESPACE_DIVIDER = ":"; var LOCAL_EVENT_PREFIX = "__local__"; var SYNTHETIC_EVENT_PREFIX = "__synthetic__"; var PRODUCT_PREFIX_VIDEO = "video"; var PRODUCT_PREFIX_CHAT = "chat"; var PRODUCT_PREFIX_PUBSUB = "chat"; var GLOBAL_VIDEO_EVENTS = ["room.started", "room.ended"]; var INTERNAL_GLOBAL_VIDEO_EVENTS = GLOBAL_VIDEO_EVENTS.map( (event) => `${PRODUCT_PREFIX_VIDEO}.${event}` ); var SYMBOL_EXECUTE_CONNECTION_CLOSED = Symbol.for( "sw-execute-connection-closed" ); var SYMBOL_EXECUTE_TIMEOUT = Symbol.for("sw-execute-timeout"); var SYMBOL_CONNECT_ERROR = Symbol.for("sw-connect-error"); // src/utils/logger.ts var import_loglevel = __toESM(require("loglevel")); var datetime = () => (/* @__PURE__ */ new Date()).toISOString(); var defaultLogger = import_loglevel.default.getLogger("signalwire"); var originalFactory = defaultLogger.methodFactory; defaultLogger.methodFactory = (methodName, logLevel, loggerName) => { const rawMethod = originalFactory(methodName, logLevel, loggerName); return function(...args) { args.unshift(datetime(), "-"); rawMethod.apply(void 0, args); }; }; var defaultLoggerLevel = ( // @ts-ignore "development" === process.env.NODE_ENV ? defaultLogger.levels.DEBUG : defaultLogger.getLevel() ); defaultLogger.setLevel(defaultLoggerLevel); var userLogger; var setLogger = (logger) => { userLogger = logger; }; var debugOptions = {}; var setDebugOptions = (options) => { if (options == null) { debugOptions = {}; return; } Object.assign(debugOptions, options); }; var getLoggerInstance = () => { return userLogger != null ? userLogger : defaultLogger; }; var shouldStringify = (payload) => { if ("method" in payload && payload.method === "signalwire.ping") { return false; } return true; }; var wsTraffic = ({ type, payload }) => { const logger = getLoggerInstance(); const { logWsTraffic } = debugOptions || {}; if (!logWsTraffic) { return void 0; } const msg = shouldStringify(payload) ? JSON.stringify(payload, null, 2) : payload; return logger.info(`${type.toUpperCase()}: `, msg, "\n"); }; var getLogger = () => { const logger = getLoggerInstance(); return new Proxy(logger, { get(target, prop, receiver) { if (prop === "wsTraffic") { return wsTraffic; } return Reflect.get(target, prop, receiver); } }); }; // src/utils/common.ts var UPPERCASE_REGEX = /[A-Z]/g; var fromCamelToSnakeCase = (value) => { return value.replace(UPPERCASE_REGEX, (letter) => { return `_${letter.toLowerCase()}`; }); }; var WEBRTC_EVENT_TYPES = [ "webrtc.message" // 'webrtc.verto', ]; var isWebrtcEventType = (eventType) => { return WEBRTC_EVENT_TYPES.includes(eventType); }; // src/utils/index.ts var import_uuid = require("uuid"); // src/utils/parseRPCResponse.ts var parseRPCResponse = ({ response, request }) => { const { result = {}, error } = response; if (error) { return { error }; } switch (request.method) { case "signalwire.connect": return { result }; default: return parseResponse(response); } }; var whitelistCodeRegex = /^2[0-9][0-9]$/; var parseResponse = (response, nodeId) => { const { result = {}, error } = response; if (error) { return { error }; } const { code, node_id, result: nestedResult = null } = result; if (code && !whitelistCodeRegex.test(code)) { return { error: result }; } if (nestedResult === null) { if (nodeId) { result.node_id = nodeId; } return { result }; } if (nestedResult) { if (nestedResult.jsonrpc) { return parseResponse(nestedResult, node_id); } return { result: nestedResult }; } return { result }; }; // src/utils/toExternalJSON.ts var toDateObject = (timestamp) => { if (typeof timestamp === "undefined") { return timestamp; } const date = new Date(timestamp * 1e3); if (isNaN(date.getTime())) { return timestamp; } return date; }; var DEFAULT_OPTIONS = { /** * Properties coming from the server where their value will be * converted to camelCase */ propsToUpdateValue: [ "updated", "layers", "members", "recordings", "playbacks" ] }; var isTimestampProperty = (prop) => { return prop.endsWith("At"); }; var toExternalJSON = (input, options = DEFAULT_OPTIONS) => { if ((input == null ? void 0 : input.__sw_symbol) || (input == null ? void 0 : input.__sw_proxy)) { return input; } return Object.entries(input).reduce((reducer, [key, value]) => { const prop = fromSnakeToCamelCase(key); const propType = typeof value; if (propType === "object" && value) { if (Array.isArray(value)) { if (options.propsToUpdateValue.includes(key)) { reducer[prop] = value.map((v) => { if (typeof v === "string") { return fromSnakeToCamelCase(v); } return toExternalJSON(v); }); } else { reducer[prop] = value; } } else { reducer[prop] = toExternalJSON(value); } } else { if (isTimestampProperty(prop)) { reducer[prop] = toDateObject(value); } else { reducer[prop] = value; } } return reducer; }, {}); }; var fromSnakeToCamelCase = (input) => { if (!input.includes("_")) { return input; } return input.split("_").reduce((reducer, part, index) => { const fc = part.trim().charAt(0); const remainingChars = part.substr(1).toLowerCase(); return `${reducer}${index === 0 ? fc.toLowerCase() : fc.toUpperCase()}${remainingChars}`; }, ""); }; // src/utils/toInternalEventName.ts var toInternalEventName = ({ event, namespace }) => { if (typeof event === "string") { event = getNamespacedEvent({ event, namespace }); event = fromCamelToSnakeCase(event); } return event; }; var getNamespacedEvent = ({ namespace, event }) => { if (!namespace || event.startsWith(namespace)) { return event; } return `${namespace}${EVENT_NAMESPACE_DIVIDER}${event}`; }; // src/utils/toInternalAction.ts var toInternalAction = (event) => { const { event_type, params, node_id } = event; if (event_type === "queuing.relay.tasks") { return { type: event_type, payload: event }; } if (isWebrtcEventType(event_type) && (params == null ? void 0 : params.jsonrpc)) { const vertoRPC = params; if (vertoRPC.params) { vertoRPC.params.nodeId = node_id; } return { type: event_type, payload: vertoRPC }; } return { type: event_type, payload: params }; }; // src/utils/toSnakeCaseKeys.ts var toSnakeCaseKeys = (obj, transform = (value) => value, result = {}) => { if (Array.isArray(obj)) { result = obj.map((item, index) => { if (typeof item === "object") { return toSnakeCaseKeys(item, transform, result[index]); } return item; }); } else { Object.keys(obj).forEach((key) => { const newKey = fromCamelToSnakeCase(key); if (obj[key] && typeof obj[key] === "object") { result[newKey] = toSnakeCaseKeys(obj[key], transform, result[newKey]); } else { result[newKey] = transform(obj[key]); } }); } return result; }; // src/utils/extendComponent.ts var extendComponent = (klass, methods) => { Object.keys(methods).forEach((methodName) => { if (klass.prototype.hasOwnProperty(methodName)) { throw new Error(`[extendComponent] Duplicated method name: ${methodName}`); } }); Object.defineProperties(klass.prototype, methods); return klass; }; // src/utils/debounce.ts function debounce(fn, wait2 = 0, callFirst) { let timeout = null; let debouncedFn = null; const clear = function() { if (timeout) { clearTimeout(timeout); debouncedFn = null; timeout = null; } }; const flush = function() { const call4 = debouncedFn; clear(); if (call4) { call4(); } }; const debounceWrapper = function() { if (!wait2) { return fn.apply(this, arguments); } const context = this; const args = arguments; const callNow = callFirst && !timeout; clear(); debouncedFn = function() { fn.apply(context, args); }; timeout = setTimeout(function() { timeout = null; if (!callNow) { const call4 = debouncedFn; debouncedFn = null; return call4 == null ? void 0 : call4(); } }, wait2); if (callNow && debouncedFn) { return debouncedFn(); } }; debounceWrapper.cancel = clear; debounceWrapper.flush = flush; return debounceWrapper; } // src/utils/SWCloseEvent.ts var SWCloseEvent = class { constructor(type, options = {}) { this.type = type; __publicField(this, "code"); __publicField(this, "reason"); __publicField(this, "wasClean"); this.code = options.code === void 0 ? 0 : options.code; this.reason = options.reason === void 0 ? "" : options.reason; this.wasClean = options.wasClean === void 0 ? false : options.wasClean; } }; // src/utils/eventUtils.ts var stripNamespacePrefix = (event, namespace) => { if (namespace && typeof namespace === "string") { const regex = new RegExp(`^${namespace}.`); return event.replace(regex, ""); } const items = event.split("."); if (items.length > 1) { items.shift(); return items.join("."); } return event; }; // src/utils/asyncRetry.ts var DEFAULT_MAX_RETRIES = 10; var DEFAULT_INITIAL_DELAY = 100; var DEFAULT_DELAY_VARIATION = 1; var increasingDelay = ({ delayLimit: upperDelayLimit = Number.MAX_SAFE_INTEGER, initialDelay = DEFAULT_INITIAL_DELAY, variation = DEFAULT_DELAY_VARIATION }) => { if (initialDelay < 0 || upperDelayLimit < 0 || variation < 0) { throw new Error("No Negative Numbers"); } if (initialDelay > upperDelayLimit) { throw new Error("initialDelay must be lte delayLimit"); } let delay = Math.min(initialDelay, upperDelayLimit); return () => { if (delay === upperDelayLimit) { return upperDelayLimit; } const currentDelay = delay; delay = Math.min(delay + variation, upperDelayLimit); return currentDelay; }; }; var decreasingDelay = ({ delayLimit: bottomDelayLimit = 0, initialDelay = DEFAULT_INITIAL_DELAY, variation = DEFAULT_DELAY_VARIATION }) => { if (initialDelay < 0 || bottomDelayLimit < 0 || variation < 0) { throw new Error("No Negative Numbers"); } if (initialDelay < bottomDelayLimit) { throw new Error("initialDelay must be gte delayLimit"); } let delay = Math.max(initialDelay, bottomDelayLimit); return () => { if (delay === bottomDelayLimit) { return bottomDelayLimit; } const currentDelay = delay; delay = Math.max(delay - variation, bottomDelayLimit); return currentDelay; }; }; var constDelay = ({ initialDelay = DEFAULT_INITIAL_DELAY }) => { if (initialDelay < 0) { throw new Error("No Negative Numbers"); } return () => initialDelay; }; var asyncRetry = (_0) => __async(void 0, [_0], function* ({ asyncCallable, maxRetries: retries = DEFAULT_MAX_RETRIES, delayFn, validator, expectedErrorHandler }) { let remainingAttempts = retries - 1; let wait2 = 0; const promiseAttempt = () => __async(void 0, null, function* () { var _a; try { let result; if (wait2 <= 0) { result = yield asyncCallable(); } else { result = yield new Promise( (resolve, reject) => setTimeout(() => { asyncCallable().then(resolve).catch(reject); }, wait2) ); } if (remainingAttempts) { validator == null ? void 0 : validator(result); } return result; } catch (error) { if (remainingAttempts-- > 0 && !(expectedErrorHandler == null ? void 0 : expectedErrorHandler(error))) { wait2 = (_a = delayFn == null ? void 0 : delayFn()) != null ? _a : 0; getLogger().debug( `Retrying request: ${retries - remainingAttempts} of ${retries}` ); return promiseAttempt(); } else { throw error; } } }); return promiseAttempt(); }); // src/utils/index.ts var safeParseJson = (value) => { if (typeof value !== "string") { return value; } try { return JSON.parse(value); } catch (error) { return value; } }; var PROTOCOL_PATTERN = /^(ws|wss):\/\//; var checkWebSocketHost = (host) => { const protocol = PROTOCOL_PATTERN.test(host) ? "" : "wss://"; return `${protocol}${host}`; }; var timeoutPromise = (promise, time, exception) => { let timer = null; return Promise.race([ promise, new Promise( (_resolve, reject) => timer = setTimeout(reject, time, exception) ) ]).finally(() => clearTimeout(timer)); }; var isGlobalEvent = (event) => { return GLOBAL_VIDEO_EVENTS.includes(event); }; var isSyntheticEvent = (event) => { return event.includes(SYNTHETIC_EVENT_PREFIX); }; var isSessionEvent = (event) => { return event.includes("session."); }; var cleanupEventNamespace = (event) => { const eventParts = event.split(EVENT_NAMESPACE_DIVIDER); return eventParts[eventParts.length - 1]; }; var WITH_CUSTOM_EVENT_NAMES = [ "video.member.updated", "video.member.talking" ]; var CLIENT_SIDE_EVENT_NAMES = [ "video.room.joined", // generated client-side "video.track", "video.active", "video.answering", "video.destroy", "video.early", "video.hangup", "video.held", "video.new", "video.purge", "video.recovering", "video.requesting", "video.ringing", "video.trying", "video.media.connected", "video.media.reconnecting", "video.media.disconnected", "video.microphone.updated", "video.camera.updated", "video.speaker.updated", "video.microphone.disconnected", "video.camera.disconnected", "video.speaker.disconnected" ]; var validateEventsToSubscribe = (events) => { const valid = events.map((internalEvent) => { if (typeof internalEvent === "string") { const event = cleanupEventNamespace(internalEvent); if (CLIENT_SIDE_EVENT_NAMES.includes(event) || isSyntheticEvent(event) || isLocalEvent(event) || isSessionEvent(event)) { return null; } const found = WITH_CUSTOM_EVENT_NAMES.find((withCustomName) => { return event.startsWith(withCustomName); }); return found || event; } return internalEvent; }); return Array.from(new Set(valid)).filter(Boolean); }; var isLocalEvent = (event) => { return event.includes(LOCAL_EVENT_PREFIX); }; var toLocalEvent = (event) => { const eventParts = event.split("."); const prefix = eventParts[0]; return event.split(".").reduce((reducer, item) => { reducer.push(item); if (item === prefix) { reducer.push(LOCAL_EVENT_PREFIX); } return reducer; }, []).join("."); }; var toSyntheticEvent = (event) => { const eventParts = event.split("."); const prefix = eventParts[0]; return event.split(".").reduce((reducer, item) => { reducer.push(item); if (item === prefix) { reducer.push(SYNTHETIC_EVENT_PREFIX); } return reducer; }, []).join("."); }; var isJSONRPCRequest = (e) => { return Boolean(e.method); }; var isJSONRPCResponse = (e) => { return !isJSONRPCRequest(e); }; var isSATAuth = (e) => { return typeof e !== "undefined" && "jti" in e; }; var isConnectRequest = (e) => isJSONRPCRequest(e) && e.method == "signalwire.connect"; var isVertoInvite = (e) => { var _a; return isJSONRPCRequest(e) && e.method == "webrtc.verto" && ((_a = e.params) == null ? void 0 : _a.message.method) === "verto.invite"; }; // src/RPCMessages/helpers.ts var makeRPCRequest = (params) => { var _a; return __spreadValues({ jsonrpc: "2.0", id: (_a = params.id) != null ? _a : (0, import_uuid.v4)() }, params); }; var makeRPCResponse = (params) => { return __spreadValues({ jsonrpc: "2.0" }, params); }; // src/RPCMessages/RPCConnect.ts var DEFAULT_CONNECT_VERSION = { major: 3, minor: 0, revision: 0 }; var UNIFIED_CONNECT_VERSION = { major: 4, minor: 0, revision: 0 }; var RPCConnect = (params) => { return makeRPCRequest({ method: "signalwire.connect", params: __spreadValues({ version: DEFAULT_CONNECT_VERSION, event_acks: true }, params) }); }; // src/RPCMessages/RPCReauthenticate.ts var RPCReauthenticate = (authentication) => { return makeRPCRequest({ method: "signalwire.reauthenticate", params: { authentication } }); }; // src/RPCMessages/RPCPing.ts var RPCPing = () => { return makeRPCRequest({ method: "signalwire.ping", params: { timestamp: Date.now() / 1e3 } }); }; var RPCPingResponse = (id, timestamp) => { return makeRPCResponse({ id, result: { timestamp: timestamp || Date.now() / 1e3 } }); }; // src/RPCMessages/RPCExecute.ts var RPCExecute = ({ method, params }) => { return makeRPCRequest({ method, params }); }; // src/RPCMessages/RPCDisconnect.ts var RPCDisconnectResponse = (id) => { return makeRPCResponse({ id, result: {} }); }; // src/RPCMessages/VertoMessages.ts var tmpMap = { id: "callID", destinationNumber: "destination_number", remoteCallerName: "remote_caller_id_name", remoteCallerNumber: "remote_caller_id_number", callerName: "caller_id_name", callerNumber: "caller_id_number", fromFabricAddressId: "from_fabric_address_id" }; var filterVertoParams = (params) => { if (params.hasOwnProperty("dialogParams")) { const _a = params.dialogParams, { remoteSdp, localStream, remoteStream } = _a, dialogParams = __objRest(_a, [ "remoteSdp", "localStream", "remoteStream" ]); for (const key in tmpMap) { if (key && dialogParams.hasOwnProperty(key)) { dialogParams[tmpMap[key]] = dialogParams[key]; delete dialogParams[key]; } } params.dialogParams = dialogParams; } return params; }; var buildVertoRPCMessage = (method) => { return (params = {}) => { return makeRPCRequest({ method, params: filterVertoParams(params) }); }; }; var VertoInvite = buildVertoRPCMessage("verto.invite"); var VertoBye = buildVertoRPCMessage("verto.bye"); var VertoAttach = buildVertoRPCMessage("verto.attach"); var VertoModify = buildVertoRPCMessage("verto.modify"); var VertoInfo = buildVertoRPCMessage("verto.info"); var VertoAnswer = buildVertoRPCMessage("verto.answer"); var VertoSubscribe = buildVertoRPCMessage("verto.subscribe"); var VertoPong = buildVertoRPCMessage("verto.pong"); var VertoResult = (id, method) => { return makeRPCResponse({ id, result: { method } }); }; // src/RPCMessages/RPCEventAck.ts var RPCEventAckResponse = (id) => makeRPCResponse({ id, result: {} }); // src/redux/actions.ts var actions_exports = {}; __export(actions_exports, { authErrorAction: () => authErrorAction, authExpiringAction: () => authExpiringAction, authSuccessAction: () => authSuccessAction, createAction: () => createAction, destroyAction: () => destroyAction, getCustomSagaActionType: () => getCustomSagaActionType, initAction: () => initAction, makeCustomSagaAction: () => makeCustomSagaAction, reauthAction: () => reauthAction, sessionDisconnectedAction: () => sessionDisconnectedAction, sessionForceCloseAction: () => sessionForceCloseAction, sessionReconnectingAction: () => sessionReconnectingAction, socketMessageAction: () => socketMessageAction }); // src/redux/toolkit/index.ts var toolkit_exports = {}; __export(toolkit_exports, { configureStore: () => configureStore, createAction: () => createAction }); __reExport(toolkit_exports, require("redux")); // src/redux/toolkit/createAction.ts function createAction(type, prepareAction) { function actionCreator(...args) { if (prepareAction) { let prepared = prepareAction(...args); if (!prepared) { throw new Error("prepareAction did not return an object"); } return __spreadValues(__spreadValues({ type, payload: prepared.payload }, "meta" in prepared && { meta: prepared.meta }), "error" in prepared && { error: prepared.error }); } return { type, payload: args[0] }; } actionCreator.toString = () => `${type}`; actionCreator.type = type; actionCreator.match = (action) => action.type === type; return actionCreator; } // src/redux/toolkit/configureStore.ts var import_redux2 = require("redux"); // src/redux/toolkit/devtoolsExtension.ts var import_redux = require("redux"); var composeWithDevTools = typeof window !== "undefined" && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ : function() { if (arguments.length === 0) return void 0; if (typeof arguments[0] === "object") return import_redux.compose; return import_redux.compose.apply(null, arguments); }; var devToolsEnhancer = typeof window !== "undefined" && window.__REDUX_DEVTOOLS_EXTENSION__ ? window.__REDUX_DEVTOOLS_EXTENSION__ : function() { return function(noop) { return noop; }; }; // src/redux/toolkit/isPlainObject.ts function isPlainObject(value) { if (typeof value !== "object" || value === null) return false; let proto = Object.getPrototypeOf(value); if (proto === null) return true; let baseProto = proto; while (Object.getPrototypeOf(baseProto) !== null) { baseProto = Object.getPrototypeOf(baseProto); } return proto === baseProto; } // src/redux/toolkit/getDefaultMiddleware.ts function curryGetDefaultMiddleware() { return function curriedGetDefaultMiddleware() { return []; }; } // src/redux/toolkit/configureStore.ts var IS_PRODUCTION = process.env.NODE_ENV === "production"; function configureStore(options) { const curriedGetDefaultMiddleware = curryGetDefaultMiddleware(); const { reducer = void 0, middleware = curriedGetDefaultMiddleware(), devTools = true, preloadedState = void 0, enhancers = void 0 } = options || {}; let rootReducer2; if (typeof reducer === "function") { rootReducer2 = reducer; } else if (isPlainObject(reducer)) { rootReducer2 = (0, import_redux2.combineReducers)(reducer); } else { throw new Error( '"reducer" is a required argument, and must be a function or an object of functions that can be passed to combineReducers' ); } let finalMiddleware = middleware; if (typeof finalMiddleware === "function") { finalMiddleware = finalMiddleware(curriedGetDefaultMiddleware); if (!IS_PRODUCTION && !Array.isArray(finalMiddleware)) { throw new Error( "when using a middleware builder function, an array of middleware must be returned" ); } } if (!IS_PRODUCTION && finalMiddleware.some((item) => typeof item !== "function")) { throw new Error( "each middleware provided to configureStore must be a function" ); } const middlewareEnhancer = (0, import_redux2.applyMiddleware)(...finalMiddleware); let finalCompose = import_redux2.compose; if (devTools) { finalCompose = composeWithDevTools(__spreadValues({ // Enable capture of stack traces for dispatched Redux actions trace: !IS_PRODUCTION }, typeof devTools === "object" && devTools)); } let storeEnhancers = [middlewareEnhancer]; if (Array.isArray(enhancers)) { storeEnhancers = [middlewareEnhancer, ...enhancers]; } else if (typeof enhancers === "function") { storeEnhancers = enhancers(storeEnhancers); } const composedEnhancer = finalCompose(...storeEnhancers); return (0, import_redux2.createStore)(rootReducer2, preloadedState, composedEnhancer); } // src/redux/actions.ts var initAction = createAction("swSdk/init"); var destroyAction = createAction("swSdk/destroy"); var reauthAction = createAction("swSdk/reauth"); var authErrorAction = createAction( "auth/error" ); var authSuccessAction = createAction("auth/success"); var authExpiringAction = createAction("auth/expiring"); var socketMessageAction = createAction( "socket/message" ); var sessionDisconnectedAction = createAction( "session.disconnected" ); var sessionReconnectingAction = createAction( "session.reconnecting" ); var sessionForceCloseAction = createAction( "session.forceClose" ); var formatCustomSagaAction = (id, action) => { return `${action.type}/${id}`; }; var makeCustomSagaAction = (id, action) => { return __spreadProps(__spreadValues({}, action), { type: formatCustomSagaAction(id, action) }); }; var getCustomSagaActionType = (id, action) => { return formatCustomSagaAction(id, action); }; // src/redux/toolkit/mapBuilders.ts function executeReducerBuilderCallback(builderCallback) { const actionsMap = {}; const actionMatchers = []; let defaultCaseReducer; const builder = { addCase(typeOrActionCreator, reducer) { if (process.env.NODE_ENV !== "production") { if (actionMatchers.length > 0) { throw new Error( "`builder.addCase` should only be called before calling `builder.addMatcher`" ); } if (defaultCaseReducer) { throw new Error( "`builder.addCase` should only be called before calling `builder.addDefaultCase`" ); } } const type = typeof typeOrActionCreator === "string" ? typeOrActionCreator : typeOrActionCreator.type; if (type in actionsMap) { throw new Error( "addCase cannot be called with two reducers for the same action type" ); } actionsMap[type] = reducer; return builder; }, addMatcher(matcher, reducer) { if (process.env.NODE_ENV !== "production") { if (defaultCaseReducer) { throw new Error( "`builder.addMatcher` should only be called before calling `builder.addDefaultCase`" ); } } actionMatchers.push({ matcher, reducer }); return builder; }, addDefaultCase(reducer) { if (process.env.NODE_ENV !== "production") { if (defaultCaseReducer) { throw new Error("`builder.addDefaultCase` can only be called once"); } } defaultCaseReducer = reducer; return builder; } }; builderCallback(builder); return [actionsMap, actionMatchers, defaultCaseReducer]; } // src/redux/toolkit/createReducer.ts function isStateFunction(x) { return typeof x === "function"; } function createReducer(initialState, mapOrBuilderCallback, actionMatchers = [], defaultCaseReducer) { let [actionsMap, finalActionMatchers, finalDefaultCaseReducer] = typeof mapOrBuilderCallback === "function" ? executeReducerBuilderCallback(mapOrBuilderCallback) : [mapOrBuilderCallback, actionMatchers, defaultCaseReducer]; let getInitialState; if (isStateFunction(initialState)) { getInitialState = () => initialState(); } else { getInitialState = () => initialState; } function reducer(state = getInitialState(), action) { let caseReducers = [ actionsMap[action.type], ...finalActionMatchers.filter(({ matcher }) => matcher(action)).map(({ reducer: reducer2 }) => reducer2) ]; if (caseReducers.filter((cr) => !!cr).length === 0) { caseReducers = [finalDefaultCaseReducer]; } return caseReducers.reduce((previousState, caseReducer) => { if (caseReducer) { return caseReducer(previousState, action); } return previousState; }, state); } reducer.getInitialState = getInitialState; return reducer; } // src/redux/toolkit/createSlice.ts function getType(slice, actionKey) { return `${slice}/${actionKey}`; } function createSlice(options) { const { name } = options; if (!name) { throw new Error("`name` is a required option for createSlice"); } const initialState = options.initialState; const reducers = options.reducers || {}; const reducerNames = Object.keys(reducers); const sliceCaseReducersByName = {}; const sliceCaseReducersByType = {}; const actionCreators = {}; reducerNames.forEach((reducerName) => { const maybeReducerWithPrepare = reducers[reducerName]; const type = getType(name, reducerName); let caseReducer; let prepareCallback; if ("reducer" in maybeReducerWithPrepare) { caseReducer = maybeReducerWithPrepare.reducer; prepareCallback = maybeReducerWithPrepare.prepare; } else { caseReducer = maybeReducerWithPrepare; } sliceCaseReducersByName[reducerName] = caseReducer; sliceCaseReducersByType[type] = caseReducer; actionCreators[reducerName] = prepareCallback ? createAction(type, prepareCallback) : createAction(type); }); function buildReducer() { const [ extraReducers = {}, actionMatchers = [], defaultCaseReducer = void 0 ] = typeof options.extraReducers === "function" ? executeReducerBuilderCallback(options.extraReducers) : [options.extraReducers]; const finalCaseReducers = __spreadValues(__spreadValues({}, extraReducers), sliceCaseReducersByType); return createReducer( initialState, finalCaseReducers, actionMatchers, defaultCaseReducer ); } let _reducer; return { name, reducer(state, action) { if (!_reducer) _reducer = buildReducer(); return _reducer(state, action); }, actions: actionCreators, caseReducers: sliceCaseReducersByName, getInitialState() { if (!_reducer) _reducer = buildReducer(); return _reducer.getInitialState(); } }; } // src/redux/utils/createDestroyableSlice.ts var createDestroyableSlice = ({ name = "", initialState, reducers, extraReducers }) => { return createSlice({ name, initialState, reducers, extraReducers: (builder) => { builder.addCase(destroyAction.type, () => { return initialState; }); if (typeof extraReducers === "function") { extraReducers(builder); } } }); }; // src/redux/features/session/sessionSlice.ts var initialSessionState = { protocol: "", iceServers: [], authStatus: "unknown", authorization: void 0, authorizationState: void 0, authError: void 0, authCount: 0 }; function authorizingAction(action) { return [initAction.type, reauthAction.type].includes(action.type); } var sessionSlice = createDestroyableSlice({ name: "session", initialState: initialSessionState, reducers: { connected: (state, { payload }) => { var _a, _b; return __spreadProps(__spreadValues({}, state), { authStatus: "authorized", authorization: payload == null ? void 0 : payload.authorization, authCount: state.authCount + 1, protocol: (_a = payload == null ? void 0 : payload.protocol) != null ? _a : "", iceServers: (_b = payload == null ? void 0 : payload.ice_servers) != null ? _b : [] }); }, authStatus: (state, { payload }) => { return __spreadProps(__spreadValues({}, state), { authStatus: payload }); }, updateAuthorization: (state, { payload }) => { return __spreadProps(__spreadValues({}, state), { authorization: payload }); }, updateAuthorizationState: (state, { payload }) => { return __spreadProps(__spreadValues({}, state), { authorizationState: payload }); } }, extraReducers: (builder) => { builder.addCase( authErrorAction.type, (state, { payload }) => { return __spreadProps(__spreadValues({}, state), { authStatus: "unauthorized", authError: payload.error }); } ); builder.addMatcher(authorizingAction, (state) => { return __spreadProps(__spreadValues({}, state), { authStatus: "authorizing" }); }); } }); var { actions: sessionActions, reducer: sessionReducer } = sessionSlice; // src/BaseSession.ts var SW_SYMBOL = Symbol("BaseSession"); var randomInt = (min, max) => { return Math.floor(Math.random() * (max - min + 1) + min); }; var reconnectDelay = () => { return randomInt(1, 4) * 1e3; }; var BaseSession = class { constructor(options) { this.options = options; /** @internal */ __publicField(this, "__sw_symbol", SW_SYMBOL); __publicField(this, "uuid", (0, import_uuid.v4)()); __publicField(this, "WebSocketConstructor"); __publicField(this, "CloseEventConstructor"); __publicField(this, "agent"); __publicField(this, "connectVersion", DEFAULT_CONNECT_VERSION); __publicField(this, "_rpcConnectResult"); __publicField(this, "_requests", /* @__PURE__ */ new Map()); __publicField(this, "_socket", null); __publicField(this, "_host", DEFAULT_HOST); __publicField(this, "_executeTimeoutMs", 10 * 1e3); __publicField(this, "_executeTimeoutError", SYMBOL_EXECUTE_TIMEOUT); __publicField(this, "_executeQueue", /* @__PURE__ */ new Set()); __publicField(this, "_swConnectError", SYMBOL_CONNECT_ERROR); __publicField(this, "_executeConnectionClosed", SYMBOL_EXECUTE_CONNECTION_CLOSED); __publicField(this, "_checkPingDelay", 15 * 1e3); __publicField(this, "_checkPingTimer", null); __publicField(this, "_reconnectTimer"); __publicField(this, "_status", "unknown"); __publicField(this, "_resolveWaitConnected", null); __publicField(this, "_sessionChannel"); __publicField(this, "wsOpenHandler"); __publicField(this, "wsCloseHandler"); __publicField(this, "wsErrorHandler"); var _a, _b; const { host, logLevel = "info", sessionChannel } = options; if (host) { this._host = checkWebSocketHost(host); } if (sessionChannel) { this._sessionChannel = sessionChannel; } if (logLevel) { (_b = (_a = this.logger).setLevel) == null ? void 0 : _b.call(_a, logLevel); } this._onSocketOpen = this._onSocketOpen.bind(this); this._onSocketError = this._onSocketError.bind(this); this._onSocketClose = this._onSocketClose.bind(this); this._onSocketMessage = this._onSocketMessage.bind(this); this.execute = this.execute.bind(this); this.connect = this.connect.bind(this); this.wsOpenHandler = (event) => { var _a2; (_a2 = this._socket) == null ? void 0 : _a2.removeEventListener("open", this.wsOpenHandler); this._onSocketOpen(event); }; this.wsCloseHandler = (event) => { var _a2; (_a2 = this._socket) == null ? void 0 : _a2.removeEventListener("close", this.wsCloseHandler); this._onSocketClose(event); }; this.wsErrorHandler = (event) => { var _a2; (_a2 = this._socket) == null ? void 0 : _a2.removeEventListener("error", this.wsErrorHandler); this._onSocketError(event); }; } get host() { return this._host; } get rpcConnectResult() { return this._rpcConnectResult; } get relayProtocol() { var _a, _b; return (_b = (_a = this._rpcConnectResult) == null ? void 0 : _a.protocol) != null ? _b : ""; } get signature() { if (this._rpcConnectResult) { const { authorization } = this._rpcConnectResult; return authorization.signature; } return void 0; } get logger() { return getLogger(); } get connecting() { var _a; return ((_a = this._socket) == null ? void 0 : _a.readyState) === 0 /* CONNECTING */; } get connected() { var _a; return ((_a = this._socket) == null ? void 0 : _a.readyState) === 1 /* OPEN */; } get closing() { var _a; return ((_a = this._socket) == null ? void 0 : _a.readyState) === 2 /* CLOSING */; } get closed() { return this._socket ? this._socket.readyState === 3 /* CLOSED */ : true; } get status() { return this._status; } get idle() { return this._status === "idle"; } get ready() { return !Boolean(this.idle || !this.connected); } _waitConnected() { return __async(this, null, function* () { return new Promise((resolve) => { if (this.connected) { resolve(); } else { this._resolveWaitConnected = resolve; } }); }); } set token(token) { this.options.token = token; } /** * Connect the websocket * * @return void */ connect() { if (!(this == null ? void 0 : this.WebSocketConstructor)) { throw new Error("Missing WebSocketConstructor"); } if (!(this == null ? void 0 : this.CloseEventConstructor)) { throw new Error("Missing CloseEventConstructor"); } this._clearTimers(); if (this.connecting || this.connected) { this.logger.warn("Session already connected."); return; } this._removeSocketListeners(); this.destroySocket(); this._clearCheckPingTimer(); this._socket = this._createSocket(); this._addSocketListeners(); } /** * Allow children classes to override it. * @return WebSocket instance */ _createSocket() { return new this.WebSocketConstructor(this._host); } /** Allow children classes to override it. */ destroySocket() { if (this._socket) { this._socket.close(); this.wsCloseHandler( new this.CloseEventConstructor("close", { reason: "Client-side closed" }) ); this._socket = null; } } _addSocketListeners() { if (!this._socket) { return this.logger.debug("Invalid socket instance to add listeners"); } this._removeSocketListeners(); this._socket.addEventListener("open", this.wsOpenHandler); this._socket.addEventListener("close", this.wsCloseHandler); this._socket.addEventListener("error", this.wsErrorHandler); this._socket.addEventListener("message", this._onSocketMessage); } _removeSocketListeners() { if (!this._socket) { return this.logger.debug("Invalid socket instance to remove listeners"); } this._socket.removeEventListener("open", this.wsOpenHandler); this._socket.removeEventListener("close", this.wsCloseHandler); this._socket.removeEventListener("error", this.wsErrorHandler); this._socket.removeEventListener("message", this._onSocketMessage); } /** * Clear the Session and close the WS connection. * @return void */ disconnect() { if (!this._socket || this.closing) { this.logger.debug("Session not connected or already in closing state."); return; } this._status = "disconnecting"; this._checkCurrentStatus(); } /** * Send a JSON object to the server. * @return Promise that will resolve/reject depending on the server response */ execute(msg) { if (this._status === "disconnecting") { this.logger.warn( "Reject request because the session is disconnecting", msg ); return Promise.reject({ code: "400", message: "The SDK session is disconnecting" }); } if (this._status === "disconnected") { return Promise.reject({ code: "400", message: "The SDK is disconnected" }); } let promise = Promise.resolve(); if ("params" in msg) { promise = new Promise((resolve, reject) => { this._requests.set(msg.id, { rpcRequest: msg, resolve, reject }); }); } if (!this.ready) { this._addToExecuteQueue(msg); this.connect(); return promise; } this._send(msg); return timeoutPromise( promise, this._executeTimeoutMs, this._executeTimeoutError ).catch((error) => { if (error === this._executeConnectionClosed) { throw this._executeConnectionClosed; } else if (error === this._executeTimeoutError) { if (isConnectRequest(msg)) { throw this._swConnectError; } this._checkCurrentStatus(); this.logger.error("Request Timeout", msg); if (this.status === "disconnected") { return this.logger.debug( "Request failed because the session is disconnected", this.status, this._socket ); } this._closeConnection("reconnecting"); } else { throw error; } }); } get _connectParams() { return { agent: this.agent, version: this.connectVersion, authentication: { project: this.options.project, token: this.options.token } }; } /** * Authenticate with the SignalWire Network * @return Promise<void> */ authenticate() { return __async(this, null, function* () { var _a, _b; const params = this._connectParams; if (this._relayProtocolIsValid()) { params.protocol = this.relayProtocol; } if ((_a = this.options.topics) == null ? void 0 : _a.length) { params.contexts = this.options.topics; } else if ((_b = this.options.contexts) == null ? void 0 : _b.length) { params.contexts = this.options.contexts; } this._rpcConnectResult = yield this.execute(RPCConnect(params)); }); } authError(error) { this._removeSocketListeners(); this.dispatch(authErrorAction({ error })); } forceClose() { this._removeSocketListeners(); return this._closeConnection("reconnecting"); } _onSocketOpen(event) { return __async(this, null, function* () { var _a; this.logger.debug("_onSocketOpen", event.type); try { this._status = "unknown"; this._clearTimers(); yield this.authenticate(); this._status = "connected"; (_a = this._resolveWaitConnected) == null ? void 0 : _a.call(this); this._flushExecuteQueue(); this.dispatch(authSuccessAction()); } catch (error) { if (error === this._swConnectError || error === this._executeConnectionClosed) { this.logger.debug( "Invalid connect or connection closed. Waiting for retry." ); return; } this.logger.error("Auth Error", error); this.authError(error); } }); } _onSocketError(event) { this.logger.debug("_onSocketError", event); } _onSocketClose(event) { this.logger.debug("_onSocketClose", event.type, event.code, event.reason); if (this._status !== "disconnected") { this._status = "reconnecting"; this.dispatch(sessionReconnectingAction()); this._clearTimers(); this._clearPendingRequests(); this._reconnectTimer = setTimeout(() => { this.connect();