tyo-mq
Version:
Distributed Message Pub/Sub Service with socket.io
1,920 lines (1,633 loc) • 295 kB
JavaScript
(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(require,module,exports){
function Constants() {
this.ANONYMOUS = 'ANONYMOUS';
this.EVENT_DEFAULT = 'tyo-mq-mt-default';
this.EVENT_ALL = 'TM-ALL';
this.SYSTEM = 'TYO-MQ-SYSTEM';
this.ALL_PUBLISHERS = 'TYO-MQ-ALL';
this.SCOPE_ALL = "all";
this.SCOPE_DEFAULT = "default";
this.DEFAULT_PORT = 17352;
}
var constants = constants || new Constants();
module.exports = constants;
},{}],2:[function(require,module,exports){
/**
* @file events.js
*/
const constants = require( "./constants");
function Events () {
}
/**
* Normailise event string
*/
Events.prototype.toEventString = function (event, prefix, suffix) {
var eventStr;
if (typeof event === 'string') {
eventStr = event;
}
else if (typeof event === 'object' && event.event) {
eventStr = event.event;
}
else
throw new Error ('Unknown event object: should be a string or object with event string');
return (prefix ? (prefix + '-') : '') + eventStr + (suffix ? ('-' + suffix) : '');
};
/**
* This is different to the cosume event
* this is the event name that the consumer socker will listen to
*/
Events.prototype.toConsumerEvent = function (event, producer, scope_all) {
if (scope_all)
this.toEventString(event, producer.toLowerCase());
return this.toEventString(event, producer).toLowerCase();
}
/**
* To Consume Event
*/
Events.prototype.toConsumeEvent = function (event) {
/**
* COSUMER EVENT = "CONSUME" + Lower(event)
* System event or defined Event are all capitalized
*/
//var capEvent = event.toUpperCase();
return this.toEventString(event, 'CONSUME');
};
/**
*
*/
Events.prototype.toOnDisconnectEvent = function (id) {
return 'DISCONNECT-' + id;
};
/**
*
*/
Events.prototype.toOnUnsubscribeEvent =function (event, id) {
// var eventStr = this.toEventString(event);
// return 'UNSUBSCRIBE-' + eventStr + '-' + id;
return this.toEventString(event, 'UNSUBSCRIBE', id);
};
/**
*
*/
Events.prototype.toOnSubscribeEvent = function (id) {
return 'SUBSCRIBE-TO' + ((id) ? "-" + id : "");
};
Events.prototype.toConsumeEventAll = function (producer) {
// return 'CONSUME-' + producer + "-ALL";
return this.toEventString('CONSUME', this.toConsumerEventAll(producer));
};
Events.prototype.toConsumerEventAll = function (producer) {
// return 'producer + "-ALL";
return this.toEventString(producer.toLowerCase(), null, constants.EVENT_ALL);
};
var events = events || new Events();
module.exports = events;
},{"./constants":1}],3:[function(require,module,exports){
/**
* @file message-queue.js
*
* It may not be a good idea to name it message queue here, but for now leave it as it is
*/
var Socket = require('./socket'),
Subscriber = require('./subscriber'),
Producer = require('./publisher');
function Factory (options) {
options = options || {};
/**
* for log
*/
this.logger = options.logger || console;
this.port = options.port || null;
this.host = options.host || null;
this.protocol = options.protocol || null;
this.args = options.args || {};
var mq = this;
/**
* Create the comminucation channel (e.g. socket)
*/
this.createSocket = function (callback, port, host, protocol, args) {
var mySocket = new Socket();
if (this.logger)
mySocket.logger = this.logger;
if (callback) {
mySocket.connect(function () {
callback(mySocket);
},
port || mq.port,
host || mq.host,
protocol || mq.protocol,
args || mq.args
);
}
return mySocket;
};
/**
* private function
*/
this.createConsumerPrivate = function (context, name, callback, port, host, protocol, args, onErrorCallback) {
var consumer = new Subscriber(name);
if (context && context.logger)
consumer.logger = context.logger;
if (callback) {
consumer.connect(function () {
onErrorCallback = onErrorCallback || function (message) {
if (mq.logger)
mq.logger.error("Error message received: " + message);
};
if (onErrorCallback) {
var oldOnError = consumer.onError;
consumer.on('ERROR', function () {
oldOnError.call(consumer);
onErrorCallback();
});
}
callback(consumer);
},
port,
host,
protocol,
args
);
}
return consumer;
};
/**
* Create a consumer
*/
this.createConsumer = function (context, name, callback, port, host, protocol, args, onErrorCallback) {
var self = this;
if (context && typeof context === 'string') {
onErrorCallback = args;
args = protocol;
protocol = host;
host = port;
port = callback;
callback = name;
name = context;
context = this;
}
if (!callback) {
return new Promise(function (resolve, reject) {
try {
mq.createConsumerPrivate.call(
self,
context,
name,
function (consumer) {
resolve(consumer);
},
port || mq.port,
host || mq.host,
protocol || mq.protocol,
args || mq.args,
onErrorCallback);
}
catch (err) {
reject(err);
}
});
}
else
mq.createConsumerPrivate(context,
name,
callback,
port || mq.port,
host || mq.host,
protocol || mq.protocol,
args || mq.args,
onErrorCallback);
};
/**
* Alias of createConsumer
*/
this.createSubscriber = this.createConsumer;
/**
* private function
*/
this.createProducerPrivate = function (context, name, eventDefault, callback, port, host, protocol, args) {
if (!callback && typeof eventDefault === 'function') {
args = protocol;
protocol = host;
host = port;
port = callback;
callback = eventDefault;
eventDefault = null;
}
var producer = new Producer(name, eventDefault);
if (context && context.logger)
producer.logger = context.logger;
if (callback)
return producer.connect(function () {
callback(producer);
},
port,
host,
protocol,
args
);
return producer;
};
/**
* Create a producer
*/
this.createProducer = function (name, eventDefault, callback, port, host, protocol, args) {
var self = this;
if (eventDefault && typeof eventDefault === 'function') {
args = protocol;
protocol = host;
host = port;
port = callback;
callback = eventDefault;
eventDefault = null;
}
if (!callback) {
return new Promise(function (resolve, reject) {
try {
mq.createProducerPrivate.call(
self,
self,
name,
eventDefault,
function (producer) {
resolve(producer);
},
port || mq.port,
host || mq.host,
protocol || mq.protocol,
args || mq.args);
}
catch (err) {
reject(err);
}
});
}
else
mq.createProducerPrivate(self,
name,
eventDefault,
callback,
port || mq.port,
host || mq.host,
protocol || mq.protocol,
args || mq.args
);
};
}
Factory.Producer = Producer;
Factory.Consumer = Subscriber;
Factory.Publisher = Producer;
Factory.Subscriber = Subscriber;
module.exports = Factory;
},{"./publisher":4,"./socket":5,"./subscriber":6}],4:[function(require,module,exports){
/**
* @file producer.js
*/
const util = require('util'),
events = require('./events'),
Constants = require('./constants');
var Subscriber = require('./subscriber');
function SubscriberInfo (name) {
this.name = name;
}
function Publisher (name, event) {
// call parent constructor
Subscriber.call(this, name);
var producer = this;
this.eventDefault = event || Constants.EVENT_DEFAULT;
this.subscribers = {};
this.onSubscriptionListener = null;
/**
* @override
*
*/
this.sendIdentificationInfo = function () {
this.sendMessage.call(producer, 'PRODUCER', {name: name});
};
/**
* Event produce function
* @param event
* @param data
* @param validFor, the valid time (lifespan of the message)
*/
this.produce = function (event, data, validFor, to) {
var self = this;
if (!data) {
data = event;
event = this.eventDefault;
if (!event)
throw new Error('Default event name is not set.');
}
/**
* @todo
* 1) make an encryption option
* 2) Encrypt the message when a cryto algorithm is negotiated
*/
var message = {event:event, message:data, from:self.name, lifespan: validFor || -1};
// Maybe we could delay a bit
self.sendMessage.call(self, 'PRODUCE', message, self.name);
};
/**
* On Subscribe
*/
this.setOnSubscriptionListener = function (callback) {
var event = events.toOnSubscribeEvent(this.getId());
this.on(event, function (data) {
producer.logger.log("Received subscription information: " + JSON.stringify(data));
producer.subscribers[data.id] = data;
// further listener
if (producer.onSubscriptionListener)
producer.onSubscriptionListener.call(producer, data);
if (callback)
callback(data);
});
};
this.onSubscribed = this.setOnSubscriptionListener;
/**
* On Lost connections with subscriber(s)
*/
this.setOnSubscriberLostListener = function (callback) {
var event = events.toOnDisconnectEvent(this.getId());
this.on(event, function(data) {
producer.logger.log("Lost subscriber's connection");
if (callback)
callback(data);
});
};
this.onSubscriberLost = this.setOnSubscriberLostListener;
/**
* On Unsubsribe
*/
this.setOnUnsubscribedListener = function (callback) {
var event = events.toOnUnsubscribeEvent(this.getId());
this.on(event, function() {
if (callback)
callback();
});
};
this.onUnsubscribed = this.setOnUnsubscribedListener;
// Initialisation
this.addConnectionListener(function () {
var producerName = producer.name || Constants.ANONYMOUS;
// producer.sendMessage.call(producer, 'PRODUCER', {name: producerName});
producer.setOnSubscriptionListener();
});
this.setOnSubscriberLostListener = function (callback) {
var event = events.toOnDisconnectEvent(this.getId());
this.on(event, function(data) {
producer.logger.log("Lost subscriber's connection");
if (callback)
callback(data);
});
};
this.setOnSubscriberLost = this.setOnSubscriberLostListener;
this.setOnSubscriberOnlineListener = function (callback) {
var event = events.toOnConnectEvent(this.getId());
this.on(event, function(data) {
producer.logger.log("Subscriber is online");
if (callback)
callback(data);
});
}
this.whenSubscriberOnline = this.setOnSubscriberOnline = this.setOnSubscriberOnlineListener;
}
/**
* Inherits from Socket
*/
util.inherits(Publisher, Subscriber);
module.exports = Publisher;
},{"./constants":1,"./events":2,"./subscriber":6,"util":62}],5:[function(require,module,exports){
(function (process){(function (){
/**
* @file socket.js
*
* A Socket.io connection
*
*/
const Constants = require('./constants');
/**
* Socket Class
*/
function Socket() {
/**
* Autoconnect
*/
this.autoreconnect = true;
/**
* SocketIO instance
*/
this.io = require('socket.io-client');
/**
* Socket Instance from socket.io
*/
this.socket = null;
this.connected = false;
/**
* Socket Id
*/
this.id = function () {
return this.socket.id;
};
this.logger = this.logger || /* (process.env.NODE_ENV === 'production') ? null : */console;
this.onConnectListeners = null;
/**
* Add on connect listener
*/
this.addConnectionListener = function (listener) {
this.onConnectListeners = this.onConnectListeners || [];
this.onConnectListeners.push(listener);
};
var self = this;
this.onDisconnectListener = function(socket) {
self.connected = false;
if (self.logger)
self.logger.log("connection lost");
};
// Only available from the server side
// this.disable = function (what) {
// this.io.disable(what);
// }
/**
* The name of the socket such as a name of an App
*/
this.name = Constants.ANONYMOUS;
/**
* Alias
*/
this.alias = null;
/**
this.serial_id = -1;
this.sendIdentificationInfo = function () {
// do nothing yet
}
*/
/**
* Check if it is connected
*/
this.isConnected = function () {
return this.connected;
}
/**
* On Error
*/
this.onError = function (message) {
if (this.logger && this.logger.error && this.logger.error.apply)
this.logger.error(message);
}
/**
* On Connect
*/
this.onConnect = function () {
this.sendIdentificationInfo();
this.on("ERROR", function (message) {
if (self.onError)
self.onError.call(self, message);
});
}
/**
* On Disconnect
*/
this.onDisconnect = function () {
this.connected = false;
this.logger.log("Socket (" + this.getId() + ") is disconnected");
}
}
/**
*
*/
Socket.prototype.generateConnectionUrl = function() {
var host_url = this.protocol + "://" + this.host + ":" + this.port + "/";
return host_url;
};
/**
* Disconnect
*/
Socket.prototype.disconnect = function (callback) {
if (this.socket && this.socket.connected) {
this.socket.disconnect();
}
};
/**
* Flush
*/
Socket.prototype.flush = function (callback) {
this.socket.flush();
setTimeout(function() {
callback();
}, 10);
};
/**
* Connect to the Socket.io server
*/
Socket.prototype.connect = function (callback, port, host, protocol, args) {
var self = this;
if (this.socket && this.socket.connected) {
if (callback) {
return callback();
}
return;
}
this.host = this.host || host || process.env.TYO_MQ_HOST || 'localhost';
this.port = this.port || port || process.env.TYO_MQ_PORT || '17352';
this.protocol = this.protocol || protocol || 'http';
/**
*/
this.connectString = this.connectString || (this.protocol + "://" + this.host + ':' + this.port);
this.connectWith(callback, this.connectString, args);
};
/**
* Connect to ther server with connection string
*/
Socket.prototype.connectWith = function (callback, connectString, args) {
var self = this;
this.callback = callback;
if (self.logger)
self.logger.log(this.name + " connecting to " + connectString + "...");
this.socket = this.io.connect(connectString, args || { transports: ["websocket"] });
this.socket.on('connect', function(socket) {
self.connected = true;
if (self.logger)
self.logger.log(self.name + " connected to message queue server");
self.onConnect();
if (self.onConnectListeners && self.onConnectListeners.length > 0) {
self.onConnectListeners.forEach(function (listener) {
listener();
});
}
if (self.callback) {
self.callback();
// callback = null;
}
});
// let self has a chance to register a custom onDisconnectListener
this.socket.on('disconnect', (() => {
if (self.onDisconnectListener && typeof self.onDisconnectListener === 'function')
self.onDisconnectListener();
}));
};
/**
* Send Event Message
*/
Socket.prototype.sendMessage = function (event, msg, from, callback) {
if (!this.socket)
throw new Error("Socket isn't initialized yet");
if (!this.socket.connected) {
var futureFunc = this.socket.emit.bind(this.socket, event, msg);
if (this.autoreconnect)
this.connect(function (){
futureFunc.call();
});
else
throw new Error("Socket is created but not connected");
return;
}
this.socket.emit(event, msg);
if (callback && typeof callback === 'function') {
callback();
}
};
/**
* On Event
*/
Socket.prototype.on = function (event, callback) {
if (event === 'connect') {
this.addConnectionListener(callback);
return;
}
this.socket.on(event, callback);
};
/**
* Get Socket Id
*/
Socket.prototype.getSocketId = function () {
return this.socket ? this.socket.id : null;
};
Socket.prototype.getId = Socket.prototype.getSocketId;
module.exports = Socket;
}).call(this)}).call(this,require('_process'))
},{"./constants":1,"_process":59,"socket.io-client":32}],6:[function(require,module,exports){
/**
* @file subscriber.js
*/
const util = require('util'),
events = require('./events'),
Constants = require('./constants');
var Socket = require('./socket');
const { constants } = require('buffer');
/**
*
*/
function Subscriber (name) {
this.context = null;
Socket.call(this);
this.name = name || Constants.ANONYMOUS;
var subscriber = this;
/**
* @override
*
*/
this.sendIdentificationInfo = function () {
this.sendMessage.call(subscriber, 'CONSUMER', {name: name});
}
/**
* Resend the subscription message after a connection is lost (particularily when server is gone) and reconnected
*/
this.resubscribeWhenReconnect = function (context, who, event, onConsumeCallback, reSubscribe) {
var self = this;
if (reSubscribe === null)
reSubscribe = true;
if (!onConsumeCallback) {
onConsumeCallback = event;
event = who;
who = context;
context = self;
}
function resubscribeListener() {
subscribeInternal();
}
function subscribeInternal() {
if ((typeof event) !== "string") {
onConsumeCallback = event;
event = null;
}
var eventStr;
var scope;
var scope_all = true;
if (event)
eventStr = events.toEventString(event);
else {
eventStr = constants.EVENT_ALL; // who + "-ALL";
scope = Constants.SCOPE_ALL;
scope_all = true;
}
/**
* @todo
*
* deal with the ALL events later
*/
function sendSubscritionMessage () {
self.sendMessage('SUBSCRIBE', {event:eventStr, producer: who, consumer:self.name, scope: scope});
}
// On Connect Message will be trigger by system
sendSubscritionMessage();
// the connection should be ready before we subscribe the message
// this.on('connect', function () {
// sendSubscritionMessage();
// });
if (!self.consumes)
self.consumes = {};
var consumerEventStr = events.toConsumerEvent(eventStr, who, scope_all);
var targetEventStr = events.toEventString(event, who).toLowerCase();
self.consumes[consumerEventStr] = function (obj) {
//var intendedEvent = obj.event;
// if the message is encrypted, then it needs to be decrypted first
var message = obj.message;
var from = obj.from || message.from;
//if (intendedEvent === targetEventStr) {
onConsumeCallback(message, from);
//}
};
var consumeEventStr = events.toConsumeEvent(consumerEventStr);
self.on(consumeEventStr, function (obj) {
if (context)
self.consumes[consumerEventStr].call(context, obj);
else
self.consumes[consumerEventStr](obj);
});
}
subscribeInternal();
if (reSubscribe)
self.addConnectionListener(resubscribeListener);
}
/**
* Subscribe message
*
* If an event name is not provided, then we subscribe all the messages from the producer
*/
this.subscribe = function (context, who, event, onConsumeCallback, reconcect) {
this.resubscribeWhenReconnect(context, who, event, onConsumeCallback, reconcect);
};
/**
* Subscribe only once, if the connection is gone, let it be
*/
this.subscribeOnce = function (context, who, event, onConsumeCallback) {
this.subscribe(context, who, event, onConsumeCallback, false);
};
/**
* Subscribe all events with this name whatever providers are publishing
*/
this.subscribeAll = function (context, event, onConsumeCallback) {
this.subscribe(context, Constants.ALL_PUBLISHERS, event, onConsumeCallback);
}
this.unsubscribe = function (event, who) {
var eventStr = events.toConsumerEvent(event, who);
// this.sendMessage('UNSUBSCRIBE', {event:eventStr});
this.socket.off(eventStr);
}
this.unsubscribeAll = function () {
this.socket.removeAllListeners();
}
this.setOnProducerOnlineListener = function (producer, callback) {
var eventStr = events.toEventString(producer, null, "ONLINE");
this.on(eventStr, callback);
}
this.whenProducerOnline = this.setOnProducerOnline = this.setOnProducerOnlineListener;
}
/**
* Inherits from Socket
*/
util.inherits(Subscriber, Socket);
module.exports = Subscriber;
},{"./constants":1,"./events":2,"./socket":5,"buffer":44,"util":62}],7:[function(require,module,exports){
/**
* Expose `Emitter`.
*/
exports.Emitter = Emitter;
/**
* Initialize a new `Emitter`.
*
* @api public
*/
function Emitter(obj) {
if (obj) return mixin(obj);
}
/**
* Mixin the emitter properties.
*
* @param {Object} obj
* @return {Object}
* @api private
*/
function mixin(obj) {
for (var key in Emitter.prototype) {
obj[key] = Emitter.prototype[key];
}
return obj;
}
/**
* Listen on the given `event` with `fn`.
*
* @param {String} event
* @param {Function} fn
* @return {Emitter}
* @api public
*/
Emitter.prototype.on =
Emitter.prototype.addEventListener = function(event, fn){
this._callbacks = this._callbacks || {};
(this._callbacks['$' + event] = this._callbacks['$' + event] || [])
.push(fn);
return this;
};
/**
* Adds an `event` listener that will be invoked a single
* time then automatically removed.
*
* @param {String} event
* @param {Function} fn
* @return {Emitter}
* @api public
*/
Emitter.prototype.once = function(event, fn){
function on() {
this.off(event, on);
fn.apply(this, arguments);
}
on.fn = fn;
this.on(event, on);
return this;
};
/**
* Remove the given callback for `event` or all
* registered callbacks.
*
* @param {String} event
* @param {Function} fn
* @return {Emitter}
* @api public
*/
Emitter.prototype.off =
Emitter.prototype.removeListener =
Emitter.prototype.removeAllListeners =
Emitter.prototype.removeEventListener = function(event, fn){
this._callbacks = this._callbacks || {};
// all
if (0 == arguments.length) {
this._callbacks = {};
return this;
}
// specific event
var callbacks = this._callbacks['$' + event];
if (!callbacks) return this;
// remove all handlers
if (1 == arguments.length) {
delete this._callbacks['$' + event];
return this;
}
// remove specific handler
var cb;
for (var i = 0; i < callbacks.length; i++) {
cb = callbacks[i];
if (cb === fn || cb.fn === fn) {
callbacks.splice(i, 1);
break;
}
}
// Remove event specific arrays for event types that no
// one is subscribed for to avoid memory leak.
if (callbacks.length === 0) {
delete this._callbacks['$' + event];
}
return this;
};
/**
* Emit `event` with the given args.
*
* @param {String} event
* @param {Mixed} ...
* @return {Emitter}
*/
Emitter.prototype.emit = function(event){
this._callbacks = this._callbacks || {};
var args = new Array(arguments.length - 1)
, callbacks = this._callbacks['$' + event];
for (var i = 1; i < arguments.length; i++) {
args[i - 1] = arguments[i];
}
if (callbacks) {
callbacks = callbacks.slice(0);
for (var i = 0, len = callbacks.length; i < len; ++i) {
callbacks[i].apply(this, args);
}
}
return this;
};
// alias used for reserved events (protected method)
Emitter.prototype.emitReserved = Emitter.prototype.emit;
/**
* Return array of callbacks for `event`.
*
* @param {String} event
* @return {Array}
* @api public
*/
Emitter.prototype.listeners = function(event){
this._callbacks = this._callbacks || {};
return this._callbacks['$' + event] || [];
};
/**
* Check if this emitter has `event` handlers.
*
* @param {String} event
* @return {Boolean}
* @api public
*/
Emitter.prototype.hasListeners = function(event){
return !! this.listeners(event).length;
};
},{}],8:[function(require,module,exports){
(function (process){(function (){
/* eslint-env browser */
/**
* This is the web browser implementation of `debug()`.
*/
exports.formatArgs = formatArgs;
exports.save = save;
exports.load = load;
exports.useColors = useColors;
exports.storage = localstorage();
exports.destroy = (() => {
let warned = false;
return () => {
if (!warned) {
warned = true;
console.warn('Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.');
}
};
})();
/**
* Colors.
*/
exports.colors = [
'#0000CC',
'#0000FF',
'#0033CC',
'#0033FF',
'#0066CC',
'#0066FF',
'#0099CC',
'#0099FF',
'#00CC00',
'#00CC33',
'#00CC66',
'#00CC99',
'#00CCCC',
'#00CCFF',
'#3300CC',
'#3300FF',
'#3333CC',
'#3333FF',
'#3366CC',
'#3366FF',
'#3399CC',
'#3399FF',
'#33CC00',
'#33CC33',
'#33CC66',
'#33CC99',
'#33CCCC',
'#33CCFF',
'#6600CC',
'#6600FF',
'#6633CC',
'#6633FF',
'#66CC00',
'#66CC33',
'#9900CC',
'#9900FF',
'#9933CC',
'#9933FF',
'#99CC00',
'#99CC33',
'#CC0000',
'#CC0033',
'#CC0066',
'#CC0099',
'#CC00CC',
'#CC00FF',
'#CC3300',
'#CC3333',
'#CC3366',
'#CC3399',
'#CC33CC',
'#CC33FF',
'#CC6600',
'#CC6633',
'#CC9900',
'#CC9933',
'#CCCC00',
'#CCCC33',
'#FF0000',
'#FF0033',
'#FF0066',
'#FF0099',
'#FF00CC',
'#FF00FF',
'#FF3300',
'#FF3333',
'#FF3366',
'#FF3399',
'#FF33CC',
'#FF33FF',
'#FF6600',
'#FF6633',
'#FF9900',
'#FF9933',
'#FFCC00',
'#FFCC33'
];
/**
* Currently only WebKit-based Web Inspectors, Firefox >= v31,
* and the Firebug extension (any Firefox version) are known
* to support "%c" CSS customizations.
*
* TODO: add a `localStorage` variable to explicitly enable/disable colors
*/
// eslint-disable-next-line complexity
function useColors() {
// NB: In an Electron preload script, document will be defined but not fully
// initialized. Since we know we're in Chrome, we'll just detect this case
// explicitly
if (typeof window !== 'undefined' && window.process && (window.process.type === 'renderer' || window.process.__nwjs)) {
return true;
}
// Internet Explorer and Edge do not support colors.
if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) {
return false;
}
// Is webkit? http://stackoverflow.com/a/16459606/376773
// document is undefined in react-native: https://github.com/facebook/react-native/pull/1632
return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) ||
// Is firebug? http://stackoverflow.com/a/398120/376773
(typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) ||
// Is firefox >= v31?
// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
(typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) ||
// Double check webkit in userAgent just in case we are in a worker
(typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/));
}
/**
* Colorize log arguments if enabled.
*
* @api public
*/
function formatArgs(args) {
args[0] = (this.useColors ? '%c' : '') +
this.namespace +
(this.useColors ? ' %c' : ' ') +
args[0] +
(this.useColors ? '%c ' : ' ') +
'+' + module.exports.humanize(this.diff);
if (!this.useColors) {
return;
}
const c = 'color: ' + this.color;
args.splice(1, 0, c, 'color: inherit');
// The final "%c" is somewhat tricky, because there could be other
// arguments passed either before or after the %c, so we need to
// figure out the correct index to insert the CSS into
let index = 0;
let lastC = 0;
args[0].replace(/%[a-zA-Z%]/g, match => {
if (match === '%%') {
return;
}
index++;
if (match === '%c') {
// We only are interested in the *last* %c
// (the user may have provided their own)
lastC = index;
}
});
args.splice(lastC, 0, c);
}
/**
* Invokes `console.debug()` when available.
* No-op when `console.debug` is not a "function".
* If `console.debug` is not available, falls back
* to `console.log`.
*
* @api public
*/
exports.log = console.debug || console.log || (() => {});
/**
* Save `namespaces`.
*
* @param {String} namespaces
* @api private
*/
function save(namespaces) {
try {
if (namespaces) {
exports.storage.setItem('debug', namespaces);
} else {
exports.storage.removeItem('debug');
}
} catch (error) {
// Swallow
// XXX (@Qix-) should we be logging these?
}
}
/**
* Load `namespaces`.
*
* @return {String} returns the previously persisted debug modes
* @api private
*/
function load() {
let r;
try {
r = exports.storage.getItem('debug');
} catch (error) {
// Swallow
// XXX (@Qix-) should we be logging these?
}
// If debug isn't set in LS, and we're in Electron, try to load $DEBUG
if (!r && typeof process !== 'undefined' && 'env' in process) {
r = process.env.DEBUG;
}
return r;
}
/**
* Localstorage attempts to return the localstorage.
*
* This is necessary because safari throws
* when a user disables cookies/localstorage
* and you attempt to access it.
*
* @return {LocalStorage}
* @api private
*/
function localstorage() {
try {
// TVMLKit (Apple TV JS Runtime) does not have a window object, just localStorage in the global context
// The Browser also has localStorage in the global context.
return localStorage;
} catch (error) {
// Swallow
// XXX (@Qix-) should we be logging these?
}
}
module.exports = require('./common')(exports);
const {formatters} = module.exports;
/**
* Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
*/
formatters.j = function (v) {
try {
return JSON.stringify(v);
} catch (error) {
return '[UnexpectedJSONParseError]: ' + error.message;
}
};
}).call(this)}).call(this,require('_process'))
},{"./common":9,"_process":59}],9:[function(require,module,exports){
/**
* This is the common logic for both the Node.js and web browser
* implementations of `debug()`.
*/
function setup(env) {
createDebug.debug = createDebug;
createDebug.default = createDebug;
createDebug.coerce = coerce;
createDebug.disable = disable;
createDebug.enable = enable;
createDebug.enabled = enabled;
createDebug.humanize = require('ms');
createDebug.destroy = destroy;
Object.keys(env).forEach(key => {
createDebug[key] = env[key];
});
/**
* The currently active debug mode names, and names to skip.
*/
createDebug.names = [];
createDebug.skips = [];
/**
* Map of special "%n" handling functions, for the debug "format" argument.
*
* Valid key names are a single, lower or upper-case letter, i.e. "n" and "N".
*/
createDebug.formatters = {};
/**
* Selects a color for a debug namespace
* @param {String} namespace The namespace string for the debug instance to be colored
* @return {Number|String} An ANSI color code for the given namespace
* @api private
*/
function selectColor(namespace) {
let hash = 0;
for (let i = 0; i < namespace.length; i++) {
hash = ((hash << 5) - hash) + namespace.charCodeAt(i);
hash |= 0; // Convert to 32bit integer
}
return createDebug.colors[Math.abs(hash) % createDebug.colors.length];
}
createDebug.selectColor = selectColor;
/**
* Create a debugger with the given `namespace`.
*
* @param {String} namespace
* @return {Function}
* @api public
*/
function createDebug(namespace) {
let prevTime;
let enableOverride = null;
let namespacesCache;
let enabledCache;
function debug(...args) {
// Disabled?
if (!debug.enabled) {
return;
}
const self = debug;
// Set `diff` timestamp
const curr = Number(new Date());
const ms = curr - (prevTime || curr);
self.diff = ms;
self.prev = prevTime;
self.curr = curr;
prevTime = curr;
args[0] = createDebug.coerce(args[0]);
if (typeof args[0] !== 'string') {
// Anything else let's inspect with %O
args.unshift('%O');
}
// Apply any `formatters` transformations
let index = 0;
args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => {
// If we encounter an escaped % then don't increase the array index
if (match === '%%') {
return '%';
}
index++;
const formatter = createDebug.formatters[format];
if (typeof formatter === 'function') {
const val = args[index];
match = formatter.call(self, val);
// Now we need to remove `args[index]` since it's inlined in the `format`
args.splice(index, 1);
index--;
}
return match;
});
// Apply env-specific formatting (colors, etc.)
createDebug.formatArgs.call(self, args);
const logFn = self.log || createDebug.log;
logFn.apply(self, args);
}
debug.namespace = namespace;
debug.useColors = createDebug.useColors();
debug.color = createDebug.selectColor(namespace);
debug.extend = extend;
debug.destroy = createDebug.destroy; // XXX Temporary. Will be removed in the next major release.
Object.defineProperty(debug, 'enabled', {
enumerable: true,
configurable: false,
get: () => {
if (enableOverride !== null) {
return enableOverride;
}
if (namespacesCache !== createDebug.namespaces) {
namespacesCache = createDebug.namespaces;
enabledCache = createDebug.enabled(namespace);
}
return enabledCache;
},
set: v => {
enableOverride = v;
}
});
// Env-specific initialization logic for debug instances
if (typeof createDebug.init === 'function') {
createDebug.init(debug);
}
return debug;
}
function extend(namespace, delimiter) {
const newDebug = createDebug(this.namespace + (typeof delimiter === 'undefined' ? ':' : delimiter) + namespace);
newDebug.log = this.log;
return newDebug;
}
/**
* Enables a debug mode by namespaces. This can include modes
* separated by a colon and wildcards.
*
* @param {String} namespaces
* @api public
*/
function enable(namespaces) {
createDebug.save(namespaces);
createDebug.namespaces = namespaces;
createDebug.names = [];
createDebug.skips = [];
let i;
const split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/);
const len = split.length;
for (i = 0; i < len; i++) {
if (!split[i]) {
// ignore empty strings
continue;
}
namespaces = split[i].replace(/\*/g, '.*?');
if (namespaces[0] === '-') {
createDebug.skips.push(new RegExp('^' + namespaces.slice(1) + '$'));
} else {
createDebug.names.push(new RegExp('^' + namespaces + '$'));
}
}
}
/**
* Disable debug output.
*
* @return {String} namespaces
* @api public
*/
function disable() {
const namespaces = [
...createDebug.names.map(toNamespace),
...createDebug.skips.map(toNamespace).map(namespace => '-' + namespace)
].join(',');
createDebug.enable('');
return namespaces;
}
/**
* Returns true if the given mode name is enabled, false otherwise.
*
* @param {String} name
* @return {Boolean}
* @api public
*/
function enabled(name) {
if (name[name.length - 1] === '*') {
return true;
}
let i;
let len;
for (i = 0, len = createDebug.skips.length; i < len; i++) {
if (createDebug.skips[i].test(name)) {
return false;
}
}
for (i = 0, len = createDebug.names.length; i < len; i++) {
if (createDebug.names[i].test(name)) {
return true;
}
}
return false;
}
/**
* Convert regexp to namespace
*
* @param {RegExp} regxep
* @return {String} namespace
* @api private
*/
function toNamespace(regexp) {
return regexp.toString()
.substring(2, regexp.toString().length - 2)
.replace(/\.\*\?$/, '*');
}
/**
* Coerce `val`.
*
* @param {Mixed} val
* @return {Mixed}
* @api private
*/
function coerce(val) {
if (val instanceof Error) {
return val.stack || val.message;
}
return val;
}
/**
* XXX DO NOT USE. This is a temporary stub function.
* XXX It WILL be removed in the next major release.
*/
function destroy() {
console.warn('Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.');
}
createDebug.enable(createDebug.load());
return createDebug;
}
module.exports = setup;
},{"ms":30}],10:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.hasCORS = void 0;
// imported from https://github.com/component/has-cors
let value = false;
try {
value = typeof XMLHttpRequest !== 'undefined' &&
'withCredentials' in new XMLHttpRequest();
}
catch (err) {
// if XMLHttp support is disabled in IE then it will throw
// when trying to create
}
exports.hasCORS = value;
},{}],11:[function(require,module,exports){
"use strict";
// imported from https://github.com/galkn/querystring
/**
* Compiles a querystring
* Returns string representation of the object
*
* @param {Object}
* @api private
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.decode = exports.encode = void 0;
function encode(obj) {
let str = '';
for (let i in obj) {
if (obj.hasOwnProperty(i)) {
if (str.length)
str += '&';
str += encodeURIComponent(i) + '=' + encodeURIComponent(obj[i]);
}
}
return str;
}
exports.encode = encode;
/**
* Parses a simple querystring into an object
*
* @param {String} qs
* @api private
*/
function decode(qs) {
let qry = {};
let pairs = qs.split('&');
for (let i = 0, l = pairs.length; i < l; i++) {
let pair = pairs[i].split('=');
qry[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
}
return qry;
}
exports.decode = decode;
},{}],12:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parse = void 0;
// imported from https://github.com/galkn/parseuri
/**
* Parses a URI
*
* Note: we could also have used the built-in URL object, but it isn't supported on all platforms.
*
* See:
* - https://developer.mozilla.org/en-US/docs/Web/API/URL
* - https://caniuse.com/url
* - https://www.rfc-editor.org/rfc/rfc3986#appendix-B
*
* History of the parse() method:
* - first commit: https://github.com/socketio/socket.io-client/commit/4ee1d5d94b3906a9c052b459f1a818b15f38f91c
* - export into its own module: https://github.com/socketio/engine.io-client/commit/de2c561e4564efeb78f1bdb1ba39ef81b2822cb3
* - reimport: https://github.com/socketio/engine.io-client/commit/df32277c3f6d622eec5ed09f493cae3f3391d242
*
* @author Steven Levithan <stevenlevithan.com> (MIT license)
* @api private
*/
const re = /^(?:(?![^:@\/?#]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@\/?#]*)(?::([^:@\/?#]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/;
const parts = [
'source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 'anchor'
];
function parse(str) {
const src = str, b = str.indexOf('['), e = str.indexOf(']');
if (b != -1 && e != -1) {
str = str.substring(0, b) + str.substring(b, e).replace(/:/g, ';') + str.substring(e, str.length);
}
let m = re.exec(str || ''), uri = {}, i = 14;
while (i--) {
uri[parts[i]] = m[i] || '';
}
if (b != -1 && e != -1) {
uri.source = src;
uri.host = uri.host.substring(1, uri.host.length - 1).replace(/;/g, ':');
uri.authority = uri.authority.replace('[', '').replace(']', '').replace(/;/g, ':');
uri.ipv6uri = true;
}
uri.pathNames = pathNames(uri, uri['path']);
uri.queryKey = queryKey(uri, uri['query']);
return uri;
}
exports.parse = parse;
function pathNames(obj, path) {
const regx = /\/{2,9}/g, names = path.replace(regx, "/").split("/");
if (path.slice(0, 1) == '/' || path.length === 0) {
names.splice(0, 1);
}
if (path.slice(-1) == '/') {
names.splice(names.length - 1, 1);
}
return names;
}
function queryKey(uri, query) {
const data = {};
query.replace(/(?:^|&)([^&=]*)=?([^&]*)/g, function ($0, $1, $2) {
if ($1) {
data[$1] = $2;
}
});
return data;
}
},{}],13:[function(require,module,exports){
// imported from https://github.com/unshiftio/yeast
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.yeast = exports.decode = exports.encode = void 0;
const alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_'.split(''), length = 64, map = {};
let seed = 0, i = 0, prev;
/**
* Return a string representing the specified number.
*
* @param {Number} num The number to convert.
* @returns {String} The string representation of the number.
* @api public
*/
function encode(num) {
let encoded = '';
do {
encoded = alphabet[num % length] + encoded;
num = Math.floor(num / length);
} while (num > 0);
return encoded;
}
exports.encode = encode;
/**
* Return the integer value specified by the given string.
*
* @param {String} str The string to convert.
* @returns {Number} The integer value represented by the string.
* @api public
*/
function decode(str) {
let decoded = 0;
for (i = 0; i < str.length; i++) {
decoded = decoded * length + map[str.charAt(i)];
}
return decoded;
}
exports.decode = decode;
/**
* Yeast: A tiny growing id generator.
*
* @returns {String} A unique id.
* @api public
*/
function yeast() {
const now = encode(+new Date());
if (now !== prev)
return seed = 0, prev = now;
return now + '.' + encode(seed++);
}
exports.yeast = yeast;
//
// Map each character to its index.
//
for (; i < length; i++)
map[alphabet[i]] = i;
},{}],14:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.globalThisShim = void 0;
exports.globalThisShim = (() => {
if (typeof self !== "undefined") {
return self;
}
else if (typeof window !== "undefined") {
return window;
}
else {
return Function("return this")();
}
})();
},{}],15:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.nextTick = exports.parse = exports.installTimerFunctions = exports.transports = exports.Transport = exports.protocol = exports.Socket = void 0;
const socket_js_1 = require("./socket.js");
Object.defineProperty(exports, "Socket", { enumerable: true, get: function () { return socket_js_1.Socket; } });
exports.protocol = socket_js_1.Socket.protocol;
var transport_js_1 = require("./transport.js");
Object.defineProperty(exports, "Transport", { enumerable: true, get: function () { return transport_js_1.Transport; } });
var index_js_1 = require("./transports/index.js");
Object.defineProperty(exports, "transports", { enumerable: true, get: function () { return index_js_1.transports; } });
var util_js_1 = require("./util.js");
Object.defineProperty(exports, "installTimerFunctions", { enumerable: true, get: function () { return util_js_1.installTimerFunctions; } });
var parseuri_js_1 = require("./contrib/parseuri.js");
Object.defineProperty(exports, "parse", { enumerable: true, get: function () { return parseuri_js_1.parse; } });
var websocket_constructor_js_1 = require("./transports/websocket-constructor.js");
Object.defineProperty(exports, "nextTick", { enumerable: true, get: function () { return websocket_constructor_js_1.nextTick; } });
},{"./contrib/parseuri.js":12,"./socket.js":16,"./transport.js":17,"./transports/index.js":18,"./transports/websocket-constructor.js":20,"./util.js":24}],16:[function(require,module,exports){
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Socket = void 0;
const index_js_1 = require("./transports/index.js");
const util_js_1 = require("./util.js");
const parseqs_js_1 = require("./contrib/parseqs.js");
const parseuri_js_1 = require("./contrib/parseuri.js");
const debug_1 = __importDefault(require("debug")); // debug()
const component_emitter_1 = require("@socket.io/component-emitter");
const engine_io_parser_1 = require("engine.io-parser");
const websocket_constructor_js_1 = require("./transports/websocket-constructor.js");
const debug = (0, debug_1.default)("engine.io-client:socket"); // debug()
class Socket extends component_emitter_1.Emitter {
/**
* Socket constructor.
*
* @param {String|Object} uri - uri or options
* @param {Object} opts - options
*/
constructor(uri, opts = {}) {
super();
this.binaryType = websocket_constructor_js_1.defaultBinaryType;
this.writeBuffer = [];
if (uri && "object" === typeof uri) {
opts = uri;
uri = null;
}
if (uri) {
uri = (0, parseuri_js_1.parse)(uri);
opts.hostname = uri.host;
opts.secure = uri.protocol === "https" || uri.protocol === "wss";
opts.port = uri.port;
if (uri.query)
opts.query = uri.query;
}
else if (opts.host) {
opts.hostname = (0, parseuri_js_1.parse)(opts.host).host;
}
(0, util_js_1.installTimerFunctions)(this, opts);
this.secure =
null != opts.secure
? opts.secure
: typeof location !== "undefined" && "https:" === location.protocol;
if (opts.hostname && !opts.port) {
// if no port is specified manually, use the protocol default
opts.port = this.secure ? "443" : "80";
}
this.hostname =
opts.hostname ||
(typeof location !== "undefined" ? location.hostname : "localhost");
this.port =
opts.port ||
(typeof location !== "undefined" && location.port
? location.port
: this.secure