Source: sdk.js

'use strict';
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
/*
 * Nexmo Client SDK
 *  Main wrapper
 *
 * Copyright (c) Nexmo Inc.
*/
const WildEmitter = require('wildemitter');
const loglevel_plugin_prefix_1 = __importDefault(require("loglevel-plugin-prefix"));
const loglevel_1 = __importDefault(require("loglevel"));
const nexmoClientError_1 = require("./nexmoClientError");
const socket_io_client_1 = __importDefault(require("socket.io-client"));
const js_1 = __importDefault(require("@bugsnag/js"));
const publicip_1 = __importDefault(require("./modules/publicip"));
const utils_1 = __importDefault(require("./utils"));
const application_1 = __importDefault(require("./application"));
const errors_emitter_1 = __importDefault(require("./modules/errors_emitter"));
const user_1 = __importDefault(require("./user"));
const rtc_helper_1 = __importDefault(require("./modules/rtc_helper"));
loglevel_plugin_prefix_1.default.reg(loglevel_1.default);
loglevel_plugin_prefix_1.default.apply(loglevel_1.default, {
    template: '[%t] %l (NXM-%n):',
    timestampFormatter: (date) => {
        return date.toTimeString().replace(/.*(\d{2}:\d{2}:\d{2}).*/, '$1');
    },
    levelFormatter: (level) => {
        return level.toUpperCase();
    },
    nameFormatter: (name) => {
        return name || 'SDK';
    }
});
/**
 * The parent NexmoClient class.
 *
 * @class NexmoClient
 *
 * @param {object} params the settings to initialise the SDK
 * @param {string} params.debug='silent' set mode to 'debug', 'info', 'warn', or 'error' for customized logging levels in the console
 * @param {string} params.url='nexmo_ws_url' Nexmo Conversation Websocket url, default is wss://ws.nexmo.com (wss://ws-us-1.nexmo.com for WDC, wss://ws-us-2.nexmo.com for DAL, wss://ws-eu-1.nexmo.com for LON, wss://ws-sg-1.nexmo.com for SNG)
 * @param {string} params.nexmo_api_url=Nexmo Conversation Api url, default is https://api.nexmo.com (https://api-us-1.nexmo.com for WDC, https://api-us-2.nexmo.com for DAL, https://api-eu-1.nexmo.com for LON, https://api-sg-1.nexmo.com for SNG)
 * @param {string} params.ips_url='ips_url' Nexmo IPS url for image upload, default is https://api.nexmo.com/v1/image (https://api-us-1.nexmo.com/v1/image for WDC, https://api-us-2.nexmo.com/v1/image for DAL, https://api-eu-1.nexmo.com/v1/image for LON, https://api-sg-1.nexmo.com/v1/image for SNG)
 * @param {string} params.path='/v2/rtc' Nexmo Conversation Websocket url path suffix
 * @param {RTCStatsConfig} params.rtcstats set reporting for stream statistics (Internal event emit)
 * @param {Boolean} params.rtcstats.emit_events=false receive rtcstats:report event (deprecated)
 * @param {Boolean} params.rtcstats.emit_rtc_analytics=false receive rtcstats:analytics event
 * @param {number} params.rtcstats.emit_interval=1000 interval in ms for rtcstats:report and rtcstats:analytics
 * @param {Boolean} params.rtcstats.remote_collection=true collect client logs internally
 * @param {Boolean} params.rtcstats.remote_collection_url='gollum_url' url for collecting client logs internally
 * @param {number} params.rtcstats.remote_collection_interval=5000 interval in ms to collect client logs internally
 * @param {object} params.socket_io configure socket.io
 * @param {Boolean} params.socket_io.forceNew=true configure socket.io forceNew attribute
 * @param {Boolean} params.socket_io.autoConnect=true socket.io autoConnect attribute
 * @param {Boolean} params.socket_io.reconnection=true socket.io reconnection attribute
 * @param {number} params.socket_io.reconnectionAttempts=5 socket.io reconnectionAttempts attribute
 * @param {string[]} params.socket_io.transports='websocket' socket.io transports protocols
 * @param {string} params.sync='none' {'none' || 'lite' || 'full'} after a successful session creation, synchronise conversations, include events or nothing
 * @param {string} params.environment='production' development / production environment
 * @param {object[]} [params.iceServers=[]] configure iceServers for RTCPeerConnection
 * @param {object} params.log_reporter configure log reports for bugsnag tool
 * @param {Boolean} params.log_reporter.enabled=true
 * @param {string} params.log_reporter.bugsnag_key your bugsnag api key / defaults to Nexmo api key
 * @param {object} params.conversations_page_config configure paginated requests for conversations
 * @param {number} params.conversations_page_config.page_size=10 the number of resources returned in a single request list
 * @param {string} params.conversations_page_config.order=asc 'asc' or 'desc' ordering of resources (usually based on creation time)
 * @param {string} params.conversations_page_config.cursor cursor parameter to access the next or previous page of a data set
 * @param {object} params.events_page_config configure paginated requests for events
 * @param {number} params.events_page_config.page_size=10 the number of resources returned in a single request list
 * @param {string} params.events_page_config.order=asc 'asc' or 'desc' ordering of resources (usually based on creation time)
 * @param {string} params.events_page_config.event_type the type of event used to filter event requests. Supports wildcard options with :* eg. 'members:*'
 * @param {Boolean} params.enableEventsQueue=true mechanism to guarantee order of events received during a session
 * @param {Boolean} params.enableInboundOffer=false configure receiving offer for faster inbound call setup
 * @param {Boolean} params.acknowledgeEvents=true send acknowledgment over websocket to server for received events
 * @param {string} params.token the jwt token for network requests
 *
 * @emits NexmoClient#disconnect
 * @emits NexmoClient#error
 * @emits NexmoClient#ready
 * @emits NexmoClient#reconnect
 * @emits NexmoClient#reconnecting
*/
class NexmoClient {
    constructor(params = {}) {
        // save an array of instances
        const inputParams = params;
        this.config = {
            debug: 'silent',
            log_reporter: {
                enabled: false,
                bugsnag_key: null
            },
            environment: '@@environment',
            ips_url: '@@ips_url',
            nexmo_api_url: '@@nexmo_api_url',
            path: '/v2/rtc',
            repository: '@@repository',
            SDK_version: '@@SDK_version',
            socket_io: {
                reconnection: true,
                reconnectionAttempts: 5,
                reconnectionDelay: 2000,
                randomizationFactor: 0.75,
                reconnectionDelayMax: 15000,
                forceNew: true,
                autoConnect: true,
                transports: ['websocket'],
            },
            sync: 'none',
            url: '@@ws_url',
            iceServers: [],
            rtcstats: {
                remote_collection: true,
                remote_collection_url: '@@gollum_url',
                remote_collection_interval: 5000,
                emit_events: false,
                emit_rtc_analytics: false,
                emit_interval: 1000,
            },
            conversations_page_config: {
                page_size: 10,
                order: 'asc',
                cursor: ''
            },
            events_page_config: {
                page_size: 10,
                order: 'asc',
                event_type: ''
            },
            enableEventsQueue: true,
            enableInboundOffer: false,
            acknowledgeEvents: true,
            token: null
        };
        this.config.socket_io.query = {
            token: '',
            SDK_version: this.config.SDK_version,
            session_version: params.enableInboundOffer ? '0.0.3' : '0.0.1',
            acknowledge_events: this.config.acknowledgeEvents,
            OS_family: 'js',
            OS_revision: (typeof navigator !== 'undefined') ? navigator.userAgent : (typeof window !== 'undefined') ? window.navigator.userAgent : 'Generic JS navigator'
        };
        this.sessionReady = false;
        this.disconnectTimestamp = null;
        this.session_id = null;
        this.apiKey = null;
        this.requests = {};
        this.application = null;
        /*
          Definitions of log levels
          error: major error messages, some lost functionality
          warn: error messages which do not cause a functional failure
          info: informational messages, showing completion, progress, etc.
          debug: messages to help in diagnosing a problem
        */
        if (['debug', 'info', 'warn', 'error'].includes(inputParams.debug)) {
            loglevel_1.default.setLevel(inputParams.debug);
        }
        else if (inputParams.debug === true) {
            loglevel_1.default.setLevel('debug');
        }
        else {
            loglevel_1.default.setLevel('silent');
        }
        this.log = loglevel_1.default.noConflict();
        // set our config from the inputParams
        this.config = utils_1.default.deepMergeObj(this.config, this._sanitizeConfig(inputParams));
        // inject bug reporting tool
        if (this.config.log_reporter.enabled) {
            const bugsnagConfig = {
                apiKey: this.config.log_reporter.bugsnag_key || utils_1.default._getBugsnagKey(),
                appVersion: this.config.socket_io.query.SDK_version,
                releaseStage: this.config.environment
            };
            global.NXMbugsnagClient = js_1.default(bugsnagConfig);
        }
        WildEmitter.mixin(NexmoClient);
    }
    /**
     * Creates and sets the socket_io connection
     *
     * @private
    */
    _createAndSetConnection() {
        let connection;
        // Create the socket.io connection and allow multiple instances
        let socket_io_config = Object.assign({ path: this.config.path }, this.config.socket_io);
        connection = socket_io_client_1.default.connect(this.config.url, socket_io_config);
        this.connection = connection;
        // Listen to socket.io events
        /**
         * Client listening for ready event.
         *
         * @event NexmoClient#ready
         * @example <caption>Listen for websocket ready event </caption>
         *     rtc.on("ready", () => {
         *      console.log("connection ready");
         *     });
        */
        connection.on('connect', () => {
            this.emit('ready');
            this.sessionReady = true;
            this.log.info('websocket ready');
        });
        /**
         * Client listening for disconnect event.
         *
         * @event NexmoClient#disconnect
         * @example <caption>Listen for websocket disconnect event </caption>
         *     rtc.on("disconnect", (reason) => {
         *      console.log("disconnect", reason);
         *     });
        */
        connection.on('disconnect', (reason, details) => {
            this.emit('disconnect', (reason === "io client disconnect")
                ? NexmoClient.DISCONNECT_REASON.ClientDisconnected
                : (reason === "io server disconnect" && this.session_id) ? NexmoClient.DISCONNECT_REASON.TokenExpired
                    : NexmoClient.DISCONNECT_REASON.ConnectionError);
            this.disconnectTimestamp = Date.now();
            this.log.info('websocket disconnected', reason, details);
        });
        /**
         * Client listening for reconnect event.
         *
         * @event NexmoClient#reconnect
         * @example <caption>Listen for websocket reconnect event </caption>
         *     rtc.on("reconnect", (retry_number) => {
         *      console.log("reconnect", retry_number);
         *     });
        */
        connection.io.on('reconnect', (retry_number) => {
            this.emit('reconnect', retry_number);
            this.log.info('websocket reconnect', retry_number);
            utils_1.default.networkRequest({
                url: `${this.config.url.replace('wss://', 'https://')}/v2/rtc/metrics`,
                type: 'POST',
                token: this.config.token,
                data: {
                    sessionId: this.session_id,
                    metrics: [
                        {
                            name: 'TIME_TO_RECONNECT_SUCCESS',
                            time: this.disconnectTimestamp ? Date.now() - this.disconnectTimestamp : undefined,
                            attempt: retry_number
                        }
                    ]
                }
            }).catch((err) => {
                this.log.warn('Failed to publish socket reconnection metrics', err);
            });
        });
        /**
         * Client listening for reconnecting event.
         *
         * @event NexmoClient#reconnecting
         * @example <caption>Listen for websocket reconnecting event </caption>
         *     rtc.on("reconnecting", (retry_number): void => {
         *      console.log("reconnecting", retry_number);
         *     });
        */
        connection.io.on('reconnect_attempt', (retry_number) => {
            this.emit('reconnecting', retry_number);
            this.log.info('websocket reconnecting', retry_number);
        });
        /**
         * Client listening for error event.
         *
         * @event NexmoClient#error
         * @example <caption>Listen for websocket error event </caption>
         *     rtc.on("error", (error) => {
         *      console.log("error", error);
         *     });
        */
        connection.on('connect_error', (error) => {
            this.emit('error', new nexmoClientError_1.NexmoClientError(error));
            this.log.error('Socket.io reported a generic error', error);
        });
        connection.io.on("reconnect_failed", () => {
            this.emit('error', new nexmoClientError_1.NexmoClientError("error:client:reconnection_failed"));
            this.log.error('websocket reconnection error');
        });
        connection.io.on('packet', (packet) => {
            if (packet.type !== 2)
                return;
            if (packet.data[0] === 'echo')
                return; // ignore echo events
            const callbackAck = packet.data[2];
            if (callbackAck && typeof (callbackAck) === "function") {
                callbackAck();
            }
            const response = packet.data[1];
            // Set the type of the response
            response.type = packet.data[0];
            this.log.debug('<--', response.type, response);
            if (this.requests['session:login']) {
                const callback = this.requests['session:login'].callback;
                delete this.requests['session:login'];
                callback(response);
            }
            else if (response.rid in this.requests) {
                const callback = this.requests[response.rid].callback;
                delete this.requests[response.rid];
                delete response.delay;
                if (this.errorsEmitter) {
                    this.errorsEmitter.emitResponseIfError(response);
                }
                callback(response);
            }
            else {
                // This is an unsolicited event we emit it in application level
                // Excluding session:* events from being processed and check if event type is a system:error:* one
                if (this.errorsEmitter && response.type.startsWith('system:error:')) {
                    this.errorsEmitter.emitResponseIfError(response);
                }
                else if (response.type.startsWith('session:')) {
                    // Handle Events emitted from Reconnection
                    this.updateSession(response);
                }
                else if (this.application) {
                    this.application._enqueueEvent(response);
                }
            }
        });
        return connection;
    }
    /**
     * Revert any invalid params to our default
     *
     * @param {object} config the object to sanitize
     * @private
    */
    _sanitizeConfig(incomingConfig) {
        // make sure we allow specific values for the params
        // Sync
        let sanitizedConfig = incomingConfig;
        if (incomingConfig.sync && ['none', 'lite', 'full'].indexOf(incomingConfig.sync) === -1) {
            this.log.warn(`invalid param '${incomingConfig.sync}' for sync, reverting to ${this.config.sync}`);
            sanitizedConfig.sync = this.config.sync;
        }
        return sanitizedConfig;
    }
    /**
     * Conversation listening for text events.
     *
     * @event Conversation#text
     *
     * @property {Member} sender - The sender of the text
     * @property {TextEvent} text - The text message received
     * @example <caption>listen for text events</caption>
     *  conversation.on("text",(sender, message) => {
     *    console.log(sender, message);
     *    // Identify your own message.
     *    if (message.from === conversation.me.id){
     *        renderMyMessages(message)
     *    } else {
     *        renderOtherMessages(message)
     *    }
     *  });
     */
    /**
     *
     * Conversation listening for image events.
     *
     * @event Conversation#image
     *
     * @property {Member} sender - The sender of the image
     * @property {ImageEvent} image - The image message received
     * @example <caption>listen for image events</caption>
     *  conversation.on("image", (sender, image) => {
     *    console.log(sender,image);
     *    // Identify if your own imageEvent or someone else's.
     *    if (image.from !== conversation.me.id){
     *        displayImages(image);
     *    }
     *  });
     */
    /**
     * Conversation listening for deleted events.
     *
     * @event Conversation#event:delete
     *
     * @property {Member} member - the Member who deleted an event
     * @property {NXMEvent} event - deleted event: event.id
     * @example <caption>get details about the deleted event</caption>
     * conversation.on("event:delete", (member, event) => {
     *  console.log(event.id);
     *  console.log(event.body.timestamp.deleted);
     * });
     */
    /**
     * Conversation listening for ephemeral events.
     * @param {object} body customizable key value pairs
     *
     * @returns {Promise<NXMEvent>} - the ephemeral event that was sent
     *
     * @example <caption> get ephemeral event </caption>
     * conversation.on("ephemeral", (member, event) => {
     *  console.log(event);
     * });
     *
     */
    /**
     * Conversation listening for new Members.
     *
     * @event Conversation#member:joined
     *
     * @property {Member} member - the Member that joined
     * @property {NXMEvent} event - the join event
     * @example <caption>get the name of the new Member</caption>
     * conversation.on("member:joined", (member, event) => {
     *  console.log(event.id)
     *  console.log(member.userName+ " joined the conversation");
     * });
     */
    /**
     * Conversation listening for Members being invited.
     *
     * @event Conversation#member:invited
     *
     * @property {Member} member - the Member that is invited
     * @property {NXMEvent} event - data regarding the receiver of the invitation
     * @example <caption>get the name of the invited Member</caption>
     * conversation.on("member:invited", (member, event) => {
     *  console.log(member.userName + " invited to the conversation");
     * });
     */
    /**
     * Conversation listening for Members callStatus changes.
     *
     * @event Conversation#member:call:status
     *
     * @property {Member} member - the Member that has left
     * @example <caption>get the callStatus of the member that changed call status</caption>
     * conversation.on("member:call:status", (member) => {
     *  console.log(member.callStatus);
     * });
     */
    /**
     * Conversation listening for Members leaving (kicked or left).
     *
     * @event Conversation#member:left
     *
     * @property {Member} member - the Member that has left
     * @property {NXMEvent} event - data regarding the receiver of the invitation
     * @example <caption>get the username of the Member that left</caption>
     * conversation.on("member:left", (member , event) => {
     *  console.log(member.userName + " left");
     *  console.log(event.body.reason);
     * });
     */
    /**
     * Conversation listening for Members typing.
     *
     * @event Conversation#text:typing:on
     *
     * @property {Member} member - the member that started typing
     * @property {NXMEvent} event - the start typing event
     * @example <caption>get the display name of the Member that is typing</caption>
     * conversation.on("text:typing:on", (member, event) => {
     *  console.log(member.displayName + " is typing...");
     * });
     */
    /**
     * Conversation listening for Members stopped typing.
     *
     * @event Conversation#text:typing:off
     *
     * @property {Member} member - the member that stopped typing
     * @property {NXMEvent} event - the stop typing event
     * @example <caption>get the display name of the Member that stopped typing</caption>
     * conversation.on("text:typing:off", (member, event) => {
     *  console.log(member.displayName + " stopped typing...");
     * });
     */
    /**
     * Conversation listening for Members' seen texts.
     *
     * @event Conversation#text:seen
     *
     * @property {Member} member - the Member that saw the text
     * @property {TextEvent} text - the text that was seen
     * @example <caption>listen for seen text events</caption>
     * conversation.on("text:seen", (member, text) => {
     *  console.log(text);
     *  if (conversation.me.id !== member.memberId) {
     *    console.log(member);
     *  }
     * });
     */
    /**
     * Conversation listening for Members' seen images.
     * @event Conversation#image:seen
     *
     * @property {Member} member - the member that saw the image
     * @property {ImageEvent} image - the image that was seen
     * @example <caption>listen for seen image events</caption>
     * conversation.on("image:seen", (member, image) => {
     *  console.log(image);
     *  if (conversation.me.id !== member.memberId) {
     *    console.log(member);
     *  };
     * });
     */
    /**
     * Conversation listening for Members submitted messages.
     * @event Conversation#message:submitted
     *
     * @property {Member} member - the member that message was submitted to
     * @property {MessageEvent} message - the message that was submitted
     * @example <caption>listen for submitted message events</caption>
     * conversation.on("message:submitted", (member, message) => {
     *  console.log(message);
     *  if (conversation.me.id !== member.memberId) {
     *    console.log(member);
     *  };
     * });
     */
    /**
     * Conversation listening for Members rejected messages.
     * @event Conversation#message:rejected
     *
     * @property {Member} member - the member that message was rejected by
     * @property {MessageEvent} message - the message that was rejected
     * @example <caption>listen for rejected message events</caption>
     * conversation.on("message:rejected", (member, message) => {
     *  console.log(message);
     *  if (conversation.me.id !== member.memberId) {
     *    console.log(member);
     *  };
     * });
     */
    /**
     * Conversation listening for Members undeliverable messages.
     * @event Conversation#message:undeliverable
     *
     * @property {Member} member - the member that message was undeliverable to
     * @property {MessageEvent} message - the message that was undeliverable
     * @example <caption>listen for undeliverable message events</caption>
     * conversation.on("message:undeliverable", (member, message) => {
     *  console.log(message);
     *  if (conversation.me.id !== member.memberId) {
     *    console.log(member);
     *  };
     * });
     */
    /**
     * Conversation listening for Members delivered messages.
     * @event Conversation#message:delivered
     *
     * @property {Member} member - the member that message was delivered to
     * @property {MessageEvent} message - the message that was delivered
     * @example <caption>listen for delivered message events</caption>
     * conversation.on("message:delivered", (member, message) => {
     *  console.log(message);
     *  if (conversation.me.id !== member.memberId) {
     *    console.log(member);
     *  };
     * });
     */
    /**
     * Conversation listening for Members seen messages.
     * @event Conversation#message:seen
     *
     * @property {Member} member - the member that message was seen by
     * @property {MessageEvent} message - the message that was seen
     * @example <caption>listen for seen message events</caption>
     * conversation.on("message:seen", (member, message) => {
     *  console.log(message);
     *  if (conversation.me.id !== member.memberId) {
     *    console.log(member);
     *  };
     * });
     */
    /**
     * Conversation listening for Members media changes (audio,text)
     *
     * Change in media presence state. They are in the Conversation with text or audio.
     *
     * @event Conversation#member:media
     *
     * @property {Member} member - the Member object linked to this event
     * @property {NXMEvent} event - information about media presence state
     * @property {boolean} event.body.audio  - is audio enabled
     * @example <caption>get every Member's media change events </caption>
     * conversation.on("member:media", (member, event) => {
     *  console.log(event.body.media); //{"audio": true, "audio_settings": {"enabled": true, "muted": false, "earmuffed": false}}
     * });
     */
    /**
     * Conversation listening for mute on events
     * A Member has muted their audio
     *
     * @event Conversation#audio:mute:on
     *
     * @property {Member} member - the Member object linked to this event
     * @property {NXMEvent} event - information about the mute event
     * @example <caption>listen for audio mute on events </caption>
     * conversation.on("audio:mute:on", (member, event) => {
     *  console.log("member that is muted ", member);
     *  console.log(event);
     * });
     */
    /**
     * Conversation listening for mute off events
     * A member has unmuted their audio
     *
     * @event Conversation#audio:mute:off
     *
     * @property {Member} member - the member object linked to this event
     * @property {NXMEvent} event - information about the mute event
     * @example <caption>listen for audio mute off events </caption>
     * conversation.on("audio:mute:off", (member, event) => {
     *  console.log("member that is unmuted ", member);
     *  console.log(event);
     * });
     */
    sendRequest(request, callback) {
        // Add a message ID to the request and set up a listener for the reply (or error)
        request.tid = utils_1.default.allocateUUID();
        const type = request.type;
        delete request.type;
        this.log.debug('-->', type, request);
        this.log.info('-->', type, request.tid);
        this.connection.emit(type, request);
        this.requests[request.tid] = {
            type: type,
            request,
            callback
        };
    }
    async sendNetworkRequest(params) {
        const version = params.version || 'beta';
        const url = `${this.config.nexmo_api_url}/${version}/${params.path}`;
        if (!(params.type === 'GET' || params.type === 'DELETE')) {
            if (params.data) {
                params.data.originating_session = this.session_id;
            }
            else {
                params.data = {
                    originating_session: this.session_id
                };
            }
        }
        try {
            const request = {
                type: params.type,
                url,
                data: (params.data) ? params.data : null,
                token: (params.data || {}).token ? params.data.token : this.config.token || null
            };
            this.log.debug('sendNetworkRequest: ', { request });
            const { response } = await utils_1.default.networkRequest(request);
            return response;
        }
        catch ({ response }) {
            throw response;
        }
    }
    /**
     * Create a new Session.
     * @param {string} token - the user JSON Web Token (JWT)
     * @returns  {Promise<Application>} - the application logged in to
     * @example <caption>Create a session and return the Application</caption>
     *  rtc.createSession(token).then((application) => {
     *    console.log(application);
     *  }).catch((error) => {
     *    console.log(error);
     *  });
    */
    createSession(token) {
        this.config.socket_io.query.token = token;
        this._createAndSetConnection();
        // return a promise for the application
        return new Promise((resolve, reject) => {
            this.log.info(`Client-SDK Version: ${this.config.SDK_version}`);
            this.config.token = null;
            this.requests['session:login'] = {
                type: 'session:login',
                callback: async (response) => {
                    if (response.type === 'session:success') {
                        this.session_id = response.body.id;
                        this.apiKey = response.body.api_key;
                        // Store token in config
                        this.config.token = token;
                        // adds the session id as a query parameter in order to
                        // connect to the same session in case of a reconnection
                        this.connection.io.opts.query = {
                            session_id: this.session_id,
                            token: this.config.token
                        };
                        if (!this.application || (this.application.me && this.application.me.id !== response.body.user_id)) {
                            this.application = new application_1.default(this, {});
                        }
                        if (!this.application.me) {
                            this.application.me = new user_1.default(this.application, {
                                id: response.body.user_id,
                                name: response.body.name
                            });
                        }
                        if (!this.errorsEmitter) {
                            this.errorsEmitter = new errors_emitter_1.default(this.application);
                        }
                        // Set Bugsnag user to application.me.id
                        if (this.config.log_reporter.enabled) {
                            global.NXMbugsnagClient.user = {
                                id: this.application.me.id,
                                name: this.application.me.name,
                                session_id: response.body.id
                            };
                        }
                        if (this.config.sync !== 'none') {
                            // Retrieve the existing conversation data for this user
                            try {
                                await this.application.getConversations();
                                resolve(this.application);
                            }
                            catch (error) {
                                reject(error);
                            }
                        }
                        else {
                            resolve(this.application);
                        }
                    }
                    else {
                        reject(new nexmoClientError_1.NexmoApiError(response));
                    }
                }
            };
        });
    }
    /**
     * Delete existing Session.
     * @returns  {Promise<CAPIResponse>} - response with rid and type
     * @example <caption>Delete existing session</caption>
     *  rtc.deleteSession().then((response) => {
     *    console.log(response);
     *  }).catch((error) => {
     *    console.log(error);
     *  });
    */
    deleteSession() {
        return new Promise(async (resolve, reject) => {
            const logoutRequest = () => {
                return this.sendRequest({
                    type: 'session:logout',
                    body: {}
                }, (response) => {
                    if (response.type === 'session:logged-out' || response.type === 'session:terminated') {
                        this.disconnect();
                        delete this.errorsEmitter;
                        delete this.application;
                        delete this.connection;
                        this.requests = {};
                        this.sessionReady = false;
                        resolve(response);
                    }
                    else {
                        reject(response);
                    }
                });
            };
            // prepare for deleteSession
            if (this.application) {
                let disablePromises = [];
                if (this.application.conversations.size) {
                    for (let conversation of this.application.conversations.values()) {
                        disablePromises.push(conversation.media.disable());
                    }
                }
                try {
                    await Promise.all(disablePromises);
                }
                catch (error) {
                    this.log.error("deleteSession: ", error);
                }
                return logoutRequest();
            }
            else {
                return logoutRequest();
            }
        });
    }
    updateSession(event) {
        if (event.type === 'session:success') {
            this.session_id = event.body.id;
            this.connection.io.opts.query.session_id = event.body.id;
            if (this.application && event.body.pending_events) {
                event.body.pending_events.forEach((pending_event) => this.application._enqueueEvent(pending_event));
            }
        }
    }
    /**
     * Disconnect from the cloud.
     *
    */
    disconnect() {
        return this.connection.disconnect();
    }
    /**
     * Connect to the cloud.
     *
    */
    connect() {
        return this.connection.connect();
    }
    /**
       * Get a connectivity report for all Vonage DCs and Media Servers.
     * @param {string} token - the JSON Web Token (JWT)
     * @param {object} options - configure the connectivityReport
     * @param {Function} options.dcListCallback - a callback function to edit the list of datacenters before connectivity checks
       * @returns  {Promise<Report>}
       * @example <caption>Get a connectivity report</caption>
     *
       *  rtc.connectivityReport(token, {
     *    dcListCallback: (dcList) => {...dcList, additionalDc}
     *  }).then((report) => {
     *    console.log(report);
     *  }).catch((error) => {
     *    console.log(error);
     *  });
    */
    async connectivityReport(token, options) {
        const ip = !rtc_helper_1.default.isNode() ? await publicip_1.default.v4() : undefined;
        const report = {
            machineInfo: { ip },
            connectivityReport: []
        };
        try {
            const { response } = await utils_1.default.networkRequest({
                type: 'GET',
                url: `${this.config.nexmo_api_url}/v0.3/discovery/api`,
                token
            });
            let dcList = response;
            if (options === null || options === void 0 ? void 0 : options.dcListCallback) {
                dcList = options.dcListCallback(dcList);
            }
            for (const dc in dcList) {
                const endpoint = dcList[dc].endpoint;
                const apiUrl = dcList[dc].https;
                const wsUrl = dcList[dc].ws;
                try {
                    const httpRes = await utils_1.default._checkHttpConnectivity(apiUrl);
                    const wsRes = await utils_1.default._checkWsConnectivity(wsUrl, this.config.path, this.config.socket_io);
                    const mediaConnectionReport = await utils_1.default._checkMediaServers(token, endpoint, dc);
                    const rep = {
                        name: dc,
                        signalConnectionReport: {
                            https: httpRes,
                            ws: wsRes,
                        },
                        mediaConnectionReport
                    };
                    report.connectivityReport.push(rep);
                }
                catch (error) {
                    this.log.error(`Error generating report for ${dc}`, error);
                }
            }
        }
        catch (error) {
            this.log.error(`Error fetching nexmo servers information`, error);
        }
        return report;
    }
    /**
      * Return a list with the connection health of the media servers for a specific datacenter.
      * @returns  {Promise<MediaConnectionReport[]>}
    * @param {string} token - the JSON Web Token (JWT)
    * @param {string} nexmo_api_url - url of the nexmo api to be called
    * @param {string} datacenter - datacenter of interest
      * @example <caption>Return a list with the connection health of the media servers</caption>
    *
      *  rtc.checkMediaServers('nexmo-api-url','dc').then((responseArray) => {
      *    console.log(responseArray);
      *  }).catch((error) => {
      *    console.log(error);
      *  });
     */
    async checkMediaServers(token, nexmo_api_url, datacenter) {
        return await utils_1.default._checkMediaServers(token, nexmo_api_url, datacenter);
    }
    /**
    * Return the connection health of a single media server including possible connectionTime in ms.
    * @returns  {Promise<MediaConnectionReport>}
    * @param {string} ip - ip address of the Media Server
    * @param {string} port - port number of the Media Server
    * @example <caption>Return the connection health of a single media server</caption>
    *
    *  rtc.checkMediaConnectivity('ip-address','1').then((response) => {
    *    console.log(`IP Address of media server: ${response.ip}`);
    *    console.log(`Able to connect: ${response.canConnect}`);
    *    console.log(`ConnectionTime in ms: ${resonse.connectionTime}`);
    *  }).catch((error) => {
    *    console.log(error);
    *  });
   */
    async checkMediaConnectivity(ip, port) {
        return await utils_1.default._checkMediaConnectivity(ip, port);
    }
}
exports.default = NexmoClient;
/**
 * Enum for NexmoClient disconnection reason.
 * @readonly
 * @enum {string}
 * @alias NexmoClient.DISCONNECT_REASON
*/
NexmoClient.DISCONNECT_REASON = {
    ClientDisconnected: 'ClientDisconnected',
    TokenExpired: 'TokenExpired',
    ConnectionError: 'ConnectionError'
};
module.exports = NexmoClient;