UNPKG

tyo-mq

Version:

Distributed Message Pub/Sub Service with socket.io

1,777 lines (1,475 loc) 358 kB
(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