Socket
Socket
Sign inDemoInstall

twilio-sync

Package Overview
Dependencies
Maintainers
2
Versions
608
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

twilio-sync - npm Package Compare versions

Comparing version 0.3.7 to 0.3.8

12

lib/client.js

@@ -47,2 +47,4 @@ 'use strict';

var _twilioTransport2 = _interopRequireDefault(_twilioTransport);
var _twilioNotifications = require('twilio-notifications');

@@ -167,3 +169,3 @@

options.twilsockClient = options.twilsockClient || new _twilsock2.default(token, options);
options.transport = options.transport || new _twilioTransport.Transport(options.twilsockClient);
options.transport = options.transport || new _twilioTransport2.default(options.twilsockClient);
options.notificationsClient = options.notificationsClient || new _twilioNotifications2.default(token, options);

@@ -176,3 +178,3 @@

var network = new _network2.default(productId, new _clientInfo2.default(SDK_VERSION), config, transport);
var subscriptions = new _subscriptions2.default(config, network, _twilioTransport.TwilsockUnavailableError);
var subscriptions = new _subscriptions2.default(config, network);
var router = new _router2.default({ config: config, subscriptions: subscriptions, notifications: notifications });

@@ -387,3 +389,7 @@

value: function shutdown() {
return _promise2.default.all([this._subscriptions.shutdown(), this._twilsock.disconnect()]).then(function () {});
var _this5 = this;
return this._subscriptions.shutdown().then(function () {
return _this5._twilsock.disconnect();
}).then(function () {});
}

@@ -390,0 +396,0 @@

@@ -22,3 +22,3 @@ 'use strict';

var CDS_URI = 'https://cds.twilio.com';
var SUBSCRIPTIONS_PATH = '/v4/Subscriptions';
var SUBSCRIPTIONS_PATH = '/v3/Subscriptions';
var MAPS_PATH = '/v3/Maps';

@@ -25,0 +25,0 @@ var LISTS_PATH = '/v3/Lists';

@@ -37,5 +37,2 @@ 'use strict';

/**
* Base class for all Sync entity types
*/
var Entity = function (_EventEmitter) {

@@ -76,3 +73,3 @@ (0, _inherits3.default)(Entity, _EventEmitter);

value: function _unsubscribe() {
this._deps.router.unsubscribe(this.sid);
this._deps.router.unsubscribe(this.sid, this);
return this;

@@ -82,14 +79,2 @@ }

/**
* Report an error, which is occured during internal processes
*/
}, {
key: '_reportFailure',
value: function _reportFailure(err) {
this.emit('failure', err);
}
/**
* Closes current entity
* Remove server subscription, local cache, and other related stuff
* @public

@@ -96,0 +81,0 @@ */

@@ -62,6 +62,2 @@ 'use strict';

* Make a GET request by given URI
* @param {string} uri is the target of the HTTP fetch.
* @param {boolean} forceTwilsock instructs (iff true) the transport layer to execute this request
* only when a Twilsock connection is avaliable. A request made at any other time will throw
* the exception of the Twilsock library in use.
* @Returns Promise<Response> Result of successful get request

@@ -74,22 +70,9 @@ */

value: function get(uri) {
var forceTwilsock = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var headers = this._generateHeaders();
_logger2.default.debug('GET', uri, 'ID:', headers['Twilio-Request-Id']);
return this._transport.get(uri, headers, forceTwilsock);
return this._transport.get(uri, headers);
}
/**
* Make a POST request to the given URI.
* @param {string} uri is the target of the HTTP fetch.
* @param {boolean} forceTwilsock instructs (iff true) the transport layer to execute this request
* only when a Twilsock connection is avaliable. A request made at any other time will throw
* the exception of the Twilsock library in use.
*/
}, {
key: 'post',
value: function post(uri, body, revision) {
var forceTwilsock = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
var headers = this._generateHeaders();

@@ -99,22 +82,23 @@ if (typeof revision !== 'undefined') {

}
_logger2.default.debug('POST', uri, 'ID:', headers['Twilio-Request-Id']);
return this._transport.post(uri, headers, body, forceTwilsock);
return this._transport.post(uri, headers, body);
}
}, {
key: 'put',
value: function put(uri, body, revision) {
var headers = this._generateHeaders();
if (typeof revision !== 'undefined') {
headers['If-Match'] = revision;
}
/**
* Make a DELETE request to the given URI.
* @param {string} uri is the target of the HTTP fetch.
* @param {boolean} forceTwilsock instructs (iff true) the transport layer to execute this request
* only when a Twilsock connection is avaliable. A request made at any other time will throw
* the exception of the Twilsock library in use.
*/
_logger2.default.debug('PUT', uri, 'ID:', headers['Twilio-Request-Id']);
return this._transport.put(uri, headers, body);
}
}, {
key: 'delete',
value: function _delete(uri) {
var forceTwilsock = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var headers = this._generateHeaders();
_logger2.default.debug('DELETE', uri, 'ID:', headers['Twilio-Request-Id']);
return this._transport.delete(uri, headers, forceTwilsock);
return this._transport.delete(uri, headers);
}

@@ -121,0 +105,0 @@ }]);

@@ -7,6 +7,2 @@ 'use strict';

var _promise = require('babel-runtime/core-js/promise');
var _promise2 = _interopRequireDefault(_promise);
var _defineProperties = require('babel-runtime/core-js/object/define-properties');

@@ -28,2 +24,4 @@

var _utils = require('./utils');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

@@ -34,3 +32,2 @@

var COREDATA_MAP_NOTIFICATION_TYPE = 'com.twilio.rtd.cds.map';
var SYNC_NOTIFICATION_TYPE = 'twilio.sync.event';

@@ -57,3 +54,2 @@ /**

this._notifications.subscribe(COREDATA_MAP_NOTIFICATION_TYPE);
this._notifications.subscribe(SYNC_NOTIFICATION_TYPE);

@@ -78,6 +74,25 @@ this._notifications.on('message', this.onMessage.bind(this));

value: function onMessage(type, message) {
// As it happens, all message types are intended for the Subscriptions manager.
//
_logger2.default.trace('Notification type:', type, 'content:', message);
this._subscriptions.acceptMessage(message);
var id = void 0;
switch (type) {
case COREDATA_DOCUMENT_NOTIFICATION_TYPE:
id = message.event.document_sid;
break;
case COREDATA_LIST_NOTIFICATION_TYPE:
id = message.event.list_sid;
break;
case COREDATA_MAP_NOTIFICATION_TYPE:
id = message.event.map_sid;
break;
default:
_logger2.default.warn('Unknown message type:', type, 'ignoring');
}
if (id) {
message.event.type = message.event_type; // Patch message
this._subscriptions.getSubscribers(id).forEach(function (entity) {
entity._update((0, _utils.deepClone)(message.event));
});
}
}

@@ -92,4 +107,9 @@

value: function subscribe(sid, entity) {
this._subscriptions.add(sid, entity);
return _promise2.default.resolve(entity);
return this._subscriptions.add(sid, entity).then(function (isNewSubscription) {
if (isNewSubscription) {
entity._softSync();
}
}).then(function () {
return entity;
});
}

@@ -103,4 +123,6 @@

key: 'unsubscribe',
value: function unsubscribe(sid) {
return this._subscriptions.remove(sid);
value: function unsubscribe(sid, entity) {
return this._subscriptions.remove(sid, entity).then(function () {
return entity;
});
}

@@ -116,3 +138,5 @@

value: function onConnected() {
this._subscriptions.poke();
this._subscriptions.forEach(function (id, endpoint) {
endpoint._softSync();
});
}

@@ -119,0 +143,0 @@ }]);

@@ -7,9 +7,9 @@ 'use strict';

var _slicedToArray2 = require('babel-runtime/helpers/slicedToArray');
var _set = require('babel-runtime/core-js/set');
var _slicedToArray3 = _interopRequireDefault(_slicedToArray2);
var _set2 = _interopRequireDefault(_set);
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _promise = require('babel-runtime/core-js/promise');
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _promise2 = _interopRequireDefault(_promise);

@@ -20,2 +20,6 @@ var _map = require('babel-runtime/core-js/map');

var _defineProperties = require('babel-runtime/core-js/object/define-properties');
var _defineProperties2 = _interopRequireDefault(_defineProperties);
var _extends2 = require('babel-runtime/helpers/extends');

@@ -25,6 +29,2 @@

var _defineProperties = require('babel-runtime/core-js/object/define-properties');
var _defineProperties2 = _interopRequireDefault(_defineProperties);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');

@@ -38,2 +38,10 @@

var _slicedToArray2 = require('babel-runtime/helpers/slicedToArray');
var _slicedToArray3 = _interopRequireDefault(_slicedToArray2);
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _backoff = require('backoff');

@@ -47,81 +55,62 @@

var _syncerror = require('./syncerror');
var _syncerror2 = _interopRequireDefault(_syncerror);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var MAX_BATCH_SIZE = 1000;
var MAX_SUBSCRIPTION_BATCH_SIZE = 100;
/**
* A data container used by the Subscriptions class to track subscribed entities' local
* representations and their state.
*/
/* eslint-disable key-spacing */
function substract(p1, p2, limit) {
var result = [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
var SubscribedEntity = function () {
function SubscribedEntity(entity) {
var _this = this;
try {
for (var _iterator = (0, _getIterator3.default)(p1), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var _step$value = (0, _slicedToArray3.default)(_step.value, 2),
key = _step$value[0],
value = _step$value[1];
(0, _classCallCheck3.default)(this, SubscribedEntity);
(0, _defineProperties2.default)(this, {
sid: { get: function get() {
return entity.sid;
} },
type: { get: function get() {
return entity.type;
} },
lastEventId: { get: function get() {
return entity.lastEventId;
} },
localObject: { value: entity },
correlationId: { value: null, writable: true },
pendingPokeTimer: { value: null, writable: true },
rejectedWithError: { value: false, writable: true },
isInTransition: { get: function get() {
return _this.correlationId !== null;
} }
});
if (!p2.has(key)) {
result.push({ sid: key, type: value.type });
if (limit && result.length >= limit) {
break;
}
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
(0, _createClass3.default)(SubscribedEntity, [{
key: 'markAsFailed',
value: function markAsFailed(error) {
this.rejectedWithError = error;
}
}]);
return SubscribedEntity;
}();
return result;
}
/**
* @class Subscriptions
* @classdesc A manager which, in batches of varying size, continuously persists the
* subscription intent of the caller to the Sync backend until it achieves a
* converged state.
* @classdesc Subscriptions container for CDS objects
*/
var Subscriptions = function () {
/**
* @constructor
* Prepares a new Subscriptions manager object with zero subscribed or persisted subscriptions.
*
* @param {object} config may include a key 'backoffConfig', wherein any of the parameters
* of Backoff.exponential (from npm 'backoff') are valid and will override the defaults.
*
* @param {Network} must be a viable running Sync Network object, useful for routing requests.
*/
function Subscriptions(config, network, TwilsockUnvailableErrorType) {
var _this2 = this;
function Subscriptions(config, network) {
var _this = this;
(0, _classCallCheck3.default)(this, Subscriptions);
function createBackoff() {
var defaultBackoffConfig = {
randomisationFactor: 0.2,
initialDelay: 100,
initialDelay: 50,
maxDelay: 2 * 60 * 1000

@@ -131,4 +120,4 @@ };

}
(0, _defineProperties2.default)(this, {
_ExceptionOnTwilsockUnavailable: { value: TwilsockUnvailableErrorType, writable: false },
_config: { value: config },

@@ -138,27 +127,26 @@ _network: { value: network },

// This is always the full set of subscribables (SubscribedEntity instances) intended by
// the client. At any point, whatever the state of these subscriptions on the server, this
// is the intent of the user to which the SDK must converge.
_types: { value: new _map2.default() },
_subscriptions: { value: new _map2.default() },
// This includes the set of subscribables (SubscribedEntity instances) for whom a request
// has been dispatched (whether or not this particular request ultimately succeeds) to
// establish a live subscription. Entities are removed when the corresponding "cancel"
// request is dispatched.
_persisted: { value: new _map2.default() }
});
// This block is triggered by #_persist. Every request is executed in a series of (ideally 1)
// backoff 'ready' event, at which point a new subscription set is calculated.
//
this._backoff.on('ready', function () {
var _getSubscriptionUpdat = _this2._getSubscriptionUpdateBatch(),
action = _getSubscriptionUpdat.action,
subscriptionRequests = _getSubscriptionUpdat.subscriptions;
var _getAction2 = _this._getAction(),
action = _getAction2.action,
list = _getAction2.list;
if (action) {
_this2._applyNewSubscriptionUpdateBatch(action, subscriptionRequests);
_this._request(action, list.map(function (x) {
return { object_sid: x.sid, object_type: x.type };
})) // eslint-disable-line camelcase
.then(function () {
_this._backoff.reset();
_this._backoff.backoff();
}).catch(function (e) {
_logger2.default.error('Error while persisting subscriptions, retrying', action, e);
_this._backoff.backoff(e);
});
} else {
_this2._backoff.reset();
_logger2.default.info('All subscriptions resolved.');
_this._backoff.reset();
_logger2.default.info('Subscription list persisted');
}

@@ -169,52 +157,16 @@ });

(0, _createClass3.default)(Subscriptions, [{
key: '_getSubscriptionUpdateBatch',
value: function _getSubscriptionUpdateBatch() {
function substract(these, those, ignoreCurrentOp, limit) {
var result = [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
key: '_getAction',
value: function _getAction() {
var _this2 = this;
try {
for (var _iterator = (0, _getIterator3.default)(these), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var _step$value = (0, _slicedToArray3.default)(_step.value, 2),
thisKey = _step$value[0],
thisValue = _step$value[1];
var otherValue = those.get(thisKey);
if (!otherValue && (ignoreCurrentOp || !thisValue.isInTransition) && !thisValue.rejectedWithError) {
result.push(thisValue);
if (limit && result.length >= limit) {
break;
}
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return result;
var listToAdd = substract(this._subscriptions, this._persisted, MAX_SUBSCRIPTION_BATCH_SIZE);
if (listToAdd.length > 0) {
return { action: 'establish', list: listToAdd };
}
var listToAdd = substract(this._subscriptions, this._persisted, false, MAX_BATCH_SIZE).map(function (x) {
return new SubscribedEntity(x);
var listToRemove = substract(this._persisted, this._subscriptions, MAX_SUBSCRIPTION_BATCH_SIZE).map(function (x) {
return { sid: x.sid, type: _this2._types.get(x.sid) };
});
if (listToAdd.length > 0) {
return { action: 'establish', subscriptions: listToAdd };
}
var listToRemove = substract(this._persisted, this._subscriptions, true, MAX_BATCH_SIZE);
if (listToRemove.length > 0) {
return { action: 'cancel', subscriptions: listToRemove };
return { action: 'cancel', list: listToRemove };
}

@@ -231,142 +183,3 @@

}
}, {
key: '_applyNewSubscriptionUpdateBatch',
value: function _applyNewSubscriptionUpdateBatch(action, requests) {
var _this3 = this;
// Keeping in mind that events may begin flowing _before_ we receive the response
requests = this._processLocalActions(action, requests);
var correlationId = new Date().getTime();
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = (0, _getIterator3.default)(requests), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var subscribed = _step2.value;
this._recordActionAttemptOn(subscribed, action, correlationId);
}
// Send this batch to the service
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
this._request(action, correlationId, requests.map(function (object) {
return {
object_sid: object.sid, // eslint-disable-line camelcase
object_type: object.type, // eslint-disable-line camelcase
last_event_id: action === 'establish' ? object.lastEventId : undefined // eslint-disable-line no-undefined, camelcase
};
})).then(function (response) {
if (action === 'establish') {
var _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
try {
for (var _iterator3 = (0, _getIterator3.default)(requests), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var subscribed = _step3.value;
if (subscribed.correlationId === correlationId) {
_this3._beginReplayTimeout(response.body.estimated_delivery_in_ms, subscribed, action, correlationId);
}
}
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3.return) {
_iterator3.return();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
}
}
_this3._backoff.reset();
}).catch(function (e) {
var _iteratorNormalCompletion4 = true;
var _didIteratorError4 = false;
var _iteratorError4 = undefined;
try {
for (var _iterator4 = (0, _getIterator3.default)(requests), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
var attemptedSubscription = _step4.value;
_this3._recordActionFailureOn(attemptedSubscription, action);
}
} catch (err) {
_didIteratorError4 = true;
_iteratorError4 = err;
} finally {
try {
if (!_iteratorNormalCompletion4 && _iterator4.return) {
_iterator4.return();
}
} finally {
if (_didIteratorError4) {
throw _iteratorError4;
}
}
}
if (e instanceof _this3._ExceptionOnTwilsockUnavailable) {
_logger2.default.debug('Twilsock connection (required for subscription) not ready (c:' + correlationId + '); waiting\u2026');
_this3._backoff.reset();
} else {
_logger2.default.debug('Failed an attempt to ' + action + ' subscriptions (c:' + correlationId + '); retrying', e);
_this3._backoff.backoff(e);
}
});
}
}, {
key: '_processLocalActions',
value: function _processLocalActions(action, requests) {
if (action === 'cancel') {
return requests.filter(function (request) {
return !request.rejectedWithError;
});
}
return requests;
}
}, {
key: '_recordActionAttemptOn',
value: function _recordActionAttemptOn(attemptedSubscription, action, correlationId) {
if (action === 'establish') {
this._persisted.set(attemptedSubscription.sid, attemptedSubscription);
attemptedSubscription.correlationId = correlationId;
} else {
// cancel
var persistedSubscription = this._persisted.get(attemptedSubscription.sid);
if (persistedSubscription) {
persistedSubscription.correlationId = null;
}
}
}
}, {
key: '_recordActionFailureOn',
value: function _recordActionFailureOn(attemptedSubscription, action) {
attemptedSubscription.correlationId = null;
if (action === 'establish') {
this._persisted.delete(attemptedSubscription.sid);
}
}
/**

@@ -378,45 +191,34 @@ * @private

key: '_request',
value: function _request(action, correlationId, requests) {
_logger2.default.debug('Attempting \'' + action + '\' request (c:' + correlationId + '):', requests);
/* eslint-disable camelcase */
value: function _request(action, requests) {
var _this3 = this;
_logger2.default.debug('Modifying server subscriptions: ', action, requests);
var requestBody = {
event_protocol_version: 3,
event_protocol_version: 3, // eslint-disable-line camelcase
action: action,
correlation_id: correlationId,
requests: requests
};
/* eslint-enable camelcase */
return this._network.post(this._config.subscriptionsUri, requestBody, null, true);
}
}, {
key: '_beginReplayTimeout',
value: function _beginReplayTimeout(timeout, subscription, action, failingCorrelationId) {
var _this4 = this;
var isNumeric = !isNaN(parseFloat(timeout)) && isFinite(timeout);
var isValidTimeout = isNumeric && timeout > 0;
if (isValidTimeout) {
subscription.pendingPokeTimer = setTimeout(function () {
if (subscription.correlationId === failingCorrelationId) {
_logger2.default.debug('Attempt to ' + action + ' ' + subscription.sid + ' (c:' + failingCorrelationId + ') timed out without confirmation; trying again.');
subscription.correlationId = null;
_this4._persisted.delete(subscription.sid);
_this4._persist();
return this._network.post(this._config.subscriptionsUri, requestBody).then(function (response) {
response.body.results.forEach(function (persisted) {
if (!persisted.subscription) {
_this3._persisted.delete(persisted.object_sid);
_this3._types.delete(persisted.object_sid);
} else {
_this3._persisted.set(persisted.object_sid, persisted.subscription);
var subscription = _this3._subscriptions.get(persisted.object_sid);
if (subscription) {
subscription.resolve(true);
}
}
}, timeout);
}
});
});
}
/**
* Establishes intent to be subscribed to this entity. That subscription will be effected
* asynchronously.
* If subscription to the given sid already exists, it will be overwritten.
*
* @param {string} sid should be a well-formed SID, uniquely identifying a single instance of a Sync entity.
* @param {object} entity should represent the (singular) local representation of this entity.
* Incoming events and modifications to the entity will be directed at the _update() function
* of this provided reference.
*
* @return undefined
* Add subscription
* @param {string} uri URI to the server object
* @param {object} endpoint Endpoint object
* @return Promise<boolean> true if subscription haven't been there before
*/

@@ -427,24 +229,37 @@

value: function add(sid, entity) {
_logger2.default.debug('Establishing intent to subscribe to ' + sid);
var existingSubscription = this._subscriptions.get(sid);
if (existingSubscription && existingSubscription.lastEventId === entity.lastEventId) {
// If last event id is the same as before - we're fine
return;
var _this4 = this;
var subscribed = this._subscriptions.has(sid);
var persisted = this._persisted.has(sid);
if (subscribed && persisted) {
var _subscription = this._subscriptions.get(sid);
_subscription.subscribers.add(entity);
return _promise2.default.resolve(false);
} else if (this._subscribed && !persisted) {
var _subscription2 = this._subscriptions.get(sid);
return _subscription2.promise;
}
this._persisted.delete(sid);
this._subscriptions.set(sid, entity);
this._persist();
var subscription = { type: entity.type,
subscribers: new _set2.default([entity]),
promise: null,
resolve: null,
reject: null
};
this._subscriptions.set(sid, subscription);
var promiseToSubscribe = new _promise2.default(function (resolve, reject) {
subscription.resolve = resolve;
subscription.reject = reject;
_this4._persist();
});
subscription.promise = promiseToSubscribe;
return promiseToSubscribe;
}
/**
* Establishes the caller's intent to no longer be subscribed to this entity. Following this
* call, no further events shall be routed to the local representation of the entity, even
* though a server-side subscription may take more time to actually terminate.
*
* @param {string} sid should be any well-formed SID, uniquely identifying a Sync entity.
* This call only has meaningful effect if that entity is subscribed at the
* time of call. Otherwise does nothing.
*
* @return undefined
* Remove subscription for the entity
* @param {string} entityUri URI
* @param {object} endpoint Endpoint object
*/

@@ -454,167 +269,88 @@

key: 'remove',
value: function remove(sid) {
_logger2.default.debug('Establishing intent to unsubscribe from ' + sid);
var removed = this._subscriptions.delete(sid);
if (removed) {
this._persist();
value: function remove(id, entity) {
if (!this._subscriptions.has(id)) {
return _promise2.default.resolve(true);
}
}
/**
* The point of ingestion for remote incoming messages (e.g. new data was written to a map
* to which we are subscribed).
*
* @param {object} message is the full, unaltered body of the incoming notification.
*
* @return undefined
*/
var subscription = this._subscriptions.get(id);
if (!subscription.subscribers.has(entity)) {
return _promise2.default.resolve(false);
}
}, {
key: 'acceptMessage',
value: function acceptMessage(message) {
_logger2.default.trace('Subscriptions received', message);
subscription.subscribers.delete(entity);
if (subscription.subscribers.size > 0) {
return _promise2.default.resolve(false);
}
switch (message.event_type) {
case 'subscription_established':
this._applySubscriptionEstablishedMessage(message.event, message.correlation_id);
break;
case 'subscription_canceled':
this._applySubscriptionCancelledMessage(message.event, message.correlation_id);
break;
case 'subscription_failed':
this._applySubscriptionFailedMessage(message.event, message.correlation_id);
break;
case (message.event_type.match(/^(?:map|list|document)_/) || {}).input:
{
var typedSid = function typedSid() {
if (message.event_type.match(/^map_/)) return message.event.map_sid;else if (message.event_type.match(/^list_/)) return message.event.list_sid;else if (message.event_type.match(/^document_/)) return message.event.document_sid;else return undefined; // eslint-disable-line no-undefined
};
// It's a hack: since server responce doesn't contain a type, but we need it
// we should always keep it locally
this._types.set(id, subscription.type);
this._applyEventToSubscribedEntity(typedSid(), message);
}
break;
default:
_logger2.default.debug('Dropping unknown message type ' + message.event_type);
break;
}
this._subscriptions.delete(id);
this._persist();
return _promise2.default.resolve(true);
}
}, {
key: '_applySubscriptionEstablishedMessage',
value: function _applySubscriptionEstablishedMessage(message, correlationId) {
var sid = message.object_sid;
var subscriptionIntent = this._persisted.get(message.object_sid);
if (subscriptionIntent && subscriptionIntent.correlationId === correlationId) {
if (message.replay_status === 'interrupted') {
_logger2.default.debug('Event Replay for subscription to ' + sid + ' (c:' + correlationId + ') interrupted; continuing eagerly.');
clearTimeout(subscriptionIntent.pendingPokeTimer);
subscriptionIntent.pendingPokeTimer = null;
subscriptionIntent.correlationId = null;
this._persisted.delete(subscriptionIntent.sid);
this._backoff.reset();
} else if (message.replay_status === 'completed') {
_logger2.default.debug('Event Replay for subscription to ' + sid + ' (c:' + correlationId + ') completed. Subscription is ready.');
clearTimeout(subscriptionIntent.pendingPokeTimer);
subscriptionIntent.pendingPokeTimer = null;
subscriptionIntent.correlationId = null;
this._persisted.set(message.object_sid, subscriptionIntent);
/**
* Query subscribers for given URI
* @return {set}
*/
this._backoff.reset();
}
} else {
_logger2.default.debug('Late message for ' + message.object_sid + ' (c:' + correlationId + ') dropped.');
}
this._persist();
}
}, {
key: '_applySubscriptionCancelledMessage',
value: function _applySubscriptionCancelledMessage(message, correlationId) {
var persistedSubscription = this._persisted.get(message.object_sid);
if (persistedSubscription && persistedSubscription.correlationId === correlationId) {
clearTimeout(persistedSubscription.pendingPokeTimer);
persistedSubscription.pendingPokeTimer = null;
persistedSubscription.correlationId = null;
this._persisted.delete(message.object_sid);
} else {
_logger2.default.debug('Late message for ' + message.object_sid + ' (c:' + correlationId + ') dropped.');
}
this._persist();
key: 'getSubscribers',
value: function getSubscribers(id) {
var subscription = this._subscriptions.get(id);
return subscription && subscription.subscribers ? subscription.subscribers : new _set2.default();
}
}, {
key: '_applySubscriptionFailedMessage',
value: function _applySubscriptionFailedMessage(message, correlationId) {
var sid = message.object_sid;
var subscriptionIntent = this._subscriptions.get(sid);
var subscription = this._persisted.get(sid);
if (subscriptionIntent && subscription) {
if (subscription.correlationId === correlationId) {
subscription.markAsFailed(message.error);
_logger2.default.error('Failed to subscribe on ' + subscription.sid, message.error);
subscriptionIntent._reportFailure(new _syncerror2.default('Failed to subscribe on service events'));
}
} else if (!subscriptionIntent && subscription) {
this._persisted.delete(sid);
}
this._persist();
}
/**
* Iterates through all subscriptions
* @param {function} hanlder function to call for each subscription
*/
}, {
key: '_applyEventToSubscribedEntity',
value: function _applyEventToSubscribedEntity(sid, message) {
var subscriptionIntent = sid ? this._subscriptions.get(sid) : null;
if (subscriptionIntent) {
message.event.type = message.event_type;
subscriptionIntent._update(message.event, true);
} else {
_logger2.default.debug('Message dropped for SID \'' + sid + '\', for which there is no subscription.');
}
key: 'forEach',
value: function forEach(handler) {
this._subscriptions.forEach(function (subscription, id) {
subscription.subscribers.forEach(function (subscriber) {
return handler(id, subscriber);
});
});
}
/**
* Prompts a playback of any missed changes made to any subscribed object. This method
* should be invoked whenever the connectivity layer has experienced cross-cutting
* delivery failures that would affect the entire local sync set. Any tangible result
* of this operation will result in calls to the _update() function of subscribed
* Sync entities.
* Currently just resets retries
* Should remove all subscriptions
*/
}, {
key: 'poke',
value: function poke() {
_logger2.default.info('Triggering event replay for all subscriptions.');
key: 'shutdown',
value: function shutdown() {
this._backoff.reset();
this._subscriptions.clear();
var failedSubscriptions = [];
var listToRemove = [];
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
var _iteratorNormalCompletion5 = true;
var _didIteratorError5 = false;
var _iteratorError5 = undefined;
try {
for (var _iterator5 = (0, _getIterator3.default)(this._persisted), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
var _step5$value = (0, _slicedToArray3.default)(_step5.value, 2),
_ = _step5$value[0],
it = _step5$value[1];
for (var _iterator2 = (0, _getIterator3.default)(this._persisted), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var _step2$value = (0, _slicedToArray3.default)(_step2.value, 2),
key = _step2$value[0],
value = _step2$value[1];
// eslint-disable-line no-unused-vars
clearTimeout(it.pendingPokeTimer);
it.pendingPokeTimer = null;
it.correlationId = null;
if (it.rejectedWithError) {
failedSubscriptions.push(it);
}
listToRemove.push({ object_sid: key, type: value.type }); // eslint-disable-line camelcase
}
} catch (err) {
_didIteratorError5 = true;
_iteratorError5 = err;
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion5 && _iterator5.return) {
_iterator5.return();
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError5) {
throw _iteratorError5;
if (_didIteratorError2) {
throw _iteratorError2;
}

@@ -624,41 +360,7 @@ }

this._persisted.clear();
var _iteratorNormalCompletion6 = true;
var _didIteratorError6 = false;
var _iteratorError6 = undefined;
try {
for (var _iterator6 = (0, _getIterator3.default)(failedSubscriptions), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
var it = _step6.value;
this._persisted.set(it.sid, it);
}
} catch (err) {
_didIteratorError6 = true;
_iteratorError6 = err;
} finally {
try {
if (!_iteratorNormalCompletion6 && _iterator6.return) {
_iterator6.return();
}
} finally {
if (_didIteratorError6) {
throw _iteratorError6;
}
}
if (listToRemove.length > 0) {
return this._request('cancel', listToRemove).catch(function () {});
}
this._persist();
return _promise2.default.resolve();
}
/**
* Stops all communication, clears any subscription intent, and returns.
*/
}, {
key: 'shutdown',
value: function shutdown() {
this._backoff.reset();
this._subscriptions.clear();
}
}]);

@@ -665,0 +367,0 @@ return Subscriptions;

@@ -369,4 +369,2 @@ 'use strict';

value: function _onRemoved(locally) {
this._unsubscribe();
// Should also do some cleanup here

@@ -400,7 +398,2 @@ this.emit('removed', locally);

}
}, {
key: 'lastEventId',
get: function get() {
return this._lastEventId;
}
}]);

@@ -407,0 +400,0 @@ return SyncDocument;

@@ -443,4 +443,2 @@ 'use strict';

value: function _onRemoved(locally) {
this._unsubscribe();
// Should also do some cleanup here

@@ -479,8 +477,3 @@ this.emit('collectionRemoved', locally);

/**
* Handle update event
*
* @param {Object} update
* @param {Boolean} reliable Caller should point if transport reliable or not
* For reliable transport it will advance lastEventId even if gaps in events were found
*
* Handle update, which came from the server
* @private

@@ -491,3 +484,3 @@ */

key: '_update',
value: function _update(update, reliableTransport) {
value: function _update(update) {
var itemIndex = Number(update.item_index);

@@ -522,7 +515,4 @@ switch (update.type) {

this._revision = update.list_revision;
var updateHasNoGap = update.id - this._lastEventId === 1;
var shouldAdvance = this._lastEventId < update.id && (reliableTransport || updateHasNoGap);
if (shouldAdvance) {
if (this._lastEventId < update.id) {
this._revision = update.list_revision;
this._lastEventId = update.id;

@@ -599,7 +589,2 @@ }

}
}, {
key: 'lastEventId',
get: function get() {
return this._lastEventId;
}
}]);

@@ -606,0 +591,0 @@ return SyncList;

@@ -572,8 +572,3 @@ 'use strict';

/**
* Handle update event
*
* @param {Object} update
* @param {Boolean} reliable Caller should point if transport reliable or not
* For reliable transport it will advance lastEventId even if gaps in events were found
*
* Handle update from the server
* @private

@@ -584,3 +579,3 @@ */

key: '_update',
value: function _update(update, reliableTransport) {
value: function _update(update) {
switch (update.type) {

@@ -614,7 +609,4 @@ case 'map_item_added':

this._revision = update.map_revision;
var updateHasNoGap = update.id - this._lastEventId === 1;
var shouldAdvance = this._lastEventId < update.id && (reliableTransport || updateHasNoGap);
if (shouldAdvance) {
if (this._lastEventId < update.id) {
this._revision = update.map_revision;
this._lastEventId = update.id;

@@ -691,4 +683,2 @@ }

value: function _onRemoved(locally) {
this._unsubscribe();
// Should also do some cleanup here

@@ -722,7 +712,2 @@ this.emit('collectionRemoved', locally);

}
}, {
key: 'lastEventId',
get: function get() {
return this._lastEventId;
}
}]);

@@ -729,0 +714,0 @@ return SyncMap;

{
"name": "twilio-sync",
"version": "0.3.7",
"version": "0.3.8",
"description": "Twilio Sync client library",

@@ -16,26 +16,26 @@ "main": "lib/index.js",

"dependencies": {
"babel-runtime": "^6.20.0",
"babel-runtime": "^6.11.6",
"loglevel": "^1.4.1",
"platform": "^1.3.3",
"platform": "^1.3.1",
"rfc6902": "^1.2.2",
"twilio-notifications": "^0.2.1",
"twilio-transport": "^0.1.1",
"twilsock": "^0.2.1",
"twilio-notifications": "^0.2.0",
"twilio-transport": "^0.0.8",
"twilsock": "^0.2.0",
"uuid": "^3.0.1"
},
"devDependencies": {
"async-test-tools": "^1.0.6",
"babel-cli": "^6.18.0",
"babel-eslint": "^7.1.1",
"async-test-tools": "^1.0.5",
"babel-cli": "^6.14.0",
"babel-eslint": "^7.0.0",
"babel-plugin-add-module-exports": "^0.2.1",
"babel-plugin-transform-object-assign": "^6.8.0",
"babel-plugin-transform-runtime": "^6.15.0",
"babel-preset-es2015": "^6.18.0",
"babel-preset-es2015": "^6.9.0",
"babelify": "^7.3.0",
"backoff": "^2.5.0",
"browserify": "^13.3.0",
"browserify": "^13.1.0",
"chai": "^3.5.0",
"chai-as-promised": "^6.0.0",
"cheerio": "^0.22.0",
"del": "^2.2.2",
"del": "^2.2.1",
"event-to-promise": "^0.7.0",

@@ -47,3 +47,3 @@ "gulp": "^3.9.1",

"gulp-insert": "^0.5.0",
"gulp-istanbul": "^1.1.1",
"gulp-istanbul": "^1.0.0",
"gulp-mocha": "^3.0.1",

@@ -54,7 +54,7 @@ "gulp-rename": "^1.2.2",

"gulp-uglify": "^2.0.0",
"gulp-util": "^3.0.8",
"ink-docstrap": "^1.3.0",
"gulp-util": "^3.0.7",
"ink-docstrap": "^1.2.1",
"isparta": "^4.0.0",
"jsdoc": "^3.4.3",
"jsonwebtoken": "^7.2.1",
"jsdoc": "^3.4.0",
"jsonwebtoken": "^7.1.6",
"karma": "^1.3.0",

@@ -66,3 +66,3 @@ "karma-browserify": "^5.1.0",

"run-sequence": "^1.2.2",
"sinon": "^1.17.7",
"sinon": "^1.17.5",
"sinon-as-promised": "^4.0.2",

@@ -73,5 +73,4 @@ "sinon-chai": "^2.8.0",

"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0",
"watchify": "^3.8.0"
"vinyl-source-stream": "^1.1.0"
}
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc