Skip to content

Commit

Permalink
fix: Transpile node for different js build systems
Browse files Browse the repository at this point in the history
We need to transpile nodeJS code as well as the browserify in order
to allow build systems that bundle without transpiling to work.

While this is a step in a direction we didn't really want to take
(transpiling everything, even if tiny steps) it will make life alot
easier and has proven to be more popular with contributors.
  • Loading branch information
Alex Harley committed Apr 11, 2017
1 parent 7a1e82f commit 37750b7
Show file tree
Hide file tree
Showing 24 changed files with 4,595 additions and 3 deletions.
2 changes: 1 addition & 1 deletion dist/deepstream.min.js

Large diffs are not rendered by default.

266 changes: 266 additions & 0 deletions dist/lib/client.js
@@ -0,0 +1,266 @@
'use strict';

var C = require('./constants/constants');
var MS = require('./constants/merge-strategies');
var Emitter = require('component-emitter2');
var Connection = require('./message/connection');
var EventHandler = require('./event/event-handler');
var RpcHandler = require('./rpc/rpc-handler');
var RecordHandler = require('./record/record-handler');
var PresenceHandler = require('./presence/presence-handler');
var defaultOptions = require('./default-options');
var AckTimeoutRegistry = require('./utils/ack-timeout-registry');

/**
* deepstream.io javascript client
*
* @copyright 2016 deepstreamHub GmbH
* @author deepstreamHub GmbH
*
*
* @{@link http://deepstream.io}
*
*
* @param {String} url URL to connect to. The protocol can be ommited, e.g. <host>:<port>.
* @param {Object} options A map of options that extend the ones specified in default-options.js
*
* @public
* @constructor
*/
var Client = function Client(url, options) {
this._url = url;
this._options = this._getOptions(options || {});

this._connection = new Connection(this, this._url, this._options);
this._ackTimeoutRegistry = new AckTimeoutRegistry(this, this._options);

this.event = new EventHandler(this._options, this._connection, this);
this.rpc = new RpcHandler(this._options, this._connection, this);
this.record = new RecordHandler(this._options, this._connection, this);
this.presence = new PresenceHandler(this._options, this._connection, this);

this._messageCallbacks = {};
this._messageCallbacks[C.TOPIC.EVENT] = this.event._$handle.bind(this.event);
this._messageCallbacks[C.TOPIC.RPC] = this.rpc._$handle.bind(this.rpc);
this._messageCallbacks[C.TOPIC.RECORD] = this.record._$handle.bind(this.record);
this._messageCallbacks[C.TOPIC.PRESENCE] = this.presence._$handle.bind(this.presence);
this._messageCallbacks[C.TOPIC.ERROR] = this._onErrorMessage.bind(this);
};

Emitter(Client.prototype); // eslint-disable-line

/**
* Send authentication parameters to the client to fully open
* the connection.
*
* Please note: Authentication parameters are send over an already established
* connection, rather than appended to the server URL. This means the parameters
* will be encrypted when used with a WSS / HTTPS connection. If the deepstream server
* on the other side has message logging enabled it will however be written to the logs in
* plain text. If additional security is a requirement it might therefor make sense to hash
* the password on the client.
*
* If the connection is not yet established the authentication parameter will be
* stored and send once it becomes available
*
* authParams can be any JSON serializable data structure and its up for the
* permission handler on the server to make sense of them, although something
* like { username: 'someName', password: 'somePass' } will probably make the most sense.
*
* login can be called multiple times until either the connection is authenticated or
* forcefully closed by the server since its maxAuthAttempts threshold has been exceeded
*
* @param {Object} authParams JSON.serializable authentication data
* @param {Function} callback Will be called with either (true) or (false, data)
*
* @public
* @returns {Client}
*/
Client.prototype.login = function (authParams, callback) {
this._connection.authenticate(authParams || {}, callback);
return this;
};

/**
* Closes the connection to the server.
*
* @public
* @returns {void}
*/
Client.prototype.close = function () {
this._connection.close();
};

/**
* Returns the current state of the connection.
*
* connectionState is one of CONSTANTS.CONNECTION_STATE
*
* @returns {[type]} [description]
*/
Client.prototype.getConnectionState = function () {
return this._connection.getState();
};

/**
* Returns a random string. The first block of characters
* is a timestamp, in order to allow databases to optimize for semi-
* sequentuel numberings
*
* @public
* @returns {String} unique id
*/
Client.prototype.getUid = function () {
var timestamp = new Date().getTime().toString(36);
var randomString = (Math.random() * 10000000000000000).toString(36).replace('.', '');

return timestamp + '-' + randomString;
};

/**
* Package private ack timeout registry. This is how all classes can get access to
* register timeouts.
* (Well... that's the intention anyways)
*
* @package private
* @returns {AckTimeoutRegistry}
*/
Client.prototype._$getAckTimeoutRegistry = function () {
return this._ackTimeoutRegistry;
};

/**
* Package private callback for parsed incoming messages. Will be invoked
* by the connection class
*
* @param {Object} message parsed deepstream message
*
* @package private
* @returns {void}
*/
Client.prototype._$onMessage = function (message) {
if (this._messageCallbacks[message.topic]) {
this._messageCallbacks[message.topic](message);
} else {
message.processedError = true;
this._$onError(message.topic, C.EVENT.MESSAGE_PARSE_ERROR, 'Received message for unknown topic ' + message.topic);
}

if (message.action === C.ACTIONS.ERROR && !message.processedError) {
this._$onError(message.topic, message.data[0], message.data.slice(0));
}
};

/**
* Package private error callback. This is the single point at which
* errors are thrown in the client. (Well... that's the intention anyways)
*
* The expectations would be for implementations to subscribe
* to the client's error event to prevent errors from being thrown
* and then decide based on the event and topic parameters how
* to handle the errors
*
* IMPORTANT: Errors that are specific to a request, e.g. a RPC
* timing out or a record not being permissioned are passed directly
* to the method that requested them
*
* @param {String} topic One of CONSTANTS.TOPIC
* @param {String} event One of CONSTANTS.EVENT
* @param {String} msg Error dependent message
*
* @package private
* @returns {void}
*/
Client.prototype._$onError = function (topic, event, msg) {
var errorMsg = void 0;

/*
* Help to diagnose the problem quicker by checking for
* some common problems
*/
if (event === C.EVENT.ACK_TIMEOUT || event === C.EVENT.RESPONSE_TIMEOUT) {
if (this.getConnectionState() === C.CONNECTION_STATE.AWAITING_AUTHENTICATION) {
errorMsg = 'Your message timed out because you\'re not authenticated. Have you called login()?';
setTimeout(this._$onError.bind(this, C.EVENT.NOT_AUTHENTICATED, C.TOPIC.ERROR, errorMsg), 1);
}
}

if (this.hasListeners('error')) {
this.emit('error', msg, event, topic);
this.emit(event, topic, msg);
} else {
console.log('--- You can catch all deepstream errors by subscribing to the error event ---');

errorMsg = event + ': ' + msg;

if (topic) {
errorMsg += ' (' + topic + ')';
}

throw new Error(errorMsg);
}
};

/**
* Passes generic messages from the error topic
* to the _$onError handler
*
* @param {Object} errorMessage parsed deepstream error message
*
* @private
* @returns {void}
*/
Client.prototype._onErrorMessage = function (errorMessage) {
this._$onError(errorMessage.topic, errorMessage.data[0], errorMessage.data[1]);
};

/**
* Creates a new options map by extending default
* options with the passed in options
*
* @param {Object} options The user specified client configuration options
*
* @private
* @returns {Object} merged options
*/
Client.prototype._getOptions = function (options) {
var mergedOptions = {};

for (var key in defaultOptions) {
if (typeof options[key] === 'undefined') {
mergedOptions[key] = defaultOptions[key];
} else {
mergedOptions[key] = options[key];
}
}

return mergedOptions;
};

/**
* Exports factory function to adjust to the current JS style of
* disliking 'new' :-)
*
* @param {String} url URL to connect to. The protocol can be ommited, e.g. <host>:<port>.
* @param {Object} options A map of options that extend the ones specified in default-options.js
*
* @public
* @returns {void}
*/
function createDeepstream(url, options) {
return new Client(url, options);
}

/**
* Expose constants to allow consumers to access them
*/
Client.prototype.CONSTANTS = C;
createDeepstream.CONSTANTS = C;

/**
* Expose merge strategies to allow consumers to access them
*/
Client.prototype.MERGE_STRATEGIES = MS;
createDeepstream.MERGE_STRATEGIES = MS;

module.exports = createDeepstream;
103 changes: 103 additions & 0 deletions dist/lib/constants/constants.js
@@ -0,0 +1,103 @@
'use strict';

exports.CONNECTION_STATE = {};

exports.CONNECTION_STATE.CLOSED = 'CLOSED';
exports.CONNECTION_STATE.AWAITING_CONNECTION = 'AWAITING_CONNECTION';
exports.CONNECTION_STATE.CHALLENGING = 'CHALLENGING';
exports.CONNECTION_STATE.AWAITING_AUTHENTICATION = 'AWAITING_AUTHENTICATION';
exports.CONNECTION_STATE.AUTHENTICATING = 'AUTHENTICATING';
exports.CONNECTION_STATE.OPEN = 'OPEN';
exports.CONNECTION_STATE.ERROR = 'ERROR';
exports.CONNECTION_STATE.RECONNECTING = 'RECONNECTING';

exports.MESSAGE_SEPERATOR = String.fromCharCode(30); // ASCII Record Seperator 1E
exports.MESSAGE_PART_SEPERATOR = String.fromCharCode(31); // ASCII Unit Separator 1F

exports.TYPES = {};
exports.TYPES.STRING = 'S';
exports.TYPES.OBJECT = 'O';
exports.TYPES.NUMBER = 'N';
exports.TYPES.NULL = 'L';
exports.TYPES.TRUE = 'T';
exports.TYPES.FALSE = 'F';
exports.TYPES.UNDEFINED = 'U';

exports.TOPIC = {};
exports.TOPIC.CONNECTION = 'C';
exports.TOPIC.AUTH = 'A';
exports.TOPIC.ERROR = 'X';
exports.TOPIC.EVENT = 'E';
exports.TOPIC.RECORD = 'R';
exports.TOPIC.RPC = 'P';
exports.TOPIC.PRESENCE = 'U';
exports.TOPIC.PRIVATE = 'PRIVATE/';

exports.EVENT = {};
exports.EVENT.CONNECTION_ERROR = 'connectionError';
exports.EVENT.CONNECTION_STATE_CHANGED = 'connectionStateChanged';
exports.EVENT.MAX_RECONNECTION_ATTEMPTS_REACHED = 'MAX_RECONNECTION_ATTEMPTS_REACHED';
exports.EVENT.CONNECTION_AUTHENTICATION_TIMEOUT = 'CONNECTION_AUTHENTICATION_TIMEOUT';
exports.EVENT.ACK_TIMEOUT = 'ACK_TIMEOUT';
exports.EVENT.NO_RPC_PROVIDER = 'NO_RPC_PROVIDER';
exports.EVENT.RESPONSE_TIMEOUT = 'RESPONSE_TIMEOUT';
exports.EVENT.DELETE_TIMEOUT = 'DELETE_TIMEOUT';
exports.EVENT.UNSOLICITED_MESSAGE = 'UNSOLICITED_MESSAGE';
exports.EVENT.MESSAGE_DENIED = 'MESSAGE_DENIED';
exports.EVENT.MESSAGE_PARSE_ERROR = 'MESSAGE_PARSE_ERROR';
exports.EVENT.VERSION_EXISTS = 'VERSION_EXISTS';
exports.EVENT.NOT_AUTHENTICATED = 'NOT_AUTHENTICATED';
exports.EVENT.MESSAGE_PERMISSION_ERROR = 'MESSAGE_PERMISSION_ERROR';
exports.EVENT.LISTENER_EXISTS = 'LISTENER_EXISTS';
exports.EVENT.NOT_LISTENING = 'NOT_LISTENING';
exports.EVENT.TOO_MANY_AUTH_ATTEMPTS = 'TOO_MANY_AUTH_ATTEMPTS';
exports.EVENT.INVALID_AUTH_MSG = 'INVALID_AUTH_MSG';
exports.EVENT.IS_CLOSED = 'IS_CLOSED';
exports.EVENT.RECORD_NOT_FOUND = 'RECORD_NOT_FOUND';
exports.EVENT.NOT_SUBSCRIBED = 'NOT_SUBSCRIBED';

exports.ACTIONS = {};
exports.ACTIONS.PING = 'PI';
exports.ACTIONS.PONG = 'PO';
exports.ACTIONS.ACK = 'A';
exports.ACTIONS.REDIRECT = 'RED';
exports.ACTIONS.CHALLENGE = 'CH';
exports.ACTIONS.CHALLENGE_RESPONSE = 'CHR';
exports.ACTIONS.READ = 'R';
exports.ACTIONS.CREATE = 'C';
exports.ACTIONS.UPDATE = 'U';
exports.ACTIONS.PATCH = 'P';
exports.ACTIONS.DELETE = 'D';
exports.ACTIONS.SUBSCRIBE = 'S';
exports.ACTIONS.UNSUBSCRIBE = 'US';
exports.ACTIONS.HAS = 'H';
exports.ACTIONS.SNAPSHOT = 'SN';
exports.ACTIONS.INVOKE = 'I';
exports.ACTIONS.SUBSCRIPTION_FOR_PATTERN_FOUND = 'SP';
exports.ACTIONS.SUBSCRIPTION_FOR_PATTERN_REMOVED = 'SR';
exports.ACTIONS.SUBSCRIPTION_HAS_PROVIDER = 'SH';
exports.ACTIONS.LISTEN = 'L';
exports.ACTIONS.UNLISTEN = 'UL';
exports.ACTIONS.LISTEN_ACCEPT = 'LA';
exports.ACTIONS.LISTEN_REJECT = 'LR';
exports.ACTIONS.PROVIDER_UPDATE = 'PU';
exports.ACTIONS.QUERY = 'Q';
exports.ACTIONS.CREATEORREAD = 'CR';
exports.ACTIONS.EVENT = 'EVT';
exports.ACTIONS.ERROR = 'E';
exports.ACTIONS.REQUEST = 'REQ';
exports.ACTIONS.RESPONSE = 'RES';
exports.ACTIONS.REJECTION = 'REJ';
exports.ACTIONS.PRESENCE_JOIN = 'PNJ';
exports.ACTIONS.PRESENCE_LEAVE = 'PNL';
exports.ACTIONS.QUERY = 'Q';
exports.ACTIONS.WRITE_ACKNOWLEDGEMENT = 'WA';

exports.CALL_STATE = {};
exports.CALL_STATE.INITIAL = 'INITIAL';
exports.CALL_STATE.CONNECTING = 'CONNECTING';
exports.CALL_STATE.ESTABLISHED = 'ESTABLISHED';
exports.CALL_STATE.ACCEPTED = 'ACCEPTED';
exports.CALL_STATE.DECLINED = 'DECLINED';
exports.CALL_STATE.ENDED = 'ENDED';
exports.CALL_STATE.ERROR = 'ERROR';
17 changes: 17 additions & 0 deletions dist/lib/constants/merge-strategies.js
@@ -0,0 +1,17 @@
'use strict';

module.exports = {
/**
* Choose the server's state over the client's
**/
REMOTE_WINS: function REMOTE_WINS(record, remoteValue, remoteVersion, callback) {
callback(null, remoteValue);
},

/**
* Choose the local state over the server's
**/
LOCAL_WINS: function LOCAL_WINS(record, remoteValue, remoteVersion, callback) {
callback(null, record.get());
}
};

0 comments on commit 37750b7

Please sign in to comment.