Merge pull request 'feat: jwt auth.' (#7) from feat-jwt-auth into master
Reviewed-on: #7
This commit is contained in:
		
							
								
								
									
										3
									
								
								.vscode/extensions.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.vscode/extensions.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
{
 | 
			
		||||
  "recommendations": []
 | 
			
		||||
}
 | 
			
		||||
@@ -16,5 +16,9 @@ db:
 | 
			
		||||
    prefix: fennec
 | 
			
		||||
  rabbitmq:
 | 
			
		||||
    uri: 'amqp://fennec:fennec@192.168.31.194:5672'
 | 
			
		||||
  etcd:
 | 
			
		||||
    hosts:
 | 
			
		||||
      - 'http://192.168.31.194:2379'
 | 
			
		||||
 | 
			
		||||
workspaces:
 | 
			
		||||
  root: '/Users/ivanli/Projects/fennec/workspaces'
 | 
			
		||||
							
								
								
									
										11378
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										11378
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										15
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								package.json
									
									
									
									
									
								
							@@ -1,8 +1,8 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "fennec-be",
 | 
			
		||||
  "version": "0.1.0",
 | 
			
		||||
  "description": "",
 | 
			
		||||
  "author": "",
 | 
			
		||||
  "version": "0.1.1",
 | 
			
		||||
  "description": "a ci/cd project.",
 | 
			
		||||
  "author": "Ivan Li\b<ivanli2048@gmail.com>",
 | 
			
		||||
  "private": true,
 | 
			
		||||
  "license": "UNLICENSED",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
@@ -22,7 +22,7 @@
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "@golevelup/nestjs-rabbitmq": "^1.16.1",
 | 
			
		||||
    "@nestjs/bull": "^0.3.1",
 | 
			
		||||
    "@nestjs-lib/auth": "^0.2.0",
 | 
			
		||||
    "@nestjs/common": "^7.5.1",
 | 
			
		||||
    "@nestjs/config": "^0.6.2",
 | 
			
		||||
    "@nestjs/core": "^7.5.1",
 | 
			
		||||
@@ -30,19 +30,19 @@
 | 
			
		||||
    "@nestjs/platform-express": "^7.5.1",
 | 
			
		||||
    "@nestjs/typeorm": "^7.1.5",
 | 
			
		||||
    "@types/amqplib": "^0.8.0",
 | 
			
		||||
    "@types/bull": "^3.15.0",
 | 
			
		||||
    "@types/ramda": "^0.27.38",
 | 
			
		||||
    "apollo-server-express": "^2.19.2",
 | 
			
		||||
    "bcrypt": "^5.0.0",
 | 
			
		||||
    "body-parser": "^1.19.0",
 | 
			
		||||
    "bull": "^3.20.1",
 | 
			
		||||
    "class-transformer": "^0.3.2",
 | 
			
		||||
    "class-validator": "^0.13.1",
 | 
			
		||||
    "debug": "^4.3.1",
 | 
			
		||||
    "graphql": "^15.5.0",
 | 
			
		||||
    "graphql-tools": "^7.0.2",
 | 
			
		||||
    "ioredis": "^4.25.0",
 | 
			
		||||
    "jose": "^3.14.0",
 | 
			
		||||
    "js-yaml": "^4.0.0",
 | 
			
		||||
    "nestjs-etcd": "^0.2.0",
 | 
			
		||||
    "nestjs-pino": "^1.4.0",
 | 
			
		||||
    "nestjs-redis": "^1.2.8",
 | 
			
		||||
    "observable-to-async-generator": "^1.0.1-rc",
 | 
			
		||||
@@ -96,6 +96,9 @@
 | 
			
		||||
    "collectCoverageFrom": [
 | 
			
		||||
      "**/*.(t|j)s"
 | 
			
		||||
    ],
 | 
			
		||||
    "moduleNameMapper": {
 | 
			
		||||
      "^jose/(.*)$": "<rootDir>/../node_modules/jose/dist/node/cjs/$1"
 | 
			
		||||
    },
 | 
			
		||||
    "coverageDirectory": "../coverage",
 | 
			
		||||
    "testEnvironment": "node"
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
import { CommonsModule } from './commons/commons.module';
 | 
			
		||||
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
 | 
			
		||||
import { ConfigModule, ConfigService } from '@nestjs/config';
 | 
			
		||||
import { GraphQLModule } from '@nestjs/graphql';
 | 
			
		||||
@@ -12,13 +13,13 @@ import { PipelineTasksModule } from './pipeline-tasks/pipeline-tasks.module';
 | 
			
		||||
import configuration from './commons/config/configuration';
 | 
			
		||||
import { RedisModule } from 'nestjs-redis';
 | 
			
		||||
import { WebhooksModule } from './webhooks/webhooks.module';
 | 
			
		||||
import { RawBodyMiddleware } from './commons/middlewares/raw-body.middleware';
 | 
			
		||||
import { RawBodyMiddleware } from './commons/middleware/raw-body.middleware';
 | 
			
		||||
import { GiteaWebhooksController } from './webhooks/gitea-webhooks.controller';
 | 
			
		||||
import { ParseBodyMiddleware } from './commons/middlewares/parse-body.middleware';
 | 
			
		||||
import { BullModule } from '@nestjs/bull';
 | 
			
		||||
import { ParseBodyMiddleware } from './commons/middleware/parse-body.middleware';
 | 
			
		||||
import { LoggerModule } from 'nestjs-pino';
 | 
			
		||||
 | 
			
		||||
import { EtcdModule } from 'nestjs-etcd';
 | 
			
		||||
import pinoPretty from 'pino-pretty';
 | 
			
		||||
import { fromPairs, map, pipe, toPairs } from 'ramda';
 | 
			
		||||
 | 
			
		||||
@Module({
 | 
			
		||||
  imports: [
 | 
			
		||||
@@ -63,16 +64,21 @@ import pinoPretty from 'pino-pretty';
 | 
			
		||||
        playground: true,
 | 
			
		||||
        autoSchemaFile: true,
 | 
			
		||||
        installSubscriptionHandlers: true,
 | 
			
		||||
      }),
 | 
			
		||||
      inject: [ConfigService],
 | 
			
		||||
    }),
 | 
			
		||||
    BullModule.forRootAsync({
 | 
			
		||||
      imports: [ConfigModule],
 | 
			
		||||
      useFactory: (configService: ConfigService) => ({
 | 
			
		||||
        redis: {
 | 
			
		||||
          host: configService.get<string>('db.redis.host', 'localhost'),
 | 
			
		||||
          port: configService.get<number>('db.redis.port', undefined),
 | 
			
		||||
          password: configService.get<string>('db.redis.password', undefined),
 | 
			
		||||
        context: ({ req, connection, ...args }) => {
 | 
			
		||||
          return connection ? { req: connection.context } : { req };
 | 
			
		||||
        },
 | 
			
		||||
        subscriptions: {
 | 
			
		||||
          onConnect: (connectionParams: Record<string, string>) => {
 | 
			
		||||
            const connectionParamsWithLowerKeys = pipe(
 | 
			
		||||
              toPairs,
 | 
			
		||||
              map(([key, value]) => [key.toLowerCase(), value]),
 | 
			
		||||
              fromPairs,
 | 
			
		||||
            )(connectionParams);
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
              headers: connectionParamsWithLowerKeys,
 | 
			
		||||
            };
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      }),
 | 
			
		||||
      inject: [ConfigService],
 | 
			
		||||
@@ -91,7 +97,15 @@ import pinoPretty from 'pino-pretty';
 | 
			
		||||
      }),
 | 
			
		||||
      inject: [ConfigService],
 | 
			
		||||
    }),
 | 
			
		||||
    EtcdModule.forRootAsync({
 | 
			
		||||
      imports: [ConfigModule],
 | 
			
		||||
      useFactory: (configService: ConfigService) => ({
 | 
			
		||||
        hosts: configService.get<string>('db.etcd.hosts', 'localhost:2379'),
 | 
			
		||||
      }),
 | 
			
		||||
      inject: [ConfigService],
 | 
			
		||||
    }),
 | 
			
		||||
    WebhooksModule,
 | 
			
		||||
    CommonsModule,
 | 
			
		||||
  ],
 | 
			
		||||
  controllers: [AppController],
 | 
			
		||||
  providers: [AppService, AppResolver],
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,11 @@
 | 
			
		||||
import { Module } from '@nestjs/common';
 | 
			
		||||
import { PasswordConverter } from './services/password-converter';
 | 
			
		||||
import { RedisMutexModule } from './redis-mutex/redis-mutex.module';
 | 
			
		||||
import { AuthModule } from '@nestjs-lib/auth';
 | 
			
		||||
 | 
			
		||||
@Module({
 | 
			
		||||
  imports: [RedisMutexModule, AuthModule],
 | 
			
		||||
  providers: [PasswordConverter],
 | 
			
		||||
  exports: [PasswordConverter, RedisMutexModule],
 | 
			
		||||
  imports: [RedisMutexModule],
 | 
			
		||||
  exports: [PasswordConverter, RedisMutexModule, AuthModule],
 | 
			
		||||
})
 | 
			
		||||
export class CommonsModule {}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,13 +6,18 @@ import {
 | 
			
		||||
  HttpStatus,
 | 
			
		||||
} from '@nestjs/common';
 | 
			
		||||
import { ApolloError } from 'apollo-server-errors';
 | 
			
		||||
import { PinoLogger, InjectPinoLogger } from 'nestjs-pino';
 | 
			
		||||
 | 
			
		||||
@Catch(HttpException)
 | 
			
		||||
export class HttpExceptionFilter implements ExceptionFilter {
 | 
			
		||||
  constructor(
 | 
			
		||||
    @InjectPinoLogger(HttpExceptionFilter.name)
 | 
			
		||||
    private readonly logger: PinoLogger,
 | 
			
		||||
  ) {}
 | 
			
		||||
  catch(exception: HttpException, host: ArgumentsHost) {
 | 
			
		||||
    switch (host.getType<'http' | 'graphql' | string>()) {
 | 
			
		||||
      case 'graphql': {
 | 
			
		||||
        const message = exception.message;
 | 
			
		||||
        const errorName = exception.message;
 | 
			
		||||
        const extensions: Record<string, any> = {};
 | 
			
		||||
        const err = exception.getResponse();
 | 
			
		||||
        if (typeof err === 'string') {
 | 
			
		||||
@@ -21,8 +26,10 @@ export class HttpExceptionFilter implements ExceptionFilter {
 | 
			
		||||
          Object.assign(extensions, (err as any).extension);
 | 
			
		||||
          extensions.message = (err as any).message;
 | 
			
		||||
        }
 | 
			
		||||
        extensions.error = errorName;
 | 
			
		||||
        this.logger.error(extensions);
 | 
			
		||||
        return new ApolloError(
 | 
			
		||||
          message,
 | 
			
		||||
          extensions.message,
 | 
			
		||||
          exception.getStatus().toString(),
 | 
			
		||||
          extensions,
 | 
			
		||||
        );
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
import { Test, TestingModule } from '@nestjs/testing';
 | 
			
		||||
import { RedisService } from 'nestjs-redis';
 | 
			
		||||
import { RedisMutexService } from './redis-mutex.service';
 | 
			
		||||
 | 
			
		||||
describe('RedisMutexService', () => {
 | 
			
		||||
@@ -6,7 +7,13 @@ describe('RedisMutexService', () => {
 | 
			
		||||
 | 
			
		||||
  beforeEach(async () => {
 | 
			
		||||
    const module: TestingModule = await Test.createTestingModule({
 | 
			
		||||
      providers: [RedisMutexService],
 | 
			
		||||
      providers: [
 | 
			
		||||
        RedisMutexService,
 | 
			
		||||
        {
 | 
			
		||||
          provide: RedisService,
 | 
			
		||||
          useValue: {},
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
    }).compile();
 | 
			
		||||
 | 
			
		||||
    service = module.get<RedisMutexService>(RedisMutexService);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
import { PinoLogger } from 'nestjs-pino';
 | 
			
		||||
import { ValidationPipe } from '@nestjs/common';
 | 
			
		||||
import { ConfigService } from '@nestjs/config';
 | 
			
		||||
import { NestFactory } from '@nestjs/core';
 | 
			
		||||
@@ -14,7 +15,8 @@ async function bootstrap() {
 | 
			
		||||
      transform: true,
 | 
			
		||||
    }),
 | 
			
		||||
  );
 | 
			
		||||
  app.useGlobalFilters(new HttpExceptionFilter());
 | 
			
		||||
  const httpExceptionFilterLogger = await app.resolve(PinoLogger);
 | 
			
		||||
  app.useGlobalFilters(new HttpExceptionFilter(httpExceptionFilterLogger));
 | 
			
		||||
  await app.listen(configService.get<number>('http.port'));
 | 
			
		||||
}
 | 
			
		||||
bootstrap();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,6 @@
 | 
			
		||||
import { Field, InputType, Int, ObjectType } from '@nestjs/graphql';
 | 
			
		||||
import { Type } from 'class-transformer';
 | 
			
		||||
import { IsInstance, isInstance, ValidateNested } from 'class-validator';
 | 
			
		||||
import { WorkUnit } from './work-unit.model';
 | 
			
		||||
 | 
			
		||||
@InputType('WorkUnitMetadataInput')
 | 
			
		||||
@@ -6,5 +8,9 @@ import { WorkUnit } from './work-unit.model';
 | 
			
		||||
export class WorkUnitMetadata {
 | 
			
		||||
  @Field(() => Int)
 | 
			
		||||
  version = 1;
 | 
			
		||||
 | 
			
		||||
  @Type(() => WorkUnit)
 | 
			
		||||
  @IsInstance(WorkUnit, { each: true })
 | 
			
		||||
  @ValidateNested({ each: true })
 | 
			
		||||
  units: WorkUnit[];
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
import { Field, InputType, ObjectType } from '@nestjs/graphql';
 | 
			
		||||
import { IsNotEmpty } from 'class-validator';
 | 
			
		||||
import {
 | 
			
		||||
  PipelineUnits,
 | 
			
		||||
  PipelineUnits as PipelineUnitTypes,
 | 
			
		||||
@@ -9,5 +10,7 @@ import {
 | 
			
		||||
export class WorkUnit {
 | 
			
		||||
  @Field(() => PipelineUnits)
 | 
			
		||||
  type: PipelineUnitTypes;
 | 
			
		||||
 | 
			
		||||
  @IsNotEmpty({ each: true })
 | 
			
		||||
  scripts: string[];
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -16,9 +16,11 @@ import {
 | 
			
		||||
} from './pipeline-tasks.constants';
 | 
			
		||||
import { PipelineTaskLogger } from './pipeline-task.logger';
 | 
			
		||||
import { PipelineTaskFlushService } from './pipeline-task-flush.service';
 | 
			
		||||
import { CommonsModule } from '../commons/commons.module';
 | 
			
		||||
 | 
			
		||||
@Module({
 | 
			
		||||
  imports: [
 | 
			
		||||
    CommonsModule,
 | 
			
		||||
    TypeOrmModule.forFeature([PipelineTask, Pipeline]),
 | 
			
		||||
    RedisModule,
 | 
			
		||||
    ReposModule,
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,9 @@ import { plainToClass } from 'class-transformer';
 | 
			
		||||
import { PipelineTaskLogger } from './pipeline-task.logger';
 | 
			
		||||
import { observableToAsyncIterable } from '@graphql-tools/utils';
 | 
			
		||||
import { PipelineTaskEvent } from './models/pipeline-task-event';
 | 
			
		||||
import { Roles, AccountRole } from '@nestjs-lib/auth';
 | 
			
		||||
 | 
			
		||||
@Roles(AccountRole.admin, AccountRole.super)
 | 
			
		||||
@Resolver()
 | 
			
		||||
export class PipelineTasksResolver {
 | 
			
		||||
  constructor(
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
import { Roles, AccountRole } from '@nestjs-lib/auth';
 | 
			
		||||
import { Query } from '@nestjs/graphql';
 | 
			
		||||
import {
 | 
			
		||||
  Args,
 | 
			
		||||
@@ -10,6 +11,7 @@ import { PipelineTasksService } from '../pipeline-tasks/pipeline-tasks.service';
 | 
			
		||||
import { Commit, LogFields } from '../repos/dtos/log-list.model';
 | 
			
		||||
import { PipelinesService } from './pipelines.service';
 | 
			
		||||
 | 
			
		||||
@Roles(AccountRole.admin, AccountRole.super)
 | 
			
		||||
@Resolver(() => Commit)
 | 
			
		||||
export class CommitLogsResolver {
 | 
			
		||||
  constructor(
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,13 @@
 | 
			
		||||
import { Type } from 'class-transformer';
 | 
			
		||||
import { InputType } from '@nestjs/graphql';
 | 
			
		||||
import { WorkUnitMetadata } from '../../pipeline-tasks/models/work-unit-metadata.model';
 | 
			
		||||
import {
 | 
			
		||||
  IsObject,
 | 
			
		||||
  IsInstance,
 | 
			
		||||
  IsOptional,
 | 
			
		||||
  IsString,
 | 
			
		||||
  IsUUID,
 | 
			
		||||
  MaxLength,
 | 
			
		||||
  ValidateNested,
 | 
			
		||||
} from 'class-validator';
 | 
			
		||||
 | 
			
		||||
@InputType({ isAbstract: true })
 | 
			
		||||
@@ -21,7 +23,9 @@ export class CreatePipelineInput {
 | 
			
		||||
  @MaxLength(32)
 | 
			
		||||
  name: string;
 | 
			
		||||
 | 
			
		||||
  @Type(() => WorkUnitMetadata)
 | 
			
		||||
  @IsOptional()
 | 
			
		||||
  @IsObject()
 | 
			
		||||
  @ValidateNested()
 | 
			
		||||
  @IsInstance(WorkUnitMetadata)
 | 
			
		||||
  workUnitMetadata: WorkUnitMetadata;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -8,9 +8,11 @@ import { PipelineTasksModule } from '../pipeline-tasks/pipeline-tasks.module';
 | 
			
		||||
import { ReposModule } from '../repos/repos.module';
 | 
			
		||||
import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq';
 | 
			
		||||
import { ConfigModule, ConfigService } from '@nestjs/config';
 | 
			
		||||
import { CommonsModule } from '../commons/commons.module';
 | 
			
		||||
 | 
			
		||||
@Module({
 | 
			
		||||
  imports: [
 | 
			
		||||
    CommonsModule,
 | 
			
		||||
    TypeOrmModule.forFeature([Pipeline]),
 | 
			
		||||
    PipelineTasksModule,
 | 
			
		||||
    RabbitMQModule.forRootAsync(RabbitMQModule, {
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,9 @@ import { UpdatePipelineInput } from './dtos/update-pipeline.input';
 | 
			
		||||
import { Pipeline } from './pipeline.entity';
 | 
			
		||||
import { PipelinesService } from './pipelines.service';
 | 
			
		||||
import { ListPipelineArgs } from './dtos/list-pipelines.args';
 | 
			
		||||
import { Roles, AccountRole } from '@nestjs-lib/auth';
 | 
			
		||||
 | 
			
		||||
@Roles(AccountRole.admin, AccountRole.super)
 | 
			
		||||
@Resolver()
 | 
			
		||||
export class PipelinesResolver {
 | 
			
		||||
  constructor(private readonly service: PipelinesService) {}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,9 +6,11 @@ import { Project } from './project.entity';
 | 
			
		||||
import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq';
 | 
			
		||||
import { ConfigModule, ConfigService } from '@nestjs/config';
 | 
			
		||||
import { EXCHANGE_PROJECT_FANOUT } from './projects.constants';
 | 
			
		||||
import { CommonsModule } from '../commons/commons.module';
 | 
			
		||||
 | 
			
		||||
@Module({
 | 
			
		||||
  imports: [
 | 
			
		||||
    CommonsModule,
 | 
			
		||||
    TypeOrmModule.forFeature([Project]),
 | 
			
		||||
    RabbitMQModule.forRootAsync(RabbitMQModule, {
 | 
			
		||||
      imports: [ConfigModule],
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,11 @@
 | 
			
		||||
import { AccountRole, Roles } from '@nestjs-lib/auth';
 | 
			
		||||
import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
 | 
			
		||||
import { CreateProjectInput } from './dtos/create-project.input';
 | 
			
		||||
import { UpdateProjectInput } from './dtos/update-project.input';
 | 
			
		||||
import { Project } from './project.entity';
 | 
			
		||||
import { ProjectsService } from './projects.service';
 | 
			
		||||
 | 
			
		||||
@Roles(AccountRole.admin, AccountRole.super)
 | 
			
		||||
@Resolver(() => Project)
 | 
			
		||||
export class ProjectsResolver {
 | 
			
		||||
  constructor(private readonly service: ProjectsService) {}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ import { readFile } from 'fs/promises';
 | 
			
		||||
import { getLoggerToken, PinoLogger } from 'nestjs-pino';
 | 
			
		||||
import { Nack } from '@golevelup/nestjs-rabbitmq';
 | 
			
		||||
import { getInstanceName } from '../commons/utils/rabbit-mq';
 | 
			
		||||
import { RedisMutexService } from '../commons/redis-mutex/redis-mutex.service';
 | 
			
		||||
 | 
			
		||||
const getTest1Project = () =>
 | 
			
		||||
  ({
 | 
			
		||||
@@ -52,6 +53,14 @@ describe('ReposService', () => {
 | 
			
		||||
          provide: getLoggerToken(ReposService.name),
 | 
			
		||||
          useValue: new PinoLogger({}),
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          provide: RedisMutexService,
 | 
			
		||||
          useValue: {
 | 
			
		||||
            lock: jest.fn(() =>
 | 
			
		||||
              Promise.resolve(() => Promise.resolve(undefined)),
 | 
			
		||||
            ),
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
    }).compile();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@
 | 
			
		||||
    "experimentalDecorators": true,
 | 
			
		||||
    "allowSyntheticDefaultImports": true,
 | 
			
		||||
    "target": "es2017",
 | 
			
		||||
    "lib": ["ES2021"],
 | 
			
		||||
    "sourceMap": true,
 | 
			
		||||
    "outDir": "./dist",
 | 
			
		||||
    "baseUrl": "./",
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user