text: import { fromByteArray } from 'base64-js';
  import _extends from '@babel/runtime/helpers/extends';
  import _typeof from '@babel/runtime/helpers/typeof';
  import _objectWithoutProperties from '@babel/runtime/helpers/objectWithoutProperties';
  import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray';
  import _regeneratorRuntime from '@babel/runtime/regenerator';
  import _asyncToGenerator from '@babel/runtime/helpers/asyncToGenerator';
  import _classCallCheck from '@babel/runtime/helpers/classCallCheck';
  import _createClass from '@babel/runtime/helpers/createClass';
  import _defineProperty from '@babel/runtime/helpers/defineProperty';
  import axios from 'axios';
  import _slicedToArray from '@babel/runtime/helpers/slicedToArray';
  import FormData from 'form-data';
  import WebSocket from 'isomorphic-ws';
  
  function isString(arrayOrString) {
    return typeof arrayOrString === 'string';
  }
  
  function isMapStringCallback(arrayOrString, callback) {
    return callback && isString(arrayOrString);
  } // source - https://github.com/beatgammit/base64-js/blob/master/test/convert.js#L72
  
  
  function map(arrayOrString, callback) {
    var res = [];
  
    if (isString(arrayOrString) && isMapStringCallback(arrayOrString, callback)) {
      for (var k = 0, len = arrayOrString.length; k < len; k++) {
        if (arrayOrString.charAt(k)) {
          var kValue = arrayOrString.charAt(k);
          var mappedValue = callback(kValue, k, arrayOrString);
          res[k] = mappedValue;
        }
      }
    } else if (!isString(arrayOrString) && !isMapStringCallback(arrayOrString, callback)) {
      for (var _k = 0, _len = arrayOrString.length; _k < _len; _k++) {
        if (_k in arrayOrString) {
          var _kValue = arrayOrString[_k];
  
          var _mappedValue = callback(_kValue, _k, arrayOrString);
  
          res[_k] = _mappedValue;
        }
      }
    }
  
    return res;
  }
  
  var encodeBase64 = function encodeBase64(data) {
    return fromByteArray(new Uint8Array(map(data, function (char) {
      return char.charCodeAt(0);
    })));
  }; // base-64 decoder throws exception if encoded string is not padded by '=' to make string length
  // in multiples of 4. So gonna use our own method for this purpose to keep backwards compatibility
  // https://github.com/beatgammit/base64-js/blob/master/index.js#L26
  
  var decodeBase64 = function decodeBase64(s) {
    var e = {},
        w = String.fromCharCode,
        L = s.length;
    var i,
        b = 0,
        c,
        x,
        l = 0,
        a,
        r = '';
    var A = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
  
    for (i = 0; i < 64; i++) {
      e[A.charAt(i)] = i;
    }
  
    for (x = 0; x < L; x++) {
      c = e[s.charAt(x)];
      b = (b << 6) + c;
      l += 6;
  
      while (l >= 8) {
        ((a = b >>> (l -= 8) & 0xff) || x < L - 2) && (r += w(a));
      }
    }
  
    return r;
  };
  
  var https = null;
  
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
  
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
  
  /**
   * ChannelState - A container class for the channel state.
   */
  var ChannelState = /*#__PURE__*/function () {
    /**
     * Flag which indicates if channel state contain latest/recent messages or no.
     * This flag should be managed by UI sdks using a setter - setIsUpToDate.
     * When false, any new message (received by websocket event - message.new) will not
     * be pushed on to message list.
     */
    function ChannelState(channel) {
      var _this = this,
          _channel$state;
  
      _classCallCheck(this, ChannelState);
  
      _defineProperty(this, "_channel", void 0);
  
      _defineProperty(this, "watcher_count", void 0);
  
      _defineProperty(this, "typing", void 0);
  
      _defineProperty(this, "read", void 0);
  
      _defineProperty(this, "messages", void 0);
  
      _defineProperty(this, "pinnedMessages", void 0);
  
      _defineProperty(this, "threads", void 0);
  
      _defineProperty(this, "mutedUsers", void 0);
  
      _defineProperty(this, "watchers", void 0);
  
      _defineProperty(this, "members", void 0);
  
      _defineProperty(this, "unreadCount", void 0);
  
      _defineProperty(this, "membership", void 0);
  
      _defineProperty(this, "last_message_at", void 0);
  
      _defineProperty(this, "isUpToDate", void 0);
  
      _defineProperty(this, "setIsUpToDate", function (isUpToDate) {
        _this.isUpToDate = isUpToDate;
      });
  
      _defineProperty(this, "removeMessageFromArray", function (msgArray, msg) {
        var result = msgArray.filter(function (message) {
          return !(!!message.id && !!msg.id && message.id === msg.id);
        });
        return {
          removed: result.length < msgArray.length,
          result: result
        };
      });
  
      this._channel = channel;
      this.watcher_count = 0;
      this.typing = {};
      this.read = {};
      this.messages = [];
      this.pinnedMessages = [];
      this.threads = {}; // a list of users to hide messages from
  
      this.mutedUsers = [];
      this.watchers = {};
      this.members = {};
      this.membership = {};
      this.unreadCount = 0;
      /**
       * Flag which indicates if channel state contain latest/recent messages or no.
       * This flag should be managed by UI sdks using a setter - setIsUpToDate.
       * When false, any new message (received by websocket event - message.new) will not
       * be pushed on to message list.
       */
  
      this.isUpToDate = true;
      this.last_message_at = (channel === null || channel === void 0 ? void 0 : (_channel$state = channel.state) === null || _channel$state === void 0 ? void 0 : _channel$state.last_message_at) != null ? new Date(channel.state.last_message_at) : null;
    }
    /**
     * addMessageSorted - Add a message to the state
     *
     * @param {MessageResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>} newMessage A new message
     * @param {boolean} timestampChanged Whether updating a message with changed created_at value.
     *
     */
  
  
    _createClass(ChannelState, [{
      key: "addMessageSorted",
      value: function addMessageSorted(newMessage) {
        var timestampChanged = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
        return this.addMessagesSorted([newMessage], timestampChanged);
      }
      /**
       * formatMessage - Takes the message object. Parses the dates, sets __html
       * and sets the status to received if missing. Returns a message object
       *
       * @param {MessageResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>} message a message object
       *
       */
  
    }, {
      key: "formatMessage",
      value: function formatMessage(message) {
        return _objectSpread(_objectSpread({}, message), {}, {
          __html: message.html,
          // parse the date..
          pinned_at: message.pinned_at ? new Date(message.pinned_at) : null,
          created_at: message.created_at ? new Date(message.created_at) : new Date(),
          updated_at: message.updated_at ? new Date(message.updated_at) : new Date(),
          status: message.status || 'received'
        });
      }
      /**
       * addMessagesSorted - Add the list of messages to state and resorts the messages
       *
       * @param {Array<MessageResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>>} newMessages A list of messages
       * @param {boolean} timestampChanged Whether updating messages with changed created_at value.
       * @param {boolean} initializing Whether channel is being initialized.
       *
       */
  
    }, {
      key: "addMessagesSorted",
      value: function addMessagesSorted(newMessages) {
        var timestampChanged = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
        var initializing = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
  
        for (var i = 0; i < newMessages.length; i += 1) {
          var message = this.formatMessage(newMessages[i]);
  
          if (initializing && message.id && this.threads[message.id]) {
            // If we are initializing the state of channel (e.g., in case of connection recovery),
            // then in that case we remove thread related to this message from threads object.
            // This way we can ensure that we don't have any stale data in thread object
            // and consumer can refetch the replies.
            delete this.threads[message.id];
          }
  
          if (!this.last_message_at) {
            this.last_message_at = new Date(message.created_at.getTime());
          }
  
          if (message.created_at.getTime() > this.last_message_at.getTime()) {
            this.last_message_at = new Date(message.created_at.getTime());
          } // update or append the messages...
  
  
          var parentID = message.parent_id; // add to the main message list
  
          if (!parentID || message.show_in_channel) {
            this.messages = this._addToMessageList(this.messages, message, timestampChanged);
          } // add to the thread if applicable..
  
  
          if (parentID) {
            var thread = this.threads[parentID] || [];
  
            var threadMessages = this._addToMessageList(thread, message, timestampChanged);
  
            this.threads[parentID] = threadMessages;
          }
        }
      }
      /**
       * addPinnedMessages - adds messages in pinnedMessages property
       *
       * @param {Array<MessageResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>>} pinnedMessages A list of pinned messages
       *
       */
  
    }, {
      key: "addPinnedMessages",
      value: function addPinnedMessages(pinnedMessages) {
        for (var i = 0; i < pinnedMessages.length; i += 1) {
          this.addPinnedMessage(pinnedMessages[i]);
        }
      }
      /**
       * addPinnedMessage - adds message in pinnedMessages
       *
       * @param {MessageResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>} pinnedMessage message to update
       *
       */
  
    }, {
      key: "addPinnedMessage",
      value: function addPinnedMessage(pinnedMessage) {
        this.pinnedMessages = this._addToMessageList(this.pinnedMessages, this.formatMessage(pinnedMessage), false, 'pinned_at');
      }
      /**
       * removePinnedMessage - removes pinned message from pinnedMessages
       *
       * @param {MessageResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>} message message to remove
       *
       */
  
    }, {
      key: "removePinnedMessage",
      value: function removePinnedMessage(message) {
        var _this$removeMessageFr = this.removeMessageFromArray(this.pinnedMessages, message),
            result = _this$removeMessageFr.result;
  
        this.pinnedMessages = result;
      }
    }, {
      key: "addReaction",
      value: function addReaction(reaction, message, enforce_unique) {
        var _this2 = this;
  
        if (!message) return;
        var messageWithReaction = message;
  
        this._updateMessage(message, function (msg) {
          var newMsg = _this2._addReactionToMessage(msg, reaction, enforce_unique);
  
          messageWithReaction = _objectSpread(_objectSpread({}, messageWithReaction), {}, {
            own_reactions: newMsg.own_reactions
          });
          return newMsg;
        });
  
        return messageWithReaction;
      }
    }, {
      key: "_addReactionToMessage",
      value: function _addReactionToMessage(message, reaction, enforce_unique) {
        var newMessage = message;
  
        if (enforce_unique) {
          newMessage.own_reactions = [];
        } else {
          this._removeOwnReactionFromMessage(message, reaction);
        }
  
        newMessage.own_reactions = newMessage.own_reactions || [];
  
        if (this._channel.getClient().userID === reaction.user_id) {
          newMessage.own_reactions.push(reaction);
        }
  
        return newMessage;
      }
    }, {
      key: "_removeOwnReactionFromMessage",
      value: function _removeOwnReactionFromMessage(message, reaction) {
        if (message.own_reactions) {
          message.own_reactions = message.own_reactions.filter(function (item) {
            return item.user_id !== reaction.user_id || item.type !== reaction.type;
          });
        }
      } // eslint-disable-next-line sonarjs/cognitive-complexity
  
    }, {
      key: "removeReaction",
      value: function removeReaction(reaction, message) {
        var _this3 = this;
  
        if (!message) return;
        var messageWithReaction = message;
  
        this._updateMessage(message, function (msg) {
          _this3._removeOwnReactionFromMessage(msg, reaction);
  
          messageWithReaction = _objectSpread(_objectSpread({}, messageWithReaction), {}, {
            own_reactions: msg.own_reactions
          });
          return msg;
        });
  
        return messageWithReaction;
      }
      /**
       * Updates all instances of given message in channel state
       * @param message
       * @param updateFunc
       */
  
    }, {
      key: "_updateMessage",
      value: function _updateMessage(message, updateFunc) {
        var parent_id = message.parent_id,
            show_in_channel = message.show_in_channel;
  
        if (parent_id && this.threads[parent_id]) {
          var thread = this.threads[parent_id];
          var msgIndex = thread.findIndex(function (msg) {
            return msg.id === message.id;
          });
  
          if (msgIndex !== -1) {
            thread[msgIndex] = updateFunc(thread[msgIndex]);
            this.threads[parent_id] = thread;
          }
        }
  
        if (!show_in_channel && !parent_id || show_in_channel) {
          var _msgIndex = this.messages.findIndex(function (msg) {
            return msg.id === message.id;
          });
  
          if (_msgIndex !== -1) {
            this.messages[_msgIndex] = updateFunc(this.messages[_msgIndex]);
          }
        }
      }
      /**
       * Setter for isUpToDate.
       *
       * @param isUpToDate  Flag which indicates if channel state contain latest/recent messages or no.
       *                    This flag should be managed by UI sdks using a setter - setIsUpToDate.
       *                    When false, any new message (received by websocket event - message.new) will not
       *                    be pushed on to message list.
       */
  
    }, {
      key: "_addToMessageList",
      value:
      /**
       * _addToMessageList - Adds a message to a list of messages, tries to update first, appends if message isn't found
       *
       * @param {Array<ReturnType<ChannelState<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType>['formatMessage']>>} messages A list of messages
       * @param message
       * @param {boolean} timestampChanged Whether updating a message with changed created_at value.
       * @param {string} sortBy field name to use to sort the messages by
       */
      function _addToMessageList(messages, message) {
        var timestampChanged = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
        var sortBy = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'created_at';
        var messageArr = messages; // if created_at has changed, message should be filtered and re-inserted in correct order
        // slow op but usually this only happens for a message inserted to state before actual response with correct timestamp
  
        if (timestampChanged) {
          messageArr = messageArr.filter(function (msg) {
            return !(msg.id && message.id === msg.id);
          });
        } // for empty list just concat and return
  
  
        if (messageArr.length === 0) return messageArr.concat(message);
        var messageTime = message[sortBy].getTime(); // if message is newer than last item in the list concat and return
  
        if (messageArr[messageArr.length - 1][sortBy].getTime() < messageTime) return messageArr.concat(message); // find the closest index to push the new message
  
        var left = 0;
        var middle = 0;
        var right = messageArr.length - 1;
  
        while (left <= right) {
          middle = Math.floor((right + left) / 2);
          if (messageArr[middle][sortBy].getTime() <= messageTime) left = middle + 1;else right = middle - 1;
        } // message already exists and not filtered due to timestampChanged, update and return
  
  
        if (!timestampChanged && message.id) {
          if (messageArr[left] && message.id === messageArr[left].id) {
            messageArr[left] = message;
            return messageArr;
          }
  
          if (messageArr[left - 1] && message.id === messageArr[left - 1].id) {
            messageArr[left - 1] = message;
            return messageArr;
          }
        }
  
        messageArr.splice(left, 0, message);
        return messageArr;
      }
      /**
       * removeMessage - Description
       *
       * @param {{ id: string; parent_id?: string }} messageToRemove Object of the message to remove. Needs to have at id specified.
       *
       * @return {boolean} Returns if the message was removed
       */
  
    }, {
      key: "removeMessage",
      value: function removeMessage(messageToRemove) {
        var isRemoved = false;
  
        if (messageToRemove.parent_id && this.threads[messageToRemove.parent_id]) {
          var _this$removeMessageFr2 = this.removeMessageFromArray(this.threads[messageToRemove.parent_id], messageToRemove),
              removed = _this$removeMessageFr2.removed,
              threadMessages = _this$removeMessageFr2.result;
  
          this.threads[messageToRemove.parent_id] = threadMessages;
          isRemoved = removed;
        } else {
          var _this$removeMessageFr3 = this.removeMessageFromArray(this.messages, messageToRemove),
              _removed = _this$removeMessageFr3.removed,
              messages = _this$removeMessageFr3.result;
  
          this.messages = messages;
          isRemoved = _removed;
        }
  
        return isRemoved;
      }
    }, {
      key: "filterErrorMessages",
      value:
      /**
       * filterErrorMessages - Removes error messages from the channel state.
       *
       */
      function filterErrorMessages() {
        var filteredMessages = this.messages.filter(function (message) {
          return message.type !== 'error';
        });
        this.messages = filteredMessages;
      }
      /**
       * clean - Remove stale data such as users that stayed in typing state for more than 5 seconds
       */
  
    }, {
      key: "clean",
      value: function clean() {
        var now = new Date(); // prevent old users from showing up as typing
  
        for (var _i = 0, _Object$entries = Object.entries(this.typing); _i < _Object$entries.length; _i++) {
          var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
              userID = _Object$entries$_i[0],
              lastEvent = _Object$entries$_i[1];
  
          var since = typeof lastEvent.received_at === 'string' && now.getTime() - new Date(lastEvent.received_at).getTime();
  
          if (since > 7000) {
            delete this.typing[userID];
  
            this._channel.getClient().dispatchEvent({
              cid: this._channel.cid,
              type: 'typing.stop',
              user: {
                id: userID
              }
            });
          }
        }
      }
    }, {
      key: "clearMessages",
      value: function clearMessages() {
        this.messages = [];
      }
    }]);
  
    return ChannelState;
  }();
  
  function ownKeys$1(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
  
  function _objectSpread$1(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys$1(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys$1(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
  
  var EVENT_MAP = {
    'channel.created': true,
    'channel.deleted': true,
    'channel.hidden': true,
    'channel.muted': true,
    'channel.truncated': true,
    'channel.unmuted': true,
    'channel.updated': true,
    'channel.visible': true,
    'health.check': true,
    'member.added': true,
    'member.removed': true,
    'member.updated': true,
    'message.deleted': true,
    'message.new': true,
    'message.read': true,
    'message.updated': true,
    'notification.added_to_channel': true,
    'notification.channel_deleted': true,
    'notification.channel_mutes_updated': true,
    'notification.channel_truncated': true,
    'notification.invite_accepted': true,
    'notification.invite_rejected': true,
    'notification.invited': true,
    'notification.mark_read': true,
    'notification.message_new': true,
    'notification.mutes_updated': true,
    'notification.removed_from_channel': true,
    'reaction.deleted': true,
    'reaction.new': true,
    'reaction.updated': true,
    'typing.start': true,
    'typing.stop': true,
    'user.banned': true,
    'user.deleted': true,
    'user.presence.changed': true,
    'user.unbanned': true,
    'user.updated': true,
    'user.watching.start': true,
    'user.watching.stop': true,
    // local events
    'connection.changed': true,
    'connection.recovered': true
  };
  
  var IS_VALID_EVENT_MAP_TYPE = _objectSpread$1(_objectSpread$1({}, EVENT_MAP), {}, {
    all: true
  });
  
  var isValidEventType = function isValidEventType(eventType) {
    return IS_VALID_EVENT_MAP_TYPE[eventType] || false;
  };
  
  function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
  
  function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
  
  function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
  
  /**
   * logChatPromiseExecution - utility function for logging the execution of a promise..
   *  use this when you want to run the promise and handle errors by logging a warning
   *
   * @param {Promise<T>} promise The promise you want to run and log
   * @param {string} name    A descriptive name of what the promise does for log output
   *
   */
  function logChatPromiseExecution(promise, name) {
    promise.then().catch(function (error) {
      console.warn("failed to do ".concat(name, ", ran into error: "), error);
    });
  }
  var sleep = function sleep(m) {
    return new Promise(function (r) {
      return setTimeout(r, m);
    });
  };
  function isFunction(value) {
    return value && (Object.prototype.toString.call(value) === '[object Function]' || 'function' === typeof value || value instanceof Function);
  }
  var chatCodes = {
    TOKEN_EXPIRED: 40,
    WS_CLOSED_SUCCESS: 1000
  };
  
  function isReadableStream(obj) {
    return obj !== null && _typeof(obj) === 'object' && (obj.readable || typeof obj._read === 'function');
  }
  
  function isBuffer(obj) {
    return obj != null && obj.constructor != null && // @ts-expect-error
    typeof obj.constructor.isBuffer === 'function' && // @ts-expect-error
    obj.constructor.isBuffer(obj);
  }
  
  function isFileWebAPI(uri) {
    return typeof window !== 'undefined' && 'File' in window && uri instanceof File;
  }
  
  function isOwnUser(user) {
    return (user === null || user === void 0 ? void 0 : user.total_unread_count) !== undefined;
  }
  function addFileToFormData(uri, name, contentType) {
    var data = new FormData();
  
    if (isReadableStream(uri) || isBuffer(uri) || isFileWebAPI(uri)) {
      if (name) data.append('file', uri, name);else data.append('file', uri);
    } else {
      data.append('file', {
        uri: uri,
        name: name || uri.split('/').reverse()[0],
        contentType: contentType || undefined,
        type: contentType || undefined
      });
    }
  
    return data;
  }
  function normalizeQuerySort(sort) {
    var sortFields = [];
    var sortArr = Array.isArray(sort) ? sort : [sort];
  
    var _iterator = _createForOfIteratorHelper(sortArr),
        _step;
  
    try {
      for (_iterator.s(); !(_step = _iterator.n()).done;) {
        var item = _step.value;
        var entries = Object.entries(item);
  
        if (entries.length > 1) {
          console.warn("client._buildSort() - multiple fields in a single sort object detected. Object's field order is not guaranteed");
        }
  
        for (var _i = 0, _entries = entries; _i < _entries.length; _i++) {
          var _entries$_i = _slicedToArray(_entries[_i], 2),
              field = _entries$_i[0],
              direction = _entries$_i[1];
  
          sortFields.push({
            field: field,
            direction: direction
          });
        }
      }
    } catch (err) {
      _iterator.e(err);
    } finally {
      _iterator.f();
    }
  
    return sortFields;
  }
  /** adopted from https://github.com/ai/nanoid/blob/master/non-secure/index.js */
  
  var alphabet = 'ModuleSymbhasOwnPr0123456789ABCDEFGHNRVfgctiUvzKqYTJkLxpZXIjQW';
  function randomId() {
    var id = '';
  
    for (var i = 0; i < 21; i++) {
      id += alphabet[Math.random() * 64 | 0];
    }
  
    return id;
  }
  
  function _createForOfIteratorHelper$1(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray$1(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
  
  function _unsupportedIterableToArray$1(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray$1(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$1(o, minLen); }
  
  function _arrayLikeToArray$1(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
  
  function ownKeys$2(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
  
  function _objectSpread$2(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys$2(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys$2(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
  
  /**
   * Channel - The Channel class manages it's own state.
   */
  var Channel = /*#__PURE__*/function () {
    /**
     * constructor - Create a channel
     *
     * @param {StreamChat<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType>} client the chat client
     * @param {string} type  the type of channel
     * @param {string} [id]  the id of the chat
     * @param {ChannelData<ChannelType>} data any additional custom params
     *
     * @return {Channel<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType>} Returns a new uninitialized channel
     */
    function Channel(client, type, id, data) {
      var _this = this;
  
      _classCallCheck(this, Channel);
  
      _defineProperty(this, "_client", void 0);
  
      _defineProperty(this, "type", void 0);
  
      _defineProperty(this, "id", void 0);
  
      _defineProperty(this, "data", void 0);
  
      _defineProperty(this, "_data", void 0);
  
      _defineProperty(this, "cid", void 0);
  
      _defineProperty(this, "listeners", void 0);
  
      _defineProperty(this, "state", void 0);
  
      _defineProperty(this, "initialized", void 0);
  
      _defineProperty(this, "lastKeyStroke", void 0);
  
      _defineProperty(this, "lastTypingEvent", void 0);
  
      _defineProperty(this, "isTyping", void 0);
  
      _defineProperty(this, "disconnected", void 0);
  
      _defineProperty(this, "create", /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
        var options;
        return _regeneratorRuntime.wrap(function _callee$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                options = {
                  watch: false,
                  state: false,
                  presence: false
                };
                _context.next = 3;
                return _this.query(options);
  
              case 3:
                return _context.abrupt("return", _context.sent);
  
              case 4:
              case "end":
                return _context.stop();
            }
          }
        }, _callee);
      })));
  
      _defineProperty(this, "_callChannelListeners", function (event) {
        var channel = _this; // gather and call the listeners
  
        var listeners = [];
  
        if (channel.listeners.all) {
          listeners.push.apply(listeners, _toConsumableArray(channel.listeners.all));
        }
  
        if (channel.listeners[event.type]) {
          listeners.push.apply(listeners, _toConsumableArray(channel.listeners[event.type]));
        } // call the event and send it to the listeners
  
  
        for (var _i = 0, _listeners = listeners; _i < _listeners.length; _i++) {
          var listener = _listeners[_i];
  
          if (typeof listener !== 'string') {
            listener(event);
          }
        }
      });
  
      _defineProperty(this, "_channelURL", function () {
        if (!_this.id) {
          throw new Error('channel id is not defined');
        }
  
        return "".concat(_this.getClient().baseURL, "/channels/").concat(_this.type, "/").concat(_this.id);
      });
  
      var validTypeRe = /^[\w_-]+$/;
      var validIDRe = /^[\w!_-]+$/;
  
      if (!validTypeRe.test(type)) {
        throw new Error("Invalid chat type ".concat(type, ", letters, numbers and \"_-\" are allowed"));
      }
  
      if (typeof id === 'string' && !validIDRe.test(id)) {
        throw new Error("Invalid chat id ".concat(id, ", letters, numbers and \"!-_\" are allowed"));
      }
  
      this._client = client;
      this.type = type;
      this.id = id; // used by the frontend, gets updated:
  
      this.data = data; // this._data is used for the requests...
  
      this._data = _objectSpread$2({}, data);
      this.cid = "".concat(type, ":").concat(id);
      this.listeners = {}; // perhaps the state variable should be private
  
      this.state = new ChannelState(this);
      this.initialized = false;
      this.lastTypingEvent = null;
      this.isTyping = false;
      this.disconnected = false;
    }
    /**
     * getClient - Get the chat client for this channel. If client.disconnect() was called, this function will error
     *
     * @return {StreamChat<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType>}
     */
  
  
    _createClass(Channel, [{
      key: "getClient",
      value: function getClient() {
        if (this.disconnected === true) {
          throw Error("You can't use a channel after client.disconnect() was called");
        }
  
        return this._client;
      }
      /**
       * getConfig - Get the configs for this channel type
       *
       * @return {Record<string, unknown>}
       */
  
    }, {
      key: "getConfig",
      value: function getConfig() {
        var client = this.getClient();
        return client.configs[this.type];
      }
      /**
       * sendMessage - Send a message to this channel
       *
       * @param {Message<AttachmentType, MessageType, UserType>} message The Message object
       * @param {{ skip_push?: boolean }} [options] Option object, {skip_push: true} to skip sending push notifications
       *
       * @return {Promise<SendMessageAPIResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>>} The Server Response
       */
  
    }, {
      key: "sendMessage",
      value: function () {
        var _sendMessage = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(message, options) {
          var sendMessageResponse;
          return _regeneratorRuntime.wrap(function _callee2$(_context2) {
            while (1) {
              switch (_context2.prev = _context2.next) {
                case 0:
                  _context2.next = 2;
                  return this.getClient().post(this._channelURL() + '/message', _objectSpread$2({
                    message: message
                  }, options));
  
                case 2:
                  sendMessageResponse = _context2.sent;
                  // Reset unreadCount to 0.
                  this.state.unreadCount = 0;
                  return _context2.abrupt("return", sendMessageResponse);
  
                case 5:
                case "end":
                  return _context2.stop();
              }
            }
          }, _callee2, this);
        }));
  
        function sendMessage(_x, _x2) {
          return _sendMessage.apply(this, arguments);
        }
  
        return sendMessage;
      }()
    }, {
      key: "sendFile",
      value: function sendFile(uri, name, contentType, user) {
        return this.getClient().sendFile("".concat(this._channelURL(), "/file"), uri, name, contentType, user);
      }
    }, {
      key: "sendImage",
      value: function sendImage(uri, name, contentType, user) {
        return this.getClient().sendFile("".concat(this._channelURL(), "/image"), uri, name, contentType, user);
      }
    }, {
      key: "deleteFile",
      value: function deleteFile(url) {
        return this.getClient().delete("".concat(this._channelURL(), "/file"), {
          url: url
        });
      }
    }, {
      key: "deleteImage",
      value: function deleteImage(url) {
        return this.getClient().delete("".concat(this._channelURL(), "/image"), {
          url: url
        });
      }
      /**
       * sendEvent - Send an event on this channel
       *
       * @param {Event<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType>} event for example {type: 'message.read'}
       *
       * @return {Promise<EventAPIResponse<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType>>} The Server Response
       */
  
    }, {
      key: "sendEvent",
      value: function () {
        var _sendEvent = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3(event) {
          return _regeneratorRuntime.wrap(function _callee3$(_context3) {
            while (1) {
              switch (_context3.prev = _context3.next) {
                case 0:
                  this._checkInitialized();
  
                  _context3.next = 3;
                  return this.getClient().post(this._channelURL() + '/event', {
                    event: event
                  });
  
                case 3:
                  return _context3.abrupt("return", _context3.sent);
  
                case 4:
                case "end":
                  return _context3.stop();
              }
            }
          }, _callee3, this);
        }));
  
        function sendEvent(_x3) {
          return _sendEvent.apply(this, arguments);
        }
  
        return sendEvent;
      }()
      /**
       * search - Query messages
       *
       * @param {MessageFilters<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType> | string}  query search query or object MongoDB style filters
       * @param {{client_id?: string; connection_id?: string; limit?: number; offset?: number; query?: string; message_filter_conditions?: MessageFilters<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>}} options Option object, {user_id: 'tommaso'}
       *
       * @return {Promise<SearchAPIResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>>} search messages response
       */
  
    }, {
      key: "search",
      value: function () {
        var _search = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4(query) {
          var options,
              payload,
              _args4 = arguments;
          return _regeneratorRuntime.wrap(function _callee4$(_context4) {
            while (1) {
              switch (_context4.prev = _context4.next) {
                case 0:
                  options = _args4.length > 1 && _args4[1] !== undefined ? _args4[1] : {};
                  // Return a list of channels
                  payload = _objectSpread$2({
                    filter_conditions: {
                      cid: this.cid
                    }
                  }, options);
  
                  if (!(typeof query === 'string')) {
                    _context4.next = 6;
                    break;
                  }
  
                  payload.query = query;
                  _context4.next = 11;
                  break;
  
                case 6:
                  if (!(_typeof(query) === 'object')) {
                    _context4.next = 10;
                    break;
                  }
  
                  payload.message_filter_conditions = query;
                  _context4.next = 11;
                  break;
  
                case 10:
                  throw Error("Invalid type ".concat(_typeof(query), " for query parameter"));
  
                case 11:
                  _context4.next = 13;
                  return this.getClient().wsPromise;
  
                case 13:
                  _context4.next = 15;
                  return this.getClient().get(this.getClient().baseURL + '/search', {
                    payload: payload
                  });
  
                case 15:
                  return _context4.abrupt("return", _context4.sent);
  
                case 16:
                case "end":
                  return _context4.stop();
              }
            }
          }, _callee4, this);
        }));
  
        function search(_x4) {
          return _search.apply(this, arguments);
        }
  
        return search;
      }()
      /**
       * queryMembers - Query Members
       *
       * @param {UserFilters<UserType>}  filterConditions object MongoDB style filters
       * @param {UserSort<UserType>} [sort] Sort options, for instance [{created_at: -1}].
       * When using multiple fields, make sure you use array of objects to guarantee field order, for instance [{last_active: -1}, {created_at: 1}]
       * @param {{ limit?: number; offset?: number }} [options] Option object, {limit: 10, offset:10}
       *
       * @return {Promise<ChannelMemberAPIResponse<UserType>>} Query Members response
       */
  
    }, {
      key: "queryMembers",
      value: function () {
        var _queryMembers = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee5(filterConditions) {
          var _this$data;
  
          var sort,
              options,
              id,
              type,
              members,
              _args5 = arguments;
          return _regeneratorRuntime.wrap(function _callee5$(_context5) {
            while (1) {
              switch (_context5.prev = _context5.next) {
                case 0:
                  sort = _args5.length > 1 && _args5[1] !== undefined ? _args5[1] : [];
                  options = _args5.length > 2 && _args5[2] !== undefined ? _args5[2] : {};
                  type = this.type;
  
                  if (this.id) {
                    id = this.id;
                  } else if ((_this$data = this.data) !== null && _this$data !== void 0 && _this$data.members && Array.isArray(this.data.members)) {
                    members = this.data.members;
                  } // Return a list of members
  
  
                  _context5.next = 6;
                  return this.getClient().get(this.getClient().baseURL + '/members', {
                    payload: _objectSpread$2({
                      type: type,
                      id: id,
                      members: members,
                      sort: normalizeQuerySort(sort),
                      filter_conditions: filterConditions
                    }, options)
                  });
  
                case 6:
                  return _context5.abrupt("return", _context5.sent);
  
                case 7:
                case "end":
                  return _context5.stop();
              }
            }
          }, _callee5, this);
        }));
  
        function queryMembers(_x5) {
          return _queryMembers.apply(this, arguments);
        }
  
        return queryMembers;
      }()
      /**
       * sendReaction - Send a reaction about a message
       *
       * @param {string} messageID the message id
       * @param {Reaction<ReactionType, UserType>} reaction the reaction object for instance {type: 'love'}
       * @param {{ enforce_unique?: boolean, skip_push?: boolean }} [options] Option object, {enforce_unique: true, skip_push: true} to override any existing reaction or skip sending push notifications
       *
       * @return {Promise<ReactionAPIResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>>} The Server Response
       */
  
    }, {
      key: "sendReaction",
      value: function () {
        var _sendReaction = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee6(messageID, reaction, options) {
          return _regeneratorRuntime.wrap(function _callee6$(_context6) {
            while (1) {
              switch (_context6.prev = _context6.next) {
                case 0:
                  if (messageID) {
                    _context6.next = 2;
                    break;
                  }
  
                  throw Error("Message id is missing");
  
                case 2:
                  if (!(!reaction || Object.keys(reaction).length === 0)) {
                    _context6.next = 4;
                    break;
                  }
  
                  throw Error("Reaction object is missing");
  
                case 4:
                  _context6.next = 6;
                  return this.getClient().post(this.getClient().baseURL + "/messages/".concat(messageID, "/reaction"), _objectSpread$2({
                    reaction: reaction
                  }, options));
  
                case 6:
                  return _context6.abrupt("return", _context6.sent);
  
                case 7:
                case "end":
                  return _context6.stop();
              }
            }
          }, _callee6, this);
        }));
  
        function sendReaction(_x6, _x7, _x8) {
          return _sendReaction.apply(this, arguments);
        }
  
        return sendReaction;
      }()
      /**
       * deleteReaction - Delete a reaction by user and type
       *
       * @param {string} messageID the id of the message from which te remove the reaction
       * @param {string} reactionType the type of reaction that should be removed
       * @param {string} [user_id] the id of the user (used only for server side request) default null
       *
       * @return {Promise<ReactionAPIResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>>} The Server Response
       */
  
    }, {
      key: "deleteReaction",
      value: function deleteReaction(messageID, reactionType, user_id) {
        this._checkInitialized();
  
        if (!reactionType || !messageID) {
          throw Error('Deleting a reaction requires specifying both the message and reaction type');
        }
  
        var url = this.getClient().baseURL + "/messages/".concat(messageID, "/reaction/").concat(reactionType); //provided when server side request
  
        if (user_id) {
          return this.getClient().delete(url, {
            user_id: user_id
          });
        }
  
        return this.getClient().delete(url, {});
      }
      /**
       * update - Edit the channel's custom properties
       *
       * @param {ChannelData<ChannelType>} channelData The object to update the custom properties of this channel with
       * @param {Message<AttachmentType, MessageType, UserType>} [updateMessage] Optional message object for channel members notification
       * @param {{ skip_push?: boolean }} [options] Option object, {skip_push: true} to skip sending push notifications
       * @return {Promise<UpdateChannelAPIResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>>} The server response
       */
  
    }, {
      key: "update",
      value: function () {
        var _update = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee7() {
          var channelData,
              updateMessage,
              options,
              reserved,
              data,
              _args7 = arguments;
          return _regeneratorRuntime.wrap(function _callee7$(_context7) {
            while (1) {
              switch (_context7.prev = _context7.next) {
                case 0:
                  channelData = _args7.length > 0 && _args7[0] !== undefined ? _args7[0] : {};
                  updateMessage = _args7.length > 1 ? _args7[1] : undefined;
                  options = _args7.length > 2 ? _args7[2] : undefined;
                  // Strip out reserved names that will result in API errors.
                  reserved = ['config', 'cid', 'created_by', 'id', 'member_count', 'type', 'created_at', 'updated_at', 'last_message_at'];
                  reserved.forEach(function (key) {
                    delete channelData[key];
                  });
                  _context7.next = 7;
                  return this.getClient().post(this._channelURL(), _objectSpread$2({
                    message: updateMessage,
                    data: channelData
                  }, options));
  
                case 7:
                  data = _context7.sent;
                  this.data = data.channel;
                  return _context7.abrupt("return", data);
  
                case 10:
                case "end":
                  return _context7.stop();
              }
            }
          }, _callee7, this);
        }));
  
        function update() {
          return _update.apply(this, arguments);
        }
  
        return update;
      }()
      /**
       * updatePartial - partial update channel properties
       *
       * @param {PartialUpdateChannel<ChannelType>} partial update request
       *
       * @return {Promise<PartialUpdateChannelAPIResponse<ChannelType,CommandType, UserType>>}
       */
  
    }, {
      key: "updatePartial",
      value: function () {
        var _updatePartial = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee8(update) {
          return _regeneratorRuntime.wrap(function _callee8$(_context8) {
            while (1) {
              switch (_context8.prev = _context8.next) {
                case 0:
                  _context8.next = 2;
                  return this.getClient().patch(this._channelURL(), update);
  
                case 2:
                  return _context8.abrupt("return", _context8.sent);
  
                case 3:
                case "end":
                  return _context8.stop();
              }
            }
          }, _callee8, this);
        }));
  
        function updatePartial(_x9) {
          return _updatePartial.apply(this, arguments);
        }
  
        return updatePartial;
      }()
      /**
       * enableSlowMode - enable slow mode
       *
       * @param {number} coolDownInterval the cooldown interval in seconds
       * @return {Promise<UpdateChannelAPIResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>>} The server response
       */
  
    }, {
      key: "enableSlowMode",
      value: function () {
        var _enableSlowMode = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee9(coolDownInterval) {
          var data;
          return _regeneratorRuntime.wrap(function _callee9$(_context9) {
            while (1) {
              switch (_context9.prev = _context9.next) {
                case 0:
                  _context9.next = 2;
                  return this.getClient().post(this._channelURL(), {
                    cooldown: coolDownInterval
                  });
  
                case 2:
                  data = _context9.sent;
                  this.data = data.channel;
                  return _context9.abrupt("return", data);
  
                case 5:
                case "end":
                  return _context9.stop();
              }
            }
          }, _callee9, this);
        }));
  
        function enableSlowMode(_x10) {
          return _enableSlowMode.apply(this, arguments);
        }
  
        return enableSlowMode;
      }()
      /**
       * disableSlowMode - disable slow mode
       *
       * @return {Promise<UpdateChannelAPIResponse<ChannelType, AttachmentType, MessageType, ReactionType, UserType>>} The server response
       */
  
    }, {
      key: "disableSlowMode",
      value: function () {
        var _disableSlowMode = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee10() {
          var data;
          return _regeneratorRuntime.wrap(function _callee10$(_context10) {
            while (1) {
              switch (_context10.prev = _context10.next) {
                case 0:
                  _context10.next = 2;
                  return this.getClient().post(this._channelURL(), {
                    cooldown: 0
                  });
  
                case 2:
                  data = _context10.sent;
                  this.data = data.channel;
                  return _context10.abrupt("return", data);
  
                case 5:
                case "end":
                  return _context10.stop();
              }
            }
          }, _callee10, this);
        }));
  
        function disableSlowMode() {
          return _disableSlowMode.apply(this, arguments);
        }
  
        return disableSlowMode;
      }()
      /**
       * delete - Delete the channel. Messages are permanently removed.
       *
       * @return {Promise<DeleteChannelAPIResponse<ChannelType, CommandType, UserType>>} The server response
       */
  
    }, {
      key: "delete",
      value: function () {
        var _delete2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee11() {
          return _regeneratorRuntime.wrap(function _callee11$(_context11) {
            while (1) {
              switch (_context11.prev = _context11.next) {
                case 0:
                  _context11.next = 2;
                  return this.getClient().delete(this._channelURL(), {});
  
                case 2:
                  return _context11.abrupt("return", _context11.sent);
  
                case 3:
                case "end":
                  return _context11.stop();
              }
            }
          }, _callee11, this);
        }));
  
        function _delete() {
          return _delete2.apply(this, arguments);
        }
  
        return _delete;
      }()
      /**
       * truncate - Removes all messages from the channel
       *
       * @return {Promise<TruncateChannelAPIResponse<ChannelType, CommandType, UserType>>} The server response
       */
  
    }, {
      key: "truncate",
      value: function () {
        var _truncate = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee12() {
          return _regeneratorRuntime.wrap(function _callee12$(_context12) {
            while (1) {
              switch (_context12.prev = _context12.next) {
                case 0:
                  _context12.next = 2;
                  return this.getClient().post(this._channelURL() + '/truncate', {});
  
                case 2:
                  return _context12.abrupt("return", _context12.sent);
  
                case 3:
                case "end":
                  return _context12.stop();
              }
            }
          }, _callee12, this);
        }));
  
        function truncate() {
          return _truncate.apply(this, arguments);
        }
  
        return truncate;
      }()
      /**
       * acceptInvite - accept invitation to the channel
       *
       * @param {InviteOptions<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>} [options] The object to update the custom properties of this channel with
       *
       * @return {Promise<UpdateChannelAPIResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>>} The server response
       */
  
    }, {
      key: "acceptInvite",
      value: function () {
        var _acceptInvite = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee13() {
          var options,
              data,
              _args13 = arguments;
          return _regeneratorRuntime.wrap(function _callee13$(_context13) {
            while (1) {
              switch (_context13.prev = _context13.next) {
                case 0:
                  options = _args13.length > 0 && _args13[0] !== undefined ? _args13[0] : {};
                  _context13.next = 3;
                  return this.getClient().post(this._channelURL(), _objectSpread$2({
                    accept_invite: true
                  }, options));
  
                case 3:
                  data = _context13.sent;
                  this.data = data.channel;
                  return _context13.abrupt("return", data);
  
                case 6:
                case "end":
                  return _context13.stop();
              }
            }
          }, _callee13, this);
        }));
  
        function acceptInvite() {
          return _acceptInvite.apply(this, arguments);
        }
  
        return acceptInvite;
      }()
      /**
       * rejectInvite - reject invitation to the channel
       *
       * @param {InviteOptions<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>} [options] The object to update the custom properties of this channel with
       *
       * @return {Promise<UpdateChannelAPIResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>>} The server response
       */
  
    }, {
      key: "rejectInvite",
      value: function () {
        var _rejectInvite = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee14() {
          var options,
              data,
              _args14 = arguments;
          return _regeneratorRuntime.wrap(function _callee14$(_context14) {
            while (1) {
              switch (_context14.prev = _context14.next) {
                case 0:
                  options = _args14.length > 0 && _args14[0] !== undefined ? _args14[0] : {};
                  _context14.next = 3;
                  return this.getClient().post(this._channelURL(), _objectSpread$2({
                    reject_invite: true
                  }, options));
  
                case 3:
                  data = _context14.sent;
                  this.data = data.channel;
                  return _context14.abrupt("return", data);
  
                case 6:
                case "end":
                  return _context14.stop();
              }
            }
          }, _callee14, this);
        }));
  
        function rejectInvite() {
          return _rejectInvite.apply(this, arguments);
        }
  
        return rejectInvite;
      }()
      /**
       * addMembers - add members to the channel
       *
       * @param {string[]} members An array of member identifiers
       * @param {Message<AttachmentType, MessageType, UserType>} [message] Optional message object for channel members notification
       * @return {Promise<UpdateChannelAPIResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>>} The server response
       */
  
    }, {
      key: "addMembers",
      value: function () {
        var _addMembers = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee15(members, message) {
          var data;
          return _regeneratorRuntime.wrap(function _callee15$(_context15) {
            while (1) {
              switch (_context15.prev = _context15.next) {
                case 0:
                  _context15.next = 2;
                  return this.getClient().post(this._channelURL(), {
                    add_members: members,
                    message: message
                  });
  
                case 2:
                  data = _context15.sent;
                  this.data = data.channel;
                  return _context15.abrupt("return", data);
  
                case 5:
                case "end":
                  return _context15.stop();
              }
            }
          }, _callee15, this);
        }));
  
        function addMembers(_x11, _x12) {
          return _addMembers.apply(this, arguments);
        }
  
        return addMembers;
      }()
      /**
       * addModerators - add moderators to the channel
       *
       * @param {string[]} members An array of member identifiers
       * @param {Message<AttachmentType, MessageType, UserType>} [message] Optional message object for channel members notification
       * @return {Promise<UpdateChannelAPIResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>>} The server response
       */
  
    }, {
      key: "addModerators",
      value: function () {
        var _addModerators = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee16(members, message) {
          var data;
          return _regeneratorRuntime.wrap(function _callee16$(_context16) {
            while (1) {
              switch (_context16.prev = _context16.next) {
                case 0:
                  _context16.next = 2;
                  return this.getClient().post(this._channelURL(), {
                    add_moderators: members,
                    message: message
                  });
  
                case 2:
                  data = _context16.sent;
                  this.data = data.channel;
                  return _context16.abrupt("return", data);
  
                case 5:
                case "end":
                  return _context16.stop();
              }
            }
          }, _callee16, this);
        }));
  
        function addModerators(_x13, _x14) {
          return _addModerators.apply(this, arguments);
        }
  
        return addModerators;
      }()
      /**
       * inviteMembers - invite members to the channel
       *
       * @param {string[]} members An array of member identifiers
       * @param {Message<AttachmentType, MessageType, UserType>} [message] Optional message object for channel members notification
       * @return {Promise<UpdateChannelAPIResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>>} The server response
       */
  
    }, {
      key: "inviteMembers",
      value: function () {
        var _inviteMembers = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee17(members, message) {
          var data;
          return _regeneratorRuntime.wrap(function _callee17$(_context17) {
            while (1) {
              switch (_context17.prev = _context17.next) {
                case 0:
                  _context17.next = 2;
                  return this.getClient().post(this._channelURL(), {
                    invites: members,
                    message: message
                  });
  
                case 2:
                  data = _context17.sent;
                  this.data = data.channel;
                  return _context17.abrupt("return", data);
  
                case 5:
                case "end":
                  return _context17.stop();
              }
            }
          }, _callee17, this);
        }));
  
        function inviteMembers(_x15, _x16) {
          return _inviteMembers.apply(this, arguments);
        }
  
        return inviteMembers;
      }()
      /**
       * removeMembers - remove members from channel
       *
       * @param {string[]} members An array of member identifiers
       * @param {Message<AttachmentType, MessageType, UserType>} [message] Optional message object for channel members notification
       * @return {Promise<UpdateChannelAPIResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>>} The server response
       */
  
    }, {
      key: "removeMembers",
      value: function () {
        var _removeMembers = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee18(members, message) {
          var data;
          return _regeneratorRuntime.wrap(function _callee18$(_context18) {
            while (1) {
              switch (_context18.prev = _context18.next) {
                case 0:
                  _context18.next = 2;
                  return this.getClient().post(this._channelURL(), {
                    remove_members: members,
                    message: message
                  });
  
                case 2:
                  data = _context18.sent;
                  this.data = data.channel;
                  return _context18.abrupt("return", data);
  
                case 5:
                case "end":
                  return _context18.stop();
              }
            }
          }, _callee18, this);
        }));
  
        function removeMembers(_x17, _x18) {
          return _removeMembers.apply(this, arguments);
        }
  
        return removeMembers;
      }()
      /**
       * demoteModerators - remove moderator role from channel members
       *
       * @param {string[]} members An array of member identifiers
       * @param {Message<AttachmentType, MessageType, UserType>} [message] Optional message object for channel members notification
       * @return {Promise<UpdateChannelAPIResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>>} The server response
       */
  
    }, {
      key: "demoteModerators",
      value: function () {
        var _demoteModerators = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee19(members, message) {
          var data;
          return _regeneratorRuntime.wrap(function _callee19$(_context19) {
            while (1) {
              switch (_context19.prev = _context19.next) {
                case 0:
                  _context19.next = 2;
                  return this.getClient().post(this._channelURL(), {
                    demote_moderators: members,
                    message: message
                  });
  
                case 2:
                  data = _context19.sent;
                  this.data = data.channel;
                  return _context19.abrupt("return", data);
  
                case 5:
                case "end":
                  return _context19.stop();
              }
            }
          }, _callee19, this);
        }));
  
        function demoteModerators(_x19, _x20) {
          return _demoteModerators.apply(this, arguments);
        }
  
        return demoteModerators;
      }()
      /**
       * mute - mutes the current channel
       * @param {{ user_id?: string, expiration?: string }} opts expiration in minutes or user_id
       * @return {Promise<MuteChannelAPIResponse<ChannelType, CommandType, UserType>>} The server response
       *
       * example with expiration:
       * await channel.mute({expiration: moment.duration(2, 'weeks')});
       *
       * example server side:
       * await channel.mute({user_id: userId});
       *
       */
  
    }, {
      key: "mute",
      value: function () {
        var _mute = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee20() {
          var opts,
              _args20 = arguments;
          return _regeneratorRuntime.wrap(function _callee20$(_context20) {
            while (1) {
              switch (_context20.prev = _context20.next) {
                case 0:
                  opts = _args20.length > 0 && _args20[0] !== undefined ? _args20[0] : {};
                  _context20.next = 3;
                  return this.getClient().post(this.getClient().baseURL + '/moderation/mute/channel', _objectSpread$2({
                    channel_cid: this.cid
                  }, opts));
  
                case 3:
                  return _context20.abrupt("return", _context20.sent);
  
                case 4:
                case "end":
                  return _context20.stop();
              }
            }
          }, _callee20, this);
        }));
  
        function mute() {
          return _mute.apply(this, arguments);
        }
  
        return mute;
      }()
      /**
       * unmute - mutes the current channel
       * @param {{ user_id?: string}} opts user_id
       * @return {Promise<APIResponse>} The server response
       *
       * example server side:
       * await channel.unmute({user_id: userId});
       */
  
    }, {
      key: "unmute",
      value: function () {
        var _unmute = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee21() {
          var opts,
              _args21 = arguments;
          return _regeneratorRuntime.wrap(function _callee21$(_context21) {
            while (1) {
              switch (_context21.prev = _context21.next) {
                case 0:
                  opts = _args21.length > 0 && _args21[0] !== undefined ? _args21[0] : {};
                  _context21.next = 3;
                  return this.getClient().post(this.getClient().baseURL + '/moderation/unmute/channel', _objectSpread$2({
                    channel_cid: this.cid
                  }, opts));
  
                case 3:
                  return _context21.abrupt("return", _context21.sent);
  
                case 4:
                case "end":
                  return _context21.stop();
              }
            }
          }, _callee21, this);
        }));
  
        function unmute() {
          return _unmute.apply(this, arguments);
        }
  
        return unmute;
      }()
      /**
       * muteStatus - returns the mute status for the current channel
       * @return {{ muted: boolean; createdAt?: string | null; expiresAt?: string | null }} { muted: true | false, createdAt: Date | null, expiresAt: Date | null}
       */
  
    }, {
      key: "muteStatus",
      value: function muteStatus() {
        this._checkInitialized();
  
        return this.getClient()._muteStatus(this.cid);
      }
    }, {
      key: "sendAction",
      value: function sendAction(messageID, formData) {
        this._checkInitialized();
  
        if (!messageID) {
          throw Error("Message id is missing");
        }
  
        return this.getClient().post(this.getClient().baseURL + "/messages/".concat(messageID, "/action"), {
          message_id: messageID,
          form_data: formData,
          id: this.id,
          type: this.type
        });
      }
      /**
       * keystroke - First of the typing.start and typing.stop events based on the users keystrokes.
       * Call this on every keystroke
       * @see {@link https://getstream.io/chat/docs/typing_indicators/?language=js|Docs}
       * @param {string} [parent_id] set this field to `message.id` to indicate that typing event is happening in a thread
       */
  
    }, {
      key: "keystroke",
      value: function () {
        var _keystroke = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee22(parent_id) {
          var _this$getConfig;
  
          var now, diff;
          return _regeneratorRuntime.wrap(function _callee22$(_context22) {
            while (1) {
              switch (_context22.prev = _context22.next) {
                case 0:
                  if ((_this$getConfig = this.getConfig()) !== null && _this$getConfig !== void 0 && _this$getConfig.typing_events) {
                    _context22.next = 2;
                    break;
                  }
  
                  return _context22.abrupt("return");
  
                case 2:
                  now = new Date();
                  diff = this.lastTypingEvent && now.getTime() - this.lastTypingEvent.getTime();
                  this.lastKeyStroke = now;
                  this.isTyping = true; // send a typing.start every 2 seconds
  
                  if (!(diff === null || diff > 2000)) {
                    _context22.next = 10;
                    break;
                  }
  
                  this.lastTypingEvent = new Date();
                  _context22.next = 10;
                  return this.sendEvent({
                    type: 'typing.start',
                    parent_id: parent_id
                  });
  
                case 10:
                case "end":
                  return _context22.stop();
              }
            }
          }, _callee22, this);
        }));
  
        function keystroke(_x21) {
          return _keystroke.apply(this, arguments);
        }
  
        return keystroke;
      }()
      /**
       * stopTyping - Sets last typing to null and sends the typing.stop event
       * @see {@link https://getstream.io/chat/docs/typing_indicators/?language=js|Docs}
       * @param {string} [parent_id] set this field to `message.id` to indicate that typing event is happening in a thread
       */
  
    }, {
      key: "stopTyping",
      value: function () {
        var _stopTyping = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee23(parent_id) {
          var _this$getConfig2;
  
          return _regeneratorRuntime.wrap(function _callee23$(_context23) {
            while (1) {
              switch (_context23.prev = _context23.next) {
                case 0:
                  if ((_this$getConfig2 = this.getConfig()) !== null && _this$getConfig2 !== void 0 && _this$getConfig2.typing_events) {
                    _context23.next = 2;
                    break;
                  }
  
                  return _context23.abrupt("return");
  
                case 2:
                  this.lastTypingEvent = null;
                  this.isTyping = false;
                  _context23.next = 6;
                  return this.sendEvent({
                    type: 'typing.stop',
                    parent_id: parent_id
                  });
  
                case 6:
                case "end":
                  return _context23.stop();
              }
            }
          }, _callee23, this);
        }));
  
        function stopTyping(_x22) {
          return _stopTyping.apply(this, arguments);
        }
  
        return stopTyping;
      }()
      /**
       * lastMessage - return the last message, takes into account that last few messages might not be perfectly sorted
       *
       * @return {ReturnType<ChannelState<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType>['formatMessage']> | undefined} Description
       */
  
    }, {
      key: "lastMessage",
      value: function lastMessage() {
        // get last 5 messages, sort, return the latest
        // get a slice of the last 5
        var min = this.state.messages.length - 5;
  
        if (min < 0) {
          min = 0;
        }
  
        var max = this.state.messages.length + 1;
        var messageSlice = this.state.messages.slice(min, max); // sort by pk desc
  
        messageSlice.sort(function (a, b) {
          return b.created_at.getTime() - a.created_at.getTime();
        });
        return messageSlice[0];
      }
      /**
       * markRead - Send the mark read event for this user, only works if the `read_events` setting is enabled
       *
       * @param {MarkReadOptions<UserType>} data
       * @return {Promise<EventAPIResponse<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType> | null>} Description
       */
  
    }, {
      key: "markRead",
      value: function () {
        var _markRead = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee24() {
          var _this$getConfig3;
  
          var data,
              _args24 = arguments;
          return _regeneratorRuntime.wrap(function _callee24$(_context24) {
            while (1) {
              switch (_context24.prev = _context24.next) {
                case 0:
                  data = _args24.length > 0 && _args24[0] !== undefined ? _args24[0] : {};
  
                  this._checkInitialized();
  
                  if ((_this$getConfig3 = this.getConfig()) !== null && _this$getConfig3 !== void 0 && _this$getConfig3.read_events) {
                    _context24.next = 4;
                    break;
                  }
  
                  return _context24.abrupt("return", Promise.resolve(null));
  
                case 4:
                  _context24.next = 6;
                  return this.getClient().post(this._channelURL() + '/read', _objectSpread$2({}, data));
  
                case 6:
                  return _context24.abrupt("return", _context24.sent);
  
                case 7:
                case "end":
                  return _context24.stop();
              }
            }
          }, _callee24, this);
        }));
  
        function markRead() {
          return _markRead.apply(this, arguments);
        }
  
        return markRead;
      }()
      /**
       * clean - Cleans the channel state and fires stop typing if needed
       */
  
    }, {
      key: "clean",
      value: function clean() {
        if (this.lastKeyStroke) {
          var now = new Date();
          var diff = now.getTime() - this.lastKeyStroke.getTime();
  
          if (diff > 1000 && this.isTyping) {
            logChatPromiseExecution(this.stopTyping(), 'stop typing event');
          }
        }
  
        this.state.clean();
      }
      /**
       * watch - Loads the initial channel state and watches for changes
       *
       * @param {ChannelQueryOptions<ChannelType, CommandType, UserType>} options additional options for the query endpoint
       *
       * @return {Promise<ChannelAPIResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>>} The server response
       */
  
    }, {
      key: "watch",
      value: function () {
        var _watch = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee25(options) {
          var defaultOptions, combined, state;
          return _regeneratorRuntime.wrap(function _callee25$(_context25) {
            while (1) {
              switch (_context25.prev = _context25.next) {
                case 0:
                  defaultOptions = {
                    state: true,
                    watch: true,
                    presence: false
                  }; // Make sure we wait for the connect promise if there is a pending one
  
                  _context25.next = 3;
                  return this.getClient().wsPromise;
  
                case 3:
                  if (!this.getClient()._hasConnectionID()) {
                    defaultOptions.watch = false;
                  }
  
                  combined = _objectSpread$2(_objectSpread$2({}, defaultOptions), options);
                  _context25.next = 7;
                  return this.query(combined);
  
                case 7:
                  state = _context25.sent;
                  this.initialized = true;
                  this.data = state.channel;
  
                  this._client.logger('info', "channel:watch() - started watching channel ".concat(this.cid), {
                    tags: ['channel'],
                    channel: this
                  });
  
                  return _context25.abrupt("return", state);
  
                case 12:
                case "end":
                  return _context25.stop();
              }
            }
          }, _callee25, this);
        }));
  
        function watch(_x23) {
          return _watch.apply(this, arguments);
        }
  
        return watch;
      }()
      /**
       * stopWatching - Stops watching the channel
       *
       * @return {Promise<APIResponse>} The server response
       */
  
    }, {
      key: "stopWatching",
      value: function () {
        var _stopWatching = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee26() {
          var response;
          return _regeneratorRuntime.wrap(function _callee26$(_context26) {
            while (1) {
              switch (_context26.prev = _context26.next) {
                case 0:
                  _context26.next = 2;
                  return this.getClient().post(this._channelURL() + '/stop-watching', {});
  
                case 2:
                  response = _context26.sent;
  
                  this._client.logger('info', "channel:watch() - stopped watching channel ".concat(this.cid), {
                    tags: ['channel'],
                    channel: this
                  });
  
                  return _context26.abrupt("return", response);
  
                case 5:
                case "end":
                  return _context26.stop();
              }
            }
          }, _callee26, this);
        }));
  
        function stopWatching() {
          return _stopWatching.apply(this, arguments);
        }
  
        return stopWatching;
      }()
      /**
       * getReplies - List the message replies for a parent message
       *
       * @param {string} parent_id The message parent id, ie the top of the thread
       * @param {PaginationOptions & { user?: UserResponse<UserType>; user_id?: string }} options Pagination params, ie {limit:10, id_lte: 10}
       *
       * @return {Promise<GetRepliesAPIResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>>} A response with a list of messages
       */
  
    }, {
      key: "getReplies",
      value: function () {
        var _getReplies = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee27(parent_id, options) {
          var data;
          return _regeneratorRuntime.wrap(function _callee27$(_context27) {
            while (1) {
              switch (_context27.prev = _context27.next) {
                case 0:
                  _context27.next = 2;
                  return this.getClient().get(this.getClient().baseURL + "/messages/".concat(parent_id, "/replies"), _objectSpread$2({}, options));
  
                case 2:
                  data = _context27.sent;
  
                  // add any messages to our thread state
                  if (data.messages) {
                    this.state.addMessagesSorted(data.messages);
                  }
  
                  return _context27.abrupt("return", data);
  
                case 5:
                case "end":
                  return _context27.stop();
              }
            }
          }, _callee27, this);
        }));
  
        function getReplies(_x24, _x25) {
          return _getReplies.apply(this, arguments);
        }
  
        return getReplies;
      }()
      /**
       * getReactions - List the reactions, supports pagination
       *
       * @param {string} message_id The message id
       * @param {{ limit?: number; offset?: number }} options The pagination options
       *
       * @return {Promise<GetReactionsAPIResponse<ReactionType, UserType>>} Server response
       */
  
    }, {
      key: "getReactions",
      value: function getReactions(message_id, options) {
        return this.getClient().get(this.getClient().baseURL + "/messages/".concat(message_id, "/reactions"), _objectSpread$2({}, options));
      }
      /**
       * getMessagesById - Retrieves a list of messages by ID
       *
       * @param {string[]} messageIds The ids of the messages to retrieve from this channel
       *
       * @return {Promise<GetMultipleMessagesAPIResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>>} Server response
       */
  
    }, {
      key: "getMessagesById",
      value: function getMessagesById(messageIds) {
        return this.getClient().get(this._channelURL() + '/messages', {
          ids: messageIds.join(',')
        });
      }
      /**
       * lastRead - returns the last time the user marked the channel as read if the user never marked the channel as read, this will return null
       * @return {Date | null | undefined}
       */
  
    }, {
      key: "lastRead",
      value: function lastRead() {
        this._checkInitialized();
  
        var _this$getClient = this.getClient(),
            userID = _this$getClient.userID;
  
        if (userID) {
          return this.state.read[userID] ? this.state.read[userID].last_read : null;
        }
      }
    }, {
      key: "_countMessageAsUnread",
      value: function _countMessageAsUnread(message) {
        var _message$user, _message$user2;
  
        if (message.shadowed) return false;
        if (message.silent) return false;
        if (((_message$user = message.user) === null || _message$user === void 0 ? void 0 : _message$user.id) === this.getClient().userID) return false;
        if ((_message$user2 = message.user) !== null && _message$user2 !== void 0 && _message$user2.id && this.getClient().userMuteStatus(message.user.id)) return false;
        return true;
      }
      /**
       * countUnread - Count of unread messages
       *
       * @param {Date | null} [lastRead] lastRead the time that the user read a message, defaults to current user's read state
       *
       * @return {number} Unread count
       */
  
    }, {
      key: "countUnread",
      value: function countUnread(lastRead) {
        if (!lastRead) return this.state.unreadCount;
        var count = 0;
  
        for (var i = 0; i < this.state.messages.length; i += 1) {
          var message = this.state.messages[i];
  
          if (message.created_at > lastRead && this._countMessageAsUnread(message)) {
            count++;
          }
        }
  
        return count;
      }
      /**
       * countUnread - Count the number of unread messages mentioning the current user
       *
       * @return {number} Unread mentions count
       */
  
    }, {
      key: "countUnreadMentions",
      value: function countUnreadMentions() {
        var lastRead = this.lastRead();
        var userID = this.getClient().userID;
        var count = 0;
  
        for (var i = 0; i < this.state.messages.length; i += 1) {
          var _message$mentioned_us;
  
          var message = this.state.messages[i];
  
          if (this._countMessageAsUnread(message) && (!lastRead || message.created_at > lastRead) && (_message$mentioned_us = message.mentioned_users) !== null && _message$mentioned_us !== void 0 && _message$mentioned_us.some(function (user) {
            return user.id === userID;
          })) {
            count++;
          }
        }
  
        return count;
      }
      /**
       * create - Creates a new channel
       *
       * @return {Promise<ChannelAPIResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>>} The Server Response
       */
  
    }, {
      key: "query",
      value:
      /**
       * query - Query the API, get messages, members or other channel fields
       *
       * @param {ChannelQueryOptions<ChannelType, CommandType, UserType>} options The query options
       *
       * @return {Promise<ChannelAPIResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>>} Returns a query response
       */
      function () {
        var _query = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee28(options) {
          var queryURL, state, membersStr, tempChannelCid;
          return _regeneratorRuntime.wrap(function _callee28$(_context28) {
            while (1) {
              switch (_context28.prev = _context28.next) {
                case 0:
                  _context28.next = 2;
                  return this.getClient().wsPromise;
  
                case 2:
                  queryURL = "".concat(this.getClient().baseURL, "/channels/").concat(this.type);
  
                  if (this.id) {
                    queryURL += "/".concat(this.id);
                  }
  
                  _context28.next = 6;
                  return this.getClient().post(queryURL + '/query', _objectSpread$2({
                    data: this._data,
                    state: true
                  }, options));
  
                case 6:
                  state = _context28.sent;
  
                  // update the channel id if it was missing
                  if (!this.id) {
                    this.id = state.channel.id;
                    this.cid = state.channel.cid; // set the channel as active...
  
                    membersStr = state.members.map(function (member) {
                      var _member$user;
  
                      return member.user_id || ((_member$user = member.user) === null || _member$user === void 0 ? void 0 : _member$user.id);
                    }).sort().join(',');
                    tempChannelCid = "".concat(this.type, ":!members-").concat(membersStr);
  
                    if (tempChannelCid in this.getClient().activeChannels) {
                      // This gets set in `client.channel()` function, when channel is created
                      // using members, not id.
                      delete this.getClient().activeChannels[tempChannelCid];
                    }
  
                    if (!(this.cid in this.getClient().activeChannels)) {
                      this.getClient().activeChannels[this.cid] = this;
                    }
                  }
  
                  this.getClient()._addChannelConfig(state); // add any messages to our channel state
  
  
                  this._initializeState(state);
  
                  return _context28.abrupt("return", state);
  
                case 11:
                case "end":
                  return _context28.stop();
              }
            }
          }, _callee28, this);
        }));
  
        function query(_x26) {
          return _query.apply(this, arguments);
        }
  
        return query;
      }()
      /**
       * banUser - Bans a user from a channel
       *
       * @param {string} targetUserID
       * @param {BanUserOptions<UserType>} options
       * @returns {Promise<APIResponse>}
       */
  
    }, {
      key: "banUser",
      value: function () {
        var _banUser = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee29(targetUserID, options) {
          return _regeneratorRuntime.wrap(function _callee29$(_context29) {
            while (1) {
              switch (_context29.prev = _context29.next) {
                case 0:
                  this._checkInitialized();
  
                  _context29.next = 3;
                  return this.getClient().banUser(targetUserID, _objectSpread$2(_objectSpread$2({}, options), {}, {
                    type: this.type,
                    id: this.id
                  }));
  
                case 3:
                  return _context29.abrupt("return", _context29.sent);
  
                case 4:
                case "end":
                  return _context29.stop();
              }
            }
          }, _callee29, this);
        }));
  
        function banUser(_x27, _x28) {
          return _banUser.apply(this, arguments);
        }
  
        return banUser;
      }()
      /**
       * hides the channel from queryChannels for the user until a message is added
       * If clearHistory is set to true - all messages will be removed for the user
       *
       * @param {string | null} userId
       * @param {boolean} clearHistory
       * @returns {Promise<APIResponse>}
       */
  
    }, {
      key: "hide",
      value: function () {
        var _hide = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee30() {
          var userId,
              clearHistory,
              _args30 = arguments;
          return _regeneratorRuntime.wrap(function _callee30$(_context30) {
            while (1) {
              switch (_context30.prev = _context30.next) {
                case 0:
                  userId = _args30.length > 0 && _args30[0] !== undefined ? _args30[0] : null;
                  clearHistory = _args30.length > 1 && _args30[1] !== undefined ? _args30[1] : false;
  
                  this._checkInitialized();
  
                  _context30.next = 5;
                  return this.getClient().post("".concat(this._channelURL(), "/hide"), {
                    user_id: userId,
                    clear_history: clearHistory
                  });
  
                case 5:
                  return _context30.abrupt("return", _context30.sent);
  
                case 6:
                case "end":
                  return _context30.stop();
              }
            }
          }, _callee30, this);
        }));
  
        function hide() {
          return _hide.apply(this, arguments);
        }
  
        return hide;
      }()
      /**
       * removes the hidden status for a channel
       *
       * @param {string | null} userId
       * @returns {Promise<APIResponse>}
       */
  
    }, {
      key: "show",
      value: function () {
        var _show = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee31() {
          var userId,
              _args31 = arguments;
          return _regeneratorRuntime.wrap(function _callee31$(_context31) {
            while (1) {
              switch (_context31.prev = _context31.next) {
                case 0:
                  userId = _args31.length > 0 && _args31[0] !== undefined ? _args31[0] : null;
  
                  this._checkInitialized();
  
                  _context31.next = 4;
                  return this.getClient().post("".concat(this._channelURL(), "/show"), {
                    user_id: userId
                  });
  
                case 4:
                  return _context31.abrupt("return", _context31.sent);
  
                case 5:
                case "end":
                  return _context31.stop();
              }
            }
          }, _callee31, this);
        }));
  
        function show() {
          return _show.apply(this, arguments);
        }
  
        return show;
      }()
      /**
       * unbanUser - Removes the bans for a user on a channel
       *
       * @param {string} targetUserID
       * @returns {Promise<APIResponse>}
       */
  
    }, {
      key: "unbanUser",
      value: function () {
        var _unbanUser = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee32(targetUserID) {
          return _regeneratorRuntime.wrap(function _callee32$(_context32) {
            while (1) {
              switch (_context32.prev = _context32.next) {
                case 0:
                  this._checkInitialized();
  
                  _context32.next = 3;
                  return this.getClient().unbanUser(targetUserID, {
                    type: this.type,
                    id: this.id
                  });
  
                case 3:
                  return _context32.abrupt("return", _context32.sent);
  
                case 4:
                case "end":
                  return _context32.stop();
              }
            }
          }, _callee32, this);
        }));
  
        function unbanUser(_x29) {
          return _unbanUser.apply(this, arguments);
        }
  
        return unbanUser;
      }()
      /**
       * shadowBan - Shadow bans a user from a channel
       *
       * @param {string} targetUserID
       * @param {BanUserOptions<UserType>} options
       * @returns {Promise<APIResponse>}
       */
  
    }, {
      key: "shadowBan",
      value: function () {
        var _shadowBan = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee33(targetUserID, options) {
          return _regeneratorRuntime.wrap(function _callee33$(_context33) {
            while (1) {
              switch (_context33.prev = _context33.next) {
                case 0:
                  this._checkInitialized();
  
                  _context33.next = 3;
                  return this.getClient().shadowBan(targetUserID, _objectSpread$2(_objectSpread$2({}, options), {}, {
                    type: this.type,
                    id: this.id
                  }));
  
                case 3:
                  return _context33.abrupt("return", _context33.sent);
  
                case 4:
                case "end":
                  return _context33.stop();
              }
            }
          }, _callee33, this);
        }));
  
        function shadowBan(_x30, _x31) {
          return _shadowBan.apply(this, arguments);
        }
  
        return shadowBan;
      }()
      /**
       * removeShadowBan - Removes the shadow ban for a user on a channel
       *
       * @param {string} targetUserID
       * @returns {Promise<APIResponse>}
       */
  
    }, {
      key: "removeShadowBan",
      value: function () {
        var _removeShadowBan = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee34(targetUserID) {
          return _regeneratorRuntime.wrap(function _callee34$(_context34) {
            while (1) {
              switch (_context34.prev = _context34.next) {
                case 0:
                  this._checkInitialized();
  
                  _context34.next = 3;
                  return this.getClient().removeShadowBan(targetUserID, {
                    type: this.type,
                    id: this.id
                  });
  
                case 3:
                  return _context34.abrupt("return", _context34.sent);
  
                case 4:
                case "end":
                  return _context34.stop();
              }
            }
          }, _callee34, this);
        }));
  
        function removeShadowBan(_x32) {
          return _removeShadowBan.apply(this, arguments);
        }
  
        return removeShadowBan;
      }()
      /**
       * on - Listen to events on this channel.
       *
       * channel.on('message.new', event => {console.log("my new message", event, channel.state.messages)})
       * or
       * channel.on(event => {console.log(event.type)})
       *
       * @param {EventHandler<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType> | EventTypes} callbackOrString  The event type to listen for (optional)
       * @param {EventHandler<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType>} [callbackOrNothing] The callback to call
       */
  
    }, {
      key: "on",
      value: function on(callbackOrString, callbackOrNothing) {
        var key = callbackOrNothing ? callbackOrString : 'all';
        var valid = isValidEventType(key);
  
        if (!valid) {
          throw Error("Invalid event type ".concat(key));
        }
  
        var callback = callbackOrNothing ? callbackOrNothing : callbackOrString;
  
        if (!(key in this.listeners)) {
          this.listeners[key] = [];
        }
  
        this._client.logger('info', "Attaching listener for ".concat(key, " event on channel ").concat(this.cid), {
          tags: ['event', 'channel'],
          channel: this
        });
  
        this.listeners[key].push(callback);
      }
      /**
       * off - Remove the event handler
       *
       */
  
    }, {
      key: "off",
      value: function off(callbackOrString, callbackOrNothing) {
        var key = callbackOrNothing ? callbackOrString : 'all';
        var valid = isValidEventType(key);
  
        if (!valid) {
          throw Error("Invalid event type ".concat(key));
        }
  
        var callback = callbackOrNothing ? callbackOrNothing : callbackOrString;
  
        if (!(key in this.listeners)) {
          this.listeners[key] = [];
        }
  
        this._client.logger('info', "Removing listener for ".concat(key, " event from channel ").concat(this.cid), {
          tags: ['event', 'channel'],
          channel: this
        });
  
        this.listeners[key] = this.listeners[key].filter(function (value) {
          return value !== callback;
        });
      } // eslint-disable-next-line sonarjs/cognitive-complexity
  
    }, {
      key: "_handleChannelEvent",
      value: function _handleChannelEvent(event) {
        var _event$user, _event$user2, _event$user3, _event$user5, _event$user6, _event$member, _event$user9;
  
        var channel = this;
  
        this._client.logger('info', "channel:_handleChannelEvent - Received event of type { ".concat(event.type, " } on ").concat(this.cid), {
          tags: ['event', 'channel'],
          channel: this
        });
  
        var channelState = channel.state;
  
        switch (event.type) {
          case 'typing.start':
            if ((_event$user = event.user) !== null && _event$user !== void 0 && _event$user.id) {
              channelState.typing[event.user.id] = event;
            }
  
            break;
  
          case 'typing.stop':
            if ((_event$user2 = event.user) !== null && _event$user2 !== void 0 && _event$user2.id) {
              delete channelState.typing[event.user.id];
            }
  
            break;
  
          case 'message.read':
            if ((_event$user3 = event.user) !== null && _event$user3 !== void 0 && _event$user3.id) {
              var _event$user4, _this$getClient$user;
  
              channelState.read[event.user.id] = {
                // because in client.ts the handleEvent call that flows to this sets this `event.received_at = new Date();`
                last_read: event.received_at,
                user: event.user
              };
  
              if (((_event$user4 = event.user) === null || _event$user4 === void 0 ? void 0 : _event$user4.id) === ((_this$getClient$user = this.getClient().user) === null || _this$getClient$user === void 0 ? void 0 : _this$getClient$user.id)) {
                channelState.unreadCount = 0;
              }
            }
  
            break;
  
          case 'user.watching.start':
          case 'user.updated':
            if ((_event$user5 = event.user) !== null && _event$user5 !== void 0 && _event$user5.id) {
              channelState.watchers[event.user.id] = event.user;
            }
  
            break;
  
          case 'user.watching.stop':
            if ((_event$user6 = event.user) !== null && _event$user6 !== void 0 && _event$user6.id) {
              delete channelState.watchers[event.user.id];
            }
  
            break;
  
          case 'message.deleted':
            if (event.message) {
              if (event.hard_delete) channelState.removeMessage(event.message);else channelState.addMessageSorted(event.message);
  
              if (event.message.pinned) {
                channelState.removePinnedMessage(event.message);
              }
            }
  
            break;
  
          case 'message.new':
            if (event.message) {
              var _event$user7, _this$getClient$user2, _event$user8;
  
              /* if message belongs to current user, always assume timestamp is changed to filter it out and add again to avoid duplication */
              var ownMessage = ((_event$user7 = event.user) === null || _event$user7 === void 0 ? void 0 : _event$user7.id) === ((_this$getClient$user2 = this.getClient().user) === null || _this$getClient$user2 === void 0 ? void 0 : _this$getClient$user2.id);
              var isThreadMessage = event.message.parent_id && !event.message.show_in_channel;
  
              if (this.state.isUpToDate || isThreadMessage) {
                channelState.addMessageSorted(event.message, ownMessage);
              }
  
              if (event.message.pinned) {
                channelState.addPinnedMessage(event.message);
              }
  
              if (ownMessage && (_event$user8 = event.user) !== null && _event$user8 !== void 0 && _event$user8.id) {
                channelState.unreadCount = 0;
                channelState.read[event.user.id] = {
                  last_read: new Date(event.created_at),
                  user: event.user
                };
              } else if (this._countMessageAsUnread(event.message)) {
                channelState.unreadCount = channelState.unreadCount + 1;
              }
            }
  
            break;
  
          case 'message.updated':
            if (event.message) {
              channelState.addMessageSorted(event.message);
  
              if (event.message.pinned) {
                channelState.addPinnedMessage(event.message);
              } else {
                channelState.removePinnedMessage(event.message);
              }
            }
  
            break;
  
          case 'channel.truncated':
            channelState.clearMessages();
            channelState.unreadCount = 0;
            break;
  
          case 'member.added':
          case 'member.updated':
            if ((_event$member = event.member) !== null && _event$member !== void 0 && _event$member.user_id) {
              channelState.members[event.member.user_id] = event.member;
            }
  
            break;
  
          case 'member.removed':
            if ((_event$user9 = event.user) !== null && _event$user9 !== void 0 && _event$user9.id) {
              delete channelState.members[event.user.id];
            }
  
            break;
  
          case 'channel.updated':
            if (event.channel) {
              channel.data = event.channel;
            }
  
            break;
  
          case 'reaction.new':
            if (event.message && event.reaction) {
              event.message = channelState.addReaction(event.reaction, event.message);
            }
  
            break;
  
          case 'reaction.deleted':
            if (event.reaction) {
              event.message = channelState.removeReaction(event.reaction, event.message);
            }
  
            break;
  
          case 'reaction.updated':
            if (event.reaction) {
              // assuming reaction.updated is only called if enforce_unique is true
              event.message = channelState.addReaction(event.reaction, event.message, true);
            }
  
            break;
  
          case 'channel.hidden':
            if (event.clear_history) {
              channelState.clearMessages();
            }
  
            break;
        } // any event can send over the online count
  
  
        if (event.watcher_count !== undefined) {
          channel.state.watcher_count = event.watcher_count;
        }
      }
    }, {
      key: "_checkInitialized",
      value: function _checkInitialized() {
        if (!this.initialized && !this.getClient()._isUsingServerAuth()) {
          throw Error("Channel ".concat(this.cid, " hasn't been initialized yet. Make sure to call .watch() and wait for it to resolve"));
        }
      } // eslint-disable-next-line sonarjs/cognitive-complexity
  
    }, {
      key: "_initializeState",
      value: function _initializeState(state) {
        var _this$getClient2 = this.getClient(),
            clientState = _this$getClient2.state,
            user = _this$getClient2.user,
            userID = _this$getClient2.userID; // add the Users
  
  
        if (state.members) {
          var _iterator = _createForOfIteratorHelper$1(state.members),
              _step;
  
          try {
            for (_iterator.s(); !(_step = _iterator.n()).done;) {
              var member = _step.value;
  
              if (member.user) {
                clientState.updateUserReference(member.user, this.cid);
              }
            }
          } catch (err) {
            _iterator.e(err);
          } finally {
            _iterator.f();
          }
        }
  
        this.state.membership = state.membership || {};
        var messages = state.messages || [];
  
        if (!this.state.messages) {
          this.state.messages = [];
        }
  
        this.state.addMessagesSorted(messages, false, true);
  
        if (!this.state.pinnedMessages) {
          this.state.pinnedMessages = [];
        }
  
        this.state.addPinnedMessages(state.pinned_messages || []);
        this.state.watcher_count = state.watcher_count || 0; // convert the arrays into objects for easier syncing...
  
        if (state.watchers) {
          var _iterator2 = _createForOfIteratorHelper$1(state.watchers),
              _step2;
  
          try {
            for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
              var watcher = _step2.value;
  
              if (watcher) {
                clientState.updateUserReference(watcher, this.cid);
                this.state.watchers[watcher.id] = watcher;
              }
            }
          } catch (err) {
            _iterator2.e(err);
          } finally {
            _iterator2.f();
          }
        } // initialize read state to last message or current time if the channel is empty
        // if the user is a member, this value will be overwritten later on otherwise this ensures
        // that everything up to this point is not marked as unread
  
  
        if (userID != null) {
          var last_read = this.state.last_message_at || new Date();
  
          if (user) {
            this.state.read[user.id] = {
              user: user,
              last_read: last_read
            };
          }
        } // apply read state if part of the state
  
  
        if (state.read) {
          var _iterator3 = _createForOfIteratorHelper$1(state.read),
              _step3;
  
          try {
            for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
              var read = _step3.value;
  
              var parsedRead = _objectSpread$2(_objectSpread$2({}, read), {}, {
                last_read: new Date(read.last_read)
              });
  
              this.state.read[read.user.id] = parsedRead;
  
              if (read.user.id === (user === null || user === void 0 ? void 0 : user.id) && typeof parsedRead.unread_messages === 'number') {
                this.state.unreadCount = parsedRead.unread_messages;
              }
            }
          } catch (err) {
            _iterator3.e(err);
          } finally {
            _iterator3.f();
          }
        }
  
        if (state.members) {
          var _iterator4 = _createForOfIteratorHelper$1(state.members),
              _step4;
  
          try {
            for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
              var _member = _step4.value;
  
              if (_member.user) {
                this.state.members[_member.user.id] = _member;
              }
            }
          } catch (err) {
            _iterator4.e(err);
          } finally {
            _iterator4.f();
          }
        }
      }
    }, {
      key: "_disconnect",
      value: function _disconnect() {
        this._client.logger('info', "channel:disconnect() - Disconnecting the channel ".concat(this.cid), {
          tags: ['connection', 'channel'],
          channel: this
        });
  
        this.disconnected = true;
      }
    }]);
  
    return Channel;
  }();
  
  function _createForOfIteratorHelper$2(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray$2(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
  
  function _unsupportedIterableToArray$2(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray$2(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$2(o, minLen); }
  
  function _arrayLikeToArray$2(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
  
  /**
   * ClientState - A container class for the client state.
   */
  var ClientState = /*#__PURE__*/function () {
    function ClientState() {
      _classCallCheck(this, ClientState);
  
      _defineProperty(this, "users", void 0);
  
      _defineProperty(this, "userChannelReferences", void 0);
  
      // show the status for a certain user...
      // ie online, offline etc
      this.users = {}; // store which channels contain references to the specified user...
  
      this.userChannelReferences = {};
    }
  
    _createClass(ClientState, [{
      key: "updateUsers",
      value: function updateUsers(users) {
        var _iterator = _createForOfIteratorHelper$2(users),
            _step;
  
        try {
          for (_iterator.s(); !(_step = _iterator.n()).done;) {
            var user = _step.value;
            this.updateUser(user);
          }
        } catch (err) {
          _iterator.e(err);
        } finally {
          _iterator.f();
        }
      }
    }, {
      key: "updateUser",
      value: function updateUser(user) {
        if (user != null) {
          this.users[user.id] = user;
        }
      }
    }, {
      key: "updateUserReference",
      value: function updateUserReference(user, channelID) {
        if (user == null) {
          return;
        }
  
        this.updateUser(user);
  
        if (!this.userChannelReferences[user.id]) {
          this.userChannelReferences[user.id] = {};
        }
  
        this.userChannelReferences[user.id][channelID] = true;
      }
    }]);
  
    return ClientState;
  }();
  
  // Type guards to check WebSocket error type
  var isCloseEvent = function isCloseEvent(res) {
    return res.code !== undefined;
  };
  
  var isErrorEvent = function isErrorEvent(res) {
    return res.error !== undefined;
  };
  
  /**
   * StableWSConnection - A WS connection that reconnects upon failure.
   * - the browser will sometimes report that you're online or offline
   * - the WS connection can break and fail (there is a 30s health check)
   * - sometimes your WS connection will seem to work while the user is in fact offline
   * - to speed up online/offline detection you can use the window.addEventListener('offline');
   *
   * There are 4 ways in which a connection can become unhealthy:
   * - websocket.onerror is called
   * - websocket.onclose is called
   * - the health check fails and no event is received for ~40 seconds
   * - the browser indicates the connection is now offline
   *
   * There are 2 assumptions we make about the server:
   * - state can be recovered by querying the channel again
   * - if the servers fails to publish a message to the client, the WS connection is destroyed
   */
  var StableWSConnection = /*#__PURE__*/function () {
    function StableWSConnection(_ref) {
      var _this = this;
  
      var apiKey = _ref.apiKey,
          authType = _ref.authType,
          clientID = _ref.clientID,
          eventCallback = _ref.eventCallback,
          logger = _ref.logger,
          messageCallback = _ref.messageCallback,
          recoverCallback = _ref.recoverCallback,
          tokenManager = _ref.tokenManager,
          user = _ref.user,
          userAgent = _ref.userAgent,
          userID = _ref.userID,
          wsBaseURL = _ref.wsBaseURL;
  
      _classCallCheck(this, StableWSConnection);
  
      _defineProperty(this, "apiKey", void 0);
  
      _defineProperty(this, "authType", void 0);
  
      _defineProperty(this, "clientID", void 0);
  
      _defineProperty(this, "eventCallback", void 0);
  
      _defineProperty(this, "logger", void 0);
  
      _defineProperty(this, "messageCallback", void 0);
  
      _defineProperty(this, "recoverCallback", void 0);
  
      _defineProperty(this, "tokenManager", void 0);
  
      _defineProperty(this, "user", void 0);
  
      _defineProperty(this, "userAgent", void 0);
  
      _defineProperty(this, "userID", void 0);
  
      _defineProperty(this, "wsBaseURL", void 0);
  
      _defineProperty(this, "connectionID", void 0);
  
      _defineProperty(this, "connectionOpen", void 0);
  
      _defineProperty(this, "consecutiveFailures", void 0);
  
      _defineProperty(this, "healthCheckInterval", void 0);
  
      _defineProperty(this, "healthCheckIntervalRef", void 0);
  
      _defineProperty(this, "isConnecting", void 0);
  
      _defineProperty(this, "isHealthy", void 0);
  
      _defineProperty(this, "isResolved", void 0);
  
      _defineProperty(this, "lastEvent", void 0);
  
      _defineProperty(this, "monitorInterval", void 0);
  
      _defineProperty(this, "monitorIntervalRef", void 0);
  
      _defineProperty(this, "rejectPromise", void 0);
  
      _defineProperty(this, "resolvePromise", void 0);
  
      _defineProperty(this, "totalFailures", void 0);
  
      _defineProperty(this, "ws", void 0);
  
      _defineProperty(this, "wsID", void 0);
  
      _defineProperty(this, "_buildUrl", function () {
        var params = {
          user_id: _this.user.id,
          user_details: _this.user,
          user_token: _this.tokenManager.getToken(),
          server_determines_connection_id: true
        };
        var qs = encodeURIComponent(JSON.stringify(params));
  
        var token = _this.tokenManager.getToken();
  
        return "".concat(_this.wsBaseURL, "/connect?json=").concat(qs, "&api_key=").concat(_this.apiKey, "&authorization=").concat(token, "&stream-auth-type=").concat(_this.authType, "&x-stream-client=").concat(_this.userAgent);
      });
  
      _defineProperty(this, "onlineStatusChanged", function (event) {
        if (event.type === 'offline') {
          // mark the connection as down
          _this.logger('info', 'connection:onlineStatusChanged() - Status changing to offline', {
            tags: ['connection']
          });
  
          _this._setHealth(false);
        } else if (event.type === 'online') {
          // retry right now...
          // We check this.isHealthy, not sure if it's always
          // smart to create a new WS connection if the old one is still up and running.
          // it's possible we didn't miss any messages, so this process is just expensive and not needed.
          _this.logger('info', "connection:onlineStatusChanged() - Status changing to online. isHealthy: ".concat(_this.isHealthy), {
            tags: ['connection']
          });
  
          if (!_this.isHealthy) {
            _this._reconnect({
              interval: 10
            });
          }
        }
      });
  
      _defineProperty(this, "onopen", function (wsID) {
        if (_this.wsID !== wsID) return;
  
        _this.logger('info', 'connection:onopen() - onopen callback', {
          tags: ['connection'],
          wsID: wsID
        }); // set healthy..
  
  
        _this._setHealth(true);
      });
  
      _defineProperty(this, "onmessage", function (wsID, event) {
        if (_this.wsID !== wsID) return; // we wait till the first message before we consider the connection open..
        // the reason for this is that auth errors and similar errors trigger a ws.onopen and immediately
        // after that a ws.onclose..
  
        if (!_this.isResolved && typeof event.data === 'string') {
          var data = JSON.parse(event.data);
  
          if (data.error != null) {
            var _this$rejectPromise;
  
            (_this$rejectPromise = _this.rejectPromise) === null || _this$rejectPromise === void 0 ? void 0 : _this$rejectPromise.call(_this, _this._errorFromWSEvent(data, false));
            return;
          } else {
            var _this$resolvePromise;
  
            (_this$resolvePromise = _this.resolvePromise) === null || _this$resolvePromise === void 0 ? void 0 : _this$resolvePromise.call(_this, event);
          }
        } // trigger the event..
  
  
        _this.lastEvent = new Date();
  
        _this.logger('info', 'connection:onmessage() - onmessage callback', {
          tags: ['connection'],
          event: event,
          wsID: wsID
        });
  
        _this.messageCallback(event);
      });
  
      _defineProperty(this, "onclose", function (wsID, event) {
        _this.logger('info', 'connection:onclose() - onclose callback - ' + event.code, {
          tags: ['connection'],
          event: event,
          wsID: wsID
        });
  
        if (_this.wsID !== wsID) return;
  
        if (event.code === chatCodes.WS_CLOSED_SUCCESS) {
          var _this$rejectPromise2;
  
          // this is a permanent error raised by stream..
          // usually caused by invalid auth details
          var error = new Error("WS connection reject with error ".concat(event.reason));
          error.reason = event.reason;
          (_this$rejectPromise2 = _this.rejectPromise) === null || _this$rejectPromise2 === void 0 ? void 0 : _this$rejectPromise2.call(_this, error);
  
          _this.logger('info', "connection:onclose() - WS connection reject with error ".concat(event.reason), {
            tags: ['connection'],
            event: event
          });
        } else {
          var _this$rejectPromise3;
  
          _this.consecutiveFailures += 1;
          _this.totalFailures += 1;
  
          _this._setHealth(false);
  
          (_this$rejectPromise3 = _this.rejectPromise) === null || _this$rejectPromise3 === void 0 ? void 0 : _this$rejectPromise3.call(_this, _this._errorFromWSEvent(event));
  
          _this.logger('info', "connection:onclose() - WS connection closed. Calling reconnect ...", {
            tags: ['connection'],
            event: event
          }); // reconnect if its an abnormal failure
  
  
          _this._reconnect();
        }
      });
  
      _defineProperty(this, "onerror", function (wsID, event) {
        var _this$rejectPromise4;
  
        if (_this.wsID !== wsID) return;
        _this.consecutiveFailures += 1;
        _this.totalFailures += 1;
  
        _this._setHealth(false);
  
        (_this$rejectPromise4 = _this.rejectPromise) === null || _this$rejectPromise4 === void 0 ? void 0 : _this$rejectPromise4.call(_this, _this._errorFromWSEvent(event));
  
        _this.logger('info', "connection:onerror() - WS connection resulted into error", {
          tags: ['connection'],
          event: event
        });
  
        _this._reconnect();
      });
  
      _defineProperty(this, "_setHealth", function (healthy) {
        if (healthy && !_this.isHealthy) {
          // yes we are online:
          _this.isHealthy = true;
  
          _this.eventCallback({
            type: 'connection.changed',
            online: true
          });
        }
  
        if (!healthy && _this.isHealthy) {
          // bummer we are offline
          _this.isHealthy = false;
          setTimeout(function () {
            if (!_this.isHealthy) {
              _this.eventCallback({
                type: 'connection.changed',
                online: false
              });
            }
          }, 5000);
        }
      });
  
      _defineProperty(this, "_errorFromWSEvent", function (event) {
        var isWSFailure = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
        var code;
        var statusCode;
        var message;
  
        if (isCloseEvent(event)) {
          code = event.code;
          statusCode = 'unknown';
          message = event.reason;
        }
  
        if (isErrorEvent(event)) {
          code = event.error.code;
          statusCode = event.error.StatusCode;
          message = event.error.message;
        } // Keeping this `warn` level log, to avoid cluttering of error logs from ws failures.
  
  
        _this.logger('warn', "connection:_errorFromWSEvent() - WS failed with code ".concat(code), {
          tags: ['connection'],
          event: event
        });
  
        var error = new Error("WS failed with code ".concat(code, " and reason - ").concat(message));
        error.code = code;
        /**
         * StatusCode does not exist on any event types but has been left
         * as is to preserve JS functionality during the TS implementation
         */
  
        error.StatusCode = statusCode;
        error.isWSFailure = isWSFailure;
        return error;
      });
  
      _defineProperty(this, "_listenForConnectionChanges", function () {
        if (typeof window !== 'undefined' && window != null && window.addEventListener != null) {
          window.addEventListener('offline', _this.onlineStatusChanged);
          window.addEventListener('online', _this.onlineStatusChanged);
        }
      });
  
      _defineProperty(this, "_removeConnectionListeners", function () {
        if (typeof window !== 'undefined' && window != null && window.addEventListener != null) {
          window.removeEventListener('offline', _this.onlineStatusChanged);
          window.removeEventListener('online', _this.onlineStatusChanged);
        }
      });
  
      _defineProperty(this, "_retryInterval", function () {
        // try to reconnect in 0.25-25 seconds (random to spread out the load from failures)
        var max = Math.min(500 + _this.consecutiveFailures * 2000, 25000);
        var min = Math.min(Math.max(250, (_this.consecutiveFailures - 1) * 2000), 25000);
        return Math.floor(Math.random() * (max - min) + min);
      });
  
      _defineProperty(this, "_setupConnectionPromise", function () {
        var that = _this;
        _this.isResolved = false;
        /** a promise that is resolved once ws.open is called */
  
        _this.connectionOpen = new Promise(function (resolve, reject) {
          that.resolvePromise = resolve;
          that.rejectPromise = reject;
        }).then(function (e) {
          if (e.data && typeof e.data === 'string') {
            var data = JSON.parse(e.data);
  
            if (data && data.error != null) {
              throw new Error(JSON.stringify(data.error));
            }
  
            return data;
          } else {
            return undefined;
          }
        }, function (error) {
          throw error;
        });
      });
  
      this.wsBaseURL = wsBaseURL;
      this.clientID = clientID;
      this.userID = userID;
      this.user = user;
      this.authType = authType;
      this.userAgent = userAgent;
      this.apiKey = apiKey;
      this.tokenManager = tokenManager;
      /** consecutive failures influence the duration of the timeout */
  
      this.consecutiveFailures = 0;
      /** keep track of the total number of failures */
  
      this.totalFailures = 0;
      /** We only make 1 attempt to reconnect at the same time.. */
  
      this.isConnecting = false;
      /** Boolean that indicates if we have a working connection to the server */
  
      this.isHealthy = false;
      /** Callback when the connection fails and recovers */
  
      this.recoverCallback = recoverCallback;
      this.messageCallback = messageCallback;
      this.eventCallback = eventCallback;
      this.logger = logger;
      /** Incremented when a new WS connection is made */
  
      this.wsID = 1;
      /** Store the last event time for health checks */
  
      this.lastEvent = null;
      /** Send a health check message every 30 seconds */
  
      this.healthCheckInterval = 30 * 1000;
      /** Every second we verify that we didn't miss any health checks */
  
      this.monitorInterval = 1 * 1000;
  
      this._listenForConnectionChanges();
    }
    /**
     * connect - Connect to the WS URL
     *
     * @return {ConnectAPIResponse<ChannelType, CommandType, UserType>} Promise that completes once the first health check message is received
     */
  
  
    _createClass(StableWSConnection, [{
      key: "connect",
      value: function () {
        var _connect2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
          var healthCheck;
          return _regeneratorRuntime.wrap(function _callee$(_context) {
            while (1) {
              switch (_context.prev = _context.next) {
                case 0:
                  if (!this.isConnecting) {
                    _context.next = 2;
                    break;
                  }
  
                  throw Error("You've called connect twice, can only attempt 1 connection at the time");
  
                case 2:
                  _context.prev = 2;
                  this.isConnecting = true;
                  _context.next = 6;
                  return this._connect();
  
                case 6:
                  healthCheck = _context.sent;
                  this.isConnecting = false;
                  this.consecutiveFailures = 0;
  
                  this._startMonitor();
  
                  this._startHealthCheck();
  
                  this.logger('info', "connection:connect() - Established ws connection with healthcheck: ".concat(healthCheck), {
                    tags: ['connection']
                  });
                  return _context.abrupt("return", healthCheck);
  
                case 15:
                  _context.prev = 15;
                  _context.t0 = _context["catch"](2);
                  this.isConnecting = false;
                  this.isHealthy = false;
                  this.consecutiveFailures += 1;
  
                  if (!(_context.t0.code === chatCodes.TOKEN_EXPIRED && !this.tokenManager.isStatic())) {
                    _context.next = 23;
                    break;
                  }
  
                  this.logger('info', 'connection:connect() - WS failure due to expired token, so going to try to reload token and reconnect', {
                    tags: ['connection']
                  });
                  return _context.abrupt("return", this._reconnect({
                    refreshToken: true
                  }));
  
                case 23:
                  if (_context.t0.isWSFailure) {
                    _context.next = 25;
                    break;
                  }
  
                  throw new Error(JSON.stringify({
                    code: _context.t0.code,
                    StatusCode: _context.t0.StatusCode,
                    message: _context.t0.message,
                    isWSFailure: _context.t0.isWSFailure
                  }));
  
                case 25:
                case "end":
                  return _context.stop();
              }
            }
          }, _callee, this, [[2, 15]]);
        }));
  
        function connect() {
          return _connect2.apply(this, arguments);
        }
  
        return connect;
      }()
    }, {
      key: "disconnect",
      value:
      /**
       * disconnect - Disconnect the connection and doesn't recover...
       *
       */
      function disconnect(timeout) {
        var _this2 = this;
  
        this.logger('info', "connection:disconnect() - Closing the websocket connection for wsID ".concat(this.wsID), {
          tags: ['connection']
        });
        this.wsID += 1; // start by removing all the listeners
  
        if (this.healthCheckIntervalRef) {
          clearInterval(this.healthCheckIntervalRef);
        }
  
        if (this.monitorIntervalRef) {
          clearInterval(this.monitorIntervalRef);
        }
  
        this._removeConnectionListeners();
  
        this.isHealthy = false; // remove ws handlers...
  
        if (this.ws && this.ws.removeAllListeners) {
          this.ws.removeAllListeners();
        }
  
        var isClosedPromise; // and finally close...
        // Assigning to local here because we will remove it from this before the
        // promise resolves.
  
        var ws = this.ws;
  
        if (ws && ws.close && ws.readyState === ws.OPEN) {
          isClosedPromise = new Promise(function (resolve) {
            var onclose = function onclose(event) {
              _this2.logger('info', "connection:disconnect() - resolving isClosedPromise ".concat(event ? 'with' : 'without', " close frame"), {
                tags: ['connection'],
                event: event
              });
  
              resolve();
            };
  
            ws.onclose = onclose; // In case we don't receive close frame websocket server in time,
            // lets not wait for more than 1 seconds.
  
            setTimeout(onclose, timeout != null ? timeout : 1000);
          });
          this.logger('info', "connection:disconnect() - Manually closed connection by calling client.disconnect()", {
            tags: ['connection']
          });
          ws.close(chatCodes.WS_CLOSED_SUCCESS, 'Manually closed connection by calling client.disconnect()');
        } else {
          this.logger('info', "connection:disconnect() - ws connection doesn't exist or it is already closed.", {
            tags: ['connection']
          });
          isClosedPromise = Promise.resolve();
        }
  
        delete this.ws;
        return isClosedPromise;
      }
      /**
       * _connect - Connect to the WS endpoint
       *
       * @return {ConnectAPIResponse<ChannelType, CommandType, UserType>} Promise that completes once the first health check message is received
       */
  
    }, {
      key: "_connect",
      value: function () {
        var _connect3 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
          var wsURL, response;
          return _regeneratorRuntime.wrap(function _callee2$(_context2) {
            while (1) {
              switch (_context2.prev = _context2.next) {
                case 0:
                  _context2.next = 2;
                  return this.tokenManager.tokenReady();
  
                case 2:
                  this._setupConnectionPromise();
  
                  wsURL = this._buildUrl();
                  this.ws = new WebSocket(wsURL);
                  this.ws.onopen = this.onopen.bind(this, this.wsID);
                  this.ws.onclose = this.onclose.bind(this, this.wsID);
                  this.ws.onerror = this.onerror.bind(this, this.wsID);
                  this.ws.onmessage = this.onmessage.bind(this, this.wsID);
                  _context2.next = 11;
                  return this.connectionOpen;
  
                case 11:
                  response = _context2.sent;
  
                  if (!response) {
                    _context2.next = 15;
                    break;
                  }
  
                  this.connectionID = response.connection_id;
                  return _context2.abrupt("return", response);
  
                case 15:
                  return _context2.abrupt("return", undefined);
  
                case 16:
                case "end":
                  return _context2.stop();
              }
            }
          }, _callee2, this);
        }));
  
        function _connect() {
          return _connect3.apply(this, arguments);
        }
  
        return _connect;
      }()
      /**
       * _reconnect - Retry the connection to WS endpoint
       *
       * @param {{ interval?: number; refreshToken?: boolean }} options Following options are available
       *
       * - `interval`	{int}			number of ms that function should wait before reconnecting
       * - `refreshToken` {boolean}	reload/refresh user token be refreshed before attempting reconnection.
       */
  
    }, {
      key: "_reconnect",
      value: function () {
        var _reconnect2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3() {
          var options,
              interval,
              _open,
              _args3 = arguments;
  
          return _regeneratorRuntime.wrap(function _callee3$(_context3) {
            while (1) {
              switch (_context3.prev = _context3.next) {
                case 0:
                  options = _args3.length > 0 && _args3[0] !== undefined ? _args3[0] : {};
                  this.logger('info', 'connection:_reconnect() - Initiating the reconnect', {
                    tags: ['connection']
                  }); // only allow 1 connection at the time
  
                  if (!(this.isConnecting || this.isHealthy)) {
                    _context3.next = 5;
                    break;
                  }
  
                  this.logger('info', 'connection:_reconnect() - Abort (1) since already connecting or healthy', {
                    tags: ['connection']
                  });
                  return _context3.abrupt("return");
  
                case 5:
                  // reconnect in case of on error or on close
                  // also reconnect if the health check cycle fails
                  interval = options.interval;
  
                  if (!interval) {
                    interval = this._retryInterval();
                  } // reconnect, or try again after a little while...
  
  
                  _context3.next = 9;
                  return sleep(interval);
  
                case 9:
                  if (!(this.isConnecting || this.isHealthy)) {
                    _context3.next = 12;
                    break;
                  }
  
                  this.logger('info', 'connection:_reconnect() - Abort (2) since already connecting or healthy', {
                    tags: ['connection']
                  });
                  return _context3.abrupt("return");
  
                case 12:
                  this.isConnecting = true; // cleanup the old connection
  
                  this.logger('info', 'connection:_reconnect() - Destroying current WS connection', {
                    tags: ['connection']
                  });
  
                  this._destroyCurrentWSConnection();
  
                  if (!options.refreshToken) {
                    _context3.next = 18;
                    break;
                  }
  
                  _context3.next = 18;
                  return this.tokenManager.loadToken();
  
                case 18:
                  _context3.prev = 18;
                  _context3.next = 21;
                  return this._connect();
  
                case 21:
                  _open = _context3.sent;
  
                  if (!this.recoverCallback) {
                    _context3.next = 27;
                    break;
                  }
  
                  this.logger('info', 'connection:_reconnect() - Waiting for recoverCallBack', {
                    tags: ['connection']
                  });
                  _context3.next = 26;
                  return this.recoverCallback(_open);
  
                case 26:
                  this.logger('info', 'connection:_reconnect() - Finished recoverCallBack', {
                    tags: ['connection']
                  });
  
                case 27:
                  this.isConnecting = false;
                  this.consecutiveFailures = 0;
                  _context3.next = 40;
                  break;
  
                case 31:
                  _context3.prev = 31;
                  _context3.t0 = _context3["catch"](18);
                  this.isConnecting = false;
                  this.isHealthy = false;
                  this.consecutiveFailures += 1;
  
                  if (!(_context3.t0.code === chatCodes.TOKEN_EXPIRED && !this.tokenManager.isStatic())) {
                    _context3.next = 39;
                    break;
                  }
  
                  this.logger('info', 'connection:_reconnect() - WS failure due to expired token, so going to try to reload token and reconnect', {
                    tags: ['connection']
                  });
                  return _context3.abrupt("return", this._reconnect({
                    refreshToken: true
                  }));
  
                case 39:
                  // reconnect on WS failures, don't reconnect if there is a code bug
                  if (_context3.t0.isWSFailure) {
                    this.logger('info', 'connection:_reconnect() - WS failure, so going to try to reconnect', {
                      tags: ['connection']
                    });
  
                    this._reconnect();
                  }
  
                case 40:
                  this.logger('info', 'connection:_reconnect() - == END ==', {
                    tags: ['connection']
                  });
  
                case 41:
                case "end":
                  return _context3.stop();
              }
            }
          }, _callee3, this, [[18, 31]]);
        }));
  
        function _reconnect() {
          return _reconnect2.apply(this, arguments);
        }
  
        return _reconnect;
      }()
      /**
       * onlineStatusChanged - this function is called when the browser connects or disconnects from the internet.
       *
       * @param {Event} event Event with type online or offline
       *
       */
  
    }, {
      key: "_destroyCurrentWSConnection",
      value:
      /**
       * _destroyCurrentWSConnection - Removes the current WS connection
       *
       */
      function _destroyCurrentWSConnection() {
        // increment the ID, meaning we will ignore all messages from the old
        // ws connection from now on.
        this.wsID += 1;
  
        try {
          if (this.ws && this.ws.removeAllListeners) {
            this.ws.removeAllListeners();
          }
  
          if (this.ws && this.ws.close) {
            this.ws.close();
          }
        } catch (e) {// we don't care
        }
      }
      /**
       * _retryInterval - A retry interval which increases after consecutive failures
       *
       * @return {number} Duration to wait in milliseconds
       */
  
    }, {
      key: "_startHealthCheck",
      value:
      /**
       * _startHealthCheck - Sends a message every 30s or so to see if the ws connection still works
       *
       */
      function _startHealthCheck() {
        var that = this; // 30 seconds is the recommended interval (messenger uses this)
  
        this.healthCheckIntervalRef = setInterval(function () {
          // send the healthcheck.., server replies with a health check event
          var data = [{
            type: 'health.check',
            client_id: that.clientID,
            user_id: that.userID
          }]; // try to send on the connection
  
          try {
            var _that$ws;
  
            (_that$ws = that.ws) === null || _that$ws === void 0 ? void 0 : _that$ws.send(JSON.stringify(data));
          } catch (e) {// error will already be detected elsewhere
          }
        }, that.healthCheckInterval);
      }
      /**
       * _startMonitor - Verifies we didn't miss any events. Marks the connection as failed in case we did.
       *
       */
  
    }, {
      key: "_startMonitor",
      value: function _startMonitor() {
        var _this3 = this;
  
        var that = this;
        this.monitorIntervalRef = setInterval(function () {
          var now = new Date(); // means we missed a health check
  
          if (that.lastEvent && now.getTime() - that.lastEvent.getTime() > _this3.healthCheckInterval + 10 * 1000) {
            _this3.logger('info', 'connection:_startMonitor - going to reconnect', {
              tags: ['connection']
            });
  
            that._setHealth(false);
  
            that._reconnect();
          }
        }, that.monitorInterval);
      }
    }]);
  
    return StableWSConnection;
  }();
  
  var jwt = null;
  
  var crypto = null;
  
  function ownKeys$3(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
  
  function _objectSpread$3(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys$3(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys$3(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
  
  /**
   * Creates the JWT token that can be used for a UserSession
   * @method JWTUserToken
   * @memberof signing
   * @private
   * @param {Secret} apiSecret - API Secret key
   * @param {string} userId - The user_id key in the JWT payload
   * @param {UnknownType} [extraData] - Extra that should be part of the JWT token
   * @param {SignOptions} [jwtOptions] - Options that can be past to jwt.sign
   * @return {string} JWT Token
   */
  function JWTUserToken(apiSecret, userId) {
    var extraData = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
    var jwtOptions = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
  
    if (typeof userId !== 'string') {
      throw new TypeError('userId should be a string');
    }
  
    var payload = _objectSpread$3({
      user_id: userId
    }, extraData); // make sure we return a clear error when jwt is shimmed (ie. browser build)
  
  
    {
      throw Error("Unable to find jwt crypto, if you are getting this error is probably because you are trying to generate tokens on browser or React Native (or other environment where crypto functions are not available). Please Note: token should only be generated server-side.");
    }
  
    var opts = _extends({
      algorithm: 'HS256',
      noTimestamp: true
    }, jwtOptions);
  }
  function JWTServerToken(apiSecret) {
    var jwtOptions = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    var payload = {
      server: true
    };
  
    var opts = _extends({
      algorithm: 'HS256',
      noTimestamp: true
    }, jwtOptions);
  
    return jwt.sign(payload, apiSecret, opts);
  }
  function UserFromToken(token) {
    var fragments = token.split('.');
  
    if (fragments.length !== 3) {
      return '';
    }
  
    var b64Payload = fragments[1];
    var payload = decodeBase64(b64Payload);
    var data = JSON.parse(payload);
    return data.user_id;
  }
  /**
   *
   * @param {string} userId the id of the user
   * @return {string}
   */
  
  function DevToken(userId) {
    return ['eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9', //{"alg": "HS256", "typ": "JWT"}
    encodeBase64(JSON.stringify({
      user_id: userId
    })), 'devtoken' // hardcoded signature
    ].join('.');
  }
  /**
   *
   * @param {string} body the signed message
   * @param {string} secret the shared secret used to generate the signature (Stream API secret)
   * @param {string} signature the signature to validate
   * @return {boolean}
   */
  
  function CheckSignature(body, secret, signature) {
    var key = Buffer.from(secret, 'ascii');
    var hash = crypto.createHmac('sha256', key).update(body).digest('hex');
    return hash === signature;
  }
  
  /**
   * TokenManager
   *
   * Handles all the operations around user token.
   */
  var TokenManager =
  /**
   * Constructor
   *
   * @param {Secret} secret
   */
  function TokenManager(secret) {
    var _this = this;
  
    _classCallCheck(this, TokenManager);
  
    _defineProperty(this, "loadTokenPromise", void 0);
  
    _defineProperty(this, "type", void 0);
  
    _defineProperty(this, "secret", void 0);
  
    _defineProperty(this, "token", void 0);
  
    _defineProperty(this, "tokenProvider", void 0);
  
    _defineProperty(this, "user", void 0);
  
    _defineProperty(this, "setTokenOrProvider", /*#__PURE__*/function () {
      var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(tokenOrProvider, user) {
        return _regeneratorRuntime.wrap(function _callee$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                _this.validateToken(tokenOrProvider, user);
  
                _this.user = user;
  
                if (isFunction(tokenOrProvider)) {
                  _this.tokenProvider = tokenOrProvider;
                  _this.type = 'provider';
                }
  
                if (typeof tokenOrProvider === 'string') {
                  _this.token = tokenOrProvider;
                  _this.type = 'static';
                }
  
                if (!tokenOrProvider && _this.user && _this.secret) {
                  _this.token = JWTUserToken(_this.secret, user.id, {}, {});
                  _this.type = 'static';
                }
  
                _context.next = 7;
                return _this.loadToken();
  
              case 7:
              case "end":
                return _context.stop();
            }
          }
        }, _callee);
      }));
  
      return function (_x, _x2) {
        return _ref.apply(this, arguments);
      };
    }());
  
    _defineProperty(this, "reset", function () {
      _this.token = undefined;
      _this.user = undefined;
      _this.loadTokenPromise = null;
    });
  
    _defineProperty(this, "validateToken", function (tokenOrProvider, user) {
      // allow empty token for anon user
      if (user && user.anon && !tokenOrProvider) return; // Don't allow empty token for non-server side client.
  
      if (!_this.secret && !tokenOrProvider) {
        throw new Error('User token can not be empty');
      }
  
      if (tokenOrProvider && typeof tokenOrProvider !== 'string' && !isFunction(tokenOrProvider)) {
        throw new Error('user token should either be a string or a function');
      }
  
      if (typeof tokenOrProvider === 'string') {
        // Allow empty token for anonymous users
        if (user.anon && tokenOrProvider === '') return;
        var tokenUserId = UserFromToken(tokenOrProvider);
  
        if (tokenOrProvider != null && (tokenUserId == null || tokenUserId === '' || tokenUserId !== user.id)) {
          throw new Error('userToken does not have a user_id or is not matching with user.id');
        }
      }
    });
  
    _defineProperty(this, "tokenReady", function () {
      return _this.loadTokenPromise;
    });
  
    _defineProperty(this, "loadToken", function () {
      // eslint-disable-next-line no-async-promise-executor
      _this.loadTokenPromise = new Promise( /*#__PURE__*/function () {
        var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(resolve) {
          return _regeneratorRuntime.wrap(function _callee2$(_context2) {
            while (1) {
              switch (_context2.prev = _context2.next) {
                case 0:
                  if (!(_this.type === 'static')) {
                    _context2.next = 2;
                    break;
                  }
  
                  return _context2.abrupt("return", resolve(_this.token));
  
                case 2:
                  if (!(_this.tokenProvider && typeof _this.tokenProvider !== 'string')) {
                    _context2.next = 7;
                    break;
                  }
  
                  _context2.next = 5;
                  return _this.tokenProvider();
  
                case 5:
                  _this.token = _context2.sent;
                  resolve(_this.token);
  
                case 7:
                case "end":
                  return _context2.stop();
              }
            }
          }, _callee2);
        }));
  
        return function (_x3) {
          return _ref2.apply(this, arguments);
        };
      }());
      return _this.loadTokenPromise;
    });
  
    _defineProperty(this, "getToken", function () {
      if (_this.token) {
        return _this.token;
      }
  
      if (_this.user && _this.user.anon && !_this.token) {
        return _this.token;
      }
  
      if (_this.secret) {
        return JWTServerToken(_this.secret);
      }
  
      throw new Error("Both secret and user tokens are not set. Either client.connectUser wasn't called or client.disconnect was called");
    });
  
    _defineProperty(this, "isStatic", function () {
      return _this.type === 'static';
    });
  
    this.loadTokenPromise = null;
  
    if (secret) {
      this.secret = secret;
    }
  
    this.type = 'static';
  
    if (this.secret) {
      this.token = JWTServerToken(this.secret);
    }
  }
  /**
   * Set the static string token or token provider.
   * Token provider should return a token string or a promise which resolves to string token.
   *
   * @param {TokenOrProvider} tokenOrProvider
   * @param {UserResponse<UserType>} user
   */
  ;
  
  function _createForOfIteratorHelper$3(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray$3(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
  
  function _unsupportedIterableToArray$3(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray$3(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$3(o, minLen); }
  
  function _arrayLikeToArray$3(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
  
  function ownKeys$4(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
  
  function _objectSpread$4(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys$4(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys$4(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
  
  function isString$1(x) {
    return typeof x === 'string' || x instanceof String;
  }
  
  var StreamChat = /*#__PURE__*/function () {
    function StreamChat(_key, secretOrOptions, _options) {
      var _this = this;
  
      _classCallCheck(this, StreamChat);
  
      _defineProperty(this, "_user", void 0);
  
      _defineProperty(this, "activeChannels", void 0);
  
      _defineProperty(this, "anonymous", void 0);
  
      _defineProperty(this, "axiosInstance", void 0);
  
      _defineProperty(this, "baseURL", void 0);
  
      _defineProperty(this, "browser", void 0);
  
      _defineProperty(this, "cleaningIntervalRef", void 0);
  
      _defineProperty(this, "clientID", void 0);
  
      _defineProperty(this, "configs", void 0);
  
      _defineProperty(this, "connecting", void 0);
  
      _defineProperty(this, "connectionEstablishedCount", void 0);
  
      _defineProperty(this, "connectionID", void 0);
  
      _defineProperty(this, "failures", void 0);
  
      _defineProperty(this, "key", void 0);
  
      _defineProperty(this, "listeners", void 0);
  
      _defineProperty(this, "logger", void 0);
  
      _defineProperty(this, "recoverStateOnReconnect", void 0);
  
      _defineProperty(this, "mutedChannels", void 0);
  
      _defineProperty(this, "mutedUsers", void 0);
  
      _defineProperty(this, "node", void 0);
  
      _defineProperty(this, "options", void 0);
  
      _defineProperty(this, "secret", void 0);
  
      _defineProperty(this, "setUserPromise", void 0);
  
      _defineProperty(this, "state", void 0);
  
      _defineProperty(this, "tokenManager", void 0);
  
      _defineProperty(this, "user", void 0);
  
      _defineProperty(this, "userAgent", void 0);
  
      _defineProperty(this, "userID", void 0);
  
      _defineProperty(this, "wsBaseURL", void 0);
  
      _defineProperty(this, "wsConnection", void 0);
  
      _defineProperty(this, "wsPromise", void 0);
  
      _defineProperty(this, "_setupConnection", function () {
        _this.clientID = "".concat(_this.userID, "--").concat(randomId());
        _this.wsPromise = _this.connect();
  
        _this._startCleaning();
  
        return _this.wsPromise;
      });
  
      _defineProperty(this, "_hasConnectionID", function () {
        return Boolean(_this.connectionID);
      });
  
      _defineProperty(this, "connectUser", function (user, userTokenOrProvider) {
        if (!user.id) {
          throw new Error('The "id" field on the user is missing');
        }
        /**
         * Calling connectUser multiple times is potentially the result of a  bad integration, however,
         * If the user id remains the same we don't throw error
         */
  
  
        if (_this.userID === user.id && _this.setUserPromise) {
          console.warn('Consecutive calls to connectUser is detected, ideally you should only call this function once in your app.');
          return _this.setUserPromise;
        }
  
        if (_this.userID) {
          throw new Error('Use client.disconnect() before trying to connect as a different user. connectUser was called twice.');
        }
  
        if ((_this._isUsingServerAuth() || _this.node) && !_this.options.allowServerSideConnect) {
          console.warn('Please do not use connectUser server side. connectUser impacts MAU and concurrent connection usage and thus your bill. If you have a valid use-case, add "allowServerSideConnect: true" to the client options to disable this warning.');
        } // we generate the client id client side
  
  
        _this.userID = user.id;
        _this.anonymous = false;
  
        var setTokenPromise = _this._setToken(user, userTokenOrProvider);
  
        _this._setUser(user);
  
        var wsPromise = _this._setupConnection();
  
        _this.setUserPromise = Promise.all([setTokenPromise, wsPromise]).then(function (result) {
          return result[1];
        } // We only return connection promise;
        );
        return _this.setUserPromise;
      });
  
      _defineProperty(this, "setUser", this.connectUser);
  
      _defineProperty(this, "_setToken", function (user, userTokenOrProvider) {
        return _this.tokenManager.setTokenOrProvider(userTokenOrProvider, user);
      });
  
      _defineProperty(this, "connectAnonymousUser", function () {
        if ((_this._isUsingServerAuth() || _this.node) && !_this.options.allowServerSideConnect) {
          console.warn('Please do not use connectUser server side. connectUser impacts MAU and concurrent connection usage and thus your bill. If you have a valid use-case, add "allowServerSideConnect: true" to the client options to disable this warning.');
        }
  
        _this.anonymous = true;
        _this.userID = randomId();
        var anonymousUser = {
          id: _this.userID,
          anon: true
        };
  
        _this._setToken(anonymousUser, '');
  
        _this._setUser(anonymousUser);
  
        return _this._setupConnection();
      });
  
      _defineProperty(this, "setAnonymousUser", this.connectAnonymousUser);
  
      _defineProperty(this, "doAxiosRequest", /*#__PURE__*/function () {
        var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(type, url, data) {
          var options,
              requestConfig,
              response,
              _args = arguments;
          return _regeneratorRuntime.wrap(function _callee$(_context) {
            while (1) {
              switch (_context.prev = _context.next) {
                case 0:
                  options = _args.length > 3 && _args[3] !== undefined ? _args[3] : {};
                  _context.next = 3;
                  return _this.tokenManager.tokenReady();
  
                case 3:
                  requestConfig = _this._enrichAxiosOptions(options);
                  _context.prev = 4;
  
                  _this._logApiRequest(type, url, data, requestConfig);
  
                  _context.t0 = type;
                  _context.next = _context.t0 === 'get' ? 9 : _context.t0 === 'delete' ? 13 : _context.t0 === 'post' ? 17 : _context.t0 === 'put' ? 21 : _context.t0 === 'patch' ? 25 : _context.t0 === 'options' ? 29 : 33;
                  break;
  
                case 9:
                  _context.next = 11;
                  return _this.axiosInstance.get(url, requestConfig);
  
                case 11:
                  response = _context.sent;
                  return _context.abrupt("break", 34);
  
                case 13:
                  _context.next = 15;
                  return _this.axiosInstance.delete(url, requestConfig);
  
                case 15:
                  response = _context.sent;
                  return _context.abrupt("break", 34);
  
                case 17:
                  _context.next = 19;
                  return _this.axiosInstance.post(url, data, requestConfig);
  
                case 19:
                  response = _context.sent;
                  return _context.abrupt("break", 34);
  
                case 21:
                  _context.next = 23;
                  return _this.axiosInstance.put(url, data, requestConfig);
  
                case 23:
                  response = _context.sent;
                  return _context.abrupt("break", 34);
  
                case 25:
                  _context.next = 27;
                  return _this.axiosInstance.patch(url, data, requestConfig);
  
                case 27:
                  response = _context.sent;
                  return _context.abrupt("break", 34);
  
                case 29:
                  _context.next = 31;
                  return _this.axiosInstance.options(url, requestConfig);
  
                case 31:
                  response = _context.sent;
                  return _context.abrupt("break", 34);
  
                case 33:
                  throw new Error('Invalid request type');
  
                case 34:
                  _this._logApiResponse(type, url, response);
  
                  return _context.abrupt("return", _this.handleResponse(response));
  
                case 38:
                  _context.prev = 38;
                  _context.t1 = _context["catch"](4);
  
                  _this._logApiError(type, url, _context.t1);
  
                  if (!_context.t1.response) {
                    _context.next = 50;
                    break;
                  }
  
                  if (!(_context.t1.response.data.code === chatCodes.TOKEN_EXPIRED && !_this.tokenManager.isStatic())) {
                    _context.next = 47;
                    break;
                  }
  
                  _this.tokenManager.loadToken();
  
                  _context.next = 46;
                  return _this.doAxiosRequest(type, url, data, options);
  
                case 46:
                  return _context.abrupt("return", _context.sent);
  
                case 47:
                  return _context.abrupt("return", _this.handleResponse(_context.t1.response));
  
                case 50:
                  throw _context.t1;
  
                case 51:
                case "end":
                  return _context.stop();
              }
            }
          }, _callee, null, [[4, 38]]);
        }));
  
        return function (_x, _x2, _x3) {
          return _ref.apply(this, arguments);
        };
      }());
  
      _defineProperty(this, "dispatchEvent", function (event) {
        // client event handlers
        _this._handleClientEvent(event); // channel event handlers
  
  
        var cid = event.cid;
        var channel = cid ? _this.activeChannels[cid] : undefined;
  
        if (channel) {
          channel._handleChannelEvent(event);
        }
  
        _this._callClientListeners(event);
  
        if (channel) {
          channel._callChannelListeners(event);
        }
      });
  
      _defineProperty(this, "handleEvent", function (messageEvent) {
        // dispatch the event to the channel listeners
        var jsonString = messageEvent.data;
        var event = JSON.parse(jsonString);
        event.received_at = new Date();
  
        _this.dispatchEvent(event);
      });
  
      _defineProperty(this, "_callClientListeners", function (event) {
        var client = _this; // gather and call the listeners
  
        var listeners = [];
  
        if (client.listeners.all) {
          listeners.push.apply(listeners, _toConsumableArray(client.listeners.all));
        }
  
        if (client.listeners[event.type]) {
          listeners.push.apply(listeners, _toConsumableArray(client.listeners[event.type]));
        } // call the event and send it to the listeners
  
  
        for (var _i = 0, _listeners = listeners; _i < _listeners.length; _i++) {
          var listener = _listeners[_i];
          listener(event);
        }
      });
  
      _defineProperty(this, "recoverState", /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
        var _this$wsConnection, _this$wsConnection2;
  
        var cids;
        return _regeneratorRuntime.wrap(function _callee2$(_context2) {
          while (1) {
            switch (_context2.prev = _context2.next) {
              case 0:
                _this.logger('info', "client:recoverState() - Start of recoverState with connectionID ".concat((_this$wsConnection = _this.wsConnection) === null || _this$wsConnection === void 0 ? void 0 : _this$wsConnection.connectionID), {
                  tags: ['connection']
                });
  
                _this.connectionID = (_this$wsConnection2 = _this.wsConnection) === null || _this$wsConnection2 === void 0 ? void 0 : _this$wsConnection2.connectionID;
                cids = Object.keys(_this.activeChannels);
  
                if (!(cids.length && _this.recoverStateOnReconnect)) {
                  _context2.next = 11;
                  break;
                }
  
                _this.logger('info', "client:recoverState() - Start the querying of ".concat(cids.length, " channels"), {
                  tags: ['connection', 'client']
                });
  
                _context2.next = 7;
                return _this.queryChannels({
                  cid: {
                    $in: cids
                  }
                }, {
                  last_message_at: -1
                }, {
                  limit: 30
                });
  
              case 7:
                _this.logger('info', 'client:recoverState() - Querying channels finished', {
                  tags: ['connection', 'client']
                });
  
                _this.dispatchEvent({
                  type: 'connection.recovered'
                });
  
                _context2.next = 12;
                break;
  
              case 11:
                _this.dispatchEvent({
                  type: 'connection.recovered'
                });
  
              case 12:
                _this.wsPromise = Promise.resolve();
                _this.setUserPromise = Promise.resolve();
  
              case 14:
              case "end":
                return _context2.stop();
            }
          }
        }, _callee2);
      })));
  
      _defineProperty(this, "getChannelByMembers", function (channelType, custom) {
        // Check if the channel already exists.
        // Only allow 1 channel object per cid
        var membersStr = _toConsumableArray(custom.members || []).sort().join(',');
  
        var tempCid = "".concat(channelType, ":!members-").concat(membersStr);
  
        if (!membersStr) {
          throw Error('Please specify atleast one member when creating unique conversation');
        } // channel could exist in `activeChannels` list with either one of the following two keys:
        // 1. cid - Which gets set on channel only after calling channel.query or channel.watch or channel.create
        // 2. Sorted membersStr - E.g., "messaging:amin,vishal" OR "messaging:amin,jaap,tom"
        //                        This is set when you create a channel, but haven't queried yet. After query,
        //                        we will replace it with `cid`
  
  
        for (var _key2 in _this.activeChannels) {
          var _channel = _this.activeChannels[_key2];
  
          if (_key2 === tempCid) {
            return _channel;
          }
  
          if (_key2.indexOf("".concat(channelType, ":!members-")) === 0) {
            var membersStrInExistingChannel = Object.keys(_channel.state.members).sort().join(',');
  
            if (membersStrInExistingChannel === membersStr) {
              return _channel;
            }
          }
        }
  
        var channel = new Channel(_this, channelType, undefined, custom); // For the time being set the key as membersStr, since we don't know the cid yet.
        // In channel.query, we will replace it with 'cid'.
  
        _this.activeChannels[tempCid] = channel;
        return channel;
      });
  
      _defineProperty(this, "getChannelById", function (channelType, channelID, custom) {
        if (typeof channelID === 'string' && ~channelID.indexOf(':')) {
          throw Error("Invalid channel id ".concat(channelID, ", can't contain the : character"));
        } // only allow 1 channel object per cid
  
  
        var cid = "".concat(channelType, ":").concat(channelID);
  
        if (cid in _this.activeChannels) {
          var _channel2 = _this.activeChannels[cid];
  
          if (Object.keys(custom).length > 0) {
            _channel2.data = custom;
            _channel2._data = custom;
          }
  
          return _channel2;
        }
  
        var channel = new Channel(_this, channelType, channelID, custom);
        _this.activeChannels[channel.cid] = channel;
        return channel;
      });
  
      _defineProperty(this, "updateUsers", this.upsertUsers);
  
      _defineProperty(this, "updateUser", this.upsertUser);
  
      _defineProperty(this, "_isUsingServerAuth", function () {
        return !!_this.secret;
      });
  
      // set the key
      this.key = _key;
      this.listeners = {};
      this.state = new ClientState(); // a list of channels to hide ws events from
  
      this.mutedChannels = [];
      this.mutedUsers = []; // set the secret
  
      if (secretOrOptions && isString$1(secretOrOptions)) {
        this.secret = secretOrOptions;
      } // set the options... and figure out defaults...
  
  
      var inputOptions = _options ? _options : secretOrOptions && !isString$1(secretOrOptions) ? secretOrOptions : {};
      this.browser = typeof inputOptions.browser !== 'undefined' ? inputOptions.browser : typeof window !== 'undefined';
      this.node = !this.browser;
      this.options = _objectSpread$4({
        timeout: 3000,
        withCredentials: false,
        // making sure cookies are not sent
        warmUp: false,
        recoverStateOnReconnect: true
      }, inputOptions);
  
      if (this.node) {
        this.options.httpsAgent = new https.Agent({
          keepAlive: true,
          keepAliveMsecs: 3000
        });
      }
  
      this.axiosInstance = axios.create(this.options);
      this.setBaseURL('https://chat-us-east-1.stream-io-api.com');
  
      if (typeof process !== 'undefined' && process.env.STREAM_LOCAL_TEST_RUN) {
        this.setBaseURL('http://localhost:3030');
      }
  
      if (typeof process !== 'undefined' && process.env.STREAM_LOCAL_TEST_HOST) {
        this.setBaseURL('http://' + process.env.STREAM_LOCAL_TEST_HOST);
      } // WS connection is initialized when setUser is called
  
  
      this.wsConnection = null;
      this.wsPromise = null;
      this.setUserPromise = null; // keeps a reference to all the channels that are in use
  
      this.activeChannels = {}; // mapping between channel groups and configs
  
      this.configs = {};
      this.anonymous = false; // If its a server-side client, then lets initialize the tokenManager, since token will be
      // generated from secret.
  
      this.tokenManager = new TokenManager(this.secret);
      /**
       * logger function should accept 3 parameters:
       * @param logLevel string
       * @param message   string
       * @param extraData object
       *
       * e.g.,
       * const client = new StreamChat('api_key', {}, {
       * 		logger = (logLevel, message, extraData) => {
       * 			console.log(message);
       * 		}
       * })
       *
       * extraData contains tags array attached to log message. Tags can have one/many of following values:
       * 1. api
       * 2. api_request
       * 3. api_response
       * 4. client
       * 5. channel
       * 6. connection
       * 7. event
       *
       * It may also contains some extra data, some examples have been mentioned below:
       * 1. {
       * 		tags: ['api', 'api_request', 'client'],
       * 		url: string,
       * 		payload: object,
       * 		config: object
       * }
       * 2. {
       * 		tags: ['api', 'api_response', 'client'],
       * 		url: string,
       * 		response: object
       * }
       * 3. {
       * 		tags: ['api', 'api_response', 'client'],
       * 		url: string,
       * 		error: object
       * }
       * 4. {
       * 		tags: ['event', 'client'],
       * 		event: object
       * }
       * 5. {
       * 		tags: ['channel'],
       * 		channel: object
       * }
       */
  
      this.logger = isFunction(inputOptions.logger) ? inputOptions.logger : function () {
        return null;
      };
      this.recoverStateOnReconnect = this.options.recoverStateOnReconnect;
    }
    /**
     * Get a client instance
     *
     * This function always returns the same Client instance to avoid issues raised by multiple Client and WS connections
     *
     * **After the first call, the client configuration will not change if the key or options parameters change**
     *
     * @param {string} key - the api key
     * @param {string} [secret] - the api secret
     * @param {StreamChatOptions} [options] - additional options, here you can pass custom options to axios instance
     * @param {boolean} [options.browser] - enforce the client to be in browser mode
     * @param {boolean} [options.warmUp] - default to false, if true, client will open a connection as soon as possible to speed up following requests
     * @param {Logger} [options.Logger] - custom logger
     * @param {number} [options.timeout] - default to 3000
     * @param {httpsAgent} [options.httpsAgent] - custom httpsAgent, in node it's default to https.agent()
     * @example <caption>initialize the client in user mode</caption>
     * StreamChat.getInstance('api_key')
     * @example <caption>initialize the client in user mode with options</caption>
     * StreamChat.getInstance('api_key', { timeout:5000 })
     * @example <caption>secret is optional and only used in server side mode</caption>
     * StreamChat.getInstance('api_key', "secret", { httpsAgent: customAgent })
     */
  
  
    _createClass(StreamChat, [{
      key: "devToken",
      value: function devToken(userID) {
        return DevToken(userID);
      }
    }, {
      key: "getAuthType",
      value: function getAuthType() {
        return this.anonymous ? 'anonymous' : 'jwt';
      }
    }, {
      key: "setBaseURL",
      value: function setBaseURL(baseURL) {
        this.baseURL = baseURL;
        this.wsBaseURL = this.baseURL.replace('http', 'ws');
      }
    }, {
      key: "_setUser",
      value: function _setUser(user) {
        // this one is used by the frontend
        this.user = user; // this one is actually used for requests...
  
        this._user = _objectSpread$4({}, user);
      }
      /**
      * updateAppSettings - updates application settings
      *
      * @param {AppSettings} options App settings.
      * 		IE: {
       			"apn_config": {
      			"auth_type": "token",
      			"auth_key": fs.readFileSync(
      				'./apn-push-auth-key.p8',
      				'utf-8',
      			),
      			"key_id": "keyid",
      			"team_id": "teamid", //either ALL these 3
      			"notification_template": "notification handlebars template",
      			"bundle_id": "com.apple.your.app",
      			"development": true
      		},
      		"firebase_config": {
      			"server_key": "server key from fcm",
      			"notification_template": "notification handlebars template"
      			"data_template": "data handlebars template"
      		},
      		"webhook_url": "https://acme.com/my/awesome/webhook/"
      	}
      */
  
    }, {
      key: "updateAppSettings",
      value: function () {
        var _updateAppSettings = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3(options) {
          var _options$apn_config;
  
          return _regeneratorRuntime.wrap(function _callee3$(_context3) {
            while (1) {
              switch (_context3.prev = _context3.next) {
                case 0:
                  if ((_options$apn_config = options.apn_config) !== null && _options$apn_config !== void 0 && _options$apn_config.p12_cert) {
                    options.apn_config.p12_cert = Buffer.from(options.apn_config.p12_cert).toString('base64');
                  }
  
                  _context3.next = 3;
                  return this.patch(this.baseURL + '/app', options);
  
                case 3:
                  return _context3.abrupt("return", _context3.sent);
  
                case 4:
                case "end":
                  return _context3.stop();
              }
            }
          }, _callee3, this);
        }));
  
        function updateAppSettings(_x4) {
          return _updateAppSettings.apply(this, arguments);
        }
  
        return updateAppSettings;
      }()
      /**
       * getAppSettings - retrieves application settings
       */
  
    }, {
      key: "getAppSettings",
      value: function () {
        var _getAppSettings = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4() {
          return _regeneratorRuntime.wrap(function _callee4$(_context4) {
            while (1) {
              switch (_context4.prev = _context4.next) {
                case 0:
                  _context4.next = 2;
                  return this.get(this.baseURL + '/app');
  
                case 2:
                  return _context4.abrupt("return", _context4.sent);
  
                case 3:
                case "end":
                  return _context4.stop();
              }
            }
          }, _callee4, this);
        }));
  
        function getAppSettings() {
          return _getAppSettings.apply(this, arguments);
        }
  
        return getAppSettings;
      }()
      /**
      * testPushSettings - Tests the push settings for a user with a random chat message and the configured push templates
      *
      * @param {string} userID User ID. If user has no devices, it will error
      * @param {TestPushDataInput} [data] Overrides for push templates/message used
      * 		IE: {
      		  messageID: 'id-of-message',//will error if message does not exist
      		  apnTemplate: '{}', //if app doesn't have apn configured it will error
      		  firebaseTemplate: '{}', //if app doesn't have firebase configured it will error
      		  firebaseDataTemplate: '{}', //if app doesn't have firebase configured it will error
      		  skipDevices: true, // skip config/device checks and sending to real devices
      	}
      */
  
    }, {
      key: "testPushSettings",
      value: function () {
        var _testPushSettings = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee5(userID) {
          var data,
              _args5 = arguments;
          return _regeneratorRuntime.wrap(function _callee5$(_context5) {
            while (1) {
              switch (_context5.prev = _context5.next) {
                case 0:
                  data = _args5.length > 1 && _args5[1] !== undefined ? _args5[1] : {};
                  _context5.next = 3;
                  return this.post(this.baseURL + '/check_push', _objectSpread$4(_objectSpread$4(_objectSpread$4(_objectSpread$4(_objectSpread$4({
                    user_id: userID
                  }, data.messageID ? {
                    message_id: data.messageID
                  } : {}), data.apnTemplate ? {
                    apn_template: data.apnTemplate
                  } : {}), data.firebaseTemplate ? {
                    firebase_template: data.firebaseTemplate
                  } : {}), data.firebaseDataTemplate ? {
                    firebase_data_template: data.firebaseDataTemplate
                  } : {}), data.skipDevices ? {
                    skip_devices: true
                  } : {}));
  
                case 3:
                  return _context5.abrupt("return", _context5.sent);
  
                case 4:
                case "end":
                  return _context5.stop();
              }
            }
          }, _callee5, this);
        }));
  
        function testPushSettings(_x5) {
          return _testPushSettings.apply(this, arguments);
        }
  
        return testPushSettings;
      }()
      /**
       * testSQSSettings - Tests that the given or configured SQS configuration is valid
       *
       * @param {string} userID User ID. If user has no devices, it will error
       * @param {TestPushDataInput} [data] Overrides for push templates/message used
       * 		IE: {
      		  messageID: 'id-of-message',//will error if message does not exist
      		  apnTemplate: '{}', //if app doesn't have apn configured it will error
      		  firebaseTemplate: '{}', //if app doesn't have firebase configured it will error
      		  firebaseDataTemplate: '{}', //if app doesn't have firebase configured it will error
      	}
       */
  
    }, {
      key: "testSQSSettings",
      value: function () {
        var _testSQSSettings = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee6() {
          var data,
              _args6 = arguments;
          return _regeneratorRuntime.wrap(function _callee6$(_context6) {
            while (1) {
              switch (_context6.prev = _context6.next) {
                case 0:
                  data = _args6.length > 0 && _args6[0] !== undefined ? _args6[0] : {};
                  _context6.next = 3;
                  return this.post(this.baseURL + '/check_sqs', data);
  
                case 3:
                  return _context6.abrupt("return", _context6.sent);
  
                case 4:
                case "end":
                  return _context6.stop();
              }
            }
          }, _callee6, this);
        }));
  
        function testSQSSettings() {
          return _testSQSSettings.apply(this, arguments);
        }
  
        return testSQSSettings;
      }()
      /**
       * disconnect - closes the WS connection
       */
  
    }, {
      key: "disconnect",
      value: function disconnect(timeout) {
        this.logger('info', 'client:disconnect() - Disconnecting the client', {
          tags: ['connection', 'client']
        }); // remove the user specific fields
  
        delete this.user;
        delete this._user;
        delete this.userID;
  
        if (this.cleaningIntervalRef != null) {
          clearInterval(this.cleaningIntervalRef);
          this.cleaningIntervalRef = undefined;
        }
  
        this.anonymous = false;
        this.connectionEstablishedCount = 0;
  
        for (var _i2 = 0, _Object$values = Object.values(this.activeChannels); _i2 < _Object$values.length; _i2++) {
          var _channel3 = _Object$values[_i2];
  
          _channel3._disconnect();
        } // ensure we no longer return inactive channels
  
  
        this.activeChannels = {}; // reset client state
  
        this.state = new ClientState(); // reset token manager
  
        this.tokenManager.reset(); // close the WS connection
  
        if (this.wsConnection) {
          return this.wsConnection.disconnect(timeout);
        }
  
        return Promise.resolve();
      }
      /**
       * connectAnonymousUser - Set an anonymous user and open a WebSocket connection
       */
  
    }, {
      key: "setGuestUser",
      value:
      /**
       * setGuestUser - Setup a temporary guest user
       *
       * @param {UserResponse<UserType>} user Data about this user. IE {name: "john"}
       *
       * @return {ConnectAPIResponse<ChannelType, CommandType, UserType>} Returns a promise that resolves when the connection is setup
       */
      function () {
        var _setGuestUser = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee7(user) {
          var response, _response$user, guestUser;
  
          return _regeneratorRuntime.wrap(function _callee7$(_context7) {
            while (1) {
              switch (_context7.prev = _context7.next) {
                case 0:
                  this.anonymous = true;
                  _context7.prev = 1;
                  _context7.next = 4;
                  return this.post(this.baseURL + '/guest', {
                    user: user
                  });
  
                case 4:
                  response = _context7.sent;
                  _context7.next = 11;
                  break;
  
                case 7:
                  _context7.prev = 7;
                  _context7.t0 = _context7["catch"](1);
                  this.anonymous = false;
                  throw _context7.t0;
  
                case 11:
                  this.anonymous = false; // eslint-disable-next-line @typescript-eslint/no-unused-vars
  
                  _response$user = response.user, _response$user.created_at, _response$user.updated_at, _response$user.last_active, _response$user.online, guestUser = _objectWithoutProperties(_response$user, ["created_at", "updated_at", "last_active", "online"]);
                  _context7.next = 15;
                  return this.connectUser(guestUser, response.access_token);
  
                case 15:
                  return _context7.abrupt("return", _context7.sent);
  
                case 16:
                case "end":
                  return _context7.stop();
              }
            }
          }, _callee7, this, [[1, 7]]);
        }));
  
        function setGuestUser(_x6) {
          return _setGuestUser.apply(this, arguments);
        }
  
        return setGuestUser;
      }()
      /**
       * createToken - Creates a token to authenticate this user. This function is used server side.
       * The resulting token should be passed to the client side when the users registers or logs in
       *
       * @param {string} userID The User ID
       * @param {number} [exp] The expiration time for the token expressed in the number of seconds since the epoch
       *
       * @return {string} Returns a token
       */
  
    }, {
      key: "createToken",
      value: function createToken(userID, exp) {
        if (this.secret == null) {
          throw Error("tokens can only be created server-side using the API Secret");
        }
  
        var extra = {};
  
        if (exp) {
          extra.exp = exp;
        }
  
        return JWTUserToken(this.secret, userID, extra, {});
      }
      /**
       * on - Listen to events on all channels and users your watching
       *
       * client.on('message.new', event => {console.log("my new message", event, channel.state.messages)})
       * or
       * client.on(event => {console.log(event.type)})
       *
       * @param {EventHandler<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType> | string} callbackOrString  The event type to listen for (optional)
       * @param {EventHandler<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType>} [callbackOrNothing] The callback to call
       *
       * @return {{ unsubscribe: () => void }} Description
       */
  
    }, {
      key: "on",
      value: function on(callbackOrString, callbackOrNothing) {
        var _this2 = this;
  
        var key = callbackOrNothing ? callbackOrString : 'all';
        var valid = isValidEventType(key);
  
        if (!valid) {
          throw Error("Invalid event type ".concat(key));
        }
  
        var callback = callbackOrNothing ? callbackOrNothing : callbackOrString;
  
        if (!(key in this.listeners)) {
          this.listeners[key] = [];
        }
  
        this.logger('info', "Attaching listener for ".concat(key, " event"), {
          tags: ['event', 'client']
        });
        this.listeners[key].push(callback);
        return {
          unsubscribe: function unsubscribe() {
            _this2.listeners[key] = _this2.listeners[key].filter(function (el) {
              return el !== callback;
            });
          }
        };
      }
      /**
       * off - Remove the event handler
       *
       */
  
    }, {
      key: "off",
      value: function off(callbackOrString, callbackOrNothing) {
        var key = callbackOrNothing ? callbackOrString : 'all';
        var valid = isValidEventType(key);
  
        if (!valid) {
          throw Error("Invalid event type ".concat(key));
        }
  
        var callback = callbackOrNothing ? callbackOrNothing : callbackOrString;
  
        if (!(key in this.listeners)) {
          this.listeners[key] = [];
        }
  
        this.logger('info', "Removing listener for ".concat(key, " event"), {
          tags: ['event', 'client']
        });
        this.listeners[key] = this.listeners[key].filter(function (value) {
          return value !== callback;
        });
      }
    }, {
      key: "_logApiRequest",
      value: function _logApiRequest(type, url, data, config) {
        this.logger('info', "client: ".concat(type, " - Request - ").concat(url), {
          tags: ['api', 'api_request', 'client'],
          url: url,
          payload: data,
          config: config
        });
      }
    }, {
      key: "_logApiResponse",
      value: function _logApiResponse(type, url, response) {
        this.logger('info', "client:".concat(type, " - Response - url: ").concat(url, " > status ").concat(response.status), {
          tags: ['api', 'api_response', 'client'],
          url: url,
          response: response
        });
      }
    }, {
      key: "_logApiError",
      value: function _logApiError(type, url, error) {
        this.logger('error', "client:".concat(type, " - Error - url: ").concat(url), {
          tags: ['api', 'api_response', 'client'],
          url: url,
          error: error
        });
      }
    }, {
      key: "get",
      value: function get(url, params) {
        return this.doAxiosRequest('get', url, null, {
          params: params
        });
      }
    }, {
      key: "put",
      value: function put(url, data) {
        return this.doAxiosRequest('put', url, data);
      }
    }, {
      key: "post",
      value: function post(url, data) {
        return this.doAxiosRequest('post', url, data);
      }
    }, {
      key: "patch",
      value: function patch(url, data) {
        return this.doAxiosRequest('patch', url, data);
      }
    }, {
      key: "delete",
      value: function _delete(url, params) {
        return this.doAxiosRequest('delete', url, null, {
          params: params
        });
      }
    }, {
      key: "sendFile",
      value: function sendFile(url, uri, name, contentType, user) {
        var data = addFileToFormData(uri, name, contentType);
        if (user != null) data.append('user', JSON.stringify(user));
        return this.doAxiosRequest('post', url, data, {
          headers: data.getHeaders ? data.getHeaders() : {},
          // node vs browser
          config: {
            timeout: 0,
            maxContentLength: Infinity,
            maxBodyLength: Infinity
          }
        });
      }
    }, {
      key: "errorFromResponse",
      value: function errorFromResponse(response) {
        var err;
        err = new Error("StreamChat error HTTP code: ".concat(response.status));
  
        if (response.data && response.data.code) {
          err = new Error("StreamChat error code ".concat(response.data.code, ": ").concat(response.data.message));
          err.code = response.data.code;
        }
  
        err.response = response;
        err.status = response.status;
        return err;
      }
    }, {
      key: "handleResponse",
      value: function handleResponse(response) {
        var data = response.data;
  
        if ((response.status + '')[0] !== '2') {
          throw this.errorFromResponse(response);
        }
  
        return data;
      }
    }, {
      key: "_handleClientEvent",
      value: function _handleClientEvent(event) {
        var _event$me, _event$me2;
  
        var client = this;
        this.logger('info', "client:_handleClientEvent - Received event of type { ".concat(event.type, " }"), {
          tags: ['event', 'client'],
          event: event
        }); // update the client.state with any changes to users
  
        if (event.user && (event.type === 'user.presence.changed' || event.type === 'user.updated')) {
          var _event$user;
  
          if (((_event$user = event.user) === null || _event$user === void 0 ? void 0 : _event$user.id) === this.userID) {
            this.user = this.user && _objectSpread$4(_objectSpread$4({}, this.user), event.user); // Updating only available properties in _user object.
  
            Object.keys(event.user).forEach(function (key) {
              if (client._user && key in client._user) {
                // @ts-expect-error
                client._user[key] = event.user[key];
              }
            });
          }
  
          client.state.updateUser(event.user);
  
          client._updateUserReferences(event.user);
        }
  
        if (event.type === 'health.check' && event.me) {
          client.user = event.me;
          client.state.updateUser(event.me);
          client.mutedChannels = event.me.channel_mutes;
          client.mutedUsers = event.me.mutes;
        }
  
        if (event.channel && event.type === 'notification.message_new') {
          this.configs[event.channel.type] = event.channel.config;
        }
  
        if (event.type === 'notification.channel_mutes_updated' && (_event$me = event.me) !== null && _event$me !== void 0 && _event$me.channel_mutes) {
          this.mutedChannels = event.me.channel_mutes;
        }
  
        if (event.type === 'notification.mutes_updated' && (_event$me2 = event.me) !== null && _event$me2 !== void 0 && _event$me2.mutes) {
          this.mutedUsers = event.me.mutes;
        }
      }
    }, {
      key: "_muteStatus",
      value: function _muteStatus(cid) {
        var muteStatus;
        this.mutedChannels.forEach(function (mute) {
          var _mute$channel;
  
          if (((_mute$channel = mute.channel) === null || _mute$channel === void 0 ? void 0 : _mute$channel.cid) === cid) {
            var muted = true;
  
            if (mute.expires) {
              muted = new Date(mute.expires).getTime() > new Date().getTime();
            }
  
            muteStatus = {
              muted: muted,
              createdAt: mute.created_at ? new Date(mute.created_at) : new Date(),
              expiresAt: mute.expires ? new Date(mute.expires) : null
            };
          }
        });
  
        if (muteStatus) {
          return muteStatus;
        }
  
        return {
          muted: false,
          createdAt: null,
          expiresAt: null
        };
      }
    }, {
      key: "_updateUserReferences",
      value:
      /*
      _updateUserReferences updates the members and watchers of the currently active channels
      that contain this user
      */
      function _updateUserReferences(user) {
        var refMap = this.state.userChannelReferences[user.id] || {};
        var refs = Object.keys(refMap);
  
        for (var _i3 = 0, _refs = refs; _i3 < _refs.length; _i3++) {
          var _channelID = _refs[_i3];
          var _channel4 = this.activeChannels[_channelID]; // search the members and watchers and update as needed...
  
          if (_channel4 !== null && _channel4 !== void 0 && _channel4.state) {
            if (_channel4.state.members[user.id]) {
              _channel4.state.members[user.id].user = user;
            }
  
            if (_channel4.state.watchers[user.id]) {
              _channel4.state.watchers[user.id] = user;
            }
          }
        }
      }
    }, {
      key: "connect",
      value: function () {
        var _connect = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee8() {
          var client, warmUpPromise, handshake;
          return _regeneratorRuntime.wrap(function _callee8$(_context8) {
            while (1) {
              switch (_context8.prev = _context8.next) {
                case 0:
                  this.connecting = true;
                  client = this;
                  this.failures = 0;
  
                  if (!(client.userID == null || this._user == null)) {
                    _context8.next = 5;
                    break;
                  }
  
                  throw Error('Call connectUser or connectAnonymousUser before starting the connection');
  
                case 5:
                  if (!(client.wsBaseURL == null)) {
                    _context8.next = 7;
                    break;
                  }
  
                  throw Error('Websocket base url not set');
  
                case 7:
                  if (!(client.clientID == null)) {
                    _context8.next = 9;
                    break;
                  }
  
                  throw Error('clientID is not set');
  
                case 9:
                  // The StableWSConnection handles all the reconnection logic.
                  this.wsConnection = new StableWSConnection({
                    wsBaseURL: client.wsBaseURL,
                    clientID: client.clientID,
                    userID: client.userID,
                    tokenManager: client.tokenManager,
                    user: this._user,
                    authType: this.getAuthType(),
                    userAgent: this.getUserAgent(),
                    apiKey: this.key,
                    recoverCallback: this.recoverState,
                    messageCallback: this.handleEvent,
                    eventCallback: this.dispatchEvent,
                    logger: this.logger
                  });
  
                  if (this.options.warmUp) {
                    warmUpPromise = this.doAxiosRequest('options', this.baseURL + '/connect');
                  }
  
                  _context8.next = 13;
                  return this.wsConnection.connect();
  
                case 13:
                  handshake = _context8.sent;
                  _context8.prev = 14;
                  _context8.next = 17;
                  return warmUpPromise;
  
                case 17:
                  _context8.next = 22;
                  break;
  
                case 19:
                  _context8.prev = 19;
                  _context8.t0 = _context8["catch"](14);
                  this.logger('error', 'Warmup request failed', {
                    error: _context8.t0
                  });
  
                case 22:
                  this.connectionID = this.wsConnection.connectionID;
                  return _context8.abrupt("return", handshake);
  
                case 24:
                case "end":
                  return _context8.stop();
              }
            }
          }, _callee8, this, [[14, 19]]);
        }));
  
        function connect() {
          return _connect.apply(this, arguments);
        }
  
        return connect;
      }()
      /**
       * queryUsers - Query users and watch user presence
       *
       * @param {UserFilters<UserType>} filterConditions MongoDB style filter conditions
       * @param {UserSort<UserType>} sort Sort options, for instance [{last_active: -1}].
       * When using multiple fields, make sure you use array of objects to guarantee field order, for instance [{last_active: -1}, {created_at: 1}]
       * @param {UserOptions} options Option object, {presence: true}
       *
       * @return {Promise<APIResponse & { users: Array<UserResponse<UserType>> }>} User Query Response
       */
  
    }, {
      key: "queryUsers",
      value: function () {
        var _queryUsers = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee9(filterConditions) {
          var sort,
              options,
              defaultOptions,
              data,
              _args9 = arguments;
          return _regeneratorRuntime.wrap(function _callee9$(_context9) {
            while (1) {
              switch (_context9.prev = _context9.next) {
                case 0:
                  sort = _args9.length > 1 && _args9[1] !== undefined ? _args9[1] : [];
                  options = _args9.length > 2 && _args9[2] !== undefined ? _args9[2] : {};
                  defaultOptions = {
                    presence: false
                  }; // Make sure we wait for the connect promise if there is a pending one
  
                  _context9.next = 5;
                  return this.setUserPromise;
  
                case 5:
                  if (!this._hasConnectionID()) {
                    defaultOptions.presence = false;
                  } // Return a list of users
  
  
                  _context9.next = 8;
                  return this.get(this.baseURL + '/users', {
                    payload: _objectSpread$4(_objectSpread$4({
                      filter_conditions: filterConditions,
                      sort: normalizeQuerySort(sort)
                    }, defaultOptions), options)
                  });
  
                case 8:
                  data = _context9.sent;
                  this.state.updateUsers(data.users);
                  return _context9.abrupt("return", data);
  
                case 11:
                case "end":
                  return _context9.stop();
              }
            }
          }, _callee9, this);
        }));
  
        function queryUsers(_x7) {
          return _queryUsers.apply(this, arguments);
        }
  
        return queryUsers;
      }()
      /**
       * queryChannels - Query channels
       *
       * @param {ChannelFilters<ChannelType, CommandType, UserType>} filterConditions object MongoDB style filters
       * @param {ChannelSort<ChannelType>} [sort] Sort options, for instance {created_at: -1}.
       * When using multiple fields, make sure you use array of objects to guarantee field order, for instance [{last_updated: -1}, {created_at: 1}]
       * @param {ChannelOptions} [options] Options object
       *
       * @return {Promise<APIResponse & { channels: Array<ChannelAPIResponse<AttachmentType,ChannelType,CommandType,MessageType,ReactionType,UserType>>}> } search channels response
       */
  
    }, {
      key: "queryChannels",
      value: function () {
        var _queryChannels = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee10(filterConditions) {
          var sort,
              options,
              defaultOptions,
              payload,
              data,
              channels,
              _iterator,
              _step,
              channelState,
              _iterator2,
              _step2,
              _channelState,
              c,
              _args10 = arguments;
  
          return _regeneratorRuntime.wrap(function _callee10$(_context10) {
            while (1) {
              switch (_context10.prev = _context10.next) {
                case 0:
                  sort = _args10.length > 1 && _args10[1] !== undefined ? _args10[1] : [];
                  options = _args10.length > 2 && _args10[2] !== undefined ? _args10[2] : {};
                  defaultOptions = {
                    state: true,
                    watch: true,
                    presence: false
                  }; // Make sure we wait for the connect promise if there is a pending one
  
                  _context10.next = 5;
                  return this.setUserPromise;
  
                case 5:
                  if (!this._hasConnectionID()) {
                    defaultOptions.watch = false;
                  } // Return a list of channels
  
  
                  payload = _objectSpread$4(_objectSpread$4({
                    filter_conditions: filterConditions,
                    sort: normalizeQuerySort(sort)
                  }, defaultOptions), options);
                  _context10.next = 9;
                  return this.post(this.baseURL + '/channels', payload);
  
                case 9:
                  data = _context10.sent;
                  channels = []; // update our cache of the configs
  
                  _iterator = _createForOfIteratorHelper$3(data.channels);
  
                  try {
                    for (_iterator.s(); !(_step = _iterator.n()).done;) {
                      channelState = _step.value;
  
                      this._addChannelConfig(channelState);
                    }
                  } catch (err) {
                    _iterator.e(err);
                  } finally {
                    _iterator.f();
                  }
  
                  _iterator2 = _createForOfIteratorHelper$3(data.channels);
  
                  try {
                    for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
                      _channelState = _step2.value;
                      c = this.channel(_channelState.channel.type, _channelState.channel.id);
                      c.data = _channelState.channel;
                      c.initialized = true;
  
                      c._initializeState(_channelState);
  
                      channels.push(c);
                    }
                  } catch (err) {
                    _iterator2.e(err);
                  } finally {
                    _iterator2.f();
                  }
  
                  return _context10.abrupt("return", channels);
  
                case 16:
                case "end":
                  return _context10.stop();
              }
            }
          }, _callee10, this);
        }));
  
        function queryChannels(_x8) {
          return _queryChannels.apply(this, arguments);
        }
  
        return queryChannels;
      }()
      /**
       * search - Query messages
       *
       * @param {ChannelFilters<ChannelType, CommandType, UserType>} filterConditions MongoDB style filter conditions
       * @param {MessageFilters<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType> | string} query search query or object MongoDB style filters
       * @param {SearchOptions} [options] Option object, {user_id: 'tommaso'}
       *
       * @return {Promise<SearchAPIResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>>} search messages response
       */
  
    }, {
      key: "search",
      value: function () {
        var _search = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee11(filterConditions, query) {
          var options,
              payload,
              _args11 = arguments;
          return _regeneratorRuntime.wrap(function _callee11$(_context11) {
            while (1) {
              switch (_context11.prev = _context11.next) {
                case 0:
                  options = _args11.length > 2 && _args11[2] !== undefined ? _args11[2] : {};
                  // Return a list of channels
                  payload = _objectSpread$4({
                    filter_conditions: filterConditions
                  }, options);
  
                  if (!(typeof query === 'string')) {
                    _context11.next = 6;
                    break;
                  }
  
                  payload.query = query;
                  _context11.next = 11;
                  break;
  
                case 6:
                  if (!(_typeof(query) === 'object')) {
                    _context11.next = 10;
                    break;
                  }
  
                  payload.message_filter_conditions = query;
                  _context11.next = 11;
                  break;
  
                case 10:
                  throw Error("Invalid type ".concat(_typeof(query), " for query parameter"));
  
                case 11:
                  _context11.next = 13;
                  return this.setUserPromise;
  
                case 13:
                  _context11.next = 15;
                  return this.get(this.baseURL + '/search', {
                    payload: payload
                  });
  
                case 15:
                  return _context11.abrupt("return", _context11.sent);
  
                case 16:
                case "end":
                  return _context11.stop();
              }
            }
          }, _callee11, this);
        }));
  
        function search(_x9, _x10) {
          return _search.apply(this, arguments);
        }
  
        return search;
      }()
      /**
       * addDevice - Adds a push device for a user.
       *
       * @param {string} id the device id
       * @param {'apn' | 'firebase'} push_provider the push provider (apn or firebase)
       * @param {string} [userID] the user id (defaults to current user)
       *
       */
  
    }, {
      key: "addDevice",
      value: function () {
        var _addDevice = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee12(id, push_provider, userID) {
          return _regeneratorRuntime.wrap(function _callee12$(_context12) {
            while (1) {
              switch (_context12.prev = _context12.next) {
                case 0:
                  _context12.next = 2;
                  return this.post(this.baseURL + '/devices', _objectSpread$4({
                    id: id,
                    push_provider: push_provider
                  }, userID != null ? {
                    user_id: userID
                  } : {}));
  
                case 2:
                  return _context12.abrupt("return", _context12.sent);
  
                case 3:
                case "end":
                  return _context12.stop();
              }
            }
          }, _callee12, this);
        }));
  
        function addDevice(_x11, _x12, _x13) {
          return _addDevice.apply(this, arguments);
        }
  
        return addDevice;
      }()
      /**
       * getDevices - Returns the devices associated with a current user
       *
       * @param {string} [userID] User ID. Only works on serverside
       *
       * @return {APIResponse & Device<UserType>[]} Array of devices
       */
  
    }, {
      key: "getDevices",
      value: function () {
        var _getDevices = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee13(userID) {
          return _regeneratorRuntime.wrap(function _callee13$(_context13) {
            while (1) {
              switch (_context13.prev = _context13.next) {
                case 0:
                  _context13.next = 2;
                  return this.get(this.baseURL + '/devices', userID ? {
                    user_id: userID
                  } : {});
  
                case 2:
                  return _context13.abrupt("return", _context13.sent);
  
                case 3:
                case "end":
                  return _context13.stop();
              }
            }
          }, _callee13, this);
        }));
  
        function getDevices(_x14) {
          return _getDevices.apply(this, arguments);
        }
  
        return getDevices;
      }()
      /**
       * removeDevice - Removes the device with the given id. Clientside users can only delete their own devices
       *
       * @param {string} id The device id
       * @param {string} [userID] The user id. Only specify this for serverside requests
       *
       */
  
    }, {
      key: "removeDevice",
      value: function () {
        var _removeDevice = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee14(id, userID) {
          return _regeneratorRuntime.wrap(function _callee14$(_context14) {
            while (1) {
              switch (_context14.prev = _context14.next) {
                case 0:
                  _context14.next = 2;
                  return this.delete(this.baseURL + '/devices', _objectSpread$4({
                    id: id
                  }, userID ? {
                    user_id: userID
                  } : {}));
  
                case 2:
                  return _context14.abrupt("return", _context14.sent);
  
                case 3:
                case "end":
                  return _context14.stop();
              }
            }
          }, _callee14, this);
        }));
  
        function removeDevice(_x15, _x16) {
          return _removeDevice.apply(this, arguments);
        }
  
        return removeDevice;
      }()
    }, {
      key: "_addChannelConfig",
      value: function _addChannelConfig(channelState) {
        this.configs[channelState.channel.type] = channelState.channel.config;
      }
      /**
       * channel - Returns a new channel with the given type, id and custom data
       *
       * If you want to create a unique conversation between 2 or more users; you can leave out the ID parameter and provide the list of members.
       * Make sure to await channel.create() or channel.watch() before accessing channel functions:
       * ie. channel = client.channel("messaging", {members: ["tommaso", "thierry"]})
       * await channel.create() to assign an ID to channel
       *
       * @param {string} channelType The channel type
       * @param {string | ChannelData<ChannelType> | null} [channelIDOrCustom]   The channel ID, you can leave this out if you want to create a conversation channel
       * @param {object} [custom]    Custom data to attach to the channel
       *
       * @return {channel} The channel object, initialize it using channel.watch()
       */
  
    }, {
      key: "channel",
      value: function channel(channelType, channelIDOrCustom) {
        var custom = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  
        if (!this.userID && !this._isUsingServerAuth()) {
          throw Error('Call connectUser or connectAnonymousUser before creating a channel');
        }
  
        if (~channelType.indexOf(':')) {
          throw Error("Invalid channel group ".concat(channelType, ", can't contain the : character"));
        } // support channel("messaging", null, {options})
        // support channel("messaging", undefined, {options})
        // support channel("messaging", "", {options})
  
  
        if (channelIDOrCustom == null || channelIDOrCustom === '') {
          return new Channel(this, channelType, undefined, custom);
        } // support channel("messaging", {options})
  
  
        if (_typeof(channelIDOrCustom) === 'object') {
          return this.getChannelByMembers(channelType, channelIDOrCustom);
        }
  
        return this.getChannelById(channelType, channelIDOrCustom, custom);
      }
      /**
       * It's a helper method for `client.channel()` method, used to create unique conversation or
       * channel based on member list instead of id.
       *
       * If the channel already exists in `activeChannels` list, then we simply return it, since that
       * means the same channel was already requested or created.
       *
       * Otherwise we create a new instance of Channel class and return it.
       *
       * @private
       *
       * @param {string} channelType The channel type
       * @param {object} [custom]    Custom data to attach to the channel
       *
       * @return {channel} The channel object, initialize it using channel.watch()
       */
  
    }, {
      key: "partialUpdateUser",
      value:
      /**
       * partialUpdateUser - Update the given user object
       *
       * @param {PartialUserUpdate<UserType>} partialUserObject which should contain id and any of "set" or "unset" params;
       * example: {id: "user1", set:{field: value}, unset:["field2"]}
       *
       * @return {Promise<APIResponse & { users: { [key: string]: UserResponse<UserType> } }>} list of updated users
       */
      function () {
        var _partialUpdateUser = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee15(partialUserObject) {
          return _regeneratorRuntime.wrap(function _callee15$(_context15) {
            while (1) {
              switch (_context15.prev = _context15.next) {
                case 0:
                  _context15.next = 2;
                  return this.partialUpdateUsers([partialUserObject]);
  
                case 2:
                  return _context15.abrupt("return", _context15.sent);
  
                case 3:
                case "end":
                  return _context15.stop();
              }
            }
          }, _callee15, this);
        }));
  
        function partialUpdateUser(_x17) {
          return _partialUpdateUser.apply(this, arguments);
        }
  
        return partialUpdateUser;
      }()
      /**
       * upsertUsers - Batch upsert the list of users
       *
       * @param {UserResponse<UserType>[]} users list of users
       *
       * @return {Promise<APIResponse & { users: { [key: string]: UserResponse<UserType> } }>}
       */
  
    }, {
      key: "upsertUsers",
      value: function () {
        var _upsertUsers = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee16(users) {
          var userMap, _iterator3, _step3, userObject;
  
          return _regeneratorRuntime.wrap(function _callee16$(_context16) {
            while (1) {
              switch (_context16.prev = _context16.next) {
                case 0:
                  userMap = {};
                  _iterator3 = _createForOfIteratorHelper$3(users);
                  _context16.prev = 2;
  
                  _iterator3.s();
  
                case 4:
                  if ((_step3 = _iterator3.n()).done) {
                    _context16.next = 11;
                    break;
                  }
  
                  userObject = _step3.value;
  
                  if (userObject.id) {
                    _context16.next = 8;
                    break;
                  }
  
                  throw Error('User ID is required when updating a user');
  
                case 8:
                  userMap[userObject.id] = userObject;
  
                case 9:
                  _context16.next = 4;
                  break;
  
                case 11:
                  _context16.next = 16;
                  break;
  
                case 13:
                  _context16.prev = 13;
                  _context16.t0 = _context16["catch"](2);
  
                  _iterator3.e(_context16.t0);
  
                case 16:
                  _context16.prev = 16;
  
                  _iterator3.f();
  
                  return _context16.finish(16);
  
                case 19:
                  _context16.next = 21;
                  return this.post(this.baseURL + '/users', {
                    users: userMap
                  });
  
                case 21:
                  return _context16.abrupt("return", _context16.sent);
  
                case 22:
                case "end":
                  return _context16.stop();
              }
            }
          }, _callee16, this, [[2, 13, 16, 19]]);
        }));
  
        function upsertUsers(_x18) {
          return _upsertUsers.apply(this, arguments);
        }
  
        return upsertUsers;
      }()
      /**
       * @deprecated Please use upsertUsers() function instead.
       *
       * updateUsers - Batch update the list of users
       *
       * @param {UserResponse<UserType>[]} users list of users
       * @return {Promise<APIResponse & { users: { [key: string]: UserResponse<UserType> } }>}
       */
  
    }, {
      key: "upsertUser",
      value:
      /**
       * upsertUser - Update or Create the given user object
       *
       * @param {UserResponse<UserType>} userObject user object, the only required field is the user id. IE {id: "myuser"} is valid
       *
       * @return {Promise<APIResponse & { users: { [key: string]: UserResponse<UserType> } }>}
       */
      function upsertUser(userObject) {
        return this.upsertUsers([userObject]);
      }
      /**
       * @deprecated Please use upsertUser() function instead.
       *
       * updateUser - Update or Create the given user object
       *
       * @param {UserResponse<UserType>} userObject user object, the only required field is the user id. IE {id: "myuser"} is valid
       * @return {Promise<APIResponse & { users: { [key: string]: UserResponse<UserType> } }>}
       */
  
    }, {
      key: "partialUpdateUsers",
      value:
      /**
       * partialUpdateUsers - Batch partial update of users
       *
       * @param {PartialUserUpdate<UserType>[]} users list of partial update requests
       *
       * @return {Promise<APIResponse & { users: { [key: string]: UserResponse<UserType> } }>}
       */
      function () {
        var _partialUpdateUsers = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee17(users) {
          var _iterator4, _step4, userObject;
  
          return _regeneratorRuntime.wrap(function _callee17$(_context17) {
            while (1) {
              switch (_context17.prev = _context17.next) {
                case 0:
                  _iterator4 = _createForOfIteratorHelper$3(users);
                  _context17.prev = 1;
  
                  _iterator4.s();
  
                case 3:
                  if ((_step4 = _iterator4.n()).done) {
                    _context17.next = 9;
                    break;
                  }
  
                  userObject = _step4.value;
  
                  if (userObject.id) {
                    _context17.next = 7;
                    break;
                  }
  
                  throw Error('User ID is required when updating a user');
  
                case 7:
                  _context17.next = 3;
                  break;
  
                case 9:
                  _context17.next = 14;
                  break;
  
                case 11:
                  _context17.prev = 11;
                  _context17.t0 = _context17["catch"](1);
  
                  _iterator4.e(_context17.t0);
  
                case 14:
                  _context17.prev = 14;
  
                  _iterator4.f();
  
                  return _context17.finish(14);
  
                case 17:
                  _context17.next = 19;
                  return this.patch(this.baseURL + '/users', {
                    users: users
                  });
  
                case 19:
                  return _context17.abrupt("return", _context17.sent);
  
                case 20:
                case "end":
                  return _context17.stop();
              }
            }
          }, _callee17, this, [[1, 11, 14, 17]]);
        }));
  
        function partialUpdateUsers(_x19) {
          return _partialUpdateUsers.apply(this, arguments);
        }
  
        return partialUpdateUsers;
      }()
    }, {
      key: "deleteUser",
      value: function () {
        var _deleteUser = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee18(userID, params) {
          return _regeneratorRuntime.wrap(function _callee18$(_context18) {
            while (1) {
              switch (_context18.prev = _context18.next) {
                case 0:
                  _context18.next = 2;
                  return this.delete(this.baseURL + "/users/".concat(userID), params);
  
                case 2:
                  return _context18.abrupt("return", _context18.sent);
  
                case 3:
                case "end":
                  return _context18.stop();
              }
            }
          }, _callee18, this);
        }));
  
        function deleteUser(_x20, _x21) {
          return _deleteUser.apply(this, arguments);
        }
  
        return deleteUser;
      }()
    }, {
      key: "reactivateUser",
      value: function () {
        var _reactivateUser = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee19(userID, options) {
          return _regeneratorRuntime.wrap(function _callee19$(_context19) {
            while (1) {
              switch (_context19.prev = _context19.next) {
                case 0:
                  _context19.next = 2;
                  return this.post(this.baseURL + "/users/".concat(userID, "/reactivate"), _objectSpread$4({}, options));
  
                case 2:
                  return _context19.abrupt("return", _context19.sent);
  
                case 3:
                case "end":
                  return _context19.stop();
              }
            }
          }, _callee19, this);
        }));
  
        function reactivateUser(_x22, _x23) {
          return _reactivateUser.apply(this, arguments);
        }
  
        return reactivateUser;
      }()
    }, {
      key: "deactivateUser",
      value: function () {
        var _deactivateUser = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee20(userID, options) {
          return _regeneratorRuntime.wrap(function _callee20$(_context20) {
            while (1) {
              switch (_context20.prev = _context20.next) {
                case 0:
                  _context20.next = 2;
                  return this.post(this.baseURL + "/users/".concat(userID, "/deactivate"), _objectSpread$4({}, options));
  
                case 2:
                  return _context20.abrupt("return", _context20.sent);
  
                case 3:
                case "end":
                  return _context20.stop();
              }
            }
          }, _callee20, this);
        }));
  
        function deactivateUser(_x24, _x25) {
          return _deactivateUser.apply(this, arguments);
        }
  
        return deactivateUser;
      }()
    }, {
      key: "exportUser",
      value: function () {
        var _exportUser = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee21(userID, options) {
          return _regeneratorRuntime.wrap(function _callee21$(_context21) {
            while (1) {
              switch (_context21.prev = _context21.next) {
                case 0:
                  _context21.next = 2;
                  return this.get(this.baseURL + "/users/".concat(userID, "/export"), _objectSpread$4({}, options));
  
                case 2:
                  return _context21.abrupt("return", _context21.sent);
  
                case 3:
                case "end":
                  return _context21.stop();
              }
            }
          }, _callee21, this);
        }));
  
        function exportUser(_x26, _x27) {
          return _exportUser.apply(this, arguments);
        }
  
        return exportUser;
      }()
      /** banUser - bans a user from all channels
       *
       * @param {string} targetUserID
       * @param {BanUserOptions<UserType>} [options]
       * @returns {Promise<APIResponse>}
       */
  
    }, {
      key: "banUser",
      value: function () {
        var _banUser = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee22(targetUserID, options) {
          return _regeneratorRuntime.wrap(function _callee22$(_context22) {
            while (1) {
              switch (_context22.prev = _context22.next) {
                case 0:
                  if ((options === null || options === void 0 ? void 0 : options.user_id) !== undefined) {
                    options.banned_by_id = options.user_id;
                    delete options.user_id;
                    console.warn("banUser: 'user_id' is deprecated, please consider switching to 'banned_by_id'");
                  }
  
                  if ((options === null || options === void 0 ? void 0 : options.user) !== undefined) {
                    options.banned_by = options.user;
                    delete options.user;
                    console.warn("banUser: 'user' is deprecated, please consider switching to 'banned_by'");
                  }
  
                  _context22.next = 4;
                  return this.post(this.baseURL + '/moderation/ban', _objectSpread$4({
                    target_user_id: targetUserID
                  }, options));
  
                case 4:
                  return _context22.abrupt("return", _context22.sent);
  
                case 5:
                case "end":
                  return _context22.stop();
              }
            }
          }, _callee22, this);
        }));
  
        function banUser(_x28, _x29) {
          return _banUser.apply(this, arguments);
        }
  
        return banUser;
      }()
      /** unbanUser - revoke global ban for a user
       *
       * @param {string} targetUserID
       * @param {UnBanUserOptions} [options]
       * @returns {Promise<APIResponse>}
       */
  
    }, {
      key: "unbanUser",
      value: function () {
        var _unbanUser = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee23(targetUserID, options) {
          return _regeneratorRuntime.wrap(function _callee23$(_context23) {
            while (1) {
              switch (_context23.prev = _context23.next) {
                case 0:
                  _context23.next = 2;
                  return this.delete(this.baseURL + '/moderation/ban', _objectSpread$4({
                    target_user_id: targetUserID
                  }, options));
  
                case 2:
                  return _context23.abrupt("return", _context23.sent);
  
                case 3:
                case "end":
                  return _context23.stop();
              }
            }
          }, _callee23, this);
        }));
  
        function unbanUser(_x30, _x31) {
          return _unbanUser.apply(this, arguments);
        }
  
        return unbanUser;
      }()
      /** shadowBan - shadow bans a user from all channels
       *
       * @param {string} targetUserID
       * @param {BanUserOptions<UserType>} [options]
       * @returns {Promise<APIResponse>}
       */
  
    }, {
      key: "shadowBan",
      value: function () {
        var _shadowBan = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee24(targetUserID, options) {
          return _regeneratorRuntime.wrap(function _callee24$(_context24) {
            while (1) {
              switch (_context24.prev = _context24.next) {
                case 0:
                  _context24.next = 2;
                  return this.banUser(targetUserID, _objectSpread$4({
                    shadow: true
                  }, options));
  
                case 2:
                  return _context24.abrupt("return", _context24.sent);
  
                case 3:
                case "end":
                  return _context24.stop();
              }
            }
          }, _callee24, this);
        }));
  
        function shadowBan(_x32, _x33) {
          return _shadowBan.apply(this, arguments);
        }
  
        return shadowBan;
      }()
      /** removeShadowBan - revoke global shadow ban for a user
       *
       * @param {string} targetUserID
       * @param {UnBanUserOptions} [options]
       * @returns {Promise<APIResponse>}
       */
  
    }, {
      key: "removeShadowBan",
      value: function () {
        var _removeShadowBan = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee25(targetUserID, options) {
          return _regeneratorRuntime.wrap(function _callee25$(_context25) {
            while (1) {
              switch (_context25.prev = _context25.next) {
                case 0:
                  _context25.next = 2;
                  return this.unbanUser(targetUserID, _objectSpread$4({
                    shadow: true
                  }, options));
  
                case 2:
                  return _context25.abrupt("return", _context25.sent);
  
                case 3:
                case "end":
                  return _context25.stop();
              }
            }
          }, _callee25, this);
        }));
  
        function removeShadowBan(_x34, _x35) {
          return _removeShadowBan.apply(this, arguments);
        }
  
        return removeShadowBan;
      }()
      /** muteUser - mutes a user
       *
       * @param {string} targetID
       * @param {string} [userID] Only used with serverside auth
       * @param {MuteUserOptions<UserType>} [options]
       * @returns {Promise<MuteUserResponse<ChannelType, CommandType, UserType>>}
       */
  
    }, {
      key: "muteUser",
      value: function () {
        var _muteUser = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee26(targetID, userID) {
          var options,
              _args26 = arguments;
          return _regeneratorRuntime.wrap(function _callee26$(_context26) {
            while (1) {
              switch (_context26.prev = _context26.next) {
                case 0:
                  options = _args26.length > 2 && _args26[2] !== undefined ? _args26[2] : {};
                  _context26.next = 3;
                  return this.post(this.baseURL + '/moderation/mute', _objectSpread$4(_objectSpread$4({
                    target_id: targetID
                  }, userID ? {
                    user_id: userID
                  } : {}), options));
  
                case 3:
                  return _context26.abrupt("return", _context26.sent);
  
                case 4:
                case "end":
                  return _context26.stop();
              }
            }
          }, _callee26, this);
        }));
  
        function muteUser(_x36, _x37) {
          return _muteUser.apply(this, arguments);
        }
  
        return muteUser;
      }()
      /** unmuteUser - unmutes a user
       *
       * @param {string} targetID
       * @param {string} [currentUserID] Only used with serverside auth
       * @returns {Promise<APIResponse>}
       */
  
    }, {
      key: "unmuteUser",
      value: function () {
        var _unmuteUser = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee27(targetID, currentUserID) {
          return _regeneratorRuntime.wrap(function _callee27$(_context27) {
            while (1) {
              switch (_context27.prev = _context27.next) {
                case 0:
                  _context27.next = 2;
                  return this.post(this.baseURL + '/moderation/unmute', _objectSpread$4({
                    target_id: targetID
                  }, currentUserID ? {
                    user_id: currentUserID
                  } : {}));
  
                case 2:
                  return _context27.abrupt("return", _context27.sent);
  
                case 3:
                case "end":
                  return _context27.stop();
              }
            }
          }, _callee27, this);
        }));
  
        function unmuteUser(_x38, _x39) {
          return _unmuteUser.apply(this, arguments);
        }
  
        return unmuteUser;
      }()
      /** userMuteStatus - check if a user is muted or not, can be used after connectUser() is called
       *
       * @param {string} targetID
       * @returns {boolean}
       */
  
    }, {
      key: "userMuteStatus",
      value: function userMuteStatus(targetID) {
        if (!this.user || !this.wsPromise) {
          throw new Error('Make sure to await connectUser() first.');
        }
  
        for (var i = 0; i < this.mutedUsers.length; i += 1) {
          if (this.mutedUsers[i].target.id === targetID) return true;
        }
  
        return false;
      }
      /**
       * flagMessage - flag a message
       * @param {string} targetMessageID
       * @param {string} [options.user_id] currentUserID, only used with serverside auth
       * @returns {Promise<APIResponse>}
       */
  
    }, {
      key: "flagMessage",
      value: function () {
        var _flagMessage = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee28(targetMessageID) {
          var options,
              _args28 = arguments;
          return _regeneratorRuntime.wrap(function _callee28$(_context28) {
            while (1) {
              switch (_context28.prev = _context28.next) {
                case 0:
                  options = _args28.length > 1 && _args28[1] !== undefined ? _args28[1] : {};
                  _context28.next = 3;
                  return this.post(this.baseURL + '/moderation/flag', _objectSpread$4({
                    target_message_id: targetMessageID
                  }, options));
  
                case 3:
                  return _context28.abrupt("return", _context28.sent);
  
                case 4:
                case "end":
                  return _context28.stop();
              }
            }
          }, _callee28, this);
        }));
  
        function flagMessage(_x40) {
          return _flagMessage.apply(this, arguments);
        }
  
        return flagMessage;
      }()
      /**
       * flagUser - flag a user
       * @param {string} targetID
       * @param {string} [options.user_id] currentUserID, only used with serverside auth
       * @returns {Promise<APIResponse>}
       */
  
    }, {
      key: "flagUser",
      value: function () {
        var _flagUser = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee29(targetID) {
          var options,
              _args29 = arguments;
          return _regeneratorRuntime.wrap(function _callee29$(_context29) {
            while (1) {
              switch (_context29.prev = _context29.next) {
                case 0:
                  options = _args29.length > 1 && _args29[1] !== undefined ? _args29[1] : {};
                  _context29.next = 3;
                  return this.post(this.baseURL + '/moderation/flag', _objectSpread$4({
                    target_user_id: targetID
                  }, options));
  
                case 3:
                  return _context29.abrupt("return", _context29.sent);
  
                case 4:
                case "end":
                  return _context29.stop();
              }
            }
          }, _callee29, this);
        }));
  
        function flagUser(_x41) {
          return _flagUser.apply(this, arguments);
        }
  
        return flagUser;
      }()
      /**
       * unflagMessage - unflag a message
       * @param {string} targetMessageID
       * @param {string} [options.user_id] currentUserID, only used with serverside auth
       * @returns {Promise<APIResponse>}
       */
  
    }, {
      key: "unflagMessage",
      value: function () {
        var _unflagMessage = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee30(targetMessageID) {
          var options,
              _args30 = arguments;
          return _regeneratorRuntime.wrap(function _callee30$(_context30) {
            while (1) {
              switch (_context30.prev = _context30.next) {
                case 0:
                  options = _args30.length > 1 && _args30[1] !== undefined ? _args30[1] : {};
                  _context30.next = 3;
                  return this.post(this.baseURL + '/moderation/unflag', _objectSpread$4({
                    target_message_id: targetMessageID
                  }, options));
  
                case 3:
                  return _context30.abrupt("return", _context30.sent);
  
                case 4:
                case "end":
                  return _context30.stop();
              }
            }
          }, _callee30, this);
        }));
  
        function unflagMessage(_x42) {
          return _unflagMessage.apply(this, arguments);
        }
  
        return unflagMessage;
      }()
      /**
       * unflagUser - unflag a user
       * @param {string} targetID
       * @param {string} [options.user_id] currentUserID, only used with serverside auth
       * @returns {Promise<APIResponse>}
       */
  
    }, {
      key: "unflagUser",
      value: function () {
        var _unflagUser = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee31(targetID) {
          var options,
              _args31 = arguments;
          return _regeneratorRuntime.wrap(function _callee31$(_context31) {
            while (1) {
              switch (_context31.prev = _context31.next) {
                case 0:
                  options = _args31.length > 1 && _args31[1] !== undefined ? _args31[1] : {};
                  _context31.next = 3;
                  return this.post(this.baseURL + '/moderation/unflag', _objectSpread$4({
                    target_user_id: targetID
                  }, options));
  
                case 3:
                  return _context31.abrupt("return", _context31.sent);
  
                case 4:
                case "end":
                  return _context31.stop();
              }
            }
          }, _callee31, this);
        }));
  
        function unflagUser(_x43) {
          return _unflagUser.apply(this, arguments);
        }
  
        return unflagUser;
      }()
      /**
       * markAllRead - marks all channels for this user as read
       * @param {MarkAllReadOptions<UserType>} [data]
       *
       * @return {Promise<APIResponse>}
       */
  
    }, {
      key: "markAllRead",
      value: function () {
        var _markAllRead = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee32() {
          var data,
              _args32 = arguments;
          return _regeneratorRuntime.wrap(function _callee32$(_context32) {
            while (1) {
              switch (_context32.prev = _context32.next) {
                case 0:
                  data = _args32.length > 0 && _args32[0] !== undefined ? _args32[0] : {};
                  _context32.next = 3;
                  return this.post(this.baseURL + '/channels/read', _objectSpread$4({}, data));
  
                case 3:
                case "end":
                  return _context32.stop();
              }
            }
          }, _callee32, this);
        }));
  
        function markAllRead() {
          return _markAllRead.apply(this, arguments);
        }
  
        return markAllRead;
      }()
    }, {
      key: "createCommand",
      value: function createCommand(data) {
        return this.post(this.baseURL + '/commands', data);
      }
    }, {
      key: "getCommand",
      value: function getCommand(name) {
        return this.get(this.baseURL + "/commands/".concat(name));
      }
    }, {
      key: "updateCommand",
      value: function updateCommand(name, data) {
        return this.put(this.baseURL + "/commands/".concat(name), data);
      }
    }, {
      key: "deleteCommand",
      value: function deleteCommand(name) {
        return this.delete(this.baseURL + "/commands/".concat(name));
      }
    }, {
      key: "listCommands",
      value: function listCommands() {
        return this.get(this.baseURL + "/commands");
      }
    }, {
      key: "createChannelType",
      value: function createChannelType(data) {
        var channelData = _extends({}, {
          commands: ['all']
        }, data);
  
        return this.post(this.baseURL + '/channeltypes', channelData);
      }
    }, {
      key: "getChannelType",
      value: function getChannelType(channelType) {
        return this.get(this.baseURL + "/channeltypes/".concat(channelType));
      }
    }, {
      key: "updateChannelType",
      value: function updateChannelType(channelType, data) {
        return this.put(this.baseURL + "/channeltypes/".concat(channelType), data);
      }
    }, {
      key: "deleteChannelType",
      value: function deleteChannelType(channelType) {
        return this.delete(this.baseURL + "/channeltypes/".concat(channelType));
      }
    }, {
      key: "listChannelTypes",
      value: function listChannelTypes() {
        return this.get(this.baseURL + "/channeltypes");
      }
      /**
       * translateMessage - adds the translation to the message
       *
       * @param {string} messageId
       * @param {string} language
       *
       * @return {APIResponse & MessageResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>} Response that includes the message
       */
  
    }, {
      key: "translateMessage",
      value: function () {
        var _translateMessage = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee33(messageId, language) {
          return _regeneratorRuntime.wrap(function _callee33$(_context33) {
            while (1) {
              switch (_context33.prev = _context33.next) {
                case 0:
                  _context33.next = 2;
                  return this.post(this.baseURL + "/messages/".concat(messageId, "/translate"), {
                    language: language
                  });
  
                case 2:
                  return _context33.abrupt("return", _context33.sent);
  
                case 3:
                case "end":
                  return _context33.stop();
              }
            }
          }, _callee33, this);
        }));
  
        function translateMessage(_x44, _x45) {
          return _translateMessage.apply(this, arguments);
        }
  
        return translateMessage;
      }()
      /**
       * pinMessage - pins provided message
       * @param {UpdatedMessage<AttachmentType,ChannelType,CommandType,MessageType,ReactionType,UserType>} message object
       * @param {undefined|number|string|Date} timeoutOrExpirationDate expiration date or timeout. Use number type to set timeout in seconds, string or Date to set exact expiration date
       */
  
    }, {
      key: "pinMessage",
      value: function pinMessage(message, timeoutOrExpirationDate) {
        var pinExpires;
  
        if (typeof timeoutOrExpirationDate === 'number') {
          var now = new Date();
          now.setSeconds(now.getSeconds() + timeoutOrExpirationDate);
          pinExpires = now.toISOString();
        } else if (isString$1(timeoutOrExpirationDate)) {
          pinExpires = timeoutOrExpirationDate;
        } else if (timeoutOrExpirationDate instanceof Date) {
          pinExpires = timeoutOrExpirationDate.toISOString();
        }
  
        return this.updateMessage(_objectSpread$4(_objectSpread$4({}, message), {}, {
          pinned: true,
          pin_expires: pinExpires
        }));
      }
      /**
       * unpinMessage - unpins provided message
       * @param {UpdatedMessage<AttachmentType,ChannelType,CommandType,MessageType,ReactionType,UserType>} message object
       */
  
    }, {
      key: "unpinMessage",
      value: function unpinMessage(message) {
        return this.updateMessage(_objectSpread$4(_objectSpread$4({}, message), {}, {
          pinned: false
        }));
      }
      /**
       * updateMessage - Update the given message
       *
       * @param {Omit<MessageResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>, 'mentioned_users'> & { mentioned_users?: string[] }} message object, id needs to be specified
       * @param {string | { id: string }} [userId]
       *
       * @return {APIResponse & { message: MessageResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType> }} Response that includes the message
       */
  
    }, {
      key: "updateMessage",
      value: function () {
        var _updateMessage = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee34(message, userId) {
          var clonedMessage, reservedMessageFields;
          return _regeneratorRuntime.wrap(function _callee34$(_context34) {
            while (1) {
              switch (_context34.prev = _context34.next) {
                case 0:
                  if (message.id) {
                    _context34.next = 2;
                    break;
                  }
  
                  throw Error('Please specify the message id when calling updateMessage');
  
                case 2:
                  clonedMessage = _extends({}, message);
                  delete clonedMessage.id;
                  reservedMessageFields = ['command', 'created_at', 'html', 'latest_reactions', 'own_reactions', 'reaction_counts', 'reply_count', 'type', 'updated_at', 'user'];
                  reservedMessageFields.forEach(function (item) {
                    if (clonedMessage[item] != null) {
                      delete clonedMessage[item];
                    }
                  });
  
                  if (userId != null) {
                    if (isString$1(userId)) {
                      clonedMessage.user_id = userId;
                    } else {
                      clonedMessage.user = {
                        id: userId.id
                      };
                    }
                  }
  
                  _context34.next = 9;
                  return this.post(this.baseURL + "/messages/".concat(message.id), {
                    message: clonedMessage
                  });
  
                case 9:
                  return _context34.abrupt("return", _context34.sent);
  
                case 10:
                case "end":
                  return _context34.stop();
              }
            }
          }, _callee34, this);
        }));
  
        function updateMessage(_x46, _x47) {
          return _updateMessage.apply(this, arguments);
        }
  
        return updateMessage;
      }()
    }, {
      key: "deleteMessage",
      value: function () {
        var _deleteMessage = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee35(messageID, hardDelete) {
          var params;
          return _regeneratorRuntime.wrap(function _callee35$(_context35) {
            while (1) {
              switch (_context35.prev = _context35.next) {
                case 0:
                  params = {};
  
                  if (hardDelete) {
                    params = {
                      hard: true
                    };
                  }
  
                  _context35.next = 4;
                  return this.delete(this.baseURL + "/messages/".concat(messageID), params);
  
                case 4:
                  return _context35.abrupt("return", _context35.sent);
  
                case 5:
                case "end":
                  return _context35.stop();
              }
            }
          }, _callee35, this);
        }));
  
        function deleteMessage(_x48, _x49) {
          return _deleteMessage.apply(this, arguments);
        }
  
        return deleteMessage;
      }()
    }, {
      key: "getMessage",
      value: function () {
        var _getMessage = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee36(messageID) {
          return _regeneratorRuntime.wrap(function _callee36$(_context36) {
            while (1) {
              switch (_context36.prev = _context36.next) {
                case 0:
                  _context36.next = 2;
                  return this.get(this.baseURL + "/messages/".concat(messageID));
  
                case 2:
                  return _context36.abrupt("return", _context36.sent);
  
                case 3:
                case "end":
                  return _context36.stop();
              }
            }
          }, _callee36, this);
        }));
  
        function getMessage(_x50) {
          return _getMessage.apply(this, arguments);
        }
  
        return getMessage;
      }()
    }, {
      key: "getUserAgent",
      value: function getUserAgent() {
        return this.userAgent || "stream-chat-javascript-client-".concat(this.node ? 'node' : 'browser', "-", "3.1.0");
      }
    }, {
      key: "setUserAgent",
      value: function setUserAgent(userAgent) {
        this.userAgent = userAgent;
      }
      /**
       * _isUsingServerAuth - Returns true if we're using server side auth
       */
  
    }, {
      key: "_enrichAxiosOptions",
      value: function _enrichAxiosOptions() {
        var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {
          params: {},
          headers: {},
          config: {}
        };
  
        var token = this._getToken();
  
        return _objectSpread$4({
          params: _objectSpread$4(_objectSpread$4({
            user_id: this.userID
          }, options.params), {}, {
            api_key: this.key,
            connection_id: this.connectionID
          }),
          headers: _objectSpread$4({
            Authorization: token,
            'stream-auth-type': this.getAuthType(),
            'x-stream-client': this.getUserAgent()
          }, options.headers)
        }, options.config);
      }
    }, {
      key: "_getToken",
      value: function _getToken() {
        if (!this.tokenManager || this.anonymous) return null;
        return this.tokenManager.getToken();
      }
    }, {
      key: "_startCleaning",
      value: function _startCleaning() {
        var that = this;
  
        if (this.cleaningIntervalRef != null) {
          return;
        }
  
        this.cleaningIntervalRef = setInterval(function () {
          // call clean on the channel, used for calling the stop.typing event etc.
          for (var _i4 = 0, _Object$values2 = Object.values(that.activeChannels); _i4 < _Object$values2.length; _i4++) {
            var _channel5 = _Object$values2[_i4];
  
            _channel5.clean();
          }
        }, 500);
      }
    }, {
      key: "verifyWebhook",
      value: function verifyWebhook(requestBody, xSignature) {
        return !!this.secret && CheckSignature(requestBody, this.secret, xSignature);
      }
      /** getPermission - gets the definition for a permission
       *
       * @param {string} name
       * @returns {Promise<PermissionAPIResponse>}
       */
  
    }, {
      key: "getPermission",
      value: function getPermission(name) {
        return this.get("".concat(this.baseURL, "/custom_permission/").concat(name));
      }
      /** createPermission - creates a custom permission
       *
       * @param {CustomPermissionOptions} permissionData the permission data
       * @returns {Promise<APIResponse>}
       */
  
    }, {
      key: "createPermission",
      value: function createPermission(permissionData) {
        return this.post("".concat(this.baseURL, "/custom_permission"), _objectSpread$4({}, permissionData));
      }
      /** updatePermission - updates an existing custom permission
       *
       * @param {string} name
       * @param {CustomPermissionOptions} permissionData the permission data
       * @returns {Promise<APIResponse>}
       */
  
    }, {
      key: "updatePermission",
      value: function updatePermission(name, permissionData) {
        return this.post("".concat(this.baseURL, "/custom_permission/").concat(name), _objectSpread$4({}, permissionData));
      }
      /** deletePermission - deletes a custom permission
       *
       * @param {string} name
       * @returns {Promise<APIResponse>}
       */
  
    }, {
      key: "deletePermission",
      value: function deletePermission(name) {
        return this.delete("".concat(this.baseURL, "/custom_permission/").concat(name));
      }
      /** listPermissions - returns the list of custom permissions for this application
       *
       * @returns {Promise<APIResponse>}
       */
  
    }, {
      key: "listPermissions",
      value: function listPermissions() {
        return this.get("".concat(this.baseURL, "/custom_permission"));
      }
      /** createRole - creates a custom role
       *
       * @param {string} name the new role name
       * @returns {Promise<APIResponse>}
       */
  
    }, {
      key: "createRole",
      value: function createRole(name) {
        return this.post("".concat(this.baseURL, "/custom_role"), {
          name: name
        });
      }
      /** listRoles - returns the list of custom roles for this application
       *
       * @returns {Promise<APIResponse>}
       */
  
    }, {
      key: "listRoles",
      value: function listRoles() {
        return this.get("".concat(this.baseURL, "/custom_role"));
      }
      /** deleteRole - deletes a custom role
       *
       * @param {string} name the role name
       * @returns {Promise<APIResponse>}
       */
  
    }, {
      key: "deleteRole",
      value: function deleteRole(name) {
        return this.delete("".concat(this.baseURL, "/custom_role/").concat(name));
      }
      /** sync - returns all events that happened for a list of channels since last sync
       * @param {string[]} channel_cids list of channel CIDs
       * @param {string} last_sync_at last time the user was online and in sync. RFC3339 ie. "2020-05-06T15:05:01.207Z"
       */
  
    }, {
      key: "sync",
      value: function sync(channel_cids, last_sync_at) {
        return this.post("".concat(this.baseURL, "/sync"), {
          channel_cids: channel_cids,
          last_sync_at: last_sync_at
        });
      }
    }, {
      key: "createBlockList",
      value: function createBlockList(blockList) {
        return this.post("".concat(this.baseURL, "/blocklists"), blockList);
      }
    }, {
      key: "listBlockLists",
      value: function listBlockLists() {
        return this.get("".concat(this.baseURL, "/blocklists"));
      }
    }, {
      key: "getBlockList",
      value: function getBlockList(name) {
        return this.get("".concat(this.baseURL, "/blocklists/").concat(name));
      }
    }, {
      key: "updateBlockList",
      value: function updateBlockList(name, data) {
        return this.put("".concat(this.baseURL, "/blocklists/").concat(name), data);
      }
    }, {
      key: "deleteBlockList",
      value: function deleteBlockList(name) {
        return this.delete("".concat(this.baseURL, "/blocklists/").concat(name));
      }
    }, {
      key: "exportChannels",
      value: function exportChannels(request) {
        var payload = {
          channels: request
        };
        return this.post("".concat(this.baseURL, "/export_channels"), payload);
      }
    }, {
      key: "exportChannel",
      value: function exportChannel(request) {
        return this.exportChannels([request]);
      }
    }, {
      key: "getExportChannelStatus",
      value: function getExportChannelStatus(id) {
        return this.get("".concat(this.baseURL, "/export_channels/").concat(id));
      }
    }], [{
      key: "getInstance",
      value: function getInstance(key, secretOrOptions, options) {
        if (!StreamChat._instance) {
          if (typeof secretOrOptions === 'string') {
            StreamChat._instance = new StreamChat(key, secretOrOptions, options);
          } else {
            StreamChat._instance = new StreamChat(key, secretOrOptions);
          }
        }
  
        return StreamChat._instance;
      }
    }]);
  
    return StreamChat;
  }();
  
  _defineProperty(StreamChat, "_instance", void 0);
  
  var Allow = 'Allow';
  var Deny = 'Deny';
  var AnyResource = ['*'];
  var AnyRole = ['*'];
  var MaxPriority = 999;
  var MinPriority = 1; // deprecated permission object class, you should use the new permission system v2 and use permissions
  // defined in BuiltinPermissions to configure your channel types
  
  var Permission = function Permission(name, priority) {
    var resources = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : AnyResource;
    var roles = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : AnyRole;
    var owner = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
    var action = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : Allow;
  
    _classCallCheck(this, Permission);
  
    _defineProperty(this, "name", void 0);
  
    _defineProperty(this, "action", void 0);
  
    _defineProperty(this, "owner", void 0);
  
    _defineProperty(this, "priority", void 0);
  
    _defineProperty(this, "resources", void 0);
  
    _defineProperty(this, "roles", void 0);
  
    this.name = name;
    this.action = action;
    this.owner = owner;
    this.priority = priority;
    this.resources = resources;
    this.roles = roles;
  }; // deprecated
  
  var AllowAll = new Permission('Allow all', MaxPriority, AnyResource, AnyRole, false, Allow); // deprecated
  
  var DenyAll = new Permission('Deny all', MinPriority, AnyResource, AnyRole, false, Deny);
  var BuiltinRoles = {
    Admin: 'admin',
    Anonymous: 'anonymous',
    ChannelMember: 'channel_member',
    ChannelModerator: 'channel_moderator',
    Guest: 'guest',
    User: 'user'
  };
  var BuiltinPermissions = {
    AddLinks: 'Add Links',
    BanUser: 'Ban User',
    CreateChannel: 'Create Channel',
    CreateMessage: 'Create Message',
    CreateReaction: 'Create Reaction',
    DeleteAnyAttachment: 'Delete Any Attachment',
    DeleteAnyChannel: 'Delete Any Channel',
    DeleteAnyMessage: 'Delete Any Message',
    DeleteAnyReaction: 'Delete Any Reaction',
    DeleteOwnAttachment: 'Delete Own Attachment',
    DeleteOwnChannel: 'Delete Own Channel',
    DeleteOwnMessage: 'Delete Own Message',
    DeleteOwnReaction: 'Delete Own Reaction',
    ReadAnyChannel: 'Read Any Channel',
    ReadOwnChannel: 'Read Own Channel',
    RunMessageAction: 'Run Message Action',
    UpdateAnyChannel: 'Update Any Channel',
    UpdateAnyMessage: 'Update Any Message',
    UpdateMembersAnyChannel: 'Update Members Any Channel',
    UpdateMembersOwnChannel: 'Update Members Own Channel',
    UpdateOwnChannel: 'Update Own Channel',
    UpdateOwnMessage: 'Update Own Message',
    UploadAttachment: 'Upload Attachment',
    UseFrozenChannel: 'Send messages and reactions to frozen channels'
  };
  
  export { Allow, AllowAll, AnyResource, AnyRole, BuiltinPermissions, BuiltinRoles, Channel, ChannelState, CheckSignature, ClientState, Deny, DenyAll, DevToken, EVENT_MAP, JWTServerToken, JWTUserToken, MaxPriority, MinPriority, Permission, StableWSConnection, StreamChat, TokenManager, UserFromToken, chatCodes, decodeBase64, encodeBase64, isOwnUser, isValidEventType, logChatPromiseExecution };
  //# sourceMappingURL=browser.es.js.map
  