UNPKG

irc-block-tracker

Version:

A block tracker for the Ir blockchain. Keeps track of the latest block.

513 lines (434 loc) 17.1 kB
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.BaseBlockTracker = f()}})(function(){var define,module,exports;return (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){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. function EventEmitter() { this._events = this._events || {}; this._maxListeners = this._maxListeners || undefined; } module.exports = EventEmitter; // Backwards-compat with node 0.10.x EventEmitter.EventEmitter = EventEmitter; EventEmitter.prototype._events = undefined; EventEmitter.prototype._maxListeners = undefined; // By default EventEmitters will print a warning if more than 10 listeners are // added to it. This is a useful default which helps finding memory leaks. EventEmitter.defaultMaxListeners = 10; // Obviously not all Emitters should be limited to 10. This function allows // that to be increased. Set to zero for unlimited. EventEmitter.prototype.setMaxListeners = function(n) { if (!isNumber(n) || n < 0 || isNaN(n)) throw TypeError('n must be a positive number'); this._maxListeners = n; return this; }; EventEmitter.prototype.emit = function(type) { var er, handler, len, args, i, listeners; if (!this._events) this._events = {}; // If there is no 'error' event listener then throw. if (type === 'error') { if (!this._events.error || (isObject(this._events.error) && !this._events.error.length)) { er = arguments[1]; if (er instanceof Error) { throw er; // Unhandled 'error' event } else { // At least give some kind of context to the user var err = new Error('Uncaught, unspecified "error" event. (' + er + ')'); err.context = er; throw err; } } } handler = this._events[type]; if (isUndefined(handler)) return false; if (isFunction(handler)) { switch (arguments.length) { // fast cases case 1: handler.call(this); break; case 2: handler.call(this, arguments[1]); break; case 3: handler.call(this, arguments[1], arguments[2]); break; // slower default: args = Array.prototype.slice.call(arguments, 1); handler.apply(this, args); } } else if (isObject(handler)) { args = Array.prototype.slice.call(arguments, 1); listeners = handler.slice(); len = listeners.length; for (i = 0; i < len; i++) listeners[i].apply(this, args); } return true; }; EventEmitter.prototype.addListener = function(type, listener) { var m; if (!isFunction(listener)) throw TypeError('listener must be a function'); if (!this._events) this._events = {}; // To avoid recursion in the case that type === "newListener"! Before // adding it to the listeners, first emit "newListener". if (this._events.newListener) this.emit('newListener', type, isFunction(listener.listener) ? listener.listener : listener); if (!this._events[type]) // Optimize the case of one listener. Don't need the extra array object. this._events[type] = listener; else if (isObject(this._events[type])) // If we've already got an array, just append. this._events[type].push(listener); else // Adding the second element, need to change to array. this._events[type] = [this._events[type], listener]; // Check for listener leak if (isObject(this._events[type]) && !this._events[type].warned) { if (!isUndefined(this._maxListeners)) { m = this._maxListeners; } else { m = EventEmitter.defaultMaxListeners; } if (m && m > 0 && this._events[type].length > m) { this._events[type].warned = true; console.error('(node) warning: possible EventEmitter memory ' + 'leak detected. %d listeners added. ' + 'Use emitter.setMaxListeners() to increase limit.', this._events[type].length); if (typeof console.trace === 'function') { // not supported in IE 10 console.trace(); } } } return this; }; EventEmitter.prototype.on = EventEmitter.prototype.addListener; EventEmitter.prototype.once = function(type, listener) { if (!isFunction(listener)) throw TypeError('listener must be a function'); var fired = false; function g() { this.removeListener(type, g); if (!fired) { fired = true; listener.apply(this, arguments); } } g.listener = listener; this.on(type, g); return this; }; // emits a 'removeListener' event iff the listener was removed EventEmitter.prototype.removeListener = function(type, listener) { var list, position, length, i; if (!isFunction(listener)) throw TypeError('listener must be a function'); if (!this._events || !this._events[type]) return this; list = this._events[type]; length = list.length; position = -1; if (list === listener || (isFunction(list.listener) && list.listener === listener)) { delete this._events[type]; if (this._events.removeListener) this.emit('removeListener', type, listener); } else if (isObject(list)) { for (i = length; i-- > 0;) { if (list[i] === listener || (list[i].listener && list[i].listener === listener)) { position = i; break; } } if (position < 0) return this; if (list.length === 1) { list.length = 0; delete this._events[type]; } else { list.splice(position, 1); } if (this._events.removeListener) this.emit('removeListener', type, listener); } return this; }; EventEmitter.prototype.removeAllListeners = function(type) { var key, listeners; if (!this._events) return this; // not listening for removeListener, no need to emit if (!this._events.removeListener) { if (arguments.length === 0) this._events = {}; else if (this._events[type]) delete this._events[type]; return this; } // emit removeListener for all listeners on all events if (arguments.length === 0) { for (key in this._events) { if (key === 'removeListener') continue; this.removeAllListeners(key); } this.removeAllListeners('removeListener'); this._events = {}; return this; } listeners = this._events[type]; if (isFunction(listeners)) { this.removeListener(type, listeners); } else if (listeners) { // LIFO order while (listeners.length) this.removeListener(type, listeners[listeners.length - 1]); } delete this._events[type]; return this; }; EventEmitter.prototype.listeners = function(type) { var ret; if (!this._events || !this._events[type]) ret = []; else if (isFunction(this._events[type])) ret = [this._events[type]]; else ret = this._events[type].slice(); return ret; }; EventEmitter.prototype.listenerCount = function(type) { if (this._events) { var evlistener = this._events[type]; if (isFunction(evlistener)) return 1; else if (evlistener) return evlistener.length; } return 0; }; EventEmitter.listenerCount = function(emitter, type) { return emitter.listenerCount(type); }; function isFunction(arg) { return typeof arg === 'function'; } function isNumber(arg) { return typeof arg === 'number'; } function isObject(arg) { return typeof arg === 'object' && arg !== null; } function isUndefined(arg) { return arg === void 0; } },{}],2:[function(require,module,exports){ 'use strict'; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var EventEmitter = require('events'); var calculateSum = function calculateSum(accumulator, currentValue) { return accumulator + currentValue; }; var blockTrackerEvents = ['sync', 'latest']; var sec = 1000; var BaseBlockTracker = function (_EventEmitter) { _inherits(BaseBlockTracker, _EventEmitter); // public function BaseBlockTracker() { var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; _classCallCheck(this, BaseBlockTracker); // config var _this = _possibleConstructorReturn(this, (BaseBlockTracker.__proto__ || Object.getPrototypeOf(BaseBlockTracker)).call(this)); _this._blockResetDuration = opts.blockResetDuration || 20 * sec; // state _this._blockResetTimeout = null; _this._currentBlock = null; _this._isRunning = false; // bind functions for internal use _this._onNewListener = _this._onNewListener.bind(_this); _this._onRemoveListener = _this._onRemoveListener.bind(_this); _this._resetCurrentBlock = _this._resetCurrentBlock.bind(_this); // listen for handler changes _this._setupInternalEvents(); return _this; } _createClass(BaseBlockTracker, [{ key: 'isRunning', value: function isRunning() { return this._isRunning; } }, { key: 'getCurrentBlock', value: function getCurrentBlock() { return this._currentBlock; } }, { key: 'getLatestBlock', value: async function getLatestBlock() { var _this2 = this; // return if available if (this._currentBlock) return this._currentBlock; // wait for a new latest block var latestBlock = await new Promise(function (resolve) { return _this2.once('latest', resolve); }); // return newly set current block return latestBlock; } // dont allow module consumer to remove our internal event listeners }, { key: 'removeAllListeners', value: function removeAllListeners(eventName) { // perform default behavior, preserve fn arity if (eventName) { _get(BaseBlockTracker.prototype.__proto__ || Object.getPrototypeOf(BaseBlockTracker.prototype), 'removeAllListeners', this).call(this, eventName); } else { _get(BaseBlockTracker.prototype.__proto__ || Object.getPrototypeOf(BaseBlockTracker.prototype), 'removeAllListeners', this).call(this); } // re-add internal events this._setupInternalEvents(); // trigger stop check just in case this._onRemoveListener(); } // // to be implemented in subclass // }, { key: '_start', value: function _start() { // default behavior is noop } }, { key: '_end', value: function _end() {} // default behavior is noop // // private // }, { key: '_setupInternalEvents', value: function _setupInternalEvents() { // first remove listeners for idempotence this.removeListener('newListener', this._onNewListener); this.removeListener('removeListener', this._onRemoveListener); // then add them this.on('newListener', this._onNewListener); this.on('removeListener', this._onRemoveListener); } }, { key: '_onNewListener', value: function _onNewListener(eventName, handler) { // `newListener` is called *before* the listener is added if (!blockTrackerEvents.includes(eventName)) return; this._maybeStart(); } }, { key: '_onRemoveListener', value: function _onRemoveListener(eventName, handler) { // `removeListener` is called *after* the listener is removed if (this._getBlockTrackerEventCount() > 0) return; this._maybeEnd(); } }, { key: '_maybeStart', value: function _maybeStart() { if (this._isRunning) return; this._isRunning = true; // cancel setting latest block to stale this._cancelBlockResetTimeout(); this._start(); } }, { key: '_maybeEnd', value: function _maybeEnd() { if (!this._isRunning) return; this._isRunning = false; this._setupBlockResetTimeout(); this._end(); } }, { key: '_getBlockTrackerEventCount', value: function _getBlockTrackerEventCount() { var _this3 = this; return blockTrackerEvents.map(function (eventName) { return _this3.listenerCount(eventName); }).reduce(calculateSum); } }, { key: '_newPotentialLatest', value: function _newPotentialLatest(newBlock) { var currentBlock = this._currentBlock; // only update if blok number is higher if (currentBlock && hexToInt(newBlock) <= hexToInt(currentBlock)) return; this._setCurrentBlock(newBlock); } }, { key: '_setCurrentBlock', value: function _setCurrentBlock(newBlock) { var oldBlock = this._currentBlock; this._currentBlock = newBlock; this.emit('latest', newBlock); this.emit('sync', { oldBlock: oldBlock, newBlock: newBlock }); } }, { key: '_setupBlockResetTimeout', value: function _setupBlockResetTimeout() { // clear any existing timeout this._cancelBlockResetTimeout(); // clear latest block when stale this._blockResetTimeout = setTimeout(this._resetCurrentBlock, this._blockResetDuration); // nodejs - dont hold process open if (this._blockResetTimeout.unref) { this._blockResetTimeout.unref(); } } }, { key: '_cancelBlockResetTimeout', value: function _cancelBlockResetTimeout() { clearTimeout(this._blockResetTimeout); } }, { key: '_resetCurrentBlock', value: function _resetCurrentBlock() { this._currentBlock = null; } }]); return BaseBlockTracker; }(EventEmitter); module.exports = BaseBlockTracker; function hexToInt(hexInt) { return Number.parseInt(hexInt, 16); } },{"events":1}]},{},[2])(2) });