From d6c81bfd85b5b4cc9831acfcec1491826807cdb5 Mon Sep 17 00:00:00 2001 From: minenkom Date: Tue, 7 Apr 2020 09:45:35 +0200 Subject: [PATCH 1/7] Inject custom logger --- lib/classes/rmq-intercepter.class.ts | 3 +++ lib/classes/rmq-pipe.class.ts | 3 +++ lib/interfaces/rmq-options.interface.ts | 2 ++ lib/rmq.service.ts | 22 +++++++++++----------- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/lib/classes/rmq-intercepter.class.ts b/lib/classes/rmq-intercepter.class.ts index a1b6262..993f41b 100644 --- a/lib/classes/rmq-intercepter.class.ts +++ b/lib/classes/rmq-intercepter.class.ts @@ -1,6 +1,9 @@ import { Message } from 'amqplib'; +import { LoggerService } from '@nestjs/common'; export class RMQIntercepterClass { + constructor(protected logger?: LoggerService) {} + async intercept(res: any, msg: Message, error?: Error): Promise { return res; } diff --git a/lib/classes/rmq-pipe.class.ts b/lib/classes/rmq-pipe.class.ts index 7cd9380..6fa39c2 100644 --- a/lib/classes/rmq-pipe.class.ts +++ b/lib/classes/rmq-pipe.class.ts @@ -1,7 +1,10 @@ import { Message } from 'amqplib'; +import { LoggerService } from '@nestjs/common'; // tslint:disable-next-line: interface-name export class RMQPipeClass { + constructor(protected logger?: LoggerService) {} + async transform(msg: Message): Promise { return msg; } diff --git a/lib/interfaces/rmq-options.interface.ts b/lib/interfaces/rmq-options.interface.ts index 0b335b4..66742bb 100644 --- a/lib/interfaces/rmq-options.interface.ts +++ b/lib/interfaces/rmq-options.interface.ts @@ -1,6 +1,7 @@ import { RMQPipeClass } from '../classes/rmq-pipe.class'; import { RMQIntercepterClass } from '../classes/rmq-intercepter.class'; import { RMQErrorHandler } from '../classes/rmq-error-handler.class'; +import { LoggerService } from '@nestjs/common'; export interface IRMQServiceOptions { exchangeName: string; @@ -16,6 +17,7 @@ export interface IRMQServiceOptions { reconnectTimeInSeconds?: number; messagesTimeout?: number; logMessages?: boolean; + logger?: LoggerService; middleware?: typeof RMQPipeClass[]; intercepters?: typeof RMQIntercepterClass[]; errorHandler?: typeof RMQErrorHandler; diff --git a/lib/rmq.service.ts b/lib/rmq.service.ts index 93d9242..42cf09d 100644 --- a/lib/rmq.service.ts +++ b/lib/rmq.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, LoggerService } from '@nestjs/common'; import { CONNECTED_MESSAGE, CONNECTING_MESSAGE, @@ -37,11 +37,11 @@ export class RMQService { private sendResponseEmitter: EventEmitter = new EventEmitter(); private replyQueue: string = REPLY_QUEUE; private queueMeta: IQueueMeta[]; - private logger: any; + private logger: LoggerService; constructor(options: IRMQServiceOptions) { this.options = options; - this.logger = new Signale({ + this.logger = options.logger ? options.logger : new Signale({ config: { displayTimestamp: true, displayDate: true, @@ -53,7 +53,7 @@ export class RMQService { } public async init(): Promise { - this.logger.watch(CONNECTING_MESSAGE); + this.logger.log(CONNECTING_MESSAGE); const connectionURLs: string[] = this.options.connections.map((connection: IRMQConnection) => { return `amqp://${connection.login}:${connection.password}@${connection.host}`; }); @@ -82,7 +82,7 @@ export class RMQService { if (this.options.queueName) { this.listen(channel); } - this.logger.success(CONNECTED_MESSAGE); + this.logger.log(CONNECTED_MESSAGE); }, }); @@ -106,7 +106,7 @@ export class RMQService { } const { content } = msg; if (content.toString()) { - this.logger.recieved(`[${topic}] ${content.toString()}`); + this.logger.debug(`[${topic}] ${content.toString()}`); resolve(JSON.parse(content.toString())); } else { reject(new RMQError(ERROR_NONE_RPC, ERROR_TYPE.TRANSPORT)); @@ -116,13 +116,13 @@ export class RMQService { replyTo: this.replyQueue, correlationId, }); - this.logger.sent(`[${topic}] ${JSON.stringify(message)}`); + this.logger.debug(`[${topic}] ${JSON.stringify(message)}`); }); } public async notify(topic: string, message: IMessage): Promise { await this.channel.publish(this.options.exchangeName, topic, Buffer.from(JSON.stringify(message))); - this.logger.sent(`[${topic}] ${JSON.stringify(message)}`); + this.logger.debug(`[${topic}] ${JSON.stringify(message)}`); } public async disconnect() { @@ -181,7 +181,7 @@ export class RMQService { ...this.buildError(error), }, }); - this.logger.sent(`[${msg.fields.routingKey}] ${JSON.stringify(res)}`); + this.logger.debug(`[${msg.fields.routingKey}] ${JSON.stringify(res)}`); } private getUniqId(): string { @@ -202,7 +202,7 @@ export class RMQService { return msg; } for (const middleware of this.options.middleware) { - msg = await new middleware().transform(msg); + msg = await new middleware(this.logger).transform(msg); } return msg; } @@ -212,7 +212,7 @@ export class RMQService { return res; } for (const intercepter of this.options.intercepters) { - res = await new intercepter().intercept(res, msg, error); + res = await new intercepter(this.logger).intercept(res, msg, error); } return res; } From f88f696f2bf76e4932b3ed99464c6ff4e0eb0916 Mon Sep 17 00:00:00 2001 From: minenkom Date: Tue, 7 Apr 2020 16:08:00 +0200 Subject: [PATCH 2/7] Inject custom logger --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 77ddef7..7e41440 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,8 @@ "amqplib": "^0.5.5", "class-validator": "^0.11.1", "reflect-metadata": "^0.1.13", - "signale": "^1.4.0" + "signale": "^1.4.0", + "@nestjs/common": "^7.0.1" }, "devDependencies": { "@nestjs/common": "^7.0.0", From 608eb24bb10e7abcd0c937a1e181e6355a090d09 Mon Sep 17 00:00:00 2001 From: minenkom Date: Tue, 14 Apr 2020 14:44:10 +0200 Subject: [PATCH 3/7] Inject custom logger --- lib/classes/rmq-intercepter.class.ts | 6 +++++- lib/classes/rmq-pipe.class.ts | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/classes/rmq-intercepter.class.ts b/lib/classes/rmq-intercepter.class.ts index 993f41b..6be376e 100644 --- a/lib/classes/rmq-intercepter.class.ts +++ b/lib/classes/rmq-intercepter.class.ts @@ -2,7 +2,11 @@ import { Message } from 'amqplib'; import { LoggerService } from '@nestjs/common'; export class RMQIntercepterClass { - constructor(protected logger?: LoggerService) {} + protected logger: LoggerService; + + constructor(logger: LoggerService = console) { + this.logger = logger; + } async intercept(res: any, msg: Message, error?: Error): Promise { return res; diff --git a/lib/classes/rmq-pipe.class.ts b/lib/classes/rmq-pipe.class.ts index 6fa39c2..a625998 100644 --- a/lib/classes/rmq-pipe.class.ts +++ b/lib/classes/rmq-pipe.class.ts @@ -3,7 +3,11 @@ import { LoggerService } from '@nestjs/common'; // tslint:disable-next-line: interface-name export class RMQPipeClass { - constructor(protected logger?: LoggerService) {} + protected logger: LoggerService; + + constructor(logger: LoggerService = console) { + this.logger = logger; + } async transform(msg: Message): Promise { return msg; From 7f0b965eb5082e6b6cae1623a23c13cc02fd1cff Mon Sep 17 00:00:00 2001 From: minenkom Date: Fri, 17 Apr 2020 09:50:46 +0200 Subject: [PATCH 4/7] Inject custom logger --- lib/rmq.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rmq.service.ts b/lib/rmq.service.ts index 42cf09d..3583ef7 100644 --- a/lib/rmq.service.ts +++ b/lib/rmq.service.ts @@ -148,7 +148,7 @@ export class RMQService { await channel.consume( this.options.queueName, async (msg: Message) => { - this.logger.recieved(`[${msg.fields.routingKey}] ${msg.content}`); + this.logger.log(`[${msg.fields.routingKey}] ${msg.content}`); if (this.isTopicExists(msg.fields.routingKey)) { msg = await this.useMiddleware(msg); requestEmitter.emit(msg.fields.routingKey, msg); From 8735ddf82d347f1c89450ba06e2c2b412f77aab3 Mon Sep 17 00:00:00 2001 From: minenkom Date: Fri, 17 Apr 2020 09:52:46 +0200 Subject: [PATCH 5/7] Inject custom logger --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 7e41440..77ddef7 100644 --- a/package.json +++ b/package.json @@ -30,8 +30,7 @@ "amqplib": "^0.5.5", "class-validator": "^0.11.1", "reflect-metadata": "^0.1.13", - "signale": "^1.4.0", - "@nestjs/common": "^7.0.1" + "signale": "^1.4.0" }, "devDependencies": { "@nestjs/common": "^7.0.0", From db0c4e9604dbef6dc29592405181b46d000e161b Mon Sep 17 00:00:00 2001 From: AlariCode Date: Thu, 23 Apr 2020 20:38:39 +0300 Subject: [PATCH 6/7] feature - removed signale, added color-output, updated docs --- CHANGELOG.md | 3 + README.md | 19 +- e2e/mocks/double.pipe.ts | 2 +- e2e/mocks/zero.intercepter.ts | 2 +- e2e/tests/rmq.e2e-spec.ts | 2 +- lib/constants.ts | 16 - lib/decorators/rmq-controller.decorator.ts | 11 +- lib/helpers/logger.ts | 30 ++ lib/rmq.service.ts | 26 +- package-lock.json | 498 +++++++++++++++------ package.json | 7 +- 11 files changed, 411 insertions(+), 205 deletions(-) create mode 100644 lib/helpers/logger.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 168ed6a..034d199 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ ## 1.5.2 - Fixed double logging +## 1.6.0 +- Custom logger injection (thx to @minenkom) + ## 1.5.1 - Fixed ack race condition - Added tests diff --git a/README.md b/README.md index bf79c20..60442d7 100644 --- a/README.md +++ b/README.md @@ -73,12 +73,13 @@ Additionally, you can use optional parameters: - **isQueueDurable** (boolean) - Makes created queue durable. Default is true. - **isExchangeDurable** (boolean) - Makes created exchange durable. Default is true. - **logMessages** (boolean) - Enable printing all sent and recieved messages in console with its route and content. Default is false. -- **middleware** (array) - Array of middleware functions that implements `RMQPipeClass` with one method `transform`. They will be triggered right after recieving message, before pipes and controller method. Trigger order is equal to array order. -- **errorHandler** (class) - custom error handler for dealing with errors from replies, use `errorHandler` in module options and pass class that implements `RMQErrorHandler`. +- **logger** (LoggerService) - Your custom logger service that implements `LoggerService` interface. Compatible with Winston and other loggers. +- **middleware** (array) - Array of middleware functions that extends `RMQPipeClass` with one method `transform`. They will be triggered right after recieving message, before pipes and controller method. Trigger order is equal to array order. +- **errorHandler** (class) - custom error handler for dealing with errors from replies, use `errorHandler` in module options and pass class that extends `RMQErrorHandler`. - **serviceName** (string) - service name for debugging. ```javascript -class LogMiddleware implements RMQPipeClass { +class LogMiddleware extends RMQPipeClass { async transfrom(msg: Message): Promise { console.log(msg); return msg; @@ -86,10 +87,10 @@ class LogMiddleware implements RMQPipeClass { } ``` -- **intercepters** (array) - Array of intercepter functions that implements `RMQIntercepterClass` with one method `intercept`. They will be triggered before replying on any message. Trigger order is equal to array order. +- **intercepters** (array) - Array of intercepter functions that extends `RMQIntercepterClass` with one method `intercept`. They will be triggered before replying on any message. Trigger order is equal to array order. ```javascript -export class MyIntercepter implements RMQIntercepterClass { +export class MyIntercepter extends RMQIntercepterClass { async intercept(res: any, msg: Message, error: Error): Promise { // res - response body // msg - initial message we are replying to @@ -253,10 +254,10 @@ myMethod(numbers: number[]): number { } ``` -where `MyPipeClass` implements `RMQPipeClass` with one method `transform`: +where `MyPipeClass` extends `RMQPipeClass` with one method `transform`: ```javascript -class MyPipeClass implements RMQPipeClass { +class MyPipeClass extends RMQPipeClass { async transfrom(msg: Message): Promise { // do something return msg; @@ -265,10 +266,10 @@ class MyPipeClass implements RMQPipeClass { ``` ## Using RMQErrorHandler -If you want to use custom error handler for dealing with errors from replies, use `errorHandler` in module options and pass class that implements `RMQErrorHandler`: +If you want to use custom error handler for dealing with errors from replies, use `errorHandler` in module options and pass class that extends `RMQErrorHandler`: ```javascript -class MyErrorHandler implements RMQErrorHandler { +class MyErrorHandler extends RMQErrorHandler { public static handle(headers: IRmqErrorHeaders): Error | RMQError { // do something return new RMQError( diff --git a/e2e/mocks/double.pipe.ts b/e2e/mocks/double.pipe.ts index d92238a..5a051d5 100644 --- a/e2e/mocks/double.pipe.ts +++ b/e2e/mocks/double.pipe.ts @@ -2,7 +2,7 @@ import { RMQPipeClass } from '../../lib'; import { Message } from 'amqplib'; import { MultiplyContracts } from '../contracts/mock.contracts'; -export class DoublePipe implements RMQPipeClass { +export class DoublePipe extends RMQPipeClass { async transform(msg: Message): Promise { if (msg.fields.routingKey === MultiplyContracts.topic) { let { arrayToMultiply }: MultiplyContracts.Request = JSON.parse(msg.content.toString()); diff --git a/e2e/mocks/zero.intercepter.ts b/e2e/mocks/zero.intercepter.ts index d0003da..ce73175 100644 --- a/e2e/mocks/zero.intercepter.ts +++ b/e2e/mocks/zero.intercepter.ts @@ -2,7 +2,7 @@ import { RMQIntercepterClass } from '../../lib'; import { Message } from 'amqplib'; import { DivideContracts } from '../contracts/mock.contracts'; -export class ZeroIntercepter implements RMQIntercepterClass { +export class ZeroIntercepter extends RMQIntercepterClass { async intercept(res: any, msg: Message, error: Error): Promise { if (msg.fields.routingKey === DivideContracts.topic) { res.result = 0; diff --git a/e2e/tests/rmq.e2e-spec.ts b/e2e/tests/rmq.e2e-spec.ts index d860d71..19b20df 100644 --- a/e2e/tests/rmq.e2e-spec.ts +++ b/e2e/tests/rmq.e2e-spec.ts @@ -1,6 +1,6 @@ import { Test } from '@nestjs/testing'; import { RMQModule, RMQService } from '../../lib'; -import { INestApplication } from '@nestjs/common'; +import { INestApplication, Logger } from '@nestjs/common'; import { ApiController } from '../mocks/api.controller'; import { MicroserviceController } from '../mocks/microservice.controller'; import { ERROR_UNDEFINED_FROM_RPC } from '../../lib/constants'; diff --git a/lib/constants.ts b/lib/constants.ts index f0818b9..95a8d6f 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -1,7 +1,6 @@ export const RMQ_ROUTES_META: string = 'RMQ_ROUTES_META'; export const DISCONNECT_EVENT: string = 'disconnect'; export const DISCONNECT_MESSAGE: string = 'Disconnected from RMQ. Trying to reconnect'; -export const CONNECTING_MESSAGE: string = 'Connecting to RMQ'; export const CONNECTED_MESSAGE: string = 'Successfully connected to RMQ'; export const REPLY_QUEUE: string = 'amq.rabbitmq.reply-to'; export const EXCHANGE_TYPE: string = 'topic'; @@ -20,18 +19,3 @@ export enum ERROR_TYPE { TRANSPORT = 'TRANSPORT', RMQ = 'RMQ' } - -export const CUSTOM_LOGS = { - recieved: { - badge: '▼', - color: 'blue', - label: 'recieved', - logLevel: 'info', - }, - sent: { - badge: '▲', - color: 'blue', - label: 'sent', - logLevel: 'info', - }, -}; diff --git a/lib/decorators/rmq-controller.decorator.ts b/lib/decorators/rmq-controller.decorator.ts index 3763404..e094793 100644 --- a/lib/decorators/rmq-controller.decorator.ts +++ b/lib/decorators/rmq-controller.decorator.ts @@ -6,23 +6,16 @@ import { import { IQueueMeta } from '../interfaces/queue-meta.interface'; import { requestEmitter, responseEmitter, ResponseEmmiterResult } from '../emmiters/router.emmiter'; import { RMQService } from '../rmq.service'; -import { Signale } from 'signale'; import { Message } from 'amqplib'; import { RMQError } from '..'; +import { Logger } from '@nestjs/common'; export function RMQController(): ClassDecorator { return function(target: any) { - const logger = new Signale({ - config: { - displayTimestamp: true, - displayDate: true, - }, - logLevel: 'error', - }); let topics: IQueueMeta[] = Reflect.getMetadata(RMQ_ROUTES_META, RMQService); topics = topics ? topics.filter(topic => topic.target === target.prototype) : []; if(topics.length === 0) { - logger.error(`${ERROR_NO_ROUTE_FOR_CONTROLLER} ${target.prototype.constructor.name}`); + Logger.error(`${ERROR_NO_ROUTE_FOR_CONTROLLER} ${target.prototype.constructor.name}`); } target = class extends (target as { new(...args): any }) { constructor(...args: any) { diff --git a/lib/helpers/logger.ts b/lib/helpers/logger.ts new file mode 100644 index 0000000..a851f00 --- /dev/null +++ b/lib/helpers/logger.ts @@ -0,0 +1,30 @@ +import { Logger, LoggerService } from '@nestjs/common'; +import { blueBright, white, yellow } from 'chalk'; + +export class RQMColorLogger implements LoggerService { + logMessages: boolean; + + constructor(logMessages: boolean) { + this.logMessages = logMessages ?? false; + } + log(message: any, context?: string): any { + Logger.log(message, context); + } + error(message: any, trace?: string, context?: string): any { + Logger.error(message, trace, context); + } + debug(message: any, context?: string): any { + if(!this.logMessages) { + return; + } + const split = new RegExp(/(.*?)(\[.*?])(.*)/g).exec(message); + if(split[3]) { + Logger.log(`${blueBright(split[1])} ${yellow(split[2])} ${white(split[3])}`); + } else { + Logger.log(message, context); + } + } + warn(message: any, context?: string): any { + Logger.warn(message, context); + } +} diff --git a/lib/rmq.service.ts b/lib/rmq.service.ts index 3583ef7..5d6486f 100644 --- a/lib/rmq.service.ts +++ b/lib/rmq.service.ts @@ -1,8 +1,7 @@ import { Injectable, LoggerService } from '@nestjs/common'; import { CONNECTED_MESSAGE, - CONNECTING_MESSAGE, - CUSTOM_LOGS, DEFAULT_PREFETCH_COUNT, + DEFAULT_PREFETCH_COUNT, DEFAULT_RECONNECT_TIME, DEFAULT_TIMEOUT, DISCONNECT_EVENT, @@ -17,7 +16,6 @@ import { } from './constants'; import { EventEmitter } from 'events'; import { Channel, Message } from 'amqplib'; -import { Signale } from 'signale'; import * as amqp from 'amqp-connection-manager'; // tslint:disable-next-line:no-duplicate-imports import { AmqpConnectionManager, ChannelWrapper } from 'amqp-connection-manager'; @@ -28,6 +26,7 @@ import { IQueueMeta } from './interfaces/queue-meta.interface'; import { RMQError } from './classes/rmq-error.class'; import { RMQErrorHandler } from './classes/rmq-error-handler.class'; import { hostname } from 'os'; +import { RQMColorLogger } from './helpers/logger'; @Injectable() export class RMQService { @@ -41,19 +40,11 @@ export class RMQService { constructor(options: IRMQServiceOptions) { this.options = options; - this.logger = options.logger ? options.logger : new Signale({ - config: { - displayTimestamp: true, - displayDate: true, - }, - logLevel: this.options.logMessages ? 'info' : 'error', - types: CUSTOM_LOGS, - }); + this.logger = options.logger ? options.logger : new RQMColorLogger(this.options.logMessages); this.init(); } public async init(): Promise { - this.logger.log(CONNECTING_MESSAGE); const connectionURLs: string[] = this.options.connections.map((connection: IRMQConnection) => { return `amqp://${connection.login}:${connection.password}@${connection.host}`; }); @@ -87,8 +78,7 @@ export class RMQService { }); this.server.on(DISCONNECT_EVENT, err => { - this.logger.error(DISCONNECT_MESSAGE); - this.logger.error(err.err); + this.logger.error(DISCONNECT_MESSAGE, err); }); } @@ -106,7 +96,7 @@ export class RMQService { } const { content } = msg; if (content.toString()) { - this.logger.debug(`[${topic}] ${content.toString()}`); + this.logger.debug(`Received ▼ [${topic}] ${content.toString()}`); resolve(JSON.parse(content.toString())); } else { reject(new RMQError(ERROR_NONE_RPC, ERROR_TYPE.TRANSPORT)); @@ -116,7 +106,7 @@ export class RMQService { replyTo: this.replyQueue, correlationId, }); - this.logger.debug(`[${topic}] ${JSON.stringify(message)}`); + this.logger.debug(`Sent ▲ [${topic}] ${JSON.stringify(message)}`); }); } @@ -148,7 +138,7 @@ export class RMQService { await channel.consume( this.options.queueName, async (msg: Message) => { - this.logger.log(`[${msg.fields.routingKey}] ${msg.content}`); + this.logger.debug(`Received ▼ [${msg.fields.routingKey}] ${msg.content}`); if (this.isTopicExists(msg.fields.routingKey)) { msg = await this.useMiddleware(msg); requestEmitter.emit(msg.fields.routingKey, msg); @@ -181,7 +171,7 @@ export class RMQService { ...this.buildError(error), }, }); - this.logger.debug(`[${msg.fields.routingKey}] ${JSON.stringify(res)}`); + this.logger.debug(`Sent ▲ [${msg.fields.routingKey}] ${JSON.stringify(res)}`); } private getUniqId(): string { diff --git a/package-lock.json b/package-lock.json index 194cf39..9235782 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "nestjs-rmq", - "version": "1.5.1", + "version": "1.6.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -55,8 +55,45 @@ "@babel/helper-validator-identifier": "^7.9.0", "chalk": "^2.0.0", "js-tokens": "^4.0.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + } + } + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" } }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", @@ -66,6 +103,12 @@ "ms": "^2.1.1" } }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -77,6 +120,15 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -225,6 +277,58 @@ "chalk": "^2.0.0", "esutils": "^2.0.2", "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "@babel/parser": { @@ -280,6 +384,58 @@ "@babel/helper-validator-identifier": "^7.9.0", "chalk": "^2.0.0", "js-tokens": "^4.0.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + } + } + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" } } } @@ -319,8 +475,45 @@ "@babel/helper-validator-identifier": "^7.9.0", "chalk": "^2.0.0", "js-tokens": "^4.0.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + } + } + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" } }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", @@ -330,11 +523,26 @@ "ms": "^2.1.1" } }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -962,6 +1170,58 @@ "chalk": "^2.4.1", "consola": "^2.3.0", "node-fetch": "^2.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "@sinonjs/commons": { @@ -1039,11 +1299,19 @@ "integrity": "sha512-0Vk/kqkukxPKSzP9c8WJgisgGDx5oZDbsLLWIP5t70yThO/YleE+GEm2S1GlRALTaack3O7U5OS5qEm7q2kciA==", "dev": true }, + "@types/chalk": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/chalk/-/chalk-2.2.0.tgz", + "integrity": "sha512-1zzPV9FDe1I/WHhRkf9SNgqtRJWZqrBWgu7JGveuHmmyR9CnAPCie2N/x+iHrgnpYBIcCJWHBoMRv2TRWktsvw==", + "dev": true, + "requires": { + "chalk": "*" + } + }, "@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", - "dev": true + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" }, "@types/istanbul-lib-coverage": { "version": "2.0.1", @@ -1213,11 +1481,12 @@ "dev": true }, "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", "requires": { - "color-convert": "^1.9.0" + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" } }, "anymatch": { @@ -1696,13 +1965,12 @@ "dev": true }, "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", + "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, "ci-info": { @@ -1792,17 +2060,17 @@ } }, "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "requires": { - "color-name": "1.1.3" + "color-name": "~1.1.4" } }, "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "combined-stream": { "version": "1.0.8", @@ -2180,14 +2448,6 @@ "once": "^1.4.0" } }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "requires": { - "is-arrayish": "^0.2.1" - } - }, "es5-ext": { "version": "0.10.53", "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", @@ -2241,7 +2501,8 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true }, "escodegen": { "version": "1.14.1", @@ -2593,14 +2854,6 @@ "bser": "2.1.1" } }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -2625,14 +2878,6 @@ "unpipe": "~1.0.0" } }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "requires": { - "locate-path": "^2.0.0" - } - }, "follow-redirects": { "version": "1.5.10", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", @@ -2774,7 +3019,8 @@ "graceful-fs": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true }, "growly": { "version": "1.3.0", @@ -2800,9 +3046,9 @@ } }, "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "has-value": { "version": "1.0.0", @@ -2981,11 +3227,6 @@ } } }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" - }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", @@ -4550,11 +4791,6 @@ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" - }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", @@ -4630,26 +4866,6 @@ "type-check": "~0.3.2" } }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, "lodash": { "version": "4.17.15", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", @@ -5125,36 +5341,6 @@ "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", "dev": true }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, "parse5": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", @@ -5173,11 +5359,6 @@ "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", "dev": true }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" - }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -5214,11 +5395,6 @@ "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", "dev": true }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" - }, "pirates": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", @@ -5228,15 +5404,6 @@ "node-modules-regexp": "^1.0.0" } }, - "pkg-conf": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", - "integrity": "sha1-ISZRTKbyq/69FoWW3xi6V4Z/AFg=", - "requires": { - "find-up": "^2.0.0", - "load-json-file": "^4.0.0" - } - }, "pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -5933,16 +6100,6 @@ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", "dev": true }, - "signale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/signale/-/signale-1.4.0.tgz", - "integrity": "sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==", - "requires": { - "chalk": "^2.3.2", - "figures": "^2.0.0", - "pkg-conf": "^2.1.0" - } - }, "sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -6240,11 +6397,6 @@ } } }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" - }, "strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", @@ -6258,11 +6410,11 @@ "dev": true }, "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" } }, "supports-hyperlinks": { @@ -6492,6 +6644,58 @@ "semver": "^5.3.0", "tslib": "^1.10.0", "tsutils": "^2.29.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "tsutils": { diff --git a/package.json b/package.json index 77ddef7..67e1267 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nestjs-rmq", - "version": "1.5.2", + "version": "1.6.0", "description": "NestJS RabbitMQ Module", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -28,9 +28,9 @@ "dependencies": { "amqp-connection-manager": "^3.2.0", "amqplib": "^0.5.5", + "chalk": "^4.0.0", "class-validator": "^0.11.1", - "reflect-metadata": "^0.1.13", - "signale": "^1.4.0" + "reflect-metadata": "^0.1.13" }, "devDependencies": { "@nestjs/common": "^7.0.0", @@ -39,6 +39,7 @@ "@nestjs/testing": "^7.0.0", "@types/amqp-connection-manager": "^2.0.8", "@types/amqplib": "^0.5.13", + "@types/chalk": "^2.2.0", "@types/jest": "^25.1.4", "@types/node": "^13.9.5", "jest": "^25.2.4", From 88ed201b7dce54889db50c8ee1618527f91578d6 Mon Sep 17 00:00:00 2001 From: AlariCode Date: Fri, 24 Apr 2020 16:48:50 +0300 Subject: [PATCH 7/7] feature - reconnect fix and async init --- CHANGELOG.md | 8 +++-- e2e/tests/rmq.e2e-spec.ts | 10 +++--- lib/rmq.module.ts | 4 ++- lib/rmq.service.ts | 76 ++++++++++++++++++++------------------- package-lock.json | 8 ++--- package.json | 4 +-- 6 files changed, 60 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 034d199..c8d0721 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,15 @@ # Change log -## 1.5.2 -- Fixed double logging +## 1.7.0 +- Fixed reconnection bug +- Async init all modules loaded (thx to mjarmoc) ## 1.6.0 - Custom logger injection (thx to @minenkom) +## 1.5.2 +- Fixed double logging + ## 1.5.1 - Fixed ack race condition - Added tests diff --git a/e2e/tests/rmq.e2e-spec.ts b/e2e/tests/rmq.e2e-spec.ts index 19b20df..b3c4170 100644 --- a/e2e/tests/rmq.e2e-spec.ts +++ b/e2e/tests/rmq.e2e-spec.ts @@ -32,7 +32,7 @@ describe('RMQe2e', () => { middleware: [DoublePipe], intercepters: [ZeroIntercepter], errorHandler: ErrorHostHandler, - serviceName: 'test-service' + serviceName: 'test-service', }), ], controllers: [ApiController, MicroserviceController], @@ -68,7 +68,7 @@ describe('RMQe2e', () => { }); it('get common Error from method', async () => { try { - const { result } = await apiController.sumSuccess([0,0,0]); + const { result } = await apiController.sumSuccess([0, 0, 0]); expect(result).not.toBe(0); } catch (error) { expect(error.message).toBe('My error from method'); @@ -81,7 +81,7 @@ describe('RMQe2e', () => { }); it('get RMQError from method', async () => { try { - const { result } = await apiController.sumSuccess([-1,0,0]); + const { result } = await apiController.sumSuccess([-1, 0, 0]); expect(result).not.toBe(-1); } catch (error) { expect(error.message).toBe('My RMQError from method'); @@ -94,7 +94,7 @@ describe('RMQe2e', () => { }); it('get undefined return Error', async () => { try { - const { result } = await apiController.sumSuccess([-11,0,0]); + const { result } = await apiController.sumSuccess([-11, 0, 0]); expect(result).not.toBe(-11); } catch (error) { expect(error.message).toBe(ERROR_UNDEFINED_FROM_RPC); @@ -138,7 +138,7 @@ describe('RMQe2e', () => { describe('errorHandler', () => { it('error host change', async () => { try { - const { result } = await apiController.sumSuccess([0,0,0]); + const { result } = await apiController.sumSuccess([0, 0, 0]); expect(result).not.toBe(0); } catch (error) { expect(error.host).toBe('handler'); diff --git a/lib/rmq.module.ts b/lib/rmq.module.ts index 41c8e91..0f45e18 100644 --- a/lib/rmq.module.ts +++ b/lib/rmq.module.ts @@ -9,7 +9,9 @@ export class RMQModule { const rmqServiceProvider = { provide: RMQService, useFactory: async (): Promise => { - return new RMQService(options); + const RMQInstance = new RMQService(options); + await RMQInstance.init(); + return RMQInstance; }, }; return { diff --git a/lib/rmq.service.ts b/lib/rmq.service.ts index 5d6486f..55da35e 100644 --- a/lib/rmq.service.ts +++ b/lib/rmq.service.ts @@ -41,45 +41,49 @@ export class RMQService { constructor(options: IRMQServiceOptions) { this.options = options; this.logger = options.logger ? options.logger : new RQMColorLogger(this.options.logMessages); - this.init(); } public async init(): Promise { - const connectionURLs: string[] = this.options.connections.map((connection: IRMQConnection) => { - return `amqp://${connection.login}:${connection.password}@${connection.host}`; - }); - const connectionOptions = { - reconnectTimeInSeconds: this.options.reconnectTimeInSeconds ?? DEFAULT_RECONNECT_TIME - }; - this.server = amqp.connect(connectionURLs, connectionOptions); - this.channel = this.server.createChannel({ - json: false, - setup: async (channel: Channel) => { - await channel.assertExchange(this.options.exchangeName, EXCHANGE_TYPE, { - durable: this.options.isExchangeDurable ?? true, - }); - await channel.prefetch( - this.options.prefetchCount ?? DEFAULT_PREFETCH_COUNT, - this.options.isGlobalPrefetchCount ?? false - ); - await channel.consume( - this.replyQueue, - (msg: Message) => { - this.sendResponseEmitter.emit(msg.properties.correlationId, msg); - }, - { noAck: true } - ); - this.waitForReply(); - if (this.options.queueName) { - this.listen(channel); - } - this.logger.log(CONNECTED_MESSAGE); - }, - }); + return new Promise((resolve => { + const connectionURLs: string[] = this.options.connections.map((connection: IRMQConnection) => { + return `amqp://${connection.login}:${connection.password}@${connection.host}`; + }); + const connectionOptions = { + reconnectTimeInSeconds: this.options.reconnectTimeInSeconds ?? DEFAULT_RECONNECT_TIME + }; + this.server = amqp.connect(connectionURLs, connectionOptions); + this.channel = this.server.createChannel({ + json: false, + setup: async (channel: Channel) => { + await channel.assertExchange(this.options.exchangeName, EXCHANGE_TYPE, { + durable: this.options.isExchangeDurable ?? true, + }); + await channel.prefetch( + this.options.prefetchCount ?? DEFAULT_PREFETCH_COUNT, + this.options.isGlobalPrefetchCount ?? false + ); + await channel.consume( + this.replyQueue, + (msg: Message) => { + this.sendResponseEmitter.emit(msg.properties.correlationId, msg); + }, + { noAck: true } + ); + this.waitForReply(); + if (this.options.queueName) { + this.listen(channel); + } + this.logger.log(CONNECTED_MESSAGE); + resolve(); + }, + }); + + this.server.on(DISCONNECT_EVENT, err => { + this.logger.error(DISCONNECT_MESSAGE); + this.logger.error(err.err); + }); + })); - this.server.on(DISCONNECT_EVENT, err => { - this.logger.error(DISCONNECT_MESSAGE, err); - }); } public async send(topic: string, message: IMessage): Promise { @@ -150,7 +154,7 @@ export class RMQService { ); } - private async waitForReply(): Promise { + private waitForReply(): void { responseEmitter.on(ResponseEmmiterResult.success, async (msg, result) => { this.reply(result, msg); }); diff --git a/package-lock.json b/package-lock.json index 9235782..7fc8625 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "nestjs-rmq", - "version": "1.6.0", + "version": "1.5.9-beta3", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1234,9 +1234,9 @@ } }, "@types/amqp-connection-manager": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@types/amqp-connection-manager/-/amqp-connection-manager-2.0.8.tgz", - "integrity": "sha512-GQasAK7KWooig0SC5pvMqqC4NEI0RiD+tRz6Zj4DIELG2uLxQ33cGH8PfQnq8qNYO1c9TR6XSvhZTgE5CUB0EA==", + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@types/amqp-connection-manager/-/amqp-connection-manager-2.0.9.tgz", + "integrity": "sha512-FV+UszFvarv0BWsGOASMfQGLDzfvgZIA2P9UFwo+4QX8C10c8TXegeIhz+h63UC5aTcEpgWJ4e7MhQZseNzK1w==", "dev": true, "requires": { "@types/amqplib": "*" diff --git a/package.json b/package.json index 67e1267..27650a9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nestjs-rmq", - "version": "1.6.0", + "version": "1.7.0", "description": "NestJS RabbitMQ Module", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -37,7 +37,7 @@ "@nestjs/core": "^7.0.0", "@nestjs/platform-express": "^7.0.0", "@nestjs/testing": "^7.0.0", - "@types/amqp-connection-manager": "^2.0.8", + "@types/amqp-connection-manager": "^2.0.9", "@types/amqplib": "^0.5.13", "@types/chalk": "^2.2.0", "@types/jest": "^25.1.4",