tyo-mq
Version:
Distributed Message Pub/Sub Service with socket.io
1,777 lines (1,475 loc) • 358 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 consume 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.toOnConnectEvent = function (id) {
return 'CONNECT-' + 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 || {};
this.auth = options.auth || (options.token ? { token: options.token } : null);
var mq = this;
/**
* Create the comminucation channel (e.g. socket)
*/
this.createSocket = function (callback, port, host, protocol, args) {
var mySocket = new Socket();
mySocket.auth = mq.auth;
if (this.logger)
mySocket.logger = this.logger;
if (callback) {
mySocket.connect(function (err) {
if (err)
return callback(null, err);
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);
consumer.auth = mq.auth;
if (context && context.logger)
consumer.logger = context.logger;
if (callback) {
consumer.connect(function (err) {
if (err) {
if (onErrorCallback)
onErrorCallback(err);
callback(null, err);
return;
}
onErrorCallback = onErrorCallback || function (message) {
if (mq.logger && mq.logger.error)
mq.logger.error("Error message received: " + message);
};
if (onErrorCallback) {
var oldOnError = consumer.onError;
consumer.on('ERROR', function (message) {
oldOnError.call(consumer, message);
onErrorCallback(message);
});
}
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, err) {
if (err)
return reject(err);
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 producerOptions = null;
if (eventDefault && typeof eventDefault === 'object' && !Array.isArray(eventDefault)) {
producerOptions = eventDefault;
eventDefault = null;
}
var producer = new Producer(name, eventDefault, producerOptions);
producer.auth = mq.auth;
if (context && context.logger)
producer.logger = context.logger;
if (callback)
return producer.connect(function (err) {
if (err) {
if (producer.onError)
producer.onError(err);
callback(null, err);
return;
}
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, err) {
if (err)
return reject(err);
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, options) {
if (event && typeof event === 'object' && !Array.isArray(event)) {
options = event;
event = null;
}
options = options || {};
// call parent constructor
Subscriber.call(this, name);
var producer = this;
this.eventDefault = event || Constants.EVENT_DEFAULT;
if (options.default_ttl !== undefined)
this.default_ttl = options.default_ttl;
else if (options.defaultTtl !== undefined)
this.default_ttl = options.defaultTtl;
else if (options.ttl !== undefined)
this.default_ttl = options.ttl;
else
this.default_ttl = null;
this.subscribers = {};
this.onSubscriptionListener = null;
/**
* @override
*
*/
this.sendIdentificationInfo = function () {
var payload = {name: name};
if (producer.default_ttl !== null && producer.default_ttl !== undefined)
payload.default_ttl = producer.default_ttl;
this.sendMessage.call(producer, 'PRODUCER', payload);
};
/**
* Chunk size threshold: messages serialised above this byte length are split
* into multiple PRODUCE_CHUNK frames instead of one PRODUCE frame, keeping
* each frame safely under the engine.io maxHttpBufferSize limit.
*/
Publisher.CHUNK_SIZE = Publisher.CHUNK_SIZE || 256 * 1024; // 256 KB
/**
* 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 === undefined) {
data = event;
event = this.eventDefault;
if (!event)
throw new Error('Default event name is not set.');
}
var options = {};
if (validFor && typeof validFor === 'object' && !Array.isArray(validFor))
options = validFor;
var message = {event:event, message:data, from:self.name};
if (options.guaranteed || options.guaranteed_delivery || options.guaranteedDelivery || options.delivery === 'guaranteed' || options.delivery_mode === 'guaranteed')
message.guaranteed = true;
if (options.ttl !== undefined)
message.ttl = options.ttl;
else if (self.default_ttl !== null && self.default_ttl !== undefined)
message.ttl = self.default_ttl;
else if (validFor !== undefined && !(validFor && typeof validFor === 'object'))
message.lifespan = validFor;
if (options.broadcast) {
message.method = 'broadcast';
message.broadcast = options.broadcast === 'group' ? 'group' : 'realm';
if (options.group)
message.group = options.group;
}
if (to)
message.to = to;
var str = JSON.stringify(message);
var chunkSize = Publisher.CHUNK_SIZE;
if (str.length <= chunkSize) {
self.sendMessage.call(self, 'PRODUCE', message, self.name);
return;
}
// Large message — split into chunks
var total = Math.ceil(str.length / chunkSize);
var transferId = Date.now().toString(36) + '-' + Math.random().toString(36).slice(2);
for (var i = 0; i < total; i++) {
self.sendMessage.call(self, 'PRODUCE_CHUNK', {
transferId: transferId,
index: i,
total: total,
data: str.slice(i * chunkSize, (i + 1) * chunkSize)
}, self.name);
}
};
/**
* On Subscribe
*/
this.setOnSubscriptionListener = function (callback) {
var event = events.toOnSubscribeEvent(this.getId());
this.off(event); // clear all previous listeners
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.off(event); // clear all previous listeners
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.off(event); // clear all previous listeners
this.on(event, function(data) {
producer.logger.log("Subscriber is online: name='" + (data && data.consumer) + "' id=" + (data && data.id) + " socket=" + (data && data.socket));
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":101}],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;
this.auth = null;
this.authInfo = null;
/**
* 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 (callback) {
this.on("ERROR", function (message) {
if (self.onError)
self.onError.call(self, message);
});
this.authenticate(function (err) {
if (!err)
self.sendIdentificationInfo();
if (callback)
callback(err);
});
}
this.sendIdentificationInfo = function () {};
/**
* On Disconnect
*/
this.onDisconnect = function () {
this.connected = false;
this.logger.log("Socket (" + this.getId() + ") is disconnected");
}
}
Socket.prototype.authenticate = function (callback) {
var self = this;
var auth = this.auth;
if (!auth || (!auth.token && !auth.key && !auth.role && !auth.realm)) {
callback();
return;
}
var done = false;
var timeout = setTimeout(function () {
finish(new Error("Authentication timeout"));
}, auth.timeout || 5000);
function finish (err, info) {
if (done)
return;
done = true;
clearTimeout(timeout);
self.socket.off('AUTH_OK', onAuthOk);
self.socket.off('AUTH_FAIL', onAuthFail);
if (info)
self.authInfo = info;
if (err && self.onError)
self.onError.call(self, err);
callback(err);
}
function onAuthOk (info) {
finish(null, info);
}
function onAuthFail (message) {
finish(new Error((message && message.message) || "Authentication failed"));
}
this.socket.once('AUTH_OK', onAuthOk);
this.socket.once('AUTH_FAIL', onAuthFail);
var payload = {};
if (auth.token) payload.token = auth.token;
if (auth.realm) payload.realm = auth.realm;
if (auth.role) payload.role = auth.role;
if (auth.key) payload.key = auth.key;
this.socket.emit('AUTHENTICATION', payload);
};
/**
*
*/
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 (!this.connecting) {
this.socket = this.io.connect(connectString, args || { transports: ["websocket"] });
this.connecting = true;
if (self.logger)
self.logger.log(this.name + " connecting to " + connectString + "...");
this.socket.on('connect', function(socket) {
self.connected = true;
self.connecting = false;
if (self.logger)
self.logger.log(self.name + " connected to message queue server: socket id - " + self.socket.id);
self.onConnect(function (err) {
if (err) {
if (self.callback)
self.callback(err);
return;
}
if (self.onConnectListeners && self.onConnectListeners.length > 0) {
self.onConnectListeners.forEach(function (listener) {
listener();
});
}
if (self.callback) {
self.callback();
}
});
});
// let self has a chance to register a custom onDisconnectListener
this.socket.on('disconnect', (() => {
self.connecting = false;
self.connected = false;
// Drop any in-flight inbound chunk transfers — they will never complete
Object.keys(inboundChunks).forEach(function (key) { delete inboundChunks[key]; });
if (self.onDisconnectListener && typeof self.onDisconnectListener === 'function')
self.onDisconnectListener();
}));
// Reassemble large inbound messages that the server split into CONSUME_CHUNK frames
var inboundChunks = {};
this.socket.on('CONSUME_CHUNK', function (chunk) {
var key = chunk.transferId;
if (!inboundChunks[key]) {
inboundChunks[key] = { parts: new Array(chunk.total), received: 0, event: chunk.event };
}
var transfer = inboundChunks[key];
transfer.parts[chunk.index] = chunk.data;
transfer.received++;
if (transfer.received === chunk.total) {
delete inboundChunks[key];
var assembled;
try {
assembled = JSON.parse(transfer.parts.join(''));
} catch (e) {
if (self.logger) self.logger.warn('CONSUME_CHUNK: reassembly parse failed: ' + e);
return;
}
// Dispatch directly to listeners registered for this consume event
self.socket.listeners(transfer.event).forEach(function (fn) { fn(assembled); });
}
});
}
};
/**
* 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) {
/**
* The following code could potentially cause lots of recursive calls
* if the main thread is blocked due to intensive computation
* so let the main thread to handle the reconnection
*/
// var futureFunc = this.socket.emit.bind(this.socket, event, msg);
// if (this.autoreconnect)
// this.connect(function (){
// futureFunc.call();
// });
// else
if (this.logger && this.logger.warn && this.logger.warn.apply)
this.logger.warn("Socket is created but not connected");
if (callback && typeof callback === 'function')
callback(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);
};
/**
* On Event
*/
Socket.prototype.off = function (event) {
this.socket.off(event);
};
/**
* 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":85,"socket.io-client":89}],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, options) {
options = options || {};
this.context = null;
Socket.call(this);
this.name = name || Constants.ANONYMOUS;
this.consumer_id = options.consumer_id || options.consumerId || this.name;
var subscriber = this;
/**
* @override
*
*/
this.sendIdentificationInfo = function () {
this.sendMessage.call(subscriber, 'CONSUMER', {
name: subscriber.name,
id: subscriber.consumer_id,
consumer_id: subscriber.consumer_id
});
}
/**
* 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;
var subscribeOptions = {};
if (reSubscribe === null || reSubscribe === undefined)
reSubscribe = true;
if (typeof who === 'function' && event && typeof event === 'object' && !Array.isArray(event)) {
subscribeOptions = event;
onConsumeCallback = who;
event = context;
who = Constants.ALL_PUBLISHERS;
context = self;
}
else if (typeof event === 'function') {
var optionArg = onConsumeCallback;
onConsumeCallback = event;
event = who;
who = context;
context = self;
if (optionArg && typeof optionArg === 'object')
subscribeOptions = optionArg;
else if (typeof optionArg === 'boolean')
reSubscribe = optionArg;
}
else if (!onConsumeCallback) {
onConsumeCallback = event;
event = who;
who = context;
context = self;
}
else if (reSubscribe && typeof reSubscribe === 'object') {
subscribeOptions = reSubscribe;
reSubscribe = subscribeOptions.reconnect;
}
function resubscribeListener() {
subscribeInternal();
}
function subscribeInternal() {
if ((typeof event) !== "string") {
onConsumeCallback = event;
event = null;
}
var eventStr;
var scope;
var scope_all = false;
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 sendSubscriptionMessage () {
var ackEnabled = !!(subscribeOptions.ack
|| subscribeOptions.require_ack
|| subscribeOptions.requireAck
|| subscribeOptions.manual_ack
|| subscribeOptions.manualAck);
self.sendMessage('SUBSCRIBE', {
event:eventStr,
producer: who,
consumer:self.name,
scope: scope,
durable: !!subscribeOptions.durable,
consumer_id: subscribeOptions.consumer_id || subscribeOptions.consumerId || self.consumer_id,
ack: ackEnabled,
manual_ack: !!(subscribeOptions.manual_ack || subscribeOptions.manualAck),
ack_timeout: subscribeOptions.ack_timeout || subscribeOptions.ackTimeout,
retry: subscribeOptions.retry || null,
mode: subscribeOptions.mode || null,
group: subscribeOptions.group || null
});
}
// On Connect Message will be trigger by system
sendSubscriptionMessage();
// 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;
var msgId = obj.msgId || obj.msg_id;
var manualAck = !!(subscribeOptions.manual_ack || subscribeOptions.manualAck);
var acked = false;
function ack(callback) {
if (!msgId || acked) {
if (callback)
callback();
return;
}
acked = true;
self.sendMessage('ACK', {msgId: msgId}, null, callback);
}
//if (intendedEvent === targetEventStr) {
var result = onConsumeCallback(message, from, ack, obj);
if (msgId && !manualAck) {
Promise.resolve(result).then(function () {
ack();
}).catch(function (err) {
if (self.logger && self.logger.warn)
self.logger.warn('Message handler rejected before ACK: ' + err.message);
});
}
//}
};
var consumeEventStr = events.toConsumeEvent(consumerEventStr);
// remove the old listener, we only need one listener for each event
self.off(consumeEventStr);
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, reconnect) {
this.resubscribeWhenReconnect(context, who, event, onConsumeCallback, reconnect);
};
/**
* 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.off(eventStr);
}
this.unsubscribeAll = function () {
this.socket.removeAllListeners();
}
/**
* Remove only the consume-event handlers that this subscriber registered,
* leaving the socket.io system handlers (connect / disconnect / CONSUME_CHUNK)
* intact so reconnection and chunk reassembly keep working.
*/
this.clearSubscriptions = function () {
if (!subscriber.consumes) return;
Object.keys(subscriber.consumes).forEach(function (consumerEventStr) {
var consumeEventStr = events.toConsumeEvent(consumerEventStr);
subscriber.off(consumeEventStr);
});
subscriber.consumes = {};
};
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":10,"util":101}],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 (global){(function (){
'use strict';
var possibleNames = require('possible-typed-array-names');
var g = typeof globalThis === 'undefined' ? global : globalThis;
/** @type {import('.')} */
module.exports = function availableTypedArrays() {
var /** @type {ReturnType<typeof availableTypedArrays>} */ out = [];
for (var i = 0; i < possibleNames.length; i++) {
if (typeof g[possibleNames[i]] === 'function') {
// @ts-expect-error
out[out.length] = possibleNames[i];
}
}
return out;
};
}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"possible-typed-array-names":84}],9:[function(require,module,exports){
'use strict'
exports.byteLength = byteLength
exports.toByteArray = toByteArray
exports.fromByteArray = fromByteArray
var lookup = []
var revLookup = []
var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array
var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
for (var i = 0, len = code.length; i < len; ++i) {
lookup[i] = code[i]
revLookup[code.charCodeAt(i)] = i
}
// Support decoding URL-safe base64 strings, as Node.js does.
// See: https://en.wikipedia.org/wiki/Base64#URL_applications
revLookup['-'.charCodeAt(0)] = 62
revLookup['_'.charCodeAt(0)] = 63
function getLens (b64) {
var len = b64.length
if (len % 4 > 0) {
throw new Error('Invalid string. Length must be a multiple of 4')
}
// Trim off extra bytes after placeholder bytes are found
// See: https://github.com/beatgammit/base64-js/issues/42
var validLen = b64.indexOf('=')
if (validLen === -1) validLen = len
var placeHoldersLen = validLen === len
? 0
: 4 - (validLen % 4)
return [validLen, placeHoldersLen]
}
// base64 is 4/3 + up to two characters of the original data
function byteLength (b64) {
var lens = getLens(b64)
var validLen = lens[0]
var placeHoldersLen = lens[1]
return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
}
function _byteLength (b64, validLen, placeHoldersLen) {
return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
}
function toByteArray (b64) {
var tmp
var lens = getLens(b64)
var validLen = lens[0]
var placeHoldersLen = lens[1]
var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen))
var curByte = 0
// if there are placeholders, only get up to the last complete 4 chars
var len = placeHoldersLen > 0
? validLen - 4
: validLen
var i
for (i = 0; i < len; i += 4) {
tmp =
(revLookup[b64.charCodeAt(i)] << 18) |
(revLookup[b64.charCodeAt(i + 1)] << 12) |
(revLookup[b64.charCodeAt(i + 2)] << 6) |
revLookup[b64.charCodeAt(i + 3)]
arr[curByte++] = (tmp >> 16) & 0xFF
arr[curByte++] = (tmp >> 8) & 0xFF
arr[curByte++] = tmp & 0xFF
}
if (placeHoldersLen === 2) {
tmp =
(revLookup[b64.charCodeAt(i)] << 2) |
(revLookup[b64.charCodeAt(i + 1)] >> 4)
arr[curByte++] = tmp & 0xFF
}
if (placeHoldersLen === 1) {
tmp =
(revLookup[b64.charCodeAt(i)] << 10) |
(revLookup[b64.charCodeAt(i + 1)] << 4) |
(revLookup[b64.charCodeAt(i + 2)] >> 2)
arr[curByte++] = (tmp >> 8) & 0xFF
arr[curByte++] = tmp & 0xFF
}
return arr
}
function tripletToBase64 (num) {
return lookup[num >> 18 & 0x3F] +
lookup[num >> 12 & 0x3F] +
lookup[num >> 6 & 0x3F] +
lookup[num & 0x3F]
}
function encodeChunk (uint8, start, end) {
var tmp
var output = []
for (var i = start; i < end; i += 3) {
tmp =
((uint8[i] << 16) & 0xFF0000) +
((uint8[i + 1] << 8) & 0xFF00) +
(uint8[i + 2] & 0xFF)
output.push(tripletToBase64(tmp))
}
return output.join('')
}
function fromByteArray (uint8) {
var tmp
var len = uint8.length
var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes
var parts = []
var maxChunkLength = 16383 // must be multiple of 3
// go through the array every three bytes, we'll deal with trailing stuff later
for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)))
}
// pad the end with zeros, but make sure to not forget the extra bytes
if (extraBytes === 1) {
tmp = uint8[len - 1]
parts.push(
lookup[tmp >> 2] +
lookup[(tmp << 4) & 0x3F] +
'=='
)
} else if (extraBytes === 2) {
tmp = (uint8[len - 2] << 8) + uint8[len - 1]
parts.push(
lookup[tmp >> 10] +
lookup[(tmp >> 4) & 0x3F] +
lookup[(tmp << 2) & 0x3F] +
'='
)
}
return parts.join('')
}
},{}],10:[function(require,module,exports){
(function (Buffer){(function (){
/*!
* The buffer module from node.js, for the browser.
*
* @author Feross Aboukhadijeh <https://feross.org>
* @license MIT
*/
/* eslint-disable no-proto */
'use strict'
var base64 = require('base64-js')
var ieee754 = require('ieee754')
exports.Buffer = Buffer
exports.SlowBuffer = SlowBuffer
exports.INSPECT_MAX_BYTES = 50
var K_MAX_LENGTH = 0x7fffffff
exports.kMaxLength = K_MAX_LENGTH
/**
* If `Buffer.TYPED_ARRAY_SUPPORT`:
* === true Use Uint8Array implementation (fastest)
* === false Print warning and recommend using `buffer` v4.x which has an Object
* implementation (most compatible, even IE6)
*
* Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,
* Opera 11.6+, iOS 4.2+.
*
* We report that the browser does not support typed arrays if the are not subclassable
* using __proto__. Firefox 4-29 lacks support for adding new properties to `Uint8Array`
* (See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438). IE 10 lacks support
* for __proto__ and has a buggy typed array implementation.
*/
Buffer.TYPED_ARRAY_SUPPORT = typedArraySupport()
if (!Buffer.TYPED_ARRAY_SUPPORT && typeof console !== 'undefined' &&
typeof console.error === 'function') {
console.error(
'This browser lacks typed array (Uint8Array) support which is required by ' +
'`buffer` v5.x. Use `buffer` v4.x if you require old browser support.'
)
}
function typedArraySupport () {
// Can typed array instances can be augmented?
try {
var arr = new Uint8Array(1)
arr.__proto__ = { __proto__: Uint8Array.prototype, foo: function () { return 42 } }
return arr.foo() === 42
} catch (e) {
return false
}
}
Object.defineProperty(Buffer.prototype, 'parent', {
enumerable: true,
get: function () {
if (!Buffer.isBuffer(this)) return undefined
return this.buffer
}
})
Object.defineProperty(Buffer.prototype, 'offset', {
enumerable: true,
get: function () {
if (!Buffer.isBuffer(this)) return undefined
return this.byteOffset
}
})
function createBuffer (length) {
if (length > K_MAX_LENGTH) {
throw new RangeError('The value "' + length + '" is invalid for option "size"')
}
// Return an augmented `Uint8Array` instance
var buf = new Uint8Array(length)
buf.__proto__ = Buffer.prototype
return buf
}
/**
* The Buffer constructor returns instances of `Uint8Array` that have their
* prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of
* `Uint8Array`, so the returned instances will have all the node `Buffer` methods
* and the `Uint8Array` methods. Square bracket notation works as expected -- it
* returns a single octet.
*
* The `Uint8Array` prototype remains unmodified.
*/
function Buffer (arg, encodingOrOffset, length) {
// Common case.
if (typeof arg === 'number') {
if (typeof encodingOrOffset === 'string') {
throw new TypeError(
'The "string" argument must be of type string. Received type number'
)
}
return allocUnsafe(arg)
}
return from(arg, encodingOrOffset, length)
}
// Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97
if (typeof Symbol !== 'undefined' && Symbol.species != null &&
Buffer[Symbol.species] === Buffer) {
Object.defineProperty(Buffer, Symbol.species, {
value: null,
configurable: true,
enumerable: false,
writable: false
})
}
Buffer.poolSize = 8192 // not used by this implementation
function from (value, encodingOrOffset, length) {
if (typeof value === 'string') {
return fromString(value, encodingOrOffset)
}
if (ArrayBuffer.isView(value)) {
return fromArrayLike(value)
}
if (value == null) {
throw TypeError(
'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' +
'or Array-like Object. Received type ' + (typeof value)
)
}
if (isInstance(value, ArrayBuffer) ||
(value && isInstance(value.buffer, ArrayBuffer))) {
return fromArrayBuffer(value, encodingOrOffset, length)
}
if (typeof value === 'number') {
throw new TypeError(
'The "value" argument must not be of type number. Received type number'
)
}
var valueOf = value.valueOf && value.valueOf()
if (valueOf != null && valueOf !== value) {
return Buffer.from(valueOf, encodingOrOffset, length)
}
var b = fromObject(value)
if (b) return b
if (typeof Symbol !== 'undefined' && Symbol.toPrimitive != null &&
typeof value[Symbol.toPrimitive] === 'function') {
return Buffer.from(
value[Symbol.toPrimitive]('string'), encodingOrOffset, length
)
}
throw new TypeError(
'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' +
'or Array-like Object. Received type ' + (typeof value)
)
}
/**
* Functionally equivalent to Buffer(arg, encoding) but throws a TypeError
* if value is a number.
* Buffer.from(str[, encoding])
* Buffer.from(array)
* Buffer.from(buffer)
* Buffer.from(arrayBuffer[, byteOffset[, length]])
**/
Buffer.from = function (value, encodingOrOffset, length) {
return from(value, encodingOrOffset, length)
}
// Note: Change prototype *after* Buffer.from is defined to workaround Chrome bug:
// https://github.com/feross/buffer/pull/148
Buffer.prototype.__proto__ = Uint8Array.prototype
Buffer.__proto__ = Uint8Array
function assertSize (size) {
if (typeof size !== 'number') {
throw new TypeError('"size" argument must be of type number')
} else if (size < 0) {
throw new RangeError('The value "' + size + '" is invalid for option "size"')
}
}
function alloc (size, fill, encoding) {
assertSize(size)
if (size <= 0) {
return createBuffer(size)
}
if (fill !== undefined) {
// Only pay attention to encoding if it's a string. This
// prevents accidentally sending in a number that would
// be interpretted as a start offset.
return typeof encoding === 'string'
? createBuffer(size).fill(fill, encoding)
: createBuffer(size).fill(fill)
}
return createBuffer