From 512344c5c6da7d54821e888e14e84266ea3ceb12 Mon Sep 17 00:00:00 2001 From: wanghao Date: Mon, 24 May 2021 12:43:14 -0700 Subject: [PATCH 1/3] x --- .gitignore | 6 ++++-- src/PollingBlockTracker.ts | 10 +++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 81c74e38..1022dd21 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ # Created by https://www.gitignore.io/api/osx,node # Build directory -/dist/ +#/dist/ ### Node ### # Logs @@ -89,4 +89,6 @@ Network Trash Folder Temporary Items .apdisk -# End of https://www.gitignore.io/api/osx,node \ No newline at end of file +# End of https://www.gitignore.io/api/osx,node + +.idea diff --git a/src/PollingBlockTracker.ts b/src/PollingBlockTracker.ts index 8aaf5815..4cba378f 100644 --- a/src/PollingBlockTracker.ts +++ b/src/PollingBlockTracker.ts @@ -30,6 +30,8 @@ export class PollingBlockTracker extends BaseBlockTracker { private _setSkipCacheFlag: boolean; + private _errorCount = 0; + constructor(opts: Partial = {}) { // parse + validate args if (!opts.provider) { @@ -63,10 +65,16 @@ export class PollingBlockTracker extends BaseBlockTracker { try { await this._updateLatestBlock(); await timeout(this._pollingInterval, !this._keepEventLoopActive); + this._errorCount = 0; } catch (err) { + const newErr = new Error(`PollingBlockTracker - encountered an error while attempting to update latest block:\n${err.stack}`); try { - this.emit('error', newErr); + // only emit error when the error count is > 3 + this._errorCount++; + if (this._errorCount > 3) { + this.emit('error', newErr); + } } catch (emitErr) { console.error(newErr); } From 8897a1a9f612d0658cb1d90f4fa05480da6d0b24 Mon Sep 17 00:00:00 2001 From: wanghao Date: Mon, 24 May 2021 13:55:27 -0700 Subject: [PATCH 2/3] [ignore-some-errors] new file: dist/BaseBlockTracker.d.ts --- dist/BaseBlockTracker.d.ts | 39 ++++++++ dist/BaseBlockTracker.js | 148 ++++++++++++++++++++++++++++++ dist/BaseBlockTracker.js.map | 1 + dist/PollingBlockTracker.d.ts | 23 +++++ dist/PollingBlockTracker.js | 91 ++++++++++++++++++ dist/PollingBlockTracker.js.map | 1 + dist/SubscribeBlockTracker.d.ts | 16 ++++ dist/SubscribeBlockTracker.js | 70 ++++++++++++++ dist/SubscribeBlockTracker.js.map | 1 + dist/index.d.ts | 3 + dist/index.js | 9 ++ dist/index.js.map | 1 + 12 files changed, 403 insertions(+) create mode 100644 dist/BaseBlockTracker.d.ts create mode 100644 dist/BaseBlockTracker.js create mode 100644 dist/BaseBlockTracker.js.map create mode 100644 dist/PollingBlockTracker.d.ts create mode 100644 dist/PollingBlockTracker.js create mode 100644 dist/PollingBlockTracker.js.map create mode 100644 dist/SubscribeBlockTracker.d.ts create mode 100644 dist/SubscribeBlockTracker.js create mode 100644 dist/SubscribeBlockTracker.js.map create mode 100644 dist/index.d.ts create mode 100644 dist/index.js create mode 100644 dist/index.js.map diff --git a/dist/BaseBlockTracker.d.ts b/dist/BaseBlockTracker.d.ts new file mode 100644 index 00000000..2d90c761 --- /dev/null +++ b/dist/BaseBlockTracker.d.ts @@ -0,0 +1,39 @@ +import SafeEventEmitter from '@metamask/safe-event-emitter'; +import { JsonRpcRequest, JsonRpcResponse } from 'json-rpc-engine'; +export interface Provider extends SafeEventEmitter { + sendAsync: (req: JsonRpcRequest, cb: (err: Error, response: JsonRpcResponse) => void) => void; +} +interface BaseBlockTrackerArgs { + blockResetDuration?: number; +} +export declare class BaseBlockTracker extends SafeEventEmitter { + protected _isRunning: boolean; + private _blockResetDuration; + private _currentBlock; + private _blockResetTimeout?; + constructor(opts?: BaseBlockTrackerArgs); + isRunning(): boolean; + getCurrentBlock(): string | null; + getLatestBlock(): Promise; + removeAllListeners(eventName: string | symbol): this; + /** + * To be implemented in subclass. + */ + protected _start(): void; + /** + * To be implemented in subclass. + */ + protected _end(): void; + private _setupInternalEvents; + private _onNewListener; + private _onRemoveListener; + private _maybeStart; + private _maybeEnd; + private _getBlockTrackerEventCount; + protected _newPotentialLatest(newBlock: string): void; + private _setCurrentBlock; + private _setupBlockResetTimeout; + private _cancelBlockResetTimeout; + private _resetCurrentBlock; +} +export {}; diff --git a/dist/BaseBlockTracker.js b/dist/BaseBlockTracker.js new file mode 100644 index 00000000..9a8fca9c --- /dev/null +++ b/dist/BaseBlockTracker.js @@ -0,0 +1,148 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const safe_event_emitter_1 = __importDefault(require("@metamask/safe-event-emitter")); +const sec = 1000; +const calculateSum = (accumulator, currentValue) => accumulator + currentValue; +const blockTrackerEvents = ['sync', 'latest']; +class BaseBlockTracker extends safe_event_emitter_1.default { + constructor(opts = {}) { + super(); + // config + this._blockResetDuration = opts.blockResetDuration || 20 * sec; + // state + 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(); + } + isRunning() { + return this._isRunning; + } + getCurrentBlock() { + return this._currentBlock; + } + async getLatestBlock() { + // return if available + if (this._currentBlock) { + return this._currentBlock; + } + // wait for a new latest block + const latestBlock = await new Promise((resolve) => this.once('latest', resolve)); + // return newly set current block + return latestBlock; + } + // dont allow module consumer to remove our internal event listeners + removeAllListeners(eventName) { + // perform default behavior, preserve fn arity + if (eventName) { + super.removeAllListeners(eventName); + } + else { + super.removeAllListeners(); + } + // re-add internal events + this._setupInternalEvents(); + // trigger stop check just in case + this._onRemoveListener(); + return this; + } + /** + * To be implemented in subclass. + */ + _start() { + // default behavior is noop + } + /** + * To be implemented in subclass. + */ + _end() { + // default behavior is noop + } + _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); + } + _onNewListener(eventName) { + // `newListener` is called *before* the listener is added + if (blockTrackerEvents.includes(eventName)) { + this._maybeStart(); + } + } + _onRemoveListener() { + // `removeListener` is called *after* the listener is removed + if (this._getBlockTrackerEventCount() > 0) { + return; + } + this._maybeEnd(); + } + _maybeStart() { + if (this._isRunning) { + return; + } + this._isRunning = true; + // cancel setting latest block to stale + this._cancelBlockResetTimeout(); + this._start(); + } + _maybeEnd() { + if (!this._isRunning) { + return; + } + this._isRunning = false; + this._setupBlockResetTimeout(); + this._end(); + } + _getBlockTrackerEventCount() { + return blockTrackerEvents + .map((eventName) => this.listenerCount(eventName)) + .reduce(calculateSum); + } + _newPotentialLatest(newBlock) { + const currentBlock = this._currentBlock; + // only update if blok number is higher + if (currentBlock && (hexToInt(newBlock) <= hexToInt(currentBlock))) { + return; + } + this._setCurrentBlock(newBlock); + } + _setCurrentBlock(newBlock) { + const oldBlock = this._currentBlock; + this._currentBlock = newBlock; + this.emit('latest', newBlock); + this.emit('sync', { oldBlock, newBlock }); + } + _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(); + } + } + _cancelBlockResetTimeout() { + if (this._blockResetTimeout) { + clearTimeout(this._blockResetTimeout); + } + } + _resetCurrentBlock() { + this._currentBlock = null; + } +} +exports.BaseBlockTracker = BaseBlockTracker; +function hexToInt(hexInt) { + return Number.parseInt(hexInt, 16); +} +//# sourceMappingURL=BaseBlockTracker.js.map \ No newline at end of file diff --git a/dist/BaseBlockTracker.js.map b/dist/BaseBlockTracker.js.map new file mode 100644 index 00000000..c49ea30b --- /dev/null +++ b/dist/BaseBlockTracker.js.map @@ -0,0 +1 @@ +{"version":3,"file":"BaseBlockTracker.js","sourceRoot":"","sources":["../src/BaseBlockTracker.ts"],"names":[],"mappings":"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","sourcesContent":["import SafeEventEmitter from '@metamask/safe-event-emitter';\nimport { JsonRpcRequest, JsonRpcResponse } from 'json-rpc-engine';\n\nconst sec = 1000;\n\nconst calculateSum = (accumulator: number, currentValue: number) => accumulator + currentValue;\nconst blockTrackerEvents: (string | symbol)[] = ['sync', 'latest'];\n\nexport interface Provider extends SafeEventEmitter {\n sendAsync: (req: JsonRpcRequest, cb: (err: Error, response: JsonRpcResponse) => void) => void;\n}\n\ninterface BaseBlockTrackerArgs {\n blockResetDuration?: number;\n}\n\nexport class BaseBlockTracker extends SafeEventEmitter {\n\n protected _isRunning: boolean;\n\n private _blockResetDuration: number;\n\n private _currentBlock: string | null;\n\n private _blockResetTimeout?: ReturnType;\n\n constructor(opts: BaseBlockTrackerArgs = {}) {\n super();\n\n // config\n this._blockResetDuration = opts.blockResetDuration || 20 * sec;\n // state\n this._currentBlock = null;\n this._isRunning = false;\n\n // bind functions for internal use\n this._onNewListener = this._onNewListener.bind(this);\n this._onRemoveListener = this._onRemoveListener.bind(this);\n this._resetCurrentBlock = this._resetCurrentBlock.bind(this);\n\n // listen for handler changes\n this._setupInternalEvents();\n }\n\n isRunning(): boolean {\n return this._isRunning;\n }\n\n getCurrentBlock(): string | null {\n return this._currentBlock;\n }\n\n async getLatestBlock(): Promise {\n // return if available\n if (this._currentBlock) {\n return this._currentBlock;\n }\n // wait for a new latest block\n const latestBlock: string = await new Promise((resolve) => this.once('latest', resolve));\n // return newly set current block\n return latestBlock;\n }\n\n // dont allow module consumer to remove our internal event listeners\n removeAllListeners(eventName: string | symbol) {\n // perform default behavior, preserve fn arity\n if (eventName) {\n super.removeAllListeners(eventName);\n } else {\n super.removeAllListeners();\n }\n\n // re-add internal events\n this._setupInternalEvents();\n // trigger stop check just in case\n this._onRemoveListener();\n\n return this;\n }\n\n /**\n * To be implemented in subclass.\n */\n protected _start(): void {\n // default behavior is noop\n }\n\n /**\n * To be implemented in subclass.\n */\n protected _end(): void {\n // default behavior is noop\n }\n\n private _setupInternalEvents(): void {\n // first remove listeners for idempotence\n this.removeListener('newListener', this._onNewListener);\n this.removeListener('removeListener', this._onRemoveListener);\n // then add them\n this.on('newListener', this._onNewListener);\n this.on('removeListener', this._onRemoveListener);\n }\n\n private _onNewListener(eventName: string | symbol): void {\n // `newListener` is called *before* the listener is added\n if (blockTrackerEvents.includes(eventName)) {\n this._maybeStart();\n }\n }\n\n private _onRemoveListener(): void {\n // `removeListener` is called *after* the listener is removed\n if (this._getBlockTrackerEventCount() > 0) {\n return;\n }\n this._maybeEnd();\n }\n\n private _maybeStart(): void {\n if (this._isRunning) {\n return;\n }\n this._isRunning = true;\n // cancel setting latest block to stale\n this._cancelBlockResetTimeout();\n this._start();\n }\n\n private _maybeEnd(): void {\n if (!this._isRunning) {\n return;\n }\n this._isRunning = false;\n this._setupBlockResetTimeout();\n this._end();\n }\n\n private _getBlockTrackerEventCount(): number {\n return blockTrackerEvents\n .map((eventName) => this.listenerCount(eventName))\n .reduce(calculateSum);\n }\n\n protected _newPotentialLatest(newBlock: string): void {\n const currentBlock = this._currentBlock;\n // only update if blok number is higher\n if (currentBlock && (hexToInt(newBlock) <= hexToInt(currentBlock))) {\n return;\n }\n this._setCurrentBlock(newBlock);\n }\n\n private _setCurrentBlock(newBlock: string): void {\n const oldBlock = this._currentBlock;\n this._currentBlock = newBlock;\n this.emit('latest', newBlock);\n this.emit('sync', { oldBlock, newBlock });\n }\n\n private _setupBlockResetTimeout(): void {\n // clear any existing timeout\n this._cancelBlockResetTimeout();\n // clear latest block when stale\n this._blockResetTimeout = setTimeout(this._resetCurrentBlock, this._blockResetDuration);\n\n // nodejs - dont hold process open\n if (this._blockResetTimeout.unref) {\n this._blockResetTimeout.unref();\n }\n }\n\n private _cancelBlockResetTimeout(): void {\n if (this._blockResetTimeout) {\n clearTimeout(this._blockResetTimeout);\n }\n }\n\n private _resetCurrentBlock(): void {\n this._currentBlock = null;\n }\n\n}\n\nfunction hexToInt(hexInt: string): number {\n return Number.parseInt(hexInt, 16);\n}\n"]} \ No newline at end of file diff --git a/dist/PollingBlockTracker.d.ts b/dist/PollingBlockTracker.d.ts new file mode 100644 index 00000000..f700f9bc --- /dev/null +++ b/dist/PollingBlockTracker.d.ts @@ -0,0 +1,23 @@ +import { BaseBlockTracker, Provider } from './BaseBlockTracker'; +interface PollingBlockTrackerArgs { + provider: Provider; + pollingInterval: number; + retryTimeout: number; + keepEventLoopActive: boolean; + setSkipCacheFlag: boolean; +} +export declare class PollingBlockTracker extends BaseBlockTracker { + private _provider; + private _pollingInterval; + private _retryTimeout; + private _keepEventLoopActive; + private _setSkipCacheFlag; + private _errorCount; + constructor(opts?: Partial); + checkForLatestBlock(): Promise; + protected _start(): void; + private _synchronize; + private _updateLatestBlock; + private _fetchLatestBlock; +} +export {}; diff --git a/dist/PollingBlockTracker.js b/dist/PollingBlockTracker.js new file mode 100644 index 00000000..94237a63 --- /dev/null +++ b/dist/PollingBlockTracker.js @@ -0,0 +1,91 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const json_rpc_random_id_1 = __importDefault(require("json-rpc-random-id")); +const pify_1 = __importDefault(require("pify")); +const BaseBlockTracker_1 = require("./BaseBlockTracker"); +const createRandomId = json_rpc_random_id_1.default(); +const sec = 1000; +class PollingBlockTracker extends BaseBlockTracker_1.BaseBlockTracker { + constructor(opts = {}) { + this._errorCount = 0; + // parse + validate args + if (!opts.provider) { + throw new Error('PollingBlockTracker - no provider specified.'); + } + super({ + blockResetDuration: opts.pollingInterval, + }); + // config + this._provider = opts.provider; + this._pollingInterval = opts.pollingInterval || 20 * sec; + this._retryTimeout = opts.retryTimeout || this._pollingInterval / 10; + this._keepEventLoopActive = opts.keepEventLoopActive === undefined ? true : opts.keepEventLoopActive; + this._setSkipCacheFlag = opts.setSkipCacheFlag || false; + } + // trigger block polling + async checkForLatestBlock() { + await this._updateLatestBlock(); + return await this.getLatestBlock(); + } + _start() { + this._synchronize().catch((err) => this.emit('error', err)); + } + async _synchronize() { + while (this._isRunning) { + try { + await this._updateLatestBlock(); + await timeout(this._pollingInterval, !this._keepEventLoopActive); + this._errorCount = 0; + } + catch (err) { + const newErr = new Error(`PollingBlockTracker - encountered an error while attempting to update latest block:\n${err.stack}`); + try { + // only emit error when the error count is > 3 + this._errorCount++; + if (this._errorCount > 3) { + this.emit('error', newErr); + } + } + catch (emitErr) { + console.error(newErr); + } + await timeout(this._retryTimeout, !this._keepEventLoopActive); + } + } + } + async _updateLatestBlock() { + // fetch + set latest block + const latestBlock = await this._fetchLatestBlock(); + this._newPotentialLatest(latestBlock); + } + async _fetchLatestBlock() { + const req = { + jsonrpc: '2.0', + id: createRandomId(), + method: 'eth_blockNumber', + params: [], + }; + if (this._setSkipCacheFlag) { + req.skipCache = true; + } + const res = await pify_1.default((cb) => this._provider.sendAsync(req, cb))(); + if (res.error) { + throw new Error(`PollingBlockTracker - encountered error fetching block:\n${res.error}`); + } + return res.result; + } +} +exports.PollingBlockTracker = PollingBlockTracker; +function timeout(duration, unref) { + return new Promise((resolve) => { + const timeoutRef = setTimeout(resolve, duration); + // don't keep process open + if (timeoutRef.unref && unref) { + timeoutRef.unref(); + } + }); +} +//# sourceMappingURL=PollingBlockTracker.js.map \ No newline at end of file diff --git a/dist/PollingBlockTracker.js.map b/dist/PollingBlockTracker.js.map new file mode 100644 index 00000000..27ca5d7c --- /dev/null +++ b/dist/PollingBlockTracker.js.map @@ -0,0 +1 @@ +{"version":3,"file":"PollingBlockTracker.js","sourceRoot":"","sources":["../src/PollingBlockTracker.ts"],"names":[],"mappings":"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","sourcesContent":["import getCreateRandomId from 'json-rpc-random-id';\nimport pify from 'pify';\nimport { JsonRpcRequest } from 'json-rpc-engine';\nimport { BaseBlockTracker, Provider } from './BaseBlockTracker';\n\nconst createRandomId = getCreateRandomId();\nconst sec = 1000;\n\ninterface PollingBlockTrackerArgs {\n provider: Provider;\n pollingInterval: number;\n retryTimeout: number;\n keepEventLoopActive: boolean;\n setSkipCacheFlag: boolean;\n}\n\ninterface ExtendedJsonRpcRequest extends JsonRpcRequest {\n skipCache?: boolean;\n}\n\nexport class PollingBlockTracker extends BaseBlockTracker {\n\n private _provider: Provider;\n\n private _pollingInterval: number;\n\n private _retryTimeout: number;\n\n private _keepEventLoopActive: boolean;\n\n private _setSkipCacheFlag: boolean;\n\n private _errorCount = 0;\n\n constructor(opts: Partial = {}) {\n // parse + validate args\n if (!opts.provider) {\n throw new Error('PollingBlockTracker - no provider specified.');\n }\n\n super({\n blockResetDuration: opts.pollingInterval,\n });\n\n // config\n this._provider = opts.provider;\n this._pollingInterval = opts.pollingInterval || 20 * sec;\n this._retryTimeout = opts.retryTimeout || this._pollingInterval / 10;\n this._keepEventLoopActive = opts.keepEventLoopActive === undefined ? true : opts.keepEventLoopActive;\n this._setSkipCacheFlag = opts.setSkipCacheFlag || false;\n }\n\n // trigger block polling\n async checkForLatestBlock() {\n await this._updateLatestBlock();\n return await this.getLatestBlock();\n }\n\n protected _start(): void {\n this._synchronize().catch((err) => this.emit('error', err));\n }\n\n private async _synchronize(): Promise {\n while (this._isRunning) {\n try {\n await this._updateLatestBlock();\n await timeout(this._pollingInterval, !this._keepEventLoopActive);\n this._errorCount = 0;\n } catch (err) {\n\n const newErr = new Error(`PollingBlockTracker - encountered an error while attempting to update latest block:\\n${err.stack}`);\n try {\n // only emit error when the error count is > 3\n this._errorCount++;\n if (this._errorCount > 3) {\n this.emit('error', newErr);\n }\n } catch (emitErr) {\n console.error(newErr);\n }\n await timeout(this._retryTimeout, !this._keepEventLoopActive);\n }\n }\n }\n\n private async _updateLatestBlock(): Promise {\n // fetch + set latest block\n const latestBlock = await this._fetchLatestBlock();\n this._newPotentialLatest(latestBlock);\n }\n\n private async _fetchLatestBlock(): Promise {\n const req: ExtendedJsonRpcRequest<[]> = {\n jsonrpc: '2.0',\n id: createRandomId(),\n method: 'eth_blockNumber',\n params: [],\n };\n if (this._setSkipCacheFlag) {\n req.skipCache = true;\n }\n\n const res = await pify((cb) => this._provider.sendAsync(req, cb))();\n if (res.error) {\n throw new Error(`PollingBlockTracker - encountered error fetching block:\\n${res.error}`);\n }\n return res.result;\n }\n}\n\nfunction timeout(duration: number, unref: boolean) {\n return new Promise((resolve) => {\n const timeoutRef = setTimeout(resolve, duration);\n // don't keep process open\n if (timeoutRef.unref && unref) {\n timeoutRef.unref();\n }\n });\n}\n"]} \ No newline at end of file diff --git a/dist/SubscribeBlockTracker.d.ts b/dist/SubscribeBlockTracker.d.ts new file mode 100644 index 00000000..b311037c --- /dev/null +++ b/dist/SubscribeBlockTracker.d.ts @@ -0,0 +1,16 @@ +import { BaseBlockTracker, Provider } from './BaseBlockTracker'; +interface SubscribeBlockTrackerArgs { + provider: Provider; + blockResetDuration?: number; +} +export declare class SubscribeBlockTracker extends BaseBlockTracker { + private _provider; + private _subscriptionId; + constructor(opts?: Partial); + checkForLatestBlock(): Promise; + protected _start(): Promise; + protected _end(): Promise; + private _call; + private _handleSubData; +} +export {}; diff --git a/dist/SubscribeBlockTracker.js b/dist/SubscribeBlockTracker.js new file mode 100644 index 00000000..9a16b397 --- /dev/null +++ b/dist/SubscribeBlockTracker.js @@ -0,0 +1,70 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const json_rpc_random_id_1 = __importDefault(require("json-rpc-random-id")); +const BaseBlockTracker_1 = require("./BaseBlockTracker"); +const createRandomId = json_rpc_random_id_1.default(); +class SubscribeBlockTracker extends BaseBlockTracker_1.BaseBlockTracker { + constructor(opts = {}) { + // parse + validate args + if (!opts.provider) { + throw new Error('SubscribeBlockTracker - no provider specified.'); + } + // BaseBlockTracker constructor + super(opts); + // config + this._provider = opts.provider; + this._subscriptionId = null; + } + async checkForLatestBlock() { + return await this.getLatestBlock(); + } + async _start() { + if (this._subscriptionId === undefined || this._subscriptionId === null) { + try { + const blockNumber = await this._call('eth_blockNumber'); + this._subscriptionId = await this._call('eth_subscribe', 'newHeads', {}); + this._provider.on('data', this._handleSubData.bind(this)); + this._newPotentialLatest(blockNumber); + } + catch (e) { + this.emit('error', e); + } + } + } + async _end() { + if (this._subscriptionId !== null && this._subscriptionId !== undefined) { + try { + await this._call('eth_unsubscribe', this._subscriptionId); + this._subscriptionId = null; + } + catch (e) { + this.emit('error', e); + } + } + } + _call(method, ...params) { + return new Promise((resolve, reject) => { + this._provider.sendAsync({ + id: createRandomId(), method, params, jsonrpc: '2.0', + }, (err, res) => { + if (err) { + reject(err); + } + else { + resolve(res.result); + } + }); + }); + } + _handleSubData(_, response) { + var _a; + if (response.method === 'eth_subscription' && ((_a = response.params) === null || _a === void 0 ? void 0 : _a.subscription) === this._subscriptionId) { + this._newPotentialLatest(response.params.result.number); + } + } +} +exports.SubscribeBlockTracker = SubscribeBlockTracker; +//# sourceMappingURL=SubscribeBlockTracker.js.map \ No newline at end of file diff --git a/dist/SubscribeBlockTracker.js.map b/dist/SubscribeBlockTracker.js.map new file mode 100644 index 00000000..97091994 --- /dev/null +++ b/dist/SubscribeBlockTracker.js.map @@ -0,0 +1 @@ +{"version":3,"file":"SubscribeBlockTracker.js","sourceRoot":"","sources":["../src/SubscribeBlockTracker.ts"],"names":[],"mappings":";;;;;AAAA,4EAAmD;AAEnD,yDAAgE;AAEhE,MAAM,cAAc,GAAG,4BAAiB,EAAE,CAAC;AAY3C,MAAa,qBAAsB,SAAQ,mCAAgB;IAMzD,YAAY,OAA2C,EAAE;QACvD,wBAAwB;QACxB,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;YAClB,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;SACnE;QAED,+BAA+B;QAC/B,KAAK,CAAC,IAAI,CAAC,CAAC;QACZ,SAAS;QACT,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC/B,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,mBAAmB;QACvB,OAAO,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;IACrC,CAAC;IAES,KAAK,CAAC,MAAM;QACpB,IAAI,IAAI,CAAC,eAAe,KAAK,SAAS,IAAI,IAAI,CAAC,eAAe,KAAK,IAAI,EAAE;YACvE,IAAI;gBACF,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAW,CAAC;gBAClE,IAAI,CAAC,eAAe,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,eAAe,EAAE,UAAU,EAAE,EAAE,CAAW,CAAC;gBACnF,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;gBAC1D,IAAI,CAAC,mBAAmB,CAAC,WAAW,CAAC,CAAC;aACvC;YAAC,OAAO,CAAC,EAAE;gBACV,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;aACvB;SACF;IACH,CAAC;IAES,KAAK,CAAC,IAAI;QAClB,IAAI,IAAI,CAAC,eAAe,KAAK,IAAI,IAAI,IAAI,CAAC,eAAe,KAAK,SAAS,EAAE;YACvE,IAAI;gBACF,MAAM,IAAI,CAAC,KAAK,CAAC,iBAAiB,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;gBAC1D,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;aAC7B;YAAC,OAAO,CAAC,EAAE;gBACV,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;aACvB;SACF;IACH,CAAC;IAEO,KAAK,CAAC,MAAc,EAAE,GAAG,MAAiB;QAChD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC;gBACvB,EAAE,EAAE,cAAc,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK;aACrD,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;gBACd,IAAI,GAAG,EAAE;oBACP,MAAM,CAAC,GAAG,CAAC,CAAC;iBACb;qBAAM;oBACL,OAAO,CAAE,GAA+B,CAAC,MAAM,CAAC,CAAC;iBAClD;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,cAAc,CAAC,CAAU,EAAE,QAA6D;;QAC9F,IAAI,QAAQ,CAAC,MAAM,KAAK,kBAAkB,IAAI,OAAA,QAAQ,CAAC,MAAM,0CAAE,YAAY,MAAK,IAAI,CAAC,eAAe,EAAE;YACpG,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;SACzD;IACH,CAAC;CACF;AAlED,sDAkEC","sourcesContent":["import getCreateRandomId from 'json-rpc-random-id';\nimport { JsonRpcNotification, JsonRpcSuccess } from 'json-rpc-engine';\nimport { BaseBlockTracker, Provider } from './BaseBlockTracker';\n\nconst createRandomId = getCreateRandomId();\n\ninterface SubscribeBlockTrackerArgs {\n provider: Provider;\n blockResetDuration?: number;\n}\n\ninterface SubscriptionNotificationParams {\n subscription: string;\n result: { number: string };\n}\n\nexport class SubscribeBlockTracker extends BaseBlockTracker {\n\n private _provider: Provider;\n\n private _subscriptionId: string | null;\n\n constructor(opts: Partial = {}) {\n // parse + validate args\n if (!opts.provider) {\n throw new Error('SubscribeBlockTracker - no provider specified.');\n }\n\n // BaseBlockTracker constructor\n super(opts);\n // config\n this._provider = opts.provider;\n this._subscriptionId = null;\n }\n\n async checkForLatestBlock(): Promise {\n return await this.getLatestBlock();\n }\n\n protected async _start(): Promise {\n if (this._subscriptionId === undefined || this._subscriptionId === null) {\n try {\n const blockNumber = await this._call('eth_blockNumber') as string;\n this._subscriptionId = await this._call('eth_subscribe', 'newHeads', {}) as string;\n this._provider.on('data', this._handleSubData.bind(this));\n this._newPotentialLatest(blockNumber);\n } catch (e) {\n this.emit('error', e);\n }\n }\n }\n\n protected async _end() {\n if (this._subscriptionId !== null && this._subscriptionId !== undefined) {\n try {\n await this._call('eth_unsubscribe', this._subscriptionId);\n this._subscriptionId = null;\n } catch (e) {\n this.emit('error', e);\n }\n }\n }\n\n private _call(method: string, ...params: unknown[]): Promise {\n return new Promise((resolve, reject) => {\n this._provider.sendAsync({\n id: createRandomId(), method, params, jsonrpc: '2.0',\n }, (err, res) => {\n if (err) {\n reject(err);\n } else {\n resolve((res as JsonRpcSuccess).result);\n }\n });\n });\n }\n\n private _handleSubData(_: unknown, response: JsonRpcNotification): void {\n if (response.method === 'eth_subscription' && response.params?.subscription === this._subscriptionId) {\n this._newPotentialLatest(response.params.result.number);\n }\n }\n}\n"]} \ No newline at end of file diff --git a/dist/index.d.ts b/dist/index.d.ts new file mode 100644 index 00000000..740db8d3 --- /dev/null +++ b/dist/index.d.ts @@ -0,0 +1,3 @@ +export * from './BaseBlockTracker'; +export * from './PollingBlockTracker'; +export * from './SubscribeBlockTracker'; diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 00000000..68870695 --- /dev/null +++ b/dist/index.js @@ -0,0 +1,9 @@ +"use strict"; +function __export(m) { + for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; +} +Object.defineProperty(exports, "__esModule", { value: true }); +__export(require("./BaseBlockTracker")); +__export(require("./PollingBlockTracker")); +__export(require("./SubscribeBlockTracker")); +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/index.js.map b/dist/index.js.map new file mode 100644 index 00000000..c6b5426c --- /dev/null +++ b/dist/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;AAAA,wCAAmC;AACnC,2CAAsC;AACtC,6CAAwC","sourcesContent":["export * from './BaseBlockTracker';\nexport * from './PollingBlockTracker';\nexport * from './SubscribeBlockTracker';\n"]} \ No newline at end of file From 02a4cde616c5bd3a14f5eafc2f2982a520323bc0 Mon Sep 17 00:00:00 2001 From: wanghao Date: Mon, 24 May 2021 14:00:13 -0700 Subject: [PATCH 3/3] [ignore-some-errors] modified: dist/BaseBlockTracker.js --- dist/BaseBlockTracker.js | 1 + dist/BaseBlockTracker.js.map | 2 +- dist/PollingBlockTracker.js | 9 ++++++--- dist/PollingBlockTracker.js.map | 2 +- dist/SubscribeBlockTracker.js | 1 + dist/SubscribeBlockTracker.js.map | 2 +- dist/index.js | 19 +++++++++++++------ dist/index.js.map | 2 +- src/PollingBlockTracker.ts | 5 +++-- 9 files changed, 28 insertions(+), 15 deletions(-) diff --git a/dist/BaseBlockTracker.js b/dist/BaseBlockTracker.js index 9a8fca9c..49a57540 100644 --- a/dist/BaseBlockTracker.js +++ b/dist/BaseBlockTracker.js @@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); +exports.BaseBlockTracker = void 0; const safe_event_emitter_1 = __importDefault(require("@metamask/safe-event-emitter")); const sec = 1000; const calculateSum = (accumulator, currentValue) => accumulator + currentValue; diff --git a/dist/BaseBlockTracker.js.map b/dist/BaseBlockTracker.js.map index c49ea30b..aa7e841b 100644 --- a/dist/BaseBlockTracker.js.map +++ b/dist/BaseBlockTracker.js.map @@ -1 +1 @@ -{"version":3,"file":"BaseBlockTracker.js","sourceRoot":"","sources":["../src/BaseBlockTracker.ts"],"names":[],"mappings":"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","sourcesContent":["import SafeEventEmitter from '@metamask/safe-event-emitter';\nimport { JsonRpcRequest, JsonRpcResponse } from 'json-rpc-engine';\n\nconst sec = 1000;\n\nconst calculateSum = (accumulator: number, currentValue: number) => accumulator + currentValue;\nconst blockTrackerEvents: (string | symbol)[] = ['sync', 'latest'];\n\nexport interface Provider extends SafeEventEmitter {\n sendAsync: (req: JsonRpcRequest, cb: (err: Error, response: JsonRpcResponse) => void) => void;\n}\n\ninterface BaseBlockTrackerArgs {\n blockResetDuration?: number;\n}\n\nexport class BaseBlockTracker extends SafeEventEmitter {\n\n protected _isRunning: boolean;\n\n private _blockResetDuration: number;\n\n private _currentBlock: string | null;\n\n private _blockResetTimeout?: ReturnType;\n\n constructor(opts: BaseBlockTrackerArgs = {}) {\n super();\n\n // config\n this._blockResetDuration = opts.blockResetDuration || 20 * sec;\n // state\n this._currentBlock = null;\n this._isRunning = false;\n\n // bind functions for internal use\n this._onNewListener = this._onNewListener.bind(this);\n this._onRemoveListener = this._onRemoveListener.bind(this);\n this._resetCurrentBlock = this._resetCurrentBlock.bind(this);\n\n // listen for handler changes\n this._setupInternalEvents();\n }\n\n isRunning(): boolean {\n return this._isRunning;\n }\n\n getCurrentBlock(): string | null {\n return this._currentBlock;\n }\n\n async getLatestBlock(): Promise {\n // return if available\n if (this._currentBlock) {\n return this._currentBlock;\n }\n // wait for a new latest block\n const latestBlock: string = await new Promise((resolve) => this.once('latest', resolve));\n // return newly set current block\n return latestBlock;\n }\n\n // dont allow module consumer to remove our internal event listeners\n removeAllListeners(eventName: string | symbol) {\n // perform default behavior, preserve fn arity\n if (eventName) {\n super.removeAllListeners(eventName);\n } else {\n super.removeAllListeners();\n }\n\n // re-add internal events\n this._setupInternalEvents();\n // trigger stop check just in case\n this._onRemoveListener();\n\n return this;\n }\n\n /**\n * To be implemented in subclass.\n */\n protected _start(): void {\n // default behavior is noop\n }\n\n /**\n * To be implemented in subclass.\n */\n protected _end(): void {\n // default behavior is noop\n }\n\n private _setupInternalEvents(): void {\n // first remove listeners for idempotence\n this.removeListener('newListener', this._onNewListener);\n this.removeListener('removeListener', this._onRemoveListener);\n // then add them\n this.on('newListener', this._onNewListener);\n this.on('removeListener', this._onRemoveListener);\n }\n\n private _onNewListener(eventName: string | symbol): void {\n // `newListener` is called *before* the listener is added\n if (blockTrackerEvents.includes(eventName)) {\n this._maybeStart();\n }\n }\n\n private _onRemoveListener(): void {\n // `removeListener` is called *after* the listener is removed\n if (this._getBlockTrackerEventCount() > 0) {\n return;\n }\n this._maybeEnd();\n }\n\n private _maybeStart(): void {\n if (this._isRunning) {\n return;\n }\n this._isRunning = true;\n // cancel setting latest block to stale\n this._cancelBlockResetTimeout();\n this._start();\n }\n\n private _maybeEnd(): void {\n if (!this._isRunning) {\n return;\n }\n this._isRunning = false;\n this._setupBlockResetTimeout();\n this._end();\n }\n\n private _getBlockTrackerEventCount(): number {\n return blockTrackerEvents\n .map((eventName) => this.listenerCount(eventName))\n .reduce(calculateSum);\n }\n\n protected _newPotentialLatest(newBlock: string): void {\n const currentBlock = this._currentBlock;\n // only update if blok number is higher\n if (currentBlock && (hexToInt(newBlock) <= hexToInt(currentBlock))) {\n return;\n }\n this._setCurrentBlock(newBlock);\n }\n\n private _setCurrentBlock(newBlock: string): void {\n const oldBlock = this._currentBlock;\n this._currentBlock = newBlock;\n this.emit('latest', newBlock);\n this.emit('sync', { oldBlock, newBlock });\n }\n\n private _setupBlockResetTimeout(): void {\n // clear any existing timeout\n this._cancelBlockResetTimeout();\n // clear latest block when stale\n this._blockResetTimeout = setTimeout(this._resetCurrentBlock, this._blockResetDuration);\n\n // nodejs - dont hold process open\n if (this._blockResetTimeout.unref) {\n this._blockResetTimeout.unref();\n }\n }\n\n private _cancelBlockResetTimeout(): void {\n if (this._blockResetTimeout) {\n clearTimeout(this._blockResetTimeout);\n }\n }\n\n private _resetCurrentBlock(): void {\n this._currentBlock = null;\n }\n\n}\n\nfunction hexToInt(hexInt: string): number {\n return Number.parseInt(hexInt, 16);\n}\n"]} \ No newline at end of file +{"version":3,"file":"BaseBlockTracker.js","sourceRoot":"","sources":["../src/BaseBlockTracker.ts"],"names":[],"mappings":"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","sourcesContent":["import SafeEventEmitter from '@metamask/safe-event-emitter';\nimport { JsonRpcRequest, JsonRpcResponse } from 'json-rpc-engine';\n\nconst sec = 1000;\n\nconst calculateSum = (accumulator: number, currentValue: number) => accumulator + currentValue;\nconst blockTrackerEvents: (string | symbol)[] = ['sync', 'latest'];\n\nexport interface Provider extends SafeEventEmitter {\n sendAsync: (req: JsonRpcRequest, cb: (err: Error, response: JsonRpcResponse) => void) => void;\n}\n\ninterface BaseBlockTrackerArgs {\n blockResetDuration?: number;\n}\n\nexport class BaseBlockTracker extends SafeEventEmitter {\n\n protected _isRunning: boolean;\n\n private _blockResetDuration: number;\n\n private _currentBlock: string | null;\n\n private _blockResetTimeout?: ReturnType;\n\n constructor(opts: BaseBlockTrackerArgs = {}) {\n super();\n\n // config\n this._blockResetDuration = opts.blockResetDuration || 20 * sec;\n // state\n this._currentBlock = null;\n this._isRunning = false;\n\n // bind functions for internal use\n this._onNewListener = this._onNewListener.bind(this);\n this._onRemoveListener = this._onRemoveListener.bind(this);\n this._resetCurrentBlock = this._resetCurrentBlock.bind(this);\n\n // listen for handler changes\n this._setupInternalEvents();\n }\n\n isRunning(): boolean {\n return this._isRunning;\n }\n\n getCurrentBlock(): string | null {\n return this._currentBlock;\n }\n\n async getLatestBlock(): Promise {\n // return if available\n if (this._currentBlock) {\n return this._currentBlock;\n }\n // wait for a new latest block\n const latestBlock: string = await new Promise((resolve) => this.once('latest', resolve));\n // return newly set current block\n return latestBlock;\n }\n\n // dont allow module consumer to remove our internal event listeners\n removeAllListeners(eventName: string | symbol) {\n // perform default behavior, preserve fn arity\n if (eventName) {\n super.removeAllListeners(eventName);\n } else {\n super.removeAllListeners();\n }\n\n // re-add internal events\n this._setupInternalEvents();\n // trigger stop check just in case\n this._onRemoveListener();\n\n return this;\n }\n\n /**\n * To be implemented in subclass.\n */\n protected _start(): void {\n // default behavior is noop\n }\n\n /**\n * To be implemented in subclass.\n */\n protected _end(): void {\n // default behavior is noop\n }\n\n private _setupInternalEvents(): void {\n // first remove listeners for idempotence\n this.removeListener('newListener', this._onNewListener);\n this.removeListener('removeListener', this._onRemoveListener);\n // then add them\n this.on('newListener', this._onNewListener);\n this.on('removeListener', this._onRemoveListener);\n }\n\n private _onNewListener(eventName: string | symbol): void {\n // `newListener` is called *before* the listener is added\n if (blockTrackerEvents.includes(eventName)) {\n this._maybeStart();\n }\n }\n\n private _onRemoveListener(): void {\n // `removeListener` is called *after* the listener is removed\n if (this._getBlockTrackerEventCount() > 0) {\n return;\n }\n this._maybeEnd();\n }\n\n private _maybeStart(): void {\n if (this._isRunning) {\n return;\n }\n this._isRunning = true;\n // cancel setting latest block to stale\n this._cancelBlockResetTimeout();\n this._start();\n }\n\n private _maybeEnd(): void {\n if (!this._isRunning) {\n return;\n }\n this._isRunning = false;\n this._setupBlockResetTimeout();\n this._end();\n }\n\n private _getBlockTrackerEventCount(): number {\n return blockTrackerEvents\n .map((eventName) => this.listenerCount(eventName))\n .reduce(calculateSum);\n }\n\n protected _newPotentialLatest(newBlock: string): void {\n const currentBlock = this._currentBlock;\n // only update if blok number is higher\n if (currentBlock && (hexToInt(newBlock) <= hexToInt(currentBlock))) {\n return;\n }\n this._setCurrentBlock(newBlock);\n }\n\n private _setCurrentBlock(newBlock: string): void {\n const oldBlock = this._currentBlock;\n this._currentBlock = newBlock;\n this.emit('latest', newBlock);\n this.emit('sync', { oldBlock, newBlock });\n }\n\n private _setupBlockResetTimeout(): void {\n // clear any existing timeout\n this._cancelBlockResetTimeout();\n // clear latest block when stale\n this._blockResetTimeout = setTimeout(this._resetCurrentBlock, this._blockResetDuration);\n\n // nodejs - dont hold process open\n if (this._blockResetTimeout.unref) {\n this._blockResetTimeout.unref();\n }\n }\n\n private _cancelBlockResetTimeout(): void {\n if (this._blockResetTimeout) {\n clearTimeout(this._blockResetTimeout);\n }\n }\n\n private _resetCurrentBlock(): void {\n this._currentBlock = null;\n }\n\n}\n\nfunction hexToInt(hexInt: string): number {\n return Number.parseInt(hexInt, 16);\n}\n"]} \ No newline at end of file diff --git a/dist/PollingBlockTracker.js b/dist/PollingBlockTracker.js index 94237a63..588064bf 100644 --- a/dist/PollingBlockTracker.js +++ b/dist/PollingBlockTracker.js @@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); +exports.PollingBlockTracker = void 0; const json_rpc_random_id_1 = __importDefault(require("json-rpc-random-id")); const pify_1 = __importDefault(require("pify")); const BaseBlockTracker_1 = require("./BaseBlockTracker"); @@ -10,7 +11,6 @@ const createRandomId = json_rpc_random_id_1.default(); const sec = 1000; class PollingBlockTracker extends BaseBlockTracker_1.BaseBlockTracker { constructor(opts = {}) { - this._errorCount = 0; // parse + validate args if (!opts.provider) { throw new Error('PollingBlockTracker - no provider specified.'); @@ -18,6 +18,7 @@ class PollingBlockTracker extends BaseBlockTracker_1.BaseBlockTracker { super({ blockResetDuration: opts.pollingInterval, }); + this._errorCount = 0; // config this._provider = opts.provider; this._pollingInterval = opts.pollingInterval || 20 * sec; @@ -44,10 +45,12 @@ class PollingBlockTracker extends BaseBlockTracker_1.BaseBlockTracker { const newErr = new Error(`PollingBlockTracker - encountered an error while attempting to update latest block:\n${err.stack}`); try { // only emit error when the error count is > 3 - this._errorCount++; - if (this._errorCount > 3) { + if (this._errorCount++ > 2) { this.emit('error', newErr); } + else { + console.error(`encountered error ignored err count=${this._errorCount}`, newErr); + } } catch (emitErr) { console.error(newErr); diff --git a/dist/PollingBlockTracker.js.map b/dist/PollingBlockTracker.js.map index 27ca5d7c..1be076fb 100644 --- a/dist/PollingBlockTracker.js.map +++ b/dist/PollingBlockTracker.js.map @@ -1 +1 @@ -{"version":3,"file":"PollingBlockTracker.js","sourceRoot":"","sources":["../src/PollingBlockTracker.ts"],"names":[],"mappings":"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","sourcesContent":["import getCreateRandomId from 'json-rpc-random-id';\nimport pify from 'pify';\nimport { JsonRpcRequest } from 'json-rpc-engine';\nimport { BaseBlockTracker, Provider } from './BaseBlockTracker';\n\nconst createRandomId = getCreateRandomId();\nconst sec = 1000;\n\ninterface PollingBlockTrackerArgs {\n provider: Provider;\n pollingInterval: number;\n retryTimeout: number;\n keepEventLoopActive: boolean;\n setSkipCacheFlag: boolean;\n}\n\ninterface ExtendedJsonRpcRequest extends JsonRpcRequest {\n skipCache?: boolean;\n}\n\nexport class PollingBlockTracker extends BaseBlockTracker {\n\n private _provider: Provider;\n\n private _pollingInterval: number;\n\n private _retryTimeout: number;\n\n private _keepEventLoopActive: boolean;\n\n private _setSkipCacheFlag: boolean;\n\n private _errorCount = 0;\n\n constructor(opts: Partial = {}) {\n // parse + validate args\n if (!opts.provider) {\n throw new Error('PollingBlockTracker - no provider specified.');\n }\n\n super({\n blockResetDuration: opts.pollingInterval,\n });\n\n // config\n this._provider = opts.provider;\n this._pollingInterval = opts.pollingInterval || 20 * sec;\n this._retryTimeout = opts.retryTimeout || this._pollingInterval / 10;\n this._keepEventLoopActive = opts.keepEventLoopActive === undefined ? true : opts.keepEventLoopActive;\n this._setSkipCacheFlag = opts.setSkipCacheFlag || false;\n }\n\n // trigger block polling\n async checkForLatestBlock() {\n await this._updateLatestBlock();\n return await this.getLatestBlock();\n }\n\n protected _start(): void {\n this._synchronize().catch((err) => this.emit('error', err));\n }\n\n private async _synchronize(): Promise {\n while (this._isRunning) {\n try {\n await this._updateLatestBlock();\n await timeout(this._pollingInterval, !this._keepEventLoopActive);\n this._errorCount = 0;\n } catch (err) {\n\n const newErr = new Error(`PollingBlockTracker - encountered an error while attempting to update latest block:\\n${err.stack}`);\n try {\n // only emit error when the error count is > 3\n this._errorCount++;\n if (this._errorCount > 3) {\n this.emit('error', newErr);\n }\n } catch (emitErr) {\n console.error(newErr);\n }\n await timeout(this._retryTimeout, !this._keepEventLoopActive);\n }\n }\n }\n\n private async _updateLatestBlock(): Promise {\n // fetch + set latest block\n const latestBlock = await this._fetchLatestBlock();\n this._newPotentialLatest(latestBlock);\n }\n\n private async _fetchLatestBlock(): Promise {\n const req: ExtendedJsonRpcRequest<[]> = {\n jsonrpc: '2.0',\n id: createRandomId(),\n method: 'eth_blockNumber',\n params: [],\n };\n if (this._setSkipCacheFlag) {\n req.skipCache = true;\n }\n\n const res = await pify((cb) => this._provider.sendAsync(req, cb))();\n if (res.error) {\n throw new Error(`PollingBlockTracker - encountered error fetching block:\\n${res.error}`);\n }\n return res.result;\n }\n}\n\nfunction timeout(duration: number, unref: boolean) {\n return new Promise((resolve) => {\n const timeoutRef = setTimeout(resolve, duration);\n // don't keep process open\n if (timeoutRef.unref && unref) {\n timeoutRef.unref();\n }\n });\n}\n"]} \ No newline at end of file +{"version":3,"file":"PollingBlockTracker.js","sourceRoot":"","sources":["../src/PollingBlockTracker.ts"],"names":[],"mappings":"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","sourcesContent":["import getCreateRandomId from 'json-rpc-random-id';\nimport pify from 'pify';\nimport { JsonRpcRequest } from 'json-rpc-engine';\nimport { BaseBlockTracker, Provider } from './BaseBlockTracker';\n\nconst createRandomId = getCreateRandomId();\nconst sec = 1000;\n\ninterface PollingBlockTrackerArgs {\n provider: Provider;\n pollingInterval: number;\n retryTimeout: number;\n keepEventLoopActive: boolean;\n setSkipCacheFlag: boolean;\n}\n\ninterface ExtendedJsonRpcRequest extends JsonRpcRequest {\n skipCache?: boolean;\n}\n\nexport class PollingBlockTracker extends BaseBlockTracker {\n\n private _provider: Provider;\n\n private _pollingInterval: number;\n\n private _retryTimeout: number;\n\n private _keepEventLoopActive: boolean;\n\n private _setSkipCacheFlag: boolean;\n\n private _errorCount = 0;\n\n constructor(opts: Partial = {}) {\n // parse + validate args\n if (!opts.provider) {\n throw new Error('PollingBlockTracker - no provider specified.');\n }\n\n super({\n blockResetDuration: opts.pollingInterval,\n });\n\n // config\n this._provider = opts.provider;\n this._pollingInterval = opts.pollingInterval || 20 * sec;\n this._retryTimeout = opts.retryTimeout || this._pollingInterval / 10;\n this._keepEventLoopActive = opts.keepEventLoopActive === undefined ? true : opts.keepEventLoopActive;\n this._setSkipCacheFlag = opts.setSkipCacheFlag || false;\n }\n\n // trigger block polling\n async checkForLatestBlock() {\n await this._updateLatestBlock();\n return await this.getLatestBlock();\n }\n\n protected _start(): void {\n this._synchronize().catch((err) => this.emit('error', err));\n }\n\n private async _synchronize(): Promise {\n while (this._isRunning) {\n try {\n await this._updateLatestBlock();\n await timeout(this._pollingInterval, !this._keepEventLoopActive);\n this._errorCount = 0;\n } catch (err) {\n\n const newErr = new Error(`PollingBlockTracker - encountered an error while attempting to update latest block:\\n${err.stack}`);\n try {\n // only emit error when the error count is > 3\n if (this._errorCount++ > 2) {\n this.emit('error', newErr);\n } else {\n console.error(`encountered error ignored err count=${this._errorCount}`, newErr);\n }\n } catch (emitErr) {\n console.error(newErr);\n }\n await timeout(this._retryTimeout, !this._keepEventLoopActive);\n }\n }\n }\n\n private async _updateLatestBlock(): Promise {\n // fetch + set latest block\n const latestBlock = await this._fetchLatestBlock();\n this._newPotentialLatest(latestBlock);\n }\n\n private async _fetchLatestBlock(): Promise {\n const req: ExtendedJsonRpcRequest<[]> = {\n jsonrpc: '2.0',\n id: createRandomId(),\n method: 'eth_blockNumber',\n params: [],\n };\n if (this._setSkipCacheFlag) {\n req.skipCache = true;\n }\n\n const res = await pify((cb) => this._provider.sendAsync(req, cb))();\n if (res.error) {\n throw new Error(`PollingBlockTracker - encountered error fetching block:\\n${res.error}`);\n }\n return res.result;\n }\n}\n\nfunction timeout(duration: number, unref: boolean) {\n return new Promise((resolve) => {\n const timeoutRef = setTimeout(resolve, duration);\n // don't keep process open\n if (timeoutRef.unref && unref) {\n timeoutRef.unref();\n }\n });\n}\n"]} \ No newline at end of file diff --git a/dist/SubscribeBlockTracker.js b/dist/SubscribeBlockTracker.js index 9a16b397..112c3630 100644 --- a/dist/SubscribeBlockTracker.js +++ b/dist/SubscribeBlockTracker.js @@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); +exports.SubscribeBlockTracker = void 0; const json_rpc_random_id_1 = __importDefault(require("json-rpc-random-id")); const BaseBlockTracker_1 = require("./BaseBlockTracker"); const createRandomId = json_rpc_random_id_1.default(); diff --git a/dist/SubscribeBlockTracker.js.map b/dist/SubscribeBlockTracker.js.map index 97091994..9e799620 100644 --- a/dist/SubscribeBlockTracker.js.map +++ b/dist/SubscribeBlockTracker.js.map @@ -1 +1 @@ -{"version":3,"file":"SubscribeBlockTracker.js","sourceRoot":"","sources":["../src/SubscribeBlockTracker.ts"],"names":[],"mappings":";;;;;AAAA,4EAAmD;AAEnD,yDAAgE;AAEhE,MAAM,cAAc,GAAG,4BAAiB,EAAE,CAAC;AAY3C,MAAa,qBAAsB,SAAQ,mCAAgB;IAMzD,YAAY,OAA2C,EAAE;QACvD,wBAAwB;QACxB,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;YAClB,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;SACnE;QAED,+BAA+B;QAC/B,KAAK,CAAC,IAAI,CAAC,CAAC;QACZ,SAAS;QACT,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC/B,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,mBAAmB;QACvB,OAAO,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;IACrC,CAAC;IAES,KAAK,CAAC,MAAM;QACpB,IAAI,IAAI,CAAC,eAAe,KAAK,SAAS,IAAI,IAAI,CAAC,eAAe,KAAK,IAAI,EAAE;YACvE,IAAI;gBACF,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAW,CAAC;gBAClE,IAAI,CAAC,eAAe,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,eAAe,EAAE,UAAU,EAAE,EAAE,CAAW,CAAC;gBACnF,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;gBAC1D,IAAI,CAAC,mBAAmB,CAAC,WAAW,CAAC,CAAC;aACvC;YAAC,OAAO,CAAC,EAAE;gBACV,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;aACvB;SACF;IACH,CAAC;IAES,KAAK,CAAC,IAAI;QAClB,IAAI,IAAI,CAAC,eAAe,KAAK,IAAI,IAAI,IAAI,CAAC,eAAe,KAAK,SAAS,EAAE;YACvE,IAAI;gBACF,MAAM,IAAI,CAAC,KAAK,CAAC,iBAAiB,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;gBAC1D,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;aAC7B;YAAC,OAAO,CAAC,EAAE;gBACV,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;aACvB;SACF;IACH,CAAC;IAEO,KAAK,CAAC,MAAc,EAAE,GAAG,MAAiB;QAChD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC;gBACvB,EAAE,EAAE,cAAc,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK;aACrD,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;gBACd,IAAI,GAAG,EAAE;oBACP,MAAM,CAAC,GAAG,CAAC,CAAC;iBACb;qBAAM;oBACL,OAAO,CAAE,GAA+B,CAAC,MAAM,CAAC,CAAC;iBAClD;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,cAAc,CAAC,CAAU,EAAE,QAA6D;;QAC9F,IAAI,QAAQ,CAAC,MAAM,KAAK,kBAAkB,IAAI,OAAA,QAAQ,CAAC,MAAM,0CAAE,YAAY,MAAK,IAAI,CAAC,eAAe,EAAE;YACpG,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;SACzD;IACH,CAAC;CACF;AAlED,sDAkEC","sourcesContent":["import getCreateRandomId from 'json-rpc-random-id';\nimport { JsonRpcNotification, JsonRpcSuccess } from 'json-rpc-engine';\nimport { BaseBlockTracker, Provider } from './BaseBlockTracker';\n\nconst createRandomId = getCreateRandomId();\n\ninterface SubscribeBlockTrackerArgs {\n provider: Provider;\n blockResetDuration?: number;\n}\n\ninterface SubscriptionNotificationParams {\n subscription: string;\n result: { number: string };\n}\n\nexport class SubscribeBlockTracker extends BaseBlockTracker {\n\n private _provider: Provider;\n\n private _subscriptionId: string | null;\n\n constructor(opts: Partial = {}) {\n // parse + validate args\n if (!opts.provider) {\n throw new Error('SubscribeBlockTracker - no provider specified.');\n }\n\n // BaseBlockTracker constructor\n super(opts);\n // config\n this._provider = opts.provider;\n this._subscriptionId = null;\n }\n\n async checkForLatestBlock(): Promise {\n return await this.getLatestBlock();\n }\n\n protected async _start(): Promise {\n if (this._subscriptionId === undefined || this._subscriptionId === null) {\n try {\n const blockNumber = await this._call('eth_blockNumber') as string;\n this._subscriptionId = await this._call('eth_subscribe', 'newHeads', {}) as string;\n this._provider.on('data', this._handleSubData.bind(this));\n this._newPotentialLatest(blockNumber);\n } catch (e) {\n this.emit('error', e);\n }\n }\n }\n\n protected async _end() {\n if (this._subscriptionId !== null && this._subscriptionId !== undefined) {\n try {\n await this._call('eth_unsubscribe', this._subscriptionId);\n this._subscriptionId = null;\n } catch (e) {\n this.emit('error', e);\n }\n }\n }\n\n private _call(method: string, ...params: unknown[]): Promise {\n return new Promise((resolve, reject) => {\n this._provider.sendAsync({\n id: createRandomId(), method, params, jsonrpc: '2.0',\n }, (err, res) => {\n if (err) {\n reject(err);\n } else {\n resolve((res as JsonRpcSuccess).result);\n }\n });\n });\n }\n\n private _handleSubData(_: unknown, response: JsonRpcNotification): void {\n if (response.method === 'eth_subscription' && response.params?.subscription === this._subscriptionId) {\n this._newPotentialLatest(response.params.result.number);\n }\n }\n}\n"]} \ No newline at end of file +{"version":3,"file":"SubscribeBlockTracker.js","sourceRoot":"","sources":["../src/SubscribeBlockTracker.ts"],"names":[],"mappings":";;;;;;AAAA,4EAAmD;AAEnD,yDAAgE;AAEhE,MAAM,cAAc,GAAG,4BAAiB,EAAE,CAAC;AAY3C,MAAa,qBAAsB,SAAQ,mCAAgB;IAMzD,YAAY,OAA2C,EAAE;QACvD,wBAAwB;QACxB,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;YAClB,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;SACnE;QAED,+BAA+B;QAC/B,KAAK,CAAC,IAAI,CAAC,CAAC;QACZ,SAAS;QACT,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC/B,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,mBAAmB;QACvB,OAAO,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;IACrC,CAAC;IAES,KAAK,CAAC,MAAM;QACpB,IAAI,IAAI,CAAC,eAAe,KAAK,SAAS,IAAI,IAAI,CAAC,eAAe,KAAK,IAAI,EAAE;YACvE,IAAI;gBACF,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAW,CAAC;gBAClE,IAAI,CAAC,eAAe,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,eAAe,EAAE,UAAU,EAAE,EAAE,CAAW,CAAC;gBACnF,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;gBAC1D,IAAI,CAAC,mBAAmB,CAAC,WAAW,CAAC,CAAC;aACvC;YAAC,OAAO,CAAC,EAAE;gBACV,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;aACvB;SACF;IACH,CAAC;IAES,KAAK,CAAC,IAAI;QAClB,IAAI,IAAI,CAAC,eAAe,KAAK,IAAI,IAAI,IAAI,CAAC,eAAe,KAAK,SAAS,EAAE;YACvE,IAAI;gBACF,MAAM,IAAI,CAAC,KAAK,CAAC,iBAAiB,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;gBAC1D,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;aAC7B;YAAC,OAAO,CAAC,EAAE;gBACV,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;aACvB;SACF;IACH,CAAC;IAEO,KAAK,CAAC,MAAc,EAAE,GAAG,MAAiB;QAChD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC;gBACvB,EAAE,EAAE,cAAc,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK;aACrD,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;gBACd,IAAI,GAAG,EAAE;oBACP,MAAM,CAAC,GAAG,CAAC,CAAC;iBACb;qBAAM;oBACL,OAAO,CAAE,GAA+B,CAAC,MAAM,CAAC,CAAC;iBAClD;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,cAAc,CAAC,CAAU,EAAE,QAA6D;;QAC9F,IAAI,QAAQ,CAAC,MAAM,KAAK,kBAAkB,IAAI,CAAA,MAAA,QAAQ,CAAC,MAAM,0CAAE,YAAY,MAAK,IAAI,CAAC,eAAe,EAAE;YACpG,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;SACzD;IACH,CAAC;CACF;AAlED,sDAkEC","sourcesContent":["import getCreateRandomId from 'json-rpc-random-id';\nimport { JsonRpcNotification, JsonRpcSuccess } from 'json-rpc-engine';\nimport { BaseBlockTracker, Provider } from './BaseBlockTracker';\n\nconst createRandomId = getCreateRandomId();\n\ninterface SubscribeBlockTrackerArgs {\n provider: Provider;\n blockResetDuration?: number;\n}\n\ninterface SubscriptionNotificationParams {\n subscription: string;\n result: { number: string };\n}\n\nexport class SubscribeBlockTracker extends BaseBlockTracker {\n\n private _provider: Provider;\n\n private _subscriptionId: string | null;\n\n constructor(opts: Partial = {}) {\n // parse + validate args\n if (!opts.provider) {\n throw new Error('SubscribeBlockTracker - no provider specified.');\n }\n\n // BaseBlockTracker constructor\n super(opts);\n // config\n this._provider = opts.provider;\n this._subscriptionId = null;\n }\n\n async checkForLatestBlock(): Promise {\n return await this.getLatestBlock();\n }\n\n protected async _start(): Promise {\n if (this._subscriptionId === undefined || this._subscriptionId === null) {\n try {\n const blockNumber = await this._call('eth_blockNumber') as string;\n this._subscriptionId = await this._call('eth_subscribe', 'newHeads', {}) as string;\n this._provider.on('data', this._handleSubData.bind(this));\n this._newPotentialLatest(blockNumber);\n } catch (e) {\n this.emit('error', e);\n }\n }\n }\n\n protected async _end() {\n if (this._subscriptionId !== null && this._subscriptionId !== undefined) {\n try {\n await this._call('eth_unsubscribe', this._subscriptionId);\n this._subscriptionId = null;\n } catch (e) {\n this.emit('error', e);\n }\n }\n }\n\n private _call(method: string, ...params: unknown[]): Promise {\n return new Promise((resolve, reject) => {\n this._provider.sendAsync({\n id: createRandomId(), method, params, jsonrpc: '2.0',\n }, (err, res) => {\n if (err) {\n reject(err);\n } else {\n resolve((res as JsonRpcSuccess).result);\n }\n });\n });\n }\n\n private _handleSubData(_: unknown, response: JsonRpcNotification): void {\n if (response.method === 'eth_subscription' && response.params?.subscription === this._subscriptionId) {\n this._newPotentialLatest(response.params.result.number);\n }\n }\n}\n"]} \ No newline at end of file diff --git a/dist/index.js b/dist/index.js index 68870695..886352b0 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1,9 +1,16 @@ "use strict"; -function __export(m) { - for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; -} +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); +}; Object.defineProperty(exports, "__esModule", { value: true }); -__export(require("./BaseBlockTracker")); -__export(require("./PollingBlockTracker")); -__export(require("./SubscribeBlockTracker")); +__exportStar(require("./BaseBlockTracker"), exports); +__exportStar(require("./PollingBlockTracker"), exports); +__exportStar(require("./SubscribeBlockTracker"), exports); //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/index.js.map b/dist/index.js.map index c6b5426c..84a69687 100644 --- a/dist/index.js.map +++ b/dist/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;AAAA,wCAAmC;AACnC,2CAAsC;AACtC,6CAAwC","sourcesContent":["export * from './BaseBlockTracker';\nexport * from './PollingBlockTracker';\nexport * from './SubscribeBlockTracker';\n"]} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qDAAmC;AACnC,wDAAsC;AACtC,0DAAwC","sourcesContent":["export * from './BaseBlockTracker';\nexport * from './PollingBlockTracker';\nexport * from './SubscribeBlockTracker';\n"]} \ No newline at end of file diff --git a/src/PollingBlockTracker.ts b/src/PollingBlockTracker.ts index 4cba378f..7dbeac14 100644 --- a/src/PollingBlockTracker.ts +++ b/src/PollingBlockTracker.ts @@ -71,9 +71,10 @@ export class PollingBlockTracker extends BaseBlockTracker { const newErr = new Error(`PollingBlockTracker - encountered an error while attempting to update latest block:\n${err.stack}`); try { // only emit error when the error count is > 3 - this._errorCount++; - if (this._errorCount > 3) { + if (this._errorCount++ > 2) { this.emit('error', newErr); + } else { + console.error(`encountered error ignored err count=${this._errorCount}`, newErr); } } catch (emitErr) { console.error(newErr);