UNPKG

@jayzyaj/centrifuge-js-cyy

Version:

Centrifuge and Centrifugo client for NodeJS and browser

1,654 lines (1,521 loc) 90.4 kB
(function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define("Centrifuge", [], factory); else if(typeof exports === 'object') exports["Centrifuge"] = factory(); else root["Centrifuge"] = factory(); })(typeof self !== 'undefined' ? self : this, function() { return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { /******/ configurable: false, /******/ enumerable: true, /******/ get: getter /******/ }); /******/ } /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = 31); /******/ }) /************************************************************************/ /******/ ({ /***/ 10: /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(global) { Object.defineProperty(exports, "__esModule", { value: true }); exports.Centrifuge = undefined; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _events = __webpack_require__(6); var _events2 = _interopRequireDefault(_events); var _subscription = __webpack_require__(11); var _subscription2 = _interopRequireDefault(_subscription); var _json = __webpack_require__(12); var _utils = __webpack_require__(7); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _errorTimeout = 'timeout'; var _errorConnectionClosed = 'connection closed'; var Centrifuge = exports.Centrifuge = function (_EventEmitter) { _inherits(Centrifuge, _EventEmitter); function Centrifuge(url, options) { _classCallCheck(this, Centrifuge); var _this = _possibleConstructorReturn(this, (Centrifuge.__proto__ || Object.getPrototypeOf(Centrifuge)).call(this)); _this._url = url; _this._websocket = null; _this._sockjs = null; _this._isSockjs = false; _this._binary = false; _this._methodType = null; _this._pushType = null; _this._encoder = null; _this._decoder = null; _this._status = 'disconnected'; _this._reconnect = true; _this._reconnecting = false; _this._transport = null; _this._transportName = null; _this._transportClosed = true; _this._messageId = 0; _this._clientID = null; _this._refreshRequired = false; _this._subs = {}; _this._serverSubs = {}; _this._lastSeq = {}; _this._lastGen = {}; _this._lastOffset = {}; _this._lastEpoch = {}; _this._messages = []; _this._isBatching = false; _this._isSubscribeBatching = false; _this._privateChannels = {}; _this._numRefreshFailed = 0; _this._refreshTimeout = null; _this._pingTimeout = null; _this._pongTimeout = null; _this._subRefreshTimeouts = {}; _this._retries = 0; _this._callbacks = {}; _this._latency = null; _this._latencyStart = null; _this._connectData = null; _this._token = null; _this._xhrID = 0; _this._xhrs = {}; _this._dispatchPromise = Promise.resolve(); _this._config = { debug: false, websocket: null, sockjs: null, promise: null, minRetry: 1000, maxRetry: 20000, timeout: 5000, ping: true, pingInterval: 25000, pongWaitTimeout: 5000, privateChannelPrefix: '$', onTransportClose: null, sockjsServer: null, sockjsTransports: ['websocket', 'xdr-streaming', 'xhr-streaming', 'eventsource', 'iframe-eventsource', 'iframe-htmlfile', 'xdr-polling', 'xhr-polling', 'iframe-xhr-polling', 'jsonp-polling'], refreshEndpoint: '/centrifuge/refresh', refreshHeaders: {}, refreshParams: {}, refreshData: {}, refreshAttempts: null, refreshInterval: 1000, onRefreshFailed: null, onRefresh: null, subscribeEndpoint: '/centrifuge/subscribe', subscribeHeaders: {}, subscribeParams: {}, subRefreshInterval: 1000, onPrivateSubscribe: null }; _this._configure(options); return _this; } _createClass(Centrifuge, [{ key: 'setToken', value: function setToken(token) { this._token = token; } }, { key: 'setConnectData', value: function setConnectData(data) { this._connectData = data; } }, { key: 'setRefreshHeaders', value: function setRefreshHeaders(headers) { this._config.refreshHeaders = headers; } }, { key: 'setRefreshParams', value: function setRefreshParams(params) { this._config.refreshParams = params; } }, { key: 'setRefreshData', value: function setRefreshData(data) { this._config.refreshData = data; } }, { key: 'setSubscribeHeaders', value: function setSubscribeHeaders(headers) { this._config.subscribeHeaders = headers; } }, { key: 'setSubscribeParams', value: function setSubscribeParams(params) { this._config.subscribeParams = params; } }, { key: '_ajax', value: function _ajax(url, params, headers, data, callback) { var _this2 = this; var query = ''; this._debug('sending AJAX request to', url, 'with data', JSON.stringify(data)); var xhr = global.XMLHttpRequest ? new global.XMLHttpRequest() : new global.ActiveXObject('Microsoft.XMLHTTP'); for (var i in params) { if (params.hasOwnProperty(i)) { if (query.length > 0) { query += '&'; } query += encodeURIComponent(i) + '=' + encodeURIComponent(params[i]); } } if (query.length > 0) { query = '?' + query; } xhr.open('POST', url + query, true); if ('withCredentials' in xhr) { xhr.withCredentials = true; } xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); xhr.setRequestHeader('Content-Type', 'application/json'); for (var headerName in headers) { if (headers.hasOwnProperty(headerName)) { xhr.setRequestHeader(headerName, headers[headerName]); } } xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status === 200) { var _data = void 0, parsed = false; try { _data = JSON.parse(xhr.responseText); parsed = true; } catch (e) { callback({ error: 'Invalid JSON. Data was: ' + xhr.responseText, status: 200, data: null }); } if (parsed) { // prevents double execution. callback({ data: _data, status: 200 }); } } else { _this2._log('wrong status code in AJAX response', xhr.status); callback({ status: xhr.status, data: null }); } } }; setTimeout(function () { return xhr.send(JSON.stringify(data)); }, 20); return xhr; } }, { key: '_log', value: function _log() { (0, _utils.log)('info', arguments); } }, { key: '_debug', value: function _debug() { if (this._config.debug === true) { (0, _utils.log)('debug', arguments); } } }, { key: '_websocketSupported', value: function _websocketSupported() { if (this._config.websocket !== null) { return true; } return !(typeof WebSocket !== 'function' && (typeof WebSocket === 'undefined' ? 'undefined' : _typeof(WebSocket)) !== 'object'); } }, { key: '_setFormat', value: function _setFormat(format) { if (this._formatOverride(format)) { return; } if (format === 'protobuf') { throw new Error('not implemented by JSON only Centrifuge client – use client with Protobuf'); } this._binary = false; this._methodType = _json.JsonMethodType; this._pushType = _json.JsonPushType; this._encoder = new _json.JsonEncoder(); this._decoder = new _json.JsonDecoder(); } }, { key: '_formatOverride', value: function _formatOverride(format) { return false; } }, { key: '_configure', value: function _configure(configuration) { if (!('Promise' in global)) { throw new Error('Promise polyfill required'); } (0, _utils.extend)(this._config, configuration || {}); this._debug('centrifuge config', this._config); if (!this._url) { throw new Error('url required'); } if ((0, _utils.startsWith)(this._url, 'ws') && this._url.indexOf('format=protobuf') > -1) { this._setFormat('protobuf'); } else { this._setFormat('json'); } if ((0, _utils.startsWith)(this._url, 'http')) { this._debug('client will try to connect to SockJS endpoint'); if (this._config.sockjs !== null) { this._debug('SockJS explicitly provided in options'); this._sockjs = this._config.sockjs; } else { if (typeof global.SockJS === 'undefined') { throw new Error('SockJS not found, use ws:// in url or include SockJS'); } this._debug('use globally defined SockJS'); this._sockjs = global.SockJS; } } else { this._debug('client will connect to websocket endpoint'); } } }, { key: '_setStatus', value: function _setStatus(newStatus) { if (this._status !== newStatus) { this._debug('Status', this._status, '->', newStatus); this._status = newStatus; } } }, { key: '_isDisconnected', value: function _isDisconnected() { return this._status === 'disconnected'; } }, { key: '_isConnecting', value: function _isConnecting() { return this._status === 'connecting'; } }, { key: '_isConnected', value: function _isConnected() { return this._status === 'connected'; } }, { key: '_nextMessageId', value: function _nextMessageId() { return ++this._messageId; } }, { key: '_resetRetry', value: function _resetRetry() { this._debug('reset retries count to 0'); this._retries = 0; } }, { key: '_getRetryInterval', value: function _getRetryInterval() { var interval = (0, _utils.backoff)(this._retries, this._config.minRetry, this._config.maxRetry); this._retries += 1; return interval; } }, { key: '_abortInflightXHRs', value: function _abortInflightXHRs() { for (var xhrID in this._xhrs) { try { this._xhrs[xhrID].abort(); } catch (e) { this._debug('error aborting xhr', e); } delete this._xhrs[xhrID]; } } }, { key: '_clearConnectedState', value: function _clearConnectedState(reconnect) { this._clientID = null; this._stopPing(); // fire errbacks of registered outgoing calls. for (var id in this._callbacks) { if (this._callbacks.hasOwnProperty(id)) { var callbacks = this._callbacks[id]; clearTimeout(callbacks.timeout); var errback = callbacks.errback; if (!errback) { continue; } errback({ error: this._createErrorObject('disconnected') }); } } this._callbacks = {}; // fire unsubscribe events for (var channel in this._subs) { if (this._subs.hasOwnProperty(channel)) { var sub = this._subs[channel]; if (reconnect) { if (sub._isSuccess()) { sub._triggerUnsubscribe(); sub._recover = true; } if (sub._shouldResubscribe()) { sub._setSubscribing(); } } else { sub._setUnsubscribed(); } } } this._abortInflightXHRs(); // clear refresh timer if (this._refreshTimeout !== null) { clearTimeout(this._refreshTimeout); this._refreshTimeout = null; } // clear sub refresh timers for (var _channel in this._subRefreshTimeouts) { if (this._subRefreshTimeouts.hasOwnProperty(_channel) && this._subRefreshTimeouts[_channel]) { this._clearSubRefreshTimeout(_channel); } } this._subRefreshTimeouts = {}; if (!this._reconnect) { // completely clear subscriptions this._subs = {}; } } }, { key: '_isTransportOpen', value: function _isTransportOpen() { if (this._isSockjs) { return this._transport && this._transport.transport && this._transport.transport.readyState === this._transport.transport.OPEN; } return this._transport && this._transport.readyState === this._transport.OPEN; } }, { key: '_transportSend', value: function _transportSend(commands) { if (!commands.length) { return true; } if (!this._isTransportOpen()) { // resolve pending commands with error if transport is not open for (var command in commands) { var id = command.id; if (!(id in this._callbacks)) { continue; } var callbacks = this._callbacks[id]; clearTimeout(this._callbacks[id].timeout); delete this._callbacks[id]; var errback = callbacks.errback; errback({ error: this._createErrorObject(_errorConnectionClosed, 0) }); } return false; } this._transport.send(this._encoder.encodeCommands(commands)); return true; } }, { key: '_setupTransport', value: function _setupTransport() { var _this3 = this; this._isSockjs = false; // detect transport to use - SockJS or Websocket if (this._sockjs !== null) { var sockjsOptions = { transports: this._config.sockjsTransports }; if (this._config.sockjsServer !== null) { sockjsOptions.server = this._config.sockjsServer; } this._isSockjs = true; this._transport = new this._sockjs(this._url, null, sockjsOptions); } else { if (!this._websocketSupported()) { this._debug('No Websocket support and no SockJS configured, can not connect'); return; } if (this._config.websocket !== null) { this._websocket = this._config.websocket; } else { this._websocket = WebSocket; } this._transport = new this._websocket(this._url); if (this._binary === true) { this._transport.binaryType = 'arraybuffer'; } } this._transport.onopen = function () { _this3._transportClosed = false; if (_this3._isSockjs) { _this3._transportName = 'sockjs-' + _this3._transport.transport; _this3._transport.onheartbeat = function () { return _this3._restartPing(); }; } else { _this3._transportName = 'websocket'; } // Can omit method here due to zero value. var msg = { // method: this._methodType.CONNECT }; if (_this3._token || _this3._connectData) { msg.params = {}; } if (_this3._token) { msg.params.token = _this3._token; } if (_this3._connectData) { msg.params.data = _this3._connectData; } var subs = {}; var hasSubs = false; for (var channel in _this3._serverSubs) { if (_this3._serverSubs.hasOwnProperty(channel) && _this3._serverSubs[channel].recoverable) { hasSubs = true; var sub = { 'recover': true }; if (_this3._serverSubs[channel].seq || _this3._serverSubs[channel].gen) { if (_this3._serverSubs[channel].seq) { sub['seq'] = _this3._serverSubs[channel].seq; } if (_this3._serverSubs[channel].gen) { sub['gen'] = _this3._serverSubs[channel].gen; } } else { if (_this3._serverSubs[channel].offset) { sub['offset'] = _this3._serverSubs[channel].offset; } } if (_this3._serverSubs[channel].epoch) { sub['epoch'] = _this3._serverSubs[channel].epoch; } subs[channel] = sub; } } if (hasSubs) { if (!msg.params) { msg.params = {}; } msg.params.subs = subs; } _this3._latencyStart = new Date(); _this3._call(msg).then(function (resolveCtx) { _this3._connectResponse(_this3._decoder.decodeCommandResult(_this3._methodType.CONNECT, resolveCtx.result), hasSubs); if (resolveCtx.next) { resolveCtx.next(); } }, function (rejectCtx) { var err = rejectCtx.error; if (err.code === 109) { // token expired. _this3._refreshRequired = true; } _this3._disconnect('connect error', true); if (rejectCtx.next) { rejectCtx.next(); } }); }; this._transport.onerror = function (error) { _this3._debug('transport level error', error); }; this._transport.onclose = function (closeEvent) { _this3._transportClosed = true; var reason = _errorConnectionClosed; var needReconnect = true; if (closeEvent && 'reason' in closeEvent && closeEvent.reason) { try { var advice = JSON.parse(closeEvent.reason); _this3._debug('reason is an advice object', advice); reason = advice.reason; needReconnect = advice.reconnect; } catch (e) { reason = closeEvent.reason; _this3._debug('reason is a plain string', reason); } } // onTransportClose callback should be executed every time transport was closed. // This can be helpful to catch failed connection events (because our disconnect // event only called once and every future attempts to connect do not fire disconnect // event again). if (_this3._config.onTransportClose !== null) { _this3._config.onTransportClose({ event: closeEvent, reason: reason, reconnect: needReconnect }); } _this3._disconnect(reason, needReconnect); if (_this3._reconnect === true) { _this3._reconnecting = true; var interval = _this3._getRetryInterval(); _this3._debug('reconnect after ' + interval + ' milliseconds'); setTimeout(function () { if (_this3._reconnect === true) { if (_this3._refreshRequired) { _this3._refresh(); } else { _this3._connect(); } } }, interval); } }; this._transport.onmessage = function (event) { _this3._dataReceived(event.data); }; } }, { key: 'rpc', value: function rpc(data) { return this._rpc('', data); } }, { key: 'namedRPC', value: function namedRPC(method, data) { return this._rpc(method, data); } }, { key: '_rpc', value: function _rpc(method, data) { var _this4 = this; var params = { data: data }; if (method !== '') { params.method = method; }; var msg = { method: this._methodType.RPC, params: params }; if (!this.isConnected()) { return Promise.reject(this._createErrorObject(_errorConnectionClosed, 0)); } return this._call(msg).then(function (resolveCtx) { if (resolveCtx.next) { resolveCtx.next(); } return _this4._decoder.decodeCommandResult(_this4._methodType.RPC, resolveCtx.result); }, function (rejectCtx) { if (rejectCtx.next) { rejectCtx.next(); } return Promise.reject(rejectCtx.error); }); } }, { key: 'send', value: function send(data) { var msg = { method: this._methodType.SEND, params: { data: data } }; if (!this.isConnected()) { return Promise.reject(this._createErrorObject(_errorConnectionClosed, 0)); } var sent = this._transportSend([msg]); // can send async message to server without id set if (!sent) { return Promise.reject(this._createErrorObject(_errorConnectionClosed, 0)); }; return Promise.resolve({}); } }, { key: 'publish', value: function publish(channel, data) { var msg = { method: this._methodType.PUBLISH, params: { channel: channel, data: data } }; if (!this.isConnected()) { return Promise.reject(this._createErrorObject(_errorConnectionClosed, 0)); } return this._call(msg).then(function (result) { if (result.next) { result.next(); } return {}; }); } }, { key: '_dataReceived', value: function _dataReceived(data) { var _this5 = this; var replies = this._decoder.decodeReplies(data); // we have to guarantee order of events in replies processing - i.e. start processing // next reply only when we finished processing of current one. Without syncing things in // this way we could get wrong publication events order as reply promises resolve // on next loop tick so for loop continues before we finished emitting all reply events. this._dispatchPromise = this._dispatchPromise.then(function () { var finishDispatch = void 0; _this5._dispatchPromise = new Promise(function (resolve) { finishDispatch = resolve; }); _this5._dispatchSynchronized(replies, finishDispatch); }); this._restartPing(); } }, { key: '_dispatchSynchronized', value: function _dispatchSynchronized(replies, finishDispatch) { var _this6 = this; var p = Promise.resolve(); var _loop = function _loop(i) { if (replies.hasOwnProperty(i)) { p = p.then(function () { return _this6._dispatchReply(replies[i]); }); } }; for (var i in replies) { _loop(i); } p = p.then(function () { finishDispatch(); }); } }, { key: '_dispatchReply', value: function _dispatchReply(reply) { var next; var p = new Promise(function (resolve) { next = resolve; }); if (reply === undefined || reply === null) { this._debug('dispatch: got undefined or null reply'); next(); return p; } var id = reply.id; if (id && id > 0) { this._handleReply(reply, next); } else { this._handlePush(reply.result, next); } return p; } }, { key: '_call', value: function _call(msg) { var _this7 = this; return new Promise(function (resolve, reject) { var id = _this7._addMessage(msg); _this7._registerCall(id, resolve, reject); }); } }, { key: '_connect', value: function _connect() { if (this.isConnected()) { this._debug('connect called when already connected'); return; } if (this._status === 'connecting') { return; } this._debug('start connecting'); this._setStatus('connecting'); this._clientID = null; this._reconnect = true; this._setupTransport(); } }, { key: '_disconnect', value: function _disconnect(reason, shouldReconnect) { var reconnect = shouldReconnect || false; if (reconnect === false) { this._reconnect = false; } if (this._isDisconnected()) { if (!reconnect) { this._clearConnectedState(reconnect); } return; } this._clearConnectedState(reconnect); this._debug('disconnected:', reason, shouldReconnect); this._setStatus('disconnected'); if (this._refreshTimeout) { clearTimeout(this._refreshTimeout); this._refreshTimeout = null; } if (this._reconnecting === false) { // fire unsubscribe events for server side subs. for (var channel in this._serverSubs) { if (this._serverSubs.hasOwnProperty(channel)) { this.emit('unsubscribe', { channel: channel }); } } this.emit('disconnect', { reason: reason, reconnect: reconnect }); } if (reconnect === false) { this._subs = {}; this._serverSubs = {}; } if (!this._transportClosed) { this._transport.close(); } } }, { key: '_refreshFailed', value: function _refreshFailed() { this._numRefreshFailed = 0; if (!this._isDisconnected()) { this._disconnect('refresh failed', false); } if (this._config.onRefreshFailed !== null) { this._config.onRefreshFailed(); } } }, { key: '_refresh', value: function _refresh() { var _this8 = this; // ask application for new connection token. this._debug('refresh token'); if (this._config.refreshAttempts === 0) { this._debug('refresh attempts set to 0, do not send refresh request at all'); this._refreshFailed(); return; } if (this._refreshTimeout !== null) { clearTimeout(this._refreshTimeout); this._refreshTimeout = null; } var clientID = this._clientID; var xhrID = this._newXHRID(); var cb = function cb(resp) { if (xhrID in _this8._xhrs) { delete _this8._xhrs[xhrID]; } if (_this8._clientID !== clientID) { return; } if (resp.error || resp.status !== 200) { // We don't perform any connection status related actions here as we are // relying on server that must close connection eventually. if (resp.error) { _this8._debug('error refreshing connection token', resp.error); } else { _this8._debug('error refreshing connection token: wrong status code', resp.status); } _this8._numRefreshFailed++; if (_this8._refreshTimeout !== null) { clearTimeout(_this8._refreshTimeout); _this8._refreshTimeout = null; } if (_this8._config.refreshAttempts !== null && _this8._numRefreshFailed >= _this8._config.refreshAttempts) { _this8._refreshFailed(); return; } var jitter = Math.round(Math.random() * 1000 * Math.max(_this8._numRefreshFailed, 20)); var interval = _this8._config.refreshInterval + jitter; _this8._refreshTimeout = setTimeout(function () { return _this8._refresh(); }, interval); return; } _this8._numRefreshFailed = 0; _this8._token = resp.data.token; if (!_this8._token) { _this8._refreshFailed(); return; } if (_this8._isDisconnected() && _this8._reconnect) { _this8._debug('token refreshed, connect from scratch'); _this8._connect(); } else { _this8._debug('send refreshed token'); var msg = { method: _this8._methodType.REFRESH, params: { token: _this8._token } }; _this8._call(msg).then(function (resolveCtx) { _this8._refreshResponse(_this8._decoder.decodeCommandResult(_this8._methodType.REFRESH, resolveCtx.result)); if (resolveCtx.next) { resolveCtx.next(); } }, function (rejectCtx) { _this8._refreshError(rejectCtx.error); if (rejectCtx.next) { rejectCtx.next(); } }); } }; if (this._config.onRefresh !== null) { var context = {}; this._config.onRefresh(context, cb); } else { var xhr = this._ajax(this._config.refreshEndpoint, this._config.refreshParams, this._config.refreshHeaders, this._config.refreshData, cb); this._xhrs[xhrID] = xhr; } } }, { key: '_refreshError', value: function _refreshError(err) { var _this9 = this; this._debug('refresh error', err); if (this._refreshTimeout) { clearTimeout(this._refreshTimeout); this._refreshTimeout = null; } var interval = this._config.refreshInterval + Math.round(Math.random() * 1000); this._refreshTimeout = setTimeout(function () { return _this9._refresh(); }, interval); } }, { key: '_refreshResponse', value: function _refreshResponse(result) { var _this10 = this; if (this._refreshTimeout) { clearTimeout(this._refreshTimeout); this._refreshTimeout = null; } if (result.expires) { this._clientID = result.client; this._refreshTimeout = setTimeout(function () { return _this10._refresh(); }, this._getTTLMilliseconds(result.ttl)); } } }, { key: '_newXHRID', value: function _newXHRID() { this._xhrID++; return this._xhrID; } }, { key: '_subRefresh', value: function _subRefresh(channel) { var _this11 = this; this._debug('refresh subscription token for channel', channel); if (this._subRefreshTimeouts[channel] !== undefined) { this._clearSubRefreshTimeout(channel); } else { return; } var clientID = this._clientID; var xhrID = this._newXHRID(); var cb = function cb(resp) { if (xhrID in _this11._xhrs) { delete _this11._xhrs[xhrID]; } if (resp.error || resp.status !== 200 || _this11._clientID !== clientID) { return; } var channelsData = {}; if (resp.data.channels) { for (var i in resp.data.channels) { var channelData = resp.data.channels[i]; if (!channelData.channel) { continue; } channelsData[channelData.channel] = channelData.token; } } var token = channelsData[channel]; if (!token) { return; } var msg = { method: _this11._methodType.SUB_REFRESH, params: { channel: channel, token: token } }; var sub = _this11._getSub(channel); if (sub === null) { return; } _this11._call(msg).then(function (resolveCtx) { _this11._subRefreshResponse(channel, _this11._decoder.decodeCommandResult(_this11._methodType.SUB_REFRESH, resolveCtx.result)); if (resolveCtx.next) { resolveCtx.next(); } }, function (rejectCtx) { _this11._subRefreshError(channel, rejectCtx.error); if (rejectCtx.next) { rejectCtx.next(); } }); }; var data = { client: this._clientID, channels: [channel] }; if (this._config.onPrivateSubscribe !== null) { this._config.onPrivateSubscribe({ data: data }, cb); } else { var xhr = this._ajax(this._config.subscribeEndpoint, this._config.subscribeParams, this._config.subscribeHeaders, data, cb); this._xhrs[xhrID] = xhr; } } }, { key: '_clearSubRefreshTimeout', value: function _clearSubRefreshTimeout(channel) { if (this._subRefreshTimeouts[channel] !== undefined) { clearTimeout(this._subRefreshTimeouts[channel]); delete this._subRefreshTimeouts[channel]; } } }, { key: '_subRefreshError', value: function _subRefreshError(channel, err) { var _this12 = this; this._debug('subscription refresh error', channel, err); this._clearSubRefreshTimeout(channel); var sub = this._getSub(channel); if (sub === null) { return; } var jitter = Math.round(Math.random() * 1000); var subRefreshTimeout = setTimeout(function () { return _this12._subRefresh(channel); }, this._config.subRefreshInterval + jitter); this._subRefreshTimeouts[channel] = subRefreshTimeout; return; } }, { key: '_subRefreshResponse', value: function _subRefreshResponse(channel, result) { var _this13 = this; this._debug('subscription refresh success', channel); this._clearSubRefreshTimeout(channel); var sub = this._getSub(channel); if (sub === null) { return; } if (result.expires === true) { var subRefreshTimeout = setTimeout(function () { return _this13._subRefresh(channel); }, this._getTTLMilliseconds(result.ttl)); this._subRefreshTimeouts[channel] = subRefreshTimeout; } return; } }, { key: '_subscribe', value: function _subscribe(sub, isResubscribe) { var _this14 = this; this._debug('subscribing on', sub.channel); var channel = sub.channel; if (!(channel in this._subs)) { this._subs[channel] = sub; } if (!this.isConnected()) { // subscribe will be called later sub._setNew(); return; } sub._setSubscribing(isResubscribe); var msg = { method: this._methodType.SUBSCRIBE, params: { channel: channel } }; // If channel name does not start with privateChannelPrefix - then we // can just send subscription message to Centrifuge. If channel name // starts with privateChannelPrefix - then this is a private channel // and we should ask web application backend for permission first. if ((0, _utils.startsWith)(channel, this._config.privateChannelPrefix)) { // private channel. if (this._isSubscribeBatching) { this._privateChannels[channel] = true; } else { this.startSubscribeBatching(); this._subscribe(sub); this.stopSubscribeBatching(); } } else { var recover = sub._needRecover(); if (recover === true) { msg.params.recover = true; var seq = this._getLastSeq(channel); var gen = this._getLastGen(channel); if (seq || gen) { if (seq) { msg.params.seq = seq; } if (gen) { msg.params.gen = gen; } } else { var offset = this._getLastOffset(channel); if (offset) { msg.params.offset = offset; } } var epoch = this._getLastEpoch(channel); if (epoch) { msg.params.epoch = epoch; } } this._call(msg).then(function (resolveCtx) { _this14._subscribeResponse(channel, recover, _this14._decoder.decodeCommandResult(_this14._methodType.SUBSCRIBE, resolveCtx.result)); if (resolveCtx.next) { resolveCtx.next(); } }, function (rejectCtx) { _this14._subscribeError(channel, rejectCtx.error); if (rejectCtx.next) { rejectCtx.next(); } }); } } }, { key: '_unsubscribe', value: function _unsubscribe(sub) { delete this._subs[sub.channel]; delete this._lastOffset[sub.channel]; delete this._lastSeq[sub.channel]; delete this._lastGen[sub.channel]; if (this.isConnected()) { // No need to unsubscribe in disconnected state - i.e. client already unsubscribed. this._addMessage({ method: this._methodType.UNSUBSCRIBE, params: { channel: sub.channel } }); } } }, { key: '_getTTLMilliseconds', value: function _getTTLMilliseconds(ttl) { // https://stackoverflow.com/questions/12633405/what-is-the-maximum-delay-for-setinterval return Math.min(ttl * 1000, 2147483647); } }, { key: 'getSub', value: function getSub(channel) { return this._getSub(channel); } }, { key: '_getSub', value: function _getSub(channel) { var sub = this._subs[channel]; if (!sub) { return null; } return sub; } }, { key: '_isServerSub', value: function _isServerSub(channel) { return this._serverSubs[channel] !== undefined; } }, { key: '_connectResponse', value: function _connectResponse(result, isRecover) { var _this15 = this; var wasReconnecting = this._reconnecting; this._reconnecting = false; this._resetRetry(); this._refreshRequired = false; if (this.isConnected()) { return; } if (this._latencyStart !== null) { this._latency = new Date().getTime() - this._latencyStart.getTime(); this._latencyStart = null; } this._clientID = result.client; this._setStatus('connected'); if (this._refreshTimeout) { clearTimeout(this._refreshTimeout); } if (result.expires) { this._refreshTimeout = setTimeout(function () { return _this15._refresh(); }, this._getTTLMilliseconds(result.ttl)); } this.startBatching(); this.startSubscribeBatching(); for (var channel in this._subs) { if (this._subs.hasOwnProperty(channel)) { var sub = this._subs[channel]; if (sub._shouldResubscribe()) { this._subscribe(sub, wasReconnecting); } } } this.stopSubscribeBatching(); this.stopBatching(); this._startPing(); var ctx = { client: result.client, transport: this._transportName, latency: this._latency }; if (result.data) { ctx.data = result.data; } this.emit('connect', ctx); if (result.subs) { this._processServerSubs(result.subs, isRecover); } } }, { key: '_processServerSubs', value: function _processServerSubs(subs, isRecover) { for (var channel in subs) { if (subs.hasOwnProperty(channel)) { var sub = subs[channel]; var recovered = sub.recovered === true; var subCtx = { channel: channel, isResubscribe: isRecover, recovered: recovered }; this.emit('subscribe', subCtx); } } for (var _channel2 in subs) { if (subs.hasOwnProperty(_channel2)) { var _sub = subs[_channel2]; if (_sub.recovered) { var pubs = _sub.publications; if (pubs && pubs.length > 0) { // handle legacy order. // TODO: remove as soon as Centrifuge v1 released. if (pubs.length > 1 && (!pubs[0].offset || pubs[0].offset > pubs[1].offset)) { pubs = pubs.reverse(); } for (var i in pubs) { if (pubs.hasOwnProperty(i)) { this._handlePublication(_channel2, pubs[i]); } } } } this._serverSubs[_channel2] = { 'seq': _sub.seq, 'gen': _sub.gen, 'offset': _sub.offset, 'epoch': _sub.epoch, 'recoverable': _sub.recoverable }; } } } }, { key: '_stopPing', value: function _stopPing() { if (this._pongTimeout !== null) { clearTimeout(this._pongTimeout); this._pongTimeout = null; } if (this._pingTimeout !== null) { clearTimeout(this._pingTimeout); this._pingTimeout = null; } } }, { key: '_startPing', value: function _startPing() { var _this16 = this; if (this._config.ping !== true || this._config.pingInterval <= 0) { return; } if (!this.isConnected()) { return; } this._pingTimeout = setTimeout(function () { if (!_this16.isConnected()) { _this16._stopPing(); return; } _this16.ping(); _this16._pongTimeout = setTimeout(function () { _this16._disconnect('no ping', true); }, _this16._config.pongWaitTimeout); }, this._config.pingInterval); } }, { key: '_restartPing', value: function _restartPing() { this._stopPing(); this._startPing(); } }, { key: '_subscribeError', value: function _subscribeError(channel, error) { var sub = this._getSub(channel); if (!sub) { return; } if (!sub._isSubscribing()) { return; } if (error.code === 0 && error.message === _errorTimeout) { // client side timeout. this._disconnect('timeout', true); return; } sub._setSubscribeError(error); } }, { key: '_subscribeResponse', value: function _subscribeResponse(channel, isRecover, result) { var _this17 = this; var sub = this._getSub(channel); if (!sub) { return; } if (!sub._isSubscribing()) { return; } var recovered = false; if ('recovered' in result) { recovered = result.recovered; } sub._setSubscribeSuccess(recovered); var pubs = result.publications; if (pubs && pubs.length > 0) { if (pubs.length >= 2 && !pubs[0].offset && !pubs[1].offset) { // handle legacy order. pubs = pubs.reverse(); } for (var i in pubs) { if (pubs.hasOwnProperty(i)) { this._handlePublication(channel, pubs[i]); } } } if (result.recoverable && (!isRecover || !recovered)) { this._lastSeq[channel] = result.seq || 0; this._lastGen[channel] = result.gen || 0; this._lastOffset[channel] = result.offset || 0; } this._lastEpoch[channel] = result.epoch || ''; if (result.recoverable) { sub._recoverable = true; } if (result.expires === true) { var subRefreshTimeout = setTimeout(function () { return _this17._subRefresh(channel); }, this._getTTLMilliseconds(result.ttl)); this._subRefreshTimeouts[channel] = subRefreshTimeout; } } }, { key: '_handleReply', value: function _handleReply(reply, next) { var id = reply.id; var result = reply.result; if (!(id in this._callbacks)) { next(); return; } var callbacks = this._callbacks[id]; clearTimeout(this._callbacks[id].timeout); delete this._callbacks[id]; if (!(0, _utils.errorExists)(reply)) { var callback = callbacks.callback; if (!callback) { return; } callback({ result: result, next: next }); } else { var errback = callbacks.errback; if (!errback) { next(); return; } var error = reply.error; errback({ error: error, next: next }); } } }, { key: '_handleJoin', value: function _handleJoin(channel, join) { var ctx = { 'info': join.info }; var sub = this._getSub(channel); if (!sub) { if (this._isServerSub(channel)) { ctx.channel = channel; this.emit('join', ctx); } return; } sub.emit('join', ctx); } }, { key: '_handleLeave', value: function _handleLeave(channel, leave) { var ctx = { 'info': leave.info }; var sub = this._getSub(channel); if (!sub) { if (this._isServerSub(channel)) { ctx.channel = channel; this.emit('leave', ctx); } return; } sub.emit('leave', ctx); } }, { key: '_handleUnsub', value: function _handleUnsub(channel, unsub) { var ctx = {}; var sub = this._getSub(channel); if (!sub) { if (this._isServerSub(channel)) { delete this._serverSubs[channel]; ctx.channel = channel; this.emit('unsubscribe', ctx); } return; } sub.unsubscribe(); if (unsub.resubscribe === true) { sub.subscribe(); } } }, { key: '_handleSub', value: function _handleSub(channel, sub) { this._serverSubs[channel] = { 'seq': sub.seq, 'gen': sub.gen, 'offset': sub.offset, 'epoch': sub.epoch, 'recoverable': sub.recoverable }; var ctx = { 'channel': channel, isResubscribe: false, recovered: false }; this.emit('subscribe', ctx); } }, { key: '_handlePublication', value: function _handlePublication(channel, pub) { var sub = this._getSub(channel); var ctx = { 'data': pub.data, 'seq': pub.seq, 'gen': pub.gen, 'offset': pub.offset }; if (pub.info) { ctx.info = pub.info; } if (!sub) { if (this._isServerSub(channel)) { if (pub.seq !== undefined) { this._serverSubs[channel].seq = pub.seq; } if (pub.gen !== undefined) { this._serverSubs[channel].gen = pub.gen; } if (pub.offset !== undefined) { this._serverSubs[channel].offset = pub.offset; } ctx.channel = channel; this.emit('publish', ctx); } return; } if (pub.seq !== undefined) { this._lastSeq[channel] = pub.seq; } if (pub.gen !== undefined) { this._lastGen[channel] = pub.gen; } if (pub.offset !== undefined) { this._lastOffset[channel] = pub.offset; } sub.emit('publish', ctx); } }, { key: '_handleMessage', value: function _handleMessage(message) { this.emit('message', message.data); } }, { key: '_handlePush', value: function _handlePush(data, next) { var push = this._