mirror of
				https://github.com/lingble/twenty.git
				synced 2025-10-30 20:27:55 +00:00 
			
		
		
		
	feat: exceptions handlers (#2855)
* feat: wip exception handlers * feat: exception capturer * fix: rename exception-capturer into exception-handler * fix: remove unused variable
This commit is contained in:
		| @@ -24,6 +24,7 @@ SIGN_IN_PREFILLED=true | ||||
| # SUPPORT_FRONT_HMAC_KEY=replace_me_with_front_chat_verification_secret | ||||
| # SUPPORT_FRONT_CHAT_ID=replace_me_with_front_chat_id | ||||
| # LOGGER_DRIVER=console | ||||
| # EXCEPTION_HANDLER_DRIVER=sentry | ||||
| # SENTRY_DSN=https://xxx@xxx.ingest.sentry.io/xxx | ||||
| # LOG_LEVEL=error,warn | ||||
| # MESSAGE_QUEUE_TYPE=pg-boss | ||||
|   | ||||
| @@ -52,6 +52,7 @@ | ||||
|     "@ptc-org/nestjs-query-graphql": "4.2.0", | ||||
|     "@ptc-org/nestjs-query-typeorm": "4.2.1-alpha.2", | ||||
|     "@sentry/node": "^7.66.0", | ||||
|     "@sentry/profiling-node": "^1.2.6", | ||||
|     "@sentry/tracing": "^7.66.0", | ||||
|     "@types/lodash.camelcase": "^4.3.7", | ||||
|     "@types/lodash.merge": "^4.6.7", | ||||
|   | ||||
| @@ -10,6 +10,9 @@ import { ExtractJwt } from 'passport-jwt'; | ||||
| import { TokenExpiredError, JsonWebTokenError, verify } from 'jsonwebtoken'; | ||||
|  | ||||
| import { WorkspaceFactory } from 'src/workspace/workspace.factory'; | ||||
| import { TypeOrmExceptionFilter } from 'src/filters/typeorm-exception.filter'; | ||||
| import { HttpExceptionFilter } from 'src/filters/http-exception.filter'; | ||||
| import { GlobalExceptionFilter } from 'src/filters/global-exception.filter'; | ||||
|  | ||||
| import { AppService } from './app.service'; | ||||
|  | ||||
| @@ -22,7 +25,6 @@ import { | ||||
|   JwtAuthStrategy, | ||||
|   JwtPayload, | ||||
| } from './core/auth/strategies/jwt.auth.strategy'; | ||||
| import { ExceptionFilter } from './filters/exception.filter'; | ||||
|  | ||||
| @Module({ | ||||
|   imports: [ | ||||
| @@ -111,9 +113,20 @@ import { ExceptionFilter } from './filters/exception.filter'; | ||||
|   ], | ||||
|   providers: [ | ||||
|     AppService, | ||||
|     // Exceptions filters must be ordered from the least specific to the most specific | ||||
|     // If TypeOrmExceptionFilter handle something, HttpExceptionFilter will not handle it | ||||
|     // GlobalExceptionFilter will handle the rest of the exceptions | ||||
|     { | ||||
|       provide: APP_FILTER, | ||||
|       useClass: ExceptionFilter, | ||||
|       useClass: GlobalExceptionFilter, | ||||
|     }, | ||||
|     { | ||||
|       provide: APP_FILTER, | ||||
|       useClass: HttpExceptionFilter, | ||||
|     }, | ||||
|     { | ||||
|       provide: APP_FILTER, | ||||
|       useClass: TypeOrmExceptionFilter, | ||||
|     }, | ||||
|   ], | ||||
| }) | ||||
|   | ||||
| @@ -1,56 +0,0 @@ | ||||
| import { ArgumentsHost, Catch, HttpException } from '@nestjs/common'; | ||||
| import { GqlContextType, GqlExceptionFilter } from '@nestjs/graphql'; | ||||
|  | ||||
| import { TypeORMError } from 'typeorm'; | ||||
|  | ||||
| import { | ||||
|   AuthenticationError, | ||||
|   BaseGraphQLError, | ||||
|   ForbiddenError, | ||||
| } from 'src/filters/utils/graphql-errors.util'; | ||||
|  | ||||
| const graphQLPredefinedExceptions = { | ||||
|   401: AuthenticationError, | ||||
|   403: ForbiddenError, | ||||
| }; | ||||
|  | ||||
| @Catch() | ||||
| export class ExceptionFilter implements GqlExceptionFilter { | ||||
|   catch(exception: HttpException | TypeORMError, host: ArgumentsHost) { | ||||
|     if (host.getType<GqlContextType>() !== 'graphql') { | ||||
|       return null; | ||||
|     } | ||||
|  | ||||
|     if (exception instanceof TypeORMError) { | ||||
|       const error = new BaseGraphQLError( | ||||
|         exception.name, | ||||
|         'INTERNAL_SERVER_ERROR', | ||||
|       ); | ||||
|  | ||||
|       error.stack = exception.stack; | ||||
|       error.extensions['response'] = exception.message; | ||||
|  | ||||
|       return error; | ||||
|     } else if (exception instanceof HttpException) { | ||||
|       let error: BaseGraphQLError; | ||||
|  | ||||
|       if (exception.getStatus() in graphQLPredefinedExceptions) { | ||||
|         error = new graphQLPredefinedExceptions[exception.getStatus()]( | ||||
|           exception.message, | ||||
|         ); | ||||
|       } else { | ||||
|         error = new BaseGraphQLError( | ||||
|           exception.message, | ||||
|           exception.getStatus().toString(), | ||||
|         ); | ||||
|       } | ||||
|  | ||||
|       error.stack = exception.stack; | ||||
|       error.extensions['response'] = exception.getResponse(); | ||||
|  | ||||
|       return error; | ||||
|     } | ||||
|  | ||||
|     return exception; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										18
									
								
								server/src/filters/global-exception.filter.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								server/src/filters/global-exception.filter.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| import { Catch, Injectable } from '@nestjs/common'; | ||||
| import { GqlExceptionFilter } from '@nestjs/graphql'; | ||||
|  | ||||
| import { ExceptionHandlerService } from 'src/integrations/exception-handler/exception-handler.service'; | ||||
|  | ||||
| @Catch() | ||||
| @Injectable() | ||||
| export class GlobalExceptionFilter implements GqlExceptionFilter { | ||||
|   constructor( | ||||
|     private readonly exceptionHandlerService: ExceptionHandlerService, | ||||
|   ) {} | ||||
|  | ||||
|   catch(exception: unknown) { | ||||
|     this.exceptionHandlerService.captureException(exception); | ||||
|  | ||||
|     return exception; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										42
									
								
								server/src/filters/http-exception.filter.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								server/src/filters/http-exception.filter.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| import { ArgumentsHost, Catch, HttpException } from '@nestjs/common'; | ||||
| import { GqlContextType, GqlExceptionFilter } from '@nestjs/graphql'; | ||||
|  | ||||
| import { | ||||
|   AuthenticationError, | ||||
|   BaseGraphQLError, | ||||
|   ForbiddenError, | ||||
| } from 'src/filters/utils/graphql-errors.util'; | ||||
|  | ||||
| const graphQLPredefinedExceptions = { | ||||
|   401: AuthenticationError, | ||||
|   403: ForbiddenError, | ||||
| }; | ||||
|  | ||||
| @Catch(HttpException) | ||||
| export class HttpExceptionFilter | ||||
|   implements GqlExceptionFilter<HttpException, BaseGraphQLError | null> | ||||
| { | ||||
|   catch(exception: HttpException, host: ArgumentsHost) { | ||||
|     if (host.getType<GqlContextType>() !== 'graphql') { | ||||
|       return null; | ||||
|     } | ||||
|  | ||||
|     let error: BaseGraphQLError; | ||||
|  | ||||
|     if (exception.getStatus() in graphQLPredefinedExceptions) { | ||||
|       error = new graphQLPredefinedExceptions[exception.getStatus()]( | ||||
|         exception.message, | ||||
|       ); | ||||
|     } else { | ||||
|       error = new BaseGraphQLError( | ||||
|         exception.message, | ||||
|         exception.getStatus().toString(), | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     error.stack = exception.stack; | ||||
|     error.extensions['response'] = exception.getResponse(); | ||||
|  | ||||
|     return error; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										24
									
								
								server/src/filters/typeorm-exception.filter.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								server/src/filters/typeorm-exception.filter.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| import { ArgumentsHost, Catch } from '@nestjs/common'; | ||||
| import { GqlContextType, GqlExceptionFilter } from '@nestjs/graphql'; | ||||
|  | ||||
| import { TypeORMError } from 'typeorm'; | ||||
|  | ||||
| import { BaseGraphQLError } from 'src/filters/utils/graphql-errors.util'; | ||||
|  | ||||
| @Catch(TypeORMError) | ||||
| export class TypeOrmExceptionFilter | ||||
|   implements GqlExceptionFilter<TypeORMError, BaseGraphQLError | null> | ||||
| { | ||||
|   catch(exception: TypeORMError, host: ArgumentsHost) { | ||||
|     if (host.getType<GqlContextType>() !== 'graphql') { | ||||
|       return null; | ||||
|     } | ||||
|  | ||||
|     const error = new BaseGraphQLError(exception.name, 'INTERNAL_SERVER_ERROR'); | ||||
|  | ||||
|     error.stack = exception.stack; | ||||
|     error.extensions['response'] = exception.message; | ||||
|  | ||||
|     return error; | ||||
|   } | ||||
| } | ||||
| @@ -2,11 +2,13 @@ | ||||
| import { Injectable, LogLevel } from '@nestjs/common'; | ||||
| import { ConfigService } from '@nestjs/config'; | ||||
|  | ||||
| import { LoggerDriverType } from 'src/integrations/logger/interfaces'; | ||||
| import { ExceptionHandlerDriver } from 'src/integrations/exception-handler/interfaces'; | ||||
| import { StorageDriverType } from 'src/integrations/file-storage/interfaces'; | ||||
| import { MessageQueueDriverType } from 'src/integrations/message-queue/interfaces'; | ||||
|  | ||||
| import { AwsRegion } from './interfaces/aws-region.interface'; | ||||
| import { StorageType } from './interfaces/storage.interface'; | ||||
| import { SupportDriver } from './interfaces/support.interface'; | ||||
| import { LoggerDriver } from './interfaces/logger.interface'; | ||||
| import { MessageQueueType } from './interfaces/message-queue.interface'; | ||||
|  | ||||
| @Injectable() | ||||
| export class EnvironmentService { | ||||
| @@ -109,16 +111,17 @@ export class EnvironmentService { | ||||
|     return this.configService.get<string>('AUTH_GOOGLE_CALLBACK_URL'); | ||||
|   } | ||||
|  | ||||
|   getStorageType(): StorageType { | ||||
|   getStorageDriverType(): StorageDriverType { | ||||
|     return ( | ||||
|       this.configService.get<StorageType>('STORAGE_TYPE') ?? StorageType.Local | ||||
|       this.configService.get<StorageDriverType>('STORAGE_TYPE') ?? | ||||
|       StorageDriverType.Local | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   getMessageQueueType(): MessageQueueType { | ||||
|   getMessageQueueDriverType(): MessageQueueDriverType { | ||||
|     return ( | ||||
|       this.configService.get<MessageQueueType>('MESSAGE_QUEUE_TYPE') ?? | ||||
|       MessageQueueType.PgBoss | ||||
|       this.configService.get<MessageQueueDriverType>('MESSAGE_QUEUE_TYPE') ?? | ||||
|       MessageQueueDriverType.PgBoss | ||||
|     ); | ||||
|   } | ||||
|  | ||||
| @@ -154,9 +157,18 @@ export class EnvironmentService { | ||||
|     return this.configService.get<string>('SUPPORT_FRONT_HMAC_KEY'); | ||||
|   } | ||||
|  | ||||
|   getLoggerDriver(): string { | ||||
|   getLoggerDriverType(): LoggerDriverType { | ||||
|     return ( | ||||
|       this.configService.get<string>('LOGGER_DRIVER') ?? LoggerDriver.Console | ||||
|       this.configService.get<LoggerDriverType>('LOGGER_DRIVER') ?? | ||||
|       LoggerDriverType.Console | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   getExceptionHandlerDriverType(): ExceptionHandlerDriver { | ||||
|     return ( | ||||
|       this.configService.get<ExceptionHandlerDriver>( | ||||
|         'EXCEPTION_HANDLER_DRIVER', | ||||
|       ) ?? ExceptionHandlerDriver.Console | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -14,15 +14,16 @@ import { | ||||
|  | ||||
| import { assert } from 'src/utils/assert'; | ||||
| import { CastToStringArray } from 'src/integrations/environment/decorators/cast-to-string-array.decorator'; | ||||
| import { ExceptionHandlerDriver } from 'src/integrations/exception-handler/interfaces'; | ||||
| import { StorageDriverType } from 'src/integrations/file-storage/interfaces'; | ||||
| import { LoggerDriverType } from 'src/integrations/logger/interfaces'; | ||||
|  | ||||
| import { IsDuration } from './decorators/is-duration.decorator'; | ||||
| import { StorageType } from './interfaces/storage.interface'; | ||||
| import { AwsRegion } from './interfaces/aws-region.interface'; | ||||
| import { IsAWSRegion } from './decorators/is-aws-region.decorator'; | ||||
| import { CastToBoolean } from './decorators/cast-to-boolean.decorator'; | ||||
| import { SupportDriver } from './interfaces/support.interface'; | ||||
| import { CastToPositiveNumber } from './decorators/cast-to-positive-number.decorator'; | ||||
| import { LoggerDriver } from './interfaces/logger.interface'; | ||||
| import { CastToLogLevelArray } from './decorators/cast-to-log-level-array.decorator'; | ||||
|  | ||||
| export class EnvironmentVariables { | ||||
| @@ -110,20 +111,20 @@ export class EnvironmentVariables { | ||||
|   AUTH_GOOGLE_CALLBACK_URL?: string; | ||||
|  | ||||
|   // Storage | ||||
|   @IsEnum(StorageType) | ||||
|   @IsEnum(StorageDriverType) | ||||
|   @IsOptional() | ||||
|   STORAGE_TYPE?: StorageType; | ||||
|   STORAGE_TYPE?: StorageDriverType; | ||||
|  | ||||
|   @ValidateIf((env) => env.STORAGE_TYPE === StorageType.S3) | ||||
|   @ValidateIf((env) => env.STORAGE_TYPE === StorageDriverType.S3) | ||||
|   @IsAWSRegion() | ||||
|   STORAGE_S3_REGION?: AwsRegion; | ||||
|  | ||||
|   @ValidateIf((env) => env.STORAGE_TYPE === StorageType.S3) | ||||
|   @ValidateIf((env) => env.STORAGE_TYPE === StorageDriverType.S3) | ||||
|   @IsString() | ||||
|   STORAGE_S3_NAME?: string; | ||||
|  | ||||
|   @IsString() | ||||
|   @ValidateIf((env) => env.STORAGE_TYPE === StorageType.Local) | ||||
|   @ValidateIf((env) => env.STORAGE_TYPE === StorageDriverType.Local) | ||||
|   STORAGE_LOCAL_PATH?: string; | ||||
|  | ||||
|   // Support | ||||
| @@ -139,9 +140,13 @@ export class EnvironmentVariables { | ||||
|   @IsString() | ||||
|   SUPPORT_FRONT_HMAC_KEY?: string; | ||||
|  | ||||
|   @IsEnum(LoggerDriver) | ||||
|   @IsEnum(LoggerDriverType) | ||||
|   @IsOptional() | ||||
|   LOGGER_DRIVER?: LoggerDriver; | ||||
|   LOGGER_DRIVER?: LoggerDriverType; | ||||
|  | ||||
|   @IsEnum(ExceptionHandlerDriver) | ||||
|   @IsOptional() | ||||
|   EXCEPTION_HANDLER_DRIVER?: ExceptionHandlerDriver; | ||||
|  | ||||
|   @CastToLogLevelArray() | ||||
|   @IsOptional() | ||||
| @@ -151,7 +156,9 @@ export class EnvironmentVariables { | ||||
|   @IsOptional() | ||||
|   DEMO_WORKSPACE_IDS?: string[]; | ||||
|  | ||||
|   @ValidateIf((env) => env.LOGGER_DRIVER === LoggerDriver.Sentry) | ||||
|   @ValidateIf( | ||||
|     (env) => env.EXCEPTION_HANDLER_DRIVER === ExceptionHandlerDriver.Sentry, | ||||
|   ) | ||||
|   @IsString() | ||||
|   SENTRY_DSN?: string; | ||||
| } | ||||
|   | ||||
| @@ -1,4 +0,0 @@ | ||||
| export enum LoggerDriver { | ||||
|   Console = 'console', | ||||
|   Sentry = 'sentry', | ||||
| } | ||||
| @@ -1,3 +0,0 @@ | ||||
| export enum MemoryStorageType { | ||||
|   Local = 'local', | ||||
| } | ||||
| @@ -1,4 +0,0 @@ | ||||
| export enum MessageQueueType { | ||||
|   PgBoss = 'pg-boss', | ||||
|   BullMQ = 'bull-mq', | ||||
| } | ||||
| @@ -1,4 +0,0 @@ | ||||
| export enum StorageType { | ||||
|   S3 = 's3', | ||||
|   Local = 'local', | ||||
| } | ||||
| @@ -0,0 +1,17 @@ | ||||
| import { ExceptionHandlerDriverInterface } from 'src/integrations/exception-handler/interfaces'; | ||||
|  | ||||
| export class ExceptionHandlerConsoleDriver | ||||
|   implements ExceptionHandlerDriverInterface | ||||
| { | ||||
|   captureException(exception: unknown) { | ||||
|     console.group('Exception Captured'); | ||||
|     console.error(exception); | ||||
|     console.groupEnd(); | ||||
|   } | ||||
|  | ||||
|   captureMessage(message: string): void { | ||||
|     console.group('Message Captured'); | ||||
|     console.info(message); | ||||
|     console.groupEnd(); | ||||
|   } | ||||
| } | ||||
| @@ -0,0 +1,40 @@ | ||||
| import * as Sentry from '@sentry/node'; | ||||
| import { ProfilingIntegration } from '@sentry/profiling-node'; | ||||
|  | ||||
| import { | ||||
|   ExceptionHandlerDriverInterface, | ||||
|   ExceptionHandlerSentryDriverFactoryOptions, | ||||
| } from 'src/integrations/exception-handler/interfaces'; | ||||
|  | ||||
| export class ExceptionHandlerSentryDriver | ||||
|   implements ExceptionHandlerDriverInterface | ||||
| { | ||||
|   constructor(options: ExceptionHandlerSentryDriverFactoryOptions['options']) { | ||||
|     Sentry.init({ | ||||
|       dsn: options.dns, | ||||
|       integrations: [ | ||||
|         // enable HTTP calls tracing | ||||
|         new Sentry.Integrations.Http({ tracing: true }), | ||||
|         // enable Express.js middleware tracing | ||||
|         new Sentry.Integrations.Express({ app: options.serverInstance }), | ||||
|         new Sentry.Integrations.GraphQL(), | ||||
|         new Sentry.Integrations.Postgres({ | ||||
|           usePgNative: true, | ||||
|         }), | ||||
|         new ProfilingIntegration(), | ||||
|       ], | ||||
|       tracesSampleRate: 1.0, | ||||
|       profilesSampleRate: 1.0, | ||||
|       environment: options.debug ? 'development' : 'production', | ||||
|       debug: options.debug, | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   captureException(exception: Error) { | ||||
|     Sentry.captureException(exception); | ||||
|   } | ||||
|  | ||||
|   captureMessage(message: string) { | ||||
|     Sentry.captureMessage(message); | ||||
|   } | ||||
| } | ||||
| @@ -0,0 +1 @@ | ||||
| export const EXCEPTION_HANDLER_DRIVER = Symbol('EXCEPTION_HANDLER_DRIVER'); | ||||
| @@ -0,0 +1,25 @@ | ||||
| import { | ||||
|   ConfigurableModuleBuilder, | ||||
|   FactoryProvider, | ||||
|   ModuleMetadata, | ||||
| } from '@nestjs/common'; | ||||
|  | ||||
| import { ExceptionHandlerModuleOptions } from './interfaces'; | ||||
|  | ||||
| export const { | ||||
|   ConfigurableModuleClass, | ||||
|   MODULE_OPTIONS_TOKEN, | ||||
|   OPTIONS_TYPE, | ||||
|   ASYNC_OPTIONS_TYPE, | ||||
| } = new ConfigurableModuleBuilder<ExceptionHandlerModuleOptions>({ | ||||
|   moduleName: 'ExceptionHandlerModule', | ||||
| }) | ||||
|   .setClassMethodName('forRoot') | ||||
|   .build(); | ||||
|  | ||||
| export type ExceptionHandlerModuleAsyncOptions = { | ||||
|   useFactory: ( | ||||
|     ...args: any[] | ||||
|   ) => ExceptionHandlerModuleOptions | Promise<ExceptionHandlerModuleOptions>; | ||||
| } & Pick<ModuleMetadata, 'imports'> & | ||||
|   Pick<FactoryProvider, 'inject'>; | ||||
| @@ -0,0 +1,39 @@ | ||||
| import { HttpAdapterHost } from '@nestjs/core'; | ||||
|  | ||||
| import { EnvironmentService } from 'src/integrations/environment/environment.service'; | ||||
| import { OPTIONS_TYPE } from 'src/integrations/exception-handler/exception-handler.module-definition'; | ||||
| import { ExceptionHandlerDriver } from 'src/integrations/exception-handler/interfaces'; | ||||
|  | ||||
| /** | ||||
|  * ExceptionHandler Module factory | ||||
|  * @param environment | ||||
|  * @returns ExceptionHandlerModuleOptions | ||||
|  */ | ||||
| export const exceptionHandlerModuleFactory = async ( | ||||
|   environmentService: EnvironmentService, | ||||
|   adapterHost: HttpAdapterHost, | ||||
| ): Promise<typeof OPTIONS_TYPE> => { | ||||
|   const driverType = environmentService.getExceptionHandlerDriverType(); | ||||
|  | ||||
|   switch (driverType) { | ||||
|     case ExceptionHandlerDriver.Console: { | ||||
|       return { | ||||
|         type: ExceptionHandlerDriver.Console, | ||||
|       }; | ||||
|     } | ||||
|     case ExceptionHandlerDriver.Sentry: { | ||||
|       return { | ||||
|         type: ExceptionHandlerDriver.Sentry, | ||||
|         options: { | ||||
|           dns: environmentService.getSentryDSN() ?? '', | ||||
|           serverInstance: adapterHost.httpAdapter.getInstance(), | ||||
|           debug: environmentService.isDebugMode(), | ||||
|         }, | ||||
|       }; | ||||
|     } | ||||
|     default: | ||||
|       throw new Error( | ||||
|         `Invalid exception capturer driver type (${driverType}), check your .env file`, | ||||
|       ); | ||||
|   } | ||||
| }; | ||||
| @@ -0,0 +1,60 @@ | ||||
| import { DynamicModule, Global, Module } from '@nestjs/common'; | ||||
|  | ||||
| import { ExceptionHandlerSentryDriver } from 'src/integrations/exception-handler/drivers/sentry.driver'; | ||||
| import { ExceptionHandlerConsoleDriver } from 'src/integrations/exception-handler/drivers/console.driver'; | ||||
|  | ||||
| import { ExceptionHandlerService } from './exception-handler.service'; | ||||
| import { ExceptionHandlerDriver } from './interfaces'; | ||||
| import { EXCEPTION_HANDLER_DRIVER } from './exception-handler.constants'; | ||||
| import { | ||||
|   ConfigurableModuleClass, | ||||
|   OPTIONS_TYPE, | ||||
|   ASYNC_OPTIONS_TYPE, | ||||
| } from './exception-handler.module-definition'; | ||||
|  | ||||
| @Global() | ||||
| @Module({ | ||||
|   providers: [ExceptionHandlerService], | ||||
|   exports: [ExceptionHandlerService], | ||||
| }) | ||||
| export class ExceptionHandlerModule extends ConfigurableModuleClass { | ||||
|   static forRoot(options: typeof OPTIONS_TYPE): DynamicModule { | ||||
|     const provider = { | ||||
|       provide: EXCEPTION_HANDLER_DRIVER, | ||||
|       useValue: | ||||
|         options.type === ExceptionHandlerDriver.Console | ||||
|           ? new ExceptionHandlerConsoleDriver() | ||||
|           : new ExceptionHandlerSentryDriver(options.options), | ||||
|     }; | ||||
|     const dynamicModule = super.forRoot(options); | ||||
|  | ||||
|     return { | ||||
|       ...dynamicModule, | ||||
|       providers: [...(dynamicModule.providers ?? []), provider], | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   static forRootAsync(options: typeof ASYNC_OPTIONS_TYPE): DynamicModule { | ||||
|     const provider = { | ||||
|       provide: EXCEPTION_HANDLER_DRIVER, | ||||
|       useFactory: async (...args: any[]) => { | ||||
|         const config = await options?.useFactory?.(...args); | ||||
|  | ||||
|         if (!config) { | ||||
|           return null; | ||||
|         } | ||||
|  | ||||
|         return config.type === ExceptionHandlerDriver.Console | ||||
|           ? new ExceptionHandlerConsoleDriver() | ||||
|           : new ExceptionHandlerSentryDriver(config.options); | ||||
|       }, | ||||
|       inject: options.inject || [], | ||||
|     }; | ||||
|     const dynamicModule = super.forRootAsync(options); | ||||
|  | ||||
|     return { | ||||
|       ...dynamicModule, | ||||
|       providers: [...(dynamicModule.providers ?? []), provider], | ||||
|     }; | ||||
|   } | ||||
| } | ||||
| @@ -0,0 +1,27 @@ | ||||
| import { Test, TestingModule } from '@nestjs/testing'; | ||||
|  | ||||
| import { ExceptionHandlerService } from 'src/integrations/exception-handler/exception-handler.service'; | ||||
|  | ||||
| import { EXCEPTION_HANDLER_DRIVER } from './exception-handler.constants'; | ||||
|  | ||||
| describe('ExceptionHandlerService', () => { | ||||
|   let service: ExceptionHandlerService; | ||||
|  | ||||
|   beforeEach(async () => { | ||||
|     const module: TestingModule = await Test.createTestingModule({ | ||||
|       providers: [ | ||||
|         ExceptionHandlerService, | ||||
|         { | ||||
|           provide: EXCEPTION_HANDLER_DRIVER, | ||||
|           useValue: {}, | ||||
|         }, | ||||
|       ], | ||||
|     }).compile(); | ||||
|  | ||||
|     service = module.get<ExceptionHandlerService>(ExceptionHandlerService); | ||||
|   }); | ||||
|  | ||||
|   it('should be defined', () => { | ||||
|     expect(service).toBeDefined(); | ||||
|   }); | ||||
| }); | ||||
| @@ -0,0 +1,17 @@ | ||||
| import { Inject, Injectable } from '@nestjs/common'; | ||||
|  | ||||
| import { ExceptionHandlerDriverInterface } from 'src/integrations/exception-handler/interfaces'; | ||||
|  | ||||
| import { EXCEPTION_HANDLER_DRIVER } from './exception-handler.constants'; | ||||
|  | ||||
| @Injectable() | ||||
| export class ExceptionHandlerService { | ||||
|   constructor( | ||||
|     @Inject(EXCEPTION_HANDLER_DRIVER) | ||||
|     private driver: ExceptionHandlerDriverInterface, | ||||
|   ) {} | ||||
|  | ||||
|   captureException(exception: unknown) { | ||||
|     this.driver.captureException(exception); | ||||
|   } | ||||
| } | ||||
| @@ -0,0 +1,4 @@ | ||||
| export interface ExceptionHandlerDriverInterface { | ||||
|   captureException(exception: unknown): void; | ||||
|   captureMessage(message: string): void; | ||||
| } | ||||
| @@ -0,0 +1,23 @@ | ||||
| import { Router } from 'express'; | ||||
|  | ||||
| export enum ExceptionHandlerDriver { | ||||
|   Sentry = 'sentry', | ||||
|   Console = 'console', | ||||
| } | ||||
|  | ||||
| export interface ExceptionHandlerSentryDriverFactoryOptions { | ||||
|   type: ExceptionHandlerDriver.Sentry; | ||||
|   options: { | ||||
|     dns: string; | ||||
|     serverInstance: Router; | ||||
|     debug?: boolean; | ||||
|   }; | ||||
| } | ||||
|  | ||||
| export interface ExceptionHandlerDriverFactoryOptions { | ||||
|   type: ExceptionHandlerDriver.Console; | ||||
| } | ||||
|  | ||||
| export type ExceptionHandlerModuleOptions = | ||||
|   | ExceptionHandlerSentryDriverFactoryOptions | ||||
|   | ExceptionHandlerDriverFactoryOptions; | ||||
| @@ -0,0 +1,2 @@ | ||||
| export * from './exception-handler.interface'; | ||||
| export * from './exception-handler-driver.interface'; | ||||
| @@ -0,0 +1,53 @@ | ||||
| import { fromNodeProviderChain } from '@aws-sdk/credential-providers'; | ||||
|  | ||||
| import { EnvironmentService } from 'src/integrations/environment/environment.service'; | ||||
| import { | ||||
|   FileStorageModuleOptions, | ||||
|   StorageDriverType, | ||||
| } from 'src/integrations/file-storage/interfaces'; | ||||
|  | ||||
| /** | ||||
|  * FileStorage Module factory | ||||
|  * @param environment | ||||
|  * @returns FileStorageModuleOptions | ||||
|  */ | ||||
| export const fileStorageModuleFactory = async ( | ||||
|   environmentService: EnvironmentService, | ||||
| ): Promise<FileStorageModuleOptions> => { | ||||
|   const driverType = environmentService.getStorageDriverType(); | ||||
|  | ||||
|   switch (driverType) { | ||||
|     case StorageDriverType.Local: { | ||||
|       const storagePath = environmentService.getStorageLocalPath(); | ||||
|  | ||||
|       return { | ||||
|         type: StorageDriverType.Local, | ||||
|         options: { | ||||
|           storagePath: process.cwd() + '/' + storagePath, | ||||
|         }, | ||||
|       }; | ||||
|     } | ||||
|     case StorageDriverType.S3: { | ||||
|       const bucketName = environmentService.getStorageS3Name(); | ||||
|       const endpoint = environmentService.getStorageS3Endpoint(); | ||||
|       const region = environmentService.getStorageS3Region(); | ||||
|  | ||||
|       return { | ||||
|         type: StorageDriverType.S3, | ||||
|         options: { | ||||
|           bucketName: bucketName ?? '', | ||||
|           endpoint: endpoint, | ||||
|           credentials: fromNodeProviderChain({ | ||||
|             clientConfig: { region }, | ||||
|           }), | ||||
|           forcePathStyle: true, | ||||
|           region: region ?? '', | ||||
|         }, | ||||
|       }; | ||||
|     } | ||||
|     default: | ||||
|       throw new Error( | ||||
|         `Invalid storage driver type (${driverType}), check your .env file`, | ||||
|       ); | ||||
|   } | ||||
| }; | ||||
| @@ -1,17 +1,20 @@ | ||||
| import { FactoryProvider, ModuleMetadata } from '@nestjs/common'; | ||||
|  | ||||
| import { StorageType } from 'src/integrations/environment/interfaces/storage.interface'; | ||||
|  | ||||
| import { S3DriverOptions } from 'src/integrations/file-storage/drivers/s3.driver'; | ||||
| import { LocalDriverOptions } from 'src/integrations/file-storage/drivers/local.driver'; | ||||
|  | ||||
| export enum StorageDriverType { | ||||
|   S3 = 's3', | ||||
|   Local = 'local', | ||||
| } | ||||
|  | ||||
| export interface S3DriverFactoryOptions { | ||||
|   type: StorageType.S3; | ||||
|   type: StorageDriverType.S3; | ||||
|   options: S3DriverOptions; | ||||
| } | ||||
|  | ||||
| export interface LocalDriverFactoryOptions { | ||||
|   type: StorageType.Local; | ||||
|   type: StorageDriverType.Local; | ||||
|   options: LocalDriverOptions; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,134 +1,17 @@ | ||||
| import { Module } from '@nestjs/common'; | ||||
| import { HttpAdapterHost } from '@nestjs/core'; | ||||
|  | ||||
| import { fromNodeProviderChain } from '@aws-sdk/credential-providers'; | ||||
| import { ExceptionHandlerModule } from 'src/integrations/exception-handler/exception-handler.module'; | ||||
| import { exceptionHandlerModuleFactory } from 'src/integrations/exception-handler/exception-handler.module-factory'; | ||||
| import { fileStorageModuleFactory } from 'src/integrations/file-storage/file-storage.module-factory'; | ||||
| import { loggerModuleFactory } from 'src/integrations/logger/logger.module-factory'; | ||||
| import { messageQueueModuleFactory } from 'src/integrations/message-queue/message-queue.module-factory'; | ||||
|  | ||||
| import { EnvironmentModule } from './environment/environment.module'; | ||||
| import { EnvironmentService } from './environment/environment.service'; | ||||
| import { FileStorageModule } from './file-storage/file-storage.module'; | ||||
| import { FileStorageModuleOptions } from './file-storage/interfaces'; | ||||
| import { StorageType } from './environment/interfaces/storage.interface'; | ||||
| import { LoggerModule } from './logger/logger.module'; | ||||
| import { LoggerModuleOptions } from './logger/interfaces'; | ||||
| import { LoggerDriver } from './environment/interfaces/logger.interface'; | ||||
| import { MessageQueueModule } from './message-queue/message-queue.module'; | ||||
| import { MessageQueueModuleOptions } from './message-queue/interfaces'; | ||||
| import { MessageQueueType } from './environment/interfaces/message-queue.interface'; | ||||
|  | ||||
| /** | ||||
|  * FileStorage Module factory | ||||
|  * @param environment | ||||
|  * @returns FileStorageModuleOptions | ||||
|  */ | ||||
| const fileStorageModuleFactory = async ( | ||||
|   environmentService: EnvironmentService, | ||||
| ): Promise<FileStorageModuleOptions> => { | ||||
|   const type = environmentService.getStorageType(); | ||||
|  | ||||
|   switch (type) { | ||||
|     case StorageType.Local: { | ||||
|       const storagePath = environmentService.getStorageLocalPath(); | ||||
|  | ||||
|       return { | ||||
|         type: StorageType.Local, | ||||
|         options: { | ||||
|           storagePath: process.cwd() + '/' + storagePath, | ||||
|         }, | ||||
|       }; | ||||
|     } | ||||
|     case StorageType.S3: { | ||||
|       const bucketName = environmentService.getStorageS3Name(); | ||||
|       const endpoint = environmentService.getStorageS3Endpoint(); | ||||
|       const region = environmentService.getStorageS3Region(); | ||||
|  | ||||
|       return { | ||||
|         type: StorageType.S3, | ||||
|         options: { | ||||
|           bucketName: bucketName ?? '', | ||||
|           endpoint: endpoint, | ||||
|           credentials: fromNodeProviderChain({ | ||||
|             clientConfig: { region }, | ||||
|           }), | ||||
|           forcePathStyle: true, | ||||
|           region: region ?? '', | ||||
|         }, | ||||
|       }; | ||||
|     } | ||||
|     default: | ||||
|       throw new Error(`Invalid storage type (${type}), check your .env file`); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * Logger Module factory | ||||
|  * @param environment | ||||
|  * @returns LoggerModuleOptions | ||||
|  */ | ||||
| const loggerModuleFactory = async ( | ||||
|   environmentService: EnvironmentService, | ||||
| ): Promise<LoggerModuleOptions> => { | ||||
|   const type = environmentService.getLoggerDriver(); | ||||
|  | ||||
|   switch (type) { | ||||
|     case LoggerDriver.Console: { | ||||
|       return { | ||||
|         type: LoggerDriver.Console, | ||||
|         options: null, | ||||
|       }; | ||||
|     } | ||||
|     case LoggerDriver.Sentry: { | ||||
|       return { | ||||
|         type: LoggerDriver.Sentry, | ||||
|         options: { | ||||
|           sentryDNS: environmentService.getSentryDSN() ?? '', | ||||
|         }, | ||||
|       }; | ||||
|     } | ||||
|     default: | ||||
|       throw new Error(`Invalid logger type (${type}), check your .env file`); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * MessageQueue Module factory | ||||
|  * @param environment | ||||
|  * @returns MessageQueueModuleOptions | ||||
|  */ | ||||
| const messageQueueModuleFactory = async ( | ||||
|   environmentService: EnvironmentService, | ||||
| ): Promise<MessageQueueModuleOptions> => { | ||||
|   const type = environmentService.getMessageQueueType(); | ||||
|  | ||||
|   switch (type) { | ||||
|     case MessageQueueType.PgBoss: { | ||||
|       const connectionString = environmentService.getPGDatabaseUrl(); | ||||
|  | ||||
|       return { | ||||
|         type: MessageQueueType.PgBoss, | ||||
|         options: { | ||||
|           connectionString, | ||||
|         }, | ||||
|       }; | ||||
|     } | ||||
|     case MessageQueueType.BullMQ: { | ||||
|       const host = environmentService.getRedisHost(); | ||||
|       const port = environmentService.getRedisPort(); | ||||
|  | ||||
|       return { | ||||
|         type: MessageQueueType.BullMQ, | ||||
|         options: { | ||||
|           connection: { | ||||
|             host, | ||||
|             port, | ||||
|           }, | ||||
|         }, | ||||
|       }; | ||||
|     } | ||||
|     default: | ||||
|       throw new Error( | ||||
|         `Invalid message queue type (${type}), check your .env file`, | ||||
|       ); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| @Module({ | ||||
|   imports: [ | ||||
| @@ -145,6 +28,10 @@ const messageQueueModuleFactory = async ( | ||||
|       useFactory: messageQueueModuleFactory, | ||||
|       inject: [EnvironmentService], | ||||
|     }), | ||||
|     ExceptionHandlerModule.forRootAsync({ | ||||
|       useFactory: exceptionHandlerModuleFactory, | ||||
|       inject: [EnvironmentService, HttpAdapterHost], | ||||
|     }), | ||||
|   ], | ||||
|   exports: [], | ||||
|   providers: [], | ||||
|   | ||||
| @@ -1,53 +0,0 @@ | ||||
| import { LoggerService } from '@nestjs/common'; | ||||
|  | ||||
| import * as Sentry from '@sentry/node'; | ||||
|  | ||||
| export interface SentryDriverOptions { | ||||
|   sentryDNS: string; | ||||
| } | ||||
|  | ||||
| export class SentryDriver implements LoggerService { | ||||
|   constructor(options: SentryDriverOptions) { | ||||
|     Sentry.init({ | ||||
|       dsn: options.sentryDNS, | ||||
|       tracesSampleRate: 1.0, | ||||
|       profilesSampleRate: 1.0, | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   private logLevels = ['log', 'error', 'warning', 'debug', 'info']; | ||||
|  | ||||
|   setLogLevels(levels: string[]) { | ||||
|     this.logLevels = levels; | ||||
|   } | ||||
|  | ||||
|   log(message: any) { | ||||
|     if (this.logLevels.includes('log')) { | ||||
|       Sentry.captureMessage(message, { level: 'log' }); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   error(message: any) { | ||||
|     if (this.logLevels.includes('error')) { | ||||
|       Sentry.captureMessage(message, { level: 'error' }); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   warn(message: any) { | ||||
|     if (this.logLevels.includes('warn')) { | ||||
|       Sentry.captureMessage(message, { level: 'warning' }); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   debug?(message: any) { | ||||
|     if (this.logLevels.includes('debug')) { | ||||
|       Sentry.captureMessage(message, { level: 'debug' }); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   verbose?(message: any) { | ||||
|     if (this.logLevels.includes('verbose')) { | ||||
|       Sentry.captureMessage(message, { level: 'info' }); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @@ -1,17 +1,9 @@ | ||||
| import { LoggerDriver } from 'src/integrations/environment/interfaces/logger.interface'; | ||||
|  | ||||
| export interface SentryDriverFactoryOptions { | ||||
|   type: LoggerDriver.Sentry; | ||||
|   options: { | ||||
|     sentryDNS: string; | ||||
|   }; | ||||
| export enum LoggerDriverType { | ||||
|   Console = 'console', | ||||
| } | ||||
|  | ||||
| export interface ConsoleDriverFactoryOptions { | ||||
|   type: LoggerDriver.Console; | ||||
|   options: null; | ||||
|   type: LoggerDriverType.Console; | ||||
| } | ||||
|  | ||||
| export type LoggerModuleOptions = | ||||
|   | SentryDriverFactoryOptions | ||||
|   | ConsoleDriverFactoryOptions; | ||||
| export type LoggerModuleOptions = ConsoleDriverFactoryOptions; | ||||
|   | ||||
							
								
								
									
										28
									
								
								server/src/integrations/logger/logger.module-factory.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								server/src/integrations/logger/logger.module-factory.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| import { EnvironmentService } from 'src/integrations/environment/environment.service'; | ||||
| import { | ||||
|   LoggerModuleOptions, | ||||
|   LoggerDriverType, | ||||
| } from 'src/integrations/logger/interfaces'; | ||||
|  | ||||
| /** | ||||
|  * Logger Module factory | ||||
|  * @param environment | ||||
|  * @returns LoggerModuleOptions | ||||
|  */ | ||||
| export const loggerModuleFactory = async ( | ||||
|   environmentService: EnvironmentService, | ||||
| ): Promise<LoggerModuleOptions> => { | ||||
|   const driverType = environmentService.getLoggerDriverType(); | ||||
|  | ||||
|   switch (driverType) { | ||||
|     case LoggerDriverType.Console: { | ||||
|       return { | ||||
|         type: LoggerDriverType.Console, | ||||
|       }; | ||||
|     } | ||||
|     default: | ||||
|       throw new Error( | ||||
|         `Invalid logger driver type (${driverType}), check your .env file`, | ||||
|       ); | ||||
|   } | ||||
| }; | ||||
| @@ -1,50 +1,58 @@ | ||||
| import { DynamicModule, Global, ConsoleLogger } from '@nestjs/common'; | ||||
| import { DynamicModule, Global, ConsoleLogger, Module } from '@nestjs/common'; | ||||
|  | ||||
| import { LoggerDriver } from 'src/integrations/environment/interfaces/logger.interface'; | ||||
| import { LoggerDriverType } from 'src/integrations/logger/interfaces'; | ||||
|  | ||||
| import { LoggerService } from './logger.service'; | ||||
| import { LoggerModuleOptions } from './interfaces'; | ||||
| import { LOGGER_DRIVER } from './logger.constants'; | ||||
| import { LoggerModuleAsyncOptions } from './logger.module-definition'; | ||||
|  | ||||
| import { SentryDriver } from './drivers/sentry.driver'; | ||||
| import { | ||||
|   ASYNC_OPTIONS_TYPE, | ||||
|   ConfigurableModuleClass, | ||||
|   OPTIONS_TYPE, | ||||
| } from './logger.module-definition'; | ||||
|  | ||||
| @Global() | ||||
| export class LoggerModule { | ||||
|   static forRoot(options: LoggerModuleOptions): DynamicModule { | ||||
| @Module({ | ||||
|   providers: [LoggerService], | ||||
|   exports: [LoggerService], | ||||
| }) | ||||
| export class LoggerModule extends ConfigurableModuleClass { | ||||
|   static forRoot(options: typeof OPTIONS_TYPE): DynamicModule { | ||||
|     const provider = { | ||||
|       provide: LOGGER_DRIVER, | ||||
|       useValue: | ||||
|         options.type === LoggerDriver.Console | ||||
|         options.type === LoggerDriverType.Console | ||||
|           ? new ConsoleLogger() | ||||
|           : new SentryDriver(options.options), | ||||
|           : undefined, | ||||
|     }; | ||||
|     const dynamicModule = super.forRoot(options); | ||||
|  | ||||
|     return { | ||||
|       module: LoggerModule, | ||||
|       providers: [LoggerService, provider], | ||||
|       exports: [LoggerService], | ||||
|       ...dynamicModule, | ||||
|       providers: [...(dynamicModule.providers ?? []), provider], | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   static forRootAsync(options: LoggerModuleAsyncOptions): DynamicModule { | ||||
|   static forRootAsync(options: typeof ASYNC_OPTIONS_TYPE): DynamicModule { | ||||
|     const provider = { | ||||
|       provide: LOGGER_DRIVER, | ||||
|       useFactory: async (...args: any[]) => { | ||||
|         const config = await options.useFactory(...args); | ||||
|         const config = await options?.useFactory?.(...args); | ||||
|  | ||||
|         return config?.type === LoggerDriver.Console | ||||
|         if (!config) { | ||||
|           return null; | ||||
|         } | ||||
|  | ||||
|         return config?.type === LoggerDriverType.Console | ||||
|           ? new ConsoleLogger() | ||||
|           : new SentryDriver(config.options); | ||||
|           : undefined; | ||||
|       }, | ||||
|       inject: options.inject || [], | ||||
|     }; | ||||
|     const dynamicModule = super.forRootAsync(options); | ||||
|  | ||||
|     return { | ||||
|       module: LoggerModule, | ||||
|       imports: options.imports || [], | ||||
|       providers: [LoggerService, provider], | ||||
|       exports: [LoggerService], | ||||
|       ...dynamicModule, | ||||
|       providers: [...(dynamicModule.providers ?? []), provider], | ||||
|     }; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,12 +1,15 @@ | ||||
| import { FactoryProvider, ModuleMetadata } from '@nestjs/common'; | ||||
|  | ||||
| import { MemoryStorageType } from 'src/integrations/environment/interfaces/memory-storage.interface'; | ||||
| import { MemoryStorageSerializer } from 'src/integrations/memory-storage/serializers/interfaces/memory-storage-serializer.interface'; | ||||
|  | ||||
| import { LocalMemoryDriverOptions } from 'src/integrations/memory-storage/drivers/local.driver'; | ||||
|  | ||||
| export enum MemoryStorageDriverType { | ||||
|   Local = 'local', | ||||
| } | ||||
|  | ||||
| export interface LocalMemoryDriverFactoryOptions { | ||||
|   type: MemoryStorageType.Local; | ||||
|   type: MemoryStorageDriverType.Local; | ||||
|   options: LocalMemoryDriverOptions; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,11 +1,10 @@ | ||||
| import { DynamicModule, Global } from '@nestjs/common'; | ||||
|  | ||||
| import { MemoryStorageType } from 'src/integrations/environment/interfaces/memory-storage.interface'; | ||||
|  | ||||
| import { MemoryStorageDefaultSerializer } from 'src/integrations/memory-storage/serializers/default.serializer'; | ||||
| import { createMemoryStorageInjectionToken } from 'src/integrations/memory-storage/memory-storage.util'; | ||||
|  | ||||
| import { | ||||
|   MemoryStorageDriverType, | ||||
|   MemoryStorageModuleAsyncOptions, | ||||
|   MemoryStorageModuleOptions, | ||||
| } from './interfaces'; | ||||
| @@ -59,7 +58,7 @@ export class MemoryStorageModule { | ||||
|  | ||||
|   private static createStorageDriver(options: MemoryStorageModuleOptions) { | ||||
|     switch (options.type) { | ||||
|       case MemoryStorageType.Local: | ||||
|       case MemoryStorageDriverType.Local: | ||||
|         return new LocalMemoryDriver( | ||||
|           options.identifier, | ||||
|           options.options, | ||||
|   | ||||
| @@ -1,17 +1,20 @@ | ||||
| import { FactoryProvider, ModuleMetadata } from '@nestjs/common'; | ||||
|  | ||||
| import { MessageQueueType } from 'src/integrations/environment/interfaces/message-queue.interface'; | ||||
|  | ||||
| import { BullMQDriverOptions } from 'src/integrations/message-queue/drivers/bullmq.driver'; | ||||
| import { PgBossDriverOptions } from 'src/integrations/message-queue/drivers/pg-boss.driver'; | ||||
|  | ||||
| export enum MessageQueueDriverType { | ||||
|   PgBoss = 'pg-boss', | ||||
|   BullMQ = 'bull-mq', | ||||
| } | ||||
|  | ||||
| export interface PgBossDriverFactoryOptions { | ||||
|   type: MessageQueueType.PgBoss; | ||||
|   type: MessageQueueDriverType.PgBoss; | ||||
|   options: PgBossDriverOptions; | ||||
| } | ||||
|  | ||||
| export interface BullMQDriverFactoryOptions { | ||||
|   type: MessageQueueType.BullMQ; | ||||
|   type: MessageQueueDriverType.BullMQ; | ||||
|   options: BullMQDriverOptions; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,47 @@ | ||||
| import { EnvironmentService } from 'src/integrations/environment/environment.service'; | ||||
| import { | ||||
|   MessageQueueDriverType, | ||||
|   MessageQueueModuleOptions, | ||||
| } from 'src/integrations/message-queue/interfaces'; | ||||
|  | ||||
| /** | ||||
|  * MessageQueue Module factory | ||||
|  * @param environment | ||||
|  * @returns MessageQueueModuleOptions | ||||
|  */ | ||||
| export const messageQueueModuleFactory = async ( | ||||
|   environmentService: EnvironmentService, | ||||
| ): Promise<MessageQueueModuleOptions> => { | ||||
|   const driverType = environmentService.getMessageQueueDriverType(); | ||||
|  | ||||
|   switch (driverType) { | ||||
|     case MessageQueueDriverType.PgBoss: { | ||||
|       const connectionString = environmentService.getPGDatabaseUrl(); | ||||
|  | ||||
|       return { | ||||
|         type: MessageQueueDriverType.PgBoss, | ||||
|         options: { | ||||
|           connectionString, | ||||
|         }, | ||||
|       }; | ||||
|     } | ||||
|     case MessageQueueDriverType.BullMQ: { | ||||
|       const host = environmentService.getRedisHost(); | ||||
|       const port = environmentService.getRedisPort(); | ||||
|  | ||||
|       return { | ||||
|         type: MessageQueueDriverType.BullMQ, | ||||
|         options: { | ||||
|           connection: { | ||||
|             host, | ||||
|             port, | ||||
|           }, | ||||
|         }, | ||||
|       }; | ||||
|     } | ||||
|     default: | ||||
|       throw new Error( | ||||
|         `Invalid message queue driver type (${driverType}), check your .env file`, | ||||
|       ); | ||||
|   } | ||||
| }; | ||||
| @@ -1,9 +1,11 @@ | ||||
| import { DynamicModule, Global } from '@nestjs/common'; | ||||
|  | ||||
| import { MessageQueueDriver } from 'src/integrations/message-queue/drivers/interfaces/message-queue-driver.interface'; | ||||
| import { MessageQueueType } from 'src/integrations/environment/interfaces/message-queue.interface'; | ||||
|  | ||||
| import { MessageQueueModuleAsyncOptions } from 'src/integrations/message-queue/interfaces'; | ||||
| import { | ||||
|   MessageQueueDriverType, | ||||
|   MessageQueueModuleAsyncOptions, | ||||
| } from 'src/integrations/message-queue/interfaces'; | ||||
| import { | ||||
|   QUEUE_DRIVER, | ||||
|   MessageQueues, | ||||
| @@ -31,7 +33,7 @@ export class MessageQueueModule { | ||||
|         useFactory: async (...args: any[]) => { | ||||
|           const config = await options.useFactory(...args); | ||||
|  | ||||
|           if (config.type === MessageQueueType.PgBoss) { | ||||
|           if (config.type === MessageQueueDriverType.PgBoss) { | ||||
|             const boss = new PgBossDriver(config.options); | ||||
|  | ||||
|             await boss.init(); | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| import { Module } from '@nestjs/common'; | ||||
|  | ||||
| import { MemoryStorageType } from 'src/integrations/environment/interfaces/memory-storage.interface'; | ||||
|  | ||||
| import { MemoryStorageDriverType } from 'src/integrations/memory-storage/interfaces'; | ||||
| import { MemoryStorageModule } from 'src/integrations/memory-storage/memory-storage.module'; | ||||
| import { MemoryStorageJsonSerializer } from 'src/integrations/memory-storage/serializers/json.serializer'; | ||||
| import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity'; | ||||
| @@ -15,24 +14,24 @@ import { WorkspaceSchemaStorageService } from 'src/workspace/workspace-schema-st | ||||
|     WorkspaceCacheVersionModule, | ||||
|     MemoryStorageModule.forRoot({ | ||||
|       identifier: 'objectMetadataCollection', | ||||
|       type: MemoryStorageType.Local, | ||||
|       type: MemoryStorageDriverType.Local, | ||||
|       options: {}, | ||||
|       serializer: new MemoryStorageJsonSerializer<ObjectMetadataEntity[]>(), | ||||
|     }), | ||||
|     MemoryStorageModule.forRoot({ | ||||
|       identifier: 'typeDefs', | ||||
|       type: MemoryStorageType.Local, | ||||
|       type: MemoryStorageDriverType.Local, | ||||
|       options: {}, | ||||
|     }), | ||||
|     MemoryStorageModule.forRoot({ | ||||
|       identifier: 'usedScalarNames', | ||||
|       type: MemoryStorageType.Local, | ||||
|       type: MemoryStorageDriverType.Local, | ||||
|       options: {}, | ||||
|       serializer: new MemoryStorageJsonSerializer<string[]>(), | ||||
|     }), | ||||
|     MemoryStorageModule.forRoot({ | ||||
|       identifier: 'cacheVersion', | ||||
|       type: MemoryStorageType.Local, | ||||
|       type: MemoryStorageDriverType.Local, | ||||
|       options: {}, | ||||
|     }), | ||||
|   ], | ||||
|   | ||||
							
								
								
									
										402
									
								
								server/yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										402
									
								
								server/yarn.lock
									
									
									
									
									
								
							| @@ -1309,6 +1309,11 @@ | ||||
|   dependencies: | ||||
|     prettier "^3.1.0" | ||||
|  | ||||
| "@gar/promisify@^1.1.3": | ||||
|   version "1.1.3" | ||||
|   resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" | ||||
|   integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== | ||||
|  | ||||
| "@golevelup/nestjs-discovery@4.0.0": | ||||
|   version "4.0.0" | ||||
|   resolved "https://registry.yarnpkg.com/@golevelup/nestjs-discovery/-/nestjs-discovery-4.0.0.tgz#3428f0b620b51e4d425bc9e41cc8f2f338472dc1" | ||||
| @@ -1991,6 +1996,22 @@ | ||||
|     "@nodelib/fs.scandir" "2.1.5" | ||||
|     fastq "^1.6.0" | ||||
|  | ||||
| "@npmcli/fs@^2.1.0": | ||||
|   version "2.1.2" | ||||
|   resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-2.1.2.tgz#a9e2541a4a2fec2e69c29b35e6060973da79b865" | ||||
|   integrity sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ== | ||||
|   dependencies: | ||||
|     "@gar/promisify" "^1.1.3" | ||||
|     semver "^7.3.5" | ||||
|  | ||||
| "@npmcli/move-file@^2.0.0": | ||||
|   version "2.0.1" | ||||
|   resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-2.0.1.tgz#26f6bdc379d87f75e55739bab89db525b06100e4" | ||||
|   integrity sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ== | ||||
|   dependencies: | ||||
|     mkdirp "^1.0.4" | ||||
|     rimraf "^3.0.2" | ||||
|  | ||||
| "@nuxtjs/opencollective@0.3.2": | ||||
|   version "0.3.2" | ||||
|   resolved "https://registry.yarnpkg.com/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz#620ce1044f7ac77185e825e1936115bb38e2681c" | ||||
| @@ -2113,6 +2134,15 @@ | ||||
|     "@sentry/types" "7.84.0" | ||||
|     "@sentry/utils" "7.84.0" | ||||
|  | ||||
| "@sentry-internal/tracing@7.86.0": | ||||
|   version "7.86.0" | ||||
|   resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.86.0.tgz#657e80eb7d08d1030393902c1a7bc47fc39ccb2d" | ||||
|   integrity sha512-b4dUsNWlPWRwakGwR7bhOkqiFlqQszH1hhVFwrm/8s3kqEBZ+E4CeIfCvuHBHQ1cM/fx55xpXX/BU163cy+3iQ== | ||||
|   dependencies: | ||||
|     "@sentry/core" "7.86.0" | ||||
|     "@sentry/types" "7.86.0" | ||||
|     "@sentry/utils" "7.86.0" | ||||
|  | ||||
| "@sentry/core@7.84.0": | ||||
|   version "7.84.0" | ||||
|   resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.84.0.tgz#01d33fc452044ffd8ea57b20f60304b9cfa2b9e1" | ||||
| @@ -2121,6 +2151,23 @@ | ||||
|     "@sentry/types" "7.84.0" | ||||
|     "@sentry/utils" "7.84.0" | ||||
|  | ||||
| "@sentry/core@7.86.0", "@sentry/core@^7.76.0": | ||||
|   version "7.86.0" | ||||
|   resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.86.0.tgz#d01f538783dee9a0d79141a63145392ad2c1cb89" | ||||
|   integrity sha512-SbLvqd1bRYzhDS42u7GMnmbDMfth/zRiLElQWbLK/shmuZzTcfQSwNNdF4Yj+VfjOkqPFgGmICHSHVUc9dh01g== | ||||
|   dependencies: | ||||
|     "@sentry/types" "7.86.0" | ||||
|     "@sentry/utils" "7.86.0" | ||||
|  | ||||
| "@sentry/hub@^7.76.0": | ||||
|   version "7.86.0" | ||||
|   resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-7.86.0.tgz#58f157a8b4dad8ba5b9f70004c5b8368c2a0b676" | ||||
|   integrity sha512-tS9g+yoD/Zs4OS/gCO4/ccT0m90o3brkCIm/gRzPqI5dq2hEE1qn8bF7HM/vLQARM+bsmTEzPzZy19104U5Btg== | ||||
|   dependencies: | ||||
|     "@sentry/core" "7.86.0" | ||||
|     "@sentry/types" "7.86.0" | ||||
|     "@sentry/utils" "7.86.0" | ||||
|  | ||||
| "@sentry/node@^7.66.0": | ||||
|   version "7.84.0" | ||||
|   resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.84.0.tgz#c06167106796b2b83c0a9b52fa56f8ca820034ca" | ||||
| @@ -2132,6 +2179,31 @@ | ||||
|     "@sentry/utils" "7.84.0" | ||||
|     https-proxy-agent "^5.0.0" | ||||
|  | ||||
| "@sentry/node@^7.76.0": | ||||
|   version "7.86.0" | ||||
|   resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.86.0.tgz#416db178aeb64f7895a23ae1c3d4d65ce51ed50a" | ||||
|   integrity sha512-cB1bn/LMn2Km97Y3hv63xwWxT50/G5ixGuSxTZ3dCQM6VDhmZoCuC5NGT3itVvaRd6upQXRZa5W0Zgyh0HXKig== | ||||
|   dependencies: | ||||
|     "@sentry-internal/tracing" "7.86.0" | ||||
|     "@sentry/core" "7.86.0" | ||||
|     "@sentry/types" "7.86.0" | ||||
|     "@sentry/utils" "7.86.0" | ||||
|     https-proxy-agent "^5.0.0" | ||||
|  | ||||
| "@sentry/profiling-node@^1.2.6": | ||||
|   version "1.2.6" | ||||
|   resolved "https://registry.yarnpkg.com/@sentry/profiling-node/-/profiling-node-1.2.6.tgz#e43494896657bcfd11e75480ed6def4172e86c21" | ||||
|   integrity sha512-WsXO7VmLze5wPWHpvoRZFTtN+wHw9lYWKZs4T2FwPmvfNVaScGJey/+Wp51aM47Yy12Gj9n/BpqFYDsUXRLMvw== | ||||
|   dependencies: | ||||
|     "@sentry/core" "^7.76.0" | ||||
|     "@sentry/hub" "^7.76.0" | ||||
|     "@sentry/node" "^7.76.0" | ||||
|     "@sentry/types" "^7.76.0" | ||||
|     "@sentry/utils" "^7.76.0" | ||||
|     detect-libc "^2.0.2" | ||||
|     node-abi "^3.47.0" | ||||
|     node-gyp "^9.4.0" | ||||
|  | ||||
| "@sentry/tracing@^7.66.0": | ||||
|   version "7.84.0" | ||||
|   resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.84.0.tgz#64fbd93ed771f8e19fb60b06968cc133e3c82ea0" | ||||
| @@ -2144,6 +2216,11 @@ | ||||
|   resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.84.0.tgz#e8db86c36c61659c3b2558f0aa8b6a073a756117" | ||||
|   integrity sha512-VqGLIF3JOUrk7yIXjLXJvAORkZL1e3dDX0Q1okRehwyt/5CRE+mdUTeJZkBo9P9mBwgMyvtwklzOGGrzjb4eMA== | ||||
|  | ||||
| "@sentry/types@7.86.0", "@sentry/types@^7.76.0": | ||||
|   version "7.86.0" | ||||
|   resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.86.0.tgz#56ed2f5b15e8130ea5ecfbbc4102d88eaa3b3a67" | ||||
|   integrity sha512-pGAt0+bMfWgo0KG2epthfNV4Wae03tURpoxNjGo5Fr4cXxvLTSijSAQ6rmmO4bXBJ7+rErEjX30g30o/eEdP9g== | ||||
|  | ||||
| "@sentry/utils@7.84.0": | ||||
|   version "7.84.0" | ||||
|   resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.84.0.tgz#32861d922fa31e86dd2863a1d9dfc5a369e98952" | ||||
| @@ -2151,6 +2228,13 @@ | ||||
|   dependencies: | ||||
|     "@sentry/types" "7.84.0" | ||||
|  | ||||
| "@sentry/utils@7.86.0", "@sentry/utils@^7.76.0": | ||||
|   version "7.86.0" | ||||
|   resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.86.0.tgz#356ec19bf1e3e5c40935dd987fd15bee8c6b37ba" | ||||
|   integrity sha512-6PejFtw9VTFFy5vu0ks+U7Ozkqz+eMt+HN8AZKBKErYzX5/xs0kpkOcSRpu3ETdTYcZf8VAmLVgFgE2BE+3WuQ== | ||||
|   dependencies: | ||||
|     "@sentry/types" "7.86.0" | ||||
|  | ||||
| "@sinclair/typebox@^0.24.1": | ||||
|   version "0.24.51" | ||||
|   resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.51.tgz#645f33fe4e02defe26f2f5c0410e1c094eac7f5f" | ||||
| @@ -2672,6 +2756,11 @@ | ||||
|   resolved "https://registry.yarnpkg.com/@tokenizer/token/-/token-0.3.0.tgz#fe98a93fe789247e998c75e74e9c7c63217aa276" | ||||
|   integrity sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A== | ||||
|  | ||||
| "@tootallnate/once@2": | ||||
|   version "2.0.0" | ||||
|   resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" | ||||
|   integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== | ||||
|  | ||||
| "@tsconfig/node10@^1.0.7": | ||||
|   version "1.0.9" | ||||
|   resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" | ||||
| @@ -3499,7 +3588,7 @@ | ||||
|   resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" | ||||
|   integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== | ||||
|  | ||||
| abbrev@1: | ||||
| abbrev@1, abbrev@^1.0.0: | ||||
|   version "1.1.1" | ||||
|   resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" | ||||
|   integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== | ||||
| @@ -3537,13 +3626,20 @@ add@^2.0.6: | ||||
|   resolved "https://registry.yarnpkg.com/add/-/add-2.0.6.tgz#248f0a9f6e5a528ef2295dbeec30532130ae2235" | ||||
|   integrity sha512-j5QzrmsokwWWp6kUcJQySpbG+xfOBqqKnup3OIk1pz+kB/80SLorZ9V8zHFLO92Lcd+hbvq8bT+zOGoPkmBV0Q== | ||||
|  | ||||
| agent-base@6: | ||||
| agent-base@6, agent-base@^6.0.2: | ||||
|   version "6.0.2" | ||||
|   resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" | ||||
|   integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== | ||||
|   dependencies: | ||||
|     debug "4" | ||||
|  | ||||
| agentkeepalive@^4.2.1: | ||||
|   version "4.5.0" | ||||
|   resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923" | ||||
|   integrity sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew== | ||||
|   dependencies: | ||||
|     humanize-ms "^1.2.1" | ||||
|  | ||||
| aggregate-error@^3.0.0: | ||||
|   version "3.1.0" | ||||
|   resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" | ||||
| @@ -3753,6 +3849,14 @@ are-we-there-yet@^2.0.0: | ||||
|     delegates "^1.0.0" | ||||
|     readable-stream "^3.6.0" | ||||
|  | ||||
| are-we-there-yet@^3.0.0: | ||||
|   version "3.0.1" | ||||
|   resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz#679df222b278c64f2cdba1175cdc00b0d96164bd" | ||||
|   integrity sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg== | ||||
|   dependencies: | ||||
|     delegates "^1.0.0" | ||||
|     readable-stream "^3.6.0" | ||||
|  | ||||
| arg@^4.1.0: | ||||
|   version "4.1.3" | ||||
|   resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" | ||||
| @@ -4152,6 +4256,30 @@ bytes@3.1.2, bytes@^3.1.2: | ||||
|   resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" | ||||
|   integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== | ||||
|  | ||||
| cacache@^16.1.0: | ||||
|   version "16.1.3" | ||||
|   resolved "https://registry.yarnpkg.com/cacache/-/cacache-16.1.3.tgz#a02b9f34ecfaf9a78c9f4bc16fceb94d5d67a38e" | ||||
|   integrity sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ== | ||||
|   dependencies: | ||||
|     "@npmcli/fs" "^2.1.0" | ||||
|     "@npmcli/move-file" "^2.0.0" | ||||
|     chownr "^2.0.0" | ||||
|     fs-minipass "^2.1.0" | ||||
|     glob "^8.0.1" | ||||
|     infer-owner "^1.0.4" | ||||
|     lru-cache "^7.7.1" | ||||
|     minipass "^3.1.6" | ||||
|     minipass-collect "^1.0.2" | ||||
|     minipass-flush "^1.0.5" | ||||
|     minipass-pipeline "^1.2.4" | ||||
|     mkdirp "^1.0.4" | ||||
|     p-map "^4.0.0" | ||||
|     promise-inflight "^1.0.1" | ||||
|     rimraf "^3.0.2" | ||||
|     ssri "^9.0.0" | ||||
|     tar "^6.1.11" | ||||
|     unique-filename "^2.0.0" | ||||
|  | ||||
| call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.4, call-bind@^1.0.5: | ||||
|   version "1.0.5" | ||||
|   resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" | ||||
| @@ -4393,7 +4521,7 @@ color-string@^1.9.0: | ||||
|     color-name "^1.0.0" | ||||
|     simple-swizzle "^0.2.2" | ||||
|  | ||||
| color-support@^1.1.2: | ||||
| color-support@^1.1.2, color-support@^1.1.3: | ||||
|   version "1.1.3" | ||||
|   resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" | ||||
|   integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== | ||||
| @@ -4581,7 +4709,7 @@ debug@2.6.9: | ||||
|   dependencies: | ||||
|     ms "2.0.0" | ||||
|  | ||||
| debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: | ||||
| debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: | ||||
|   version "4.3.4" | ||||
|   resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" | ||||
|   integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== | ||||
| @@ -4790,6 +4918,13 @@ encodeurl@~1.0.2: | ||||
|   resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" | ||||
|   integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== | ||||
|  | ||||
| encoding@^0.1.13: | ||||
|   version "0.1.13" | ||||
|   resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" | ||||
|   integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== | ||||
|   dependencies: | ||||
|     iconv-lite "^0.6.2" | ||||
|  | ||||
| end-of-stream@^1.1.0, end-of-stream@^1.4.1: | ||||
|   version "1.4.4" | ||||
|   resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" | ||||
| @@ -4805,6 +4940,16 @@ enhanced-resolve@^5.0.0, enhanced-resolve@^5.14.0, enhanced-resolve@^5.7.0: | ||||
|     graceful-fs "^4.2.4" | ||||
|     tapable "^2.2.0" | ||||
|  | ||||
| env-paths@^2.2.0: | ||||
|   version "2.2.1" | ||||
|   resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" | ||||
|   integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== | ||||
|  | ||||
| err-code@^2.0.2: | ||||
|   version "2.0.3" | ||||
|   resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" | ||||
|   integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== | ||||
|  | ||||
| error-ex@^1.3.1: | ||||
|   version "1.3.2" | ||||
|   resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" | ||||
| @@ -5149,6 +5294,11 @@ expect@^28.0.0, expect@^28.1.3: | ||||
|     jest-message-util "^28.1.3" | ||||
|     jest-util "^28.1.3" | ||||
|  | ||||
| exponential-backoff@^3.1.1: | ||||
|   version "3.1.1" | ||||
|   resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" | ||||
|   integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== | ||||
|  | ||||
| express@4.18.2, express@^4.17.1: | ||||
|   version "4.18.2" | ||||
|   resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" | ||||
| @@ -5460,7 +5610,7 @@ fs-extra@^9.0.0: | ||||
|     jsonfile "^6.0.1" | ||||
|     universalify "^2.0.0" | ||||
|  | ||||
| fs-minipass@^2.0.0: | ||||
| fs-minipass@^2.0.0, fs-minipass@^2.1.0: | ||||
|   version "2.1.0" | ||||
|   resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" | ||||
|   integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== | ||||
| @@ -5517,6 +5667,20 @@ gauge@^3.0.0: | ||||
|     strip-ansi "^6.0.1" | ||||
|     wide-align "^1.1.2" | ||||
|  | ||||
| gauge@^4.0.3: | ||||
|   version "4.0.4" | ||||
|   resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.4.tgz#52ff0652f2bbf607a989793d53b751bef2328dce" | ||||
|   integrity sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg== | ||||
|   dependencies: | ||||
|     aproba "^1.0.3 || ^2.0.0" | ||||
|     color-support "^1.1.3" | ||||
|     console-control-strings "^1.1.0" | ||||
|     has-unicode "^2.0.1" | ||||
|     signal-exit "^3.0.7" | ||||
|     string-width "^4.2.3" | ||||
|     strip-ansi "^6.0.1" | ||||
|     wide-align "^1.1.5" | ||||
|  | ||||
| gensync@^1.0.0-beta.2: | ||||
|   version "1.0.0-beta.2" | ||||
|   resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" | ||||
| @@ -5598,7 +5762,7 @@ glob@^7.0.0, glob@^7.1.3, glob@^7.1.4: | ||||
|     once "^1.3.0" | ||||
|     path-is-absolute "^1.0.0" | ||||
|  | ||||
| glob@^8.0.3, glob@^8.1.0: | ||||
| glob@^8.0.1, glob@^8.0.3, glob@^8.1.0: | ||||
|   version "8.1.0" | ||||
|   resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" | ||||
|   integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== | ||||
| @@ -5657,7 +5821,7 @@ gopd@^1.0.1: | ||||
|   dependencies: | ||||
|     get-intrinsic "^1.1.3" | ||||
|  | ||||
| graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9: | ||||
| graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: | ||||
|   version "4.2.11" | ||||
|   resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" | ||||
|   integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== | ||||
| @@ -5794,6 +5958,11 @@ html-escaper@^2.0.0: | ||||
|   resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" | ||||
|   integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== | ||||
|  | ||||
| http-cache-semantics@^4.1.0: | ||||
|   version "4.1.1" | ||||
|   resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" | ||||
|   integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== | ||||
|  | ||||
| http-errors@2.0.0: | ||||
|   version "2.0.0" | ||||
|   resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" | ||||
| @@ -5816,6 +5985,15 @@ http-errors@^1.8.1: | ||||
|     statuses ">= 1.5.0 < 2" | ||||
|     toidentifier "1.0.1" | ||||
|  | ||||
| http-proxy-agent@^5.0.0: | ||||
|   version "5.0.0" | ||||
|   resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" | ||||
|   integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== | ||||
|   dependencies: | ||||
|     "@tootallnate/once" "2" | ||||
|     agent-base "6" | ||||
|     debug "4" | ||||
|  | ||||
| https-proxy-agent@^5.0.0: | ||||
|   version "5.0.1" | ||||
|   resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" | ||||
| @@ -5834,6 +6012,13 @@ human-signals@^2.1.0: | ||||
|   resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" | ||||
|   integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== | ||||
|  | ||||
| humanize-ms@^1.2.1: | ||||
|   version "1.2.1" | ||||
|   resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" | ||||
|   integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ== | ||||
|   dependencies: | ||||
|     ms "^2.0.0" | ||||
|  | ||||
| iconv-lite@0.4.24, iconv-lite@^0.4.24: | ||||
|   version "0.4.24" | ||||
|   resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" | ||||
| @@ -5841,6 +6026,13 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24: | ||||
|   dependencies: | ||||
|     safer-buffer ">= 2.1.2 < 3" | ||||
|  | ||||
| iconv-lite@^0.6.2: | ||||
|   version "0.6.3" | ||||
|   resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" | ||||
|   integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== | ||||
|   dependencies: | ||||
|     safer-buffer ">= 2.1.2 < 3.0.0" | ||||
|  | ||||
| ieee754@^1.1.13, ieee754@^1.2.1: | ||||
|   version "1.2.1" | ||||
|   resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" | ||||
| @@ -5877,6 +6069,11 @@ indent-string@^4.0.0: | ||||
|   resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" | ||||
|   integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== | ||||
|  | ||||
| infer-owner@^1.0.4: | ||||
|   version "1.0.4" | ||||
|   resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" | ||||
|   integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== | ||||
|  | ||||
| inflight@^1.0.4: | ||||
|   version "1.0.6" | ||||
|   resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" | ||||
| @@ -5987,6 +6184,11 @@ ioredis@^5.3.2: | ||||
|     redis-parser "^3.0.0" | ||||
|     standard-as-callback "^2.1.0" | ||||
|  | ||||
| ip@^2.0.0: | ||||
|   version "2.0.0" | ||||
|   resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" | ||||
|   integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== | ||||
|  | ||||
| ipaddr.js@1.9.1: | ||||
|   version "1.9.1" | ||||
|   resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" | ||||
| @@ -6084,6 +6286,11 @@ is-interactive@^1.0.0: | ||||
|   resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" | ||||
|   integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== | ||||
|  | ||||
| is-lambda@^1.0.1: | ||||
|   version "1.0.1" | ||||
|   resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" | ||||
|   integrity sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ== | ||||
|  | ||||
| is-negative-zero@^2.0.2: | ||||
|   version "2.0.2" | ||||
|   resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" | ||||
| @@ -6963,7 +7170,7 @@ lru-cache@^6.0.0: | ||||
|   dependencies: | ||||
|     yallist "^4.0.0" | ||||
|  | ||||
| lru-cache@^7.10.1, lru-cache@^7.14.1: | ||||
| lru-cache@^7.10.1, lru-cache@^7.14.1, lru-cache@^7.7.1: | ||||
|   version "7.18.3" | ||||
|   resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" | ||||
|   integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== | ||||
| @@ -7004,6 +7211,28 @@ make-error@1.x, make-error@^1.1.1: | ||||
|   resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" | ||||
|   integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== | ||||
|  | ||||
| make-fetch-happen@^10.0.3: | ||||
|   version "10.2.1" | ||||
|   resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz#f5e3835c5e9817b617f2770870d9492d28678164" | ||||
|   integrity sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w== | ||||
|   dependencies: | ||||
|     agentkeepalive "^4.2.1" | ||||
|     cacache "^16.1.0" | ||||
|     http-cache-semantics "^4.1.0" | ||||
|     http-proxy-agent "^5.0.0" | ||||
|     https-proxy-agent "^5.0.0" | ||||
|     is-lambda "^1.0.1" | ||||
|     lru-cache "^7.7.1" | ||||
|     minipass "^3.1.6" | ||||
|     minipass-collect "^1.0.2" | ||||
|     minipass-fetch "^2.0.3" | ||||
|     minipass-flush "^1.0.5" | ||||
|     minipass-pipeline "^1.2.4" | ||||
|     negotiator "^0.6.3" | ||||
|     promise-retry "^2.0.1" | ||||
|     socks-proxy-agent "^7.0.0" | ||||
|     ssri "^9.0.0" | ||||
|  | ||||
| makeerror@1.0.12: | ||||
|   version "1.0.12" | ||||
|   resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" | ||||
| @@ -7114,7 +7343,46 @@ minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.6: | ||||
|   resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" | ||||
|   integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== | ||||
|  | ||||
| minipass@^3.0.0: | ||||
| minipass-collect@^1.0.2: | ||||
|   version "1.0.2" | ||||
|   resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" | ||||
|   integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== | ||||
|   dependencies: | ||||
|     minipass "^3.0.0" | ||||
|  | ||||
| minipass-fetch@^2.0.3: | ||||
|   version "2.1.2" | ||||
|   resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-2.1.2.tgz#95560b50c472d81a3bc76f20ede80eaed76d8add" | ||||
|   integrity sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA== | ||||
|   dependencies: | ||||
|     minipass "^3.1.6" | ||||
|     minipass-sized "^1.0.3" | ||||
|     minizlib "^2.1.2" | ||||
|   optionalDependencies: | ||||
|     encoding "^0.1.13" | ||||
|  | ||||
| minipass-flush@^1.0.5: | ||||
|   version "1.0.5" | ||||
|   resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" | ||||
|   integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== | ||||
|   dependencies: | ||||
|     minipass "^3.0.0" | ||||
|  | ||||
| minipass-pipeline@^1.2.4: | ||||
|   version "1.2.4" | ||||
|   resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" | ||||
|   integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== | ||||
|   dependencies: | ||||
|     minipass "^3.0.0" | ||||
|  | ||||
| minipass-sized@^1.0.3: | ||||
|   version "1.0.3" | ||||
|   resolved "https://registry.yarnpkg.com/minipass-sized/-/minipass-sized-1.0.3.tgz#70ee5a7c5052070afacfbc22977ea79def353b70" | ||||
|   integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g== | ||||
|   dependencies: | ||||
|     minipass "^3.0.0" | ||||
|  | ||||
| minipass@^3.0.0, minipass@^3.1.1, minipass@^3.1.6: | ||||
|   version "3.3.6" | ||||
|   resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" | ||||
|   integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== | ||||
| @@ -7136,7 +7404,7 @@ minipass@^5.0.0: | ||||
|   resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c" | ||||
|   integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== | ||||
|  | ||||
| minizlib@^2.1.1: | ||||
| minizlib@^2.1.1, minizlib@^2.1.2: | ||||
|   version "2.1.2" | ||||
|   resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" | ||||
|   integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== | ||||
| @@ -7156,7 +7424,7 @@ mkdirp@^0.5.4: | ||||
|   dependencies: | ||||
|     minimist "^1.2.6" | ||||
|  | ||||
| mkdirp@^1.0.3: | ||||
| mkdirp@^1.0.3, mkdirp@^1.0.4: | ||||
|   version "1.0.4" | ||||
|   resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" | ||||
|   integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== | ||||
| @@ -7176,7 +7444,7 @@ ms@2.1.2: | ||||
|   resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" | ||||
|   integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== | ||||
|  | ||||
| ms@2.1.3, ms@^2.1.1: | ||||
| ms@2.1.3, ms@^2.0.0, ms@^2.1.1: | ||||
|   version "2.1.3" | ||||
|   resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" | ||||
|   integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== | ||||
| @@ -7280,6 +7548,13 @@ node-abi@^3.3.0: | ||||
|   dependencies: | ||||
|     semver "^7.3.5" | ||||
|  | ||||
| node-abi@^3.47.0: | ||||
|   version "3.52.0" | ||||
|   resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.52.0.tgz#ffba0a85f54e552547e5849015f40f9514d5ba7c" | ||||
|   integrity sha512-JJ98b02z16ILv7859irtXn4oUaFWADtvkzy2c0IAatNVX2Mc9Yoh8z6hZInn3QwvMEYhHuQloYi+TTQy67SIdQ== | ||||
|   dependencies: | ||||
|     semver "^7.3.5" | ||||
|  | ||||
| node-abort-controller@^3.0.1, node-abort-controller@^3.1.1: | ||||
|   version "3.1.1" | ||||
|   resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548" | ||||
| @@ -7314,6 +7589,23 @@ node-gyp-build-optional-packages@5.0.7: | ||||
|   resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.7.tgz#5d2632bbde0ab2f6e22f1bbac2199b07244ae0b3" | ||||
|   integrity sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w== | ||||
|  | ||||
| node-gyp@^9.4.0: | ||||
|   version "9.4.1" | ||||
|   resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-9.4.1.tgz#8a1023e0d6766ecb52764cc3a734b36ff275e185" | ||||
|   integrity sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ== | ||||
|   dependencies: | ||||
|     env-paths "^2.2.0" | ||||
|     exponential-backoff "^3.1.1" | ||||
|     glob "^7.1.4" | ||||
|     graceful-fs "^4.2.6" | ||||
|     make-fetch-happen "^10.0.3" | ||||
|     nopt "^6.0.0" | ||||
|     npmlog "^6.0.0" | ||||
|     rimraf "^3.0.2" | ||||
|     semver "^7.3.5" | ||||
|     tar "^6.1.2" | ||||
|     which "^2.0.2" | ||||
|  | ||||
| node-int64@^0.4.0: | ||||
|   version "0.4.0" | ||||
|   resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" | ||||
| @@ -7331,6 +7623,13 @@ nopt@^5.0.0: | ||||
|   dependencies: | ||||
|     abbrev "1" | ||||
|  | ||||
| nopt@^6.0.0: | ||||
|   version "6.0.0" | ||||
|   resolved "https://registry.yarnpkg.com/nopt/-/nopt-6.0.0.tgz#245801d8ebf409c6df22ab9d95b65e1309cdb16d" | ||||
|   integrity sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g== | ||||
|   dependencies: | ||||
|     abbrev "^1.0.0" | ||||
|  | ||||
| normalize-path@3.0.0, normalize-path@^3.0.0, normalize-path@~3.0.0: | ||||
|   version "3.0.0" | ||||
|   resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" | ||||
| @@ -7353,6 +7652,16 @@ npmlog@^5.0.1: | ||||
|     gauge "^3.0.0" | ||||
|     set-blocking "^2.0.0" | ||||
|  | ||||
| npmlog@^6.0.0: | ||||
|   version "6.0.2" | ||||
|   resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830" | ||||
|   integrity sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg== | ||||
|   dependencies: | ||||
|     are-we-there-yet "^3.0.0" | ||||
|     console-control-strings "^1.1.0" | ||||
|     gauge "^4.0.3" | ||||
|     set-blocking "^2.0.0" | ||||
|  | ||||
| oauth@0.9.x: | ||||
|   version "0.9.15" | ||||
|   resolved "https://registry.yarnpkg.com/oauth/-/oauth-0.9.15.tgz#bd1fefaf686c96b75475aed5196412ff60cfb9c1" | ||||
| @@ -7883,6 +8192,19 @@ process-nextick-args@~2.0.0: | ||||
|   resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" | ||||
|   integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== | ||||
|  | ||||
| promise-inflight@^1.0.1: | ||||
|   version "1.0.1" | ||||
|   resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" | ||||
|   integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g== | ||||
|  | ||||
| promise-retry@^2.0.1: | ||||
|   version "2.0.1" | ||||
|   resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" | ||||
|   integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== | ||||
|   dependencies: | ||||
|     err-code "^2.0.2" | ||||
|     retry "^0.12.0" | ||||
|  | ||||
| prompts@^2.0.1: | ||||
|   version "2.4.2" | ||||
|   resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" | ||||
| @@ -8121,6 +8443,11 @@ retry@0.13.1: | ||||
|   resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" | ||||
|   integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== | ||||
|  | ||||
| retry@^0.12.0: | ||||
|   version "0.12.0" | ||||
|   resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" | ||||
|   integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== | ||||
|  | ||||
| reusify@^1.0.4: | ||||
|   version "1.0.4" | ||||
|   resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" | ||||
| @@ -8195,7 +8522,7 @@ safe-regex-test@^1.0.0: | ||||
|     get-intrinsic "^1.1.3" | ||||
|     is-regex "^1.1.4" | ||||
|  | ||||
| "safer-buffer@>= 2.1.2 < 3": | ||||
| "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": | ||||
|   version "2.1.2" | ||||
|   resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" | ||||
|   integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== | ||||
| @@ -8386,6 +8713,28 @@ slash@^3.0.0: | ||||
|   resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" | ||||
|   integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== | ||||
|  | ||||
| smart-buffer@^4.2.0: | ||||
|   version "4.2.0" | ||||
|   resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" | ||||
|   integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== | ||||
|  | ||||
| socks-proxy-agent@^7.0.0: | ||||
|   version "7.0.0" | ||||
|   resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz#dc069ecf34436621acb41e3efa66ca1b5fed15b6" | ||||
|   integrity sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww== | ||||
|   dependencies: | ||||
|     agent-base "^6.0.2" | ||||
|     debug "^4.3.3" | ||||
|     socks "^2.6.2" | ||||
|  | ||||
| socks@^2.6.2: | ||||
|   version "2.7.1" | ||||
|   resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55" | ||||
|   integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ== | ||||
|   dependencies: | ||||
|     ip "^2.0.0" | ||||
|     smart-buffer "^4.2.0" | ||||
|  | ||||
| source-map-support@0.5.13: | ||||
|   version "0.5.13" | ||||
|   resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" | ||||
| @@ -8422,6 +8771,13 @@ sprintf-js@~1.0.2: | ||||
|   resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" | ||||
|   integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== | ||||
|  | ||||
| ssri@^9.0.0: | ||||
|   version "9.0.1" | ||||
|   resolved "https://registry.yarnpkg.com/ssri/-/ssri-9.0.1.tgz#544d4c357a8d7b71a19700074b6883fcb4eae057" | ||||
|   integrity sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q== | ||||
|   dependencies: | ||||
|     minipass "^3.1.1" | ||||
|  | ||||
| stack-utils@^2.0.3: | ||||
|   version "2.0.6" | ||||
|   resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" | ||||
| @@ -8688,7 +9044,7 @@ tar-stream@^3.1.5: | ||||
|     fast-fifo "^1.2.0" | ||||
|     streamx "^2.15.0" | ||||
|  | ||||
| tar@^6.1.11: | ||||
| tar@^6.1.11, tar@^6.1.2: | ||||
|   version "6.2.0" | ||||
|   resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.0.tgz#b14ce49a79cb1cd23bc9b016302dea5474493f73" | ||||
|   integrity sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ== | ||||
| @@ -9071,6 +9427,20 @@ undici-types@~5.26.4: | ||||
|   resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" | ||||
|   integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== | ||||
|  | ||||
| unique-filename@^2.0.0: | ||||
|   version "2.0.1" | ||||
|   resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-2.0.1.tgz#e785f8675a9a7589e0ac77e0b5c34d2eaeac6da2" | ||||
|   integrity sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A== | ||||
|   dependencies: | ||||
|     unique-slug "^3.0.0" | ||||
|  | ||||
| unique-slug@^3.0.0: | ||||
|   version "3.0.0" | ||||
|   resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-3.0.0.tgz#6d347cf57c8a7a7a6044aabd0e2d74e4d76dc7c9" | ||||
|   integrity sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w== | ||||
|   dependencies: | ||||
|     imurmurhash "^0.1.4" | ||||
|  | ||||
| universalify@^2.0.0: | ||||
|   version "2.0.1" | ||||
|   resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" | ||||
| @@ -9269,14 +9639,14 @@ which-typed-array@^1.1.11, which-typed-array@^1.1.13: | ||||
|     gopd "^1.0.1" | ||||
|     has-tostringtag "^1.0.0" | ||||
|  | ||||
| which@^2.0.1: | ||||
| which@^2.0.1, which@^2.0.2: | ||||
|   version "2.0.2" | ||||
|   resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" | ||||
|   integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== | ||||
|   dependencies: | ||||
|     isexe "^2.0.0" | ||||
|  | ||||
| wide-align@^1.1.2: | ||||
| wide-align@^1.1.2, wide-align@^1.1.5: | ||||
|   version "1.1.5" | ||||
|   resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" | ||||
|   integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Jérémy M
					Jérémy M