"use strict";

var _Reflect$construct = require("@babel/runtime-corejs2/core-js/reflect/construct");
var _Object$keys2 = require("@babel/runtime-corejs2/core-js/object/keys");
var _Object$getOwnPropertySymbols = require("@babel/runtime-corejs2/core-js/object/get-own-property-symbols");
var _Object$getOwnPropertyDescriptor = require("@babel/runtime-corejs2/core-js/object/get-own-property-descriptor");
var _Object$getOwnPropertyDescriptors = require("@babel/runtime-corejs2/core-js/object/get-own-property-descriptors");
var _Object$defineProperties = require("@babel/runtime-corejs2/core-js/object/define-properties");
var _Object$defineProperty = require("@babel/runtime-corejs2/core-js/object/define-property");
var _Array$from = require("@babel/runtime-corejs2/core-js/array/from");
var _Symbol = require("@babel/runtime-corejs2/core-js/symbol");
var _Symbol$iterator = require("@babel/runtime-corejs2/core-js/symbol/iterator");
var _Array$isArray = require("@babel/runtime-corejs2/core-js/array/is-array");
var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault");
_Object$defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var _weakMap = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/weak-map"));
var _map = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/map"));
var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise"));
var _keys = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/keys"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/reflect/define-property"));
var _stringify = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/json/stringify"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/createClass"));
var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/possibleConstructorReturn"));
var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/getPrototypeOf"));
var _inherits2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/inherits"));
var _defineProperty3 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/defineProperty"));
var _isObject2 = _interopRequireDefault(require("lodash/isObject"));
var _has2 = _interopRequireDefault(require("lodash/has"));
var _defaults2 = _interopRequireDefault(require("lodash/defaults"));
var _events = require("events");
var _common = require("@webex/common");
var _commonTimers = require("@webex/common-timers");
var _errors = require("../errors");
var _constants = require("./constants");
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof _Symbol && r[_Symbol$iterator] || r["@@iterator"]; if (!t) { if (_Array$isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, 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 o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? _Array$from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
function ownKeys(e, r) { var t = _Object$keys2(e); if (_Object$getOwnPropertySymbols) { var o = _Object$getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return _Object$getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty3.default)(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(e, _Object$getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { _Object$defineProperty(e, r, _Object$getOwnPropertyDescriptor(t, r)); }); } return e; }
function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? _Reflect$construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); }
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } /*!
 * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
 */ // @ts-ignore - type not available
// @ts-ignore - type not available
var sockets = new _weakMap.default();
function createTrackingId() {
  return "webex-js-sdk_".concat(crypto.randomUUID());
}

/**
 * Generalized socket abstraction
 */
var Socket = exports.default = /*#__PURE__*/function (_EventEmitter) {
  /**
   * Creates a new Socket instance.
   */
  function Socket() {
    var _this;
    (0, _classCallCheck2.default)(this, Socket);
    _this = _callSuper(this, Socket);
    (0, _defineProperty3.default)(_this, "domain", void 0);
    (0, _defineProperty3.default)(_this, "pendingResponses", void 0);
    (0, _defineProperty3.default)(_this, "forceCloseDelay", void 0);
    (0, _defineProperty3.default)(_this, "logger", void 0);
    (0, _defineProperty3.default)(_this, "refreshToken", void 0);
    (0, _defineProperty3.default)(_this, "token", void 0);
    (0, _defineProperty3.default)(_this, "trackingId", void 0);
    (0, _defineProperty3.default)(_this, "wssResponseTimeout", void 0);
    _this.domain = 'unknown-domain';
    _this.pendingResponses = new _map.default();
    _this.onmessage = _this.onmessage.bind(_this);
    _this.onclose = _this.onclose.bind(_this);
    _this.setMaxListeners(10);
    return _this;
  }

  // TODO: Circle back and check if we are using binaryType and related getters. If not, we can remove these.
  /**
   * Gets the binary type of the WebSocket connection.
   * @see https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
   */
  (0, _inherits2.default)(Socket, _EventEmitter);
  return (0, _createClass2.default)(Socket, [{
    key: "binaryType",
    get: function get() {
      var _sockets$get;
      return ((_sockets$get = sockets.get(this)) === null || _sockets$get === void 0 ? void 0 : _sockets$get.binaryType) || '';
    }

    /**
     * Gets the number of bytes of data that have been queued but not yet transmitted.
     * @see https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
     */
  }, {
    key: "bufferedAmount",
    get: function get() {
      var _sockets$get2;
      return ((_sockets$get2 = sockets.get(this)) === null || _sockets$get2 === void 0 ? void 0 : _sockets$get2.bufferedAmount) || 0;
    }

    /**
     * Gets the extensions selected by the server.
     * @see https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
     */
  }, {
    key: "extensions",
    get: function get() {
      var _sockets$get3;
      return ((_sockets$get3 = sockets.get(this)) === null || _sockets$get3 === void 0 ? void 0 : _sockets$get3.extensions) || '';
    }

    /**
     * Gets the sub-protocol selected by the server.
     * @see https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
     */
  }, {
    key: "protocol",
    get: function get() {
      var _sockets$get4;
      return ((_sockets$get4 = sockets.get(this)) === null || _sockets$get4 === void 0 ? void 0 : _sockets$get4.protocol) || '';
    }

    /**
     * Gets the current state of the WebSocket connection.
     * @see https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
     */
  }, {
    key: "readyState",
    get: function get() {
      var _sockets$get5;
      return ((_sockets$get5 = sockets.get(this)) === null || _sockets$get5 === void 0 ? void 0 : _sockets$get5.readyState) || 0;
    }

    /**
     * Gets the URL of the WebSocket connection.
     * @see https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
     */
  }, {
    key: "url",
    get: function get() {
      var _sockets$get6;
      return ((_sockets$get6 = sockets.get(this)) === null || _sockets$get6 === void 0 ? void 0 : _sockets$get6.url) || '';
    }

    /**
     * Provides the environmentally appropriate WebSocket constructor.
     *
     * @returns The WebSocket constructor (ws in NodeJS, WebSocket in browsers)
     */
  }, {
    key: "close",
    value:
    /**
     * Closes the socket.
     * @param options - Close options containing code and reason
     * @returns Promise that resolves when the socket is closed
     */
    function close(options) {
      var _this2 = this;
      return new _promise.default(function (resolve, reject) {
        var socket = sockets.get(_this2);
        if (!socket) {
          // Open has not been called yet so there is no socket to close
          resolve();
          return;
        }
        // logger is defined once open is called
        _this2.logger.info("socket,".concat(_this2.domain, ": closing"));
        if (socket.readyState === _constants.SOCKET_READY_STATE.CLOSING || socket.readyState === _constants.SOCKET_READY_STATE.CLOSED) {
          _this2.logger.info("socket,".concat(_this2.domain, ": already closed"));
          resolve();
          return;
        }
        var originalCode = options === null || options === void 0 ? void 0 : options.code;
        var originalReason = options === null || options === void 0 ? void 0 : options.reason;
        var resolvedOptions = (0, _defaults2.default)(options || {}, {
          code: 1000,
          reason: 'Done'
        });
        if (resolvedOptions.code && resolvedOptions.code !== 1000 && (resolvedOptions.code < 3000 || resolvedOptions.code > 4999)) {
          reject(new Error('`options.code` must be 1000 or between 3000 and 4999 (inclusive)'));
          return;
        }
        var closeTimer = (0, _commonTimers.safeSetTimeout)(function () {
          try {
            _this2.logger.info("socket,".concat(_this2.domain, ": no close event received, forcing closure"));
            resolve(_this2.onclose(originalCode ? {
              code: originalCode,
              reason: originalReason || 'Done (unknown)'
            } : {
              code: 1000,
              reason: 'Done (forced)'
            }));
          } catch (error) {
            _this2.logger.warn("socket,".concat(_this2.domain, ": force-close failed"), error);
          }
        }, _this2.forceCloseDelay);
        socket.onclose = function (event) {
          _this2.logger.info("socket,".concat(_this2.domain, ": close event fired"), event.code, event.reason);
          clearTimeout(closeTimer);
          _this2.onclose(event);
          resolve(event);
        };

        // If socket is still connecting, manually trigger close handler with desired code
        // because calling close() on a CONNECTING socket may not preserve custom codes
        if (socket.readyState === _constants.SOCKET_READY_STATE.CONNECTING) {
          _this2.logger.info("socket,".concat(_this2.domain, ": socket still connecting, triggering close manually"));
          clearTimeout(closeTimer);
          var closeEvent = {
            code: resolvedOptions.code,
            reason: resolvedOptions.reason
          };
          _this2.onclose(closeEvent);
          resolve(closeEvent);
          try {
            socket.close(resolvedOptions.code, resolvedOptions.reason);
          } catch (error) {
            _this2.logger.info("socket,".concat(_this2.domain, ": error while closing CONNECTING socket, likely due to browser incompatibility with custom close codes"), error);
          }
        } else {
          socket.close(resolvedOptions.code, resolvedOptions.reason);
        }
      });
    }

    /**
     * Opens a WebSocket connection and performs authentication.
     *
     * @param url - WebSocket URL to connect to
     * @param options - Socket connection options including token, logger, and timeout settings
     * @returns Promise that resolves when socket is opened and authenticated
     */
  }, {
    key: "open",
    value: function open(url, options) {
      var _this3 = this;
      try {
        this.domain = new URL(url).hostname;
      } catch (_unused) {
        this.domain = url;
      }
      return new _promise.default(function (resolve, reject) {
        /* eslint complexity: [0] */
        if (!url) {
          reject(new Error('`url` is required'));
          return;
        }
        if (sockets.get(_this3)) {
          reject(new Error('Socket#open() can only be called once per instance'));
          return;
        }
        var resolvedOptions = options || {};
        (0, _common.checkRequired)(['forceCloseDelay', 'token', 'trackingId', 'logger'], resolvedOptions);
        (0, _keys.default)(resolvedOptions).forEach(function (key) {
          (0, _defineProperty2.default)(_this3, key, {
            enumerable: false,
            value: resolvedOptions[key]
          });
        });
        var WebSocket = Socket.getWebSocketConstructor();
        _this3.logger.info("socket,".concat(_this3.domain, ": creating WebSocket"));
        var socket = new WebSocket(url, [], resolvedOptions);
        socket.binaryType = 'arraybuffer';
        socket.onmessage = _this3.onmessage;
        socket.onclose = function (event) {
          event = _this3.fixCloseCode(event);
          _this3.logger.info("socket,".concat(_this3.domain, ": closed before open"), event.code, event.reason);
          switch (event.code) {
            case 1005:
              // IE 11 doesn't seem to allow 4XXX codes, so if we get a 1005, assume
              // it's a bad websocket url. That'll trigger a device refresh; if it
              // turns out we had a bad token, the device refresh should 401 and
              // trigger a token refresh.
              return reject(new _errors.UnknownResponse(event));
            case 4400:
              return reject(new _errors.BadRequest(event));
            case 4401:
              return reject(new _errors.NotAuthorized(event));
            case 4403:
              return reject(new _errors.Forbidden(event));
            default:
              return reject(new _errors.ConnectionError(event));
          }
        };
        socket.onopen = function () {
          _this3.logger.info("socket,".concat(_this3.domain, ": connected"));
          _this3.authorize(_this3.token).then(function () {
            _this3.logger.info("socket,".concat(_this3.domain, ": authorized"));
            socket.onclose = _this3.onclose;
            resolve();
          }).catch(reject);
        };
        socket.onerror = function (event) {
          _this3.logger.warn("socket,".concat(_this3.domain, ": error event fired"), event);
        };
        sockets.set(_this3, socket);
        _this3.logger.info("socket,".concat(_this3.domain, ": waiting for server"));
      });
    }

    /**
     * Handles incoming CloseEvents.
     * @param event - The close event containing code and reason
     */
  }, {
    key: "onclose",
    value: function onclose(event) {
      this.logger.info("socket,".concat(this.domain, ": closed"), event.code, event.reason);
      event = this.fixCloseCode(event);
      this.rejectPendingResponses(new _errors.ConnectionError(event));
      this.emit('close', event);

      // Remove all listeners to (a) avoid reacting to late pongs and (b) ensure
      // we don't have a retain cycle.
      this.removeAllListeners();
    }

    /**
     * Handles incoming message events from the WebSocket.
     * @param event - The message event containing the data
     */
  }, {
    key: "onmessage",
    value: function onmessage(event) {
      var _this4 = this;
      try {
        var data = JSON.parse(event.data);
        var processedEvent = {
          data: data
        };
        if (data.type === 'async_event') {
          this.acknowledge(processedEvent).catch(function (error) {
            _this4.logger.warn("socket,".concat(_this4.domain, ": failed to acknowledge async event"), error);
          });
        }

        // Match pending request/response promises before emitting the public message event.
        // The message is still emitted afterward for any external listeners that care about it.
        this.handlePendingResponse(data);
        this.emit('message', processedEvent);
      } catch (error) {
        /* istanbul ignore next */
        this.logger.warn("socket,".concat(this.domain, ": error while receiving WebSocket message"), error);
      }
    }

    /**
     * Sends a message up the socket.
     * @param data - Data to send (string or object that will be JSON stringified)
     * @returns Promise that resolves when the message is sent
     */
  }, {
    key: "send",
    value: function send(data) {
      var _this5 = this;
      return new _promise.default(function (resolve, reject) {
        if (_this5.readyState !== _constants.SOCKET_READY_STATE.OPEN) {
          reject(new Error('INVALID_STATE_ERROR'));
          return;
        }
        if ((0, _isObject2.default)(data)) {
          data = (0, _stringify.default)(data);
        }
        var socket = sockets.get(_this5);
        if (!socket) {
          reject(new Error('INVALID_STATE_ERROR'));
          return;
        }
        socket.send(data);
        resolve();
      });
    }

    /**
     * Sends a request and resolves when the matching response arrives.
     *
     * @param data - Request data to send over the socket
     * @param options - Request options including timeout and error handlers
     * @returns Promise that resolves with the response data
     */
  }, {
    key: "sendRequest",
    value: function sendRequest(data) {
      var _this6 = this;
      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      if (!(0, _isObject2.default)(data)) {
        return _promise.default.reject(new Error('`data` is required'));
      }
      var request = _objectSpread({}, data);
      var trackingId = request.trackingId || createTrackingId();
      var timeout = options.timeout || this.wssResponseTimeout || 10000;
      if (this.pendingResponses.has(trackingId)) {
        return _promise.default.reject(new Error("socket request already sent for trackingId ".concat(trackingId)));
      }
      request.trackingId = trackingId;
      return new _promise.default(function (_resolve, _reject) {
        var timeoutId = (0, _commonTimers.safeSetTimeout)(function () {
          _this6.clearPendingResponse(trackingId);
          _reject((0, _errors.createTimeoutError)(request));
        }, timeout);
        var pendingResponse = {
          request: request,
          resolve: function resolve(response) {
            _this6.clearPendingResponse(trackingId);
            _resolve(response);
          },
          reject: function reject(error) {
            _this6.clearPendingResponse(trackingId);
            _reject(error);
          },
          timeoutId: timeoutId
        };
        _this6.pendingResponses.set(trackingId, pendingResponse);
        _this6.send(request).catch(function (error) {
          _this6.clearPendingResponse(trackingId);
          _reject(error);
        });
      });
    }

    /**
     * Sends an acknowledgment for a specific async event.
     * @param event - Message event containing the async event to acknowledge
     * @returns Promise that resolves when acknowledgment is sent
     */
  }, {
    key: "acknowledge",
    value: function acknowledge(event) {
      if (!event) {
        return _promise.default.reject(new Error('`event` is required'));
      }
      if (!(0, _has2.default)(event, 'data.eventId')) {
        return _promise.default.reject(new Error('`event.data.eventId` is required'));
      }

      // Don't try to acknowledge if socket is not in open state
      if (this.readyState !== _constants.SOCKET_READY_STATE.OPEN) {
        return _promise.default.resolve();
      }
      return this.send({
        type: _constants.MESSAGE_TYPES.EVENT_ACK,
        trackingId: event.data.trackingId || createTrackingId(),
        eventId: event.data.eventId
      }).catch(function (error) {
        if (error.message === 'INVALID_STATE_ERROR') {
          return _promise.default.resolve();
        }
        throw error;
      });
    }
  }, {
    key: "refresh",
    value: function refresh(token) {
      if (!token) {
        return _promise.default.reject(new Error('`token` is required for Socket#refresh()'));
      }
      var refreshedToken;
      if (typeof token === 'string') {
        refreshedToken = token;
      } else if (token && typeof token.toString === 'function') {
        refreshedToken = token.toString();
      } else {
        refreshedToken = String(token);
      }
      return this.authorize(refreshedToken);
    }

    /**
     * Sends an auth message up the socket with a refreshed token.
     * @param token - Authentication token to send
     * @returns Promise that resolves when authentication succeeds
     */
  }, {
    key: "authorize",
    value: function authorize(token) {
      this.logger.info("socket,".concat(this.domain, ": authorizing"));
      return this.sendRequest({
        type: _constants.MESSAGE_TYPES.AUTH,
        data: {
          token: token
        }
      });
    }

    /**
     * Clears a pending response entry.
     * @param trackingId - Tracking ID of the response to clear
     */
  }, {
    key: "clearPendingResponse",
    value: function clearPendingResponse(trackingId) {
      var pendingResponse = this.pendingResponses.get(trackingId);
      if (pendingResponse !== null && pendingResponse !== void 0 && pendingResponse.timeoutId) {
        clearTimeout(pendingResponse.timeoutId);
      }
      this.pendingResponses.delete(trackingId);
    }

    /**
     * Rejects all pending responses with the provided error.
     * @param error - Error to reject pending responses with
     */
  }, {
    key: "rejectPendingResponses",
    value: function rejectPendingResponses(error) {
      if (!this.pendingResponses.size) {
        return;
      }
      var _iterator = _createForOfIteratorHelper(this.pendingResponses.values()),
        _step;
      try {
        for (_iterator.s(); !(_step = _iterator.n()).done;) {
          var pendingResponse = _step.value;
          pendingResponse.reject(error);
        }
      } catch (err) {
        _iterator.e(err);
      } finally {
        _iterator.f();
      }
    }

    /**
     * Handles incoming responses for pending requests.
     * @param response - Response data to match against pending requests
     * @returns True if a matching pending request was found and handled
     */
  }, {
    key: "handlePendingResponse",
    value: function handlePendingResponse(response) {
      var _this7 = this;
      if (!response) {
        return false;
      }

      // Pending request correlation currently requires trackingId on the response.
      var pendingResponse = this.pendingResponses.get(response.trackingId || '');
      if (!pendingResponse) {
        return false;
      }
      if (response.subtype !== pendingResponse.request.type) {
        return false;
      }
      var statusCode = response === null || response === void 0 ? void 0 : response.statusCode;
      var statusMessage = response === null || response === void 0 ? void 0 : response.statusMessage;
      if (statusCode === 440 && (response === null || response === void 0 ? void 0 : response.subtype) !== _constants.MESSAGE_TYPES.AUTH) {
        if (typeof this.refreshToken === 'function') {
          _promise.default.resolve(this.refreshToken(response)).catch(function (error) {
            _this7.logger.warn("socket,".concat(_this7.domain, ": failed token-expiry re-auth"), error);
          });
        } else {
          this.logger.warn("socket,".concat(this.domain, ": refreshToken callback is unavailable for statusCode 440"));
        }
      }
      if (statusCode === undefined) {
        var error = (0, _errors.createWssResponseError)(response, statusCode, statusMessage || 'Socket response missing status code');
        pendingResponse.reject(error);
      } else if (statusCode >= 200 && statusCode < 300) {
        pendingResponse.resolve(response);
      } else {
        pendingResponse.reject((0, _errors.createWssResponseError)(response, statusCode, statusMessage));
      }
      return true;
    }

    /**
     * Deals with the fact that some browsers drop some close codes (but not close reasons).
     *
     * @param event - Close event to fix
     * @returns Fixed close event with corrected code if necessary
     */
  }, {
    key: "fixCloseCode",
    value: function fixCloseCode(event) {
      if (event.code === 1005 && event.reason) {
        switch (event.reason.toLowerCase()) {
          case 'replaced':
            this.logger.info("socket,".concat(this.domain, ": fixing CloseEvent code for reason: "), event.reason);
            event.code = 4000;
            break;
          case 'authentication failed':
          case 'authentication did not happen within the timeout window of 30000 seconds.':
            this.logger.info("socket,".concat(this.domain, ": fixing CloseEvent code for reason: "), event.reason);
            event.code = 1008;
            break;
          default:
          // do nothing
        }
      }
      return event;
    }
  }], [{
    key: "getWebSocketConstructor",
    value: function getWebSocketConstructor() {
      throw new Error('Socket.getWebSocketConstructor() must be implemented in an environmentally appropriate way');
    }
  }]);
}(_events.EventEmitter);
//# sourceMappingURL=socket-base.js.map
