UNPKG

@signalwire/core

Version:
4 lines 306 kB
{ "version": 3, "sources": ["../src/utils/constants.ts", "../src/utils/logger.ts", "../src/utils/common.ts", "../src/utils/index.ts", "../src/utils/parseRPCResponse.ts", "../src/utils/toExternalJSON.ts", "../src/utils/toInternalEventName.ts", "../src/utils/toInternalAction.ts", "../src/utils/toSnakeCaseKeys.ts", "../src/utils/extendComponent.ts", "../src/utils/debounce.ts", "../src/utils/SWCloseEvent.ts", "../src/utils/eventUtils.ts", "../src/utils/asyncRetry.ts", "../src/RPCMessages/helpers.ts", "../src/RPCMessages/RPCConnect.ts", "../src/RPCMessages/RPCReauthenticate.ts", "../src/RPCMessages/RPCPing.ts", "../src/RPCMessages/RPCExecute.ts", "../src/RPCMessages/RPCDisconnect.ts", "../src/RPCMessages/VertoMessages.ts", "../src/RPCMessages/RPCEventAck.ts", "../src/redux/actions.ts", "../src/redux/toolkit/index.ts", "../src/redux/toolkit/createAction.ts", "../src/redux/toolkit/configureStore.ts", "../src/redux/toolkit/devtoolsExtension.ts", "../src/redux/toolkit/isPlainObject.ts", "../src/redux/toolkit/getDefaultMiddleware.ts", "../src/redux/toolkit/mapBuilders.ts", "../src/redux/toolkit/createReducer.ts", "../src/redux/toolkit/createSlice.ts", "../src/redux/utils/createDestroyableSlice.ts", "../src/redux/features/session/sessionSlice.ts", "../src/BaseSession.ts", "../src/BaseJWTSession.ts", "../src/redux/index.ts", "../src/redux/features/component/componentSlice.ts", "../src/redux/rootReducer.ts", "../src/redux/rootSaga.ts", "../src/redux/features/session/sessionSaga.ts", "../src/redux/utils/sagaHelpers.ts", "../src/CustomErrors.ts", "../src/redux/features/session/sessionSelectors.ts", "../src/redux/connect.ts", "../src/utils/EventEmitter.ts", "../src/redux/utils/useSession.ts", "../src/redux/utils/useInstanceMap.ts", "../src/workers/executeActionWorker.ts", "../src/BaseComponent.ts", "../src/BaseClient.ts", "../src/BaseConsumer.ts", "../src/types/videoMember.ts", "../src/redux/features/component/componentSelectors.ts", "../src/types/fabricMember.ts", "../src/index.ts", "../src/rooms/index.ts", "../src/rooms/methods.ts", "../src/rooms/RoomSessionRecording.ts", "../src/rooms/RoomSessionPlayback.ts", "../src/rooms/RoomSessionStream.ts", "../src/chat/index.ts", "../src/chat/utils/toInternalChatChannels.ts", "../src/chat/utils/index.ts", "../src/chat/methods.ts", "../src/pubSub/index.ts", "../src/pubSub/workers/pubSubWorker.ts", "../src/pubSub/BasePubSub.ts", "../src/pubSub/PubSubMessage.ts", "../src/chat/workers/chatWorker.ts", "../src/chat/BaseChat.ts", "../src/chat/ChatMessage.ts", "../src/chat/ChatMember.ts", "../src/chat/applyCommonMethods.ts", "../src/memberPosition/index.ts", "../src/memberPosition/workers.ts", "../src/testUtils.ts"], "sourcesContent": ["export const STORAGE_PREFIX = '@signalwire:'\nexport const ADD = 'add'\nexport const REMOVE = 'remove'\nexport const SESSION_ID = 'sessId'\nexport const DEFAULT_HOST = 'wss://relay.signalwire.com'\n\nexport enum WebSocketState {\n CONNECTING = 0,\n OPEN = 1,\n CLOSING = 2,\n CLOSED = 3,\n}\n\n/**\n * Used for namespacing events.\n */\nexport const EVENT_NAMESPACE_DIVIDER = ':'\n\nexport const LOCAL_EVENT_PREFIX = '__local__'\nexport const SYNTHETIC_EVENT_PREFIX = '__synthetic__'\n\nexport const PRODUCT_PREFIX_VIDEO = 'video'\n/**\n * video-manager is an [internal] superset of the video apis\n */\nexport const PRODUCT_PREFIX_VIDEO_MANAGER = 'video-manager'\nexport const PRODUCT_PREFIX_CHAT = 'chat' as const\n/**\n * For now both, `PubSub` and `Chat` share the same\n * namespace but this might change in the future.\n */\nexport const PRODUCT_PREFIX_PUBSUB = 'chat' as const\nexport const PRODUCT_PREFIX_TASK = 'tasking'\nexport const PRODUCT_PREFIX_MESSAGING = 'messaging'\nexport const PRODUCT_PREFIX_VOICE = 'voice'\nexport const PRODUCT_PREFIX_VOICE_CALL = 'calling'\n\nexport const GLOBAL_VIDEO_EVENTS = ['room.started', 'room.ended'] as const\n\nexport const PRODUCT_PREFIXES = [\n PRODUCT_PREFIX_VIDEO,\n PRODUCT_PREFIX_VIDEO_MANAGER,\n PRODUCT_PREFIX_CHAT,\n PRODUCT_PREFIX_PUBSUB,\n PRODUCT_PREFIX_TASK,\n PRODUCT_PREFIX_MESSAGING,\n PRODUCT_PREFIX_VOICE,\n PRODUCT_PREFIX_VOICE_CALL,\n] as const\n\n/**\n * For internal usage only. These are the fully qualified event names\n * sent by the server\n * @internal\n */\nexport const INTERNAL_GLOBAL_VIDEO_EVENTS = GLOBAL_VIDEO_EVENTS.map(\n (event) => `${PRODUCT_PREFIX_VIDEO}.${event}` as const\n)\n\nexport const SYMBOL_EXECUTE_CONNECTION_CLOSED = Symbol.for(\n 'sw-execute-connection-closed'\n)\nexport const SYMBOL_EXECUTE_TIMEOUT = Symbol.for('sw-execute-timeout')\nexport const SYMBOL_CONNECT_ERROR = Symbol.for('sw-connect-error')\n", "import log from 'loglevel'\nimport type {\n SDKLogger,\n InternalSDKLogger,\n WsTrafficOptions,\n UserOptions,\n} from '..'\n\nconst datetime = () => new Date().toISOString()\nconst defaultLogger = log.getLogger('signalwire')\n\nconst originalFactory = defaultLogger.methodFactory\ndefaultLogger.methodFactory = (methodName, logLevel, loggerName) => {\n const rawMethod = originalFactory(methodName, logLevel, loggerName)\n\n return function (...args: any[]) {\n args.unshift(datetime(), '-')\n rawMethod.apply(undefined, args)\n }\n}\n\nconst defaultLoggerLevel =\n // @ts-ignore\n 'development' === process.env.NODE_ENV\n ? defaultLogger.levels.DEBUG\n : defaultLogger.getLevel()\ndefaultLogger.setLevel(defaultLoggerLevel)\n\nlet userLogger: SDKLogger | null\nconst setLogger = (logger: SDKLogger | null) => {\n userLogger = logger\n}\n\nlet debugOptions: UserOptions['debug'] = {}\nconst setDebugOptions = (options: any) => {\n if (options == null) {\n debugOptions = {}\n return\n }\n Object.assign(debugOptions!, options)\n}\n\nconst getLoggerInstance = (): SDKLogger => {\n return userLogger ?? (defaultLogger as any as SDKLogger)\n}\n\nconst shouldStringify = (payload: WsTrafficOptions['payload']) => {\n if ('method' in payload && payload.method === 'signalwire.ping') {\n return false\n }\n\n return true\n}\n\nconst wsTraffic: InternalSDKLogger['wsTraffic'] = ({ type, payload }) => {\n const logger = getLoggerInstance()\n const { logWsTraffic } = debugOptions || {}\n\n if (!logWsTraffic) {\n return undefined\n }\n\n const msg = shouldStringify(payload)\n ? JSON.stringify(payload, null, 2)\n : payload\n\n return logger.info(`${type.toUpperCase()}: \\n`, msg, '\\n')\n}\n\nconst getLogger = (): InternalSDKLogger => {\n const logger = getLoggerInstance()\n\n return new Proxy(logger, {\n get(target, prop: keyof InternalSDKLogger, receiver) {\n if (prop === 'wsTraffic') {\n return wsTraffic\n }\n\n return Reflect.get(target, prop, receiver)\n },\n }) as InternalSDKLogger\n}\n\nexport { setLogger, getLogger, setDebugOptions }\n", "import { WebRTCEventType } from '..'\n\nconst UPPERCASE_REGEX = /[A-Z]/g\n/**\n * Converts values from camelCase to snake_case\n * @internal\n */\nexport const fromCamelToSnakeCase = <T>(value: T): T => {\n // @ts-ignore\n return value.replace(UPPERCASE_REGEX, (letter) => {\n return `_${letter.toLowerCase()}`\n }) as T\n}\n\nexport const WEBRTC_EVENT_TYPES: WebRTCEventType[] = [\n 'webrtc.message',\n // 'webrtc.verto',\n]\nexport const isWebrtcEventType = (\n eventType: string\n): eventType is WebRTCEventType => {\n // @ts-expect-error\n return WEBRTC_EVENT_TYPES.includes(eventType)\n}\n", "import {\n Authorization,\n JSONRPCRequest,\n JSONRPCResponse,\n SATAuthorization,\n} from '..'\nimport {\n STORAGE_PREFIX,\n GLOBAL_VIDEO_EVENTS,\n INTERNAL_GLOBAL_VIDEO_EVENTS,\n EVENT_NAMESPACE_DIVIDER,\n LOCAL_EVENT_PREFIX,\n SYNTHETIC_EVENT_PREFIX,\n} from './constants'\nexport { setLogger, getLogger, setDebugOptions } from './logger'\nexport { isWebrtcEventType, WEBRTC_EVENT_TYPES } from './common'\n\nexport { v4 as uuid } from 'uuid'\nexport * from './parseRPCResponse'\nexport * from './toExternalJSON'\nexport * from './toInternalEventName'\nexport * from './toInternalAction'\nexport * from './toSnakeCaseKeys'\nexport * from './extendComponent'\nexport * from './debounce'\nexport * from './SWCloseEvent'\nexport * from './eventUtils'\nexport * from './asyncRetry'\n\nexport { LOCAL_EVENT_PREFIX }\n\nexport const mutateStorageKey = (key: string) => `${STORAGE_PREFIX}${key}`\n\nexport const safeParseJson = <T>(value: T): T | Object => {\n if (typeof value !== 'string') {\n return value\n }\n try {\n return JSON.parse(value)\n } catch (error) {\n return value\n }\n}\n\nconst PROTOCOL_PATTERN = /^(ws|wss):\\/\\//\nexport const checkWebSocketHost = (host: string): string => {\n const protocol = PROTOCOL_PATTERN.test(host) ? '' : 'wss://'\n return `${protocol}${host}`\n}\n\nexport const timeoutPromise = <T = unknown>(\n promise: Promise<T>,\n time: number,\n exception: any\n) => {\n let timer: any = null\n return Promise.race<T>([\n promise,\n new Promise(\n (_resolve, reject) => (timer = setTimeout(reject, time, exception))\n ),\n ]).finally(() => clearTimeout(timer))\n}\n\n/** @internal */\nexport const isGlobalEvent = (event: string) => {\n // @ts-ignore\n return GLOBAL_VIDEO_EVENTS.includes(event)\n}\n\n/** @internal */\nexport const isInternalGlobalEvent = (event: string) => {\n // @ts-ignore\n return INTERNAL_GLOBAL_VIDEO_EVENTS.includes(event)\n}\n\nexport const isSyntheticEvent = (event: string) => {\n return event.includes(SYNTHETIC_EVENT_PREFIX)\n}\n\nexport const isSessionEvent = (event: string) => {\n return event.includes('session.')\n}\n\nexport const getGlobalEvents = (kind: 'all' | 'video' = 'all') => {\n switch (kind) {\n case 'video':\n return GLOBAL_VIDEO_EVENTS\n default:\n // prettier-ignore\n return [\n ...GLOBAL_VIDEO_EVENTS,\n ]\n }\n}\n\nconst cleanupEventNamespace = (event: string) => {\n const eventParts = event.split(EVENT_NAMESPACE_DIVIDER)\n return eventParts[eventParts.length - 1]\n}\n\n/**\n * These events have derived events generated by the SDK\n * i.e. member.updated.audioMuted or member.talking.started\n */\nconst WITH_CUSTOM_EVENT_NAMES = [\n 'video.member.updated',\n 'video.member.talking',\n] as const\n\n/**\n * These events are generated by the SDK to make them\n * more \"user-friendly\" while others are client-side only\n * like the WebRTC ones: `track`/`active`/`destroy` for Call objects.\n */\nconst CLIENT_SIDE_EVENT_NAMES = [\n 'video.room.joined', // generated client-side\n 'video.track',\n 'video.active',\n 'video.answering',\n 'video.destroy',\n 'video.early',\n 'video.hangup',\n 'video.held',\n 'video.new',\n 'video.purge',\n 'video.recovering',\n 'video.requesting',\n 'video.ringing',\n 'video.trying',\n 'video.media.connected',\n 'video.media.reconnecting',\n 'video.media.disconnected',\n 'video.microphone.updated',\n 'video.camera.updated',\n 'video.speaker.updated',\n 'video.microphone.disconnected',\n 'video.camera.disconnected',\n 'video.speaker.disconnected',\n]\n/**\n * Check and filter the events the user attached returning only the valid ones\n * for the server.\n * IE: `member.updated.audioMuted` means `member.updated` for the server.\n * @internal\n */\nexport const validateEventsToSubscribe = <T = string>(events: T[]): T[] => {\n const valid = events.map((internalEvent) => {\n if (typeof internalEvent === 'string') {\n const event = cleanupEventNamespace(internalEvent)\n if (\n CLIENT_SIDE_EVENT_NAMES.includes(event) ||\n isSyntheticEvent(event) ||\n isLocalEvent(event) ||\n isSessionEvent(event)\n ) {\n return null\n }\n const found = WITH_CUSTOM_EVENT_NAMES.find((withCustomName) => {\n return event.startsWith(withCustomName)\n })\n return found || event\n }\n\n return internalEvent\n })\n\n return Array.from(new Set(valid)).filter(Boolean) as T[]\n}\n\n/**\n * \"Local\" events are events controlled by the SDK and the\n * server has no knowledge about them.\n */\nexport const isLocalEvent = (event: string) => {\n return event.includes(LOCAL_EVENT_PREFIX)\n}\n\nexport const toLocalEvent = <T extends string>(event: string): T => {\n const eventParts = event.split('.')\n const prefix = eventParts[0]\n\n return event\n .split('.')\n .reduce((reducer, item) => {\n reducer.push(item)\n\n if (item === prefix) {\n reducer.push(LOCAL_EVENT_PREFIX)\n }\n\n return reducer\n }, [] as string[])\n .join('.') as T\n}\n\nexport const toSyntheticEvent = <T extends string>(event: string): T => {\n const eventParts = event.split('.')\n const prefix = eventParts[0]\n\n return event\n .split('.')\n .reduce((reducer, item) => {\n reducer.push(item)\n\n if (item === prefix) {\n reducer.push(SYNTHETIC_EVENT_PREFIX)\n }\n\n return reducer\n }, [] as string[])\n .join('.') as T\n}\n\nexport const isJSONRPCRequest = (\n e: JSONRPCRequest | JSONRPCResponse\n): e is JSONRPCRequest => {\n return Boolean((e as JSONRPCRequest).method)\n}\n\nexport const isJSONRPCResponse = (\n e: JSONRPCRequest | JSONRPCResponse\n): e is JSONRPCResponse => {\n return !isJSONRPCRequest(e)\n}\n\nexport const isSATAuth = (e?: Authorization): e is SATAuthorization => {\n return typeof e !== 'undefined' && 'jti' in e\n}\n\nexport const isConnectRequest = (e: JSONRPCRequest | JSONRPCResponse) =>\n isJSONRPCRequest(e) && e.method == 'signalwire.connect'\n\nexport const isVertoInvite = (e: JSONRPCRequest | JSONRPCResponse) =>\n isJSONRPCRequest(e) &&\n e.method == 'webrtc.verto' &&\n e.params?.message.method === 'verto.invite'\n", "import { JSONRPCRequest, JSONRPCResponse } from './interfaces'\n\ntype parseRPCResponseParams = {\n response: JSONRPCResponse\n request: JSONRPCRequest\n}\nexport const parseRPCResponse = ({\n response,\n request,\n}: parseRPCResponseParams) => {\n const { result = {}, error } = response\n if (error) {\n return {\n error,\n }\n }\n\n switch (request.method) {\n case 'signalwire.connect':\n return { result }\n default:\n return parseResponse(response)\n }\n}\n\nconst whitelistCodeRegex = /^2[0-9][0-9]$/\n\n/**\n * From the socket we can get:\n * - JSON-RPC msg with 1 level of 'result' or 'error'\n * - JSON-RPC msg with 2 nested 'result' and 'code' property to identify error\n * - JSON-RPC msg with 3 nested 'result' where the third level is the Verto JSON-RPC flat msg.\n *\n * @returns Object with error | result key to identify success or fail\n */\nconst parseResponse = (\n response: JSONRPCResponse,\n nodeId?: string\n): { [key: string]: any } => {\n const { result = {}, error } = response\n if (error) {\n return { error }\n }\n const { code, node_id, result: nestedResult = null } = result\n // Throw error if the code is not whitelisted (2xx)\n if (code && !whitelistCodeRegex.test(code)) {\n return { error: result }\n }\n if (nestedResult === null) {\n if (nodeId) {\n // Attach node_id to the nestedResult\n result.node_id = nodeId\n }\n return { result }\n }\n if (nestedResult) {\n if (nestedResult.jsonrpc) {\n // This is a verto message\n return parseResponse(nestedResult, node_id)\n }\n return { result: nestedResult }\n }\n return { result }\n}\n", "import { SnakeToCamelCase, ConverToExternalTypes } from '../types/utils'\n\nconst toDateObject = (timestamp?: number) => {\n if (typeof timestamp === 'undefined') {\n return timestamp\n }\n\n const date = new Date(timestamp * 1000)\n\n /**\n * If for some reason we can't convert to a valid date\n * we'll return the original value\n */\n if (isNaN(date.getTime())) {\n return timestamp\n }\n\n return date\n}\n\nconst DEFAULT_OPTIONS = {\n /**\n * Properties coming from the server where their value will be\n * converted to camelCase\n */\n propsToUpdateValue: [\n 'updated',\n 'layers',\n 'members',\n 'recordings',\n 'playbacks',\n ],\n}\n\n/**\n * Follows the same convention as `src/types/utils/IsTimestamp`\n */\nconst isTimestampProperty = (prop: string) => {\n return prop.endsWith('At')\n}\n\nexport type ToExternalJSONResult<T> = {\n [Property in NonNullable<keyof T> as SnakeToCamelCase<\n Extract<Property, string>\n >]: ConverToExternalTypes<Extract<Property, string>, T[Property]>\n}\n\n/**\n * Converts a record (a JSON coming from the server) to a JSON meant\n * to be consumed by our users. This mostly mean converting properties\n * from snake_case to camelCase along with some other minor case\n * convertions to guarantee that our JS users will always interact\n * with camelCase properties.\n *\n * It's worth noting that this util is suited exactly to meet our\n * needs and won't (propertly) handle cases where the input record\n * doesn't have all its properties with casing other than snake_case.\n * This is on purpose to keep this util as light and fast as possible\n * since we have the guarantee that the server will always send their\n * payloads formatted this way.\n * @internal\n */\nexport const toExternalJSON = <T extends object>(\n input: T,\n options: typeof DEFAULT_OPTIONS = DEFAULT_OPTIONS\n) => {\n // @ts-expect-error\n if (input?.__sw_symbol || input?.__sw_proxy) {\n // Return if the input is a BaseComponent or a Proxy\n return input as unknown as ToExternalJSONResult<T>\n }\n\n return Object.entries(input).reduce((reducer, [key, value]) => {\n const prop = fromSnakeToCamelCase(key) as any\n const propType = typeof value\n\n /**\n * While this check won't be enough to detect all possible objects\n * it would cover our needs here since we just need to detect that\n * it's not a primitive value\n */\n if (propType === 'object' && value) {\n if (Array.isArray(value)) {\n if (options.propsToUpdateValue.includes(key)) {\n reducer[prop] = value.map((v) => {\n if (typeof v === 'string') {\n return fromSnakeToCamelCase(v)\n }\n return toExternalJSON(v)\n })\n } else {\n reducer[prop] = value\n }\n } else {\n reducer[prop] = toExternalJSON(value as T)\n }\n } else {\n if (isTimestampProperty(prop)) {\n reducer[prop] = toDateObject(value)\n } else {\n reducer[prop] = value\n }\n }\n\n return reducer\n }, {} as Record<string, unknown>) as ToExternalJSONResult<T>\n}\n\n/**\n * Converts values from snake_case to camelCase\n * @internal\n */\nexport const fromSnakeToCamelCase = (input: string) => {\n if (!input.includes('_')) {\n return input\n }\n return input.split('_').reduce((reducer, part, index) => {\n const fc = part.trim().charAt(0)\n const remainingChars = part.substr(1).toLowerCase()\n\n return `${reducer}${\n index === 0 ? fc.toLowerCase() : fc.toUpperCase()\n }${remainingChars}`\n }, '')\n}\n", "import { fromCamelToSnakeCase } from './common'\nimport { EVENT_NAMESPACE_DIVIDER } from './constants'\nimport { EventEmitter } from './EventEmitter'\n\ntype ToInternalEventNameParams<\n EventTypes extends EventEmitter.ValidEventTypes\n> = {\n event: EventEmitter.EventNames<EventTypes>\n namespace?: string\n}\n\nexport const toInternalEventName = <\n EventTypes extends EventEmitter.ValidEventTypes\n>({\n event,\n namespace,\n}: ToInternalEventNameParams<EventTypes>) => {\n // TODO: improve types of getNamespacedEvent and fromCamelToSnakeCase\n if (typeof event === 'string') {\n // other transforms here..\n event = getNamespacedEvent({\n event,\n namespace,\n }) as EventEmitter.EventNames<EventTypes>\n event = fromCamelToSnakeCase<EventEmitter.EventNames<EventTypes>>(event)\n }\n\n return event\n}\n\nconst getNamespacedEvent = ({\n namespace,\n event,\n}: {\n event: string\n namespace?: string\n}) => {\n /**\n * If getNamespacedEvent is called with the `namespace` already\n * present in the `event` or with a falsy namespace we'll return it\n * as is\n */\n if (!namespace || event.startsWith(namespace)) {\n return event\n }\n\n return `${namespace}${EVENT_NAMESPACE_DIVIDER}${event}`\n}\n", "import { JSONRPCRequest } from '..'\nimport { MapToPubSubShape } from '../redux/interfaces'\nimport { isWebrtcEventType } from './common'\n\nexport const toInternalAction = <\n T extends { event_type: string; params?: unknown; node_id?: string }\n>(\n event: T\n) => {\n const { event_type, params, node_id } = event\n\n /**\n * queuing.relay.tasks has a slightly different shape:\n * no nested \"params\" so we return the whole event.\n */\n if (event_type === 'queuing.relay.tasks') {\n return {\n type: event_type,\n payload: event,\n } as MapToPubSubShape<T>\n }\n\n /**\n * `webrtc.*` events need to carry the node_id with them\n */\n if (isWebrtcEventType(event_type) && (params as JSONRPCRequest)?.jsonrpc) {\n const vertoRPC = params as JSONRPCRequest\n if (vertoRPC.params) {\n vertoRPC.params.nodeId = node_id\n }\n return {\n type: event_type,\n payload: vertoRPC,\n } as MapToPubSubShape<T>\n }\n\n return {\n type: event_type,\n payload: params,\n } as MapToPubSubShape<T>\n}\n", "import { CamelToSnakeCase } from '../types/utils'\nimport { fromCamelToSnakeCase } from './common'\n\ntype ToSnakeCaseKeys<T> = {\n [Property in NonNullable<keyof T> as CamelToSnakeCase<\n Extract<Property, string>\n >]: T[Property]\n}\n\nexport const toSnakeCaseKeys = <T extends Record<string, any>>(\n obj: T,\n transform: (value: string) => any = (value: string) => value,\n result: Record<string, any> = {}\n) => {\n if (Array.isArray(obj)) {\n result = obj.map((item: any, index: number) => {\n if (typeof item === 'object') {\n return toSnakeCaseKeys(item, transform, result[index])\n }\n return item\n })\n } else {\n Object.keys(obj).forEach((key) => {\n const newKey = fromCamelToSnakeCase(key)\n // Both 'object's and arrays will enter this branch\n if (obj[key] && typeof obj[key] === 'object') {\n result[newKey] = toSnakeCaseKeys(obj[key], transform, result[newKey])\n } else {\n result[newKey] = transform(obj[key])\n }\n })\n }\n\n return result as ToSnakeCaseKeys<T>\n}\n", "import type { APIMethodsMap } from './interfaces'\nimport type { ConstructableType } from '../types/utils'\n\nexport const extendComponent = <T, M>(\n klass: any,\n methods: APIMethodsMap<M>\n) => {\n Object.keys(methods).forEach((methodName) => {\n if (klass.prototype.hasOwnProperty(methodName)) {\n throw new Error(`[extendComponent] Duplicated method name: ${methodName}`)\n }\n })\n\n Object.defineProperties(klass.prototype, methods)\n\n return klass as ConstructableType<T>\n}\n", "type ArgumentTypes<F extends Function> = F extends (...args: infer A) => any\n ? A\n : never\n\ntype MethodTypes = {\n cancel: () => void\n flush: () => void\n}\n\nexport function debounce<T extends Function>(\n fn: T,\n wait?: number,\n callFirst?: false\n): ((...args: ArgumentTypes<T>) => void) & MethodTypes\n\nexport function debounce<T extends Function>(\n fn: T,\n wait: number = 0,\n callFirst?: false\n) {\n let timeout: NodeJS.Timeout | null = null\n let debouncedFn: T | null = null\n\n const clear = function () {\n if (timeout) {\n clearTimeout(timeout)\n\n debouncedFn = null\n timeout = null\n }\n }\n\n const flush = function () {\n const call = debouncedFn\n clear()\n\n if (call) {\n call()\n }\n }\n\n const debounceWrapper = function () {\n if (!wait) {\n // @ts-expect-error\n return fn.apply(this, arguments)\n }\n\n // @ts-expect-error\n const context = this\n const args = arguments\n const callNow = callFirst && !timeout\n clear()\n\n // @ts-expect-error\n debouncedFn = function () {\n fn.apply(context, args)\n }\n\n timeout = setTimeout(function () {\n timeout = null\n\n if (!callNow) {\n const call = debouncedFn\n debouncedFn = null\n\n return call?.()\n }\n }, wait)\n\n if (callNow && debouncedFn) {\n return debouncedFn()\n }\n }\n\n debounceWrapper.cancel = clear\n debounceWrapper.flush = flush\n\n return debounceWrapper\n}\n", "/**\n * Class representing a close event.\n * The `ws` package does not expose it so we can easily create one in here.\n *\n * @extends Event\n */\nexport class SWCloseEvent {\n public code: number\n public reason: string\n public wasClean: boolean\n constructor(\n public type: string,\n options: { code?: number; reason?: string; wasClean?: boolean } = {}\n ) {\n this.code = options.code === undefined ? 0 : options.code\n this.reason = options.reason === undefined ? '' : options.reason\n this.wasClean = options.wasClean === undefined ? false : options.wasClean\n }\n}\n", "\n\nexport const stripNamespacePrefix = (\n event: string,\n namespace?: string\n): string => {\n if (namespace && typeof namespace === 'string') {\n const regex = new RegExp(`^${namespace}\\.`)\n return event.replace(regex, '')\n }\n const items = event.split('.')\n if (items.length > 1) {\n items.shift()\n return items.join('.')\n }\n return event\n}\n", "import { getLogger } from '../utils/logger'\n\nconst DEFAULT_MAX_RETRIES = 10\nconst DEFAULT_INITIAL_DELAY = 100\nconst DEFAULT_DELAY_VARIATION = 1\n\ninterface AsyncRetryOptions<T> {\n asyncCallable: () => Promise<T>\n maxRetries?: number\n delayFn?: () => number\n validator?: (promiseResult: T) => void | never\n expectedErrorHandler?: (error: any) => boolean\n}\n\ninterface DelayOptions {\n initialDelay?: number\n variation?: number\n delayLimit?: number\n}\n\nexport const increasingDelay = ({\n delayLimit: upperDelayLimit = Number.MAX_SAFE_INTEGER,\n initialDelay = DEFAULT_INITIAL_DELAY,\n variation = DEFAULT_DELAY_VARIATION,\n}: DelayOptions) => {\n if (initialDelay < 0 || upperDelayLimit < 0 || variation < 0) {\n throw new Error('No Negative Numbers')\n }\n if (initialDelay > upperDelayLimit) {\n throw new Error('initialDelay must be lte delayLimit')\n }\n\n let delay = Math.min(initialDelay, upperDelayLimit)\n return () => {\n if (delay === upperDelayLimit) {\n // stop incrementing the delay and just return upperDelayLimit\n return upperDelayLimit\n }\n const currentDelay = delay\n delay = Math.min(delay + variation, upperDelayLimit)\n\n return currentDelay\n }\n}\n\nexport const decreasingDelay = ({\n delayLimit: bottomDelayLimit = 0,\n initialDelay = DEFAULT_INITIAL_DELAY,\n variation = DEFAULT_DELAY_VARIATION,\n}: DelayOptions) => {\n if (initialDelay < 0 || bottomDelayLimit < 0 || variation < 0) {\n throw new Error('No Negative Numbers')\n }\n if (initialDelay < bottomDelayLimit) {\n throw new Error('initialDelay must be gte delayLimit')\n }\n\n let delay = Math.max(initialDelay, bottomDelayLimit)\n\n return () => {\n if (delay === bottomDelayLimit) {\n // stop incrementing the delay and just return upperDelayLimit\n return bottomDelayLimit\n }\n const currentDelay = delay\n delay = Math.max(delay - variation, bottomDelayLimit)\n\n return currentDelay\n }\n}\n\nexport const constDelay = ({\n initialDelay = DEFAULT_INITIAL_DELAY,\n}: Pick<DelayOptions, 'initialDelay'>) => {\n if (initialDelay < 0) {\n throw new Error('No Negative Numbers')\n }\n return () => initialDelay\n}\n\nexport const asyncRetry = async <T>({\n asyncCallable,\n maxRetries: retries = DEFAULT_MAX_RETRIES,\n delayFn,\n validator,\n expectedErrorHandler,\n}: AsyncRetryOptions<T>): Promise<T> => {\n let remainingAttempts = retries - 1 // the 1st call counts as an attempt\n let wait = 0\n\n const promiseAttempt = async () => {\n try {\n let result: Awaited<T>\n\n // Should not defer the call when: wait <= 0\n if (wait <= 0) {\n result = await asyncCallable()\n } else {\n result = await new Promise<T>((resolve, reject) =>\n setTimeout(() => {\n asyncCallable().then(resolve).catch(reject)\n }, wait)\n )\n }\n\n if (remainingAttempts) {\n // avoid messing with the normal returns in the last attempt\n validator?.(result)\n }\n\n return result\n } catch (error) {\n if (remainingAttempts-- > 0 && !expectedErrorHandler?.(error)) {\n wait = delayFn?.() ?? 0\n getLogger().debug(\n `Retrying request: ${retries - remainingAttempts} of ${retries}`\n )\n return promiseAttempt()\n } else {\n throw error\n }\n }\n }\n\n return promiseAttempt()\n}\n", "import { uuid } from '../utils'\nimport { JSONRPCMethod } from '../utils/interfaces'\n\ninterface MakeRPCRequestParams {\n id?: string\n method: JSONRPCMethod\n params: {\n // TODO: use list of types?\n [key: string]: any\n }\n}\nexport const makeRPCRequest = (params: MakeRPCRequestParams) => {\n return {\n jsonrpc: '2.0' as const,\n id: params.id ?? uuid(),\n ...params,\n }\n}\n\ninterface MakeRPCResponseParams {\n id: string\n result: {\n // TODO: use list of types?\n [key: string]: any\n }\n}\nexport const makeRPCResponse = (params: MakeRPCResponseParams) => {\n return {\n jsonrpc: '2.0' as const,\n ...params,\n }\n}\n", "import { makeRPCRequest } from './helpers'\n\ntype WithToken = { token: string; jwt_token?: never }\ntype WithJWT = { token?: never; jwt_token: string }\ntype RPCConnectAuthentication = { project?: string } & (WithToken | WithJWT)\nexport type RPCConnectParams = {\n authentication: RPCConnectAuthentication\n version?: typeof DEFAULT_CONNECT_VERSION\n agent?: string\n protocol?: string\n authorization_state?: string\n contexts?: string[]\n topics?: string[]\n eventing?: string[]\n event_acks?: boolean\n}\n\nexport const DEFAULT_CONNECT_VERSION = {\n major: 3,\n minor: 0,\n revision: 0,\n}\n\nexport const UNIFIED_CONNECT_VERSION = {\n major: 4,\n minor: 0,\n revision: 0,\n}\n\nexport const RPCConnect = (params: RPCConnectParams) => {\n return makeRPCRequest({\n method: 'signalwire.connect',\n params: {\n version: DEFAULT_CONNECT_VERSION,\n event_acks: true,\n ...params,\n },\n })\n}\n", "import { makeRPCRequest } from './helpers'\n\nexport type RPCReauthenticateParams = { project: string; jwt_token: string }\n\nexport const RPCReauthenticate = (authentication: RPCReauthenticateParams) => {\n return makeRPCRequest({\n method: 'signalwire.reauthenticate',\n params: {\n authentication,\n },\n })\n}\n", "import { makeRPCRequest, makeRPCResponse } from './helpers'\n\nexport const RPCPing = () => {\n return makeRPCRequest({\n method: 'signalwire.ping',\n params: {\n timestamp: Date.now() / 1000,\n },\n })\n}\n\nexport const RPCPingResponse = (id: string, timestamp?: number) => {\n return makeRPCResponse({\n id,\n result: {\n timestamp: timestamp || Date.now() / 1000,\n },\n })\n}\n", "import { makeRPCRequest } from './helpers'\nimport { JSONRPCMethod } from '../utils/interfaces'\n\ntype RPCExecuteParams = {\n id?: string\n method: JSONRPCMethod\n params: Record<string, unknown>\n}\n\nexport const RPCExecute = ({ method, params }: RPCExecuteParams) => {\n return makeRPCRequest({\n method,\n params,\n })\n}\n", "import { makeRPCResponse } from './helpers'\n\nexport const RPCDisconnectResponse = (id: string) => {\n return makeRPCResponse({\n id,\n result: {},\n })\n}\n", "import { makeRPCRequest, makeRPCResponse } from './helpers'\nimport { VertoMethod } from '../utils/interfaces'\n\ntype VertoParams = { [key: string]: any }\n\nconst tmpMap: VertoParams = {\n id: 'callID',\n destinationNumber: 'destination_number',\n remoteCallerName: 'remote_caller_id_name',\n remoteCallerNumber: 'remote_caller_id_number',\n callerName: 'caller_id_name',\n callerNumber: 'caller_id_number',\n fromFabricAddressId: 'from_fabric_address_id',\n}\n\n/**\n * Translate SDK fields into verto variables\n */\nconst filterVertoParams = (params: VertoParams) => {\n if (params.hasOwnProperty('dialogParams')) {\n // prettier-ignore\n const {\n remoteSdp,\n localStream,\n remoteStream,\n ...dialogParams\n } = params.dialogParams\n for (const key in tmpMap) {\n if (key && dialogParams.hasOwnProperty(key)) {\n // @ts-ignore\n dialogParams[tmpMap[key]] = dialogParams[key]\n delete dialogParams[key]\n }\n }\n params.dialogParams = dialogParams\n }\n\n return params\n}\n\nconst buildVertoRPCMessage = (method: VertoMethod) => {\n return (params: VertoParams = {}) => {\n return makeRPCRequest({\n method,\n params: filterVertoParams(params),\n })\n }\n}\n\nexport const VertoInvite = buildVertoRPCMessage('verto.invite')\nexport const VertoBye = buildVertoRPCMessage('verto.bye')\nexport const VertoAttach = buildVertoRPCMessage('verto.attach')\nexport const VertoModify = buildVertoRPCMessage('verto.modify')\nexport const VertoInfo = buildVertoRPCMessage('verto.info')\nexport const VertoAnswer = buildVertoRPCMessage('verto.answer')\nexport const VertoSubscribe = buildVertoRPCMessage('verto.subscribe')\nexport const VertoPong = buildVertoRPCMessage('verto.pong')\nexport const VertoResult = (id: string, method: VertoMethod) => {\n return makeRPCResponse({\n id,\n result: {\n method,\n },\n })\n}\n\nexport interface VertoModifyResponse {\n action: string\n callID: string\n holdState: 'held' | 'active'\n node_id?: string\n sdp?: string\n}\n", "import { makeRPCResponse } from './helpers';\n\n\nexport const RPCEventAckResponse = (id: string) => makeRPCResponse({ id, result: {} })\n", "import { createAction, Action } from './toolkit'\nimport type {\n JSONRPCRequest,\n SessionAuthError,\n SessionEvents,\n SessionActions,\n} from '../utils/interfaces'\n\nexport const initAction = createAction('swSdk/init')\nexport const destroyAction = createAction('swSdk/destroy')\n/**\n * Used to trigger a `signalwire.reauthenticate`\n */\nexport const reauthAction = createAction<{ token: string }>('swSdk/reauth')\n\nexport const authErrorAction = createAction<{ error: SessionAuthError }>(\n 'auth/error'\n)\nexport const authSuccessAction = createAction('auth/success')\nexport const authExpiringAction = createAction('auth/expiring')\n\nexport const socketMessageAction = createAction<JSONRPCRequest, string>(\n 'socket/message'\n)\n\nexport const sessionDisconnectedAction = createAction<void, SessionEvents>(\n 'session.disconnected'\n)\nexport const sessionReconnectingAction = createAction<void, SessionEvents>(\n 'session.reconnecting'\n)\nexport const sessionForceCloseAction = createAction<void, SessionActions>(\n 'session.forceClose'\n)\nconst formatCustomSagaAction = (id: string, action: Action) => {\n return `${action.type}/${id}`\n}\n\nexport const makeCustomSagaAction = (id: string, action: Action) => {\n return {\n ...action,\n type: formatCustomSagaAction(id, action),\n }\n}\n\nexport const getCustomSagaActionType = (id: string, action: Action) => {\n return formatCustomSagaAction(id, action)\n}\n\nexport { createAction }\n", "/**\n * Cherry-picked (and adapted) version of Redux Toolkit. API\n * wise it remains fully compatible but we changed a couple\n * of things to reduce the overall bundle size. Most\n * important is that our version doesn't depend on Immer and\n * we don't include any of the default middleares included\n * by RTK like thunks or inmutable state (we do this through\n * TS).\n */\nexport * from 'redux'\nexport * from './createAction'\nexport * from './configureStore'\n", "import type { Action } from 'redux'\nimport type {\n IsUnknownOrNonInferrable,\n IfMaybeUndefined,\n IfVoid,\n IsAny,\n} from './tsHelpers'\n\n\n/**\n * An action with a string type and an associated payload. This is the\n * type of action returned by `createAction()` action creators.\n *\n * @template P The type of the action's payload.\n * @template T the type used for the action type.\n * @template M The type of the action's meta (optional)\n * @template E The type of the action's error (optional)\n *\n * @public\n */\nexport type PayloadAction<\n P = void,\n T extends string = string,\n M = never,\n E = never\n> = {\n payload: P\n type: T\n} & ([M] extends [never]\n ? {}\n : {\n meta: M\n }) &\n ([E] extends [never]\n ? {}\n : {\n error: E\n })\n\n/**\n * A \"prepare\" method to be used as the second parameter of `createAction`.\n * Takes any number of arguments and returns a Flux Standard Action without\n * type (will be added later) that *must* contain a payload (might be undefined).\n *\n * @public\n */\nexport type PrepareAction<P> =\n | ((...args: any[]) => { payload: P })\n | ((...args: any[]) => { payload: P; meta: any })\n | ((...args: any[]) => { payload: P; error: any })\n | ((...args: any[]) => { payload: P; meta: any; error: any })\n\n/**\n * Internal version of `ActionCreatorWithPreparedPayload`. Not to be used externally.\n *\n * @internal\n */\nexport type _ActionCreatorWithPreparedPayload<\n PA extends PrepareAction<any> | void,\n T extends string = string\n> = PA extends PrepareAction<infer P>\n ? ActionCreatorWithPreparedPayload<\n Parameters<PA>,\n P,\n T,\n ReturnType<PA> extends {\n error: infer E\n }\n ? E\n : never,\n ReturnType<PA> extends {\n meta: infer M\n }\n ? M\n : never\n >\n : void\n\n/**\n * Basic type for all action creators.\n *\n * @inheritdoc {redux#ActionCreator}\n */\ninterface BaseActionCreator<P, T extends string, M = never, E = never> {\n type: T\n match: (action: Action<unknown>) => action is PayloadAction<P, T, M, E>\n}\n\n/**\n * An action creator that takes multiple arguments that are passed\n * to a `PrepareAction` method to create the final Action.\n * @typeParam Args arguments for the action creator function\n * @typeParam P `payload` type\n * @typeParam T `type` name\n * @typeParam E optional `error` type\n * @typeParam M optional `meta` type\n *\n * @inheritdoc {redux#ActionCreator}\n *\n * @public\n */\nexport interface ActionCreatorWithPreparedPayload<\n Args extends unknown[],\n P,\n T extends string = string,\n E = never,\n M = never\n> extends BaseActionCreator<P, T, M, E> {\n /**\n * Calling this {@link redux#ActionCreator} with `Args` will return\n * an Action with a payload of type `P` and (depending on the `PrepareAction`\n * method used) a `meta`- and `error` property of types `M` and `E` respectively.\n */\n (...args: Args): PayloadAction<P, T, M, E>\n}\n\n/**\n * An action creator of type `T` that takes an optional payload of type `P`.\n *\n * @inheritdoc {redux#ActionCreator}\n *\n * @public\n */\nexport interface ActionCreatorWithOptionalPayload<P, T extends string = string>\n extends BaseActionCreator<P, T> {\n /**\n * Calling this {@link redux#ActionCreator} with an argument will\n * return a {@link PayloadAction} of type `T` with a payload of `P`.\n * Calling it without an argument will return a PayloadAction with a payload of `undefined`.\n */\n (payload?: P): PayloadAction<P, T>\n}\n\n/**\n * An action creator of type `T` that takes no payload.\n *\n * @inheritdoc {redux#ActionCreator}\n *\n * @public\n */\nexport interface ActionCreatorWithoutPayload<T extends string = string>\n extends BaseActionCreator<undefined, T> {\n /**\n * Calling this {@link redux#ActionCreator} will\n * return a {@link PayloadAction} of type `T` with a payload of `undefined`\n */\n (): PayloadAction<undefined, T>\n}\n\n/**\n * An action creator of type `T` that requires a payload of type P.\n *\n * @inheritdoc {redux#ActionCreator}\n *\n * @public\n */\nexport interface ActionCreatorWithPayload<P, T extends string = string>\n extends BaseActionCreator<P, T> {\n /**\n * Calling this {@link redux#ActionCreator} with an argument will\n * return a {@link PayloadAction} of type `T` with a payload of `P`\n */\n (payload: P): PayloadAction<P, T>\n}\n\n/**\n * An action creator of type `T` whose `payload` type could not be inferred. Accepts everything as `payload`.\n *\n * @inheritdoc {redux#ActionCreator}\n *\n * @public\n */\nexport interface ActionCreatorWithNonInferrablePayload<\n T extends string = string\n> extends BaseActionCreator<unknown, T> {\n /**\n * Calling this {@link redux#ActionCreator} with an argument will\n * return a {@link PayloadAction} of type `T` with a payload\n * of exactly the type of the argument.\n */\n <PT extends unknown>(payload: PT): PayloadAction<PT, T>\n}\n\n/**\n * An action creator that produces actions with a `payload` attribute.\n *\n * @typeParam P the `payload` type\n * @typeParam T the `type` of the resulting action\n * @typeParam PA if the resulting action is preprocessed by a `prepare` method, the signature of said method.\n *\n * @public\n */\nexport type PayloadActionCreator<\n P = void,\n T extends string = string,\n PA extends PrepareAction<P> | void = void\n> = IfPrepareActionMethodProvided<\n PA,\n _ActionCreatorWithPreparedPayload<PA, T>,\n // else\n IsAny<\n P,\n ActionCreatorWithPayload<any, T>,\n IsUnknownOrNonInferrable<\n P,\n ActionCreatorWithNonInferrablePayload<T>,\n // else\n IfVoid<\n P,\n ActionCreatorWithoutPayload<T>,\n // else\n IfMaybeUndefined<\n P,\n ActionCreatorWithOptionalPayload<P, T>,\n // else\n ActionCreatorWithPayload<P, T>\n >\n >\n >\n >\n>\n\n/**\n * A utility function to create an action creator for the given action type\n * string. The action creator accepts a single argument, which will be included\n * in the action object as a field called payload. The action creator function\n * will also have its toString() overriden so that it returns the action type,\n * allowing it to be used in reducer logic that is looking for that action type.\n *\n * @param type The action type to use for created actions.\n * @param prepare (optional) a method that takes any number of arguments and returns { payload } or { payload, meta }.\n * If this is given, the resulting action creator will pass its arguments to this method to calculate payload & meta.\n *\n * @public\n */\nexport function createAction<P = void, T extends string = string>(\n type: T\n): PayloadActionCreator<P, T>\n\n/**\n * A utility function to create an action creator for the given action type\n * string. The action creator accepts a single argument, which will be included\n * in the action object as a field called payload. The action creator function\n * will also have its toString() overriden so that it returns the action type,\n * allowing it to be used in reducer logic that is looking for that action type.\n *\n * @param type The action type to use for created actions.\n * @param prepare (optional) a method that takes any number of arguments and returns { payload } or { payload, meta }.\n * If this is given, the resulting action creator will pass its arguments to this method to calculate payload & meta.\n *\n * @public\n */\nexport function createAction<\n PA extends PrepareAction<any>,\n T extends string = string\n>(\n type: T,\n prepareAction: PA\n): PayloadActionCreator<ReturnType<PA>['payload'], T, PA>\n\nexport function createAction(type: string, prepareAction?: Function): any {\n function actionCreator(...args: any[]) {\n if (prepareAction) {\n let prepared = prepareAction(...args)\n if (!prepared) {\n throw new Error('prepareAction did not return an object')\n }\n\n return {\n type,\n payload: prepared.payload,\n ...('meta' in prepared && { meta: prepared.meta }),\n ...('error' in prepared && { error: prepared.error }),\n }\n }\n return { type, payload: args[0] }\n }\n\n actionCreator.toString = () => `${type}`\n\n actionCreator.type = type\n\n actionCreator.match = (action: Action<unknown>): action is PayloadAction =>\n action.type === type\n\n return actionCreator\n}\n\n// helper types for more readable typings\n\ntype IfPrepareActionMethodProvided<\n PA extends PrepareAction<any> | void,\n True,\n False\n> = PA extends (...args: any[]) => any ? True : False\n", "import type {\n Reducer,\n ReducersMapObject,\n Middleware,\n Action,\n AnyAction,\n StoreEnhancer,\n Store,\n Dispatch,\n PreloadedState,\n CombinedState,\n} from 'redux'\nimport { createStore, compose, applyMiddleware, combineReducers } from 'redux'\nimport type { EnhancerOptions as DevToolsOptions } from './devtoolsExtension'\nimport { composeWithDevTools } from './devtoolsExtension'\n\nimport isPlainObject from './isPlainObject'\nimport {\n CurriedGetDefaultMiddleware,\n curryGetDefaultMiddleware,\n} from './getDefaultMiddleware'\nimport type { DispatchForMiddlewares, NoInfer } from './tsHelpers'\n\nconst IS_PRODUCTION = process.env.NODE_ENV === 'production'\n\n/**\n * Callback function type, to be used in `ConfigureStoreOptions.enhancers`\n *\n * @public\n */\nexport type ConfigureEnhancersCallback = (\n defaultEnhancers: readonly StoreEnhancer[]\n) => StoreEnhancer[]\n\n/**\n * Options for `configureStore()`.\n *\n * @public\n */\nexport interface ConfigureStoreOptions<\n S = any,\n A extends Action = AnyAction,\n M extends Middlewares<S> = Middlewares<S>\n> {\n /**\n * A single reducer function that will be used as the root reducer, or an\n * object of slice reducers that will be passed to `combineReducers()`.\n */\n reducer: Reducer<S, A> | ReducersMapObject<S, A>\n\n /**\n * An array of Redux middleware to install. If not supplied, defaults to\n * the set of middleware returned by `getDefaultMiddleware()`.\n */\n middleware?: ((getDefaultMiddleware: CurriedGetDefaultMiddleware<S>) => M) | M\n\n /**\n * Whether to enable Redux DevTools integration. Defaults to `true`.\n *\n * Additional configuration can be done by passing Redux DevTools options\n */\n devTools?: boolean | DevToolsOptions\n\n /**\n * The initial state, same as Redux's createStore.\n * You may optionally specify it to hydrate the state\n * from the server in universal apps, or to restore a previously serialized\n * user session. If you use `combineReducers()` to produce the root reducer\n * function (either directly or indirectly by passing an object as `reducer`),\n * this must be an object with the same shape as the reducer map keys.\n */\n /*\n Not 100% correct but the best approximation we can get:\n - if S is a `CombinedState` applying a second `CombinedState` on it does not change anything.\n - if it is not, there could be two cases:\n - `ReducersMapObject<S, A>` is being passed in. In this case, we will call `combineReducers` on it and `CombinedState<S>` is correct\n - `Reducer<S, A>` is being passed in. In this case, actually `CombinedState<S>` is wrong and `S` would be correct.\n As we cannot distinguish between those two cases without adding another generic paramter,\n we just make the pragmatic assumption that the latter almost never happens.\n */\n preloadedState?: PreloadedState<CombinedState<NoInfer<S>>>\n\n /**\n * The store enhancers to apply. See Redux's `createStore()`.\n * All enhancers will be included before the DevTools Extension enhancer.\n * If you need to customize the order of enhancers, supply a callback\n * function that will receive the original array (ie, `[applyMiddleware]`),\n * and should return a new array (such as `[applyMiddleware, offline]`).\n * If you only need to add middleware, you can use the `middleware` parameter instead.\n */\n enhancers?: StoreEnhancer[] | ConfigureEnhancersCallback\n}\n\ntype Middlewares<S> = ReadonlyArray<Middleware<{}, S>>\n\n/**\n * A Redux store returned by `configureStore()`. Supports dispatching\n * side-effectful _thunks_ in addition to plain actions.\n *\n * @public\n */\nexport interface EnhancedStore<\n S = any,\n A extends Action = AnyAction,\n M extends Middlewares<S> = Middlewares<S>\n> extends Store<S, A> {\n /**\n * The `dispatch` method of your store, enhanced by all its middlewares.\n *\n * @inheritdoc\n */\n dispatch: Dispatch<A> & DispatchForMiddlewares<M>\n}\n\n/**\n * A friendly abstraction over the standard Redux `createStore()` function.\n *\n * @param config The store configuration.\n * @returns A configured Redux store.\n *\n * @public\n */\nexport function configureStore<\n S extends Reducer<CombinedState<S>, A> = any,\n A extends Action = AnyAction,\n M extends Middlewares<S> = []\n>(options: ConfigureStoreOptions<S, A, M>): EnhancedStore<S, A, M> {\n const curriedGetDefaultMiddleware = curryGetDefaultMiddleware<S>()\n\n const {\n reducer = undefined,\n middleware = curriedGetDefaultMiddleware(),\n devTools = true,\n preloadedState = undefined,\n enhancers = undefined,\n } = options || {}\n\n let rootReducer\n\n if (typeof reducer === 'function') {\n rootReducer = reducer\n } else if (isPlainObject(reducer)) {\n rootReducer = combineReducers(reducer)\n } else {\n throw new Error(\n '\"reducer\" is a required argument, and must be a function or an object of functions that can be passed to combineReducers'\n )\n }\n\n let finalMiddleware = middleware\n if (typeof finalMiddleware === 'function') {\n finalMiddleware = finalMiddleware(curriedGetDefaultMiddleware)\n\n if (!IS_PRODUCTION && !Array.isArray(finalMiddleware)) {\n throw new Error(\n 'when using a middleware builder function, an array of middleware must be returned'\n )\n }\n }\n if (\n !IS_PRODUCTION &&\n finalMiddleware.some((item) => ty