diff --git a/.prettierrc b/.prettierrc index dcb7279..3ae619a 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,4 +1,9 @@ { + "arrowParens": "avoid", "singleQuote": true, - "trailingComma": "all" + "semi": false, + "tabWidth": 2, + "useTabs": false, + "trailingComma": "all", + "endOfLine": "auto" } \ No newline at end of file diff --git a/eslint.config.mjs b/eslint.config.mjs index 32465cc..99301a7 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,12 +1,12 @@ -// @ts-check import eslint from '@eslint/js'; +import * as eslintPluginImport from 'eslint-plugin-import'; import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; import globals from 'globals'; import tseslint from 'typescript-eslint'; export default tseslint.config( { - ignores: ['eslint.config.mjs'], + ignores: ['eslint.config.mjs', '.prettierrc'], }, eslint.configs.recommended, ...tseslint.configs.recommendedTypeChecked, @@ -26,10 +26,24 @@ export default tseslint.config( }, }, { + plugins: { + import: eslintPluginImport, + }, rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'warn', - '@typescript-eslint/no-unsafe-argument': 'warn' + 'prettier/prettier': 'error', + 'import/order': [ + 'error', + { + groups: [ + 'builtin', + 'external', + ['internal', 'parent', 'sibling'], + 'index', + 'object', + 'type' + ] + } + ] }, }, ); \ No newline at end of file diff --git a/package.json b/package.json index 7f25251..9673345 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { - "name": "qv-be", - "version": "0.0.1", - "description": "", - "author": "", + "name": "worldview-be", + "version": "0.1.0", + "description": "Worldview backend", + "author": "General Magic Contributors", "private": true, "license": "UNLICENSED", "scripts": { @@ -58,6 +58,7 @@ "@types/supertest": "^6.0.2", "eslint": "^9.18.0", "eslint-config-prettier": "^10.0.1", + "eslint-plugin-import": "^2.31.0", "eslint-plugin-prettier": "^5.2.2", "globals": "^15.14.0", "jest": "^29.7.0", @@ -90,4 +91,4 @@ "testEnvironment": "node" }, "packageManager": "yarn@1.22.19+sha1.4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447" -} +} \ No newline at end of file diff --git a/src/app.controller.spec.ts b/src/app.controller.spec.ts index d22f389..3e6017d 100644 --- a/src/app.controller.spec.ts +++ b/src/app.controller.spec.ts @@ -1,22 +1,22 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { AppController } from './app.controller'; -import { AppService } from './app.service'; +import { Test, TestingModule } from '@nestjs/testing' +import { AppController } from './app.controller' +import { AppService } from './app.service' describe('AppController', () => { - let appController: AppController; + let appController: AppController beforeEach(async () => { const app: TestingModule = await Test.createTestingModule({ controllers: [AppController], providers: [AppService], - }).compile(); + }).compile() - appController = app.get(AppController); - }); + appController = app.get(AppController) + }) describe('root', () => { it('should return "Hello World!"', () => { - expect(appController.getHello()).toBe('Hello World!'); - }); - }); -}); + expect(appController.getHello()).toBe('Hello World!') + }) + }) +}) diff --git a/src/app.controller.ts b/src/app.controller.ts index fd8cf02..e08747e 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -1,6 +1,6 @@ -import { Controller, Get } from '@nestjs/common'; -import { AppService } from './app.service'; -import { Public } from './auth/jwt-auth.guard'; +import { Controller, Get } from '@nestjs/common' +import { AppService } from './app.service' +import { Public } from './auth/jwt-auth.guard' @Controller() export class AppController { @@ -9,6 +9,6 @@ export class AppController { @Public() @Get() getHello(): string { - return this.appService.getHello(); + return this.appService.getHello() } } diff --git a/src/app.module.ts b/src/app.module.ts index 5765855..3236d03 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,13 +1,13 @@ -import { Module } from '@nestjs/common'; -import { AppController } from './app.controller'; -import { AppService } from './app.service'; -import { DatabaseService } from './database/database.service'; -import { DatabaseModule } from './database/database.module'; -import { PollModule } from './poll/poll.module'; -import { VoteModule } from './vote/vote.module'; -import { AuthModule } from './auth/auth.module'; -import { AuthService } from './auth/auth.service'; -import { UserModule } from './user/user.module'; +import { Module } from '@nestjs/common' +import { AppController } from './app.controller' +import { AppService } from './app.service' +import { DatabaseService } from './database/database.service' +import { DatabaseModule } from './database/database.module' +import { PollModule } from './poll/poll.module' +import { VoteModule } from './vote/vote.module' +import { AuthModule } from './auth/auth.module' +import { AuthService } from './auth/auth.service' +import { UserModule } from './user/user.module' @Module({ imports: [DatabaseModule, PollModule, VoteModule, UserModule, AuthModule], diff --git a/src/app.service.ts b/src/app.service.ts index 927d7cc..bc13055 100644 --- a/src/app.service.ts +++ b/src/app.service.ts @@ -1,8 +1,8 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common' @Injectable() export class AppService { getHello(): string { - return 'Hello World!'; + return 'Hello World!' } } diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts index a0edd2f..87c2635 100644 --- a/src/auth/auth.controller.ts +++ b/src/auth/auth.controller.ts @@ -1,24 +1,24 @@ -import { Body, Controller, Get, Post, Req, Res } from '@nestjs/common'; -import { Request, Response } from 'express'; -import { AuthService } from './auth.service'; -import { JwtService } from './jwt.service'; -import { Public } from './jwt-auth.guard'; -import { VerifyWorldIdDto } from './auth.dto'; +import { Body, Controller, Get, Post, Req, Res } from '@nestjs/common' +import { Request, Response } from 'express' import { InsufficientVerificationLevelException, SignatureVerificationFailureException, -} from 'src/common/exceptions'; -import { BadRequestException } from '@nestjs/common'; -import { VerificationLevel } from '@worldcoin/minikit-js'; +} from 'src/common/exceptions' +import { BadRequestException } from '@nestjs/common' +import { VerificationLevel } from '@worldcoin/minikit-js' +import { AuthService } from './auth.service' +import { JwtService } from './jwt.service' +import { Public } from './jwt-auth.guard' +import { VerifyWorldIdDto } from './auth.dto' function isHttps(req: Request) { return ( req.protocol === 'https' || req.headers['x-forwarded-proto'] === 'https' - ); + ) } @Controller('auth') export class AuthController { - private readonly systemVerificationLevel: VerificationLevel; + private readonly systemVerificationLevel: VerificationLevel constructor( private readonly authService: AuthService, @@ -26,21 +26,21 @@ export class AuthController { ) { this.systemVerificationLevel = (process.env.VERIFICATION_LEVEL?.toLowerCase() as VerificationLevel) || - VerificationLevel.Device; + VerificationLevel.Device } @Public() @Get('nonce') generateNonce(@Req() req: Request, @Res() res: Response) { - const nonce = this.authService.generateNonce(); + const nonce = this.authService.generateNonce() res.cookie('siwe', nonce, { httpOnly: true, secure: process.env.NODE_ENV === 'production' || isHttps(req), sameSite: 'none', maxAge: 2 * 60 * 1000, //2 minutes - }); + }) - return res.json({ nonce }); + return res.json({ nonce }) } @Public() @@ -56,29 +56,29 @@ export class AuthController { userDetails, nonce, verificationLevel, - } = body; + } = body - const isValid = await this.authService.verifyPayload(walletPayload, nonce); + const isValid = await this.authService.verifyPayload(walletPayload, nonce) if (!isValid) { - throw new SignatureVerificationFailureException(); + throw new SignatureVerificationFailureException() } if (verificationLevel !== this.systemVerificationLevel) { - throw new InsufficientVerificationLevelException(); + throw new InsufficientVerificationLevelException() } - const worldID = worldIdProof?.nullifier_hash; - const walletAddress = walletPayload?.address; + const worldID = worldIdProof?.nullifier_hash + const walletAddress = walletPayload?.address const user = await this.authService.createUser( worldID, userDetails.username, userDetails.profilePictureUrl, - ); + ) if (!user) { - throw new BadRequestException('Failed to create user'); + throw new BadRequestException('Failed to create user') } const token = this.jwtService.sign({ @@ -86,12 +86,12 @@ export class AuthController { worldID, verificationLevel, address: walletAddress, - }); + }) if (!token) { - throw new BadRequestException('Failed to generate token'); + throw new BadRequestException('Failed to generate token') } - return res.status(200).json({ isValid: true, token }); + return res.status(200).json({ isValid: true, token }) } } diff --git a/src/auth/auth.dto.ts b/src/auth/auth.dto.ts index 76a2cb0..53bef2f 100644 --- a/src/auth/auth.dto.ts +++ b/src/auth/auth.dto.ts @@ -4,53 +4,53 @@ import { IsString, IsEnum, ValidateNested, -} from 'class-validator'; +} from 'class-validator' import { MiniAppWalletAuthSuccessPayload, ISuccessResult, VerificationLevel, -} from '@worldcoin/minikit-js'; -import { Transform, Type } from 'class-transformer'; +} from '@worldcoin/minikit-js' +import { Transform, Type } from 'class-transformer' export class UserDetailsDto { @IsString() - username: string; + username: string @IsOptional() @IsString() - profilePictureUrl: string; + profilePictureUrl: string } export class VerifyWorldIdDto { @IsNotEmpty() @ValidateNested() @Type(() => Object) - walletPayload: MiniAppWalletAuthSuccessPayload; + walletPayload: MiniAppWalletAuthSuccessPayload @IsNotEmpty() @ValidateNested() @Type(() => Object) - worldIdProof: ISuccessResult; + worldIdProof: ISuccessResult @IsNotEmpty() @ValidateNested() @Type(() => Object) - userDetails: UserDetailsDto; + userDetails: UserDetailsDto @IsNotEmpty() @IsString() - nonce: string; + nonce: string @IsOptional() @Transform(({ value }) => { if (typeof value === 'string') { - value = value.toLowerCase(); + value = value.toLowerCase() } return Object.values(VerificationLevel).includes(value as VerificationLevel) ? (value as VerificationLevel) - : VerificationLevel.Device; + : VerificationLevel.Device }) @IsEnum(VerificationLevel, { message: `Verification level must be one of: ${Object.values(VerificationLevel).join(', ')}`, }) - verificationLevel: VerificationLevel = VerificationLevel.Device; + verificationLevel: VerificationLevel = VerificationLevel.Device } diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts index ff04f2f..326004d 100644 --- a/src/auth/auth.module.ts +++ b/src/auth/auth.module.ts @@ -1,7 +1,7 @@ -import { Module } from '@nestjs/common'; -import { AuthController } from './auth.controller'; -import { JwtService } from './jwt.service'; -import { AuthService } from './auth.service'; +import { Module } from '@nestjs/common' +import { AuthController } from './auth.controller' +import { JwtService } from './jwt.service' +import { AuthService } from './auth.service' @Module({ controllers: [AuthController], diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index a20cdd0..aed9537 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -1,16 +1,16 @@ -import { Injectable } from '@nestjs/common'; -import * as crypto from 'crypto'; +import * as crypto from 'crypto' +import { Injectable } from '@nestjs/common' import { MiniAppWalletAuthSuccessPayload, verifySiweMessage, SiweMessage, -} from '@worldcoin/minikit-js'; -import { DatabaseService } from 'src/database/database.service'; -import { CreateUserException } from 'src/common/exceptions'; +} from '@worldcoin/minikit-js' +import { DatabaseService } from 'src/database/database.service' +import { CreateUserException } from 'src/common/exceptions' interface IValidMessage { - isValid: boolean; - siweMessageData: SiweMessage; + isValid: boolean + siweMessageData: SiweMessage } @Injectable() @@ -21,15 +21,15 @@ export class AuthService { return crypto .randomBytes(Math.ceil(length / 2)) .toString('hex') - .slice(0, length); + .slice(0, length) } async verifyPayload(payload: MiniAppWalletAuthSuccessPayload, nonce: string) { const validMessage = (await verifySiweMessage( payload, nonce, - )) as IValidMessage; - return validMessage.isValid; + )) as IValidMessage + return validMessage.isValid } async createUser(worldID: string, name: string, profilePicture: string) { @@ -37,10 +37,10 @@ export class AuthService { where: { worldID }, update: { name, profilePicture }, create: { worldID, name, profilePicture }, - }); + }) - if (!user) throw new CreateUserException(); + if (!user) throw new CreateUserException() - return user; + return user } } diff --git a/src/auth/jwt-auth.guard.ts b/src/auth/jwt-auth.guard.ts index 2c637dc..e5926fd 100644 --- a/src/auth/jwt-auth.guard.ts +++ b/src/auth/jwt-auth.guard.ts @@ -3,27 +3,27 @@ import { ExecutionContext, Injectable, UnauthorizedException, -} from '@nestjs/common'; -import { SetMetadata } from '@nestjs/common'; -import { Reflector } from '@nestjs/core'; -import { Request } from 'express'; -import { VerificationLevel } from '@worldcoin/minikit-js'; -import { JwtService } from './jwt.service'; -import { InsufficientVerificationLevelException } from 'src/common/exceptions'; +} from '@nestjs/common' +import { SetMetadata } from '@nestjs/common' +import { Reflector } from '@nestjs/core' +import { Request } from 'express' +import { VerificationLevel } from '@worldcoin/minikit-js' +import { InsufficientVerificationLevelException } from 'src/common/exceptions' +import { JwtService } from './jwt.service' -export const IS_PUBLIC_KEY = 'isPublic'; +export const IS_PUBLIC_KEY = 'isPublic' -export const Public = () => SetMetadata(IS_PUBLIC_KEY, true); +export const Public = () => SetMetadata(IS_PUBLIC_KEY, true) export interface JwtPayload { - userId: number; - worldID: string; - address: string; - verificationLevel: VerificationLevel; + userId: number + worldID: string + address: string + verificationLevel: VerificationLevel } @Injectable() export class JwtAuthGuard implements CanActivate { - private readonly requiredVerificationLevel: VerificationLevel; + private readonly requiredVerificationLevel: VerificationLevel constructor( private reflector: Reflector, @@ -31,56 +31,54 @@ export class JwtAuthGuard implements CanActivate { ) { this.requiredVerificationLevel = (process.env.VERIFICATION_LEVEL?.toLowerCase() as VerificationLevel) || - VerificationLevel.Device; + VerificationLevel.Device } canActivate(context: ExecutionContext): boolean { const isPublic = this.reflector.getAllAndOverride(IS_PUBLIC_KEY, [ context.getHandler(), context.getClass(), - ]); + ]) if (isPublic) { - return true; + return true } - const request = context.switchToHttp().getRequest(); - return this.validateRequest(request); + const request = context.switchToHttp().getRequest() + return this.validateRequest(request) } private validateRequest(request: Request): boolean { - const authHeader = request.headers.authorization; + const authHeader = request.headers.authorization if (!authHeader) { - throw new UnauthorizedException('Authentication required'); + throw new UnauthorizedException('Authentication required') } if (!authHeader.startsWith('Bearer ')) { throw new UnauthorizedException( 'Invalid authentication format. Expected Bearer token', - ); + ) } - const token = authHeader.replace('Bearer ', '').trim(); - return this.validateToken(token, request); + const token = authHeader.replace('Bearer ', '').trim() + return this.validateToken(token, request) } private validateToken(token: string, request: Request): boolean { try { - const payload = this.jwtService.verify(token) as JwtPayload; - request.user = payload; + const payload = this.jwtService.verify(token) as JwtPayload + request.user = payload - this.validateVerificationLevel(payload.verificationLevel); + this.validateVerificationLevel(payload.verificationLevel) - return true; + return true } catch (error) { if (error instanceof UnauthorizedException) { - throw error; + throw error } - throw new UnauthorizedException( - 'Invalid or expired authentication token', - ); + throw new UnauthorizedException('Invalid or expired authentication token') } } @@ -88,7 +86,7 @@ export class JwtAuthGuard implements CanActivate { userVerificationLevel: VerificationLevel, ): void { if (userVerificationLevel !== this.requiredVerificationLevel) { - throw new InsufficientVerificationLevelException(); + throw new InsufficientVerificationLevelException() } } } diff --git a/src/auth/jwt.service.ts b/src/auth/jwt.service.ts index 9cb5439..db3be59 100644 --- a/src/auth/jwt.service.ts +++ b/src/auth/jwt.service.ts @@ -1,30 +1,30 @@ -import { Injectable } from '@nestjs/common'; -import jwt, { JwtPayload } from 'jsonwebtoken'; -import { JwtVerificationFailureException } from 'src/common/exceptions'; +import { Injectable } from '@nestjs/common' +import jwt, { JwtPayload } from 'jsonwebtoken' +import { JwtVerificationFailureException } from 'src/common/exceptions' @Injectable() export class JwtService { private readonly secret = process.env.JWT_SECRET || (function () { - throw new Error('JWT_SECRET environment variable must be set'); - })(); - private readonly expiresIn = '7d'; + throw new Error('JWT_SECRET environment variable must be set') + })() + private readonly expiresIn = '7d' sign(payload: object): string { - return jwt.sign(payload, this.secret, { expiresIn: this.expiresIn }); + return jwt.sign(payload, this.secret, { expiresIn: this.expiresIn }) } verify(token: string): JwtPayload | string { try { - return jwt.verify(token, this.secret); + return jwt.verify(token, this.secret) } catch (error) { - console.error(error); - throw new JwtVerificationFailureException(); + console.error(error) + throw new JwtVerificationFailureException() } } - decode(token: string): null | { [key: string]: any } | string { - return jwt.decode(token); + decode(token: string): null | JwtPayload | string { + return jwt.decode(token) } } diff --git a/src/auth/user.decorator.ts b/src/auth/user.decorator.ts index 14ebf12..53ed575 100644 --- a/src/auth/user.decorator.ts +++ b/src/auth/user.decorator.ts @@ -1,10 +1,10 @@ -import { createParamDecorator, ExecutionContext } from '@nestjs/common'; -import { JwtPayload } from './jwt-auth.guard'; -import { Request } from 'express'; +import { createParamDecorator, ExecutionContext } from '@nestjs/common' +import { Request } from 'express' +import { JwtPayload } from './jwt-auth.guard' export const User = createParamDecorator( (field: keyof JwtPayload, ctx: ExecutionContext) => { - const request = ctx.switchToHttp().getRequest(); - return field ? request.user?.[field] : request.user; + const request = ctx.switchToHttp().getRequest() + return field ? request.user?.[field] : request.user }, -); +) diff --git a/src/common/common.dto.ts b/src/common/common.dto.ts index 502b32b..538ffe9 100644 --- a/src/common/common.dto.ts +++ b/src/common/common.dto.ts @@ -1,15 +1,15 @@ -import { IsDateString, IsOptional, IsString, Matches } from 'class-validator'; +import { IsDateString, IsOptional, IsString, Matches } from 'class-validator' export class GetCountDto { @IsOptional() @IsString() @Matches(/^\d{4}-\d{2}-\d{2}$/) @IsDateString({ strict: true }) - from?: string; + from?: string @IsOptional() @IsString() @Matches(/^\d{4}-\d{2}-\d{2}$/) @IsDateString({ strict: true }) - to?: string; + to?: string } diff --git a/src/common/constants.ts b/src/common/constants.ts index d0147e7..1a1d204 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -1 +1 @@ -export const VOTING_POWER = 100; +export const VOTING_POWER = 100 diff --git a/src/common/exceptions.ts b/src/common/exceptions.ts index bbe18bc..4f6f99c 100644 --- a/src/common/exceptions.ts +++ b/src/common/exceptions.ts @@ -1,67 +1,67 @@ -import { HttpException, HttpStatus } from '@nestjs/common'; +import { HttpException, HttpStatus } from '@nestjs/common' export class UserNotFoundException extends HttpException { constructor(message = 'User not found') { - super(message, HttpStatus.NOT_FOUND); + super(message, HttpStatus.NOT_FOUND) } } export class UserActionNotFoundException extends HttpException { constructor(message = 'User action not found') { - super(message, HttpStatus.NOT_FOUND); + super(message, HttpStatus.NOT_FOUND) } } export class PollNotFoundException extends HttpException { constructor(message = 'Poll not found or not active') { - super(message, HttpStatus.NOT_FOUND); + super(message, HttpStatus.NOT_FOUND) } } export class VoteNotFoundException extends HttpException { constructor(message = 'Vote not found') { - super(message, HttpStatus.NOT_FOUND); + super(message, HttpStatus.NOT_FOUND) } } export class DuplicateVoteException extends HttpException { constructor(message = 'User has already voted in this poll') { - super(message, HttpStatus.BAD_REQUEST); + super(message, HttpStatus.BAD_REQUEST) } } export class VoteOptionException extends HttpException { constructor(message = 'Invalid vote option') { - super(message, HttpStatus.BAD_REQUEST); + super(message, HttpStatus.BAD_REQUEST) } } export class UnauthorizedActionException extends HttpException { constructor(message = 'You are not authorized to perform this action') { - super(message, HttpStatus.FORBIDDEN); + super(message, HttpStatus.FORBIDDEN) } } export class CreateUserException extends HttpException { constructor(message = 'User creation failed') { - super(message, HttpStatus.BAD_REQUEST); + super(message, HttpStatus.BAD_REQUEST) } } export class SignatureVerificationFailureException extends HttpException { constructor(message = 'Signature verification failed') { - super(message, HttpStatus.BAD_REQUEST); + super(message, HttpStatus.BAD_REQUEST) } } export class JwtVerificationFailureException extends HttpException { constructor(message = 'JWT verification failed') { - super(message, HttpStatus.BAD_REQUEST); + super(message, HttpStatus.BAD_REQUEST) } } export class InsufficientVerificationLevelException extends HttpException { constructor(message = 'Insufficient verification level') { - super(message, HttpStatus.UNAUTHORIZED); + super(message, HttpStatus.UNAUTHORIZED) } } diff --git a/src/common/http-exception.filter.ts b/src/common/http-exception.filter.ts index 41abcb8..6522fd8 100644 --- a/src/common/http-exception.filter.ts +++ b/src/common/http-exception.filter.ts @@ -5,37 +5,37 @@ import { HttpException, HttpStatus, Logger, -} from '@nestjs/common'; -import { Request, Response } from 'express'; +} from '@nestjs/common' +import { Request, Response } from 'express' @Catch(HttpException) export class HttpExceptionFilter implements ExceptionFilter { - private readonly logger = new Logger(HttpExceptionFilter.name); + private readonly logger = new Logger(HttpExceptionFilter.name) catch(exception: HttpException, host: ArgumentsHost) { - const ctx = host.switchToHttp(); - const response = ctx.getResponse(); - const request = ctx.getRequest(); + const ctx = host.switchToHttp() + const response = ctx.getResponse() + const request = ctx.getRequest() const status = exception instanceof HttpException ? exception.getStatus() - : HttpStatus.INTERNAL_SERVER_ERROR; + : HttpStatus.INTERNAL_SERVER_ERROR const responseBody = { statusCode: status, timestamp: new Date().toISOString(), path: request.url, message: exception.message, - }; + } // Log only internal server errors or other important errors if (status >= 500) { this.logger.error( `${request.method} ${request.url} ${status} - ${exception.message}`, exception.stack, - ); + ) } - response.status(status).json(responseBody); + response.status(status).json(responseBody) } } diff --git a/src/common/validators.ts b/src/common/validators.ts index 13b3620..2797adb 100644 --- a/src/common/validators.ts +++ b/src/common/validators.ts @@ -1,33 +1,33 @@ import { ValidatorConstraint, ValidatorConstraintInterface, -} from 'class-validator'; +} from 'class-validator' @ValidatorConstraint({ name: 'isNonNegativeInteger', async: false }) export class IsPositiveInteger implements ValidatorConstraintInterface { - validate(value: any): boolean { + validate(value: unknown): boolean { if (typeof value !== 'number' && typeof value !== 'string') { - return false; + return false } - const num = Number(value); - return Number.isInteger(num) && num > 0; + const num = Number(value) + return Number.isInteger(num) && num > 0 } defaultMessage(): string { - return '$property must be a positive integer'; + return '$property must be a positive integer' } } @ValidatorConstraint({ name: 'IsRecordStringNumber', async: false }) export class IsRecordStringNumber implements ValidatorConstraintInterface { - validate(value: any): boolean { + validate(value: unknown): boolean { if (typeof value !== 'object' || value === null || Array.isArray(value)) { - return false; + return false } - return Object.entries(value).every( + return Object.entries(value as Record).every( ([key, val]) => typeof key === 'string' && typeof val === 'number', - ); + ) } defaultMessage(): string { - return 'weightDistribution must be a record with string keys and number values'; + return 'weightDistribution must be a record with string keys and number values' } } diff --git a/src/database/database.module.ts b/src/database/database.module.ts index 9e1b6d6..ac8649a 100644 --- a/src/database/database.module.ts +++ b/src/database/database.module.ts @@ -1,5 +1,5 @@ -import { Global, Module } from '@nestjs/common'; -import { DatabaseService } from './database.service'; +import { Global, Module } from '@nestjs/common' +import { DatabaseService } from './database.service' @Global() @Module({ diff --git a/src/database/database.service.ts b/src/database/database.service.ts index 948e427..7c595b0 100644 --- a/src/database/database.service.ts +++ b/src/database/database.service.ts @@ -1,11 +1,11 @@ -import { Injectable, OnModuleInit } from '@nestjs/common'; -import { PrismaClient } from '@prisma/client'; +import { Injectable, OnModuleInit } from '@nestjs/common' +import { PrismaClient } from '@prisma/client' @Injectable() export class DatabaseService extends PrismaClient implements OnModuleInit { async onModuleInit() { await this.$connect() .then(() => console.log('Connected to DB')) - .catch((err) => console.log('Error connecting to DB', err)); + .catch(err => console.log('Error connecting to DB', err)) } } diff --git a/src/main.ts b/src/main.ts index 9d0f690..69ec6e8 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,79 +1,78 @@ -import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; -import cookieParser from 'cookie-parser'; -import { JwtAuthGuard } from './auth/jwt-auth.guard'; -import { Reflector } from '@nestjs/core'; -import { JwtService } from './auth/jwt.service'; -import { ValidationPipe, BadRequestException } from '@nestjs/common'; -import { HttpExceptionFilter } from './common/http-exception.filter'; +import { BadRequestException, ValidationPipe } from '@nestjs/common' +import { NestFactory, Reflector } from '@nestjs/core' +import cookieParser from 'cookie-parser' +import { AppModule } from './app.module' +import { JwtAuthGuard } from './auth/jwt-auth.guard' +import { JwtService } from './auth/jwt.service' +import { HttpExceptionFilter } from './common/http-exception.filter' async function bootstrap() { - const app = await NestFactory.create(AppModule); + const app = await NestFactory.create(AppModule) - const environment = process.env.NODE_ENV || 'development'; - const rawOrigins = process.env.WHITELISTED_ORIGINS || ''; - const rawTunnelDomains = process.env.TUNNEL_DOMAINS || ''; + const environment = process.env.NODE_ENV || 'development' + const rawOrigins = process.env.WHITELISTED_ORIGINS || '' + const rawTunnelDomains = process.env.TUNNEL_DOMAINS || '' const whiteListedOrigins = rawOrigins .split(',') - .map((origin) => origin.trim().replace(/^https?:\/\//, '')) - .filter((origin) => !!origin); + .map(origin => origin.trim().replace(/^https?:\/\//, '')) + .filter(origin => !!origin) const tunnelDomains = rawTunnelDomains .split(',') - .map((domain) => domain.trim()) - .filter((domain) => !!domain); + .map(domain => domain.trim()) + .filter(domain => !!domain) app.enableCors({ origin: ( origin: string | undefined, callback: (err: Error | null, allow?: boolean) => void, ) => { - if (!origin) return callback(null, true); + if (!origin) return callback(null, true) try { - const { hostname } = new URL(origin); + const { hostname } = new URL(origin) - const isWhitelisted = whiteListedOrigins.includes(hostname); + const isWhitelisted = whiteListedOrigins.includes(hostname) const isTunnelDomain = environment !== 'production' && - tunnelDomains.some((testDomain) => hostname.endsWith(testDomain)); + tunnelDomains.some(testDomain => hostname.endsWith(testDomain)) if (isWhitelisted || isTunnelDomain) { - return callback(null, true); + return callback(null, true) } - console.error(`[CORS ERROR]: Origin ${origin} is not allowed.`); - return callback(new Error(`CORS blocked for origin: ${origin}`)); + console.error(`[CORS ERROR]: Origin ${origin} is not allowed.`) + return callback(new Error(`CORS blocked for origin: ${origin}`)) } catch (error) { - console.error(`[CORS ERROR]: Malformed origin`, error); - return callback(new Error(`Invalid origin: ${origin}`)); + console.error(`[CORS ERROR]: Malformed origin`, error) + return callback(new Error(`Invalid origin: ${origin}`)) } }, credentials: true, - }); + }) - const reflector = app.get(Reflector); - app.useGlobalGuards(new JwtAuthGuard(reflector, app.get(JwtService))); + const reflector = app.get(Reflector) + app.useGlobalGuards(new JwtAuthGuard(reflector, app.get(JwtService))) - app.useGlobalFilters(new HttpExceptionFilter()); + app.useGlobalFilters(new HttpExceptionFilter()) app.useGlobalPipes( new ValidationPipe({ transform: true, - exceptionFactory: (errors) => { - const firstError = errors[0]; + exceptionFactory: errors => { + const firstError = errors[0] if (!firstError?.constraints) { - return new BadRequestException('Validation failed'); + return new BadRequestException('Validation failed') } - const message = Object.values(firstError.constraints)[0]; - return new BadRequestException(message); + const message = Object.values(firstError.constraints)[0] + return new BadRequestException(message) }, }), - ); - app.use(cookieParser()); + ) + app.use(cookieParser()) - await app.listen(process.env.PORT ?? 3000); + await app.listen(process.env.PORT ?? 3000) } -bootstrap(); +bootstrap().catch(e => console.error('bootstrap error:', e)) diff --git a/src/poll/Poll.dto.ts b/src/poll/Poll.dto.ts index fc42e68..cd5d563 100644 --- a/src/poll/Poll.dto.ts +++ b/src/poll/Poll.dto.ts @@ -1,4 +1,4 @@ -import { Transform, Type } from 'class-transformer'; +import { Transform, Type } from 'class-transformer' import { IsArray, IsBoolean, @@ -10,122 +10,122 @@ import { IsString, Min, Validate, -} from 'class-validator'; -import { IsPositiveInteger } from '../common/validators'; +} from 'class-validator' +import { IsPositiveInteger } from '../common/validators' export class CreatePollDto { @IsString() @IsNotEmpty() - title: string; + title: string @IsString() @IsNotEmpty() - description: string; + description: string @IsArray() @IsString({ each: true }) @IsNotEmpty() - options: string[]; + options: string[] @IsDateString() @IsNotEmpty() - startDate: string; + startDate: string @IsDateString() @IsNotEmpty() - endDate: string; + endDate: string @IsArray() @IsString({ each: true }) @IsNotEmpty() - tags: string[]; + tags: string[] @IsBoolean() @IsOptional() - isAnonymous?: boolean; + isAnonymous?: boolean } export class DraftPollDto { @IsOptional() @IsString() - title?: string; + title?: string @IsOptional() @IsString() - description?: string; + description?: string @IsOptional() @IsArray() @IsString({ each: true }) - options?: string[]; + options?: string[] @IsOptional() @IsDateString() - startDate?: string; + startDate?: string @IsOptional() @IsDateString() - endDate?: string; + endDate?: string @IsOptional() @IsArray() @IsString({ each: true }) - tags?: string[]; + tags?: string[] @IsOptional() @IsBoolean() - isAnonymous?: boolean; + isAnonymous?: boolean @IsOptional() @Validate(IsPositiveInteger) - @Transform(({ value }) => (value ? parseInt(value, 10) : undefined)) - pollId?: number; + @Transform(({ value }) => (value ? parseInt(value as string, 10) : undefined)) + pollId?: number } export class GetPollsDto { @IsString() @IsOptional() - search?: string; + search?: string @IsOptional() @Type(() => Number) @IsInt() @Min(1) - page?: number = 1; + page?: number = 1 @IsOptional() @Type(() => Number) @IsInt() @Min(1) - limit?: number = 10; + limit?: number = 10 @IsOptional() @IsBoolean() @Transform(({ value }) => value === 'true') - isActive?: boolean; + isActive?: boolean @IsOptional() @IsBoolean() @Transform(({ value }) => value === 'true') - userVoted?: boolean; + userVoted?: boolean @IsOptional() @IsBoolean() @Transform(({ value }) => value === 'true') - userCreated?: boolean; + userCreated?: boolean @IsOptional() @IsEnum(['endDate', 'participantCount', 'creationDate']) - sortBy?: 'endDate' | 'participantCount' | 'creationDate'; + sortBy?: 'endDate' | 'participantCount' | 'creationDate' @IsOptional() @IsEnum(['asc', 'desc']) - sortOrder?: 'asc' | 'desc'; + sortOrder?: 'asc' | 'desc' } export class GetPollVotesDto { @IsInt() @Type(() => Number) @IsNotEmpty() - pollId: number; + pollId: number } diff --git a/src/poll/poll.controller.ts b/src/poll/poll.controller.ts index a8572d4..b59a86c 100644 --- a/src/poll/poll.controller.ts +++ b/src/poll/poll.controller.ts @@ -7,12 +7,12 @@ import { Patch, Post, Query, -} from '@nestjs/common'; -import { User } from 'src/auth/user.decorator'; -import { CreatePollDto, DraftPollDto, GetPollsDto } from './Poll.dto'; -import { PollService } from './poll.service'; -import { Public } from 'src/auth/jwt-auth.guard'; -import { GetCountDto } from '../common/common.dto'; +} from '@nestjs/common' +import { User } from 'src/auth/user.decorator' +import { Public } from 'src/auth/jwt-auth.guard' +import { CreatePollDto, DraftPollDto, GetPollsDto } from './Poll.dto' +import { PollService } from './poll.service' +import { GetCountDto } from '../common/common.dto' @Controller('poll') export class PollController { @@ -23,7 +23,7 @@ export class PollController { @Body() dto: CreatePollDto, @User('worldID') worldID: string, ) { - return await this.pollService.createPoll(dto, worldID); + return await this.pollService.createPoll(dto, worldID) } @Patch('draft') @@ -31,12 +31,12 @@ export class PollController { @Body() dto: DraftPollDto, @User('worldID') worldID: string, ) { - return await this.pollService.patchDraftPoll(dto, worldID); + return await this.pollService.patchDraftPoll(dto, worldID) } @Get('draft') async getDraftPoll(@User('worldID') worldID: string) { - return await this.pollService.getUserDraftPoll(worldID); + return await this.pollService.getUserDraftPoll(worldID) } @Get() @@ -44,28 +44,28 @@ export class PollController { @Query() query: GetPollsDto, @User('worldID') worldID: string, ) { - return await this.pollService.getPolls(query, worldID); + return await this.pollService.getPolls(query, worldID) } @Get('count') @Public() async getPollsCount(@Query() query: GetCountDto): Promise { - return await this.pollService.getPollsCount(query); + return await this.pollService.getPollsCount(query) } @Get(':id') async getPollDetails(@Param('id') id: number) { - return await this.pollService.getPollDetails(Number(id)); + return await this.pollService.getPollDetails(Number(id)) } @Get(':id/votes') async getPollVotes(@Param('id') id: number) { - return await this.pollService.getPollVotes(Number(id)); + return await this.pollService.getPollVotes(Number(id)) } @Delete(':id') async deletePoll(@Param('id') id: number, @User('worldID') worldID: string) { - const poll = await this.pollService.deletePoll(Number(id), worldID); - return { message: 'Poll deleted', poll }; + const poll = await this.pollService.deletePoll(Number(id), worldID) + return { message: 'Poll deleted', poll } } } diff --git a/src/poll/poll.module.ts b/src/poll/poll.module.ts index c1dd5d1..c0a3a1c 100644 --- a/src/poll/poll.module.ts +++ b/src/poll/poll.module.ts @@ -1,8 +1,8 @@ -import { forwardRef, Module } from '@nestjs/common'; -import { PollService } from './poll.service'; -import { PollController } from './poll.controller'; -import { UserModule } from '../user/user.module'; -import { UserService } from '../user/user.service'; +import { forwardRef, Module } from '@nestjs/common' +import { PollService } from './poll.service' +import { PollController } from './poll.controller' +import { UserModule } from '../user/user.module' +import { UserService } from '../user/user.service' @Module({ imports: [forwardRef(() => UserModule)], diff --git a/src/poll/poll.service.ts b/src/poll/poll.service.ts index 3c29bcb..bab0501 100644 --- a/src/poll/poll.service.ts +++ b/src/poll/poll.service.ts @@ -3,19 +3,19 @@ import { forwardRef, Inject, Injectable, -} from '@nestjs/common'; -import { ActionType, PollStatus, Prisma } from '@prisma/client'; -import { DatabaseService } from 'src/database/database.service'; -import { UserService } from 'src/user/user.service'; -import { GetCountDto } from '../common/common.dto'; +} from '@nestjs/common' +import { ActionType, PollStatus, Prisma } from '@prisma/client' +import { DatabaseService } from 'src/database/database.service' +import { UserService } from 'src/user/user.service' +import { GetCountDto } from '../common/common.dto' import { PollNotFoundException, UnauthorizedActionException, UserNotFoundException, -} from '../common/exceptions'; -import { CreatePollDto, DraftPollDto, GetPollsDto } from './Poll.dto'; +} from '../common/exceptions' +import { CreatePollDto, DraftPollDto, GetPollsDto } from './Poll.dto' -const IS_VOTE_NORMALIZATION = process.env.ENABLE_VOTE_NORMALIZATION === 'true'; +const IS_VOTE_NORMALIZATION = process.env.ENABLE_VOTE_NORMALIZATION === 'true' @Injectable() export class PollService { @@ -29,22 +29,22 @@ export class PollService { pollId: number, prismaClient?: Prisma.TransactionClient, // To ensure Prisma transaction function runs queries in order ) { - const prisma = prismaClient || this.databaseService; + const prisma = prismaClient || this.databaseService const participantCount = await prisma.userAction.count({ where: { pollId, type: ActionType.VOTED }, - }); + }) await prisma.poll.update({ where: { pollId }, data: { participantCount }, - }); + }) } private async searchPolls(searchTerm: string): Promise { const searchQuery = searchTerm .split(' ') - .map((word) => `${word}:*`) - .join(' & '); - const includeStatus = PollStatus.PUBLISHED; + .map(word => `${word}:*`) + .join(' & ') + const includeStatus = PollStatus.PUBLISHED const searchResults = await this.databaseService.$queryRaw< { pollId: number }[] >` @@ -52,38 +52,38 @@ export class PollService { WHERE "searchVector" @@ to_tsquery('english', ${searchQuery}) AND "status" = ${includeStatus}::text::"PollStatus" ORDER BY ts_rank("searchVector", to_tsquery('english', ${searchQuery})) DESC - `; - return searchResults.map((result) => result.pollId); + ` + return searchResults.map(result => result.pollId) } // Should be used only for creating published polls async createPoll(createPollDto: CreatePollDto, worldID: string) { const user = await this.databaseService.user.findUnique({ where: { worldID }, - }); + }) if (!user) { - throw new UserNotFoundException(); + throw new UserNotFoundException() } - const startDate = new Date(createPollDto.startDate); + const startDate = new Date(createPollDto.startDate) // temporary fix by adding 1min for tiny delay in time from receiving the request - const checkDate = new Date(startDate.getTime() + 60000); - const endDate = new Date(createPollDto.endDate); - const now = new Date(); + const checkDate = new Date(startDate.getTime() + 60000) + const endDate = new Date(createPollDto.endDate) + const now = new Date() if (checkDate < now) { - throw new BadRequestException('Start date cannot be in the past'); + throw new BadRequestException('Start date cannot be in the past') } if (endDate <= startDate) { - throw new BadRequestException('End date must be after start date'); + throw new BadRequestException('End date must be after start date') } - return this.databaseService.$transaction(async (tx) => { + return this.databaseService.$transaction(async tx => { // Delete all existing drafts await tx.poll.deleteMany({ where: { authorUserId: user.id, status: PollStatus.DRAFT, }, - }); + }) const newPoll = await tx.poll.create({ data: { authorUserId: user.id, @@ -96,30 +96,30 @@ export class PollService { isAnonymous: createPollDto.isAnonymous || false, voteResults: {}, }, - }); + }) await tx.userAction.create({ data: { userId: user.id, pollId: newPoll.pollId, type: ActionType.CREATED, }, - }); + }) await this.userService.updateUserPollsCount( user.id, ActionType.CREATED, tx, - ); - return newPoll; - }); + ) + return newPoll + }) } async patchDraftPoll(draftPollDto: DraftPollDto, worldID: string) { const user = await this.databaseService.user.findUnique({ where: { worldID }, select: { id: true }, - }); + }) if (!user) { - throw new UserNotFoundException(); + throw new UserNotFoundException() } // Handle case with specified pollId @@ -128,48 +128,48 @@ export class PollService { const existingPoll = await this.databaseService.poll.findUnique({ where: { pollId: draftPollDto.pollId }, select: { authorUserId: true, status: true }, - }); + }) if (!existingPoll) { - throw new PollNotFoundException(); + throw new PollNotFoundException() } if (existingPoll.authorUserId !== user.id) { - throw new UnauthorizedActionException(); + throw new UnauthorizedActionException() } if (existingPoll.status !== PollStatus.DRAFT) { - throw new BadRequestException('Cannot update a published poll!'); + throw new BadRequestException('Cannot update a published poll!') } - const updateData: Prisma.PollUpdateInput = {}; + const updateData: Prisma.PollUpdateInput = {} if (draftPollDto.title !== undefined) - updateData.title = draftPollDto.title; + updateData.title = draftPollDto.title if (draftPollDto.description !== undefined) - updateData.description = draftPollDto.description; + updateData.description = draftPollDto.description if (draftPollDto.options !== undefined) - updateData.options = draftPollDto.options; + updateData.options = draftPollDto.options if (draftPollDto.startDate !== undefined) - updateData.startDate = new Date(draftPollDto.startDate); + updateData.startDate = new Date(draftPollDto.startDate) if (draftPollDto.endDate !== undefined) - updateData.endDate = new Date(draftPollDto.endDate); - if (draftPollDto.tags !== undefined) updateData.tags = draftPollDto.tags; + updateData.endDate = new Date(draftPollDto.endDate) + if (draftPollDto.tags !== undefined) updateData.tags = draftPollDto.tags if (draftPollDto.isAnonymous !== undefined) - updateData.isAnonymous = draftPollDto.isAnonymous; + updateData.isAnonymous = draftPollDto.isAnonymous return await this.databaseService.poll.update({ where: { pollId: draftPollDto.pollId }, data: updateData, - }); + }) } else { // For new drafts, use a transaction - return this.databaseService.$transaction(async (tx) => { + return this.databaseService.$transaction(async tx => { // Find existing drafts (if any) const existingDrafts = await tx.poll.findMany({ where: { authorUserId: user.id, status: PollStatus.DRAFT }, select: { pollId: true }, orderBy: { creationDate: 'desc' }, - }); + }) // Prepare poll data const pollData = { @@ -187,7 +187,7 @@ export class PollService { isAnonymous: draftPollDto.isAnonymous || false, status: PollStatus.DRAFT, voteResults: {}, - }; + } // Clean up multiple drafts if found if (existingDrafts.length > 1) { @@ -195,7 +195,7 @@ export class PollService { for (let i = 1; i < existingDrafts.length; i++) { await tx.poll.delete({ where: { pollId: existingDrafts[i].pollId }, - }); + }) } } @@ -204,11 +204,11 @@ export class PollService { return await tx.poll.update({ where: { pollId: existingDrafts[0].pollId }, data: pollData, - }); + }) } else { - return await tx.poll.create({ data: pollData }); + return await tx.poll.create({ data: pollData }) } - }); + }) } } @@ -216,9 +216,9 @@ export class PollService { const user = await this.databaseService.user.findUnique({ where: { worldID }, select: { id: true }, - }); + }) if (!user) { - throw new UserNotFoundException(); + throw new UserNotFoundException() } // We should only have maximum one draft poll per user const draft = await this.databaseService.poll.findFirst({ @@ -229,8 +229,8 @@ export class PollService { orderBy: { creationDate: 'desc', }, - }); - return draft; + }) + return draft } async getPolls(query: GetPollsDto, worldID: string) { @@ -243,94 +243,94 @@ export class PollService { search, sortBy = 'endDate', sortOrder = 'asc', - } = query; - const skip = (page - 1) * limit; - const now = new Date(); + } = query + const skip = (page - 1) * limit + const now = new Date() const filters: Prisma.PollWhereInput = { status: PollStatus.PUBLISHED, - }; + } if (isActive) { - filters.startDate = { lte: now }; - filters.endDate = { gt: now }; + filters.startDate = { lte: now } + filters.endDate = { gt: now } } if (isActive === false) { - filters.OR = [{ startDate: { gt: now } }, { endDate: { lte: now } }]; + filters.OR = [{ startDate: { gt: now } }, { endDate: { lte: now } }] } const user = await this.databaseService.user.findUnique({ where: { worldID }, select: { id: true }, - }); + }) if (!user) { - throw new UserNotFoundException(); + throw new UserNotFoundException() } - const userId = user.id; + const userId = user.id if (userCreated && userVoted) { const userVotes = await this.databaseService.vote.findMany({ where: { userId }, select: { pollId: true }, - }); - const votedPollIds = userVotes.map((v) => v.pollId); + }) + const votedPollIds = userVotes.map(v => v.pollId) // Create a copy of current filters to avoid overwriting status - const currentFilters = { ...filters }; + const currentFilters = { ...filters } filters.AND = [ currentFilters, { OR: [{ authorUserId: userId }, { pollId: { in: votedPollIds } }] }, - ]; + ] } else { if (userCreated) { // Create a copy of current filters to avoid overwriting status - const currentFilters = { ...filters }; - filters.AND = [currentFilters, { authorUserId: userId }]; + const currentFilters = { ...filters } + filters.AND = [currentFilters, { authorUserId: userId }] } if (userVoted) { const userVotes = await this.databaseService.vote.findMany({ where: { userId }, select: { pollId: true }, - }); - const votedPollIds = userVotes.map((v) => v.pollId); + }) + const votedPollIds = userVotes.map(v => v.pollId) // Create a copy of current filters to avoid overwriting status - const currentFilters = { ...filters }; - filters.AND = [currentFilters, { pollId: { in: votedPollIds } }]; + const currentFilters = { ...filters } + filters.AND = [currentFilters, { pollId: { in: votedPollIds } }] } } if (search) { - const pollIds = await this.searchPolls(search); + const pollIds = await this.searchPolls(search) if (!filters.AND) { // Create a copy of current filters to avoid overwriting status - const currentFilters = { ...filters }; - filters.AND = [currentFilters, { pollId: { in: pollIds } }]; + const currentFilters = { ...filters } + filters.AND = [currentFilters, { pollId: { in: pollIds } }] } else { // Add to existing AND condition by creating a new array filters.AND = [ ...(filters.AND as Prisma.PollWhereInput[]), { pollId: { in: pollIds } }, - ]; + ] } } - let orderBy: Prisma.PollOrderByWithRelationInput[] = []; + let orderBy: Prisma.PollOrderByWithRelationInput[] = [] if (sortBy === 'endDate') { const activeFilters = { ...filters, startDate: { lte: now }, endDate: { gt: now }, - }; + } const inactiveFilters = { ...filters, OR: [{ startDate: { gt: now } }, { endDate: { lte: now } }], - }; + } const [activePolls, inactivePolls, total] = await this.databaseService.$transaction([ @@ -359,29 +359,29 @@ export class PollService { take: Number(limit) + skip, }), this.databaseService.poll.count({ where: filters }), - ]); + ]) - const combinedPolls = [...activePolls, ...inactivePolls]; + const combinedPolls = [...activePolls, ...inactivePolls] - const paginatedPolls = combinedPolls.slice(skip, skip + Number(limit)); + const paginatedPolls = combinedPolls.slice(skip, skip + Number(limit)) - const pollsWithVoteStatus = paginatedPolls.map((poll) => { - const { votes, ...pollWithoutVotes } = poll; + const pollsWithVoteStatus = paginatedPolls.map(poll => { + const { votes, ...pollWithoutVotes } = poll return { ...pollWithoutVotes, hasVoted: votes.length > 0, - }; - }); + } + }) return { polls: pollsWithVoteStatus, total, - }; + } } else if (sortBy) { orderBy.push({ [sortBy]: sortOrder, - }); + }) const [polls, total] = await this.databaseService.$transaction([ this.databaseService.poll.findMany({ @@ -398,21 +398,21 @@ export class PollService { take: Number(limit), }), this.databaseService.poll.count({ where: filters }), - ]); + ]) - const pollsWithVoteStatus = polls.map((poll) => { - const { votes, ...pollWithoutVotes } = poll; + const pollsWithVoteStatus = polls.map(poll => { + const { votes, ...pollWithoutVotes } = poll return { ...pollWithoutVotes, hasVoted: votes.length > 0, - }; - }); + } + }) return { polls: pollsWithVoteStatus, total, - }; + } } else { orderBy = [ { @@ -421,7 +421,7 @@ export class PollService { { startDate: 'asc', }, - ]; + ] const [polls, total] = await this.databaseService.$transaction([ this.databaseService.poll.findMany({ @@ -438,21 +438,21 @@ export class PollService { take: Number(limit), }), this.databaseService.poll.count({ where: filters }), - ]); + ]) - const pollsWithVoteStatus = polls.map((poll) => { - const { votes, ...pollWithoutVotes } = poll; + const pollsWithVoteStatus = polls.map(poll => { + const { votes, ...pollWithoutVotes } = poll return { ...pollWithoutVotes, hasVoted: votes.length > 0, - }; - }); + } + }) return { polls: pollsWithVoteStatus, total, - }; + } } } @@ -465,84 +465,84 @@ export class PollService { include: { author: true, }, - }); + }) if (!poll) { - throw new PollNotFoundException(); + throw new PollNotFoundException() } - const now = new Date(); + const now = new Date() const isActive = poll.startDate && poll.endDate && now >= poll.startDate && - now <= poll.endDate; - const optionsTotalVotes = await this.getPollQuadraticResults(id); + now <= poll.endDate + const optionsTotalVotes = await this.getPollQuadraticResults(id) const totalVotes = Object.values(optionsTotalVotes).reduce( (acc, votes) => acc + votes, 0, - ); - return { poll, isActive, optionsTotalVotes, totalVotes }; + ) + return { poll, isActive, optionsTotalVotes, totalVotes } } async deletePoll(pollId: number, worldID: string) { const user = await this.databaseService.user.findUnique({ where: { worldID }, select: { id: true }, - }); + }) if (!user) { - throw new UserNotFoundException(); + throw new UserNotFoundException() } const poll = await this.databaseService.poll.findUnique({ where: { pollId }, - }); + }) if (!poll) { - throw new PollNotFoundException(); + throw new PollNotFoundException() } if (poll.authorUserId !== user.id) { - throw new UnauthorizedActionException(); + throw new UnauthorizedActionException() } - return this.databaseService.$transaction(async (tx) => { + return this.databaseService.$transaction(async tx => { // If it's a published poll, update user action counts if (poll.status === PollStatus.PUBLISHED) { const pollParticipants = await tx.userAction.findMany({ where: { pollId, type: ActionType.VOTED }, select: { userId: true }, - }); + }) const participantUserIds = [ - ...new Set(pollParticipants.map((v) => v.userId)), - ]; + ...new Set(pollParticipants.map(v => v.userId)), + ] const deleted = await tx.poll.delete({ where: { pollId, }, - }); + }) await this.userService.updateUserPollsCount( deleted.authorUserId, ActionType.CREATED, tx, - ); + ) for (const userId of participantUserIds) { await this.userService.updateUserPollsCount( userId, ActionType.VOTED, tx, - ); + ) } - return deleted; + return deleted } else { // If it's a draft poll, simply delete it without updating counts return await tx.poll.delete({ where: { pollId, }, - }); + }) } - }); + }) } async getPollVotes(pollId: number) { @@ -556,10 +556,10 @@ export class PollService { pollId: true, title: true, }, - }); + }) if (!poll) { - throw new PollNotFoundException(); + throw new PollNotFoundException() } const select = IS_VOTE_NORMALIZATION ? { @@ -571,32 +571,32 @@ export class PollService { quadraticWeights: true, weightDistribution: true, user: { select: { worldID: true, name: true } }, - }; + } const votes = await this.databaseService.vote.findMany({ where: { pollId }, select, - }); + }) - const formattedVotes = votes.map((vote) => { + const formattedVotes = votes.map(vote => { const quadraticWeights = IS_VOTE_NORMALIZATION ? vote.normalizedQuadraticWeights - : vote.quadraticWeights; + : vote.quadraticWeights const totalQuadraticWeights = Object.values( quadraticWeights as Record, - ).reduce((sum, value) => sum + value, 0); + ).reduce((sum, value) => sum + value, 0) return { username: vote.user.name, quadraticWeights, totalQuadraticWeights, - }; - }); + } + }) return { votes: formattedVotes, pollTitle: poll.title, pollId: poll.pollId, - }; + } } async getPollQuadraticResults( @@ -608,52 +608,52 @@ export class PollService { status: PollStatus.PUBLISHED, }, select: { options: true }, - }); + }) if (!poll) { - throw new PollNotFoundException(); + throw new PollNotFoundException() } const select = IS_VOTE_NORMALIZATION ? { normalizedQuadraticWeights: true } - : { quadraticWeights: true }; + : { quadraticWeights: true } const votes = await this.databaseService.vote.findMany({ where: { pollId }, select, - }); + }) const result: Record = poll.options.reduce( (acc, option) => { - acc[option] = 0; - return acc; + acc[option] = 0 + return acc }, {} as Record, - ); - votes.forEach((vote) => { + ) + votes.forEach(vote => { const weights = IS_VOTE_NORMALIZATION ? vote.normalizedQuadraticWeights - : vote.quadraticWeights; + : vote.quadraticWeights if (weights && typeof weights === 'object') { Object.entries(weights).forEach(([option, weight]) => { if (typeof weight === 'number') { - result[option] = (result[option] || 0) + weight; + result[option] = (result[option] || 0) + weight } - }); + }) } - }); - return result; + }) + return result } async getPollsCount(query: GetCountDto): Promise { - const { from, to } = query; - const where: Prisma.PollWhereInput = {}; + const { from, to } = query + const where: Prisma.PollWhereInput = {} if (from && to) { - where.creationDate = { gte: new Date(from), lte: new Date(to) }; + where.creationDate = { gte: new Date(from), lte: new Date(to) } } else if (from) { - where.creationDate = { gte: new Date(from) }; + where.creationDate = { gte: new Date(from) } } else if (to) { - where.creationDate = { lte: new Date(to) }; + where.creationDate = { lte: new Date(to) } } - return await this.databaseService.poll.count({ where }); + return await this.databaseService.poll.count({ where }) } } diff --git a/src/types/express.d.ts b/src/types/express.d.ts index 53a3594..6c052c1 100644 --- a/src/types/express.d.ts +++ b/src/types/express.d.ts @@ -1,9 +1,9 @@ -import { JwtPayload } from '../auth/jwt-auth.guard'; +import { JwtPayload } from '../auth/jwt-auth.guard' declare global { namespace Express { interface Request { - user?: JwtPayload; + user?: JwtPayload } } } diff --git a/src/user/user.controller.ts b/src/user/user.controller.ts index 923824b..2c8e7d9 100644 --- a/src/user/user.controller.ts +++ b/src/user/user.controller.ts @@ -1,4 +1,6 @@ -import { Body, Controller, Get, Post, Query } from '@nestjs/common'; +import { Body, Controller, Get, Post, Query } from '@nestjs/common' +import { User } from 'src/auth/user.decorator' +import { Public } from 'src/auth/jwt-auth.guard' import { CreateUserDto, CreateUserResponseDto, @@ -12,11 +14,9 @@ import { UserActivitiesResponseDto, UserDataResponseDto, UserVotesResponseDto, -} from './user.dto'; -import { UserService } from './user.service'; -import { User } from 'src/auth/user.decorator'; -import { Public } from 'src/auth/jwt-auth.guard'; -import { GetCountDto } from '../common/common.dto'; +} from './user.dto' +import { UserService } from './user.service' +import { GetCountDto } from '../common/common.dto' @Controller('user') export class UserController { @@ -26,14 +26,14 @@ export class UserController { async getUserData( @Query() query: GetUserDataDto, ): Promise { - return await this.userService.getUserData(query); + return await this.userService.getUserData(query) } @Get('getUserActivities') async getUserActivities( @Query() query: GetUserActivitiesDto, ): Promise { - return await this.userService.getUserActivities(query); + return await this.userService.getUserActivities(query) } @Get('getUserVotes') @@ -41,7 +41,7 @@ export class UserController { @Query() query: GetUserVotesDto, @User('worldID') worldID: string, ): Promise { - return await this.userService.getUserVotes(query, worldID); + return await this.userService.getUserVotes(query, worldID) } @Post('setVote') @@ -49,7 +49,7 @@ export class UserController { @Body() dto: SetVoteDto, @User('worldID') worldID: string, ): Promise { - return await this.userService.setVote(dto, worldID); + return await this.userService.setVote(dto, worldID) } @Post('editVote') @@ -57,17 +57,17 @@ export class UserController { @Body() dto: EditVoteDto, @User('worldID') worldID: string, ): Promise { - return await this.userService.editVote(dto, worldID); + return await this.userService.editVote(dto, worldID) } @Post('createUser') async createUser(@Body() dto: CreateUserDto): Promise { - return await this.userService.createUser(dto); + return await this.userService.createUser(dto) } @Get('count') @Public() async getUserCount(@Query() query: GetCountDto): Promise { - return await this.userService.getUserCount(query); + return await this.userService.getUserCount(query) } } diff --git a/src/user/user.dto.ts b/src/user/user.dto.ts index 1f6863c..f1f5982 100644 --- a/src/user/user.dto.ts +++ b/src/user/user.dto.ts @@ -1,5 +1,5 @@ -import { ActionType } from '@prisma/client'; -import { Transform, Type } from 'class-transformer'; +import { ActionType } from '@prisma/client' +import { Transform, Type } from 'class-transformer' import { IsArray, IsBoolean, @@ -11,189 +11,189 @@ import { IsOptional, IsString, Validate, -} from 'class-validator'; -import { IsPositiveInteger, IsRecordStringNumber } from '../common/validators'; +} from 'class-validator' +import { IsPositiveInteger, IsRecordStringNumber } from '../common/validators' export class GetUserDataDto { @IsString() @IsNotEmpty() - worldID: string; + worldID: string } export class UserDataResponseDto { @IsNumber() @IsNotEmpty() - pollsCreated: number; + pollsCreated: number @IsNumber() @IsNotEmpty() - pollsParticipated: number; + pollsParticipated: number @IsString() @IsNotEmpty() - worldID: string; + worldID: string @IsString() @IsOptional() - worldProfilePic?: string | null; + worldProfilePic?: string | null @IsString() @IsOptional() - name?: string | null; + name?: string | null } export class GetUserActivitiesDto { @IsString() @IsNotEmpty() - worldID: string; + worldID: string @IsOptional() @IsBoolean() @Transform(({ value }) => value === 'true') - isActive?: boolean; + isActive?: boolean @IsOptional() @IsBoolean() @Transform(({ value }) => value === 'true') - isInactive?: boolean; + isInactive?: boolean @IsOptional() @IsBoolean() @Transform(({ value }) => value === 'true') - isCreated?: boolean; + isCreated?: boolean @IsOptional() @IsBoolean() @Transform(({ value }) => value === 'true') - isParticipated?: boolean; + isParticipated?: boolean @IsString() @IsOptional() - search?: string; + search?: string } export class UserActionDto { @IsInt() - id: number; + id: number @IsEnum([ActionType.CREATED, ActionType.VOTED]) - type: ActionType; + type: ActionType @IsInt() - pollId: number; + pollId: number @IsString() - pollTitle: string; + pollTitle: string @IsString() - pollDescription: string; + pollDescription: string @IsDateString() - endDate: string; + endDate: string @IsBoolean() - isActive: boolean; + isActive: boolean @IsInt() - votersParticipated: number; + votersParticipated: number @IsString() - authorWorldId: string; + authorWorldId: string @IsString() - authorName: string; + authorName: string @IsString() @IsOptional() - authorProfilePic?: string | null; + authorProfilePic?: string | null @IsDateString() - createdAt: string; + createdAt: string } export class UserActivitiesResponseDto { @IsArray() @Type(() => UserActionDto) - userActions: UserActionDto[]; + userActions: UserActionDto[] } export class GetUserVotesDto { @IsNotEmpty() @Validate(IsPositiveInteger) - @Transform(({ value }) => parseInt(value, 10)) - pollId: number; + @Transform(({ value }) => parseInt(value as string, 10)) + pollId: number } export class UserVotesResponseDto { @IsNotEmpty() @IsString() - voteID: string; + voteID: string @IsNotEmpty() @IsString({ each: true }) - options: string[]; + options: string[] @IsNumber() @IsNotEmpty() - votingPower: number; + votingPower: number @Validate(IsRecordStringNumber) @IsOptional() - weightDistribution?: Record; + weightDistribution?: Record } export class SetVoteDto { @IsNotEmpty() @Validate(IsPositiveInteger) - pollId: number; + pollId: number @IsNotEmpty() @Validate(IsRecordStringNumber) - weightDistribution: Record; + weightDistribution: Record } export class SetVoteResponseDto { @IsNotEmpty() @IsString() - voteID: string; + voteID: string @IsNotEmpty() @IsNumber() - actionId: number; + actionId: number } export class EditVoteDto { @IsNotEmpty() @IsString() - voteID: string; + voteID: string @IsNotEmpty() @Validate(IsRecordStringNumber) - weightDistribution: Record; + weightDistribution: Record } export class EditVoteResponseDto { @IsNotEmpty() @IsNumber() - actionId: number; + actionId: number } export class CreateUserDto { @IsString() @IsNotEmpty() - name: string; + name: string @IsString() @IsNotEmpty() - worldID: string; + worldID: string @IsString() @IsOptional() - profilePicture?: string; + profilePicture?: string } export class CreateUserResponseDto { @IsNotEmpty() @IsNumber() - userId: number; + userId: number } diff --git a/src/user/user.module.ts b/src/user/user.module.ts index 053ebec..9249f2b 100644 --- a/src/user/user.module.ts +++ b/src/user/user.module.ts @@ -1,8 +1,8 @@ -import { forwardRef, Module } from '@nestjs/common'; -import { UserController } from './user.controller'; -import { UserService } from './user.service'; -import { PollModule } from '../poll/poll.module'; -import { PollService } from '../poll/poll.service'; +import { forwardRef, Module } from '@nestjs/common' +import { UserController } from './user.controller' +import { UserService } from './user.service' +import { PollModule } from '../poll/poll.module' +import { PollService } from '../poll/poll.service' @Module({ imports: [forwardRef(() => PollModule)], diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 1e2e5af..82b7339 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -1,7 +1,7 @@ -import { forwardRef, Inject, Injectable } from '@nestjs/common'; -import { ActionType, PollStatus, Prisma } from '@prisma/client'; -import { GetCountDto } from '../common/common.dto'; -import { VOTING_POWER } from '../common/constants'; +import { forwardRef, Inject, Injectable } from '@nestjs/common' +import { ActionType, PollStatus, Prisma } from '@prisma/client' +import { GetCountDto } from '../common/common.dto' +import { VOTING_POWER } from '../common/constants' import { CreateUserException, DuplicateVoteException, @@ -11,9 +11,9 @@ import { UserNotFoundException, VoteNotFoundException, VoteOptionException, -} from '../common/exceptions'; -import { DatabaseService } from '../database/database.service'; -import { PollService } from '../poll/poll.service'; +} from '../common/exceptions' +import { DatabaseService } from '../database/database.service' +import { PollService } from '../poll/poll.service' import { CreateUserDto, CreateUserResponseDto, @@ -28,7 +28,7 @@ import { UserActivitiesResponseDto, UserDataResponseDto, UserVotesResponseDto, -} from './user.dto'; +} from './user.dto' @Injectable() export class UserService { @@ -42,31 +42,31 @@ export class UserService { weightDistribution: Record, pollOptions: string[], ): void { - const weightKeys = Object.keys(weightDistribution); + const weightKeys = Object.keys(weightDistribution) const areWeightKeysMatching = weightKeys.length === pollOptions.length && - weightKeys.every((key) => pollOptions.includes(key)); + weightKeys.every(key => pollOptions.includes(key)) if (!areWeightKeysMatching) { throw new VoteOptionException( 'Weight distribution keys do not match poll options exactly', - ); + ) } const isPositiveWeight = Object.values(weightDistribution).every( - (weight) => weight >= 0, - ); + weight => weight >= 0, + ) if (!isPositiveWeight) { throw new VoteOptionException( 'Weight distribution values must be positive', - ); + ) } const totalWeight = Object.values(weightDistribution).reduce( (acc, weight) => acc + weight, 0, - ); + ) if (totalWeight > VOTING_POWER) { throw new VoteOptionException( `Total weight distribution must be equal or lower than the voting power of ${VOTING_POWER}`, - ); + ) } } @@ -75,10 +75,10 @@ export class UserService { type: ActionType, prismaClient?: Prisma.TransactionClient, // To ensure Prisma transaction function runs queries in order ) { - const prisma = prismaClient || this.databaseService; + const prisma = prismaClient || this.databaseService const pollsCount = await prisma.userAction.count({ where: { userId, type }, - }); + }) await prisma.user.update({ where: { id: userId }, data: { @@ -86,7 +86,7 @@ export class UserService { pollsParticipatedCount: type === ActionType.VOTED ? pollsCount : undefined, }, - }); + }) } async getUserData(dto: GetUserDataDto): Promise { @@ -100,9 +100,9 @@ export class UserService { pollsCreatedCount: true, pollsParticipatedCount: true, }, - }); + }) if (!user) { - throw new UserNotFoundException(); + throw new UserNotFoundException() } return { pollsCreated: user.pollsCreatedCount, @@ -110,7 +110,7 @@ export class UserService { worldID: user.worldID, worldProfilePic: user.profilePicture, name: user.name, - }; + } } async getUserActivities( @@ -119,65 +119,65 @@ export class UserService { const user = await this.databaseService.user.findUnique({ where: { worldID: dto.worldID }, select: { id: true }, - }); + }) if (!user) { - throw new UserNotFoundException(); + throw new UserNotFoundException() } const filters: Prisma.UserActionWhereInput = { userId: user.id, poll: { status: PollStatus.PUBLISHED }, - }; - const now = new Date(); + } + const now = new Date() if (dto.isActive || dto.isInactive) { if (dto.isActive && !dto.isInactive) { filters.poll = { status: PollStatus.PUBLISHED, endDate: { gte: now }, - }; + } } else if (!dto.isActive && dto.isInactive) { filters.poll = { status: PollStatus.PUBLISHED, endDate: { lt: now }, - }; + } } else { // If both are true or both are false, only filter by status - filters.poll = { status: PollStatus.PUBLISHED }; + filters.poll = { status: PollStatus.PUBLISHED } } } if (dto.isCreated || dto.isParticipated) { if (dto.isCreated && !dto.isParticipated) { - filters.type = ActionType.CREATED; + filters.type = ActionType.CREATED } else if (!dto.isCreated && dto.isParticipated) { - filters.type = ActionType.VOTED; + filters.type = ActionType.VOTED } else if (dto.isCreated && dto.isParticipated) { - filters.OR = [{ type: ActionType.CREATED }, { type: ActionType.VOTED }]; + filters.OR = [{ type: ActionType.CREATED }, { type: ActionType.VOTED }] } } - let pollIds: number[] | undefined; + let pollIds: number[] | undefined if (dto.search) { - pollIds = await this.pollService['searchPolls'](dto.search); + pollIds = await this.pollService['searchPolls'](dto.search) if (pollIds.length === 0) { - return { userActions: [] }; + return { userActions: [] } } // Add poll IDs to the filter if (filters.poll) { - filters.poll.pollId = { in: pollIds }; + filters.poll.pollId = { in: pollIds } } else if (!filters.OR) { // Only add if we're not already using OR for types - filters.poll = { pollId: { in: pollIds } }; + filters.poll = { pollId: { in: pollIds } } } else { // We have OR condition for types, need to add poll filter to each - filters.OR = filters.OR.map((condition) => ({ + filters.OR = filters.OR.map(condition => ({ ...condition, poll: { pollId: { in: pollIds } }, - })); + })) } } @@ -200,15 +200,15 @@ export class UserService { }, }, }, - }); + }) const actions: UserActionDto[] = await Promise.all( // TODO: it's a temporary work around, should add authorWorldId to UserAction later - userActions.map(async (action) => { + userActions.map(async action => { const author = await this.databaseService.user.findUnique({ where: { id: action.poll.authorUserId }, select: { worldID: true, name: true, profilePicture: true }, - }); + }) return { id: action.id, type: action.type, @@ -223,10 +223,10 @@ export class UserService { authorName: author?.name || '', authorProfilePic: author?.profilePicture || null, createdAt: action.createdAt.toISOString(), - }; + } }), - ); - return { userActions: actions }; + ) + return { userActions: actions } } async getUserVotes( @@ -236,9 +236,9 @@ export class UserService { const user = await this.databaseService.user.findUnique({ where: { worldID }, select: { id: true }, - }); + }) if (!user) { - throw new UserNotFoundException(); + throw new UserNotFoundException() } const poll = await this.databaseService.poll.findUnique({ where: { @@ -246,9 +246,9 @@ export class UserService { status: PollStatus.PUBLISHED, }, select: { endDate: true, options: true }, - }); + }) if (!poll || (poll.endDate && poll.endDate < new Date())) { - throw new PollNotFoundException(); + throw new PollNotFoundException() } const vote = await this.databaseService.vote.findFirst({ where: { @@ -260,25 +260,25 @@ export class UserService { votingPower: true, weightDistribution: true, }, - }); + }) if (!vote) { - throw new VoteNotFoundException(); + throw new VoteNotFoundException() } return { voteID: vote.voteID, options: poll.options, votingPower: vote.votingPower, weightDistribution: vote.weightDistribution as Record, - }; + } } async setVote(dto: SetVoteDto, worldID: string): Promise { const user = await this.databaseService.user.findUnique({ where: { worldID }, select: { id: true }, - }); + }) if (!user) { - throw new UserNotFoundException(); + throw new UserNotFoundException() } const poll = await this.databaseService.poll.findUnique({ where: { @@ -286,21 +286,21 @@ export class UserService { status: PollStatus.PUBLISHED, }, select: { endDate: true, options: true }, - }); + }) if (!poll || (poll.endDate && poll.endDate < new Date())) { - throw new PollNotFoundException(); + throw new PollNotFoundException() } - this.validateWeightDistribution(dto.weightDistribution, poll.options); + this.validateWeightDistribution(dto.weightDistribution, poll.options) const existingVote = await this.databaseService.vote.findFirst({ where: { pollId: dto.pollId, userId: user.id, }, - }); + }) if (existingVote) { - throw new DuplicateVoteException(); + throw new DuplicateVoteException() } - return this.databaseService.$transaction(async (prisma) => { + return this.databaseService.$transaction(async prisma => { const vote = await prisma.vote.create({ data: { userId: user.id, @@ -309,21 +309,21 @@ export class UserService { weightDistribution: dto.weightDistribution, proof: '', // Implement Bandada proof in next phase }, - }); + }) const action = await prisma.userAction.create({ data: { userId: user.id, pollId: dto.pollId, type: ActionType.VOTED, }, - }); - await this.updateUserPollsCount(user.id, ActionType.VOTED, prisma); - await this.pollService.updatePollParticipantCount(dto.pollId, prisma); + }) + await this.updateUserPollsCount(user.id, ActionType.VOTED, prisma) + await this.pollService.updatePollParticipantCount(dto.pollId, prisma) return { voteID: vote.voteID, actionId: action.id, - }; - }); + } + }) } async editVote( @@ -339,34 +339,34 @@ export class UserService { select: { endDate: true, options: true, status: true }, }, }, - }); + }) if (!vote) { - throw new VoteNotFoundException(); + throw new VoteNotFoundException() } if (vote.poll.status !== PollStatus.PUBLISHED) { - throw new PollNotFoundException(); + throw new PollNotFoundException() } if (vote.poll.endDate && vote.poll.endDate < new Date()) { - throw new PollNotFoundException(); + throw new PollNotFoundException() } // TODO: should add worldID to Vote later const user = await this.databaseService.user.findUnique({ where: { id: vote.userId }, select: { worldID: true }, - }); + }) if (user?.worldID !== worldID) { - throw new UnauthorizedActionException(); + throw new UnauthorizedActionException() } - this.validateWeightDistribution(dto.weightDistribution, vote.poll.options); - return this.databaseService.$transaction(async (prisma) => { + this.validateWeightDistribution(dto.weightDistribution, vote.poll.options) + return this.databaseService.$transaction(async prisma => { const updatedVote = await prisma.vote.update({ where: { voteID: dto.voteID }, data: { weightDistribution: dto.weightDistribution, }, - }); + }) const userAction = await prisma.userAction.findFirst({ where: { userId: vote.userId, @@ -374,26 +374,26 @@ export class UserService { type: ActionType.VOTED, }, select: { id: true }, - }); + }) if (!userAction) { - throw new UserActionNotFoundException(); + throw new UserActionNotFoundException() } return { actionId: userAction.id, - }; - }); + } + }) } async createUser(dto: CreateUserDto): Promise { - return this.databaseService.$transaction(async (prisma) => { + return this.databaseService.$transaction(async prisma => { const existingUser = await prisma.user.findUnique({ where: { worldID: dto.worldID }, select: { id: true }, - }); + }) if (existingUser) { return { userId: existingUser.id, - }; + } } const newUser = await prisma.user.create({ data: { @@ -401,28 +401,28 @@ export class UserService { worldID: dto.worldID, profilePicture: dto.profilePicture || null, }, - }); + }) if (!newUser) { - throw new CreateUserException(); + throw new CreateUserException() } return { userId: newUser.id, - }; - }); + } + }) } async getUserCount(query: GetCountDto): Promise { - const { from, to } = query; - const where: Prisma.UserWhereInput = {}; + const { from, to } = query + const where: Prisma.UserWhereInput = {} if (from && to) { - where.createdAt = { gte: new Date(from), lte: new Date(to) }; + where.createdAt = { gte: new Date(from), lte: new Date(to) } } else if (from) { - where.createdAt = { gte: new Date(from) }; + where.createdAt = { gte: new Date(from) } } else if (to) { - where.createdAt = { lte: new Date(to) }; + where.createdAt = { lte: new Date(to) } } - return await this.databaseService.user.count({ where }); + return await this.databaseService.user.count({ where }) } } diff --git a/src/vote/vote.controller.ts b/src/vote/vote.controller.ts index 1e4a6d8..bc2a18a 100644 --- a/src/vote/vote.controller.ts +++ b/src/vote/vote.controller.ts @@ -1,7 +1,7 @@ -import { Controller, Get, Query } from '@nestjs/common'; -import { Public } from 'src/auth/jwt-auth.guard'; -import { GetCountDto } from '../common/common.dto'; -import { VoteService } from './vote.service'; +import { Controller, Get, Query } from '@nestjs/common' +import { Public } from 'src/auth/jwt-auth.guard' +import { GetCountDto } from '../common/common.dto' +import { VoteService } from './vote.service' @Controller('vote') export class VoteController { @@ -10,6 +10,6 @@ export class VoteController { @Get('count') @Public() async getVotesCount(@Query() query: GetCountDto) { - return await this.voteService.getVotesCount(query); + return await this.voteService.getVotesCount(query) } } diff --git a/src/vote/vote.module.ts b/src/vote/vote.module.ts index ba0b697..40af648 100644 --- a/src/vote/vote.module.ts +++ b/src/vote/vote.module.ts @@ -1,6 +1,6 @@ -import { Module } from '@nestjs/common'; -import { VoteController } from './vote.controller'; -import { VoteService } from './vote.service'; +import { Module } from '@nestjs/common' +import { VoteController } from './vote.controller' +import { VoteService } from './vote.service' @Module({ imports: [], diff --git a/src/vote/vote.service.ts b/src/vote/vote.service.ts index 36207cd..db42aed 100644 --- a/src/vote/vote.service.ts +++ b/src/vote/vote.service.ts @@ -1,26 +1,26 @@ -import { Injectable } from '@nestjs/common'; -import { ActionType, Prisma } from '@prisma/client'; -import { DatabaseService } from 'src/database/database.service'; -import { GetCountDto } from '../common/common.dto'; +import { Injectable } from '@nestjs/common' +import { ActionType, Prisma } from '@prisma/client' +import { DatabaseService } from 'src/database/database.service' +import { GetCountDto } from '../common/common.dto' @Injectable() export class VoteService { constructor(private readonly databaseService: DatabaseService) {} async getVotesCount(query: GetCountDto) { - const { from, to } = query; - const where: Prisma.UserActionWhereInput = { type: ActionType.VOTED }; + const { from, to } = query + const where: Prisma.UserActionWhereInput = { type: ActionType.VOTED } if (from && to) { - where.createdAt = { gte: new Date(from), lte: new Date(to) }; + where.createdAt = { gte: new Date(from), lte: new Date(to) } } else if (from) { - where.createdAt = { gte: new Date(from) }; + where.createdAt = { gte: new Date(from) } } else if (to) { - where.createdAt = { lte: new Date(to) }; + where.createdAt = { lte: new Date(to) } } return await this.databaseService.userAction.count({ where, - }); + }) } } diff --git a/test/app.e2e-spec.ts b/test/app.e2e-spec.ts index 4df6580..f032630 100644 --- a/test/app.e2e-spec.ts +++ b/test/app.e2e-spec.ts @@ -1,25 +1,25 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { INestApplication } from '@nestjs/common'; -import * as request from 'supertest'; -import { App } from 'supertest/types'; -import { AppModule } from './../src/app.module'; +import { INestApplication } from '@nestjs/common' +import { Test, TestingModule } from '@nestjs/testing' +import request from 'supertest' +import { App } from 'supertest/types' +import { AppModule } from './../src/app.module' describe('AppController (e2e)', () => { - let app: INestApplication; + let app: INestApplication beforeEach(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ imports: [AppModule], - }).compile(); + }).compile() - app = moduleFixture.createNestApplication(); - await app.init(); - }); + app = moduleFixture.createNestApplication() + await app.init() + }) it('/ (GET)', () => { return request(app.getHttpServer()) .get('/') .expect(200) - .expect('Hello World!'); - }); -}); + .expect('Hello World!') + }) +}) diff --git a/yarn.lock b/yarn.lock index 246d44c..0b60253 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1386,6 +1386,11 @@ dependencies: "@prisma/debug" "6.6.0" +"@rtsao/scc@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" + integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== + "@scure/base@~1.2.2", "@scure/base@~1.2.4": version "1.2.4" resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.2.4.tgz#002eb571a35d69bdb4c214d0995dff76a8dcd2a9" @@ -1712,6 +1717,11 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== + "@types/jsonwebtoken@^9.0.9": version "9.0.9" resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.9.tgz#a4c3a446c0ebaaf467a58398382616f416345fb3" @@ -2292,16 +2302,87 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== +array-buffer-byte-length@^1.0.1, array-buffer-byte-length@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz#384d12a37295aec3769ab022ad323a18a51ccf8b" + integrity sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw== + dependencies: + call-bound "^1.0.3" + is-array-buffer "^3.0.5" + +array-includes@^3.1.8: + version "3.1.8" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.8.tgz#5e370cbe172fdd5dd6530c1d4aadda25281ba97d" + integrity sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.4" + is-string "^1.0.7" + array-timsort@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/array-timsort/-/array-timsort-1.0.3.tgz#3c9e4199e54fb2b9c3fe5976396a21614ef0d926" integrity sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ== +array.prototype.findlastindex@^1.2.5: + version "1.2.6" + resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz#cfa1065c81dcb64e34557c9b81d012f6a421c564" + integrity sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.4" + define-properties "^1.2.1" + es-abstract "^1.23.9" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + es-shim-unscopables "^1.1.0" + +array.prototype.flat@^1.3.2: + version "1.3.3" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz#534aaf9e6e8dd79fb6b9a9917f839ef1ec63afe5" + integrity sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg== + dependencies: + call-bind "^1.0.8" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-shim-unscopables "^1.0.2" + +array.prototype.flatmap@^1.3.2: + version "1.3.3" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz#712cc792ae70370ae40586264629e33aab5dd38b" + integrity sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg== + dependencies: + call-bind "^1.0.8" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-shim-unscopables "^1.0.2" + +arraybuffer.prototype.slice@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz#9d760d84dbdd06d0cbf92c8849615a1a7ab3183c" + integrity sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ== + dependencies: + array-buffer-byte-length "^1.0.1" + call-bind "^1.0.8" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + is-array-buffer "^3.0.4" + asap@^2.0.0: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== +async-function@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/async-function/-/async-function-1.0.0.tgz#509c9fca60eaf85034c6829838188e4e4c8ffb2b" + integrity sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA== + async@^3.2.3: version "3.2.6" resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" @@ -2312,6 +2393,13 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== +available-typed-arrays@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" + integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== + dependencies: + possible-typed-array-names "^1.0.0" + b4a@^1.6.4: version "1.6.7" resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.7.tgz#a99587d4ebbfbd5a6e3b21bdb5d5fa385767abe4" @@ -2553,7 +2641,7 @@ cacheable-request@^10.2.8: normalize-url "^8.0.0" responselike "^3.0.0" -call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: +call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== @@ -2561,7 +2649,17 @@ call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: es-errors "^1.3.0" function-bind "^1.1.2" -call-bound@^1.0.2: +call-bind@^1.0.7, call-bind@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.8.tgz#0736a9660f537e3388826f440d5ec45f744eaa4c" + integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww== + dependencies: + call-bind-apply-helpers "^1.0.0" + es-define-property "^1.0.0" + get-intrinsic "^1.2.4" + set-function-length "^1.2.2" + +call-bound@^1.0.2, call-bound@^1.0.3, call-bound@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== @@ -2906,6 +3004,40 @@ css-what@1.0: resolved "https://registry.yarnpkg.com/css-what/-/css-what-1.0.0.tgz#d7cc2df45180666f99d2b14462639469e00f736c" integrity sha512-60SUMPBreXrLXgvpM8kYpO0AOyMRhdRlXFX5BMQbZq1SIJCyNE56nqFQhmvREQdUJpedbGRYZ5wOyq3/F6q5Zw== +data-view-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.2.tgz#211a03ba95ecaf7798a8c7198d79536211f88570" + integrity sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + is-data-view "^1.0.2" + +data-view-byte-length@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz#9e80f7ca52453ce3e93d25a35318767ea7704735" + integrity sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + is-data-view "^1.0.2" + +data-view-byte-offset@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz#068307f9b71ab76dbbe10291389e020856606191" + integrity sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5, debug@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" @@ -2952,6 +3084,24 @@ defer-to-connect@^2.0.1: resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== +define-data-property@^1.0.1, define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + +define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -2990,6 +3140,13 @@ diff@^4.0.1: resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" + dom-serializer@0: version "0.2.2" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" @@ -3038,7 +3195,7 @@ domutils@1.5: dom-serializer "0" domelementtype "1" -dunder-proto@^1.0.1: +dunder-proto@^1.0.0, dunder-proto@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== @@ -3133,7 +3290,64 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-define-property@^1.0.1: +es-abstract@^1.23.2, es-abstract@^1.23.5, es-abstract@^1.23.9: + version "1.23.10" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.10.tgz#84792c152ff2898ec73efe33c1c1323a3dfd87f8" + integrity sha512-MtUbM072wlJNyeYAe0mhzrD+M6DIJa96CZAOBBrhDbgKnB4MApIKefcyAB1eOdYn8cUNZgvwBvEzdoAYsxgEIw== + dependencies: + array-buffer-byte-length "^1.0.2" + arraybuffer.prototype.slice "^1.0.4" + available-typed-arrays "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.4" + data-view-buffer "^1.0.2" + data-view-byte-length "^1.0.2" + data-view-byte-offset "^1.0.1" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + es-set-tostringtag "^2.1.0" + es-to-primitive "^1.3.0" + function.prototype.name "^1.1.8" + get-intrinsic "^1.3.0" + get-proto "^1.0.1" + get-symbol-description "^1.1.0" + globalthis "^1.0.4" + gopd "^1.2.0" + has-property-descriptors "^1.0.2" + has-proto "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + internal-slot "^1.1.0" + is-array-buffer "^3.0.5" + is-callable "^1.2.7" + is-data-view "^1.0.2" + is-regex "^1.2.1" + is-shared-array-buffer "^1.0.4" + is-string "^1.1.1" + is-typed-array "^1.1.15" + is-weakref "^1.1.1" + math-intrinsics "^1.1.0" + object-inspect "^1.13.4" + object-keys "^1.1.1" + object.assign "^4.1.7" + own-keys "^1.0.1" + regexp.prototype.flags "^1.5.4" + safe-array-concat "^1.1.3" + safe-push-apply "^1.0.0" + safe-regex-test "^1.1.0" + set-proto "^1.0.0" + string.prototype.trim "^1.2.10" + string.prototype.trimend "^1.0.9" + string.prototype.trimstart "^1.0.8" + typed-array-buffer "^1.0.3" + typed-array-byte-length "^1.0.3" + typed-array-byte-offset "^1.0.4" + typed-array-length "^1.0.7" + unbox-primitive "^1.1.0" + which-typed-array "^1.1.19" + +es-define-property@^1.0.0, es-define-property@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== @@ -3165,6 +3379,22 @@ es-set-tostringtag@^2.1.0: has-tostringtag "^1.0.2" hasown "^2.0.2" +es-shim-unscopables@^1.0.2, es-shim-unscopables@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz#438df35520dac5d105f3943d927549ea3b00f4b5" + integrity sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw== + dependencies: + hasown "^2.0.2" + +es-to-primitive@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.3.0.tgz#96c89c82cc49fd8794a24835ba3e1ff87f214e18" + integrity sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g== + dependencies: + is-callable "^1.2.7" + is-date-object "^1.0.5" + is-symbol "^1.0.4" + esbuild-register@3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/esbuild-register/-/esbuild-register-3.6.0.tgz#cf270cfa677baebbc0010ac024b823cbf723a36d" @@ -3228,6 +3458,47 @@ eslint-config-prettier@^10.0.1: resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-10.1.1.tgz#cf0ff6e5c4e7e15f129f1f1ce2a5ecba92dec132" integrity sha512-4EQQr6wXwS+ZJSzaR5ZCrYgLxqvUjdXctaEtBqHcbkW944B1NQyO4qpdHQbXBONfwxXdkAY81HH4+LUfrg+zPw== +eslint-import-resolver-node@^0.3.9: + version "0.3.9" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" + integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== + dependencies: + debug "^3.2.7" + is-core-module "^2.13.0" + resolve "^1.22.4" + +eslint-module-utils@^2.12.0: + version "2.12.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz#fe4cfb948d61f49203d7b08871982b65b9af0b0b" + integrity sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg== + dependencies: + debug "^3.2.7" + +eslint-plugin-import@^2.31.0: + version "2.31.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz#310ce7e720ca1d9c0bb3f69adfd1c6bdd7d9e0e7" + integrity sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A== + dependencies: + "@rtsao/scc" "^1.1.0" + array-includes "^3.1.8" + array.prototype.findlastindex "^1.2.5" + array.prototype.flat "^1.3.2" + array.prototype.flatmap "^1.3.2" + debug "^3.2.7" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.9" + eslint-module-utils "^2.12.0" + hasown "^2.0.2" + is-core-module "^2.15.1" + is-glob "^4.0.3" + minimatch "^3.1.2" + object.fromentries "^2.0.8" + object.groupby "^1.0.3" + object.values "^1.2.0" + semver "^6.3.1" + string.prototype.trimend "^1.0.8" + tsconfig-paths "^3.15.0" + eslint-plugin-prettier@^5.2.2: version "5.2.5" resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.5.tgz#0ff00b16f4c80ccdafd6a24a263effba1700087e" @@ -3600,6 +3871,13 @@ flatted@^3.2.9: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358" integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== +for-each@^0.3.3, for-each@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.5.tgz#d650688027826920feeb0af747ee7b9421a41d47" + integrity sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg== + dependencies: + is-callable "^1.2.7" + foreground-child@^3.1.0: version "3.3.1" resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f" @@ -3694,6 +3972,23 @@ function-bind@^1.1.2: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== +function.prototype.name@^1.1.6, function.prototype.name@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.8.tgz#e68e1df7b259a5c949eeef95cdbde53edffabb78" + integrity sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + define-properties "^1.2.1" + functions-have-names "^1.2.3" + hasown "^2.0.2" + is-callable "^1.2.7" + +functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + 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" @@ -3704,7 +3999,7 @@ get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.3.0: +get-intrinsic@^1.2.4, get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.2.7, get-intrinsic@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== @@ -3725,7 +4020,7 @@ get-package-type@^0.1.0: resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== -get-proto@^1.0.1: +get-proto@^1.0.0, get-proto@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== @@ -3746,6 +4041,15 @@ get-stream@^9.0.1: "@sec-ant/readable-stream" "^0.4.1" is-stream "^4.0.1" +get-symbol-description@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.1.0.tgz#7bdd54e0befe8ffc9f3b4e203220d9f1e881b6ee" + integrity sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -3804,7 +4108,15 @@ globals@^15.14.0: resolved "https://registry.yarnpkg.com/globals/-/globals-15.15.0.tgz#7c4761299d41c32b075715a4ce1ede7897ff72a8" integrity sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg== -gopd@^1.2.0: +globalthis@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" + integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== + dependencies: + define-properties "^1.2.1" + gopd "^1.0.1" + +gopd@^1.0.1, gopd@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== @@ -3836,6 +4148,11 @@ graphemer@^1.4.0: resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== +has-bigints@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.1.0.tgz#28607e965ac967e03cd2a2c70a2636a1edad49fe" + integrity sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg== + has-flag@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" @@ -3846,6 +4163,20 @@ has-own-prop@^2.0.0: resolved "https://registry.yarnpkg.com/has-own-prop/-/has-own-prop-2.0.0.tgz#f0f95d58f65804f5d218db32563bb85b8e0417af" integrity sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ== +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.2.0.tgz#5de5a6eabd95fdffd9818b43055e8065e39fe9d5" + integrity sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ== + dependencies: + dunder-proto "^1.0.0" + has-symbols@^1.0.3, has-symbols@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" @@ -3993,28 +4324,101 @@ inspect-with-kind@^1.0.5: dependencies: kind-of "^6.0.2" +internal-slot@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.1.0.tgz#1eac91762947d2f7056bc838d93e13b2e9604961" + integrity sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw== + dependencies: + es-errors "^1.3.0" + hasown "^2.0.2" + side-channel "^1.1.0" + ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== +is-array-buffer@^3.0.4, is-array-buffer@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz#65742e1e687bd2cc666253068fd8707fe4d44280" + integrity sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + get-intrinsic "^1.2.6" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== -is-core-module@^2.16.0: +is-async-function@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.1.1.tgz#3e69018c8e04e73b738793d020bfe884b9fd3523" + integrity sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ== + dependencies: + async-function "^1.0.0" + call-bound "^1.0.3" + get-proto "^1.0.1" + has-tostringtag "^1.0.2" + safe-regex-test "^1.1.0" + +is-bigint@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.1.0.tgz#dda7a3445df57a42583db4228682eba7c4170672" + integrity sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ== + dependencies: + has-bigints "^1.0.2" + +is-boolean-object@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.2.2.tgz#7067f47709809a393c71ff5bb3e135d8a9215d9e" + integrity sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A== + dependencies: + call-bound "^1.0.3" + has-tostringtag "^1.0.2" + +is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-core-module@^2.13.0, is-core-module@^2.15.1, is-core-module@^2.16.0: version "2.16.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== dependencies: hasown "^2.0.2" +is-data-view@^1.0.1, is-data-view@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.2.tgz#bae0a41b9688986c2188dda6657e56b8f9e63b8e" + integrity sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw== + dependencies: + call-bound "^1.0.2" + get-intrinsic "^1.2.6" + is-typed-array "^1.1.13" + +is-date-object@^1.0.5, is-date-object@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.1.0.tgz#ad85541996fc7aa8b2729701d27b7319f95d82f7" + integrity sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg== + dependencies: + call-bound "^1.0.2" + has-tostringtag "^1.0.2" + is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== +is-finalizationregistry@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz#eefdcdc6c94ddd0674d9c85887bf93f944a97c90" + integrity sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg== + dependencies: + call-bound "^1.0.3" + is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" @@ -4025,6 +4429,16 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== +is-generator-function@^1.0.10: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.1.0.tgz#bf3eeda931201394f57b5dba2800f91a238309ca" + integrity sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ== + dependencies: + call-bound "^1.0.3" + get-proto "^1.0.0" + has-tostringtag "^1.0.2" + safe-regex-test "^1.1.0" + is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" @@ -4037,6 +4451,19 @@ 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-map@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" + integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== + +is-number-object@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.1.1.tgz#144b21e95a1bc148205dcc2814a9134ec41b2541" + integrity sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw== + dependencies: + call-bound "^1.0.3" + has-tostringtag "^1.0.2" + is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -4052,6 +4479,28 @@ is-promise@^4.0.0: resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3" integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== +is-regex@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22" + integrity sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g== + dependencies: + call-bound "^1.0.2" + gopd "^1.2.0" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + +is-set@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" + integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg== + +is-shared-array-buffer@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz#9b67844bd9b7f246ba0708c3a93e34269c774f6f" + integrity sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A== + dependencies: + call-bound "^1.0.3" + is-stream@^2.0.0, is-stream@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" @@ -4062,16 +4511,65 @@ is-stream@^4.0.1: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-4.0.1.tgz#375cf891e16d2e4baec250b85926cffc14720d9b" integrity sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A== +is-string@^1.0.7, is-string@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.1.1.tgz#92ea3f3d5c5b6e039ca8677e5ac8d07ea773cbb9" + integrity sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA== + dependencies: + call-bound "^1.0.3" + has-tostringtag "^1.0.2" + +is-symbol@^1.0.4, is-symbol@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.1.1.tgz#f47761279f532e2b05a7024a7506dbbedacd0634" + integrity sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w== + dependencies: + call-bound "^1.0.2" + has-symbols "^1.1.0" + safe-regex-test "^1.1.0" + +is-typed-array@^1.1.13, is-typed-array@^1.1.14, is-typed-array@^1.1.15: + version "1.1.15" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.15.tgz#4bfb4a45b61cee83a5a46fba778e4e8d59c0ce0b" + integrity sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ== + dependencies: + which-typed-array "^1.1.16" + is-unicode-supported@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== +is-weakmap@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd" + integrity sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w== + +is-weakref@^1.0.2, is-weakref@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.1.1.tgz#eea430182be8d64174bd96bffbc46f21bf3f9293" + integrity sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew== + dependencies: + call-bound "^1.0.3" + +is-weakset@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.4.tgz#c9f5deb0bc1906c6d6f1027f284ddf459249daca" + integrity sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ== + dependencies: + call-bound "^1.0.3" + get-intrinsic "^1.2.6" + isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -4579,6 +5077,13 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== +json5@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== + dependencies: + minimist "^1.2.0" + json5@^2.2.2, json5@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" @@ -4930,7 +5435,7 @@ minimatch@^9.0.3, minimatch@^9.0.4: dependencies: brace-expansion "^2.0.1" -minimist@^1.2.6: +minimist@^1.2.0, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -5036,11 +5541,57 @@ object-assign@^4, object-assign@^4.1.1: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -object-inspect@^1.13.3: +object-inspect@^1.13.3, object-inspect@^1.13.4: version "1.13.4" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.7: + version "4.1.7" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.7.tgz#8c14ca1a424c6a561b0bb2a22f66f5049a945d3d" + integrity sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + has-symbols "^1.1.0" + object-keys "^1.1.1" + +object.fromentries@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.8.tgz#f7195d8a9b97bd95cbc1999ea939ecd1a2b00c65" + integrity sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" + +object.groupby@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.3.tgz#9b125c36238129f6f7b61954a1e7176148d5002e" + integrity sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + +object.values@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.1.tgz#deed520a50809ff7f75a7cfd4bc64c7a038c6216" + integrity sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + on-finished@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" @@ -5094,6 +5645,15 @@ os-tmpdir@~1.0.2: resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== +own-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/own-keys/-/own-keys-1.0.1.tgz#e4006910a2bf913585289676eebd6f390cf51358" + integrity sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg== + dependencies: + get-intrinsic "^1.2.6" + object-keys "^1.1.1" + safe-push-apply "^1.0.0" + ox@0.6.9: version "0.6.9" resolved "https://registry.yarnpkg.com/ox/-/ox-0.6.9.tgz#da1ee04fa10de30c8d04c15bfb80fe58b1f554bd" @@ -5272,6 +5832,11 @@ pluralize@8.0.0: resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== +possible-typed-array-names@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz#93e3582bc0e5426586d9d07b79ee40fc841de4ae" + integrity sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg== + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -5450,6 +6015,32 @@ reflect-metadata@^0.2.2: resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.2.2.tgz#400c845b6cba87a21f2c65c4aeb158f4fa4d9c5b" integrity sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q== +reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9: + version "1.0.10" + resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz#c629219e78a3316d8b604c765ef68996964e7bf9" + integrity sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw== + dependencies: + call-bind "^1.0.8" + define-properties "^1.2.1" + es-abstract "^1.23.9" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.7" + get-proto "^1.0.1" + which-builtin-type "^1.2.1" + +regexp.prototype.flags@^1.5.4: + version "1.5.4" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz#1ad6c62d44a259007e55b3970e00f746efbcaa19" + integrity sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA== + dependencies: + call-bind "^1.0.8" + define-properties "^1.2.1" + es-errors "^1.3.0" + get-proto "^1.0.1" + gopd "^1.2.0" + set-function-name "^2.0.2" + repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" @@ -5492,7 +6083,7 @@ resolve.exports@^2.0.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.3.tgz#41955e6f1b4013b7586f873749a635dea07ebe3f" integrity sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A== -resolve@^1.20.0: +resolve@^1.20.0, resolve@^1.22.4: version "1.22.10" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39" integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w== @@ -5553,6 +6144,17 @@ rxjs@^7.8.1: dependencies: tslib "^2.1.0" +safe-array-concat@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.3.tgz#c9e54ec4f603b0bbb8e7e5007a5ee7aecd1538c3" + integrity sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.2" + get-intrinsic "^1.2.6" + has-symbols "^1.1.0" + isarray "^2.0.5" + safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" @@ -5563,6 +6165,23 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +safe-push-apply@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz#01850e981c1602d398c85081f360e4e6d03d27f5" + integrity sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA== + dependencies: + es-errors "^1.3.0" + isarray "^2.0.5" + +safe-regex-test@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz#7f87dfb67a3150782eaaf18583ff5d1711ac10c1" + integrity sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + is-regex "^1.2.1" + "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" @@ -5668,6 +6287,37 @@ serve-static@^2.2.0: parseurl "^1.3.3" send "^1.2.0" +set-function-length@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + +set-function-name@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" + integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.2" + +set-proto@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/set-proto/-/set-proto-1.0.0.tgz#0760dbcff30b2d7e801fd6e19983e56da337565e" + integrity sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw== + dependencies: + dunder-proto "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + setprototypeof@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" @@ -5852,6 +6502,38 @@ string-width@^5.0.1, string-width@^5.1.2: emoji-regex "^9.2.2" strip-ansi "^7.0.1" +string.prototype.trim@^1.2.10: + version "1.2.10" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz#40b2dd5ee94c959b4dcfb1d65ce72e90da480c81" + integrity sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.2" + define-data-property "^1.1.4" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-object-atoms "^1.0.0" + has-property-descriptors "^1.0.2" + +string.prototype.trimend@^1.0.8, string.prototype.trimend@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz#62e2731272cd285041b36596054e9f66569b6942" + integrity sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.2" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +string.prototype.trimstart@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde" + integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -6162,6 +6844,16 @@ tsconfig-paths@4.2.0, tsconfig-paths@^4.1.2, tsconfig-paths@^4.2.0: minimist "^1.2.6" strip-bom "^3.0.0" +tsconfig-paths@^3.15.0: + version "3.15.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" + integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + tslib@2.8.1, tslib@^2.1.0, tslib@^2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" @@ -6215,6 +6907,51 @@ type-is@^2.0.1: media-typer "^1.1.0" mime-types "^3.0.0" +typed-array-buffer@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz#a72395450a4869ec033fd549371b47af3a2ee536" + integrity sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + is-typed-array "^1.1.14" + +typed-array-byte-length@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz#8407a04f7d78684f3d252aa1a143d2b77b4160ce" + integrity sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg== + dependencies: + call-bind "^1.0.8" + for-each "^0.3.3" + gopd "^1.2.0" + has-proto "^1.2.0" + is-typed-array "^1.1.14" + +typed-array-byte-offset@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz#ae3698b8ec91a8ab945016108aef00d5bff12355" + integrity sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.8" + for-each "^0.3.3" + gopd "^1.2.0" + has-proto "^1.2.0" + is-typed-array "^1.1.15" + reflect.getprototypeof "^1.0.9" + +typed-array-length@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.7.tgz#ee4deff984b64be1e118b0de8c9c877d5ce73d3d" + integrity sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + is-typed-array "^1.1.13" + possible-typed-array-names "^1.0.0" + reflect.getprototypeof "^1.0.6" + typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" @@ -6251,6 +6988,16 @@ uint8array-extras@^1.3.0: resolved "https://registry.yarnpkg.com/uint8array-extras/-/uint8array-extras-1.4.0.tgz#e42a678a6dd335ec2d21661333ed42f44ae7cc74" integrity sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ== +unbox-primitive@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.1.0.tgz#8d9d2c9edeea8460c7f35033a88867944934d1e2" + integrity sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw== + dependencies: + call-bound "^1.0.3" + has-bigints "^1.0.2" + has-symbols "^1.1.0" + which-boxed-primitive "^1.1.1" + unbzip2-stream@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7" @@ -6410,6 +7157,59 @@ webpack@5.98.0: watchpack "^2.4.1" webpack-sources "^3.2.3" +which-boxed-primitive@^1.1.0, which-boxed-primitive@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz#d76ec27df7fa165f18d5808374a5fe23c29b176e" + integrity sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA== + dependencies: + is-bigint "^1.1.0" + is-boolean-object "^1.2.1" + is-number-object "^1.1.1" + is-string "^1.1.1" + is-symbol "^1.1.1" + +which-builtin-type@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.2.1.tgz#89183da1b4907ab089a6b02029cc5d8d6574270e" + integrity sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q== + dependencies: + call-bound "^1.0.2" + function.prototype.name "^1.1.6" + has-tostringtag "^1.0.2" + is-async-function "^2.0.0" + is-date-object "^1.1.0" + is-finalizationregistry "^1.1.0" + is-generator-function "^1.0.10" + is-regex "^1.2.1" + is-weakref "^1.0.2" + isarray "^2.0.5" + which-boxed-primitive "^1.1.0" + which-collection "^1.0.2" + which-typed-array "^1.1.16" + +which-collection@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0" + integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== + dependencies: + is-map "^2.0.3" + is-set "^2.0.3" + is-weakmap "^2.0.2" + is-weakset "^2.0.3" + +which-typed-array@^1.1.16, which-typed-array@^1.1.19: + version "1.1.19" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.19.tgz#df03842e870b6b88e117524a4b364b6fc689f956" + integrity sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.4" + for-each "^0.3.5" + get-proto "^1.0.1" + gopd "^1.2.0" + has-tostringtag "^1.0.2" + which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"