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-dev-build.162 to 0.3.7-dev-build.177

12

lib/client.js

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/**
* 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

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

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

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

@@ -63,2 +67,6 @@

* 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

@@ -71,9 +79,22 @@ */

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);
return this._transport.get(uri, headers, forceTwilsock).catch(this._mapTransportError);
}
/**
* 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();

@@ -83,24 +104,33 @@ if (typeof revision !== 'undefined') {

}
_logger2.default.debug('POST', uri, 'ID:', headers['Twilio-Request-Id']);
return this._transport.post(uri, headers, body);
return this._transport.post(uri, headers, body, forceTwilsock).catch(this._mapTransportError);
}
}, {
key: 'put',
value: function put(uri, body, revision) {
var headers = this._generateHeaders();
if (typeof revision !== 'undefined') {
headers['If-Match'] = revision;
}
_logger2.default.debug('PUT', uri, 'ID:', headers['Twilio-Request-Id']);
return this._transport.put(uri, headers, body);
}
/**
* 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.
*/
}, {
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);
return this._transport.delete(uri, headers, forceTwilsock).catch(this._mapTransportError);
}
}, {
key: '_mapTransportError',
value: function _mapTransportError(transportError) {
var body = transportError.body;
if (body && body.status === 429) {
throw new _syncerror2.default(body.message, 'RATE_LIMIT_EXCEEDED');
} else {
throw transportError;
}
}
}]);

@@ -107,0 +137,0 @@ return Network;

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

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

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

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

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

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

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

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

@@ -74,25 +78,6 @@ 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);
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));
});
}
this._subscriptions.acceptMessage(message);
}

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

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

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

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

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

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

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

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

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

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

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

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

var _extends2 = require('babel-runtime/helpers/extends');
var _extends3 = _interopRequireDefault(_extends2);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');

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

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');

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

var _syncerror = require('./syncerror');
var _syncerror2 = _interopRequireDefault(_syncerror);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var MAX_SUBSCRIPTION_BATCH_SIZE = 100;
var MAX_BATCH_SIZE = 1000;
function substract(p1, p2, limit) {
var result = [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
/**
* A data container used by the Subscriptions class to track subscribed entities' local
* representations and their state.
*/
/* eslint-disable key-spacing */
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];
var SubscribedEntity = function () {
function SubscribedEntity(entity) {
var _this = this;
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, _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;
} }
});
}
return result;
}
(0, _createClass3.default)(SubscribedEntity, [{
key: 'markAsFailed',
value: function markAsFailed(error) {
this.rejectedWithError = error;
}
}]);
return SubscribedEntity;
}();
/**
* @class Subscriptions
* @classdesc Subscriptions container for CDS objects
* @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.
*/
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) {
var _this = this;
function Subscriptions(config, network, TwilsockUnvailableErrorType) {
var _this2 = this;
(0, _classCallCheck3.default)(this, Subscriptions);
function createBackoff() {
var defaultBackoffConfig = {
randomisationFactor: 0.2,
initialDelay: 50,
initialDelay: 100,
maxDelay: 2 * 60 * 1000

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

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

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

_types: { value: new _map2.default() },
// 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.
_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 _getAction2 = _this._getAction(),
action = _getAction2.action,
list = _getAction2.list;
var _getSubscriptionUpdat = _this2._getSubscriptionUpdateBatch(),
action = _getSubscriptionUpdat.action,
subscriptionRequests = _getSubscriptionUpdat.subscriptions;
if (action) {
_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);
});
_this2._applyNewSubscriptionUpdateBatch(action, subscriptionRequests);
} else {
_this._backoff.reset();
_logger2.default.info('Subscription list persisted');
_this2._backoff.reset();
_logger2.default.info('All subscriptions resolved.');
}

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

(0, _createClass3.default)(Subscriptions, [{
key: '_getAction',
value: function _getAction() {
var _this2 = this;
key: '_getSubscriptionUpdateBatch',
value: function _getSubscriptionUpdateBatch() {
function substract(these, those, ignoreCurrentOp, limit) {
var result = [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
var listToAdd = substract(this._subscriptions, this._persisted, MAX_SUBSCRIPTION_BATCH_SIZE);
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, false, MAX_BATCH_SIZE).map(function (x) {
return new SubscribedEntity(x);
});
if (listToAdd.length > 0) {
return { action: 'establish', list: listToAdd };
return { action: 'establish', subscriptions: listToAdd };
}
var listToRemove = substract(this._persisted, this._subscriptions, MAX_SUBSCRIPTION_BATCH_SIZE).map(function (x) {
return { sid: x.sid, type: _this2._types.get(x.sid) };
});
var listToRemove = substract(this._persisted, this._subscriptions, true, MAX_BATCH_SIZE);
if (listToRemove.length > 0) {
return { action: 'cancel', list: listToRemove };
return { action: 'cancel', subscriptions: listToRemove };
}

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

}
}, {
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);
}
}
/**

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

key: '_request',
value: function _request(action, requests) {
var _this3 = this;
_logger2.default.debug('Modifying server subscriptions: ', action, requests);
value: function _request(action, correlationId, requests) {
_logger2.default.debug('Attempting \'' + action + '\' request (c:' + correlationId + '):', requests);
/* eslint-disable camelcase */
var requestBody = {
event_protocol_version: 3, // eslint-disable-line camelcase
event_protocol_version: 3,
action: action,
correlation_id: correlationId,
requests: requests
};
/* eslint-enable camelcase */
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);
}
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();
}
});
});
}, timeout);
}
}
/**
* 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
* 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
*/

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

value: function add(sid, entity) {
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;
_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 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;
this._persisted.delete(sid);
this._subscriptions.set(sid, entity);
this._persist();
}
/**
* Remove subscription for the entity
* @param {string} entityUri URI
* @param {object} endpoint Endpoint object
* 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
*/

@@ -269,88 +454,193 @@

key: 'remove',
value: function remove(id, entity) {
if (!this._subscriptions.has(id)) {
return _promise2.default.resolve(true);
value: function remove(sid) {
_logger2.default.debug('Establishing intent to unsubscribe from ' + sid);
var removed = this._subscriptions.delete(sid);
if (removed) {
this._persist();
}
}
var subscription = this._subscriptions.get(id);
if (!subscription.subscribers.has(entity)) {
return _promise2.default.resolve(false);
}
/**
* 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
*/
subscription.subscribers.delete(entity);
if (subscription.subscribers.size > 0) {
return _promise2.default.resolve(false);
}, {
key: 'acceptMessage',
value: function acceptMessage(message) {
_logger2.default.trace('Subscriptions received', message);
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
};
this._applyEventToSubscribedEntity(typedSid(), message);
}
break;
default:
_logger2.default.debug('Dropping unknown message type ' + message.event_type);
break;
}
}
}, {
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.');
// 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);
clearTimeout(subscriptionIntent.pendingPokeTimer);
subscriptionIntent.pendingPokeTimer = null;
subscriptionIntent.correlationId = null;
this._persisted.set(message.object_sid, subscriptionIntent);
this._subscriptions.delete(id);
this._backoff.reset();
}
} else {
_logger2.default.debug('Late message for ' + message.object_sid + ' (c:' + correlationId + ') dropped.');
}
this._persist();
return _promise2.default.resolve(true);
}
}, {
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;
/**
* Query subscribers for given URI
* @return {set}
*/
this._persisted.delete(message.object_sid);
} else {
_logger2.default.debug('Late message for ' + message.object_sid + ' (c:' + correlationId + ') dropped.');
}
this._persist();
}
}, {
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();
}
}, {
key: 'getSubscribers',
value: function getSubscribers(id) {
var subscription = this._subscriptions.get(id);
return subscription && subscription.subscribers ? subscription.subscribers : new _set2.default();
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.');
}
}
/**
* Iterates through all subscriptions
* @param {function} hanlder function to call for each subscription
* 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.
*/
}, {
key: 'forEach',
value: function forEach(handler) {
this._subscriptions.forEach(function (subscription, id) {
subscription.subscribers.forEach(function (subscriber) {
return handler(id, subscriber);
});
});
}
key: 'poke',
value: function poke() {
_logger2.default.info('Triggering event replay for all subscriptions.');
/**
* Currently just resets retries
* Should remove all subscriptions
*/
var failedSubscriptions = [];
}, {
key: 'shutdown',
value: function shutdown() {
this._backoff.reset();
this._subscriptions.clear();
var _iteratorNormalCompletion5 = true;
var _didIteratorError5 = false;
var _iteratorError5 = undefined;
var listToRemove = [];
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = 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];
// eslint-disable-line no-unused-vars
clearTimeout(it.pendingPokeTimer);
it.pendingPokeTimer = null;
it.correlationId = null;
if (it.rejectedWithError) {
failedSubscriptions.push(it);
}
}
} catch (err) {
_didIteratorError5 = true;
_iteratorError5 = err;
} finally {
try {
if (!_iteratorNormalCompletion5 && _iterator5.return) {
_iterator5.return();
}
} finally {
if (_didIteratorError5) {
throw _iteratorError5;
}
}
}
this._persisted.clear();
var _iteratorNormalCompletion6 = true;
var _didIteratorError6 = false;
var _iteratorError6 = undefined;
try {
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];
for (var _iterator6 = (0, _getIterator3.default)(failedSubscriptions), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
var it = _step6.value;
listToRemove.push({ object_sid: key, type: value.type }); // eslint-disable-line camelcase
this._persisted.set(it.sid, it);
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
_didIteratorError6 = true;
_iteratorError6 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
if (!_iteratorNormalCompletion6 && _iterator6.return) {
_iterator6.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
if (_didIteratorError6) {
throw _iteratorError6;
}

@@ -360,7 +650,15 @@ }

if (listToRemove.length > 0) {
return this._request('cancel', listToRemove).catch(function () {});
}
return _promise2.default.resolve();
this._persist();
}
/**
* Stops all communication, clears any subscription intent, and returns.
*/
}, {
key: 'shutdown',
value: function shutdown() {
this._backoff.reset();
this._subscriptions.clear();
}
}]);

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

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

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

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

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

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

@@ -1,2 +0,2 @@

"use strict";
'use strict';

@@ -7,19 +7,19 @@ Object.defineProperty(exports, "__esModule", {

var _defineProperties = require("babel-runtime/core-js/object/define-properties");
var _defineProperties = require('babel-runtime/core-js/object/define-properties');
var _defineProperties2 = _interopRequireDefault(_defineProperties);
var _getPrototypeOf = require("babel-runtime/core-js/object/get-prototype-of");
var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
var _classCallCheck2 = require("babel-runtime/helpers/classCallCheck");
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _possibleConstructorReturn2 = require("babel-runtime/helpers/possibleConstructorReturn");
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
var _inherits2 = require("babel-runtime/helpers/inherits");
var _inherits2 = require('babel-runtime/helpers/inherits');

@@ -39,3 +39,3 @@ var _inherits3 = _interopRequireDefault(_inherits2);

(0, _defineProperties2.default)(_this, {
name: { value: _this.constructor.name, enumerable: true },
name: { value: 'SyncError', enumerable: true },
message: { value: message, enumerable: true },

@@ -51,2 +51,2 @@ code: { value: code, enumerable: true }

exports.default = SyncError;
module.exports = exports["default"];
module.exports = exports['default'];

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

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

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

/**
* Handle update, which came from the server
* 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
*
* @private

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

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

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

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

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

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

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

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

/**
* Handle update from the server
* 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
*
* @private

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

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

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

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

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

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

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

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

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

{
"name": "twilio-sync",
"version": "0.3.7-dev-build.162",
"version": "0.3.7-dev-build.177",
"description": "Twilio Sync client library",

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

"dependencies": {
"babel-runtime": "^6.11.6",
"babel-runtime": "^6.20.0",
"loglevel": "^1.4.1",
"platform": "^1.3.1",
"platform": "^1.3.3",
"rfc6902": "^1.2.2",
"twilio-notifications": "^0.2.0",
"twilio-transport": "^0.0.8",
"twilsock": "^0.2.0",
"twilio-notifications": "^0.2.1",
"twilio-transport": "^0.1.1",
"twilsock": "^0.2.1",
"uuid": "^3.0.1"
},
"devDependencies": {
"async-test-tools": "^1.0.5",
"babel-cli": "^6.14.0",
"babel-eslint": "^7.0.0",
"async-test-tools": "^1.0.6",
"babel-cli": "^6.18.0",
"babel-eslint": "^7.1.1",
"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.9.0",
"babel-preset-es2015": "^6.18.0",
"babelify": "^7.3.0",
"backoff": "^2.5.0",
"browserify": "^13.1.0",
"browserify": "^13.3.0",
"chai": "^3.5.0",
"chai-as-promised": "^6.0.0",
"cheerio": "^0.22.0",
"del": "^2.2.1",
"del": "^2.2.2",
"event-to-promise": "^0.7.0",

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

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

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

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

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

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

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

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

Sorry, the diff of this file is not supported yet

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