UNPKG

marsdb-sync-client

Version:

Standalone Meteor DDP client based on MarsDB

1,310 lines (1,082 loc) 80.4 kB
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}(g.Mars || (g.Mars = {})).Client = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ (function (process){ 'use strict'; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "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 _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; Object.defineProperty(exports, "__esModule", { value: true }); exports.createCollectionDelegate = createCollectionDelegate; var _bind2 = require('fast.js/function/bind'); var _bind3 = _interopRequireDefault(_bind2); var _forEach = require('fast.js/forEach'); var _forEach2 = _interopRequireDefault(_forEach); var _map2 = require('fast.js/map'); var _map3 = _interopRequireDefault(_map2); var _keys2 = require('fast.js/object/keys'); var _keys3 = _interopRequireDefault(_keys2); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } 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 Collection = typeof window !== 'undefined' && window.Mars ? window.Mars.Collection : require('marsdb').Collection; function createCollectionDelegate(connection) { var _currentDelegateClass = Collection.defaultDelegate(); /** * Collection manager is a factory for Mars.Collection * objects (one object by collection name). * It also syncing client/server changes. */ var CollectionManager = function (_currentDelegateClass2) { _inherits(CollectionManager, _currentDelegateClass2); function CollectionManager() { var _Object$getPrototypeO; _classCallCheck(this, CollectionManager); for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } var _this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(CollectionManager)).call.apply(_Object$getPrototypeO, [this].concat(args))); connection.on('status:connected', (0, _bind3.default)(_this._handleConnected, _this)); connection.on('message:added', (0, _bind3.default)(_this._handleRemoteAdded, _this)); connection.on('message:changed', (0, _bind3.default)(_this._handleRemoteChanged, _this)); connection.on('message:removed', (0, _bind3.default)(_this._handleRemoteRemoved, _this)); // For ensure that collection is initialized process.nextTick(function () { if (connection.isConnected) { _this._handleConnected(false); } }); return _this; } /** * Calls remote method `/_collection_name_/insert`. It reverts back * optimistic update on server fail. It also have some options * to customize working approach: * `retryOnDisconnect` option retry method call if it was failed * because dicsonnection. Default is true. * `waitResult` option disable optimistic update of the collection. * Returned Promise will be resolved when server returns * the result. * @param {Object} doc * @param {Boolean} options.retryOnDisconnect * @param {Boolean} options.waitResult * @param {Object} randomId * @return {Promise} */ _createClass(CollectionManager, [{ key: 'insert', value: function insert(doc) { var _this2 = this; var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; var randomId = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; var localInsert = _get(Object.getPrototypeOf(CollectionManager.prototype), 'insert', this).call(this, doc, options, randomId); var quiet = options.quiet; var _options$retryOnDisco = options.retryOnDisconnect; var retryOnDisconnect = _options$retryOnDisco === undefined ? true : _options$retryOnDisco; var waitResult = options.waitResult; if (!quiet) { var methodName = '/' + this.db.modelName + '/insert'; var applyOpts = { retryOnDisconnect: !!retryOnDisconnect, randomSeed: randomId.seed }; var result = connection.methodManager.apply(methodName, [doc], applyOpts).then(null, function (e) { return localInsert.then(function () { return _this2.db.remove(doc._id, { quiet: true }); }).then(function () { throw e; }); }); if (waitResult) { return result.then(function () { return localInsert; }); } } return localInsert; } /** * Calls remote method `/_collection_name_/remove`. It reverts back * optimistic update on server fail. It also have some options * to customize working approach: * `retryOnDisconnect` option retry method call if it was failed * because dicsonnection. Default is true. * `waitResult` option disable optimistic update of the collection. * Returned Promise will be resolved when server returns * the result. * @param {Object} doc * @param {Boolean} options.retryOnDisconnect * @param {Boolean} options.waitResult * @param {Object} randomId * @return {Promise} */ }, { key: 'remove', value: function remove(query) { var _this3 = this; var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; var localRemove = _get(Object.getPrototypeOf(CollectionManager.prototype), 'remove', this).call(this, query, options); var quiet = options.quiet; var _options$retryOnDisco2 = options.retryOnDisconnect; var retryOnDisconnect = _options$retryOnDisco2 === undefined ? true : _options$retryOnDisco2; var waitResult = options.waitResult; if (!quiet) { var methodName = '/' + this.db.modelName + '/remove'; var applyOpts = { retryOnDisconnect: !!retryOnDisconnect }; var result = connection.methodManager.apply(methodName, [query], applyOpts).then(null, function (e) { return localRemove.then(function (remDocs) { return _this3.db.insertAll(remDocs, { quiet: true }); }).then(function () { throw e; }); }); if (waitResult) { return result.then(function () { return localRemove; }); } } return localRemove; } /** * Calls remote method `/_collection_name_/update`. It reverts back * optimistic update on server fail. It also have some options * to customize working approach: * `retryOnDisconnect` option retry method call if it was failed * because dicsonnection. Default is true. * `waitResult` option disable optimistic update of the collection. * Returned Promise will be resolved when server returns * the result. * @param {Object} doc * @param {Boolean} options.retryOnDisconnect * @param {Boolean} options.waitResult * @param {Object} randomId * @return {Promise} */ }, { key: 'update', value: function update(query, modifier) { var _this4 = this; var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; var localUpdate = _get(Object.getPrototypeOf(CollectionManager.prototype), 'update', this).call(this, query, modifier, options); var quiet = options.quiet; var _options$retryOnDisco3 = options.retryOnDisconnect; var retryOnDisconnect = _options$retryOnDisco3 === undefined ? true : _options$retryOnDisco3; var waitResult = options.waitResult; var otherOpts = _objectWithoutProperties(options, ['quiet', 'retryOnDisconnect', 'waitResult']); if (!quiet) { var methodName = '/' + this.db.modelName + '/update'; var applyOpts = { retryOnDisconnect: !!retryOnDisconnect }; var result = connection.methodManager.apply(methodName, [query, modifier, otherOpts], applyOpts).then(null, function (e) { return localUpdate.then(function (res) { return (0, _map3.default)(res.updated, function (d, i) { if (!res.original[i]) { return _this4.db.remove(d._id, { quiet: true }); } else { var docId = res.original[i]._id; delete res.original[i]._id; return _this4.db.update({ _id: docId }, res.original[i], { quiet: true, upsert: true }); } }); }).then(function () { throw e; }); }); if (waitResult) { return result.then(function () { return localUpdate; }); } } return localUpdate; } }, { key: '_handleConnected', value: function _handleConnected(reconnected) { var _this5 = this; var methodName = '/' + this.db.modelName + '/sync'; return this.db.ids().then(function (ids) { return connection.methodManager.apply(methodName, [ids]).result(); }).then(function (removedIds) { return _this5.db.remove({ _id: { $in: removedIds } }, { quiet: true, multi: true }); }); } }, { key: '_handleRemoteAdded', value: function _handleRemoteAdded(msg) { if (msg.collection === this.db.modelName) { delete msg.fields._id; return this.db.update({ _id: msg.id }, msg.fields, { quiet: true, upsert: true }); } else { return Promise.resolve(); } } }, { key: '_handleRemoteChanged', value: function _handleRemoteChanged(msg) { var _this6 = this; if (msg.collection === this.db.modelName) { var _ret = function () { var modifier = {}; if (Array.isArray(msg.cleared) && msg.cleared.length > 0) { modifier.$unset = {}; var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = msg.cleared[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var f = _step.value; modifier.$unset[f] = 1; } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } } if (msg.fields) { delete msg.fields._id; modifier.$set = {}; (0, _forEach2.default)(msg.fields, function (v, k) { modifier.$set[k] = v; }); } if ((0, _keys3.default)(modifier).length > 0) { return { v: _this6.db.update(msg.id, modifier, { quiet: true }) }; } }(); if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === "object") return _ret.v; } else { return Promise.resolve(); } } }, { key: '_handleRemoteRemoved', value: function _handleRemoteRemoved(msg) { if (msg.collection === this.db.modelName) { return this.db.remove(msg.id, { quiet: true }); } else { return Promise.resolve(); } } }]); return CollectionManager; }(_currentDelegateClass); return CollectionManager; } }).call(this,require('_process')) },{"_process":25,"fast.js/forEach":14,"fast.js/function/bind":17,"fast.js/map":20,"fast.js/object/keys":22,"marsdb":undefined}],2:[function(require,module,exports){ 'use strict'; 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 _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; Object.defineProperty(exports, "__esModule", { value: true }); exports._isCacheValid = _isCacheValid; exports.createCursorWithSub = createCursorWithSub; var _keys2 = require('fast.js/object/keys'); var _keys3 = _interopRequireDefault(_keys2); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } 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 Collection = typeof window !== 'undefined' && window.Mars ? window.Mars.Collection : require('marsdb').Collection; // Internals function _isCacheValid(tryCache, result) { var resolveCache = false; if (typeof tryCache === 'function') { resolveCache = tryCache(result); } else if (Array.isArray(result) && result.length > 0 || Object.prototype.toString.call(result) === '[object Object]' && (0, _keys3.default)(result).length > 0) { resolveCache = true; } return resolveCache; } /** * Creates a Cursor class based on current default crusor class. * Created class adds support of `sub` field of options for * automatically subscribe/unsubscribe. * @param {DDPConnection} connection * @return {Cursor} */ function createCursorWithSub(connection) { var _currentCursorClass = Collection.defaultCursor(); /** * Cursor that automatically subscribe and unsubscribe * on cursor observing statred/stopped. */ var CursorWithSub = function (_currentCursorClass2) { _inherits(CursorWithSub, _currentCursorClass2); function CursorWithSub() { _classCallCheck(this, CursorWithSub); return _possibleConstructorReturn(this, Object.getPrototypeOf(CursorWithSub).apply(this, arguments)); } _createClass(CursorWithSub, [{ key: '_doUpdate', value: function _doUpdate(firstRun) { var _this2 = this; var _options = this.options; var sub = _options.sub; var waitReady = _options.waitReady; var tryCache = _options.tryCache; var keepSub = _options.keepSub; var superUpdate = function superUpdate() { return _get(Object.getPrototypeOf(CursorWithSub.prototype), '_doUpdate', _this2).call(_this2, firstRun); }; // When subscription is not initiated if (!this._subscription && sub) { var _connection$subManage; this._subscription = (_connection$subManage = connection.subManager).subscribe.apply(_connection$subManage, _toConsumableArray(sub)); if (!keepSub) { this.once('observeStopped', function () { _this2._subscription.stop(); delete _this2._subscription; }); } if (waitReady) { return this._subscription.ready().then(superUpdate); } else if (tryCache) { return this.exec().then(function (result) { if (_isCacheValid(tryCache, result)) { _this2._updateLatestIds(); return _this2._propagateUpdate(firstRun).then(function () { return result; }); } else { return _this2._subscription.ready().then(superUpdate); } }); } } // When subscription initiated but not ready // (this case is used when cached result is used) if (this._subscription && !this._subscription.isReady) { return this._subscription.ready().then(superUpdate); } return superUpdate(); } }, { key: 'subscription', get: function get() { return this._subscription; } }]); return CursorWithSub; }(_currentCursorClass); return CursorWithSub; } },{"fast.js/object/keys":22,"marsdb":undefined}],3:[function(require,module,exports){ 'use strict'; 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; }; }(); Object.defineProperty(exports, "__esModule", { value: true }); exports.CONN_STATUS = undefined; var _try2 = require('fast.js/function/try'); var _try3 = _interopRequireDefault(_try2); var _bind2 = require('fast.js/function/bind'); var _bind3 = _interopRequireDefault(_bind2); var _HeartbeatManager = require('./HeartbeatManager'); var _HeartbeatManager2 = _interopRequireDefault(_HeartbeatManager); 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 EventEmitter = typeof window !== 'undefined' && window.Mars ? window.Mars.EventEmitter : require('marsdb').EventEmitter; var PromiseQueue = typeof window !== 'undefined' && window.Mars ? window.Mars.PromiseQueue : require('marsdb').PromiseQueue; var EJSON = typeof window !== 'undefined' && window.Mars ? window.Mars.EJSON : require('marsdb').EJSON; var Random = typeof window !== 'undefined' && window.Mars ? window.Mars.Random : require('marsdb').Random; // Status of a DDP connection var DDP_VERSION = '1'; var HEARTBEAT_INTERVAL = 17500; var HEARTBEAT_TIMEOUT = 15000; var RECONNECT_INTERVAL = 5000; var CONN_STATUS = exports.CONN_STATUS = { CONNECTING: 'CONNECTING', CONNECTED: 'CONNECTED', DISCONNECTED: 'DISCONNECTED' }; var DDPConnection = function (_EventEmitter) { _inherits(DDPConnection, _EventEmitter); function DDPConnection(_ref) { var url = _ref.url; var socket = _ref.socket; var _ref$autoReconnect = _ref.autoReconnect; var autoReconnect = _ref$autoReconnect === undefined ? true : _ref$autoReconnect; _classCallCheck(this, DDPConnection); var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(DDPConnection).call(this)); _this.url = url; _this._processQueue = new PromiseQueue(1); _this._sessionId = null; _this._autoReconnect = autoReconnect; _this._socket = socket; _this._status = CONN_STATUS.DISCONNECTED; _this._fullConnectedOnce = false; _this._heartbeat = new _HeartbeatManager2.default(HEARTBEAT_INTERVAL, HEARTBEAT_TIMEOUT); _this._heartbeat.on('timeout', (0, _bind3.default)(_this._handleHearbeatTimeout, _this)); _this._heartbeat.on('sendPing', (0, _bind3.default)(_this.sendPing, _this)); _this._heartbeat.on('sendPong', (0, _bind3.default)(_this.sendPong, _this)); return _this; } /** * Returns true if client is fully connected to a server * @return {Boolean} */ _createClass(DDPConnection, [{ key: 'sendMethod', /** * Sends a "method" message to the server with given * parameters * @param {String} name * @param {String} params * @param {String} id * @param {String} randomSeed */ value: function sendMethod(name) { var params = arguments.length <= 1 || arguments[1] === undefined ? [] : arguments[1]; var id = arguments[2]; var randomSeed = arguments[3]; var msg = { msg: 'method', id: id, method: name, params: params }; if (randomSeed) { msg.randomSeed = randomSeed; } this._sendMessage(msg); } /** * Send "sub" message to the server with given * publusher name and parameters * @param {String} name * @param {Array} params * @param {String} id */ }, { key: 'sendSub', value: function sendSub(name) { var params = arguments.length <= 1 || arguments[1] === undefined ? [] : arguments[1]; var id = arguments[2]; this._sendMessage({ msg: 'sub', id: id, name: name, params: params }); } /** * Send "unsub" message to the server for given * subscription id * @param {String} id */ }, { key: 'sendUnsub', value: function sendUnsub(id) { this._sendMessage({ msg: 'unsub', id: id }); } /** * Send a "ping" message with randomly generated ping id */ }, { key: 'sendPing', value: function sendPing() { this._sendMessage({ msg: 'ping', id: Random.default().id(20) }); } /** * Sends a "pong" message for given id of ping message * @param {String} id */ }, { key: 'sendPong', value: function sendPong(id) { this._sendMessage({ msg: 'pong', id: id }); } /** * Make a new WebSocket connection to the server * if we are not connected yet (isDicsonnected). * Returns true if connecting, false if already connectiong * @returns {Boolean} */ }, { key: 'connect', value: function connect() { if (this.isDisconnected) { this._rawConn = new this._socket(this.url); this._rawConn.onopen = (0, _bind3.default)(this._handleOpen, this); this._rawConn.onerror = (0, _bind3.default)(this._handleError, this); this._rawConn.onclose = (0, _bind3.default)(this._handleClose, this); this._rawConn.onmessage = (0, _bind3.default)(this._handleRawMessage, this); this._setStatus(CONN_STATUS.CONNECTING); return true; } return false; } /** * Reconnect to the server with unlimited tries. A period * of tries is 5 seconds. It reconnects only if not * connected. It cancels previously scheduled `connect` by `reconnect`. * Returns a function for canceling reconnection process or undefined * if connection is not disconnected. * @return {Function} */ }, { key: 'reconnect', value: function reconnect() { var _this2 = this; if (this.isDisconnected) { clearTimeout(this._reconnTimer); this._reconnecting = true; this._reconnTimer = setTimeout((0, _bind3.default)(this.connect, this), RECONNECT_INTERVAL); return function () { clearTimeout(_this2._reconnTimer); _this2._reconnecting = false; _this2.disconnect(); }; } } /** * Close WebSocket connection. If autoReconnect is enabled * (enabled by default), then after 5 sec reconnection will * be initiated. */ }, { key: 'disconnect', value: function disconnect() { var _this3 = this; (0, _try3.default)(function () { return _this3._rawConn && _this3._rawConn.close(); }); } }, { key: '_handleOpen', value: function _handleOpen() { this._heartbeat.waitPing(); var connMsg = { msg: 'connect', version: DDP_VERSION, support: [DDP_VERSION] }; if (this._sessionId) { connMsg.session = this._sessionId; } this._sendMessage(connMsg); } }, { key: '_handleConnectedMessage', value: function _handleConnectedMessage(msg) { if (!this.isConnected) { var isTrulyReconnected = this._fullConnectedOnce && this._reconnecting; this._setStatus(CONN_STATUS.CONNECTED, isTrulyReconnected); this._sessionId = msg.session; this._reconnecting = false; this._fullConnectedOnce = true; } } }, { key: '_handleClose', value: function _handleClose() { this._heartbeat._clearTimers(); this._setStatus(CONN_STATUS.DISCONNECTED, this._fullConnectedOnce); if (this._autoReconnect) { this._reconnecting = false; this.reconnect(); } } }, { key: '_handleHearbeatTimeout', value: function _handleHearbeatTimeout() { this.disconnect(); } }, { key: '_handleError', value: function _handleError(error) { this.emit('error', error); } }, { key: '_handleRawMessage', value: function _handleRawMessage(rawMsg) { var _this4 = this; return this._processQueue.add(function () { var msgObj = EJSON.parse(rawMsg.data); return _this4._processMessage(msgObj); }).then(null, function (err) { _this4._handleError(err); }); } }, { key: '_processMessage', value: function _processMessage(msg) { switch (msg.msg) { case 'connected': return this._handleConnectedMessage(msg); case 'ping': return this._heartbeat.handlePing(msg); case 'pong': return this._heartbeat.handlePong(msg); case 'removed': case 'changed': case 'added': case 'updated': case 'result': case 'nosub': case 'ready': case 'error': return this.emitAsync('message:' + msg.msg, msg); default: // just ignore unknown message } } }, { key: '_sendMessage', value: function _sendMessage(msgObj) { var _this5 = this; var result = (0, _try3.default)(function () { return _this5._rawConn.send(EJSON.stringify(msgObj)); }); if (result instanceof Error) { this._handleError(result); } } }, { key: '_setStatus', value: function _setStatus(status, a) { this._status = status; this.emit(('status:' + status).toLowerCase(), a); } }, { key: 'isConnected', get: function get() { return this._status === CONN_STATUS.CONNECTED; } /** * Returns true if client disconnected * @return {Boolean} */ }, { key: 'isDisconnected', get: function get() { return this._status === CONN_STATUS.DISCONNECTED; } }]); return DDPConnection; }(EventEmitter); exports.default = DDPConnection; },{"./HeartbeatManager":5,"fast.js/function/bind":17,"fast.js/function/try":19,"marsdb":undefined}],4:[function(require,module,exports){ 'use strict'; 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; }; }(); Object.defineProperty(exports, "__esModule", { value: true }); var _bind2 = require('fast.js/function/bind'); var _bind3 = _interopRequireDefault(_bind2); 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"); } } /** * Manager for handling processing and remote errors. * For now it is just print warning in a console. */ var ErrorManager = function () { function ErrorManager(connection) { _classCallCheck(this, ErrorManager); this.conn = connection; connection.on('message:error', (0, _bind3.default)(this._handleError, this)); connection.on('error', (0, _bind3.default)(this._handleError, this)); } _createClass(ErrorManager, [{ key: '_handleError', value: function _handleError(error) { if (error && error.message) { console.warn(error.message + '\n' + error.stack); } else { console.warn(JSON.stringify(error)); } } }]); return ErrorManager; }(); exports.default = ErrorManager; },{"fast.js/function/bind":17}],5:[function(require,module,exports){ 'use strict'; 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; }; }(); Object.defineProperty(exports, "__esModule", { value: true }); 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 EventEmitter = typeof window !== 'undefined' && window.Mars ? window.Mars.EventEmitter : require('marsdb').EventEmitter; /** * Manages a heartbeat with a client */ var HeartbeatManager = function (_EventEmitter) { _inherits(HeartbeatManager, _EventEmitter); function HeartbeatManager() { var pingTimeout = arguments.length <= 0 || arguments[0] === undefined ? 17500 : arguments[0]; var pongTimeout = arguments.length <= 1 || arguments[1] === undefined ? 10000 : arguments[1]; _classCallCheck(this, HeartbeatManager); var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(HeartbeatManager).call(this)); _this.pingTimeout = pingTimeout; _this.pongTimeout = pongTimeout; return _this; } _createClass(HeartbeatManager, [{ key: 'waitPing', value: function waitPing() { var _this2 = this; this._clearTimers(); this.waitPingTimer = setTimeout(function () { _this2.emit('sendPing'); _this2.waitPong(); }, this.pingTimeout); } }, { key: 'waitPong', value: function waitPong() { var _this3 = this; this._clearTimers(); this.waitPongTimer = setTimeout(function () { return _this3.emit('timeout'); }, this.pongTimeout); } }, { key: 'handlePing', value: function handlePing(id) { this._clearTimers(); this.emit('sendPong', id); this.waitPing(); } }, { key: 'handlePong', value: function handlePong() { this._clearTimers(); this.waitPing(); } }, { key: '_clearTimers', value: function _clearTimers() { clearTimeout(this.waitPingTimer); clearTimeout(this.waitPongTimer); } }]); return HeartbeatManager; }(EventEmitter); exports.default = HeartbeatManager; },{"marsdb":undefined}],6:[function(require,module,exports){ 'use strict'; 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; }; }(); Object.defineProperty(exports, "__esModule", { value: true }); 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 EventEmitter = typeof window !== 'undefined' && window.Mars ? window.Mars.EventEmitter : require('marsdb').EventEmitter; var Random = typeof window !== 'undefined' && window.Mars ? window.Mars.Random : require('marsdb').Random; // Method call statuses var CALL_STATUS = exports.CALL_STATUS = { PENDING: 'PENDING', SENT: 'SENT', RESULT: 'RESULT', ERROR: 'ERROR', UPDATED: 'UPDATED' }; /** * Class for tracking method call status. */ var MethodCall = function (_EventEmitter) { _inherits(MethodCall, _EventEmitter); function MethodCall(method, params) { var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; var connection = arguments[3]; _classCallCheck(this, MethodCall); var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(MethodCall).call(this)); _this.result = function () { return _this._promiseMixed(new Promise(function (resolve, reject) { if (_this._resulted) { resolve(_this._result); } else if (_this._errored) { reject(_this._error); } else { _this.once(CALL_STATUS.RESULT, resolve); _this.once(CALL_STATUS.ERROR, reject); } })); }; _this.updated = function () { return _this._promiseMixed(new Promise(function (resolve, reject) { if (_this._updated) { resolve(); } else { _this.once(CALL_STATUS.UPDATED, resolve); } })); }; _this.id = Random.default().id(20); _this.status = CALL_STATUS.PENDING; _this.method = method; _this.params = params; _this.options = options; _this._conn = connection; return _this; } _createClass(MethodCall, [{ key: 'then', /** * Shorthand for updated and result * @param {Function} succFn * @param {Function} failFn * @return {Promise} */ value: function then(succFn, failFn) { var _this2 = this; return this.updated().then(function () { return _this2.result().then(succFn, failFn); }, failFn); } }, { key: '_invoke', value: function _invoke() { this._conn.sendMethod(this.method, this.params, this.id, this.options.randomSeed); this._setStatus(CALL_STATUS.SENT); } }, { key: '_retry', value: function _retry() { this._setStatus(CALL_STATUS.PENDING); } }, { key: '_promiseMixed', value: function _promiseMixed(promise) { var _this3 = this; return { result: this.result, updated: this.updated, then: function then() { return _this3._promiseMixed(promise.then.apply(promise, arguments)); } }; } }, { key: '_handleResult', value: function _handleResult(error, result) { if (error) { this._errored = true; this._error = error; this._setStatus(CALL_STATUS.ERROR, error); } else { this._resulted = true; this._result = result; this._setStatus(CALL_STATUS.RESULT, result); } } }, { key: '_handleUpdated', value: function _handleUpdated(msg) { this._updated = true; this._setStatus(CALL_STATUS.UPDATED); } }, { key: '_setStatus', value: function _setStatus(status, a, b, c, d) { this.status = status; this.emit(status, a, b, c, d); } }, { key: 'isPending', get: function get() { return this.status === CALL_STATUS.PENDING; } }, { key: 'isSent', get: function get() { return this.status === CALL_STATUS.SENT; } }, { key: 'isDone', get: function get() { return this.status !== CALL_STATUS.SENT && this.status !== CALL_STATUS.PENDING; } /** * Returns a promise that will be resolved when result * of funciton call is received. It is also have "result" * and "updated" fields for chaining * @return {Promise} */ /** * Returns a promise that will be resolved when updated * message received for given funciton call. It is also * have "result" and "updated" fields for chaining. * @return {Promise} */ }]); return MethodCall; }(EventEmitter); exports.default = MethodCall; },{"marsdb":undefined}],7:[function(require,module,exports){ 'use strict'; 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; }; }(); Object.defineProperty(exports, "__esModule", { value: true }); var _bind2 = require('fast.js/function/bind'); var _bind3 = _interopRequireDefault(_bind2); var _forEach = require('fast.js/forEach'); var _forEach2 = _interopRequireDefault(_forEach); var _MethodCall = require('./MethodCall'); var _MethodCall2 = _interopRequireDefault(_MethodCall); 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"); } } /** * Make an RPC calls and track results. * Track a DDP connection for canceling active * methods calls. */ var MethodCallManager = function () { function MethodCallManager(connection) { _classCallCheck(this, MethodCallManager); this.conn = connection; this._methods = {}; connection.on('status:disconnected', (0, _bind3.default)(this._handleDisconnected, this)); connection.on('status:connected', (0, _bind3.default)(this._handleConnected, this)); connection.on('message:result', (0, _bind3.default)(this._handleMethodResult, this)); connection.on('message:updated', (0, _bind3.default)(this._handleMethodUpdated, this)); } /** * Call a Meteor method * @param {String} method * @param {...} param1, param2, .. * @return {MethodCall} */ _createClass(MethodCallManager, [{ key: 'call', value: function call(method) { for (var _len = arguments.length, params = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { params[_key - 1] = arguments[_key]; } return this.apply(method, params); } /** * Apply a method with given parameters and * randomSeed * @param {String} method * @param {Array} params * @param {String} randomSeed * @return {MethodCall} */ }, { key: 'apply', value: function apply(method) { var _this = this; var params = arguments.length <= 1 || arguments[1] === undefined ? [] : arguments[1]; var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; var call = new _MethodCall2.default(method, params, options, this.conn); this._methods[call.id] = call; var cleanupCallback = function cleanupCallback() { return delete _this._methods[call.id]; }; call.then(cleanupCallback, cleanupCallback); if (this.conn.isConnected) { call._invoke(); } return call; } }, { key: '_handleMethodResult', value: function _handleMethodResult(msg) { if (msg.id && this._methods[msg.id]) { var result = msg.result; var error = msg.error; this._methods[msg.id]._handleResult(error, result); } } }, { key: '_handleMethodUpdated', value: function _handleMethodUpdated(msg) { var _this2 = this; (0, _forEach2.default)(msg.methods, function (mid) { if (_this2._methods[mid]) { _this2._methods[mid]._handleUpdated(); } }); } }, { key: '_handleDisconnected', value: function _handleDisconnected() { (0, _forEach2.default)(this._methods, function (methodCall) { if (methodCall.isSent) { if (!methodCall.options.retryOnDisconnect) { methodCall._handleResult({ reason: 'Disconnected, method can\'t be done', code: 'DISCONNECTED' }); } else { methodCall._retry(); } } }); } }, { key: '_handleConnected', value: function _handleConnected() { (0, _forEach2.default)(this._methods, function (methodCall) { if (methodCall.isPending) { methodCall._invoke(); } }); } }]); return MethodCallManager; }(); exports.default = MethodCallManager; },{"./MethodCall":6,"fast.js/forEach":14,"fast.js/function/bind":17}],8:[function(require,module,exports){ 'use strict'; 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; }; }(); Object.defineProperty(exports, "__esModule", { value: true }); 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 EventEmitter = typeof window !== 'undefined' && window.Mars ? window.Mars.EventEmitter : require('marsdb').EventEmitter; var Random = typeof window !== 'undefined' && window.Mars ? window.Mars.Random : require('marsdb').Random; // Status of the subsctiption var SUB_STATUS = exports.SUB_STATUS = { READY_PENDING: 'READY_PENDING', READY: 'READY', ERROR: 'ERROR', STOP_PENDING: 'STOP_PENDING', STOPPED: 'STOPPED', FROZEN: 'FROZEN' }; /** * Class for storing Subscription with * delayed pending feature. */ var Subscription = function (_EventEmitter) { _inherits(Subscription, _EventEmitter); function Subscription(name, params, conn) { var stopWaitTimeout = arguments.length <= 3 || arguments[3] === undefined ? 15000 : ar