happn-3
Version:
pub/sub api as a service using primus and mongo & redis or nedb, can work as cluster, single process or embedded using nedb
1,898 lines (1,626 loc) • 103 kB
JavaScript
(function UMDish(name, context, definition, plugins) {
context[name] = definition.call(context);
for (var i = 0; i < plugins.length; i++) {
plugins[i](context[name])
}
if (typeof module !== "undefined" && module.exports) {
module.exports = context[name];
} else if (typeof define === "function" && define.amd) {
define(function reference() { return context[name]; });
}
})("Primus", this || {}, function wrapper() {
var define, module, exports
, Primus = (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(_dereq_,module,exports){
'use strict';
/**
* Create a function that will cleanup the instance.
*
* @param {Array|String} keys Properties on the instance that needs to be cleared.
* @param {Object} options Additional configuration.
* @returns {Function} Destroy function
* @api public
*/
module.exports = function demolish(keys, options) {
var split = /[, ]+/;
options = options || {};
keys = keys || [];
if ('string' === typeof keys) keys = keys.split(split);
/**
* Run addition cleanup hooks.
*
* @param {String} key Name of the clean up hook to run.
* @param {Mixed} selfie Reference to the instance we're cleaning up.
* @api private
*/
function run(key, selfie) {
if (!options[key]) return;
if ('string' === typeof options[key]) options[key] = options[key].split(split);
if ('function' === typeof options[key]) return options[key].call(selfie);
for (var i = 0, type, what; i < options[key].length; i++) {
what = options[key][i];
type = typeof what;
if ('function' === type) {
what.call(selfie);
} else if ('string' === type && 'function' === typeof selfie[what]) {
selfie[what]();
}
}
}
/**
* Destroy the instance completely and clean up all the existing references.
*
* @returns {Boolean}
* @api public
*/
return function destroy() {
var selfie = this
, i = 0
, prop;
if (selfie[keys[0]] === null) return false;
run('before', selfie);
for (; i < keys.length; i++) {
prop = keys[i];
if (selfie[prop]) {
if ('function' === typeof selfie[prop].destroy) selfie[prop].destroy();
selfie[prop] = null;
}
}
if (selfie.emit) selfie.emit('destroy');
run('after', selfie);
return true;
};
};
},{}],2:[function(_dereq_,module,exports){
'use strict';
/**
* Returns a function that when invoked executes all the listeners of the
* given event with the given arguments.
*
* @returns {Function} The function that emits all the things.
* @api public
*/
module.exports = function emits() {
var self = this
, parser;
for (var i = 0, l = arguments.length, args = new Array(l); i < l; i++) {
args[i] = arguments[i];
}
//
// If the last argument is a function, assume that it's a parser.
//
if ('function' !== typeof args[args.length - 1]) return function emitter() {
for (var i = 0, l = arguments.length, arg = new Array(l); i < l; i++) {
arg[i] = arguments[i];
}
return self.emit.apply(self, args.concat(arg));
};
parser = args.pop();
/**
* The actual function that emits the given event. It returns a boolean
* indicating if the event was emitted.
*
* @returns {Boolean}
* @api public
*/
return function emitter() {
for (var i = 0, l = arguments.length, arg = new Array(l + 1); i < l; i++) {
arg[i + 1] = arguments[i];
}
/**
* Async completion method for the parser.
*
* @param {Error} err Optional error when parsing failed.
* @param {Mixed} returned Emit instructions.
* @api private
*/
arg[0] = function next(err, returned) {
if (err) return self.emit('error', err);
arg = returned === undefined
? arg.slice(1) : returned === null
? [] : returned;
self.emit.apply(self, args.concat(arg));
};
parser.apply(self, arg);
return true;
};
};
},{}],3:[function(_dereq_,module,exports){
'use strict';
var has = Object.prototype.hasOwnProperty
, prefix = '~';
/**
* Constructor to create a storage for our `EE` objects.
* An `Events` instance is a plain object whose properties are event names.
*
* @constructor
* @private
*/
function Events() {}
//
// We try to not inherit from `Object.prototype`. In some engines creating an
// instance in this way is faster than calling `Object.create(null)` directly.
// If `Object.create(null)` is not supported we prefix the event names with a
// character to make sure that the built-in object properties are not
// overridden or used as an attack vector.
//
if (Object.create) {
Events.prototype = Object.create(null);
//
// This hack is needed because the `__proto__` property is still inherited in
// some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5.
//
if (!new Events().__proto__) prefix = false;
}
/**
* Representation of a single event listener.
*
* @param {Function} fn The listener function.
* @param {*} context The context to invoke the listener with.
* @param {Boolean} [once=false] Specify if the listener is a one-time listener.
* @constructor
* @private
*/
function EE(fn, context, once) {
this.fn = fn;
this.context = context;
this.once = once || false;
}
/**
* Add a listener for a given event.
*
* @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
* @param {(String|Symbol)} event The event name.
* @param {Function} fn The listener function.
* @param {*} context The context to invoke the listener with.
* @param {Boolean} once Specify if the listener is a one-time listener.
* @returns {EventEmitter}
* @private
*/
function addListener(emitter, event, fn, context, once) {
if (typeof fn !== 'function') {
throw new TypeError('The listener must be a function');
}
var listener = new EE(fn, context || emitter, once)
, evt = prefix ? prefix + event : event;
if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++;
else if (!emitter._events[evt].fn) emitter._events[evt].push(listener);
else emitter._events[evt] = [emitter._events[evt], listener];
return emitter;
}
/**
* Clear event by name.
*
* @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
* @param {(String|Symbol)} evt The Event name.
* @private
*/
function clearEvent(emitter, evt) {
if (--emitter._eventsCount === 0) emitter._events = new Events();
else delete emitter._events[evt];
}
/**
* Minimal `EventEmitter` interface that is molded against the Node.js
* `EventEmitter` interface.
*
* @constructor
* @public
*/
function EventEmitter() {
this._events = new Events();
this._eventsCount = 0;
}
/**
* Return an array listing the events for which the emitter has registered
* listeners.
*
* @returns {Array}
* @public
*/
EventEmitter.prototype.eventNames = function eventNames() {
var names = []
, events
, name;
if (this._eventsCount === 0) return names;
for (name in (events = this._events)) {
if (has.call(events, name)) names.push(prefix ? name.slice(1) : name);
}
if (Object.getOwnPropertySymbols) {
return names.concat(Object.getOwnPropertySymbols(events));
}
return names;
};
/**
* Return the listeners registered for a given event.
*
* @param {(String|Symbol)} event The event name.
* @returns {Array} The registered listeners.
* @public
*/
EventEmitter.prototype.listeners = function listeners(event) {
var evt = prefix ? prefix + event : event
, handlers = this._events[evt];
if (!handlers) return [];
if (handlers.fn) return [handlers.fn];
for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) {
ee[i] = handlers[i].fn;
}
return ee;
};
/**
* Return the number of listeners listening to a given event.
*
* @param {(String|Symbol)} event The event name.
* @returns {Number} The number of listeners.
* @public
*/
EventEmitter.prototype.listenerCount = function listenerCount(event) {
var evt = prefix ? prefix + event : event
, listeners = this._events[evt];
if (!listeners) return 0;
if (listeners.fn) return 1;
return listeners.length;
};
/**
* Calls each of the listeners registered for a given event.
*
* @param {(String|Symbol)} event The event name.
* @returns {Boolean} `true` if the event had listeners, else `false`.
* @public
*/
EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {
var evt = prefix ? prefix + event : event;
if (!this._events[evt]) return false;
var listeners = this._events[evt]
, len = arguments.length
, args
, i;
if (listeners.fn) {
if (listeners.once) this.removeListener(event, listeners.fn, undefined, true);
switch (len) {
case 1: return listeners.fn.call(listeners.context), true;
case 2: return listeners.fn.call(listeners.context, a1), true;
case 3: return listeners.fn.call(listeners.context, a1, a2), true;
case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true;
case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;
case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;
}
for (i = 1, args = new Array(len -1); i < len; i++) {
args[i - 1] = arguments[i];
}
listeners.fn.apply(listeners.context, args);
} else {
var length = listeners.length
, j;
for (i = 0; i < length; i++) {
if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true);
switch (len) {
case 1: listeners[i].fn.call(listeners[i].context); break;
case 2: listeners[i].fn.call(listeners[i].context, a1); break;
case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break;
case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break;
default:
if (!args) for (j = 1, args = new Array(len -1); j < len; j++) {
args[j - 1] = arguments[j];
}
listeners[i].fn.apply(listeners[i].context, args);
}
}
}
return true;
};
/**
* Add a listener for a given event.
*
* @param {(String|Symbol)} event The event name.
* @param {Function} fn The listener function.
* @param {*} [context=this] The context to invoke the listener with.
* @returns {EventEmitter} `this`.
* @public
*/
EventEmitter.prototype.on = function on(event, fn, context) {
return addListener(this, event, fn, context, false);
};
/**
* Add a one-time listener for a given event.
*
* @param {(String|Symbol)} event The event name.
* @param {Function} fn The listener function.
* @param {*} [context=this] The context to invoke the listener with.
* @returns {EventEmitter} `this`.
* @public
*/
EventEmitter.prototype.once = function once(event, fn, context) {
return addListener(this, event, fn, context, true);
};
/**
* Remove the listeners of a given event.
*
* @param {(String|Symbol)} event The event name.
* @param {Function} fn Only remove the listeners that match this function.
* @param {*} context Only remove the listeners that have this context.
* @param {Boolean} once Only remove one-time listeners.
* @returns {EventEmitter} `this`.
* @public
*/
EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) {
var evt = prefix ? prefix + event : event;
if (!this._events[evt]) return this;
if (!fn) {
clearEvent(this, evt);
return this;
}
var listeners = this._events[evt];
if (listeners.fn) {
if (
listeners.fn === fn &&
(!once || listeners.once) &&
(!context || listeners.context === context)
) {
clearEvent(this, evt);
}
} else {
for (var i = 0, events = [], length = listeners.length; i < length; i++) {
if (
listeners[i].fn !== fn ||
(once && !listeners[i].once) ||
(context && listeners[i].context !== context)
) {
events.push(listeners[i]);
}
}
//
// Reset the array, or remove it completely if we have no more listeners.
//
if (events.length) this._events[evt] = events.length === 1 ? events[0] : events;
else clearEvent(this, evt);
}
return this;
};
/**
* Remove all listeners, or those of the specified event.
*
* @param {(String|Symbol)} [event] The event name.
* @returns {EventEmitter} `this`.
* @public
*/
EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) {
var evt;
if (event) {
evt = prefix ? prefix + event : event;
if (this._events[evt]) clearEvent(this, evt);
} else {
this._events = new Events();
this._eventsCount = 0;
}
return this;
};
//
// Alias methods names because people roll like that.
//
EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
EventEmitter.prototype.addListener = EventEmitter.prototype.on;
//
// Expose the prefix.
//
EventEmitter.prefixed = prefix;
//
// Allow `EventEmitter` to be imported as module namespace.
//
EventEmitter.EventEmitter = EventEmitter;
//
// Expose the module.
//
if ('undefined' !== typeof module) {
module.exports = EventEmitter;
}
},{}],4:[function(_dereq_,module,exports){
if (typeof Object.create === 'function') {
// implementation from standard node.js 'util' module
module.exports = function inherits(ctor, superCtor) {
if (superCtor) {
ctor.super_ = superCtor
ctor.prototype = Object.create(superCtor.prototype, {
constructor: {
value: ctor,
enumerable: false,
writable: true,
configurable: true
}
})
}
};
} else {
// old school shim for old browsers
module.exports = function inherits(ctor, superCtor) {
if (superCtor) {
ctor.super_ = superCtor
var TempCtor = function () {}
TempCtor.prototype = superCtor.prototype
ctor.prototype = new TempCtor()
ctor.prototype.constructor = ctor
}
}
}
},{}],5:[function(_dereq_,module,exports){
'use strict';
var regex = new RegExp('^((?:\\d+)?\\.?\\d+) *('+ [
'milliseconds?',
'msecs?',
'ms',
'seconds?',
'secs?',
's',
'minutes?',
'mins?',
'm',
'hours?',
'hrs?',
'h',
'days?',
'd',
'weeks?',
'wks?',
'w',
'years?',
'yrs?',
'y'
].join('|') +')?$', 'i');
var second = 1000
, minute = second * 60
, hour = minute * 60
, day = hour * 24
, week = day * 7
, year = day * 365;
/**
* Parse a time string and return the number value of it.
*
* @param {String} ms Time string.
* @returns {Number}
* @api private
*/
module.exports = function millisecond(ms) {
var type = typeof ms
, amount
, match;
if ('number' === type) return ms;
else if ('string' !== type || '0' === ms || !ms) return 0;
else if (+ms) return +ms;
//
// We are vulnerable to the regular expression denial of service (ReDoS).
// In order to mitigate this we don't parse the input string if it is too long.
// See https://nodesecurity.io/advisories/46.
//
if (ms.length > 10000 || !(match = regex.exec(ms))) return 0;
amount = parseFloat(match[1]);
switch (match[2].toLowerCase()) {
case 'years':
case 'year':
case 'yrs':
case 'yr':
case 'y':
return amount * year;
case 'weeks':
case 'week':
case 'wks':
case 'wk':
case 'w':
return amount * week;
case 'days':
case 'day':
case 'd':
return amount * day;
case 'hours':
case 'hour':
case 'hrs':
case 'hr':
case 'h':
return amount * hour;
case 'minutes':
case 'minute':
case 'mins':
case 'min':
case 'm':
return amount * minute;
case 'seconds':
case 'second':
case 'secs':
case 'sec':
case 's':
return amount * second;
default:
return amount;
}
};
},{}],6:[function(_dereq_,module,exports){
'use strict';
/**
* Wrap callbacks to prevent double execution.
*
* @param {Function} fn Function that should only be called once.
* @returns {Function} A wrapped callback which prevents execution.
* @api public
*/
module.exports = function one(fn) {
var called = 0
, value;
/**
* The function that prevents double execution.
*
* @api private
*/
function onetime() {
if (called) return value;
called = 1;
value = fn.apply(this, arguments);
fn = null;
return value;
}
//
// To make debugging more easy we want to use the name of the supplied
// function. So when you look at the functions that are assigned to event
// listeners you don't see a load of `onetime` functions but actually the
// names of the functions that this module will call.
//
onetime.displayName = fn.displayName || fn.name || onetime.displayName || onetime.name;
return onetime;
};
},{}],7:[function(_dereq_,module,exports){
// shim for using process in browser
var process = module.exports = {};
// cached from whatever global is present so that test runners that stub it
// don't break things. But we need to wrap it in a try catch in case it is
// wrapped in strict mode code which doesn't define any globals. It's inside a
// function because try/catches deoptimize in certain engines.
var cachedSetTimeout;
var cachedClearTimeout;
function defaultSetTimout() {
throw new Error('setTimeout has not been defined');
}
function defaultClearTimeout () {
throw new Error('clearTimeout has not been defined');
}
(function () {
try {
if (typeof setTimeout === 'function') {
cachedSetTimeout = setTimeout;
} else {
cachedSetTimeout = defaultSetTimout;
}
} catch (e) {
cachedSetTimeout = defaultSetTimout;
}
try {
if (typeof clearTimeout === 'function') {
cachedClearTimeout = clearTimeout;
} else {
cachedClearTimeout = defaultClearTimeout;
}
} catch (e) {
cachedClearTimeout = defaultClearTimeout;
}
} ())
function runTimeout(fun) {
if (cachedSetTimeout === setTimeout) {
//normal enviroments in sane situations
return setTimeout(fun, 0);
}
// if setTimeout wasn't available but was latter defined
if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
cachedSetTimeout = setTimeout;
return setTimeout(fun, 0);
}
try {
// when when somebody has screwed with setTimeout but no I.E. maddness
return cachedSetTimeout(fun, 0);
} catch(e){
try {
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
return cachedSetTimeout.call(null, fun, 0);
} catch(e){
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
return cachedSetTimeout.call(this, fun, 0);
}
}
}
function runClearTimeout(marker) {
if (cachedClearTimeout === clearTimeout) {
//normal enviroments in sane situations
return clearTimeout(marker);
}
// if clearTimeout wasn't available but was latter defined
if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
cachedClearTimeout = clearTimeout;
return clearTimeout(marker);
}
try {
// when when somebody has screwed with setTimeout but no I.E. maddness
return cachedClearTimeout(marker);
} catch (e){
try {
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
return cachedClearTimeout.call(null, marker);
} catch (e){
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
// Some versions of I.E. have different rules for clearTimeout vs setTimeout
return cachedClearTimeout.call(this, marker);
}
}
}
var queue = [];
var draining = false;
var currentQueue;
var queueIndex = -1;
function cleanUpNextTick() {
if (!draining || !currentQueue) {
return;
}
draining = false;
if (currentQueue.length) {
queue = currentQueue.concat(queue);
} else {
queueIndex = -1;
}
if (queue.length) {
drainQueue();
}
}
function drainQueue() {
if (draining) {
return;
}
var timeout = runTimeout(cleanUpNextTick);
draining = true;
var len = queue.length;
while(len) {
currentQueue = queue;
queue = [];
while (++queueIndex < len) {
if (currentQueue) {
currentQueue[queueIndex].run();
}
}
queueIndex = -1;
len = queue.length;
}
currentQueue = null;
draining = false;
runClearTimeout(timeout);
}
process.nextTick = function (fun) {
var args = new Array(arguments.length - 1);
if (arguments.length > 1) {
for (var i = 1; i < arguments.length; i++) {
args[i - 1] = arguments[i];
}
}
queue.push(new Item(fun, args));
if (queue.length === 1 && !draining) {
runTimeout(drainQueue);
}
};
// v8 likes predictible objects
function Item(fun, array) {
this.fun = fun;
this.array = array;
}
Item.prototype.run = function () {
this.fun.apply(null, this.array);
};
process.title = 'browser';
process.browser = true;
process.env = {};
process.argv = [];
process.version = ''; // empty string to avoid regexp issues
process.versions = {};
function noop() {}
process.on = noop;
process.addListener = noop;
process.once = noop;
process.off = noop;
process.removeListener = noop;
process.removeAllListeners = noop;
process.emit = noop;
process.prependListener = noop;
process.prependOnceListener = noop;
process.listeners = function (name) { return [] }
process.binding = function (name) {
throw new Error('process.binding is not supported');
};
process.cwd = function () { return '/' };
process.chdir = function (dir) {
throw new Error('process.chdir is not supported');
};
process.umask = function() { return 0; };
},{}],8:[function(_dereq_,module,exports){
'use strict';
var has = Object.prototype.hasOwnProperty
, undef;
/**
* Decode a URI encoded string.
*
* @param {String} input The URI encoded string.
* @returns {String|Null} The decoded string.
* @api private
*/
function decode(input) {
try {
return decodeURIComponent(input.replace(/\+/g, ' '));
} catch (e) {
return null;
}
}
/**
* Attempts to encode a given input.
*
* @param {String} input The string that needs to be encoded.
* @returns {String|Null} The encoded string.
* @api private
*/
function encode(input) {
try {
return encodeURIComponent(input);
} catch (e) {
return null;
}
}
/**
* Simple query string parser.
*
* @param {String} query The query string that needs to be parsed.
* @returns {Object}
* @api public
*/
function querystring(query) {
var parser = /([^=?#&]+)=?([^&]*)/g
, result = {}
, part;
while (part = parser.exec(query)) {
var key = decode(part[1])
, value = decode(part[2]);
//
// Prevent overriding of existing properties. This ensures that build-in
// methods like `toString` or __proto__ are not overriden by malicious
// querystrings.
//
// In the case if failed decoding, we want to omit the key/value pairs
// from the result.
//
if (key === null || value === null || key in result) continue;
result[key] = value;
}
return result;
}
/**
* Transform a query string to an object.
*
* @param {Object} obj Object that should be transformed.
* @param {String} prefix Optional prefix.
* @returns {String}
* @api public
*/
function querystringify(obj, prefix) {
prefix = prefix || '';
var pairs = []
, value
, key;
//
// Optionally prefix with a '?' if needed
//
if ('string' !== typeof prefix) prefix = '?';
for (key in obj) {
if (has.call(obj, key)) {
value = obj[key];
//
// Edge cases where we actually want to encode the value to an empty
// string instead of the stringified value.
//
if (!value && (value === null || value === undef || isNaN(value))) {
value = '';
}
key = encode(key);
value = encode(value);
//
// If we failed to encode the strings, we should bail out as we don't
// want to add invalid strings to the query.
//
if (key === null || value === null) continue;
pairs.push(key +'='+ value);
}
}
return pairs.length ? prefix + pairs.join('&') : '';
}
//
// Expose the module.
//
exports.stringify = querystringify;
exports.parse = querystring;
},{}],9:[function(_dereq_,module,exports){
'use strict';
var EventEmitter = _dereq_('eventemitter3')
, millisecond = _dereq_('millisecond')
, destroy = _dereq_('demolish')
, Tick = _dereq_('tick-tock')
, one = _dereq_('one-time');
/**
* Returns sane defaults about a given value.
*
* @param {String} name Name of property we want.
* @param {Recovery} selfie Recovery instance that got created.
* @param {Object} opts User supplied options we want to check.
* @returns {Number} Some default value.
* @api private
*/
function defaults(name, selfie, opts) {
return millisecond(
name in opts ? opts[name] : (name in selfie ? selfie[name] : Recovery[name])
);
}
/**
* Attempt to recover your connection with reconnection attempt.
*
* @constructor
* @param {Object} options Configuration
* @api public
*/
function Recovery(options) {
var recovery = this;
if (!(recovery instanceof Recovery)) return new Recovery(options);
options = options || {};
recovery.attempt = null; // Stores the current reconnect attempt.
recovery._fn = null; // Stores the callback.
recovery['reconnect timeout'] = defaults('reconnect timeout', recovery, options);
recovery.retries = defaults('retries', recovery, options);
recovery.factor = defaults('factor', recovery, options);
recovery.max = defaults('max', recovery, options);
recovery.min = defaults('min', recovery, options);
recovery.timers = new Tick(recovery);
}
Recovery.prototype = new EventEmitter();
Recovery.prototype.constructor = Recovery;
Recovery['reconnect timeout'] = '30 seconds'; // Maximum time to wait for an answer.
Recovery.max = Infinity; // Maximum delay.
Recovery.min = '500 ms'; // Minimum delay.
Recovery.retries = 10; // Maximum amount of retries.
Recovery.factor = 2; // Exponential back off factor.
/**
* Start a new reconnect procedure.
*
* @returns {Recovery}
* @api public
*/
Recovery.prototype.reconnect = function reconnect() {
var recovery = this;
return recovery.backoff(function backedoff(err, opts) {
opts.duration = (+new Date()) - opts.start;
if (err) return recovery.emit('reconnect failed', err, opts);
recovery.emit('reconnected', opts);
}, recovery.attempt);
};
/**
* Exponential back off algorithm for retry operations. It uses a randomized
* retry so we don't DDOS our server when it goes down under pressure.
*
* @param {Function} fn Callback to be called after the timeout.
* @param {Object} opts Options for configuring the timeout.
* @returns {Recovery}
* @api private
*/
Recovery.prototype.backoff = function backoff(fn, opts) {
var recovery = this;
opts = opts || recovery.attempt || {};
//
// Bailout when we already have a back off process running. We shouldn't call
// the callback then.
//
if (opts.backoff) return recovery;
opts['reconnect timeout'] = defaults('reconnect timeout', recovery, opts);
opts.retries = defaults('retries', recovery, opts);
opts.factor = defaults('factor', recovery, opts);
opts.max = defaults('max', recovery, opts);
opts.min = defaults('min', recovery, opts);
opts.start = +opts.start || +new Date();
opts.duration = +opts.duration || 0;
opts.attempt = +opts.attempt || 0;
//
// Bailout if we are about to make too much attempts.
//
if (opts.attempt === opts.retries) {
fn.call(recovery, new Error('Unable to recover'), opts);
return recovery;
}
//
// Prevent duplicate back off attempts using the same options object and
// increment our attempt as we're about to have another go at this thing.
//
opts.backoff = true;
opts.attempt++;
recovery.attempt = opts;
//
// Calculate the timeout, but make it randomly so we don't retry connections
// at the same interval and defeat the purpose. This exponential back off is
// based on the work of:
//
// http://dthain.blogspot.nl/2009/02/exponential-backoff-in-distributed.html
//
opts.scheduled = opts.attempt !== 1
? Math.min(Math.round(
(Math.random() + 1) * opts.min * Math.pow(opts.factor, opts.attempt - 1)
), opts.max)
: opts.min;
recovery.timers.setTimeout('reconnect', function delay() {
opts.duration = (+new Date()) - opts.start;
opts.backoff = false;
recovery.timers.clear('reconnect, timeout');
//
// Create a `one` function which can only be called once. So we can use the
// same function for different types of invocations to create a much better
// and usable API.
//
var connect = recovery._fn = one(function connect(err) {
recovery.reset();
if (err) return recovery.backoff(fn, opts);
fn.call(recovery, undefined, opts);
});
recovery.emit('reconnect', opts, connect);
recovery.timers.setTimeout('timeout', function timeout() {
var err = new Error('Failed to reconnect in a timely manner');
opts.duration = (+new Date()) - opts.start;
recovery.emit('reconnect timeout', err, opts);
connect(err);
}, opts['reconnect timeout']);
}, opts.scheduled);
//
// Emit a `reconnecting` event with current reconnect options. This allows
// them to update the UI and provide their users with feedback.
//
recovery.emit('reconnect scheduled', opts);
return recovery;
};
/**
* Check if the reconnection process is currently reconnecting.
*
* @returns {Boolean}
* @api public
*/
Recovery.prototype.reconnecting = function reconnecting() {
return !!this.attempt;
};
/**
* Tell our reconnection procedure that we're passed.
*
* @param {Error} err Reconnection failed.
* @returns {Recovery}
* @api public
*/
Recovery.prototype.reconnected = function reconnected(err) {
if (this._fn) this._fn(err);
return this;
};
/**
* Reset the reconnection attempt so it can be re-used again.
*
* @returns {Recovery}
* @api public
*/
Recovery.prototype.reset = function reset() {
this._fn = this.attempt = null;
this.timers.clear('reconnect, timeout');
return this;
};
/**
* Clean up the instance.
*
* @type {Function}
* @returns {Boolean}
* @api public
*/
Recovery.prototype.destroy = destroy('timers attempt _fn');
//
// Expose the module.
//
module.exports = Recovery;
},{"demolish":1,"eventemitter3":10,"millisecond":5,"one-time":6,"tick-tock":12}],10:[function(_dereq_,module,exports){
'use strict';
//
// We store our EE objects in a plain object whose properties are event names.
// If `Object.create(null)` is not supported we prefix the event names with a
// `~` to make sure that the built-in object properties are not overridden or
// used as an attack vector.
// We also assume that `Object.create(null)` is available when the event name
// is an ES6 Symbol.
//
var prefix = typeof Object.create !== 'function' ? '~' : false;
/**
* Representation of a single EventEmitter function.
*
* @param {Function} fn Event handler to be called.
* @param {Mixed} context Context for function execution.
* @param {Boolean} once Only emit once
* @api private
*/
function EE(fn, context, once) {
this.fn = fn;
this.context = context;
this.once = once || false;
}
/**
* Minimal EventEmitter interface that is molded against the Node.js
* EventEmitter interface.
*
* @constructor
* @api public
*/
function EventEmitter() { /* Nothing to set */ }
/**
* Holds the assigned EventEmitters by name.
*
* @type {Object}
* @private
*/
EventEmitter.prototype._events = undefined;
/**
* Return a list of assigned event listeners.
*
* @param {String} event The events that should be listed.
* @param {Boolean} exists We only need to know if there are listeners.
* @returns {Array|Boolean}
* @api public
*/
EventEmitter.prototype.listeners = function listeners(event, exists) {
var evt = prefix ? prefix + event : event
, available = this._events && this._events[evt];
if (exists) return !!available;
if (!available) return [];
if (available.fn) return [available.fn];
for (var i = 0, l = available.length, ee = new Array(l); i < l; i++) {
ee[i] = available[i].fn;
}
return ee;
};
/**
* Emit an event to all registered event listeners.
*
* @param {String} event The name of the event.
* @returns {Boolean} Indication if we've emitted an event.
* @api public
*/
EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {
var evt = prefix ? prefix + event : event;
if (!this._events || !this._events[evt]) return false;
var listeners = this._events[evt]
, len = arguments.length
, args
, i;
if ('function' === typeof listeners.fn) {
if (listeners.once) this.removeListener(event, listeners.fn, undefined, true);
switch (len) {
case 1: return listeners.fn.call(listeners.context), true;
case 2: return listeners.fn.call(listeners.context, a1), true;
case 3: return listeners.fn.call(listeners.context, a1, a2), true;
case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true;
case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;
case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;
}
for (i = 1, args = new Array(len -1); i < len; i++) {
args[i - 1] = arguments[i];
}
listeners.fn.apply(listeners.context, args);
} else {
var length = listeners.length
, j;
for (i = 0; i < length; i++) {
if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true);
switch (len) {
case 1: listeners[i].fn.call(listeners[i].context); break;
case 2: listeners[i].fn.call(listeners[i].context, a1); break;
case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break;
default:
if (!args) for (j = 1, args = new Array(len -1); j < len; j++) {
args[j - 1] = arguments[j];
}
listeners[i].fn.apply(listeners[i].context, args);
}
}
}
return true;
};
/**
* Register a new EventListener for the given event.
*
* @param {String} event Name of the event.
* @param {Functon} fn Callback function.
* @param {Mixed} context The context of the function.
* @api public
*/
EventEmitter.prototype.on = function on(event, fn, context) {
var listener = new EE(fn, context || this)
, evt = prefix ? prefix + event : event;
if (!this._events) this._events = prefix ? {} : Object.create(null);
if (!this._events[evt]) this._events[evt] = listener;
else {
if (!this._events[evt].fn) this._events[evt].push(listener);
else this._events[evt] = [
this._events[evt], listener
];
}
return this;
};
/**
* Add an EventListener that's only called once.
*
* @param {String} event Name of the event.
* @param {Function} fn Callback function.
* @param {Mixed} context The context of the function.
* @api public
*/
EventEmitter.prototype.once = function once(event, fn, context) {
var listener = new EE(fn, context || this, true)
, evt = prefix ? prefix + event : event;
if (!this._events) this._events = prefix ? {} : Object.create(null);
if (!this._events[evt]) this._events[evt] = listener;
else {
if (!this._events[evt].fn) this._events[evt].push(listener);
else this._events[evt] = [
this._events[evt], listener
];
}
return this;
};
/**
* Remove event listeners.
*
* @param {String} event The event we want to remove.
* @param {Function} fn The listener that we need to find.
* @param {Mixed} context Only remove listeners matching this context.
* @param {Boolean} once Only remove once listeners.
* @api public
*/
EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) {
var evt = prefix ? prefix + event : event;
if (!this._events || !this._events[evt]) return this;
var listeners = this._events[evt]
, events = [];
if (fn) {
if (listeners.fn) {
if (
listeners.fn !== fn
|| (once && !listeners.once)
|| (context && listeners.context !== context)
) {
events.push(listeners);
}
} else {
for (var i = 0, length = listeners.length; i < length; i++) {
if (
listeners[i].fn !== fn
|| (once && !listeners[i].once)
|| (context && listeners[i].context !== context)
) {
events.push(listeners[i]);
}
}
}
}
//
// Reset the array, or remove it completely if we have no more listeners.
//
if (events.length) {
this._events[evt] = events.length === 1 ? events[0] : events;
} else {
delete this._events[evt];
}
return this;
};
/**
* Remove all listeners or only the listeners for the specified event.
*
* @param {String} event The event want to remove all listeners for.
* @api public
*/
EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) {
if (!this._events) return this;
if (event) delete this._events[prefix ? prefix + event : event];
else this._events = prefix ? {} : Object.create(null);
return this;
};
//
// Alias methods names because people roll like that.
//
EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
EventEmitter.prototype.addListener = EventEmitter.prototype.on;
//
// This function doesn't apply anymore.
//
EventEmitter.prototype.setMaxListeners = function setMaxListeners() {
return this;
};
//
// Expose the prefix.
//
EventEmitter.prefixed = prefix;
//
// Expose the module.
//
if ('undefined' !== typeof module) {
module.exports = EventEmitter;
}
},{}],11:[function(_dereq_,module,exports){
'use strict';
/**
* Check if we're required to add a port number.
*
* @see https://url.spec.whatwg.org/#default-port
* @param {Number|String} port Port number we need to check
* @param {String} protocol Protocol we need to check against.
* @returns {Boolean} Is it a default port for the given protocol
* @api private
*/
module.exports = function required(port, protocol) {
protocol = protocol.split(':')[0];
port = +port;
if (!port) return false;
switch (protocol) {
case 'http':
case 'ws':
return port !== 80;
case 'https':
case 'wss':
return port !== 443;
case 'ftp':
return port !== 21;
case 'gopher':
return port !== 70;
case 'file':
return false;
}
return port !== 0;
};
},{}],12:[function(_dereq_,module,exports){
(function (setImmediate,clearImmediate){(function (){
'use strict';
var has = Object.prototype.hasOwnProperty
, ms = _dereq_('millisecond');
/**
* Timer instance.
*
* @constructor
* @param {Object} timer New timer instance.
* @param {Function} clear Clears the timer instance.
* @param {Function} duration Duration of the timer.
* @param {Function} fn The functions that need to be executed.
* @api private
*/
function Timer(timer, clear, duration, fn) {
this.start = +(new Date());
this.duration = duration;
this.clear = clear;
this.timer = timer;
this.fns = [fn];
}
/**
* Calculate the time left for a given timer.
*
* @returns {Number} Time in milliseconds.
* @api public
*/
Timer.prototype.remaining = function remaining() {
return this.duration - this.taken();
};
/**
* Calculate the amount of time it has taken since we've set the timer.
*
* @returns {Number}
* @api public
*/
Timer.prototype.taken = function taken() {
return +(new Date()) - this.start;
};
/**
* Custom wrappers for the various of clear{whatever} functions. We cannot
* invoke them directly as this will cause thrown errors in Google Chrome with
* an Illegal Invocation Error
*
* @see #2
* @type {Function}
* @api private
*/
function unsetTimeout(id) { clearTimeout(id); }
function unsetInterval(id) { clearInterval(id); }
function unsetImmediate(id) { clearImmediate(id); }
/**
* Simple timer management.
*
* @constructor
* @param {Mixed} context Context of the callbacks that we execute.
* @api public
*/
function Tick(context) {
if (!(this instanceof Tick)) return new Tick(context);
this.timers = {};
this.context = context || this;
}
/**
* Return a function which will just iterate over all assigned callbacks and
* optionally clear the timers from memory if needed.
*
* @param {String} name Name of the timer we need to execute.
* @param {Boolean} clear Also clear from memory.
* @returns {Function}
* @api private
*/
Tick.prototype.tock = function ticktock(name, clear) {
var tock = this;
return function tickedtock() {
if (!(name in tock.timers)) return;
var timer = tock.timers[name]
, fns = timer.fns.slice()
, l = fns.length
, i = 0;
if (clear) tock.clear(name);
else tock.start = +new Date();
for (; i < l; i++) {
fns[i].call(tock.context);
}
};
};
/**
* Add a new timeout.
*
* @param {String} name Name of the timer.
* @param {Function} fn Completion callback.
* @param {Mixed} time Duration of the timer.
* @returns {Tick}
* @api public
*/
Tick.prototype.setTimeout = function timeout(name, fn, time) {
var tick = this
, tock;
if (tick.timers[name]) {
tick.timers[name].fns.push(fn);
return tick;
}
tock = ms(time);
tick.timers[name] = new Timer(
setTimeout(tick.tock(name, true), ms(time)),
unsetTimeout,
tock,
fn
);
return tick;
};
/**
* Add a new interval.
*
* @param {String} name Name of the timer.
* @param {Function} fn Completion callback.
* @param {Mixed} time Interval of the timer.
* @returns {Tick}
* @api public
*/
Tick.prototype.setInterval = function interval(name, fn, time) {
var tick = this
, tock;
if (tick.timers[name]) {
tick.timers[name].fns.push(fn);
return tick;
}
tock = ms(time);
tick.timers[name] = new Timer(
setInterval(tick.tock(name), ms(time)),
unsetInterval,
tock,
fn
);
return tick;
};
/**
* Add a new setImmediate.
*
* @param {String} name Name of the timer.
* @param {Function} fn Completion callback.
* @returns {Tick}
* @api public
*/
Tick.prototype.setImmediate = function immediate(name, fn) {
var tick = this;
if ('function' !== typeof setImmediate) return tick.setTimeout(name, fn, 0);
if (tick.timers[name]) {
tick.timers[name].fns.push(fn);
return tick;
}
tick.timers[name] = new Timer(
setImmediate(tick.tock(name, true)),
unsetImmediate,
0,
fn
);
return tick;
};
/**
* Check if we have a timer set.
*
* @param {String} name
* @returns {Boolean}
* @api public
*/
Tick.prototype.active = function active(name) {
return name in this.timers;
};
/**
* Properly clean up all timeout references. If no arguments are supplied we
* will attempt to clear every single timer that is present.
*
* @param {Arguments} ..args.. The names of the timeouts we need to clear
* @returns {Tick}
* @api public
*/
Tick.prototype.clear = function clear() {
var args = arguments.length ? arguments : []
, tick = this
, timer, i, l;
if (args.length === 1 && 'string' === typeof args[0]) {
args = args[0].split(/[, ]+/);
}
if (!args.length) {
for (timer in tick.timers) {
if (has.call(tick.timers, timer)) args.push(timer);
}
}
for (i = 0, l = args.length; i < l; i++) {
timer = tick.timers[args[i]];
if (!timer) continue;
timer.clear(timer.timer);
timer.fns = timer.timer = timer.clear = null;
delete tick.timers[args[i]];
}
return tick;
};
/**
* Adjust a timeout or interval to a new duration.
*
* @returns {Tick}
* @api public
*/
Tick.prototype.adjust = function adjust(name, time) {
var interval
, tick = this
, tock = ms(time)
, timer = tick.timers[name];
if (!timer) return tick;
interval = timer.clear === unsetInterval;
timer.clear(timer.timer);
timer.start = +(new Date());
timer.duration = tock;
timer.timer = (interval ? setInterval : setTimeout)(tick.tock(name, !interval), tock);
return tick;
};
/**
* We will no longer use this module, prepare your self for global cleanups.
*
* @returns {Boolean}
* @api public
*/
Tick.prototype.end = Tick.prototype.destroy = function end() {
if (!this.context) return false;
this.clear();
this.context = this.timers = null;
return true;
};
//
// Expose the timer factory.
//
Tick.Timer = Timer;
module.exports = Tick;
}).call(this)}).call(this,_dereq_("timers").setImmediate,_dereq_("timers").clearImmediate)
},{"millisecond":5,"timers":13}],13:[function(_dereq_,module,exports){
(function (setImmediate,clearImmediate){(function (){
var nextTick = _dereq_('process/browser.js').nextTick;
var apply = Function.prototype.apply;
var slice = Array.prototype.slice;
var immediateIds = {};
var nextImmediateId = 0;
// DOM APIs, for completeness
exports.setTimeout = function() {
return new Timeout(apply.call(setTimeout, window, arguments), clearTimeout);
};
exports.setInterval = function() {
return new Timeout(apply.call(setInterval, window, arguments), clearInterval);
};
exports.clearTimeout =
exports.clearInterval = function(timeout) { timeout.close(); };
function Timeout(id, clearFn) {
this._id = id;
this._clearFn = clearFn;
}
Timeout.prototype.unref = Timeout.prototype.ref = function() {};
Timeout.prototype.close = function() {
this._clearFn.call(window, this._id);
};
// Does not start the time, just sets up the members needed.
exports.enroll = function(item, msecs) {
clearTimeout(item._idleTimeoutId);
item._idleTimeout = msecs;
};
exports.unenroll = function(item) {
clearTimeout(item._idleTimeoutId);
item._idleTimeout = -1;
};
exports._unrefActive = exports.active = function(item) {
clearTimeout(item._idleTimeoutId);
var msecs = item._idleTimeout;
if (msecs >= 0) {
item._idleTimeoutId = setTimeout(function onTimeout() {
if (item._onTimeout)
item._onTimeout();
}, msecs);
}
};
// That's not how node.js implements it but the exposed api is the same.
exports.setImmediate = typeof setImmediate === "function" ? setImmediate : function(fn) {
var id = nextImmediateId++;
var args = arguments.length < 2 ? false : slice.call(arguments, 1);
immediateIds[id] = true;
nextTick(function onNextTick() {
if (immediateIds[id]) {
// fn.call() is faster so we optimize for the common use-case
// @see http://jsperf.com/call-apply-segu
if (args) {
fn.apply(null, args);
} else {
fn.call(null);
}
// Prevent ids from leaking
exports.clearImmediate(id);
}
});
return id;
};
exports.clearImmediate = typeof clearImmediate === "function" ? clearImmediate : function(id) {
delete immediateIds[id];
};
}).call(this)}).call(this,_dereq_("timers").setImmediate,_dereq_("timers").clearImmediate)
},{"process/browser.js":7,"timers":13}],14:[function(_dereq_,module,exports){
(function (global){(function (){
'use strict';
var required = _dereq_('requires-port')
, qs = _dereq_('querystringify')
, controlOrWhitespace = /^[\x00-\x20\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]+/
, CRHTLF = /[\n\r\t]/g
, slashes = /^[A-Za-z][A-Za-z0-9+-.]*:\/\//
, port = /:\d+$/
, protocolre = /^([a-z][a-z0-9.+-]*:)?(\/\/)?([\\/]+)?([\S\s]*)/i
, windowsDriveLetter = /^[a-zA-Z]:/;
/**
* Remove control characters and whitespace from the beginning of a string.
*
* @param {Object|String} str String to trim.
* @returns {String} A new string representing `str` stripped of control
* characters and whitespace from its beginning.
* @public
*/
function trimLeft(str) {
return (str ? str : '').toString().replace(controlOrWhitespace, '');
}
/**
* These are the parse rules for the URL parser, it informs the parser
* about:
*
* 0. The char it Needs to parse, if it's a string it should be done using
* indexOf, RegExp using exec and NaN means set as current value.
* 1. The property we should set when parsing this value.
* 2. Indication if it's backwards or forward parsing, when set as number it's
* the value of extra chars that should be split off.
* 3. Inherit from location if non existing in the parser.
* 4. `toLowerCase` the resulting value.
*/
var rules = [
['#', 'hash'], // Extract from the back.
['?', 'query'], // Extract from the back.
function sanitize(address, url) { // Sanitize what is left of the address
return isSpecial(url.protocol) ? address.replace(/\\/g, '/') : address;
},
['/', 'pathname'], // Extract from the back.
['@', 'auth', 1], // Extract from the front.
[NaN, 'host', undefined, 1, 1], // Set left over value.
[/:(\d*)$/, 'port', undefined, 1], // RegExp the back.
[NaN, 'hostname', undefined,