marsdb-sync-client
Version:
Standalone Meteor DDP client based on MarsDB
1,310 lines (1,082 loc) • 80.4 kB
JavaScript
(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