From ef2fcd10c127030b6276a342881b83b2fb8c69cb Mon Sep 17 00:00:00 2001 From: Taras Yurchenko Date: Tue, 8 Apr 2025 18:48:52 +0300 Subject: [PATCH 01/27] feat: created timers domain --- src/auction/auction.module.ts | 7 ++- src/auction/auction.service.ts | 18 ++++---- src/game/game.gateway.ts | 84 ++++++++++++---------------------- src/game/game.module.ts | 2 + src/game/game.service.ts | 77 +++++++------------------------ src/player/player.module.ts | 2 + src/player/player.service.ts | 10 ++-- src/timer/timers.module.ts | 8 ++++ src/timer/timers.service.ts | 50 ++++++++++++++++++++ 9 files changed, 128 insertions(+), 130 deletions(-) create mode 100644 src/timer/timers.module.ts create mode 100644 src/timer/timers.service.ts diff --git a/src/auction/auction.module.ts b/src/auction/auction.module.ts index 824d1ae..c42167b 100644 --- a/src/auction/auction.module.ts +++ b/src/auction/auction.module.ts @@ -2,9 +2,14 @@ import { forwardRef, Module } from '@nestjs/common'; import { AuctionService } from './auction.service'; import { GameModule } from 'src/game/game.module'; import { PlayerModule } from 'src/player/player.module'; +import { TimerModule } from 'src/timer/timers.module'; @Module({ - imports: [forwardRef(() => GameModule), forwardRef(() => PlayerModule)], + imports: [ + forwardRef(() => GameModule), + forwardRef(() => PlayerModule), + TimerModule, + ], providers: [AuctionService], exports: [AuctionService], }) diff --git a/src/auction/auction.service.ts b/src/auction/auction.service.ts index ee3ffef..2b5cdcf 100644 --- a/src/auction/auction.service.ts +++ b/src/auction/auction.service.ts @@ -3,6 +3,7 @@ import { WsException } from '@nestjs/websockets'; import { GamePayload } from 'src/game/game.repository'; import { GameService } from 'src/game/game.service'; import { Auction } from 'src/game/types/auction.type'; +import { TimerService } from 'src/timer/timers.service'; import { PlayerPayload } from 'src/player/player.repository'; import { PlayerService } from 'src/player/player.service'; @@ -12,7 +13,8 @@ export class AuctionService { @Inject(forwardRef(() => GameService)) private gameService: GameService, @Inject(forwardRef(() => PlayerService)) - private playerService: PlayerService + private playerService: PlayerService, + private timerService: TimerService ) { this.hightestInQueue = this.hightestInQueue.bind(this); this.winAuction = this.winAuction.bind(this); @@ -27,9 +29,9 @@ export class AuctionService { game.id, player.currentFieldIndex ); - this.gameService.clearTimer(game.id); + this.timerService.clear(game.id); const bidder = this.createInitialBidder(field.price); - const turnEnds = this.gameService.calculateEndOfTurn(this.BID_TIME); + const turnEnds = this.timerService.calculateFutureTime(this.BID_TIME); this.setAuction(game.id, { fieldIndex: field.index, bidders: [bidder], @@ -119,9 +121,9 @@ export class AuctionService { bidAmount, lastBidder.bid ); - this.gameService.clearTimer(gameId); + this.timerService.clear(gameId); this.setBidderOnAuction(gameId, userId, raiseBy, false); - return this.gameService.setTimer( + return this.timerService.set( gameId, 2000, { gameId, userId, raiseBy }, @@ -136,9 +138,9 @@ export class AuctionService { accepted: boolean ) { const auction = this.getAuction(gameId); - const biddder = this.findLastAcceptedBidder(auction); + const biddder = this.findLastAcceptedBidder(auction) || auction.bidders[0]; const newBid = biddder.bid + raiseBy; - const turnEnds = this.gameService.calculateEndOfTurn(this.BID_TIME); + const turnEnds = this.timerService.calculateFutureTime(this.BID_TIME); auction.bidders[auction.bidders.length] = { userId, bid: newBid, @@ -179,7 +181,7 @@ export class AuctionService { const isAuctionComplete = auction.usersRefused.length >= activePlayers.length; if (isAuctionComplete) { - this.gameService.clearTimer(gameId); + this.timerService.clear(gameId); const hasWinner = !!this.findLastAcceptedBidder(auction); return { auction, diff --git a/src/game/game.gateway.ts b/src/game/game.gateway.ts index 7b4fb48..2e40443 100644 --- a/src/game/game.gateway.ts +++ b/src/game/game.gateway.ts @@ -30,6 +30,7 @@ import { Auction } from './types/auction.type'; import { Trade } from './types/trade.type'; import { FieldDocument } from 'src/schema/Field.schema'; import { AuctionService } from 'src/auction/auction.service'; +import { TimerService } from 'src/timer/timers.service'; @WebSocketGateway({ cors: { @@ -52,7 +53,8 @@ export class GameGateway { private configService: ConfigService, private chatService: ChatService, private webSocketServer: WebSocketServerService, - private auctionService: AuctionService + private auctionService: AuctionService, + private timerService: TimerService ) { this.rollDice = this.rollDice.bind(this); this.putUpForAuction = this.putUpForAuction.bind(this); @@ -81,7 +83,7 @@ export class GameGateway { socket.join(userId); const game = await this.gameService.getGame(gameId); if (!game || game.status !== 'ACTIVE') return; - const timers = this.gameService.timers; + const timers = this.timerService.timers; if (timers.has(gameId)) { this.rejoinGame(socket, gameId); return; @@ -99,7 +101,7 @@ export class GameGateway { timerCallback = this.rollDice; } - this.gameService.setTimer( + this.timerService.set( gameId, updatedGame.timeOfTurn, updatedGame, @@ -177,12 +179,7 @@ export class GameGateway { game, chatId: game.chat.id, }); - this.gameService.setTimer( - game.id, - game.timeOfTurn, - game, - this.rollDice - ); + this.timerService.set(game.id, game.timeOfTurn, game, this.rollDice); } } } @@ -213,7 +210,7 @@ export class GameGateway { ) { if (socket.game.dices) throw new WsException('You have already rolled dices'); - this.gameService.clearTimer(socket.game.id); + this.timerService.clear(socket.game.id); await this.rollDice(socket.game); } @@ -230,12 +227,7 @@ export class GameGateway { playerNextField.price && playerNextField.ownedBy === updatedGame?.turnOfUserId ) { - this.gameService.setTimer( - game.id, - 2500, - updatedGame, - this.passTurnToNext - ); + this.timerService.set(game.id, 2500, updatedGame, this.passTurnToNext); } this.server.to(game.id).emit('rolledDice', { fields, @@ -258,24 +250,19 @@ export class GameGateway { (player) => player.money > playerNextField.price ) ) { - this.gameService.setTimer( + this.timerService.set( game.id, game.timeOfTurn, updatedGame, this.putUpForAuction ); } else { - this.gameService.setTimer( - game.id, - 2500, - updatedGame, - this.passTurnToNext - ); + this.timerService.set(game.id, 2500, updatedGame, this.passTurnToNext); } } if (!playerNextField.price) { - // this.gameService.setTimer( + // this.timerService.set( // game.id, // game.timeOfTurn, // updatedGame, @@ -290,21 +277,11 @@ export class GameGateway { !currentField.secret && !currentField.toPay ) { - this.gameService.setTimer( - game.id, - 2500, - updatedGame, - this.passTurnToNext - ); + this.timerService.set(game.id, 2500, updatedGame, this.passTurnToNext); } if (playerNextField.isPledged) { - this.gameService.setTimer( - game.id, - 2500, - updatedGame, - this.passTurnToNext - ); + this.timerService.set(game.id, 2500, updatedGame, this.passTurnToNext); } } @@ -313,7 +290,7 @@ export class GameGateway { playerNextField: FieldDocument ) { if (playerNextField.toPay) { - this.gameService.setTimer( + this.timerService.set( game.id, game.timeOfTurn, { game, userId: game.turnOfUserId, amount: playerNextField.toPay }, @@ -347,14 +324,14 @@ export class GameGateway { ); if (secretInfo.users.length === 1) { if (secretInfo.amounts[0] < 0) { - this.gameService.setTimer( + this.timerService.set( game.id, game.timeOfTurn, { game, userId: game.turnOfUserId, amount: secretInfo.amounts[0] }, this.payToBank ); } else { - this.gameService.setTimer( + this.timerService.set( game.id, 3500, { game, userId: game.turnOfUserId, amount: secretInfo.amounts[0] }, @@ -362,14 +339,14 @@ export class GameGateway { ); } } else if (secretInfo.users.length === 2) { - this.gameService.setTimer( + this.timerService.set( game.id, game.timeOfTurn, game, this.resolveTwoUsers ); } else if (secretInfo.users.length > 2) { - this.gameService.setTimer(game.id, game.timeOfTurn, game, this.payAll); + this.timerService.set(game.id, game.timeOfTurn, game, this.payAll); } } } @@ -669,7 +646,7 @@ export class GameGateway { this.playerService.estimateAssets(player, fields) >= field.income[field.amountOfBranches] ) { - this.gameService.setTimer( + this.timerService.set( game.id, game.timeOfTurn, { game, field: field }, @@ -697,7 +674,7 @@ export class GameGateway { ); if (!socket.game.dices || !currentField.ownedBy) throw new WsException('You cant pay for that field'); - this.gameService.clearTimer(socket.game.id); + this.timerService.clear(socket.game.id); await this.payForField({ game: socket.game, field: currentField }); } @@ -736,7 +713,7 @@ export class GameGateway { this.server .to(game.id) .emit('hasPutUpForAuction', { game: updatedGame, auction }); - this.gameService.setTimer(game.id, 15000, updatedGame, this.passTurnToNext); + this.timerService.set(game.id, 15000, updatedGame, this.passTurnToNext); } @SubscribeMessage('createGame') @@ -775,7 +752,7 @@ export class GameGateway { if (auction) { this.server.to(gameId).emit('raisedPrice', { auction }); - this.gameService.setTimer( + this.timerService.set( gameId, 15000, { ...auction, gameId }, @@ -856,12 +833,7 @@ export class GameGateway { this.server .to(game.id) .emit('passTurnToNext', { game: updatedGame, fields }); - this.gameService.setTimer( - game.id, - game.timeOfTurn, - updatedGame, - this.rollDice - ); + this.timerService.set(game.id, game.timeOfTurn, updatedGame, this.rollDice); } @OnEvent('passTurnToNext') async handlePassTurnToNext(data: { game: Partial }) { @@ -877,7 +849,7 @@ export class GameGateway { this.server .to(data.trade.toUserId) .emit('tradeOffered', { trade: data.trade }); - this.gameService.setTimer( + this.timerService.set( updatedGame.id, 10000, updatedGame, @@ -886,7 +858,7 @@ export class GameGateway { } @OnEvent('setRollDiceTimer') async handleSetRollDiceTimer(game: Partial) { - this.gameService.setTimer(game.id, game.timeOfTurn, game, this.rollDice); + this.timerService.set(game.id, game.timeOfTurn, game, this.rollDice); } @OnEvent('setAfterRolledDiceTimer') async handleSetAfterRolledDiceTimer(updatedGame: Partial) { @@ -900,7 +872,7 @@ export class GameGateway { playerNextField.price && playerNextField.ownedBy === updatedGame?.turnOfUserId ) { - this.gameService.setTimer( + this.timerService.set( updatedGame.id, 2500, updatedGame, @@ -915,7 +887,7 @@ export class GameGateway { return; } if (playerNextField.price && !playerNextField.ownedBy) { - this.gameService.setTimer( + this.timerService.set( updatedGame.id, updatedGame.timeOfTurn, updatedGame, @@ -932,7 +904,7 @@ export class GameGateway { !currentField.secret && !currentField.toPay ) { - this.gameService.setTimer( + this.timerService.set( updatedGame.id, 12000, updatedGame, diff --git a/src/game/game.module.ts b/src/game/game.module.ts index 8b68399..f3aa834 100644 --- a/src/game/game.module.ts +++ b/src/game/game.module.ts @@ -13,6 +13,7 @@ import { MongooseModule } from '@nestjs/mongoose'; import { Field, FieldSchema } from 'src/schema/Field.schema'; import { GameController } from './game.controller'; import { AuctionModule } from 'src/auction/auction.module'; +import { TimerModule } from 'src/timer/timers.module'; @Module({ controllers: [GameController], @@ -31,6 +32,7 @@ import { AuctionModule } from 'src/auction/auction.module'; schema: FieldSchema, }, ]), + TimerModule, ], providers: [GameGateway, GameService, GameRepository], exports: [GameService], diff --git a/src/game/game.service.ts b/src/game/game.service.ts index 4f36765..631c8c9 100644 --- a/src/game/game.service.ts +++ b/src/game/game.service.ts @@ -1,8 +1,9 @@ -import { forwardRef, Inject, Injectable, Logger } from '@nestjs/common'; +import { forwardRef, Inject, Injectable } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { WsException } from '@nestjs/websockets'; import { ChatType, Player, Prisma } from '@prisma/client'; import { Model } from 'mongoose'; +import { AuctionService } from 'src/auction/auction.service'; import { EventService } from 'src/event/event.service'; import { PlayerService } from 'src/player/player.service'; import { Field, FieldDocument } from 'src/schema/Field.schema'; @@ -10,7 +11,7 @@ import { DEFAULT_FIELDS } from 'src/utils/fields'; import secretFields, { SecretType } from 'src/utils/fields/secretFields'; import { GamePayload, GameRepository } from './game.repository'; import { SecretInfo } from './types/secretInfo.type'; -import { AuctionService } from 'src/auction/auction.service'; +import { TimerService } from 'src/timer/timers.service'; @Injectable() export class GameService { constructor( @@ -21,16 +22,13 @@ export class GameService { @Inject(forwardRef(() => AuctionService)) private auctionService: AuctionService, @InjectModel(Field.name) - private fieldModel: Model + private fieldModel: Model, + private timerService: TimerService ) { this.passTurnToUser = this.passTurnToUser.bind(this); } readonly PLAYING_FIELDS_QUANTITY = 40; - - readonly logger = new Logger(GameService.name); - timers: Map void }> = - new Map(); readonly secrets: Map = new Map(); async getVisibleGames() { @@ -160,10 +158,7 @@ export class GameService { gameWithCreatedPlayer.playersCapacity === gameWithCreatedPlayer.players.length ) { - const randomPlayerIndex = Math.floor( - Math.random() * gameWithCreatedPlayer.players.length - ); - const turnEnds = this.calculateEndOfTurn(game.timeOfTurn); + const turnEnds = this.timerService.calculateFutureTime(game.timeOfTurn); const startedGame = await this.gameRepository.updateById(gameId, { data: { status: 'ACTIVE', @@ -248,38 +243,6 @@ export class GameService { return `${firstDice}:${secondDice}`; } - setTimer( - id: string, - time: number, - args: T, - callback: (args: T) => Promise | R - ): Promise | null { - this.clearTimer(id); - return new Promise((resolve, reject) => { - const timer = setTimeout(async () => { - try { - const res: R = await callback(args); - resolve(res); - } catch (err: unknown) { - const error = err instanceof Error ? err : new Error(String(err)); - console.log('In Timer'); - console.log(error.message); - reject(error); - } - }, time); - this.timers.set(id, { timer, reject }); - this.logger.log(`Timer with id:${id} was set for ${time / 1000} seconds`); - }).catch(() => { - return null; - }); - } - - calculateEndOfTurn(timeOfTurn: number) { - let turnEnds = Date.now(); - turnEnds += timeOfTurn; - return turnEnds.toString(); - } - async findCurrentFieldFromGame(game: Partial) { const player = await this.playerService.findByUserAndGameId( game.turnOfUserId, @@ -293,7 +256,7 @@ export class GameService { game: Partial, timeOfTurn: number | null = null ) { - const turnEnds = this.calculateEndOfTurn( + const turnEnds = this.timerService.calculateFutureTime( timeOfTurn ? timeOfTurn : game.timeOfTurn ); return this.updateById(game.id, { @@ -313,16 +276,6 @@ export class GameService { return { turnOfNextUserId }; } - clearTimer(gameId: string) { - if (this.timers.has(gameId)) { - const timerObj = this.timers.get(gameId); - timerObj.reject('Timer cleared'); - clearTimeout(timerObj.timer); - this.timers.delete(gameId); - this.logger.log(`Cleared timer for game ${gameId}.`); - } - } - async updateById( gameId: string, fieldsToUpdate: Partial @@ -429,7 +382,7 @@ export class GameService { const playerNextField = this.findPlayerFieldByIndex(fields, nextIndex); let updatedGame: null | Partial = null; if (playerNextField.ownedBy !== currentPlayer.userId) { - const turnEnds = this.calculateEndOfTurn(game.timeOfTurn); + const turnEnds = this.timerService.calculateFutureTime(game.timeOfTurn); updatedGame = await this.updateById(game.id, { dices, turnEnds, @@ -519,7 +472,7 @@ export class GameService { ) { throw new WsException('You cant buy this field'); } - this.clearTimer(game.id); + this.timerService.clear(game.id); field.ownedBy = game.turnOfUserId; await this.updateFields(fields, ['ownedBy']); const updatedPlayer = @@ -541,7 +494,7 @@ export class GameService { } const dices = ''; this.auctionService.setAuction(game.id, null); - this.clearTimer(game.id); + this.timerService.clear(game.id); let { turnOfNextUserId } = this.findNextTurnUser(game); game.turnOfUserId = turnOfNextUserId; let playersNotLost = game.players.length; @@ -554,7 +507,7 @@ export class GameService { } --playersNotLost; } - const turnEnds = this.calculateEndOfTurn(game.timeOfTurn); + const turnEnds = this.timerService.calculateFutureTime(game.timeOfTurn); const updatedGame = await this.updateById(game.id, { turnOfUserId: turnOfNextUserId, dices, @@ -599,7 +552,7 @@ export class GameService { async payToBank(game: Partial, userId: string, amount: number) { const secretInfo = this.secrets.get(game.id); - if (!secretInfo) this.clearTimer(game.id); + if (!secretInfo) this.timerService.clear(game.id); const currentPlayer = game.players.find( (player) => player.userId === userId ); @@ -738,8 +691,10 @@ export class GameService { toUserId: string; turnTime?: number; }) { - this.clearTimer(data.game.id); - const turnEnds = this.calculateEndOfTurn(data.turnTime || 10000); + this.timerService.clear(data.game.id); + const turnEnds = this.timerService.calculateFutureTime( + data.turnTime || 10000 + ); const updatedGame = await this.updateById(data.game.id, { turnOfUserId: data.toUserId, turnEnds, diff --git a/src/player/player.module.ts b/src/player/player.module.ts index 752b2a5..db78718 100644 --- a/src/player/player.module.ts +++ b/src/player/player.module.ts @@ -12,6 +12,7 @@ import { ChatModule } from 'src/chat/chat.module'; import { MongooseModule } from '@nestjs/mongoose'; import { Field, FieldSchema } from 'src/schema/Field.schema'; import { AuctionModule } from 'src/auction/auction.module'; +import { TimerModule } from 'src/timer/timers.module'; @Module({ imports: [ forwardRef(() => GameModule), @@ -28,6 +29,7 @@ import { AuctionModule } from 'src/auction/auction.module'; schema: FieldSchema, }, ]), + TimerModule, ], providers: [PlayerGateway, PlayerService, PlayerRepository], exports: [PlayerService], diff --git a/src/player/player.service.ts b/src/player/player.service.ts index 001b029..61ca403 100644 --- a/src/player/player.service.ts +++ b/src/player/player.service.ts @@ -13,6 +13,7 @@ import { EventService } from 'src/event/event.service'; import { Field, FieldDocument } from 'src/schema/Field.schema'; import { InjectModel } from '@nestjs/mongoose'; import { Model } from 'mongoose'; +import { TimerService } from 'src/timer/timers.service'; @Injectable() export class PlayerService { @@ -23,7 +24,8 @@ export class PlayerService { private webSocketServerService: WebSocketServerService, private chatService: ChatService, private eventService: EventService, - @InjectModel(Field.name) private fieldModel: Model + @InjectModel(Field.name) private fieldModel: Model, + private timerService: TimerService ) { this.refuseFromTrade = this.refuseFromTrade.bind(this); } @@ -425,7 +427,7 @@ export class PlayerService { if (!trade) throw new WsException('There is no trade to accept'); if (trade.toUserId !== userId) throw new WsException('You cant accept this trade'); - this.gameService.clearTimer(game.id); + this.timerService.clear(game.id); const fields = await this.gameService.getGameFields(game.id); if (trade.offerFieldsIndexes.length > 0) { trade.offerFieldsIndexes.forEach((index) => { @@ -497,7 +499,7 @@ export class PlayerService { } async loseGame(userId: string, gameId: string, fields: FieldDocument[]) { - this.gameService.clearTimer(gameId); + this.timerService.clear(gameId); const updatedPlayer = await this.update({ where: { userId_gameId: { @@ -534,7 +536,7 @@ export class PlayerService { }); await this.gameService.updateFields(fields, ['ownedBy']); if (this.gameService.hasWinner(updatedPlayer.game)) { - this.gameService.clearTimer(updatedPlayer.game.id); + this.timerService.clear(updatedPlayer.game.id); const game = await this.gameService.updateById(updatedPlayer.game.id, { status: 'FINISHED', }); diff --git a/src/timer/timers.module.ts b/src/timer/timers.module.ts new file mode 100644 index 0000000..736b592 --- /dev/null +++ b/src/timer/timers.module.ts @@ -0,0 +1,8 @@ +import { Module } from '@nestjs/common'; +import { TimerService } from './timers.service'; + +@Module({ + providers: [TimerService], + exports: [TimerService], +}) +export class TimerModule {} diff --git a/src/timer/timers.service.ts b/src/timer/timers.service.ts new file mode 100644 index 0000000..408c96a --- /dev/null +++ b/src/timer/timers.service.ts @@ -0,0 +1,50 @@ +import { Injectable, Logger } from '@nestjs/common'; + +@Injectable() +export class TimerService { + private readonly logger = new Logger(TimerService.name); + constructor() {} + timers: Map< + string, + { timer: NodeJS.Timeout; reject: (message: string) => void } + > = new Map(); + set( + id: string, + time: number, + args: T, + callback: (args: T) => Promise | R + ): Promise | null { + this.clear(id); + return new Promise((resolve, reject) => { + const timer = setTimeout(async () => { + try { + const res: R = await callback(args); + resolve(res); + } catch (err: unknown) { + const error = err instanceof Error ? err : new Error(String(err)); + console.log('In Timer'); + console.log(error.message); + reject(error); + } + }, time); + this.timers.set(id, { timer, reject }); + this.logger.log(`Timer with id:${id} was set for ${time / 1000} seconds`); + }).catch(() => { + return null; + }); + } + clear(gameId: string) { + if (this.timers.has(gameId)) { + const timer = this.timers.get(gameId); + clearTimeout(timer.timer); + this.timers.delete(gameId); + this.logger.log(`Cleared timer for game ${gameId}.`); + } + } + + calculateFutureTime(duration: number) { + let currentTime = Date.now(); + currentTime += duration; + return currentTime.toString(); + } +} From 477e28f9cea3a60e2677001c9d672864921e25ed Mon Sep 17 00:00:00 2001 From: Taras Yurchenko Date: Tue, 8 Apr 2025 19:06:21 +0300 Subject: [PATCH 02/27] feat: created initial domain secret --- src/app.module.ts | 2 ++ src/secret/secret.module.ts | 7 +++++++ src/secret/secret.service.ts | 4 ++++ 3 files changed, 13 insertions(+) create mode 100644 src/secret/secret.module.ts create mode 100644 src/secret/secret.service.ts diff --git a/src/app.module.ts b/src/app.module.ts index b7f0675..8954035 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -15,6 +15,7 @@ import { WebSocketServerModule } from './webSocketServer/webSocketServer.module' import { WebSocketServerService } from './webSocketServer/webSocketServer.service'; import { MongooseModule } from '@nestjs/mongoose'; import { AuctionModule } from './auction/auction.module'; +import { SecretModule } from './secret/secret.module'; @Module({ imports: [ @@ -35,6 +36,7 @@ import { AuctionModule } from './auction/auction.module'; }), }), AuctionModule, + SecretModule, ], controllers: [AppController], providers: [ diff --git a/src/secret/secret.module.ts b/src/secret/secret.module.ts new file mode 100644 index 0000000..4ba8db3 --- /dev/null +++ b/src/secret/secret.module.ts @@ -0,0 +1,7 @@ +import { Module } from '@nestjs/common'; +import { SecretService } from './secret.service'; + +@Module({ + providers: [SecretService], +}) +export class SecretModule {} diff --git a/src/secret/secret.service.ts b/src/secret/secret.service.ts new file mode 100644 index 0000000..9b46cc5 --- /dev/null +++ b/src/secret/secret.service.ts @@ -0,0 +1,4 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class SecretService {} From 3c0fb4f542f502f7175c20fa424f82500172a82a Mon Sep 17 00:00:00 2001 From: Taras Yurchenko Date: Wed, 9 Apr 2025 14:00:11 +0300 Subject: [PATCH 03/27] refactor: put logic of secrets in it's own domain --- src/game/game.gateway.ts | 22 ++++++++------- src/game/game.module.ts | 2 ++ src/game/game.service.ts | 53 +++++------------------------------- src/secret/secret.module.ts | 1 + src/secret/secret.service.ts | 51 +++++++++++++++++++++++++++++++++- 5 files changed, 72 insertions(+), 57 deletions(-) diff --git a/src/game/game.gateway.ts b/src/game/game.gateway.ts index 2e40443..2f46842 100644 --- a/src/game/game.gateway.ts +++ b/src/game/game.gateway.ts @@ -31,6 +31,7 @@ import { Trade } from './types/trade.type'; import { FieldDocument } from 'src/schema/Field.schema'; import { AuctionService } from 'src/auction/auction.service'; import { TimerService } from 'src/timer/timers.service'; +import { SecretService } from 'src/secret/secret.service'; @WebSocketGateway({ cors: { @@ -54,7 +55,8 @@ export class GameGateway { private chatService: ChatService, private webSocketServer: WebSocketServerService, private auctionService: AuctionService, - private timerService: TimerService + private timerService: TimerService, + private secretService: SecretService ) { this.rollDice = this.rollDice.bind(this); this.putUpForAuction = this.putUpForAuction.bind(this); @@ -152,7 +154,7 @@ export class GameGateway { async getGameData(@GetGameId() gameId: string) { const game = await this.gameService.getGame(gameId); const auction = this.auctionService.auctions.get(game.id); - const secretInfo = this.gameService.secrets.get(game.id); + const secretInfo = this.secretService.secrets.get(game.id); const fields = await this.gameService.getGameFields(game.id); this.server.emit('gameData', { game, fields, auction, secretInfo }); } @@ -299,7 +301,7 @@ export class GameGateway { } if (playerNextField.secret) { const secret = this.gameService.getRandomSecret(); - const secretInfo = await this.gameService.parseAndSaveSecret( + const secretInfo = await this.secretService.parseAndSaveSecret( secret, game ); @@ -352,7 +354,7 @@ export class GameGateway { } async payAll(game: Partial) { - const secretInfo = this.gameService.secrets.get(game.id); + const secretInfo = this.secretService.secrets.get(game.id); let updatedPlayer = null; for (const userId of secretInfo.users) { const firstUser = secretInfo.users[0]; @@ -376,7 +378,7 @@ export class GameGateway { } } } - this.gameService.secrets.delete(game.id); + this.secretService.secrets.delete(game.id); this.passTurnToNext(updatedPlayer?.game || game); this.server.to(game.id).emit('updatePlayers', { game: updatedPlayer?.game, @@ -384,7 +386,7 @@ export class GameGateway { } async resolveTwoUsers(game: Partial) { - let secretInfo = this.gameService.secrets.get(game.id); + let secretInfo = this.secretService.secrets.get(game.id); const firstPay = secretInfo.amounts[0] < 1; let updatedGameToReturn: null | Partial = null; const fields = await this.gameService.getGameFields(game.id); @@ -453,7 +455,7 @@ export class GameGateway { ) { const game = socket.game; const userId = socket.jwtPayload.sub; - const secretInfo = this.gameService.secrets.get(game.id); + const secretInfo = this.secretService.secrets.get(game.id); return this.payToUser({ game, userId, @@ -473,7 +475,7 @@ export class GameGateway { userToPayId: string; amount: number; }) { - let secretInfo = this.gameService.secrets.get(game.id); + let secretInfo = this.secretService.secrets.get(game.id); if (!secretInfo.users.includes(userId)) throw new WsException('You cant pay for that secret'); const indexOfUser = secretInfo.users.indexOf(userId); @@ -529,7 +531,7 @@ export class GameGateway { const currentField = await this.gameService.findCurrentFieldWithUserId( socket.game ); - const secretInfo = this.gameService.secrets.get(socket.game.id); + const secretInfo = this.secretService.secrets.get(socket.game.id); if (!socket.game.dices && !currentField.toPay && !secretInfo) throw new WsException( 'You cant pay for that field because smt is missing' @@ -625,7 +627,7 @@ export class GameGateway { argsObj.userId, argsObj.amount ); - const secretInfo = this.gameService.secrets.get(updatedGame.id); + const secretInfo = this.secretService.secrets.get(updatedGame.id); if (!secretInfo) { await this.passTurnToNext(updatedGame); } else { diff --git a/src/game/game.module.ts b/src/game/game.module.ts index f3aa834..1650cf7 100644 --- a/src/game/game.module.ts +++ b/src/game/game.module.ts @@ -14,6 +14,7 @@ import { Field, FieldSchema } from 'src/schema/Field.schema'; import { GameController } from './game.controller'; import { AuctionModule } from 'src/auction/auction.module'; import { TimerModule } from 'src/timer/timers.module'; +import { SecretModule } from 'src/secret/secret.module'; @Module({ controllers: [GameController], @@ -33,6 +34,7 @@ import { TimerModule } from 'src/timer/timers.module'; }, ]), TimerModule, + SecretModule, ], providers: [GameGateway, GameService, GameRepository], exports: [GameService], diff --git a/src/game/game.service.ts b/src/game/game.service.ts index 631c8c9..a93ac62 100644 --- a/src/game/game.service.ts +++ b/src/game/game.service.ts @@ -7,11 +7,11 @@ import { AuctionService } from 'src/auction/auction.service'; import { EventService } from 'src/event/event.service'; import { PlayerService } from 'src/player/player.service'; import { Field, FieldDocument } from 'src/schema/Field.schema'; +import { TimerService } from 'src/timer/timers.service'; import { DEFAULT_FIELDS } from 'src/utils/fields'; -import secretFields, { SecretType } from 'src/utils/fields/secretFields'; +import secretFields from 'src/utils/fields/secretFields'; import { GamePayload, GameRepository } from './game.repository'; -import { SecretInfo } from './types/secretInfo.type'; -import { TimerService } from 'src/timer/timers.service'; +import { SecretService } from 'src/secret/secret.service'; @Injectable() export class GameService { constructor( @@ -23,13 +23,13 @@ export class GameService { private auctionService: AuctionService, @InjectModel(Field.name) private fieldModel: Model, - private timerService: TimerService + private timerService: TimerService, + private secretService: SecretService ) { this.passTurnToUser = this.passTurnToUser.bind(this); } readonly PLAYING_FIELDS_QUANTITY = 40; - readonly secrets: Map = new Map(); async getVisibleGames() { return this.gameRepository.findMany({ @@ -407,45 +407,6 @@ export class GameService { return secretFields[randomSecretIndex]; } - async parseAndSaveSecret(secret: SecretType, game: Partial) { - if (secret.numOfPlayersInvolved === 'one') { - const secretInfo = { - amounts: secret.amounts, - users: [game.turnOfUserId], - }; - this.secrets.set(game.id, secretInfo); - return secretInfo; - } else if (secret.numOfPlayersInvolved === 'two') { - const playersWithoutActive = game.players.filter( - (player) => player.userId !== game.turnOfUserId && !player.lost - ); - const randomUserId = this.getRandomPlayersUserId(playersWithoutActive); - const secretInfo = { - amounts: secret.amounts, - users: [game.turnOfUserId, randomUserId], - }; - this.secrets.set(game.id, secretInfo); - return secretInfo; - } else if (secret.numOfPlayersInvolved === 'all') { - const secretInfo = { - amounts: secret.amounts, - users: [ - game.turnOfUserId, - ...game.players - .filter((player) => player.userId !== game.turnOfUserId) - .map((player) => { - if (!player.lost && player.userId !== game.turnOfUserId) { - return player.userId; - } - return ''; - }), - ], - }; - this.secrets.set(game.id, secretInfo); - return secretInfo; - } - } - getRandomPlayersUserId(players: Partial) { const randomIndex = Math.floor(Math.random() * players.length); return players[randomIndex].userId; @@ -551,7 +512,7 @@ export class GameService { } async payToBank(game: Partial, userId: string, amount: number) { - const secretInfo = this.secrets.get(game.id); + const secretInfo = this.secretService.secrets.get(game.id); if (!secretInfo) this.timerService.clear(game.id); const currentPlayer = game.players.find( (player) => player.userId === userId @@ -591,7 +552,7 @@ export class GameService { return userId === ''; }) ) { - this.secrets.delete(game.id); + this.secretService.secrets.delete(game.id); return { updatedGame: playerWhoPayed.game, fields, diff --git a/src/secret/secret.module.ts b/src/secret/secret.module.ts index 4ba8db3..cdf57b5 100644 --- a/src/secret/secret.module.ts +++ b/src/secret/secret.module.ts @@ -3,5 +3,6 @@ import { SecretService } from './secret.service'; @Module({ providers: [SecretService], + exports: [SecretService], }) export class SecretModule {} diff --git a/src/secret/secret.service.ts b/src/secret/secret.service.ts index 9b46cc5..094a9b6 100644 --- a/src/secret/secret.service.ts +++ b/src/secret/secret.service.ts @@ -1,4 +1,53 @@ import { Injectable } from '@nestjs/common'; +import { Player } from '@prisma/client'; +import { GamePayload } from 'src/game/game.repository'; +import { SecretInfo } from 'src/game/types/secretInfo.type'; +import { SecretType } from 'src/utils/fields/secretFields'; @Injectable() -export class SecretService {} +export class SecretService { + readonly secrets: Map = new Map(); + constructor() {} + getRandomPlayersUserId(players: Partial) { + const randomIndex = Math.floor(Math.random() * players.length); + return players[randomIndex].userId; + } + async parseAndSaveSecret(secret: SecretType, game: Partial) { + if (secret.numOfPlayersInvolved === 'one') { + const secretInfo = { + amounts: secret.amounts, + users: [game.turnOfUserId], + }; + this.secrets.set(game.id, secretInfo); + return secretInfo; + } else if (secret.numOfPlayersInvolved === 'two') { + const playersWithoutActive = game.players.filter( + (player) => player.userId !== game.turnOfUserId && !player.lost + ); + const randomUserId = this.getRandomPlayersUserId(playersWithoutActive); + const secretInfo = { + amounts: secret.amounts, + users: [game.turnOfUserId, randomUserId], + }; + this.secrets.set(game.id, secretInfo); + return secretInfo; + } else if (secret.numOfPlayersInvolved === 'all') { + const secretInfo = { + amounts: secret.amounts, + users: [ + game.turnOfUserId, + ...game.players + .filter((player) => player.userId !== game.turnOfUserId) + .map((player) => { + if (!player.lost && player.userId !== game.turnOfUserId) { + return player.userId; + } + return ''; + }), + ], + }; + this.secrets.set(game.id, secretInfo); + return secretInfo; + } + } +} From bd81b6146644d8a71c056d7e5bd7d685912e2649 Mon Sep 17 00:00:00 2001 From: Taras Yurchenko Date: Wed, 9 Apr 2025 21:40:49 +0300 Subject: [PATCH 04/27] feat: added field domain --- src/app.module.ts | 2 ++ src/field/field.module.ts | 7 +++++++ src/field/field.service.ts | 6 ++++++ 3 files changed, 15 insertions(+) create mode 100644 src/field/field.module.ts create mode 100644 src/field/field.service.ts diff --git a/src/app.module.ts b/src/app.module.ts index 8954035..72f52ff 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -16,6 +16,7 @@ import { WebSocketServerService } from './webSocketServer/webSocketServer.servic import { MongooseModule } from '@nestjs/mongoose'; import { AuctionModule } from './auction/auction.module'; import { SecretModule } from './secret/secret.module'; +import { FieldModule } from './field/field.module'; @Module({ imports: [ @@ -37,6 +38,7 @@ import { SecretModule } from './secret/secret.module'; }), AuctionModule, SecretModule, + FieldModule, ], controllers: [AppController], providers: [ diff --git a/src/field/field.module.ts b/src/field/field.module.ts new file mode 100644 index 0000000..42c6a45 --- /dev/null +++ b/src/field/field.module.ts @@ -0,0 +1,7 @@ +import { Module } from '@nestjs/common'; +import { FieldService } from './field.service'; + +@Module({ + providers: [FieldService] +}) +export class FieldModule {} diff --git a/src/field/field.service.ts b/src/field/field.service.ts new file mode 100644 index 0000000..9dc2407 --- /dev/null +++ b/src/field/field.service.ts @@ -0,0 +1,6 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class FieldService { + constructor() {} +} From 6611360f065e39ceb0c011fa5665f35e215cfbbf Mon Sep 17 00:00:00 2001 From: Taras Yurchenko Date: Sun, 13 Apr 2025 21:12:17 +0300 Subject: [PATCH 05/27] feat: added FieldValidator class --- src/field/field.module.ts | 3 +- src/field/field.service.ts | 4 +++ src/game/game.gateway.ts | 69 ++++++++++++++++++-------------------- src/game/game.module.ts | 2 ++ 4 files changed, 40 insertions(+), 38 deletions(-) diff --git a/src/field/field.module.ts b/src/field/field.module.ts index 42c6a45..a7be8f2 100644 --- a/src/field/field.module.ts +++ b/src/field/field.module.ts @@ -2,6 +2,7 @@ import { Module } from '@nestjs/common'; import { FieldService } from './field.service'; @Module({ - providers: [FieldService] + providers: [FieldService], + exports: [FieldService], }) export class FieldModule {} diff --git a/src/field/field.service.ts b/src/field/field.service.ts index 9dc2407..709ce79 100644 --- a/src/field/field.service.ts +++ b/src/field/field.service.ts @@ -1,6 +1,10 @@ import { Injectable } from '@nestjs/common'; +import { FieldDocument } from 'src/schema/Field.schema'; @Injectable() export class FieldService { constructor() {} + isOwnedByCurrentUser(field: FieldDocument, userId: string): boolean { + return field.ownedBy === userId; + } } diff --git a/src/game/game.gateway.ts b/src/game/game.gateway.ts index 2f46842..9177b58 100644 --- a/src/game/game.gateway.ts +++ b/src/game/game.gateway.ts @@ -32,6 +32,7 @@ import { FieldDocument } from 'src/schema/Field.schema'; import { AuctionService } from 'src/auction/auction.service'; import { TimerService } from 'src/timer/timers.service'; import { SecretService } from 'src/secret/secret.service'; +import { FieldService } from 'src/field/field.service'; @WebSocketGateway({ cors: { @@ -56,7 +57,8 @@ export class GameGateway { private webSocketServer: WebSocketServerService, private auctionService: AuctionService, private timerService: TimerService, - private secretService: SecretService + private secretService: SecretService, + private fieldService: FieldService ) { this.rollDice = this.rollDice.bind(this); this.putUpForAuction = this.putUpForAuction.bind(this); @@ -168,24 +170,32 @@ export class GameGateway { data.id, socket.jwtPayload.sub ); - if (game) { - this.leaveAllRoomsExceptInitial(socket); - socket.join(data.id); - this.server.emit('onParticipateGame', game); - if (shouldStart) { - // We can setTimeout here for some countdown on frontend - this.server.emit('clearStartedGame', { - gameId: game.id, - }); - this.server.to(game.id).emit('startGame', { - game, - chatId: game.chat.id, - }); - this.timerService.set(game.id, game.timeOfTurn, game, this.rollDice); - } + if (!game) { + this.server + .to(socket.id) + .emit('error', { message: 'Could not join game' }); + return; + } + this.leaveAllRoomsExceptInitial(socket); + socket.join(data.id); + this.server.emit('onParticipateGame', game); + if (shouldStart) { + this.startGame(game); } } + private startGame(game: Partial) { + // We can setTimeout here for some countdown on frontend + this.server.emit('clearStartedGame', { + gameId: game.id, + }); + this.server.to(game.id).emit('startGame', { + game, + chatId: game.chat.id, + }); + this.timerService.set(game.id, game.timeOfTurn, game, this.rollDice); + } + @SubscribeMessage('leaveGame') async onLeaveGame( socket: Socket & { jwtPayload: JwtPayload }, @@ -217,27 +227,18 @@ export class GameGateway { } async rollDice(game: Partial) { - const { - updatedGame, - nextIndex, - playerNextField, - hasOwner, - currentPlayer, + const { updatedGame, playerNextField, hasOwner, currentPlayer, fields } = + await this.gameService.makeTurn(game); + this.server.to(game.id).emit('rolledDice', { fields, - } = await this.gameService.makeTurn(game); + game: updatedGame, + }); if ( playerNextField.price && - playerNextField.ownedBy === updatedGame?.turnOfUserId + playerNextField.ownedBy === currentPlayer.userId ) { this.timerService.set(game.id, 2500, updatedGame, this.passTurnToNext); } - this.server.to(game.id).emit('rolledDice', { - fields, - playerNextField, - hasOwner, - moveToIndex: nextIndex, - game: updatedGame, - }); if ( hasOwner && playerNextField.ownedBy !== currentPlayer.userId && @@ -264,12 +265,6 @@ export class GameGateway { } if (!playerNextField.price) { - // this.timerService.set( - // game.id, - // game.timeOfTurn, - // updatedGame, - // this.passTurnToNext - // ); this.processSpecialField(updatedGame, playerNextField); } const currentField = diff --git a/src/game/game.module.ts b/src/game/game.module.ts index 1650cf7..f7d2a6e 100644 --- a/src/game/game.module.ts +++ b/src/game/game.module.ts @@ -15,6 +15,7 @@ import { GameController } from './game.controller'; import { AuctionModule } from 'src/auction/auction.module'; import { TimerModule } from 'src/timer/timers.module'; import { SecretModule } from 'src/secret/secret.module'; +import { FieldModule } from 'src/field/field.module'; @Module({ controllers: [GameController], @@ -35,6 +36,7 @@ import { SecretModule } from 'src/secret/secret.module'; ]), TimerModule, SecretModule, + FieldModule, ], providers: [GameGateway, GameService, GameRepository], exports: [GameService], From c02225dbb1cfa1b38dadccbe2fceed6c5d4934ad Mon Sep 17 00:00:00 2001 From: Taras Yurchenko Date: Sun, 13 Apr 2025 23:15:36 +0300 Subject: [PATCH 06/27] fix: deleted field domain, made fieldValidator --- src/app.module.ts | 2 -- src/field/FieldValidator.ts | 47 +++++++++++++++++++++++++++++++++++++ src/field/field.module.ts | 8 ------- src/field/field.service.ts | 10 -------- src/game/game.module.ts | 2 -- 5 files changed, 47 insertions(+), 22 deletions(-) create mode 100644 src/field/FieldValidator.ts delete mode 100644 src/field/field.module.ts delete mode 100644 src/field/field.service.ts diff --git a/src/app.module.ts b/src/app.module.ts index 72f52ff..8954035 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -16,7 +16,6 @@ import { WebSocketServerService } from './webSocketServer/webSocketServer.servic import { MongooseModule } from '@nestjs/mongoose'; import { AuctionModule } from './auction/auction.module'; import { SecretModule } from './secret/secret.module'; -import { FieldModule } from './field/field.module'; @Module({ imports: [ @@ -38,7 +37,6 @@ import { FieldModule } from './field/field.module'; }), AuctionModule, SecretModule, - FieldModule, ], controllers: [AppController], providers: [ diff --git a/src/field/FieldValidator.ts b/src/field/FieldValidator.ts new file mode 100644 index 0000000..13f6cd3 --- /dev/null +++ b/src/field/FieldValidator.ts @@ -0,0 +1,47 @@ +import { GamePayload } from 'src/game/game.repository'; +import { PlayerPayload } from 'src/player/player.repository'; +import { PlayerService } from 'src/player/player.service'; +import { FieldDocument } from 'src/schema/Field.schema'; + +export class FieldValidator { + private currentPlayer: Partial; + + constructor( + private field: FieldDocument, + private game: Partial, + private playerService: PlayerService + ) { + this.currentPlayer = this.playerService.findPlayerWithTurn(game); + } + isOwnedByCurrentUser(): boolean { + return ( + this.field.ownedBy === this.currentPlayer.userId && this.field.price > 0 + ); + } + isOwnedByOtherAndNotPledged(): boolean { + return ( + this.field.ownedBy && + this.field.ownedBy !== this.currentPlayer.userId && + !this.field.isPledged + ); + } + isNotOwned(): boolean { + return !this.field.ownedBy && this.field.price > 0; + } + isAffordableForSomeone(): boolean { + return this.game.players.some( + (player) => + player.userId !== this.currentPlayer.userId && + player.money > this.field.price + ); + } + isSpecialField(): boolean { + return !this.field.price; + } + isSkipable(): boolean { + return ( + (this.field?.specialField && !this.field.secret && !this.field.toPay) || + this.field.isPledged + ); + } +} diff --git a/src/field/field.module.ts b/src/field/field.module.ts deleted file mode 100644 index a7be8f2..0000000 --- a/src/field/field.module.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Module } from '@nestjs/common'; -import { FieldService } from './field.service'; - -@Module({ - providers: [FieldService], - exports: [FieldService], -}) -export class FieldModule {} diff --git a/src/field/field.service.ts b/src/field/field.service.ts deleted file mode 100644 index 709ce79..0000000 --- a/src/field/field.service.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { FieldDocument } from 'src/schema/Field.schema'; - -@Injectable() -export class FieldService { - constructor() {} - isOwnedByCurrentUser(field: FieldDocument, userId: string): boolean { - return field.ownedBy === userId; - } -} diff --git a/src/game/game.module.ts b/src/game/game.module.ts index f7d2a6e..1650cf7 100644 --- a/src/game/game.module.ts +++ b/src/game/game.module.ts @@ -15,7 +15,6 @@ import { GameController } from './game.controller'; import { AuctionModule } from 'src/auction/auction.module'; import { TimerModule } from 'src/timer/timers.module'; import { SecretModule } from 'src/secret/secret.module'; -import { FieldModule } from 'src/field/field.module'; @Module({ controllers: [GameController], @@ -36,7 +35,6 @@ import { FieldModule } from 'src/field/field.module'; ]), TimerModule, SecretModule, - FieldModule, ], providers: [GameGateway, GameService, GameRepository], exports: [GameService], From 3ec8d27a3ee2584d8e22854efb21af36abe52a82 Mon Sep 17 00:00:00 2001 From: Taras Yurchenko Date: Sun, 13 Apr 2025 23:15:58 +0300 Subject: [PATCH 07/27] feat: used fieldValidator in game.gateway --- src/game/game.gateway.ts | 43 ++++++++++++---------------------------- 1 file changed, 13 insertions(+), 30 deletions(-) diff --git a/src/game/game.gateway.ts b/src/game/game.gateway.ts index 9177b58..fc32878 100644 --- a/src/game/game.gateway.ts +++ b/src/game/game.gateway.ts @@ -32,7 +32,7 @@ import { FieldDocument } from 'src/schema/Field.schema'; import { AuctionService } from 'src/auction/auction.service'; import { TimerService } from 'src/timer/timers.service'; import { SecretService } from 'src/secret/secret.service'; -import { FieldService } from 'src/field/field.service'; +import { FieldValidator } from 'src/field/FieldValidator'; @WebSocketGateway({ cors: { @@ -57,8 +57,7 @@ export class GameGateway { private webSocketServer: WebSocketServerService, private auctionService: AuctionService, private timerService: TimerService, - private secretService: SecretService, - private fieldService: FieldService + private secretService: SecretService ) { this.rollDice = this.rollDice.bind(this); this.putUpForAuction = this.putUpForAuction.bind(this); @@ -233,26 +232,20 @@ export class GameGateway { fields, game: updatedGame, }); - if ( - playerNextField.price && - playerNextField.ownedBy === currentPlayer.userId - ) { + const fieldValidator = new FieldValidator( + playerNextField, + updatedGame, + this.playerService + ); + if (fieldValidator.isOwnedByCurrentUser()) { this.timerService.set(game.id, 2500, updatedGame, this.passTurnToNext); } - if ( - hasOwner && - playerNextField.ownedBy !== currentPlayer.userId && - !playerNextField.isPledged - ) { + if (fieldValidator.isOwnedByOtherAndNotPledged()) { this.steppedOnPrivateField(currentPlayer, playerNextField, updatedGame); return; } - if (playerNextField.price && !playerNextField.ownedBy) { - if ( - updatedGame.players.some( - (player) => player.money > playerNextField.price - ) - ) { + if (fieldValidator.isNotOwned()) { + if (fieldValidator.isAffordableForSomeone()) { this.timerService.set( game.id, game.timeOfTurn, @@ -264,20 +257,10 @@ export class GameGateway { } } - if (!playerNextField.price) { + if (fieldValidator.isSpecialField()) { this.processSpecialField(updatedGame, playerNextField); } - const currentField = - await this.gameService.findCurrentFieldWithUserId(updatedGame); - if ( - currentField?.specialField && - !currentField.secret && - !currentField.toPay - ) { - this.timerService.set(game.id, 2500, updatedGame, this.passTurnToNext); - } - - if (playerNextField.isPledged) { + if (fieldValidator.isSkipable()) { this.timerService.set(game.id, 2500, updatedGame, this.passTurnToNext); } } From fd2a5c91bcd2daff126ce5d8deb2a3ddd2dccf77 Mon Sep 17 00:00:00 2001 From: Taras Yurchenko Date: Thu, 29 May 2025 14:51:29 +0300 Subject: [PATCH 08/27] feat: added all changes before pattern --- package-lock.json | 2873 +++++------------ package.json | 2 +- src/app.module.ts | 28 +- src/auction/auction.module.ts | 4 + src/auction/auction.service.ts | 67 +- src/auth/guard/activeGame.guard.ts | 23 - src/auth/guard/index.ts | 3 +- src/auth/guard/turn.guard.ts | 3 +- ...{hasLost.guard.ts => validPlayer.guard.ts} | 11 +- src/chat/chat.gateway.ts | 16 +- src/event/event.module.ts | 10 - src/event/event.service.ts | 11 - src/field/FieldValidator.ts | 47 - src/field/field.module.ts | 21 + src/field/field.service.ts | 51 + src/game/game.module.ts | 20 +- src/game/game.service.ts | 611 +++- src/game/test/game.service.ispec.ts | 186 +- src/game/types/secretInfo.type.ts | 2 + src/payment/payment.module.ts | 20 + src/payment/payment.service.ts | 78 + src/player/player.gateway.ts | 217 +- src/player/player.module.ts | 30 +- src/player/player.service.ts | 298 +- src/secret/secret.module.ts | 14 +- src/secret/secret.service.ts | 214 +- src/timer/timers.service.ts | 2 +- src/{player => trade}/dto/offer-trade.dto.ts | 0 src/trade/trade.gateway.ts | 111 + src/trade/trade.module.ts | 28 + src/trade/trade.service.ts | 166 + src/utils/fields/fields.ts | 142 +- src/utils/fields/secretFields.ts | 2 +- .../webSocketProvider.module.ts | 8 + .../webSocketProvider.service.ts | 18 + src/webSocketServer/webSocketServer.module.ts | 8 - .../webSocketServer.service.ts | 11 - 37 files changed, 2298 insertions(+), 3058 deletions(-) delete mode 100644 src/auth/guard/activeGame.guard.ts rename src/auth/guard/{hasLost.guard.ts => validPlayer.guard.ts} (62%) delete mode 100644 src/event/event.module.ts delete mode 100644 src/event/event.service.ts delete mode 100644 src/field/FieldValidator.ts create mode 100644 src/field/field.module.ts create mode 100644 src/field/field.service.ts create mode 100644 src/payment/payment.module.ts create mode 100644 src/payment/payment.service.ts rename src/{player => trade}/dto/offer-trade.dto.ts (100%) create mode 100644 src/trade/trade.gateway.ts create mode 100644 src/trade/trade.module.ts create mode 100644 src/trade/trade.service.ts create mode 100644 src/webSocketProvider/webSocketProvider.module.ts create mode 100644 src/webSocketProvider/webSocketProvider.service.ts delete mode 100644 src/webSocketServer/webSocketServer.module.ts delete mode 100644 src/webSocketServer/webSocketServer.service.ts diff --git a/package-lock.json b/package-lock.json index a07a44b..d0500c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.1", "license": "UNLICENSED", "dependencies": { - "@nestjs-modules/mailer": "^2.0.2", + "@nestjs-modules/mailer": "^1.6.1", "@nestjs/common": "^10.0.0", "@nestjs/config": "^3.2.3", "@nestjs/core": "^10.0.0", @@ -254,13 +254,14 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "dev": true, "dependencies": { - "@babel/highlight": "^7.24.7", - "picocolors": "^1.0.0" + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" @@ -408,19 +409,17 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", - "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", - "devOptional": true, + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", - "devOptional": true, + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", "engines": { "node": ">=6.9.0" } @@ -435,111 +434,24 @@ } }, "node_modules/@babel/helpers": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.6.tgz", - "integrity": "sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==", - "dev": true, - "dependencies": { - "@babel/template": "^7.25.0", - "@babel/types": "^7.25.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.3.tgz", + "integrity": "sha512-h/eKy9agOya1IGuLaZ9tEUgz+uIRXcbtOhRtUyyMf8JFmn1iT13vnl/IGVWSkdOCG/pC57U4S1jnAabAavTMwg==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.3" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/parser": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz", - "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", - "devOptional": true, + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.3.tgz", + "integrity": "sha512-xyYxRj6+tLNDTWi0KCBcZ9V7yg3/lwL9DWh9Uwh/RIVlIfFidggcgxKX3GCXwCiswwcGRawBKbEg2LG/Y8eJhw==", "dependencies": { - "@babel/types": "^7.25.6" + "@babel/types": "^7.27.3" }, "bin": { "parser": "bin/babel-parser.js" @@ -770,27 +682,15 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/runtime": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.7.tgz", - "integrity": "sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==", - "optional": true, - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/template": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", - "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.25.0", - "@babel/types": "^7.25.0" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -824,14 +724,12 @@ } }, "node_modules/@babel/types": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", - "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", - "devOptional": true, + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.3.tgz", + "integrity": "sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw==", "dependencies": { - "@babel/helper-string-parser": "^7.24.8", - "@babel/helper-validator-identifier": "^7.24.7", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -875,176 +773,6 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/@css-inline/css-inline": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@css-inline/css-inline/-/css-inline-0.14.1.tgz", - "integrity": "sha512-u4eku+hnPqqHIGq/ZUQcaP0TrCbYeLIYBaK7qClNRGZbnh8RC4gVxLEIo8Pceo1nOK9E5G4Lxzlw5KnXcvflfA==", - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@css-inline/css-inline-android-arm-eabi": "0.14.1", - "@css-inline/css-inline-android-arm64": "0.14.1", - "@css-inline/css-inline-darwin-arm64": "0.14.1", - "@css-inline/css-inline-darwin-x64": "0.14.1", - "@css-inline/css-inline-linux-arm-gnueabihf": "0.14.1", - "@css-inline/css-inline-linux-arm64-gnu": "0.14.1", - "@css-inline/css-inline-linux-arm64-musl": "0.14.1", - "@css-inline/css-inline-linux-x64-gnu": "0.14.1", - "@css-inline/css-inline-linux-x64-musl": "0.14.1", - "@css-inline/css-inline-win32-x64-msvc": "0.14.1" - } - }, - "node_modules/@css-inline/css-inline-android-arm-eabi": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@css-inline/css-inline-android-arm-eabi/-/css-inline-android-arm-eabi-0.14.1.tgz", - "integrity": "sha512-LNUR8TY4ldfYi0mi/d4UNuHJ+3o8yLQH9r2Nt6i4qeg1i7xswfL3n/LDLRXvGjBYqeEYNlhlBQzbPwMX1qrU6A==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@css-inline/css-inline-android-arm64": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@css-inline/css-inline-android-arm64/-/css-inline-android-arm64-0.14.1.tgz", - "integrity": "sha512-tH5us0NYGoTNBHOUHVV7j9KfJ4DtFOeTLA3cM0XNoMtArNu2pmaaBMFJPqECzavfXkLc7x5Z22UPZYjoyHfvCA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@css-inline/css-inline-darwin-arm64": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@css-inline/css-inline-darwin-arm64/-/css-inline-darwin-arm64-0.14.1.tgz", - "integrity": "sha512-QE5W1YRIfRayFrtrcK/wqEaxNaqLULPI0gZB4ArbFRd3d56IycvgBasDTHPre5qL2cXCO3VyPx+80XyHOaVkag==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@css-inline/css-inline-darwin-x64": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@css-inline/css-inline-darwin-x64/-/css-inline-darwin-x64-0.14.1.tgz", - "integrity": "sha512-mAvv2sN8awNFsbvBzlFkZPbCNZ6GCWY5/YcIz7V5dPYw+bHHRbjnlkNTEZq5BsDxErVrMIGvz05PGgzuNvZvdQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@css-inline/css-inline-linux-arm-gnueabihf": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-arm-gnueabihf/-/css-inline-linux-arm-gnueabihf-0.14.1.tgz", - "integrity": "sha512-AWC44xL0X7BgKvrWEqfSqkT2tJA5kwSGrAGT+m0gt11wnTYySvQ6YpX0fTY9i3ppYGu4bEdXFjyK2uY1DTQMHA==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@css-inline/css-inline-linux-arm64-gnu": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-arm64-gnu/-/css-inline-linux-arm64-gnu-0.14.1.tgz", - "integrity": "sha512-drj0ciiJgdP3xKXvNAt4W+FH4KKMs8vB5iKLJ3HcH07sNZj58Sx++2GxFRS1el3p+GFp9OoYA6dgouJsGEqt0Q==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@css-inline/css-inline-linux-arm64-musl": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-arm64-musl/-/css-inline-linux-arm64-musl-0.14.1.tgz", - "integrity": "sha512-FzknI+st8eA8YQSdEJU9ykcM0LZjjigBuynVF5/p7hiMm9OMP8aNhWbhZ8LKJpKbZrQsxSGS4g9Vnr6n6FiSdQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@css-inline/css-inline-linux-x64-gnu": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-x64-gnu/-/css-inline-linux-x64-gnu-0.14.1.tgz", - "integrity": "sha512-yubbEye+daDY/4vXnyASAxH88s256pPati1DfVoZpU1V0+KP0BZ1dByZOU1ktExurbPH3gZOWisAnBE9xon0Uw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@css-inline/css-inline-linux-x64-musl": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-x64-musl/-/css-inline-linux-x64-musl-0.14.1.tgz", - "integrity": "sha512-6CRAZzoy1dMLPC/tns2rTt1ZwPo0nL/jYBEIAsYTCWhfAnNnpoLKVh5Nm+fSU3OOwTTqU87UkGrFJhObD/wobQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@css-inline/css-inline-win32-x64-msvc": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@css-inline/css-inline-win32-x64-msvc/-/css-inline-win32-x64-msvc-0.14.1.tgz", - "integrity": "sha512-nzotGiaiuiQW78EzsiwsHZXbxEt6DiMUFcDJ6dhiliomXxnlaPyBfZb6/FMBgRJOf6sknDt/5695OttNmbMYzg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -1206,6 +934,7 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -1222,6 +951,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, "engines": { "node": ">=12" }, @@ -1233,6 +963,7 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, "engines": { "node": ">=12" }, @@ -1243,12 +974,14 @@ "node_modules/@isaacs/cliui/node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -1265,6 +998,7 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -1279,6 +1013,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -1720,6 +1455,11 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/@jonkemp/package-utils": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@jonkemp/package-utils/-/package-utils-1.0.8.tgz", + "integrity": "sha512-bIcKnH5YmtTYr7S6J3J86dn/rFiklwRpOqbTOQ9C0WMmR9FKHVb3bxs2UYfqEmNb93O4nbA97sb6rtz33i9SyA==" + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", @@ -1812,75 +1552,70 @@ } }, "node_modules/@nestjs-modules/mailer": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@nestjs-modules/mailer/-/mailer-2.0.2.tgz", - "integrity": "sha512-+z4mADQasg0H1ZaGu4zZTuKv2pu+XdErqx99PLFPzCDNTN/q9U59WPgkxVaHnsvKHNopLj5Xap7G4ZpptduoYw==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@nestjs-modules/mailer/-/mailer-1.6.1.tgz", + "integrity": "sha512-ZfJFB3sIQyYr52g7HRUr3VOyZZSPBI+89hcm3hvLvP3flTJs/g98QbZxWR/mH2BzQVHdCAzlP98Qw/KQULPM4w==", "dependencies": { - "@css-inline/css-inline": "0.14.1", - "glob": "10.3.12" + "glob": "7.1.7", + "inline-css": "3.0.0", + "preview-email": "3.0.4" }, "optionalDependencies": { - "@types/ejs": "^3.1.5", - "@types/mjml": "^4.7.4", - "@types/pug": "^2.0.10", - "ejs": "^3.1.10", - "handlebars": "^4.7.8", - "liquidjs": "^10.11.1", - "mjml": "^4.15.3", - "preview-email": "^3.0.19", - "pug": "^3.0.2" + "@types/ejs": "^3.0.3", + "@types/pug": "2.0.5", + "ejs": "^3.1.2", + "handlebars": "^4.7.6", + "pug": "^3.0.1" }, "peerDependencies": { - "@nestjs/common": ">=7.0.9", - "@nestjs/core": ">=7.0.9", - "@types/ejs": ">=3.0.3", - "@types/mjml": ">=4.7.4", - "@types/pug": ">=2.0.6", - "ejs": ">=3.1.2", - "handlebars": ">=4.7.6", - "liquidjs": ">=10.8.2", - "mjml": ">=4.15.3", - "nodemailer": ">=6.4.6", - "preview-email": ">=3.0.19", - "pug": ">=3.0.1" + "@nestjs/common": "^7.0.9 || ^8.0.0", + "@nestjs/core": "^7.0.9 || ^8.0.0", + "@types/ejs": "^3.0.3", + "@types/pug": "2.0.5", + "ejs": "^3.1.2", + "handlebars": "^4.7.6", + "nodemailer": "^6.4.6", + "pug": "^2.0.4" + } + }, + "node_modules/@nestjs-modules/mailer/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/@nestjs-modules/mailer/node_modules/glob": { - "version": "10.3.12", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", - "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.6", - "minimatch": "^9.0.1", - "minipass": "^7.0.4", - "path-scurry": "^1.10.2" - }, - "bin": { - "glob": "dist/esm/bin.mjs" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "*" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@nestjs-modules/mailer/node_modules/jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "node_modules/@nestjs-modules/mailer/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dependencies": { - "@isaacs/cliui": "^8.0.2" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" + "node": "*" } }, "node_modules/@nestjs/cli": { @@ -2116,15 +1851,15 @@ } }, "node_modules/@nestjs/platform-express": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.4.3.tgz", - "integrity": "sha512-ss7gkofVm3eO+1P9iRhmGq6Xcjg+mIN3dWisKJZYelSV+msb0QpJmqChLvWjLkWtlqDnx915FKUk0IzCa0TVzw==", + "version": "10.4.18", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.4.18.tgz", + "integrity": "sha512-v+W+Pu5NOVK/bSG5A5mOnXyoVwN5mJUe4o0j0UJ9Ig9JMmjVxg+Zw2ydTfpOQ+R82lRYWJUjjv3dvqKaFW2z7w==", "dependencies": { "body-parser": "1.20.3", "cors": "2.8.5", - "express": "4.21.0", - "multer": "1.4.4-lts.1", - "tslib": "2.7.0" + "express": "4.21.2", + "multer": "2.0.0", + "tslib": "2.8.1" }, "funding": { "type": "opencollective", @@ -2135,13 +1870,18 @@ "@nestjs/core": "^10.0.0" } }, + "node_modules/@nestjs/platform-express/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, "node_modules/@nestjs/platform-socket.io": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-10.4.5.tgz", - "integrity": "sha512-dHkHJQArhrpkX6qBdTW2ghuja3i3cCslwy4QHY6d46u+9UyANQlsNK9wt/lZnmXfCMaci8xAJvUpyODa6YtV7g==", + "version": "10.4.18", + "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-10.4.18.tgz", + "integrity": "sha512-+X/c4I5rYJU/X5ZRhrd3jJ/bL86zWWTc6FIdhNfTvVyLneYkIH9+snCyi4+7lu0auhDXqA90zNzmaUg9Iwno1Q==", "dependencies": { - "socket.io": "4.7.5", - "tslib": "2.7.0" + "socket.io": "4.8.1", + "tslib": "2.8.1" }, "funding": { "type": "opencollective", @@ -2153,6 +1893,11 @@ "rxjs": "^7.1.0" } }, + "node_modules/@nestjs/platform-socket.io/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, "node_modules/@nestjs/schematics": { "version": "10.1.4", "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-10.1.4.tgz", @@ -2256,6 +2001,18 @@ } } }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2308,11 +2065,14 @@ "npm": ">=5.0.0" } }, - "node_modules/@one-ini/wasm": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", - "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", - "optional": true + "node_modules/@paralleldrive/cuid2": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz", + "integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==", + "dev": true, + "dependencies": { + "@noble/hashes": "^1.1.5" + } }, "node_modules/@phc/format": { "version": "1.0.0", @@ -2326,6 +2086,7 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, "optional": true, "engines": { "node": ">=14" @@ -2404,7 +2165,6 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz", "integrity": "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==", - "optional": true, "dependencies": { "domhandler": "^5.0.3", "selderee": "^0.11.0" @@ -2413,14 +2173,39 @@ "url": "https://ko-fi.com/killymxi" } }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true + "node_modules/@selderee/plugin-htmlparser2/node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", + "node_modules/@selderee/plugin-htmlparser2/node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, @@ -2526,11 +2311,6 @@ "@types/node": "*" } }, - "node_modules/@types/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" - }, "node_modules/@types/cookie-parser": { "version": "1.4.7", "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.7.tgz", @@ -2547,9 +2327,9 @@ "dev": true }, "node_modules/@types/cors": { - "version": "2.8.17", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", - "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "version": "2.8.18", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.18.tgz", + "integrity": "sha512-nX3d0sxJW41CqQvfOzVG1NCTXfFDrDWIghCZncpHeWlVFd81zxB/DLhg7avFg6eHLCRX7ckBmoIIcqa++upvJA==", "dependencies": { "@types/node": "*" } @@ -2685,21 +2465,6 @@ "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "dev": true }, - "node_modules/@types/mjml": { - "version": "4.7.4", - "resolved": "https://registry.npmjs.org/@types/mjml/-/mjml-4.7.4.tgz", - "integrity": "sha512-vyi1vzWgMzFMwZY7GSZYX0GU0dmtC8vLHwpgk+NWmwbwRSrlieVyJ9sn5elodwUfklJM7yGl0zQeet1brKTWaQ==", - "optional": true, - "dependencies": { - "@types/mjml-core": "*" - } - }, - "node_modules/@types/mjml-core": { - "version": "4.15.0", - "resolved": "https://registry.npmjs.org/@types/mjml-core/-/mjml-core-4.15.0.tgz", - "integrity": "sha512-jSRWTOpwRS/uHIBfGdvLl0a7MaoBZZYHKI+HhsFYChrUOKVJTnjSYsuV6wx0snv6ZaX3TUo5OP/gNsz/uzZz1A==", - "optional": true - }, "node_modules/@types/node": { "version": "20.16.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.5.tgz", @@ -2738,9 +2503,9 @@ } }, "node_modules/@types/pug": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz", - "integrity": "sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.5.tgz", + "integrity": "sha512-LOnASQoeNZMkzexRuyqcBBDZ6rS+rQxUMkmj5A0PkhhiSZivLIuz6Hxyr1mkGoEZEkk66faROmpMi4fFkrKsBA==", "optional": true }, "node_modules/@types/qs": { @@ -3186,15 +2951,6 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true }, - "node_modules/abbrev": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", - "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", - "optional": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -3273,46 +3029,11 @@ } } }, - "node_modules/alce": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/alce/-/alce-1.2.0.tgz", - "integrity": "sha512-XppPf2S42nO2WhvKzlwzlfcApcXHzjlod30pKmcWjRgLOtqoe5DMuqdiYoM6AgyXksc6A6pV4v1L/WW217e57w==", - "optional": true, - "dependencies": { - "esprima": "^1.2.0", - "estraverse": "^1.5.0" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/alce/node_modules/esprima": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.5.tgz", - "integrity": "sha512-S9VbPDU0adFErpDai3qDkjq8+G05ONtKzcyNrPKg/ZKa+tf879nX2KexNU95b31UoTJjRLInNBHHHjFPoCd7lQ==", - "optional": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/alce/node_modules/estraverse": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", - "integrity": "sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "devOptional": true, + "dev": true, "engines": { "node": ">=6" } @@ -3348,6 +3069,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "engines": { "node": ">=8" } @@ -3370,7 +3092,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "devOptional": true, + "dev": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -3383,7 +3105,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "devOptional": true, + "dev": true, "engines": { "node": ">=8.6" }, @@ -3444,14 +3166,12 @@ "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "devOptional": true + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" }, "node_modules/assert-never": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.3.0.tgz", - "integrity": "sha512-9Z3vxQ+berkL/JJo0dK+EY3Lp0s3NtSnP3VCLsh5HDcZPrh0M+KQRK5sWhUeyPPH+/RCxZqOxLMR+YC6vlviEQ==", - "optional": true + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.4.0.tgz", + "integrity": "sha512-5oJg84os6NMQNl27T9LnZkvvqzvAnHu03ShCnoj6bsJwS7L8AO4lf+C/XjK/nvzEqQB744moC6V128RucQd1jA==" }, "node_modules/async": { "version": "3.2.6", @@ -3470,8 +3190,17 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", + "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } }, "node_modules/babel-jest": { "version": "29.7.0", @@ -3596,7 +3325,6 @@ "version": "3.0.0-canary-5", "resolved": "https://registry.npmjs.org/babel-walk/-/babel-walk-3.0.0-canary-5.tgz", "integrity": "sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==", - "optional": true, "dependencies": { "@babel/types": "^7.9.6" }, @@ -3637,11 +3365,16 @@ "node": "^4.5.0 || >= 5.9" } }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "devOptional": true, + "dev": true, "engines": { "node": ">=8" }, @@ -3660,20 +3393,6 @@ "readable-stream": "^3.4.0" } }, - "node_modules/bl/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/body-parser": { "version": "1.20.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", @@ -3713,13 +3432,13 @@ "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "optional": true + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "devOptional": true, "dependencies": { "balanced-match": "^1.0.0" } @@ -3728,7 +3447,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "devOptional": true, + "dev": true, "dependencies": { "fill-range": "^7.1.1" }, @@ -3879,6 +3598,21 @@ "node": ">= 0.4" } }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -3888,16 +3622,6 @@ "node": ">=6" } }, - "node_modules/camel-case": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", - "integrity": "sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==", - "optional": true, - "dependencies": { - "no-case": "^2.2.0", - "upper-case": "^1.1.1" - } - }, "node_modules/camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -3955,7 +3679,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", "integrity": "sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==", - "optional": true, "dependencies": { "is-regex": "^1.0.3" } @@ -3967,48 +3690,36 @@ "dev": true }, "node_modules/cheerio": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", - "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", - "optional": true, - "dependencies": { - "cheerio-select": "^2.1.0", - "dom-serializer": "^2.0.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "htmlparser2": "^8.0.1", - "parse5": "^7.0.0", - "parse5-htmlparser2-tree-adapter": "^7.0.0" + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", + "integrity": "sha512-8/MzidM6G/TgRelkzDG13y3Y9LxBjCb+8yOEZ9+wwq5gVF2w2pV0wmHvjfT0RvuxGyR7UEuK36r+yYMbT4uKgA==", + "dependencies": { + "css-select": "~1.2.0", + "dom-serializer": "~0.1.0", + "entities": "~1.1.1", + "htmlparser2": "^3.9.1", + "lodash.assignin": "^4.0.9", + "lodash.bind": "^4.1.4", + "lodash.defaults": "^4.0.1", + "lodash.filter": "^4.4.0", + "lodash.flatten": "^4.2.0", + "lodash.foreach": "^4.3.0", + "lodash.map": "^4.4.0", + "lodash.merge": "^4.4.0", + "lodash.pick": "^4.2.1", + "lodash.reduce": "^4.4.0", + "lodash.reject": "^4.4.0", + "lodash.some": "^4.4.0" }, "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/cheeriojs/cheerio?sponsor=1" - } - }, - "node_modules/cheerio-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", - "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", - "optional": true, - "dependencies": { - "boolbase": "^1.0.0", - "css-select": "^5.1.0", - "css-what": "^6.1.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" + "node": ">= 0.6" } }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "devOptional": true, + "dev": true, "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -4041,7 +3752,7 @@ "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -4073,27 +3784,6 @@ "validator": "^13.9.0" } }, - "node_modules/clean-css": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz", - "integrity": "sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==", - "optional": true, - "dependencies": { - "source-map": "~0.6.0" - }, - "engines": { - "node": ">= 4.0" - } - }, - "node_modules/clean-css/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -4146,7 +3836,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "devOptional": true, + "dev": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -4160,7 +3850,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "devOptional": true, + "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -4218,7 +3908,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -4263,8 +3952,7 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "devOptional": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/concat-stream": { "version": "1.6.2", @@ -4280,16 +3968,25 @@ "typedarray": "^0.0.6" } }, - "node_modules/config-chain": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", - "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", - "optional": true, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dependencies": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, + "node_modules/concat-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, "node_modules/consola": { "version": "2.15.3", "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", @@ -4299,7 +3996,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz", "integrity": "sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==", - "optional": true, "dependencies": { "@babel/parser": "^7.6.0", "@babel/types": "^7.6.1" @@ -4340,11 +4036,11 @@ } }, "node_modules/cookie-parser": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", - "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", "dependencies": { - "cookie": "0.4.1", + "cookie": "0.7.2", "cookie-signature": "1.0.6" }, "engines": { @@ -4352,9 +4048,9 @@ } }, "node_modules/cookie-parser/node_modules/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "engines": { "node": ">= 0.6" } @@ -4441,9 +4137,10 @@ "dev": true }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -4453,34 +4150,43 @@ "node": ">= 8" } }, + "node_modules/css-rules": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/css-rules/-/css-rules-1.1.0.tgz", + "integrity": "sha512-7L6krLIRwAEVCaVKyCEL6PQjQXUmf8DM9bWYKutlZd0DqOe0SiKIGQOkFb59AjDBb+3If7SDp3X8UlzDAgYSow==", + "dependencies": { + "cssom": "^0.5.0" + } + }, "node_modules/css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "optional": true, + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha512-dUQOBoqdR7QwV90WysXPLXG5LO7nhYBgiWVfxF80DKPF8zx1t/pUd2FYy73emg3zrjtM6dzmYgbHKfV2rxiHQA==", "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" } }, "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "optional": true, + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", + "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" + "node": "*" } }, + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==" + }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==" + }, "node_modules/debug": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", @@ -4511,15 +4217,6 @@ } } }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "optional": true, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -4530,7 +4227,6 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -4567,7 +4263,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, "engines": { "node": ">=0.4.0" } @@ -4589,30 +4284,15 @@ "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/detect-indent": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", - "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", - "optional": true, - "engines": { - "node": ">=8" - } - }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "devOptional": true, + "dev": true, "engines": { "node": ">=8" } }, - "node_modules/detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "optional": true - }, "node_modules/dezalgo": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", @@ -4653,19 +4333,6 @@ "node": ">=8" } }, - "node_modules/display-notification": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/display-notification/-/display-notification-2.0.0.tgz", - "integrity": "sha512-TdmtlAcdqy1NU+j7zlkDdMnCL878zriLaBmoD9quOoq1ySSSGv03l0hXK5CvIFZlIfFI/hizqdQuW+Num7xuhw==", - "optional": true, - "dependencies": { - "escape-string-applescript": "^1.0.0", - "run-applescript": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -4681,62 +4348,37 @@ "node_modules/doctypes": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", - "integrity": "sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ==", - "optional": true + "integrity": "sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ==" }, "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "optional": true, + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", + "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + "domelementtype": "^1.3.0", + "entities": "^1.1.1" } }, "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "optional": true + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" }, "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "optional": true, + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" + "domelementtype": "1" } }, "node_modules/domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "optional": true, + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==", "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" + "dom-serializer": "0", + "domelementtype": "1" } }, "node_modules/dotenv": { @@ -4758,10 +4400,24 @@ "node": ">=12" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", @@ -4771,48 +4427,6 @@ "safe-buffer": "^5.0.1" } }, - "node_modules/editorconfig": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", - "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", - "optional": true, - "dependencies": { - "@one-ini/wasm": "0.1.1", - "commander": "^10.0.0", - "minimatch": "9.0.1", - "semver": "^7.5.3" - }, - "bin": { - "editorconfig": "bin/editorconfig" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/editorconfig/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/editorconfig/node_modules/minimatch": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", - "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", - "optional": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -4854,7 +4468,8 @@ "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true }, "node_modules/encodeurl": { "version": "2.0.0", @@ -4865,25 +4480,23 @@ } }, "node_modules/encoding-japanese": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/encoding-japanese/-/encoding-japanese-2.1.0.tgz", - "integrity": "sha512-58XySVxUgVlBikBTbQ8WdDxBDHIdXucB16LO5PBHR8t75D54wQrNo4cg+58+R1CtJfKnsVsvt9XlteRaR8xw1w==", - "optional": true, + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/encoding-japanese/-/encoding-japanese-2.2.0.tgz", + "integrity": "sha512-EuJWwlHPZ1LbADuKTClvHtwbaFn4rOD+dRAbWysqEOXRc2Uui0hJInNJrsdH0c+OhJA4nrCBdSkW4DD5YxAo6A==", "engines": { "node": ">=8.10.0" } }, "node_modules/engine.io": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz", - "integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==", + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", + "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", "dependencies": { - "@types/cookie": "^0.4.1", "@types/cors": "^2.8.12", "@types/node": ">=10.0.0", "accepts": "~1.3.4", "base64id": "2.0.0", - "cookie": "~0.4.1", + "cookie": "~0.7.2", "cors": "~2.8.5", "debug": "~4.3.1", "engine.io-parser": "~5.2.1", @@ -4902,9 +4515,9 @@ } }, "node_modules/engine.io/node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "engines": { "node": ">= 0.6" } @@ -4923,16 +4536,9 @@ } }, "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "optional": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" }, "node_modules/error-ex": { "version": "1.3.2", @@ -4944,12 +4550,9 @@ } }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "engines": { "node": ">= 0.4" } @@ -4968,41 +4571,31 @@ "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", "dev": true }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "devOptional": true, + "dev": true, "engines": { "node": ">=6" } }, - "node_modules/escape-goat": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-3.0.0.tgz", - "integrity": "sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==", - "optional": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, - "node_modules/escape-string-applescript": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/escape-string-applescript/-/escape-string-applescript-1.0.0.tgz", - "integrity": "sha512-4/hFwoYaC6TkpDn9A3pTC52zQPArFeXuIfhUtCGYdauTzXVP9H3BDr3oO/QzQehMpLDC7srvYgfwvImPFGfvBA==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -5345,16 +4938,16 @@ } }, "node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -5368,7 +4961,7 @@ "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", @@ -5383,13 +4976,16 @@ }, "engines": { "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/express/node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "license": "MIT", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "engines": { "node": ">= 0.6" } @@ -5408,15 +5004,9 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/express/node_modules/path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" - }, - "node_modules/extend-object": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/extend-object/-/extend-object-1.0.0.tgz", - "integrity": "sha512-0dHDIXC7y7LDmCh/lp1oYkmv73K25AMugQI07r8eFopkW6f7Ufn1q+ETMsJjnV9Am14SlElkqy3O92r6xEaxPw==", - "optional": true + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" }, "node_modules/external-editor": { "version": "3.1.0", @@ -5432,6 +5022,17 @@ "node": ">=4" } }, + "node_modules/extract-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-css/-/extract-css-2.0.1.tgz", + "integrity": "sha512-DX3+27l6NIVYNidJjBJ4VU3Z5tk/0aEO/JJ6XEJIRPFum9kyA1PifPjnEa8Ztnv4DHNQg5EF27aypGz6s/fMdw==", + "dependencies": { + "batch": "^0.6.1", + "href-content": "^2.0.1", + "list-stylesheets": "^1.2.10", + "style-data": "^1.4.8" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -5556,7 +5157,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "devOptional": true, + "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -5610,36 +5211,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/fixpack": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fixpack/-/fixpack-4.0.0.tgz", - "integrity": "sha512-5SM1+H2CcuJ3gGEwTiVo/+nd/hYpNj9Ch3iMDOQ58ndY+VGQ2QdvaUTkd3otjZvYnd/8LF/HkJ5cx7PBq0orCQ==", - "optional": true, - "dependencies": { - "alce": "1.2.0", - "chalk": "^3.0.0", - "detect-indent": "^6.0.0", - "detect-newline": "^3.1.0", - "extend-object": "^1.0.0", - "rc": "^1.2.8" - }, - "bin": { - "fixpack": "bin/fixpack" - } - }, - "node_modules/fixpack/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "optional": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/flat-cache": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", @@ -5654,16 +5225,41 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/flat-util": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/flat-util/-/flat-util-1.1.11.tgz", + "integrity": "sha512-h9ho3lHr5hDTQZKLqFDqIliDV/A8yCyP7UoSIBT4U3d7VfA/EeqsC8cxWJGIr5oCxZzMD/3BEx3SLYFX6hD8ng==" + }, "node_modules/flatted": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/foreground-child": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" @@ -5729,7 +5325,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -5740,15 +5335,18 @@ } }, "node_modules/formidable": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.1.tgz", - "integrity": "sha512-WJWKelbRHN41m5dumb0/k8TeAx7Id/y3a+Z7QfhxP/htI9Js5zYaEDtG8uMgG0vM0lOlqnmjE99/kfpOYi/0Og==", + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", + "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", "dev": true, "dependencies": { + "@paralleldrive/cuid2": "^2.2.2", "dezalgo": "^1.0.4", - "hexoid": "^1.0.0", "once": "^1.4.0" }, + "engines": { + "node": ">=14.0.0" + }, "funding": { "url": "https://ko-fi.com/tunnckoCore/commissions" } @@ -5792,8 +5390,7 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { "version": "2.3.3", @@ -5829,21 +5426,26 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "devOptional": true, + "dev": true, "engines": { "node": "6.* || 8.* || >= 10.*" } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -5861,16 +5463,16 @@ "node": ">=8.0.0" } }, - "node_modules/get-port": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", - "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", - "optional": true, - "engines": { - "node": ">=8" + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">= 0.4" } }, "node_modules/get-stream": { @@ -5889,7 +5491,7 @@ "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "devOptional": true, + "dev": true, "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -5909,7 +5511,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "devOptional": true, + "dev": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -5959,11 +5561,11 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6039,21 +5641,10 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "engines": { "node": ">= 0.4" }, @@ -6065,7 +5656,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "optional": true, "dependencies": { "has-symbols": "^1.0.3" }, @@ -6091,18 +5681,16 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "optional": true, "bin": { "he": "bin/he" } }, - "node_modules/hexoid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", - "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", - "dev": true, - "engines": { - "node": ">=8" + "node_modules/href-content": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/href-content/-/href-content-2.0.3.tgz", + "integrity": "sha512-ikrAoI1l5ihN5Be2cR9nozFfivVJxPQDpa4+sb6PLt/uaNE/a7A05rHbnJEUduoHddbB3GyT5tdqzXMUmPgJYA==", + "dependencies": { + "remote-content": "^4.0.0" } }, "node_modules/html-escaper": { @@ -6111,38 +5699,10 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, - "node_modules/html-minifier": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-4.0.0.tgz", - "integrity": "sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==", - "optional": true, - "dependencies": { - "camel-case": "^3.0.0", - "clean-css": "^4.2.1", - "commander": "^2.19.0", - "he": "^1.2.0", - "param-case": "^2.1.1", - "relateurl": "^0.2.7", - "uglify-js": "^3.5.1" - }, - "bin": { - "html-minifier": "cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/html-minifier/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "optional": true - }, "node_modules/html-to-text": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz", "integrity": "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==", - "optional": true, "dependencies": { "@selderee/plugin-htmlparser2": "^0.11.0", "deepmerge": "^4.3.1", @@ -6154,7 +5714,69 @@ "node": ">=14" } }, - "node_modules/htmlparser2": { + "node_modules/html-to-text/node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/html-to-text/node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/html-to-text/node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/html-to-text/node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/html-to-text/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/html-to-text/node_modules/htmlparser2": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", @@ -6165,7 +5787,6 @@ "url": "https://github.com/sponsors/fb55" } ], - "optional": true, "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", @@ -6173,6 +5794,19 @@ "entities": "^4.4.0" } }, + "node_modules/htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dependencies": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + } + }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -6286,7 +5920,6 @@ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -6297,11 +5930,22 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "optional": true + "node_modules/inline-css": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/inline-css/-/inline-css-3.0.0.tgz", + "integrity": "sha512-a+IE7oLaQqeVr3hMviekDDk94LA0+oZX8JEfJuXOm20diZAkOFrq/f/QZCEXpMK6qIbYr0nQNpsuioXQN1ZgXA==", + "dependencies": { + "cheerio": "^0.22.0", + "css-rules": "^1.0.9", + "extract-css": "^2.0.0", + "flat-util": "^1.1.6", + "pick-util": "^1.1.3", + "slick": "^1.12.2", + "specificity": "^0.4.1" + }, + "engines": { + "node": ">=8" + } }, "node_modules/inquirer": { "version": "8.2.6", @@ -6347,7 +5991,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "devOptional": true, + "dev": true, "dependencies": { "binary-extensions": "^2.0.0" }, @@ -6359,7 +6003,6 @@ "version": "2.15.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", - "devOptional": true, "dependencies": { "hasown": "^2.0.2" }, @@ -6374,7 +6017,6 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "optional": true, "bin": { "is-docker": "cli.js" }, @@ -6389,7 +6031,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz", "integrity": "sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==", - "optional": true, "dependencies": { "acorn": "^7.1.1", "object-assign": "^4.1.1" @@ -6399,7 +6040,6 @@ "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "optional": true, "bin": { "acorn": "bin/acorn" }, @@ -6411,7 +6051,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "devOptional": true, + "dev": true, "engines": { "node": ">=0.10.0" } @@ -6420,6 +6060,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, "engines": { "node": ">=8" } @@ -6437,7 +6078,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "devOptional": true, + "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -6458,7 +6099,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "devOptional": true, + "dev": true, "engines": { "node": ">=0.12.0" } @@ -6475,17 +6116,17 @@ "node_modules/is-promise": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", - "optional": true + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" }, "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "optional": true, + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -6522,7 +6163,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "optional": true, "dependencies": { "is-docker": "^2.0.0" }, @@ -6538,7 +6178,8 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", @@ -6627,7 +6268,7 @@ "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "devOptional": true, + "dev": true, "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -7350,41 +6991,10 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/js-beautify": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.1.tgz", - "integrity": "sha512-ESjNzSlt/sWE8sciZH8kBF8BPlwXPwhR6pWKAw8bw4Bwj+iZcnKW6ONWUutJ7eObuBZQpiIb8S7OYspWrKt7rA==", - "optional": true, - "dependencies": { - "config-chain": "^1.1.13", - "editorconfig": "^1.0.4", - "glob": "^10.3.3", - "js-cookie": "^3.0.5", - "nopt": "^7.2.0" - }, - "bin": { - "css-beautify": "js/bin/css-beautify.js", - "html-beautify": "js/bin/html-beautify.js", - "js-beautify": "js/bin/js-beautify.js" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/js-cookie": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", - "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", - "optional": true, - "engines": { - "node": ">=14" - } - }, "node_modules/js-stringify": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", - "integrity": "sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==", - "optional": true + "integrity": "sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==" }, "node_modules/js-tokens": { "version": "4.0.0", @@ -7494,40 +7104,11 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", "integrity": "sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==", - "optional": true, "dependencies": { "is-promise": "^2.0.0", "promise": "^7.0.1" } }, - "node_modules/juice": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/juice/-/juice-10.0.1.tgz", - "integrity": "sha512-ZhJT1soxJCkOiO55/mz8yeBKTAJhRzX9WBO+16ZTqNTONnnVlUPyVBIzQ7lDRjaBdTbid+bAnyIon/GM3yp4cA==", - "optional": true, - "dependencies": { - "cheerio": "1.0.0-rc.12", - "commander": "^6.1.0", - "mensch": "^0.3.4", - "slick": "^1.12.2", - "web-resource-inliner": "^6.0.1" - }, - "bin": { - "juice": "bin/juice" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/juice/node_modules/commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", - "optional": true, - "engines": { - "node": ">= 6" - } - }, "node_modules/jwa": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", @@ -7577,7 +7158,6 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz", "integrity": "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==", - "optional": true, "funding": { "url": "https://ko-fi.com/killymxi" } @@ -7607,26 +7187,23 @@ "node_modules/libbase64": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/libbase64/-/libbase64-1.3.0.tgz", - "integrity": "sha512-GgOXd0Eo6phYgh0DJtjQ2tO8dc0IVINtZJeARPeiIJqge+HdsWSuaDTe8ztQ7j/cONByDZ3zeB325AHiv5O0dg==", - "optional": true + "integrity": "sha512-GgOXd0Eo6phYgh0DJtjQ2tO8dc0IVINtZJeARPeiIJqge+HdsWSuaDTe8ztQ7j/cONByDZ3zeB325AHiv5O0dg==" }, "node_modules/libmime": { - "version": "5.3.5", - "resolved": "https://registry.npmjs.org/libmime/-/libmime-5.3.5.tgz", - "integrity": "sha512-nSlR1yRZ43L3cZCiWEw7ali3jY29Hz9CQQ96Oy+sSspYnIP5N54ucOPHqooBsXzwrX1pwn13VUE05q4WmzfaLg==", - "optional": true, + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/libmime/-/libmime-5.3.6.tgz", + "integrity": "sha512-j9mBC7eiqi6fgBPAGvKCXJKJSIASanYF4EeA4iBzSG0HxQxmXnR3KbyWqTn4CwsKSebqCv2f5XZfAO6sKzgvwA==", "dependencies": { - "encoding-japanese": "2.1.0", + "encoding-japanese": "2.2.0", "iconv-lite": "0.6.3", "libbase64": "1.3.0", - "libqp": "2.1.0" + "libqp": "2.1.1" } }, "node_modules/libmime/node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "optional": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -7640,10 +7217,9 @@ "integrity": "sha512-mF3KaORjJQR6JBNcOkluDcJKhtoQT4VTLRMrX1v/wlBayL4M8ybwEDeryyPcrSEJmD0rVwHUbBarpZwN5NfPFQ==" }, "node_modules/libqp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/libqp/-/libqp-2.1.0.tgz", - "integrity": "sha512-O6O6/fsG5jiUVbvdgT7YX3xY3uIadR6wEZ7+vy9u7PKHAlSEB6blvC1o5pHBjgsi95Uo0aiBBdkyFecj6jtb7A==", - "optional": true + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/libqp/-/libqp-2.1.1.tgz", + "integrity": "sha512-0Wd+GPz1O134cP62YU2GTOPNA7Qgl09XwCqM5zpBv87ERCXdfDtyKXvV7c9U22yWJh44QZqBocFnXN11K96qow==" }, "node_modules/lines-and-columns": { "version": "1.2.4", @@ -7655,38 +7231,17 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", - "optional": true, "dependencies": { "uc.micro": "^2.0.0" } }, - "node_modules/liquidjs": { - "version": "10.17.0", - "resolved": "https://registry.npmjs.org/liquidjs/-/liquidjs-10.17.0.tgz", - "integrity": "sha512-M4MC5/nencttIJHirl5jFTkl7Yu+grIDLn3Qgl7BPAD3BsbTCQknDxlG5VXWRwslWIjk8lSZZjVq9LioILDk1Q==", - "optional": true, + "node_modules/list-stylesheets": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/list-stylesheets/-/list-stylesheets-1.2.10.tgz", + "integrity": "sha512-F85Yx9GKLJwDr1T9U34FST5m6iIXhPzqD+MRDsljZsdmoEZwllZBDbkAVaa+EpLKrr6de+P4SRGNHwrWv6zMZA==", "dependencies": { - "commander": "^10.0.0" - }, - "bin": { - "liquid": "bin/liquid.js", - "liquidjs": "bin/liquid.js" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/liquidjs" - } - }, - "node_modules/liquidjs/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "optional": true, - "engines": { - "node": ">=14" + "cheerio": "^0.22.0", + "pick-util": "^1.1.4" } }, "node_modules/loader-runner": { @@ -7718,6 +7273,36 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.assignin": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", + "integrity": "sha512-yX/rx6d/UTVh7sSVWVSIMjfnz95evAgDFdb1ZozC35I9mSFCkmzptOzevxjgbQUsc78NR44LVHWjsoMQXy9FDg==" + }, + "node_modules/lodash.bind": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", + "integrity": "sha512-lxdsn7xxlCymgLYo1gGvVrfHmkjDiyqVv62FAeF2i5ta72BipE1SLxw8hPEPLhD4/247Ijw07UQH7Hq/chT5LA==" + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" + }, + "node_modules/lodash.filter": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", + "integrity": "sha512-pXYUy7PR8BCLwX5mgJ/aNtyOvuJTdZAo9EQFUvMIYugqmJxnrYaANvTbgndOzHSCSR0wnlBBfRXJL5SbWxo3FQ==" + }, + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" + }, + "node_modules/lodash.foreach": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", + "integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==" + }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -7748,6 +7333,11 @@ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" }, + "node_modules/lodash.map": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", + "integrity": "sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -7757,14 +7347,34 @@ "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" }, + "node_modules/lodash.pick": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", + "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==", + "deprecated": "This package is deprecated. Use destructuring assignment syntax instead." + }, + "node_modules/lodash.reduce": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", + "integrity": "sha512-6raRe2vxCYBhpBu+B+TtNGUzah+hQjVdu3E17wfusjyrXBka2nBS8OH/gjVZ5PvHOhWmIZTYri09Z6n/QfnNMw==" + }, + "node_modules/lodash.reject": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz", + "integrity": "sha512-qkTuvgEzYdyhiJBx42YPzPo71R1aEr0z79kAv7Ixg8wPFEjgRgJdUsGMG3Hf3OYSF/kHI79XhNlt+5Ar6OzwxQ==" + }, + "node_modules/lodash.some": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", + "integrity": "sha512-j7MJE+TuT51q9ggt4fSgVqro163BEFjAt3u97IqU+JA2DkWl80nFTrowzLpZ/BnpN7rrl0JA/593NAdd8p/scQ==" + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -7781,12 +7391,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lower-case": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==", - "optional": true - }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -7809,28 +7413,26 @@ } }, "node_modules/mailparser": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/mailparser/-/mailparser-3.7.1.tgz", - "integrity": "sha512-RCnBhy5q8XtB3mXzxcAfT1huNqN93HTYYyL6XawlIKycfxM/rXPg9tXoZ7D46+SgCS1zxKzw+BayDQSvncSTTw==", - "optional": true, + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/mailparser/-/mailparser-3.7.3.tgz", + "integrity": "sha512-0RM14cZF0gO1y2Q/82hhWranispZOUSYHwvQ21h12x90NwD6+D5q59S5nOLqCtCdYitHN58LJXWEHa4RWm7BYA==", "dependencies": { - "encoding-japanese": "2.1.0", + "encoding-japanese": "2.2.0", "he": "1.2.0", "html-to-text": "9.0.5", "iconv-lite": "0.6.3", - "libmime": "5.3.5", + "libmime": "5.3.6", "linkify-it": "5.0.0", - "mailsplit": "5.4.0", - "nodemailer": "6.9.13", + "mailsplit": "5.4.3", + "nodemailer": "7.0.3", "punycode.js": "2.3.1", - "tlds": "1.252.0" + "tlds": "1.259.0" } }, "node_modules/mailparser/node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "optional": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -7839,70 +7441,23 @@ } }, "node_modules/mailparser/node_modules/nodemailer": { - "version": "6.9.13", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.13.tgz", - "integrity": "sha512-7o38Yogx6krdoBf3jCAqnIN4oSQFx+fMa0I7dK1D+me9kBxx12D+/33wSb+fhOCtIxvYJ+4x4IMEhmhCKfAiOA==", - "optional": true, + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.3.tgz", + "integrity": "sha512-Ajq6Sz1x7cIK3pN6KesGTah+1gnwMnx5gKl3piQlQQE/PwyJ4Mbc8is2psWYxK3RJTVeqsDaCv8ZzXLCDHMTZw==", "engines": { "node": ">=6.0.0" } }, "node_modules/mailsplit": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/mailsplit/-/mailsplit-5.4.0.tgz", - "integrity": "sha512-wnYxX5D5qymGIPYLwnp6h8n1+6P6vz/MJn5AzGjZ8pwICWssL+CCQjWBIToOVHASmATot4ktvlLo6CyLfOXWYA==", - "optional": true, - "dependencies": { - "libbase64": "1.2.1", - "libmime": "5.2.0", - "libqp": "2.0.1" - } - }, - "node_modules/mailsplit/node_modules/encoding-japanese": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encoding-japanese/-/encoding-japanese-2.0.0.tgz", - "integrity": "sha512-++P0RhebUC8MJAwJOsT93dT+5oc5oPImp1HubZpAuCZ5kTLnhuuBhKHj2jJeO/Gj93idPBWmIuQ9QWMe5rX3pQ==", - "optional": true, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/mailsplit/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "optional": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mailsplit/node_modules/libbase64": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/libbase64/-/libbase64-1.2.1.tgz", - "integrity": "sha512-l+nePcPbIG1fNlqMzrh68MLkX/gTxk/+vdvAb388Ssi7UuUN31MI44w4Yf33mM3Cm4xDfw48mdf3rkdHszLNew==", - "optional": true - }, - "node_modules/mailsplit/node_modules/libmime": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/libmime/-/libmime-5.2.0.tgz", - "integrity": "sha512-X2U5Wx0YmK0rXFbk67ASMeqYIkZ6E5vY7pNWRKtnNzqjvdYYG8xtPDpCnuUEnPU9vlgNev+JoSrcaKSUaNvfsw==", - "optional": true, + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/mailsplit/-/mailsplit-5.4.3.tgz", + "integrity": "sha512-PFV0BBh4Tv7Omui5FtXXVtN4ExAxIi8Yvmb9JgBz+J6Hnnrv/YYXLlKKudLhXwd3/qWEATOslRsnzVCWDeCnmQ==", "dependencies": { - "encoding-japanese": "2.0.0", - "iconv-lite": "0.6.3", - "libbase64": "1.2.1", - "libqp": "2.0.1" + "libbase64": "1.3.0", + "libmime": "5.3.6", + "libqp": "2.1.1" } }, - "node_modules/mailsplit/node_modules/libqp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/libqp/-/libqp-2.0.1.tgz", - "integrity": "sha512-Ka0eC5LkF3IPNQHJmYBWljJsw0UvM6j+QdKRbWyCdTmYwvIDE6a7bCm0UkTAL/K+3KXK5qXT/ClcInU01OpdLg==", - "optional": true - }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -7933,6 +7488,14 @@ "tmpl": "1.0.5" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -7941,6 +7504,14 @@ "node": ">= 0.6" } }, + "node_modules/mediaquery-text": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mediaquery-text/-/mediaquery-text-1.2.0.tgz", + "integrity": "sha512-cJyRqgYQi+hsYhRkyd5le0s4LsEPvOB7r+6X3jdEELNqVlM9mRIgyUPg9BzF+PuTqQH1ZekgIjYVOeWSXWq35Q==", + "dependencies": { + "cssom": "^0.5.0" + } + }, "node_modules/memfs": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", @@ -7958,12 +7529,6 @@ "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" }, - "node_modules/mensch": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/mensch/-/mensch-0.3.4.tgz", - "integrity": "sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==", - "optional": true - }, "node_modules/merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", @@ -8063,6 +7628,7 @@ "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -8085,435 +7651,11 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, "engines": { "node": ">=16 || 14 >=14.17" } }, - "node_modules/mjml": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/mjml/-/mjml-4.15.3.tgz", - "integrity": "sha512-bW2WpJxm6HS+S3Yu6tq1DUPFoTxU9sPviUSmnL7Ua+oVO3WA5ILFWqvujUlz+oeuM+HCwEyMiP5xvKNPENVjYA==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.23.9", - "mjml-cli": "4.15.3", - "mjml-core": "4.15.3", - "mjml-migrate": "4.15.3", - "mjml-preset-core": "4.15.3", - "mjml-validator": "4.15.3" - }, - "bin": { - "mjml": "bin/mjml" - } - }, - "node_modules/mjml-accordion": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/mjml-accordion/-/mjml-accordion-4.15.3.tgz", - "integrity": "sha512-LPNVSj1LyUVYT9G1gWwSw3GSuDzDsQCu0tPB2uDsq4VesYNnU6v3iLCQidMiR6azmIt13OEozG700ygAUuA6Ng==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "4.15.3" - } - }, - "node_modules/mjml-body": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/mjml-body/-/mjml-body-4.15.3.tgz", - "integrity": "sha512-7pfUOVPtmb0wC+oUOn4xBsAw4eT5DyD6xqaxj/kssu6RrFXOXgJaVnDPAI9AzIvXJ/5as9QrqRGYAddehwWpHQ==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "4.15.3" - } - }, - "node_modules/mjml-button": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/mjml-button/-/mjml-button-4.15.3.tgz", - "integrity": "sha512-79qwn9AgdGjJR1vLnrcm2rq2AsAZkKC5JPwffTMG+Nja6zGYpTDZFZ56ekHWr/r1b5WxkukcPj2PdevUug8c+Q==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "4.15.3" - } - }, - "node_modules/mjml-carousel": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/mjml-carousel/-/mjml-carousel-4.15.3.tgz", - "integrity": "sha512-3ju6I4l7uUhPRrJfN3yK9AMsfHvrYbRkcJ1GRphFHzUj37B2J6qJOQUpzA547Y4aeh69TSb7HFVf1t12ejQxVw==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "4.15.3" - } - }, - "node_modules/mjml-cli": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/mjml-cli/-/mjml-cli-4.15.3.tgz", - "integrity": "sha512-+V2TDw3tXUVEptFvLSerz125C2ogYl8klIBRY1m5BHd4JvGVf3yhx8N3PngByCzA6PGcv/eydGQN+wy34SHf0Q==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.23.9", - "chokidar": "^3.0.0", - "glob": "^10.3.10", - "html-minifier": "^4.0.0", - "js-beautify": "^1.6.14", - "lodash": "^4.17.21", - "minimatch": "^9.0.3", - "mjml-core": "4.15.3", - "mjml-migrate": "4.15.3", - "mjml-parser-xml": "4.15.3", - "mjml-validator": "4.15.3", - "yargs": "^17.7.2" - }, - "bin": { - "mjml-cli": "bin/mjml" - } - }, - "node_modules/mjml-column": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/mjml-column/-/mjml-column-4.15.3.tgz", - "integrity": "sha512-hYdEFdJGHPbZJSEysykrevEbB07yhJGSwfDZEYDSbhQQFjV2tXrEgYcFD5EneMaowjb55e3divSJxU4c5q4Qgw==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "4.15.3" - } - }, - "node_modules/mjml-core": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/mjml-core/-/mjml-core-4.15.3.tgz", - "integrity": "sha512-Dmwk+2cgSD9L9GmTbEUNd8QxkTZtW9P7FN/ROZW/fGZD6Hq6/4TB0zEspg2Ow9eYjZXO2ofOJ3PaQEEShKV0kQ==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.23.9", - "cheerio": "1.0.0-rc.12", - "detect-node": "^2.0.4", - "html-minifier": "^4.0.0", - "js-beautify": "^1.6.14", - "juice": "^10.0.0", - "lodash": "^4.17.21", - "mjml-migrate": "4.15.3", - "mjml-parser-xml": "4.15.3", - "mjml-validator": "4.15.3" - } - }, - "node_modules/mjml-divider": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/mjml-divider/-/mjml-divider-4.15.3.tgz", - "integrity": "sha512-vh27LQ9FG/01y0b9ntfqm+GT5AjJnDSDY9hilss2ixIUh0FemvfGRfsGVeV5UBVPBKK7Ffhvfqc7Rciob9Spzw==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "4.15.3" - } - }, - "node_modules/mjml-group": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/mjml-group/-/mjml-group-4.15.3.tgz", - "integrity": "sha512-HSu/rKnGZVKFq3ciT46vi1EOy+9mkB0HewO4+P6dP/Y0UerWkN6S3UK11Cxsj0cAp0vFwkPDCdOeEzRdpFEkzA==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "4.15.3" - } - }, - "node_modules/mjml-head": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/mjml-head/-/mjml-head-4.15.3.tgz", - "integrity": "sha512-o3mRuuP/MB5fZycjD3KH/uXsnaPl7Oo8GtdbJTKtH1+O/3pz8GzGMkscTKa97l03DAG2EhGrzzLcU2A6eshwFw==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "4.15.3" - } - }, - "node_modules/mjml-head-attributes": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/mjml-head-attributes/-/mjml-head-attributes-4.15.3.tgz", - "integrity": "sha512-2ISo0r5ZKwkrvJgDou9xVPxxtXMaETe2AsAA02L89LnbB2KC0N5myNsHV0sEysTw9+CfCmgjAb0GAI5QGpxKkQ==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "4.15.3" - } - }, - "node_modules/mjml-head-breakpoint": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/mjml-head-breakpoint/-/mjml-head-breakpoint-4.15.3.tgz", - "integrity": "sha512-Eo56FA5C2v6ucmWQL/JBJ2z641pLOom4k0wP6CMZI2utfyiJ+e2Uuinj1KTrgDcEvW4EtU9HrfAqLK9UosLZlg==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "4.15.3" - } - }, - "node_modules/mjml-head-font": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/mjml-head-font/-/mjml-head-font-4.15.3.tgz", - "integrity": "sha512-CzV2aDPpiNIIgGPHNcBhgyedKY4SX3BJoTwOobSwZVIlEA6TAWB4Z9WwFUmQqZOgo1AkkiTHPZQvGcEhFFXH6g==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "4.15.3" - } - }, - "node_modules/mjml-head-html-attributes": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/mjml-head-html-attributes/-/mjml-head-html-attributes-4.15.3.tgz", - "integrity": "sha512-MDNDPMBOgXUZYdxhosyrA2kudiGO8aogT0/cODyi2Ed9o/1S7W+je11JUYskQbncqhWKGxNyaP4VWa+6+vUC/g==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "4.15.3" - } - }, - "node_modules/mjml-head-preview": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/mjml-head-preview/-/mjml-head-preview-4.15.3.tgz", - "integrity": "sha512-J2PxCefUVeFwsAExhrKo4lwxDevc5aKj888HBl/wN4EuWOoOg06iOGCxz4Omd8dqyFsrqvbBuPqRzQ+VycGmaA==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "4.15.3" - } - }, - "node_modules/mjml-head-style": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/mjml-head-style/-/mjml-head-style-4.15.3.tgz", - "integrity": "sha512-9J+JuH+mKrQU65CaJ4KZegACUgNIlYmWQYx3VOBR/tyz+8kDYX7xBhKJCjQ1I4wj2Tvga3bykd89Oc2kFZ5WOw==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "4.15.3" - } - }, - "node_modules/mjml-head-title": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/mjml-head-title/-/mjml-head-title-4.15.3.tgz", - "integrity": "sha512-IM59xRtsxID4DubQ0iLmoCGXguEe+9BFG4z6y2xQDrscIa4QY3KlfqgKGT69ojW+AVbXXJPEVqrAi4/eCsLItQ==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "4.15.3" - } - }, - "node_modules/mjml-hero": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/mjml-hero/-/mjml-hero-4.15.3.tgz", - "integrity": "sha512-9cLAPuc69yiuzNrMZIN58j+HMK1UWPaq2i3/Fg2ZpimfcGFKRcPGCbEVh0v+Pb6/J0+kf8yIO0leH20opu3AyQ==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "4.15.3" - } - }, - "node_modules/mjml-image": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/mjml-image/-/mjml-image-4.15.3.tgz", - "integrity": "sha512-g1OhSdofIytE9qaOGdTPmRIp7JsCtgO0zbsn1Fk6wQh2gEL55Z40j/VoghslWAWTgT2OHFdBKnMvWtN6U5+d2Q==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "4.15.3" - } - }, - "node_modules/mjml-migrate": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/mjml-migrate/-/mjml-migrate-4.15.3.tgz", - "integrity": "sha512-sr/+35RdxZroNQVegjpfRHJ5hda9XCgaS4mK2FGO+Mb1IUevKfeEPII3F/cHDpNwFeYH3kAgyqQ22ClhGLWNBA==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.23.9", - "js-beautify": "^1.6.14", - "lodash": "^4.17.21", - "mjml-core": "4.15.3", - "mjml-parser-xml": "4.15.3", - "yargs": "^17.7.2" - }, - "bin": { - "migrate": "lib/cli.js" - } - }, - "node_modules/mjml-navbar": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/mjml-navbar/-/mjml-navbar-4.15.3.tgz", - "integrity": "sha512-VsKH/Jdlf8Yu3y7GpzQV5n7JMdpqvZvTSpF6UQXL0PWOm7k6+LX+sCZimOfpHJ+wCaaybpxokjWZ71mxOoCWoA==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "4.15.3" - } - }, - "node_modules/mjml-parser-xml": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/mjml-parser-xml/-/mjml-parser-xml-4.15.3.tgz", - "integrity": "sha512-Tz0UX8/JVYICLjT+U8J1f/TFxIYVYjzZHeh4/Oyta0pLpRLeZlxEd71f3u3kdnulCKMP4i37pFRDmyLXAlEuLw==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.23.9", - "detect-node": "2.1.0", - "htmlparser2": "^9.1.0", - "lodash": "^4.17.15" - } - }, - "node_modules/mjml-parser-xml/node_modules/htmlparser2": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", - "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "optional": true, - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.1.0", - "entities": "^4.5.0" - } - }, - "node_modules/mjml-preset-core": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/mjml-preset-core/-/mjml-preset-core-4.15.3.tgz", - "integrity": "sha512-1zZS8P4O0KweWUqNS655+oNnVMPQ1Rq1GaZq5S9JfwT1Vh/m516lSmiTW9oko6gGHytt5s6Yj6oOeu5Zm8FoLw==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.23.9", - "mjml-accordion": "4.15.3", - "mjml-body": "4.15.3", - "mjml-button": "4.15.3", - "mjml-carousel": "4.15.3", - "mjml-column": "4.15.3", - "mjml-divider": "4.15.3", - "mjml-group": "4.15.3", - "mjml-head": "4.15.3", - "mjml-head-attributes": "4.15.3", - "mjml-head-breakpoint": "4.15.3", - "mjml-head-font": "4.15.3", - "mjml-head-html-attributes": "4.15.3", - "mjml-head-preview": "4.15.3", - "mjml-head-style": "4.15.3", - "mjml-head-title": "4.15.3", - "mjml-hero": "4.15.3", - "mjml-image": "4.15.3", - "mjml-navbar": "4.15.3", - "mjml-raw": "4.15.3", - "mjml-section": "4.15.3", - "mjml-social": "4.15.3", - "mjml-spacer": "4.15.3", - "mjml-table": "4.15.3", - "mjml-text": "4.15.3", - "mjml-wrapper": "4.15.3" - } - }, - "node_modules/mjml-raw": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/mjml-raw/-/mjml-raw-4.15.3.tgz", - "integrity": "sha512-IGyHheOYyRchBLiAEgw3UM11kFNmBSMupu2BDdejC6ZiDhEAdG+tyERlsCwDPYtXanvFpGWULIu3XlsUPc+RZw==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "4.15.3" - } - }, - "node_modules/mjml-section": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/mjml-section/-/mjml-section-4.15.3.tgz", - "integrity": "sha512-JfVPRXH++Hd933gmQfG8JXXCBCR6fIzC3DwiYycvanL/aW1cEQ2EnebUfQkt5QzlYjOkJEH+JpccAsq3ln6FZQ==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "4.15.3" - } - }, - "node_modules/mjml-social": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/mjml-social/-/mjml-social-4.15.3.tgz", - "integrity": "sha512-7sD5FXrESOxpT9Z4Oh36bS6u/geuUrMP1aCg2sjyAwbPcF1aWa2k9OcatQfpRf6pJEhUZ18y6/WBBXmMVmSzXg==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "4.15.3" - } - }, - "node_modules/mjml-spacer": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/mjml-spacer/-/mjml-spacer-4.15.3.tgz", - "integrity": "sha512-3B7Qj+17EgDdAtZ3NAdMyOwLTX1jfmJuY7gjyhS2HtcZAmppW+cxqHUBwCKfvSRgTQiccmEvtNxaQK+tfyrZqA==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "4.15.3" - } - }, - "node_modules/mjml-table": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/mjml-table/-/mjml-table-4.15.3.tgz", - "integrity": "sha512-FLx7DcRKTdKdcOCbMyBaeudeHaHpwPveRrBm6WyQe3LXx6FfdmOh59i71/16LFQMgBOD3N4/UJkzxLzlTJzMqQ==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "4.15.3" - } - }, - "node_modules/mjml-text": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/mjml-text/-/mjml-text-4.15.3.tgz", - "integrity": "sha512-+C0hxCmw9kg0XzT6vhE5mFkK6y225nC8UEQcN94K0fBCjPKkM+HqZMwGX205fzdGRi+Bxa55b/VhrIVwdv+8vw==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "4.15.3" - } - }, - "node_modules/mjml-validator": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/mjml-validator/-/mjml-validator-4.15.3.tgz", - "integrity": "sha512-Xb72KdqRwjv/qM2rJpV22syyP2N3cRQ9VVDrN6u2FSzLq02buFNxmSPJ7CKhat3PrUNdVHU75KZwOf/tz4UEhA==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.23.9" - } - }, - "node_modules/mjml-wrapper": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/mjml-wrapper/-/mjml-wrapper-4.15.3.tgz", - "integrity": "sha512-ditsCijeHJrmBmObtJmQ18ddLxv5oPyMTdPU8Di8APOnD2zPk7Z4UAuJSl7HXB45oFiivr3MJf4koFzMUSZ6Gg==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.23.9", - "lodash": "^4.17.21", - "mjml-core": "4.15.3", - "mjml-section": "4.15.3" - } - }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -8656,9 +7798,9 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/multer": { - "version": "1.4.4-lts.1", - "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.4-lts.1.tgz", - "integrity": "sha512-WeSGziVj6+Z2/MwQo3GvqzgR+9Uc+qt8SwHKh3gvNPiISKfsMfG4SvCOFYlxxgkXt7yIV2i1yczehm0EOKIxIg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.0.tgz", + "integrity": "sha512-bS8rPZurbAuHGAnApbM9d4h1wSoYqrOqkE+6a64KLMK9yWU7gJXBDDVklKQ3TPi9DRb85cRs6yXaC0+cjxRtRg==", "dependencies": { "append-field": "^1.0.0", "busboy": "^1.0.0", @@ -8669,7 +7811,7 @@ "xtend": "^4.0.0" }, "engines": { - "node": ">= 6.0.0" + "node": ">= 10.16.0" } }, "node_modules/mute-stream": { @@ -8698,21 +7840,6 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "devOptional": true }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "optional": true - }, - "node_modules/no-case": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", - "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", - "optional": true, - "dependencies": { - "lower-case": "^1.1.1" - } - }, "node_modules/node-abort-controller": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", @@ -8778,33 +7905,18 @@ "dev": true }, "node_modules/nodemailer": { - "version": "6.9.15", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.15.tgz", - "integrity": "sha512-AHf04ySLC6CIfuRtRiEYtGEXgRfa6INgWGluDhnxTZhHSKvrBu7lc1VVchQ0d8nPc4cFaZoPq8vkyNoZr0TpGQ==", + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.1.tgz", + "integrity": "sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA==", "engines": { "node": ">=6.0.0" } }, - "node_modules/nopt": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", - "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", - "optional": true, - "dependencies": { - "abbrev": "^2.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "devOptional": true, + "dev": true, "engines": { "node": ">=0.10.0" } @@ -8822,15 +7934,11 @@ } }, "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "optional": true, + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" + "boolbase": "~1.0.0" } }, "node_modules/object-assign": { @@ -8875,7 +7983,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "dependencies": { "wrappy": "1" } @@ -8899,7 +8006,6 @@ "version": "7.4.2", "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", - "optional": true, "dependencies": { "is-docker": "^2.0.0", "is-wsl": "^2.1.1" @@ -8960,30 +8066,6 @@ "node": ">=0.10.0" } }, - "node_modules/p-event": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz", - "integrity": "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==", - "optional": true, - "dependencies": { - "p-timeout": "^3.1.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", - "optional": true, - "engines": { - "node": ">=4" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -9014,18 +8096,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-timeout": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", - "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", - "optional": true, - "dependencies": { - "p-finally": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -9035,35 +8105,11 @@ "node": ">=6" } }, - "node_modules/p-wait-for": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/p-wait-for/-/p-wait-for-3.2.0.tgz", - "integrity": "sha512-wpgERjNkLrBiFmkMEjuZJEWKKDrNfHCKA1OhyN1wg1FrLkULbviEy6py1AyJUgZ72YWFbZ38FIpnqvVqAlDUwA==", - "optional": true, - "dependencies": { - "p-timeout": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/package-json-from-dist": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", - "devOptional": true - }, - "node_modules/param-case": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", - "integrity": "sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==", - "optional": true, - "dependencies": { - "no-case": "^2.2.0" - } + "dev": true }, "node_modules/parent-module": { "version": "1.0.1", @@ -9095,36 +8141,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "optional": true, - "dependencies": { - "entities": "^4.4.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", - "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", - "optional": true, - "dependencies": { - "domhandler": "^5.0.2", - "parse5": "^7.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, "node_modules/parseley": { "version": "0.12.1", "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.1.tgz", "integrity": "sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==", - "optional": true, "dependencies": { "leac": "^0.6.0", "peberminta": "^0.9.0" @@ -9188,7 +8208,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -9197,6 +8216,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, "engines": { "node": ">=8" } @@ -9204,13 +8224,13 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "devOptional": true + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-scurry": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -9225,7 +8245,8 @@ "node_modules/path-scurry/node_modules/lru-cache": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true }, "node_modules/path-to-regexp": { "version": "3.3.0", @@ -9250,11 +8271,18 @@ "version": "0.9.0", "resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.9.0.tgz", "integrity": "sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==", - "optional": true, "funding": { "url": "https://ko-fi.com/killymxi" } }, + "node_modules/pick-util": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/pick-util/-/pick-util-1.1.5.tgz", + "integrity": "sha512-H0MaM8T7wpQ/azvB12ChZw7kpSFzjsgv3Z+N7fUWnL1McTGSEeroCngcK4eOPiFQq08rAyKX3hadcAB1kUqfXA==", + "dependencies": { + "@jonkemp/package-utils": "^1.0.8" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -9418,25 +8446,21 @@ } }, "node_modules/preview-email": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/preview-email/-/preview-email-3.1.0.tgz", - "integrity": "sha512-ZtV1YrwscEjlrUzYrTSs6Nwo49JM3pXLM4fFOBSC3wSni+bxaWlw9/Qgk75PZO8M7cX2EybmL2iwvaV3vkAttw==", - "optional": true, + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/preview-email/-/preview-email-3.0.4.tgz", + "integrity": "sha512-g9jbnFHI8QfQAcKeCsZpSzMJT/CeGuJoV311R/NLS6PTsalJkMKkUeirSJJgMJBUYOGJLrhM7MsNVWgk1b13BA==", + "deprecated": "We just released outbound SMTP support! Try it out at @ https://forwardemail.net/docs/how-to-javascript-contact-forms-node-js 🚀 ✉️ 👽", "dependencies": { - "ci-info": "^3.8.0", - "display-notification": "2.0.0", - "fixpack": "^4.0.0", - "get-port": "5.1.1", - "mailparser": "^3.7.1", - "nodemailer": "^6.9.13", + "dayjs": "^1.10.4", + "debug": "^4.3.1", + "mailparser": "^3.1.0", + "nodemailer": "^6.5.0", "open": "7", - "p-event": "4.2.0", - "p-wait-for": "3.2.0", - "pug": "^3.0.3", - "uuid": "^9.0.1" + "pug": "^3.0.2", + "uuid": "^8.3.2" }, "engines": { - "node": ">=14" + "node": ">=10" } }, "node_modules/prisma": { @@ -9466,7 +8490,6 @@ "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "optional": true, "dependencies": { "asap": "~2.0.3" } @@ -9484,12 +8507,6 @@ "node": ">= 6" } }, - "node_modules/proto-list": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", - "optional": true - }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -9502,11 +8519,15 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/pug": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/pug/-/pug-3.0.3.tgz", "integrity": "sha512-uBi6kmc9f3SZ3PXxqcHiUZLmIXgfgWooKWXcwSGwQd2Zi5Rb0bT14+8CJjJgI8AB+nndLaNgHGrcc6bPIB665g==", - "optional": true, "dependencies": { "pug-code-gen": "^3.0.3", "pug-filters": "^4.0.0", @@ -9522,7 +8543,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-3.0.0.tgz", "integrity": "sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==", - "optional": true, "dependencies": { "constantinople": "^4.0.1", "js-stringify": "^1.0.2", @@ -9533,7 +8553,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-3.0.3.tgz", "integrity": "sha512-cYQg0JW0w32Ux+XTeZnBEeuWrAY7/HNE6TWnhiHGnnRYlCgyAUPoyh9KzCMa9WhcJlJ1AtQqpEYHc+vbCzA+Aw==", - "optional": true, "dependencies": { "constantinople": "^4.0.1", "doctypes": "^1.1.0", @@ -9548,14 +8567,12 @@ "node_modules/pug-error": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-2.1.0.tgz", - "integrity": "sha512-lv7sU9e5Jk8IeUheHata6/UThZ7RK2jnaaNztxfPYUY+VxZyk/ePVaNZ/vwmH8WqGvDz3LrNYt/+gA55NDg6Pg==", - "optional": true + "integrity": "sha512-lv7sU9e5Jk8IeUheHata6/UThZ7RK2jnaaNztxfPYUY+VxZyk/ePVaNZ/vwmH8WqGvDz3LrNYt/+gA55NDg6Pg==" }, "node_modules/pug-filters": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-4.0.0.tgz", "integrity": "sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==", - "optional": true, "dependencies": { "constantinople": "^4.0.1", "jstransformer": "1.0.0", @@ -9568,7 +8585,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-5.0.1.tgz", "integrity": "sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==", - "optional": true, "dependencies": { "character-parser": "^2.2.0", "is-expression": "^4.0.0", @@ -9579,7 +8595,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-4.0.0.tgz", "integrity": "sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==", - "optional": true, "dependencies": { "pug-error": "^2.0.0", "pug-walk": "^2.0.0" @@ -9589,7 +8604,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-3.0.0.tgz", "integrity": "sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==", - "optional": true, "dependencies": { "object-assign": "^4.1.1", "pug-walk": "^2.0.0" @@ -9599,7 +8613,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-6.0.0.tgz", "integrity": "sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==", - "optional": true, "dependencies": { "pug-error": "^2.0.0", "token-stream": "1.0.0" @@ -9608,14 +8621,12 @@ "node_modules/pug-runtime": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-3.0.1.tgz", - "integrity": "sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==", - "optional": true + "integrity": "sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==" }, "node_modules/pug-strip-comments": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz", "integrity": "sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==", - "optional": true, "dependencies": { "pug-error": "^2.0.0" } @@ -9623,8 +8634,7 @@ "node_modules/pug-walk": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz", - "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==", - "optional": true + "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==" }, "node_modules/punycode": { "version": "2.3.1", @@ -9638,7 +8648,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", - "optional": true, "engines": { "node": ">=6" } @@ -9719,33 +8728,9 @@ "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "optional": true, - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "optional": true, + }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.8" } }, "node_modules/react-is": { @@ -9755,29 +8740,23 @@ "dev": true }, "node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" } }, - "node_modules/readable-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "devOptional": true, + "dev": true, "dependencies": { "picomatch": "^2.2.1" }, @@ -9789,7 +8768,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "devOptional": true, + "dev": true, "engines": { "node": ">=8.6" }, @@ -9802,19 +8781,13 @@ "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==" }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "optional": true - }, - "node_modules/relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", - "optional": true, - "engines": { - "node": ">= 0.10" + "node_modules/remote-content": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remote-content/-/remote-content-4.0.1.tgz", + "integrity": "sha512-W2lDnjK4k1vAJg7UZArH/rkNYJqZuteHkX1jS7tO9TJUiLhDcu2Ejvj97gK/XbZNDhzld0sn11OW8vihin4cAg==", + "dependencies": { + "axios": "^1.7.9", + "proxy-from-env": "^1.1.0" } }, "node_modules/repeat-string": { @@ -9830,7 +8803,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "devOptional": true, + "dev": true, "engines": { "node": ">=0.10.0" } @@ -9848,7 +8821,6 @@ "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "devOptional": true, "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -9988,139 +8960,6 @@ "node": "*" } }, - "node_modules/run-applescript": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-3.2.0.tgz", - "integrity": "sha512-Ep0RsvAjnRcBX1p5vogbaBdAGu/8j/ewpvGqnQYunnLd9SM0vWcPJewPKNnWFggf0hF0pwIgwV5XK7qQ7UZ8Qg==", - "optional": true, - "dependencies": { - "execa": "^0.10.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/run-applescript/node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "optional": true, - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" - } - }, - "node_modules/run-applescript/node_modules/execa": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", - "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", - "optional": true, - "dependencies": { - "cross-spawn": "^6.0.0", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/run-applescript/node_modules/get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", - "optional": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/run-applescript/node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/run-applescript/node_modules/npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", - "optional": true, - "dependencies": { - "path-key": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/run-applescript/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "optional": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/run-applescript/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "optional": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/run-applescript/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "optional": true, - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/run-applescript/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/run-applescript/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "optional": true - }, - "node_modules/run-applescript/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "optional": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -10238,7 +9077,6 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.11.0.tgz", "integrity": "sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==", - "optional": true, "dependencies": { "parseley": "^0.12.0" }, @@ -10349,6 +9187,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -10360,6 +9199,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, "engines": { "node": ">=8" } @@ -10390,6 +9230,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, "engines": { "node": ">=14" }, @@ -10416,21 +9257,20 @@ "version": "1.12.2", "resolved": "https://registry.npmjs.org/slick/-/slick-1.12.2.tgz", "integrity": "sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A==", - "optional": true, "engines": { "node": "*" } }, "node_modules/socket.io": { - "version": "4.7.5", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz", - "integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", "cors": "~2.8.5", "debug": "~4.3.2", - "engine.io": "~6.5.2", + "engine.io": "~6.6.0", "socket.io-adapter": "~2.5.2", "socket.io-parser": "~4.2.4" }, @@ -10495,6 +9335,14 @@ "memory-pager": "^1.0.2" } }, + "node_modules/specificity": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/specificity/-/specificity-0.4.1.tgz", + "integrity": "sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg==", + "bin": { + "specificity": "bin/specificity" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -10568,6 +9416,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -10582,6 +9431,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -10595,6 +9445,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -10607,6 +9458,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -10623,15 +9475,6 @@ "node": ">=8" } }, - "node_modules/strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", @@ -10653,6 +9496,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/style-data": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/style-data/-/style-data-1.4.8.tgz", + "integrity": "sha512-RBJD+YQef4PzYKqC4PQEjDvyX709mwEClYg9u0A5EPXqdSkN2BtMnF/tW7EtS9Q0FnBF+lrWsK5+bEKqA+++Dg==", + "dependencies": { + "cheerio": "^0.22.0", + "mediaquery-text": "^1.2.0", + "pick-util": "^1.1.4" + } + }, "node_modules/superagent": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/superagent/-/superagent-9.0.2.tgz", @@ -10713,7 +9566,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "devOptional": true, "engines": { "node": ">= 0.4" }, @@ -10917,10 +9769,9 @@ "dev": true }, "node_modules/tlds": { - "version": "1.252.0", - "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.252.0.tgz", - "integrity": "sha512-GA16+8HXvqtfEnw/DTcwB0UU354QE1n3+wh08oFjr6Znl7ZLAeUgYzCcK+/CCrOyE0vnHR8/pu3XXG3vDijXpQ==", - "optional": true, + "version": "1.259.0", + "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.259.0.tgz", + "integrity": "sha512-AldGGlDP0PNgwppe2quAvuBl18UcjuNtOnDuUkqhd6ipPqrYYBt3aTxK1QTsBVknk97lS2JcafWMghjGWFtunw==", "bin": { "tlds": "bin.js" } @@ -10943,20 +9794,11 @@ "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "devOptional": true, - "engines": { - "node": ">=4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "devOptional": true, + "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -10975,8 +9817,7 @@ "node_modules/token-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz", - "integrity": "sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg==", - "optional": true + "integrity": "sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg==" }, "node_modules/tr46": { "version": "0.0.3", @@ -11224,8 +10065,7 @@ "node_modules/uc.micro": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", - "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", - "optional": true + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==" }, "node_modules/uglify-js": { "version": "3.19.3", @@ -11302,12 +10142,6 @@ "browserslist": ">= 4.21.0" } }, - "node_modules/upper-case": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", - "integrity": "sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==", - "optional": true - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -11331,14 +10165,9 @@ } }, "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "optional": true, + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "bin": { "uuid": "dist/bin/uuid" } @@ -11363,15 +10192,6 @@ "node": ">=10.12.0" } }, - "node_modules/valid-data-url": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/valid-data-url/-/valid-data-url-3.0.1.tgz", - "integrity": "sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA==", - "optional": true, - "engines": { - "node": ">=10" - } - }, "node_modules/validator": { "version": "13.12.0", "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", @@ -11392,7 +10212,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", - "optional": true, "engines": { "node": ">=0.10.0" } @@ -11428,132 +10247,6 @@ "defaults": "^1.0.3" } }, - "node_modules/web-resource-inliner": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/web-resource-inliner/-/web-resource-inliner-6.0.1.tgz", - "integrity": "sha512-kfqDxt5dTB1JhqsCUQVFDj0rmY+4HLwGQIsLPbyrsN9y9WV/1oFDSx3BQ4GfCv9X+jVeQ7rouTqwK53rA/7t8A==", - "optional": true, - "dependencies": { - "ansi-colors": "^4.1.1", - "escape-goat": "^3.0.0", - "htmlparser2": "^5.0.0", - "mime": "^2.4.6", - "node-fetch": "^2.6.0", - "valid-data-url": "^3.0.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/web-resource-inliner/node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "optional": true, - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/web-resource-inliner/node_modules/dom-serializer/node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "optional": true, - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/web-resource-inliner/node_modules/domhandler": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz", - "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==", - "optional": true, - "dependencies": { - "domelementtype": "^2.0.1" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/web-resource-inliner/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "optional": true, - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/web-resource-inliner/node_modules/domutils/node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "optional": true, - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/web-resource-inliner/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "optional": true, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/web-resource-inliner/node_modules/htmlparser2": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-5.0.1.tgz", - "integrity": "sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==", - "optional": true, - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^3.3.0", - "domutils": "^2.4.2", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/fb55/htmlparser2?sponsor=1" - } - }, - "node_modules/web-resource-inliner/node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "optional": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -11658,6 +10351,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -11672,7 +10366,6 @@ "version": "7.0.2", "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz", "integrity": "sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==", - "optional": true, "dependencies": { "@babel/parser": "^7.9.6", "@babel/types": "^7.9.6", @@ -11717,6 +10410,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -11732,8 +10426,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/write-file-atomic": { "version": "4.0.2", @@ -11786,7 +10479,7 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "devOptional": true, + "dev": true, "engines": { "node": ">=10" } @@ -11801,7 +10494,7 @@ "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "devOptional": true, + "dev": true, "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -11819,7 +10512,7 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "devOptional": true, + "dev": true, "engines": { "node": ">=12" } diff --git a/package.json b/package.json index 1a5c217..fde9eda 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "integration:full": "docker compose up -d && sleep 5s && npm run test:migrate && npm run test:integration && docker compose down" }, "dependencies": { - "@nestjs-modules/mailer": "^2.0.2", + "@nestjs-modules/mailer": "^1.6.1", "@nestjs/common": "^10.0.0", "@nestjs/config": "^3.2.3", "@nestjs/core": "^10.0.0", diff --git a/src/app.module.ts b/src/app.module.ts index 8954035..dd897b6 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,33 +1,34 @@ import { Module } from '@nestjs/common'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { APP_GUARD } from '@nestjs/core'; +import { MongooseModule } from '@nestjs/mongoose'; import { AppController } from './app.controller'; import { AppService } from './app.service'; -import { ConfigModule, ConfigService } from '@nestjs/config'; -import { UserModule } from './user/user.module'; +import { AuctionModule } from './auction/auction.module'; import { AuthModule } from './auth/auth.module'; -import { PrismaModule } from './prisma/prisma.module'; import { JwtGuard } from './auth/guard/jwt.guard'; -import { APP_GUARD } from '@nestjs/core'; import { ChatModule } from './chat/chat.module'; +import { FieldModule } from './field/field.module'; import { GameModule } from './game/game.module'; +import { PaymentModule } from './payment/payment.module'; import { PlayerModule } from './player/player.module'; -import { EventEmitterModule } from '@nestjs/event-emitter'; -import { WebSocketServerModule } from './webSocketServer/webSocketServer.module'; -import { WebSocketServerService } from './webSocketServer/webSocketServer.service'; -import { MongooseModule } from '@nestjs/mongoose'; -import { AuctionModule } from './auction/auction.module'; +import { PrismaModule } from './prisma/prisma.module'; import { SecretModule } from './secret/secret.module'; +import { UserModule } from './user/user.module'; +import { WebSocketProviderModule } from './webSocketProvider/webSocketProvider.module'; +import { WebSocketProvider } from './webSocketProvider/webSocketProvider.service'; +import { TradeModule } from './trade/trade.module'; @Module({ imports: [ ConfigModule.forRoot(), - EventEmitterModule.forRoot(), UserModule, AuthModule, PrismaModule, ChatModule, GameModule, PlayerModule, - WebSocketServerModule, + WebSocketProviderModule, MongooseModule.forRootAsync({ imports: [ConfigModule], inject: [ConfigService], @@ -37,6 +38,9 @@ import { SecretModule } from './secret/secret.module'; }), AuctionModule, SecretModule, + FieldModule, + PaymentModule, + TradeModule, ], controllers: [AppController], providers: [ @@ -45,7 +49,7 @@ import { SecretModule } from './secret/secret.module'; provide: APP_GUARD, useClass: JwtGuard, }, - WebSocketServerService, + WebSocketProvider, ], }) export class AppModule {} diff --git a/src/auction/auction.module.ts b/src/auction/auction.module.ts index c42167b..627e0e2 100644 --- a/src/auction/auction.module.ts +++ b/src/auction/auction.module.ts @@ -3,12 +3,16 @@ import { AuctionService } from './auction.service'; import { GameModule } from 'src/game/game.module'; import { PlayerModule } from 'src/player/player.module'; import { TimerModule } from 'src/timer/timers.module'; +import { FieldModule } from 'src/field/field.module'; +import { WebSocketProviderModule } from 'src/webSocketProvider/webSocketProvider.module'; @Module({ imports: [ forwardRef(() => GameModule), forwardRef(() => PlayerModule), TimerModule, + FieldModule, + WebSocketProviderModule, ], providers: [AuctionService], exports: [AuctionService], diff --git a/src/auction/auction.service.ts b/src/auction/auction.service.ts index 2b5cdcf..0658768 100644 --- a/src/auction/auction.service.ts +++ b/src/auction/auction.service.ts @@ -6,15 +6,19 @@ import { Auction } from 'src/game/types/auction.type'; import { TimerService } from 'src/timer/timers.service'; import { PlayerPayload } from 'src/player/player.repository'; import { PlayerService } from 'src/player/player.service'; +import { FieldService } from 'src/field/field.service'; +import { WebSocketProvider } from 'src/webSocketProvider/webSocketProvider.service'; @Injectable() export class AuctionService { constructor( - @Inject(forwardRef(() => GameService)) - private gameService: GameService, @Inject(forwardRef(() => PlayerService)) private playerService: PlayerService, - private timerService: TimerService + @Inject(forwardRef(() => GameService)) + private gameService: GameService, + private timerService: TimerService, + private fieldService: FieldService, + private webSocketProvider: WebSocketProvider ) { this.hightestInQueue = this.hightestInQueue.bind(this); this.winAuction = this.winAuction.bind(this); @@ -51,8 +55,8 @@ export class AuctionService { return player; } private async getAuctionableFieldOrThrow(gameId: string, fieldIndex: number) { - const fields = await this.gameService.getGameFields(gameId); - const field = this.gameService.findPlayerFieldByIndex(fields, fieldIndex); + const fields = await this.fieldService.getGameFields(gameId); + const field = this.fieldService.findPlayerFieldByIndex(fields, fieldIndex); if (!field.price) { throw new WsException('You cant put this field to auction'); } @@ -123,12 +127,28 @@ export class AuctionService { ); this.timerService.clear(gameId); this.setBidderOnAuction(gameId, userId, raiseBy, false); - return this.timerService.set( + const auctionWithAcceptedBid = await this.timerService.set( gameId, 2000, { gameId, userId, raiseBy }, this.hightestInQueue ); + + if (auctionWithAcceptedBid) { + this.webSocketProvider.server + .to(gameId) + .emit('raisedPrice', { auction: auctionWithAcceptedBid }); + this.timerService.set( + gameId, + 15000, + { ...auctionWithAcceptedBid, gameId }, + this.winAuction + ); + } else { + this.webSocketProvider.server.to(userId).emit('error', { + message: 'Хтось одночасно поставив з вами і перебив вашу ставку', + }); + } } setBidderOnAuction( @@ -183,19 +203,15 @@ export class AuctionService { if (isAuctionComplete) { this.timerService.clear(gameId); const hasWinner = !!this.findLastAcceptedBidder(auction); - return { - auction, - hasWinner, - finished: true, - game, - }; + if (hasWinner) { + this.winAuction({ ...auction, gameId }); + } else { + this.gameService.passTurn(game); + } } - return { - auction, - hasWinner: false, - finished: false, - game, - }; + this.webSocketProvider.server + .to(gameId) + .emit('refusedFromAuction', { auction }); } hightestInQueue(args: { gameId: string; userId: string; raiseBy: number }) { @@ -209,8 +225,17 @@ export class AuctionService { } async winAuction(auction: Auction & { gameId: string }) { - const fields = await this.gameService.getGameFields(auction.gameId); - const field = this.gameService.findPlayerFieldByIndex( + const { updatedPlayer, fields } = + await this.processVictoryOfAuction(auction); + this.webSocketProvider.server + .to(auction.gameId) + .emit('wonAuction', { auction, game: updatedPlayer.game, fields }); + this.gameService.passTurn(updatedPlayer.game); + } + + async processVictoryOfAuction(auction: Auction & { gameId: string }) { + const fields = await this.fieldService.getGameFields(auction.gameId); + const field = this.fieldService.findPlayerFieldByIndex( fields, auction.fieldIndex ); @@ -222,7 +247,7 @@ export class AuctionService { auction.gameId, lastAcceptedBidder.bid ); - await this.gameService.updateFields(fields, ['ownedBy']); + await this.fieldService.updateFields(fields, ['ownedBy']); this.setAuction(auction.gameId, null); return { updatedPlayer, fields }; } diff --git a/src/auth/guard/activeGame.guard.ts b/src/auth/guard/activeGame.guard.ts deleted file mode 100644 index 5b7815f..0000000 --- a/src/auth/guard/activeGame.guard.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { CanActivate, Injectable } from '@nestjs/common'; -import { WsException } from '@nestjs/websockets'; -import { GameService } from 'src/game/game.service'; -import { parse } from 'cookie'; - -@Injectable() -export class ActiveGameGuard implements CanActivate { - constructor(private gameService: GameService) {} - - async canActivate(context: any): Promise { - const client = context.switchToWs().getClient(); - const { gameId } = parse(client.handshake.headers.cookie); - try { - const game = await this.gameService.getGame(gameId); - if (game.status !== 'ACTIVE') - throw new WsException('Game is not active anymore'); - client.game = game; - return true; - } catch (ex) { - throw new WsException(ex.message); - } - } -} diff --git a/src/auth/guard/index.ts b/src/auth/guard/index.ts index 99057b5..b3a1b3b 100644 --- a/src/auth/guard/index.ts +++ b/src/auth/guard/index.ts @@ -1,5 +1,6 @@ export * from './jwt.guard'; export * from './jwt.refresh.guard'; export * from './role.guard'; -export * from './hasLost.guard'; +export * from './validPlayer.guard'; export * from './turn.guard'; +export * from './jwt.ws.guard'; diff --git a/src/auth/guard/turn.guard.ts b/src/auth/guard/turn.guard.ts index e8ade90..58b94be 100644 --- a/src/auth/guard/turn.guard.ts +++ b/src/auth/guard/turn.guard.ts @@ -1,10 +1,9 @@ import { CanActivate, Injectable } from '@nestjs/common'; import { WsException } from '@nestjs/websockets'; -import { GameService } from 'src/game/game.service'; @Injectable() export class TurnGuard implements CanActivate { - constructor(private gameService: GameService) {} + constructor() {} async canActivate(context: any): Promise { const client = context.switchToWs().getClient(); diff --git a/src/auth/guard/hasLost.guard.ts b/src/auth/guard/validPlayer.guard.ts similarity index 62% rename from src/auth/guard/hasLost.guard.ts rename to src/auth/guard/validPlayer.guard.ts index 9e1d832..eed73a9 100644 --- a/src/auth/guard/hasLost.guard.ts +++ b/src/auth/guard/validPlayer.guard.ts @@ -1,21 +1,26 @@ import { CanActivate, Injectable } from '@nestjs/common'; import { WsException } from '@nestjs/websockets'; import { PlayerService } from 'src/player/player.service'; +import { parse } from 'cookie'; @Injectable() -export class HasLostGuard implements CanActivate { +export class ValidPlayerGuard implements CanActivate { constructor(private playerService: PlayerService) {} async canActivate(context: any): Promise { const client = context.switchToWs().getClient(); const userId = client.jwtPayload.sub; + const { gameId } = parse(client.handshake.headers.cookie); try { const player = await this.playerService.findByUserAndGameId( userId, - client.game.id + gameId ); - if (player?.lost) throw new WsException('U have already lost'); + if (player?.game?.status !== 'ACTIVE') + throw new WsException('Game is not active anymore'); + if (player?.lost) throw new WsException('You have already lost'); client.player = player; + client.game = player.game; return true; } catch (ex) { throw new WsException(ex.message); diff --git a/src/chat/chat.gateway.ts b/src/chat/chat.gateway.ts index 36410bd..e10b8c1 100644 --- a/src/chat/chat.gateway.ts +++ b/src/chat/chat.gateway.ts @@ -42,8 +42,9 @@ export class ChatGateway implements OnModuleInit { @SubscribeMessage('newMessage') async onNewMessage( socket: Socket & { jwtPayload: JwtPayload }, - data: NewMessagePayloadDto + dataArray: [NewMessagePayloadDto, null] ) { + const data = dataArray[0]; const message = await this.chatService.onNewMessage( socket.jwtPayload.sub, data @@ -55,8 +56,9 @@ export class ChatGateway implements OnModuleInit { @SubscribeMessage('newGameMessage') async onNewGameMessage( socket: Socket & { jwtPayload: JwtPayload }, - data: NewGameMessageDto + dataArray: [NewGameMessageDto, null] ) { + const data = dataArray[0]; const message = await this.chatService.onNewMessage( socket.jwtPayload.sub, data @@ -66,12 +68,14 @@ export class ChatGateway implements OnModuleInit { } @SubscribeMessage('chatData') - onChatData(@MessageBody() data: { chatId: string }) { - return this.chatService.onChatData(data.chatId); + async onChatData(@MessageBody() data: { chatId: string }) { + const chat = await this.chatService.onChatData(data.chatId); + return chat; } @SubscribeMessage('mutualChatData') - onMutualChatData() { - return this.chatService.onMutualChatData(); + async onMutualChatData() { + const mutualChat = await this.chatService.onMutualChatData(); + return mutualChat; } } diff --git a/src/event/event.module.ts b/src/event/event.module.ts deleted file mode 100644 index 4880832..0000000 --- a/src/event/event.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Module } from '@nestjs/common'; -import { EventEmitterModule } from '@nestjs/event-emitter'; -import { EventService } from './event.service'; - -@Module({ - imports: [EventEmitterModule.forRoot()], - providers: [EventService], - exports: [EventService], -}) -export class EventModule {} diff --git a/src/event/event.service.ts b/src/event/event.service.ts deleted file mode 100644 index d98c85e..0000000 --- a/src/event/event.service.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { EventEmitter2 } from '@nestjs/event-emitter'; - -@Injectable() -export class EventService { - constructor(private eventEmitter: EventEmitter2) {} - - emitGameEvent(eventName: string, data: any) { - this.eventEmitter.emit(eventName, data); - } -} diff --git a/src/field/FieldValidator.ts b/src/field/FieldValidator.ts deleted file mode 100644 index 13f6cd3..0000000 --- a/src/field/FieldValidator.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { GamePayload } from 'src/game/game.repository'; -import { PlayerPayload } from 'src/player/player.repository'; -import { PlayerService } from 'src/player/player.service'; -import { FieldDocument } from 'src/schema/Field.schema'; - -export class FieldValidator { - private currentPlayer: Partial; - - constructor( - private field: FieldDocument, - private game: Partial, - private playerService: PlayerService - ) { - this.currentPlayer = this.playerService.findPlayerWithTurn(game); - } - isOwnedByCurrentUser(): boolean { - return ( - this.field.ownedBy === this.currentPlayer.userId && this.field.price > 0 - ); - } - isOwnedByOtherAndNotPledged(): boolean { - return ( - this.field.ownedBy && - this.field.ownedBy !== this.currentPlayer.userId && - !this.field.isPledged - ); - } - isNotOwned(): boolean { - return !this.field.ownedBy && this.field.price > 0; - } - isAffordableForSomeone(): boolean { - return this.game.players.some( - (player) => - player.userId !== this.currentPlayer.userId && - player.money > this.field.price - ); - } - isSpecialField(): boolean { - return !this.field.price; - } - isSkipable(): boolean { - return ( - (this.field?.specialField && !this.field.secret && !this.field.toPay) || - this.field.isPledged - ); - } -} diff --git a/src/field/field.module.ts b/src/field/field.module.ts new file mode 100644 index 0000000..3e971d8 --- /dev/null +++ b/src/field/field.module.ts @@ -0,0 +1,21 @@ +import { Module } from '@nestjs/common'; +import { FieldService } from './field.service'; +import { MongooseModule } from '@nestjs/mongoose'; +import { Field, FieldSchema } from 'src/schema/Field.schema'; + +@Module({ + imports: [ + MongooseModule.forFeature([ + { + name: Field.name, + schema: FieldSchema, + }, + ]), + ], + providers: [FieldService], + exports: [ + FieldService, + MongooseModule.forFeature([{ name: Field.name, schema: FieldSchema }]), + ], +}) +export class FieldModule {} diff --git a/src/field/field.service.ts b/src/field/field.service.ts new file mode 100644 index 0000000..d6b3ade --- /dev/null +++ b/src/field/field.service.ts @@ -0,0 +1,51 @@ +import { Injectable } from '@nestjs/common'; +import { InjectModel } from '@nestjs/mongoose'; +import { Model } from 'mongoose'; +import { Field, FieldDocument } from 'src/schema/Field.schema'; +import { FieldType } from 'src/utils/fields'; + +@Injectable() +export class FieldService { + constructor( + @InjectModel(Field.name) + private fieldModel: Model + ) {} + async getGameFields(gameId: string) { + return await this.fieldModel.find({ gameId }); + } + + createMany(gameFields: FieldType[]) { + return this.fieldModel.insertMany(gameFields); + } + + updateFields( + fields: T[], + propertiesToUpdate: string[] + ) { + const updates = fields.map((field) => { + const updateFields: any = {}; + + for (const property of propertiesToUpdate) { + if (field[property] !== undefined) { + updateFields[property] = field[property]; + } + } + + return { + updateOne: { + filter: { _id: field._id }, + update: { $set: updateFields }, + }, + }; + }); + return this.fieldModel.bulkWrite(updates); + } + + findPlayerFieldByIndex(fields: FieldDocument[], indexOfField: number) { + return fields.find((field) => field.index === indexOfField); + } + + async updateById(id: unknown, field: Partial) { + return await this.fieldModel.updateOne({ _id: id }, { $set: field }); + } +} diff --git a/src/game/game.module.ts b/src/game/game.module.ts index 1650cf7..5156890 100644 --- a/src/game/game.module.ts +++ b/src/game/game.module.ts @@ -7,14 +7,13 @@ import { JwtModule } from '@nestjs/jwt'; import { ConfigModule } from '@nestjs/config'; import { PlayerModule } from 'src/player/player.module'; import { ChatModule } from 'src/chat/chat.module'; -import { EventModule } from 'src/event/event.module'; -import { WebSocketServerModule } from 'src/webSocketServer/webSocketServer.module'; -import { MongooseModule } from '@nestjs/mongoose'; -import { Field, FieldSchema } from 'src/schema/Field.schema'; +import { WebSocketProviderModule } from 'src/webSocketProvider/webSocketProvider.module'; import { GameController } from './game.controller'; import { AuctionModule } from 'src/auction/auction.module'; import { TimerModule } from 'src/timer/timers.module'; import { SecretModule } from 'src/secret/secret.module'; +import { PaymentModule } from 'src/payment/payment.module'; +import { FieldModule } from 'src/field/field.module'; @Module({ controllers: [GameController], @@ -24,17 +23,12 @@ import { SecretModule } from 'src/secret/secret.module'; ConfigModule, ChatModule, forwardRef(() => PlayerModule), - EventModule, - WebSocketServerModule, forwardRef(() => AuctionModule), - MongooseModule.forFeature([ - { - name: Field.name, - schema: FieldSchema, - }, - ]), TimerModule, - SecretModule, + forwardRef(() => SecretModule), + PaymentModule, + FieldModule, + WebSocketProviderModule, ], providers: [GameGateway, GameService, GameRepository], exports: [GameService], diff --git a/src/game/game.service.ts b/src/game/game.service.ts index a93ac62..4f0003a 100644 --- a/src/game/game.service.ts +++ b/src/game/game.service.ts @@ -1,32 +1,48 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; -import { InjectModel } from '@nestjs/mongoose'; import { WsException } from '@nestjs/websockets'; -import { ChatType, Player, Prisma } from '@prisma/client'; -import { Model } from 'mongoose'; +import { ChatType, Prisma } from '@prisma/client'; import { AuctionService } from 'src/auction/auction.service'; -import { EventService } from 'src/event/event.service'; +import { HandlerChain } from 'src/common/handlerChain'; +import { FieldService } from 'src/field/field.service'; +import { FieldAnalyzer } from 'src/field/FieldAnalyzer'; +import { PaymentService } from 'src/payment/payment.service'; +import { PlayerPayload } from 'src/player/player.repository'; import { PlayerService } from 'src/player/player.service'; -import { Field, FieldDocument } from 'src/schema/Field.schema'; +import { FieldDocument } from 'src/schema/Field.schema'; +import { SecretService } from 'src/secret/secret.service'; +import { SecretAnalyzer } from 'src/secret/secretAnalyzer'; import { TimerService } from 'src/timer/timers.service'; import { DEFAULT_FIELDS } from 'src/utils/fields'; -import secretFields from 'src/utils/fields/secretFields'; +import { WebSocketProvider } from 'src/webSocketProvider/webSocketProvider.service'; import { GamePayload, GameRepository } from './game.repository'; -import { SecretService } from 'src/secret/secret.service'; +import { PassTurnHandler } from './handlers/PassTurn.handler'; +import { ProcessSpecialHandler } from './handlers/ProcessSpecial.handler'; +import { PutUpForAuctionHandler } from './handlers/PutUpForAuction.handler'; +import { SteppedOnPrivateHandler } from './handlers/SteppedOnPrivate.handler'; @Injectable() export class GameService { constructor( - private gameRepository: GameRepository, @Inject(forwardRef(() => PlayerService)) private playerService: PlayerService, - private eventService: EventService, + private webSocketProvider: WebSocketProvider, + private gameRepository: GameRepository, @Inject(forwardRef(() => AuctionService)) private auctionService: AuctionService, - @InjectModel(Field.name) - private fieldModel: Model, private timerService: TimerService, - private secretService: SecretService + private fieldService: FieldService, + @Inject(forwardRef(() => SecretService)) + private secretService: SecretService, + private paymentService: PaymentService ) { this.passTurnToUser = this.passTurnToUser.bind(this); + this.rollDice = this.rollDice.bind(this); + this.putUpForAuction = this.putUpForAuction.bind(this); + this.passTurn = this.passTurn.bind(this); + this.processPayingForField = this.processPayingForField.bind(this); + this.transferWithBank = this.transferWithBank.bind(this); + this.payAll = this.payAll.bind(this); + this.payForPrivateField = this.payForPrivateField.bind(this); + this.resolveTwoUsers = this.resolveTwoUsers.bind(this); } readonly PLAYING_FIELDS_QUANTITY = 40; @@ -50,6 +66,14 @@ export class GameService { }); } + async getGameData(gameId: string) { + const game = await this.getGame(gameId); + const auction = this.auctionService.auctions.get(game.id); + const secretInfo = this.secretService.secrets.get(game.id); + const fields = await this.fieldService.getGameFields(game.id); + return { game, auction, secretInfo, fields }; + } + async createGame(userId: string) { const activePlayer = await this.gameRepository.findFirst({ where: { @@ -62,7 +86,7 @@ export class GameService { }, }); - if (activePlayer) return null; + if (activePlayer) throw new WsException('You already have active game'); const newGame = await this.gameRepository.create({ data: { playersCapacity: 4, // TODO change players capacity to dynamic number @@ -99,14 +123,10 @@ export class GameService { gameId: newGame.id, })); - await this.fieldModel.insertMany(gameFields); + await this.fieldService.createMany(gameFields); return newGame; } - async getGameFields(gameId: string) { - return await this.fieldModel.find({ gameId }); - } - async getGame(gameId: string) { return this.gameRepository.findUnique({ where: { id: gameId }, @@ -126,7 +146,7 @@ export class GameService { }); } - async onJoinGame(gameId: string, userId: string) { + async joinGame(gameId: string, userId: string) { const game = await this.gameRepository.findFirst({ where: { id: gameId }, include: { @@ -196,12 +216,25 @@ export class GameService { }, }, }); - return { game: startedGame, shouldStart: true }; + this.startGame(startedGame); + return startedGame; } - return { game: gameWithCreatedPlayer, shouldStart: false }; + return gameWithCreatedPlayer; + } + + startGame(game: Partial) { + // We can setTimeout here for some countdown on frontend + this.webSocketProvider.server.emit('clearStartedGame', { + gameId: game.id, + }); + this.webSocketProvider.server.to(game.id).emit('startGame', { + game, + chatId: game.chat.id, + }); + this.timerService.set(game.id, game.timeOfTurn, game, this.rollDice); } - async onLeaveGame(gameId: string, userId: string) { + async leaveGame(gameId: string, userId: string) { const player = await this.playerService.findFirst({ where: { userId, gameId }, }); @@ -237,19 +270,232 @@ export class GameService { return this.gameRepository.findById(gameId); } - onRollDice() { + getRandomDicesString() { const firstDice = Math.ceil(Math.random() * 6); const secondDice = Math.ceil(Math.random() * 6); return `${firstDice}:${secondDice}`; } + async rollDice(game: Partial) { + if (game.dices) throw new WsException('You have already rolled dices'); + this.timerService.clear(game.id); + const { playerNextField, fields, updatedGame } = await this.makeTurn(game); + await this.processRolledDices(updatedGame, playerNextField, fields); + } + + private async handlePaymentField( + game: Partial, + field: FieldDocument + ) { + this.timerService.set( + game.id, + game.timeOfTurn, + { game, userId: game.turnOfUserId, amount: field.toPay }, + this.transferWithBank + ); + } + + private async handleSecretField(game: Partial) { + const { message, secretInfo } = + await this.secretService.handleSecretWithMessage(game); + + this.webSocketProvider.server.to(game.id).emit('gameChatMessage', message); + this.webSocketProvider.server.to(game.id).emit('secret', secretInfo); + + await this.processSecretByUserCount(game, secretInfo.users.length); + } + + async processSpecialField( + game: Partial, + playerNextField: FieldDocument + ) { + if (playerNextField.toPay) { + await this.handlePaymentField(game, playerNextField); + } + + if (playerNextField.secret) { + await this.handleSecretField(game); + } + } + + async processRolledDices( + game: Partial, + playerNextField?: FieldDocument, + fields?: FieldDocument[] + ) { + if (!playerNextField) { + playerNextField = await this.findCurrentFieldFromGame(game); + } + const fieldAnalyzer = new FieldAnalyzer( + playerNextField, + game, + this.playerService + ); + this.webSocketProvider.server.to(game.id).emit('rolledDice', { + ...(fields !== undefined && { fields }), + game, + }); + const chain = new HandlerChain(); + chain.addHandlers( + new PassTurnHandler(fieldAnalyzer, () => { + this.timerService.set(game.id, 2500, game, this.passTurn); + }), + new ProcessSpecialHandler(fieldAnalyzer, () => { + this.processSpecialField(game, playerNextField); + }), + new SteppedOnPrivateHandler(fieldAnalyzer, () => { + this.steppedOnPrivateField(fieldAnalyzer); + }), + new PutUpForAuctionHandler(fieldAnalyzer, () => { + this.timerService.set( + game.id, + game.timeOfTurn, + game, + this.putUpForAuction + ); + }) + ); + chain.process(); + } + + private async processSecretByUserCount( + game: Partial, + userCount: number + ) { + if (userCount === 1) { + return this.oneUserTransfer(game); + } + + if (userCount === 2) { + return this.twoUsersTransfer(game); + } + + if (userCount > 2) { + return this.multipleUsersTransfer(game); + } + } + + async oneUserTransfer(game: Partial) { + const secretInfo = this.secretService.secrets.get(game.id); + const secretAnalyzer = new SecretAnalyzer(secretInfo, game.turnOfUserId); + if (secretAnalyzer.isOneUserHaveToPay()) { + this.timerService.set( + game.id, + game.timeOfTurn, + { game, userId: game.turnOfUserId, amount: secretInfo.amounts[0] }, + this.transferWithBank + ); + } else { + this.timerService.set( + game.id, + 2000, + { game, userId: game.turnOfUserId, amount: secretInfo.amounts[0] }, + this.transferWithBank + ); + } + } + + private async twoUsersTransfer(game: Partial) { + this.timerService.set(game.id, game.timeOfTurn, game, this.resolveTwoUsers); + } + + private async multipleUsersTransfer(game: Partial) { + this.timerService.set(game.id, game.timeOfTurn, game, this.payAll); + } + + async payAll(game: Partial) { + const updatedGame = await this.secretService.payAllforSecret(game); + this.webSocketProvider.server.to(game.id).emit('updatePlayers', { + game: updatedGame, + }); + this.passTurn(updatedGame); + } + + async resolveTwoUsers(game: Partial) { + const { userId, loseGame, fields, updatedGame } = + await this.secretService.resolveTwoUsers(game); + let gameAfterLoss = null; + if (loseGame) { + gameAfterLoss = await this.loseGame(userId, game.id, fields); + } + this.passTurn(gameAfterLoss || updatedGame); + } + + async transferWithBank(argsObj: { + game: Partial; + amount: number; + userId: string; + }) { + const { updatedGame } = await this.paymentService.transferWithBank( + argsObj.game, + argsObj.userId, + argsObj.amount + ); + const secretInfo = this.secretService.secrets.get(updatedGame.id); + if (!secretInfo) { + await this.passTurn(updatedGame); + } else { + this.webSocketProvider.server.to(updatedGame.id).emit('updatePlayers', { + game: updatedGame, + secretInfo, + }); + } + } + + async payToBankForSpecialField(userId: string, game: Partial) { + const currentField = await this.findCurrentFieldWithUserId(game); + if (!game.dices && !currentField.toPay) + throw new WsException( + 'You cant pay for that field because its not special field' + ); + return this.transferWithBank({ + game: game, + userId, + amount: currentField.toPay, + }); + } + + async steppedOnPrivateField({ currentPlayer, field, game }: FieldAnalyzer) { + const fields = await this.fieldService.getGameFields(game.id); + if ( + this.playerService.estimateAssets(currentPlayer, fields) >= + field.income[field.amountOfBranches] + ) { + this.timerService.set( + game.id, + game.timeOfTurn, + game, + this.payForPrivateField + ); + return; + } + const { updatedPlayer } = await this.loseGame( + currentPlayer.userId, + game.id, + fields + ); + await this.passTurn(updatedPlayer.game); + } + + async putUpForAuction(game: Partial) { + const auction = await this.auctionService.putUpForAuction(game); + const updatedGame = await this.updateGameWithNewTurn(game, 15000); + this.webSocketProvider.server + .to(game.id) + .emit('hasPutUpForAuction', { game: updatedGame, auction }); + this.timerService.set(game.id, 15000, updatedGame, this.passTurn); + } + async findCurrentFieldFromGame(game: Partial) { const player = await this.playerService.findByUserAndGameId( game.turnOfUserId, game.id ); - const fields = await this.getGameFields(game.id); - return this.findPlayerFieldByIndex(fields, player.currentFieldIndex); + const fields = await this.fieldService.getGameFields(game.id); + return this.fieldService.findPlayerFieldByIndex( + fields, + player.currentFieldIndex + ); } async updateGameWithNewTurn( @@ -319,10 +565,6 @@ export class GameService { return resObj; } - findPlayerFieldByIndex(fields: FieldDocument[], indexOfField: number) { - return fields.find((field) => field.index === indexOfField); - } - async decrementPledgedFields(fields: FieldDocument[]) { fields.forEach((field) => { if (field.isPledged && field.turnsToUnpledge === 0) { @@ -332,42 +574,19 @@ export class GameService { field.turnsToUnpledge--; } }); - await this.updateFields(fields, [ + await this.fieldService.updateFields(fields, [ 'isPledged', 'ownedBy', 'turnsToUnpledge', ]); } - async updateFields( - fields: T[], - propertiesToUpdate: string[] - ): Promise { - const updates = fields.map((field) => { - const updateFields: any = {}; - - for (const property of propertiesToUpdate) { - if (field[property] !== undefined) { - updateFields[property] = field[property]; - } - } - - return { - updateOne: { - filter: { _id: field._id }, - update: { $set: updateFields }, - }, - }; - }); - await this.fieldModel.bulkWrite(updates); - } - async makeTurn(game: Partial) { - const dices = this.onRollDice(); + const dices = this.getRandomDicesString(); const currentPlayer = this.playerService.findPlayerWithTurn(game); const dicesArr = this.parseDicesToArr(dices); - const fields = await this.getGameFields(game.id); + const fields = await this.fieldService.getGameFields(game.id); const { nextIndex, shouldGetMoney } = this.calculateNextIndex( currentPlayer.currentFieldIndex, dicesArr, @@ -379,7 +598,10 @@ export class GameService { money: { increment: shouldGetMoney ? game.passStartBonus : 0 }, }); - const playerNextField = this.findPlayerFieldByIndex(fields, nextIndex); + const playerNextField = this.fieldService.findPlayerFieldByIndex( + fields, + nextIndex + ); let updatedGame: null | Partial = null; if (playerNextField.ownedBy !== currentPlayer.userId) { const turnEnds = this.timerService.calculateFutureTime(game.timeOfTurn); @@ -392,36 +614,35 @@ export class GameService { dices, }); } + return { updatedGame, fields, - nextIndex, playerNextField, - hasOwner: playerNextField?.ownedBy, - currentPlayer, }; } - getRandomSecret() { - const randomSecretIndex = Math.floor(Math.random() * secretFields.length); - return secretFields[randomSecretIndex]; - } - - getRandomPlayersUserId(players: Partial) { - const randomIndex = Math.floor(Math.random() * players.length); - return players[randomIndex].userId; - } - async findCurrentFieldWithUserId(game: Partial) { const player = this.playerService.findPlayerWithTurn(game); - const fields = await this.getGameFields(game.id); - return this.findPlayerFieldByIndex(fields, player.currentFieldIndex); + const fields = await this.fieldService.getGameFields(game.id); + return this.fieldService.findPlayerFieldByIndex( + fields, + player.currentFieldIndex + ); } async buyField(game: Partial) { + await this.processBuyField(game); + this.passTurn(game); + } + + async processBuyField(game: Partial) { const player = this.playerService.findPlayerWithTurn(game); - const fields = await this.getGameFields(game.id); - const field = this.findPlayerFieldByIndex(fields, player.currentFieldIndex); + const fields = await this.fieldService.getGameFields(game.id); + const field = this.fieldService.findPlayerFieldByIndex( + fields, + player.currentFieldIndex + ); if (field.ownedBy) { throw new WsException('Field is already owned'); } @@ -435,7 +656,7 @@ export class GameService { } this.timerService.clear(game.id); field.ownedBy = game.turnOfUserId; - await this.updateFields(fields, ['ownedBy']); + await this.fieldService.updateFields(fields, ['ownedBy']); const updatedPlayer = await this.playerService.decrementMoneyWithUserAndGameId( game.turnOfUserId, @@ -449,7 +670,21 @@ export class GameService { return game.players.filter((player) => !player.lost); } - async passTurnToNext(game: Partial) { + async passTurn(game: Partial, fromEvent: boolean = false) { + const fields = await this.fieldService.getGameFields(game.id); + const currentPlayer = this.playerService.findPlayerWithTurn(game); + const currentField = this.fieldService.findPlayerFieldByIndex( + fields, + currentPlayer.currentFieldIndex + ); + if ( + fromEvent && + !currentField.large && + currentField.ownedBy !== game.turnOfUserId + ) { + throw new WsException('You cant pass turn with that field'); + } + if (!game.dices) { throw new WsException('You have to roll dices first'); } @@ -474,10 +709,27 @@ export class GameService { dices, turnEnds, }); - return { updatedGame, turnEnds, turnOfNextUserId, dices }; + this.webSocketProvider.server + .to(game.id) + .emit('passTurnToNext', { game: updatedGame, fields }); + this.timerService.set(game.id, game.timeOfTurn, updatedGame, this.rollDice); } - async payForField( + async payForPrivateField(game: Partial) { + const currentField = await this.findCurrentFieldWithUserId(game); + if (!game.dices || !currentField.ownedBy) + throw new WsException('You cant pay for that field'); + this.timerService.clear(game.id); + const { updatedGame, fields: updatedFields } = + await this.processPayingForField(game, currentField); + this.webSocketProvider.server.to(game.id).emit('payedForField', { + game: updatedGame, + fields: updatedFields, + }); + this.passTurn(updatedGame); + } + + async processPayingForField( game: Partial, playerNextField: FieldDocument ) { @@ -487,9 +739,9 @@ export class GameService { currentPlayer.money < playerNextField.income[playerNextField.amountOfBranches] ) { - const fields = await this.getGameFields(game.id); + const fields = await this.fieldService.getGameFields(game.id); // We can add pledging of last owned field or smt to not make player lose immidiately - const { updatedPlayer } = await this.playerService.loseGame( + const { updatedPlayer } = await this.loseGame( currentPlayer.userId, game.id, fields @@ -511,61 +763,6 @@ export class GameService { return { updatedGame: received.game }; } - async payToBank(game: Partial, userId: string, amount: number) { - const secretInfo = this.secretService.secrets.get(game.id); - if (!secretInfo) this.timerService.clear(game.id); - const currentPlayer = game.players.find( - (player) => player.userId === userId - ); - const fields = await this.getGameFields(game.id); - if (currentPlayer.money < amount) { - // We can add pledging of last owned field or smt to not make player lose immidiately - const { updatedPlayer, updatedFields } = - await this.playerService.loseGame( - currentPlayer.userId, - game.id, - fields - ); - return { updatedGame: updatedPlayer.game, fields: updatedFields }; - } - const playerWhoPayed = - await this.playerService.incrementMoneyWithUserAndGameId( - currentPlayer.userId || game.turnOfUserId, - game.id, - amount - ); - if (secretInfo && secretInfo.users.includes(userId)) { - const userIndex = secretInfo.users.findIndex( - (userId) => userId === playerWhoPayed.userId - ); - secretInfo.users[userIndex] = ''; - } - if ( - secretInfo && - secretInfo.users.every((userId, index) => { - if (secretInfo.users.length === 2 && userId !== '') { - return secretInfo.amounts[index] > 0; - } - if (secretInfo.users.length > 2 && index === 0) { - return true; - } - return userId === ''; - }) - ) { - this.secretService.secrets.delete(game.id); - return { - updatedGame: playerWhoPayed.game, - fields, - playerWhoPayed, - }; - } - return { - updatedGame: playerWhoPayed.game, - fields, - playerWhoPayed, - }; - } - decreaseHouses(gameId: string, quantity: number) { return this.gameRepository.updateById(gameId, { data: { housesQty: { decrement: quantity } }, @@ -660,12 +857,150 @@ export class GameService { turnOfUserId: data.toUserId, turnEnds, }); - if (!data.game.dices) { - this.eventService.emitGameEvent('setRollDiceTimer', updatedGame); + if (!updatedGame.dices) { + this.timerService.set( + updatedGame.id, + updatedGame.timeOfTurn, + updatedGame, + this.rollDice + ); } else { - this.eventService.emitGameEvent('setAfterRolledDiceTimer', updatedGame); + this.processRolledDices(updatedGame); } - return { updatedGame }; } + + async buyBranch(game: Partial, index: number, userId: string) { + const fieldToBuyBranch = + await this.playerService.checkWhetherPlayerHasAllGroup( + game, + index, + userId + ); + this.playerService.checkFieldHasMaxBranches(fieldToBuyBranch); + const playerToPay = game.players.find((player) => player.userId === userId); + if (playerToPay.money < fieldToBuyBranch.branchPrice) { + throw new WsException('You dont have enough money to buy branch'); + } + const player = await this.playerService.decrementMoneyWithUserAndGameId( + userId, + game.id, + fieldToBuyBranch.branchPrice + ); + fieldToBuyBranch.amountOfBranches++; + let updatedGame = null; + if (fieldToBuyBranch.amountOfBranches === 5) { + await this.decreaseHotels(player.game.id, 1); + updatedGame = await this.increaseHouses(player.game.id, 4); + } else { + updatedGame = await this.decreaseHouses(player.game.id, 1); + } + await this.fieldService.updateById(fieldToBuyBranch._id, { + amountOfBranches: fieldToBuyBranch.amountOfBranches, + }); + const fields = await this.fieldService.getGameFields(game.id); + return { updatedGame, fields }; + } + + async sellBranch(game: Partial, index: number, userId: string) { + const fieldToSellBranch = + await this.playerService.checkWhetherPlayerHasAllGroup( + game, + index, + userId, + false + ); + this.playerService.checkFieldHasBranches(fieldToSellBranch); + const player = await this.playerService.incrementMoneyWithUserAndGameId( + userId, + game.id, + fieldToSellBranch.sellBranchPrice + ); + fieldToSellBranch.amountOfBranches--; + let updatedGame = null; + if (fieldToSellBranch.amountOfBranches === 4) { + await this.increaseHotels(player.game.id, 1); + updatedGame = await this.decreaseHouses(player.game.id, 4); + } else { + updatedGame = await this.increaseHouses(player.game.id, 1); + } + await this.fieldService.updateById(fieldToSellBranch._id, { + amountOfBranches: fieldToSellBranch.amountOfBranches, + }); + const fields = await this.fieldService.getGameFields(game.id); + return { updatedGame, fields }; + } + + async loseGame(userId: string, gameId: string, fields?: FieldDocument[]) { + if (!fields) { + fields = await this.fieldService.getGameFields(gameId); + } + this.timerService.clear(gameId); + const updatedPlayer = await this.playerService.updateLostGame( + userId, + gameId + ); + const updatedGame = await this.updateById(gameId, { + dices: 'playerLost', + }); + fields.forEach((field) => { + if (field.ownedBy === updatedPlayer.userId) { + field.ownedBy = null; + field.amountOfBranches = 0; + field.isPledged = false; + field.turnsToUnpledge = null; + } + }); + await this.fieldService.updateFields(fields, [ + 'ownedBy', + 'amountOfBranches', + 'isPledged', + 'turnsToUnpledge', + ]); + if (this.hasWinner(updatedPlayer.game)) { + this.timerService.clear(updatedPlayer.game.id); + const game = await this.updateById(updatedPlayer.game.id, { + status: 'FINISHED', + }); + this.webSocketProvider.server.to(game.id).emit('playerWon', { game }); + return { updatedPlayer, fields }; + } + this.passTurn(updatedGame); + return { updatedPlayer, updatedFields: fields }; + } + + mortgageField(game: Partial, index: number, userId?: string) { + const auction = this.auctionService.auctions.get(game.id); + const secretInfo = this.secretService.secrets.get(game.id); + if (auction || secretInfo) + throw new WsException( + 'You cant pledge field while auction or secret is active' + ); + return this.playerService.pledgeField(game, index, userId); + } + + async payToUserForSecret(game: Partial, userId: string) { + const { + game: updatedGame, + secretInfo, + loseGame, + } = await this.secretService.payToUserForSecret(game, userId); + let gameAfterLoss = null; + if (loseGame) { + gameAfterLoss = await this.loseGame(userId, game.id); + } + return { game: loseGame ? gameAfterLoss : updatedGame, secretInfo }; + } + + async payToBankForSecret(game: Partial, userId: string) { + const amountToPay = await this.secretService.payToBankForSecret( + game, + userId + ); + await this.transferWithBank({ + game: game, + userId, + amount: amountToPay, + }); + } } diff --git a/src/game/test/game.service.ispec.ts b/src/game/test/game.service.ispec.ts index 74df0ab..a2e11a7 100644 --- a/src/game/test/game.service.ispec.ts +++ b/src/game/test/game.service.ispec.ts @@ -1,24 +1,23 @@ -import {GameRepository} from "../game.repository"; -import {GameService} from "../game.service"; -import {PrismaService} from "../../prisma/prisma.service"; -import {UserModule} from "../../user/user.module"; -import {PlayerModule} from "../../player/player.module"; -import {Test} from "@nestjs/testing"; -import {JwtModule} from "@nestjs/jwt"; -import {ConfigModule} from "@nestjs/config"; -import {GameGateway} from "../game.gateway"; -import {WsException} from "@nestjs/websockets"; -import {fields} from "../../utils/fields"; - +import { GameRepository } from '../game.repository'; +import { GameService } from '../game.service'; +import { PrismaService } from '../../prisma/prisma.service'; +import { UserModule } from '../../user/user.module'; +import { PlayerModule } from '../../player/player.module'; +import { Test } from '@nestjs/testing'; +import { JwtModule } from '@nestjs/jwt'; +import { ConfigModule } from '@nestjs/config'; +import { GameGateway } from '../game.gateway'; +import { WsException } from '@nestjs/websockets'; +import { fields } from '../../utils/fields'; describe('GameService', () => { let gameService: GameService; let prismaService: PrismaService; beforeAll(async () => { - const module = await Test.createTestingModule({ + const module = await Test.createTestingModule({ imports: [UserModule, JwtModule, ConfigModule, PlayerModule], - providers: [GameGateway, GameService, GameRepository] + providers: [GameGateway, GameService, GameRepository], }).compile(); gameService = module.get(GameService); @@ -31,14 +30,25 @@ describe('GameService', () => { playersCapacity: 2, }, }); - }) + }); it('should allow a player to join a game and start the game if capacity is reached', async () => { - const game = await prismaService.game.findFirst({}); - const user1 = await prismaService.user.create({ data: { nickname: 'User1', email: 'user1@example.com', hash: 'hashedPassword1' } }); - const user2 = await prismaService.user.create({ data: { nickname: 'User2', email: 'user2@example.com', hash: 'hashedPassword2' } }); + const user1 = await prismaService.user.create({ + data: { + nickname: 'User1', + email: 'user1@example.com', + hash: 'hashedPassword1', + }, + }); + const user2 = await prismaService.user.create({ + data: { + nickname: 'User2', + email: 'user2@example.com', + hash: 'hashedPassword2', + }, + }); await gameService.onJoinGame(game.id, user1.id); const result = await gameService.onJoinGame(game.id, user2.id); @@ -51,8 +61,8 @@ describe('GameService', () => { const game = await prismaService.game.findFirst({}); const user = await prismaService.user.findFirst({ where: { - email: 'user1@example.com' - } + email: 'user1@example.com', + }, }); await expect(gameService.raisePrice(game.id, user.id, 200)).rejects.toThrow( @@ -64,8 +74,8 @@ describe('GameService', () => { const game = await prismaService.game.findFirst({}); const user = await prismaService.user.findFirst({ where: { - email: 'user1@example.com' - } + email: 'user1@example.com', + }, }); await expect(gameService.raisePrice(game.id, user.id, 200)).rejects.toThrow( @@ -77,8 +87,8 @@ describe('GameService', () => { const game = await prismaService.game.findFirst({}); const user = await prismaService.user.findFirst({ where: { - email: 'user1@example.com' - } + email: 'user1@example.com', + }, }); await prismaService.player.updateMany({ @@ -88,8 +98,8 @@ describe('GameService', () => { data: { currentFieldIndex: 2, money: 1000, - } - }) + }, + }); await gameService.putUpForAuction(game); @@ -105,8 +115,8 @@ describe('GameService', () => { const game = await prismaService.game.findFirst({}); const user = await prismaService.user.findFirst({ where: { - email: 'user1@example.com' - } + email: 'user1@example.com', + }, }); await prismaService.player.updateMany({ @@ -115,8 +125,8 @@ describe('GameService', () => { }, data: { currentFieldIndex: 2, - } - }) + }, + }); await prismaService.player.update({ where: { @@ -127,21 +137,23 @@ describe('GameService', () => { }, data: { money: 0, - } - }) + }, + }); await gameService.putUpForAuction(game); const auction = gameService.getAuction(game.id); expect(auction).toBeDefined(); - await expect(gameService.raisePrice(game.id, user.id, 200)).rejects.toThrow(new WsException('Not enough money')); + await expect(gameService.raisePrice(game.id, user.id, 200)).rejects.toThrow( + new WsException('Not enough money') + ); }); it('should throw an error if the raise amount is less than 100', async () => { const game = await prismaService.game.findFirst({}); const user = await prismaService.user.findFirst({ where: { - email: 'user1@example.com' - } + email: 'user1@example.com', + }, }); await prismaService.player.updateMany({ @@ -150,8 +162,8 @@ describe('GameService', () => { }, data: { currentFieldIndex: 2, - } - }) + }, + }); await gameService.putUpForAuction(game); @@ -164,8 +176,8 @@ describe('GameService', () => { const game = await prismaService.game.findFirst({}); const user = await prismaService.user.findFirst({ where: { - email: 'user1@example.com' - } + email: 'user1@example.com', + }, }); await prismaService.player.updateMany({ @@ -174,8 +186,8 @@ describe('GameService', () => { }, data: { currentFieldIndex: 2, - } - }) + }, + }); await prismaService.player.update({ where: { @@ -186,7 +198,7 @@ describe('GameService', () => { }, data: { money: 50, - } + }, }); await gameService.putUpForAuction(game); @@ -204,17 +216,17 @@ describe('GameService', () => { }, data: { currentFieldIndex: 2, - } - }) + }, + }); const user1 = await prismaService.user.findFirst({ where: { - email: 'user1@example.com' - } + email: 'user1@example.com', + }, }); const user2 = await prismaService.user.findFirst({ where: { - email: 'user2@example.com' - } + email: 'user2@example.com', + }, }); await prismaService.player.update({ @@ -226,7 +238,7 @@ describe('GameService', () => { }, data: { money: 5000, - } + }, }); await prismaService.player.update({ @@ -238,7 +250,7 @@ describe('GameService', () => { }, data: { money: 5000, - } + }, }); await gameService.putUpForAuction(game); @@ -253,13 +265,12 @@ describe('GameService', () => { expect(raisedAuction.auctionUpdated.userId).toEqual(user2.id); }); - it('should reset the auction timer after a price raise', async () => { const game = await prismaService.game.findFirst({}); const user = await prismaService.user.findFirst({ where: { - email: 'user1@example.com' - } + email: 'user1@example.com', + }, }); await prismaService.player.update({ @@ -271,7 +282,7 @@ describe('GameService', () => { }, data: { money: 1000, - } + }, }); await gameService.putUpForAuction(game); @@ -286,7 +297,6 @@ describe('GameService', () => { }); describe('buyField tests', () => { - it('should allow a player to buy a field if they have enough money', async () => { const game = await prismaService.game.findFirst({ include: { players: true }, @@ -329,7 +339,10 @@ describe('GameService', () => { data: { money: 5000, currentFieldIndex: 2 }, }); - const field = gameService.findPlayerFieldByIndex(fields, player.currentFieldIndex); + const field = gameService.findPlayerFieldByIndex( + fields, + player.currentFieldIndex + ); field.price = null; await expect(gameService.buyField(game as any)).rejects.toThrow( @@ -361,7 +374,6 @@ describe('GameService', () => { }); describe('makeTurn tests', () => { - it('should successfully make a turn and update the game state', async () => { const game = await prismaService.game.findFirst({ include: { players: true }, @@ -379,7 +391,7 @@ describe('GameService', () => { expect(result.updatedGame.turnEnds).toBeDefined(); }); - it('should update the player\'s field index based on the dice roll', async () => { + it("should update the player's field index based on the dice roll", async () => { const game = await prismaService.game.findFirst({ include: { players: true }, }); @@ -416,7 +428,9 @@ describe('GameService', () => { }); const result = await gameService.makeTurn(game as any); - expect(result.nextIndex).toBeLessThan(gameService.PLAYING_FIELDS_QUANTITY); + expect(result.nextIndex).toBeLessThan( + gameService.PLAYING_FIELDS_QUANTITY + ); }); it('should update the game state correctly after a turn', async () => { @@ -441,11 +455,23 @@ describe('GameService', () => { }); describe('GameService - passTurnToNext', () => { - it('should pass the turn to the next player and reset the auction state', async () => { - - const user1 = await prismaService.user.create({ data: { id: 'user3-id', nickname: 'User3', email: 'user3@example.com', hash: 'hashedPassword1' } }); - const user2 = await prismaService.user.create({ data: { id: 'user4-id', nickname: 'User4', email: 'user4@example.com', hash: 'hashedPassword2' } }); + const user1 = await prismaService.user.create({ + data: { + id: 'user3-id', + nickname: 'User3', + email: 'user3@example.com', + hash: 'hashedPassword1', + }, + }); + const user2 = await prismaService.user.create({ + data: { + id: 'user4-id', + nickname: 'User4', + email: 'user4@example.com', + hash: 'hashedPassword2', + }, + }); const game = await prismaService.game.create({ data: { @@ -493,12 +519,20 @@ describe('GameService', () => { data: { money: 5000, currentFieldIndex: 2 }, }); - const field = { index: 2, ownedBy: player2.userId, incomeWithoutBranches: 200 }; + const field = { + index: 2, + ownedBy: player2.userId, + incomeWithoutBranches: 200, + }; const result = await gameService.payForField(game as any, field as any); - const updatedPlayer1 = await prismaService.player.findUnique({ where: { id: player1.id } }); - const updatedPlayer2 = await prismaService.player.findUnique({ where: { id: player2.id } }); + const updatedPlayer1 = await prismaService.player.findUnique({ + where: { id: player1.id }, + }); + const updatedPlayer2 = await prismaService.player.findUnique({ + where: { id: player2.id }, + }); expect(updatedPlayer1.money).toBe(4800); // 5000 - 200 expect(updatedPlayer2.money).toBe(5200); // 5000 + 200 @@ -515,8 +549,8 @@ describe('GameService', () => { await prismaService.game.update({ where: { id: game.id }, data: { turnOfUserId: player1.userId }, - }) - + }); + await prismaService.player.update({ where: { id: player1.id }, data: { money: 100, currentFieldIndex: 2 }, @@ -525,13 +559,17 @@ describe('GameService', () => { const updatedGame = await prismaService.game.findFirst({ where: { id: game.id }, include: { players: true }, - }) - + }); - const field = { index: 2, ownedBy: player2.userId, incomeWithoutBranches: 200 }; + const field = { + index: 2, + ownedBy: player2.userId, + incomeWithoutBranches: 200, + }; - await expect(gameService.payForField(updatedGame as any, field as any)).rejects.toThrow(WsException); + await expect( + gameService.payForField(updatedGame as any, field as any) + ).rejects.toThrow(WsException); }); - }); -}); \ No newline at end of file +}); diff --git a/src/game/types/secretInfo.type.ts b/src/game/types/secretInfo.type.ts index 857c478..bd0180e 100644 --- a/src/game/types/secretInfo.type.ts +++ b/src/game/types/secretInfo.type.ts @@ -1,6 +1,8 @@ export interface SecretInfo { amounts: number[]; users: string[]; + text: string; + numOfPlayersInvolved: 'one' | 'two' | 'all'; } export type SecretPayments = SecretInfo[]; diff --git a/src/payment/payment.module.ts b/src/payment/payment.module.ts new file mode 100644 index 0000000..891db7e --- /dev/null +++ b/src/payment/payment.module.ts @@ -0,0 +1,20 @@ +import { forwardRef, Module } from '@nestjs/common'; +import { PaymentService } from './payment.service'; +import { SecretModule } from 'src/secret/secret.module'; +import { FieldModule } from 'src/field/field.module'; +import { PlayerModule } from 'src/player/player.module'; +import { TimerModule } from 'src/timer/timers.module'; +import { GameModule } from 'src/game/game.module'; + +@Module({ + imports: [ + forwardRef(() => SecretModule), + FieldModule, + PlayerModule, + TimerModule, + forwardRef(() => GameModule), + ], + providers: [PaymentService], + exports: [PaymentService], +}) +export class PaymentModule {} diff --git a/src/payment/payment.service.ts b/src/payment/payment.service.ts new file mode 100644 index 0000000..e4caede --- /dev/null +++ b/src/payment/payment.service.ts @@ -0,0 +1,78 @@ +import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { FieldService } from 'src/field/field.service'; +import { GamePayload } from 'src/game/game.repository'; +import { GameService } from 'src/game/game.service'; +import { PlayerService } from 'src/player/player.service'; +import { SecretService } from 'src/secret/secret.service'; +import { TimerService } from 'src/timer/timers.service'; + +@Injectable() +export class PaymentService { + constructor( + @Inject(forwardRef(() => SecretService)) + private secretService: SecretService, + private playerService: PlayerService, + private timerService: TimerService, + private fieldService: FieldService, + @Inject(forwardRef(() => GameService)) + private gameService: GameService + ) {} + + async transferWithBank( + game: Partial, + userId: string, + amount: number + ) { + const secretInfo = this.secretService.secrets.get(game.id); + if (!secretInfo) this.timerService.clear(game.id); + const currentPlayer = game.players.find( + (player) => player.userId === userId + ); + const fields = await this.fieldService.getGameFields(game.id); + if (currentPlayer.money < amount) { + // We can add pledging of last owned field or smt to not make player lose immidiately + const { updatedPlayer, updatedFields } = await this.gameService.loseGame( + currentPlayer.userId, + game.id, + fields + ); + return { updatedGame: updatedPlayer.game, fields: updatedFields }; + } + const playerWhoPayed = + await this.playerService.incrementMoneyWithUserAndGameId( + currentPlayer.userId || game.turnOfUserId, + game.id, + amount + ); + if (secretInfo && secretInfo.users.includes(userId)) { + const userIndex = secretInfo.users.findIndex( + (userId) => userId === playerWhoPayed.userId + ); + secretInfo.users[userIndex] = ''; + } + if ( + secretInfo && + secretInfo.users.every((userId, index) => { + if (secretInfo.users.length === 2 && userId !== '') { + return secretInfo.amounts[index] > 0; + } + if (secretInfo.users.length > 2 && index === 0) { + return true; + } + return userId === ''; + }) + ) { + this.secretService.secrets.delete(game.id); + return { + updatedGame: playerWhoPayed.game, + fields, + playerWhoPayed, + }; + } + return { + updatedGame: playerWhoPayed.game, + fields, + playerWhoPayed, + }; + } +} diff --git a/src/player/player.gateway.ts b/src/player/player.gateway.ts index 7d1ee69..5f6a0c6 100644 --- a/src/player/player.gateway.ts +++ b/src/player/player.gateway.ts @@ -1,35 +1,19 @@ -import { - forwardRef, - Inject, - UseFilters, - UseGuards, - UsePipes, -} from '@nestjs/common'; +import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { ConnectedSocket, MessageBody, - OnGatewayInit, SubscribeMessage, WebSocketGateway, WebSocketServer, - WsException, } from '@nestjs/websockets'; import { Server, Socket } from 'socket.io'; -import { HasLostGuard, TurnGuard } from 'src/auth/guard'; -import { ActiveGameGuard } from 'src/auth/guard/activeGame.guard'; +import { TurnGuard, ValidPlayerGuard } from 'src/auth/guard'; import { WsGuard } from 'src/auth/guard/jwt.ws.guard'; -import { JwtPayload } from 'src/auth/types/jwtPayloadType.type'; -import { ChatService } from 'src/chat/chat.service'; -import { EventService } from 'src/event/event.service'; import { GamePayload } from 'src/game/game.repository'; -import { GameService } from 'src/game/game.service'; -import { Trade } from 'src/game/types/trade.type'; import { WsValidationPipe } from 'src/pipes/wsValidation.pipe'; import { WebsocketExceptionsFilter } from 'src/utils/exceptions/websocket-exceptions.filter'; -import { WebSocketServerService } from 'src/webSocketServer/webSocketServer.service'; -import { OfferTradeDto } from './dto/offer-trade.dto'; import { PlayerService } from './player.service'; -import { AuctionService } from 'src/auction/auction.service'; +import { JwtPayload } from 'src/auth/types/jwtPayloadType.type'; @WebSocketGateway({ cors: { @@ -43,202 +27,31 @@ import { AuctionService } from 'src/auction/auction.service'; }) @UseFilters(WebsocketExceptionsFilter) @UsePipes(new WsValidationPipe()) -@UseGuards(WsGuard, ActiveGameGuard, HasLostGuard) -export class PlayerGateway implements OnGatewayInit { - constructor( - private readonly playerService: PlayerService, - private eventService: EventService, - private webSocketServerService: WebSocketServerService, - @Inject(forwardRef(() => GameService)) - private readonly gameService: GameService, - private chatService: ChatService, - @Inject(forwardRef(() => AuctionService)) - private auctionService: AuctionService - ) {} +@UseGuards(WsGuard, ValidPlayerGuard) +export class PlayerGateway { + constructor(private readonly playerService: PlayerService) {} @WebSocketServer() private server: Server; - afterInit(server: Server) { - this.webSocketServerService.setServer(server); - } - @SubscribeMessage('buyBranch') - async buyBranch( - @ConnectedSocket() - socket: Socket & { game: Partial; jwtPayload: JwtPayload }, - @MessageBody('index') index: number - ) { - const game = socket.game; - const userId = socket.jwtPayload.sub; - const fieldToBuyBranch = - await this.playerService.checkWhetherPlayerHasAllGroup( - game, - index, - userId - ); - this.playerService.checkFieldHasMaxBranches(fieldToBuyBranch); - const updatedGame = await this.playerService.buyBranch( - game, - fieldToBuyBranch, - socket.jwtPayload.sub - ); - const fields = await this.gameService.getGameFields(game.id); - this.server - .to(game.id) - .emit('updateGameData', { fields, game: updatedGame }); - } - - @SubscribeMessage('sellBranch') - async sellBranch( + @UseGuards(TurnGuard, ValidPlayerGuard) + @SubscribeMessage('unmortgageField') + async onUnmortgageField( @ConnectedSocket() socket: Socket & { game: Partial; jwtPayload: JwtPayload }, - @MessageBody('index') index: number + @MessageBody() dataArray: [{ index: number }, null] ) { + const data = dataArray[0]; + const { index } = data; const game = socket.game; const userId = socket.jwtPayload.sub; - const fields = await this.gameService.getGameFields(game.id); - const fieldToSellBranch = - await this.playerService.checkWhetherPlayerHasAllGroup( - game, - index, - userId, - false - ); - this.playerService.checkFieldHasBranches(fieldToSellBranch); - const updatedGame = await this.playerService.sellBranch( + const { player, fields } = await this.playerService.unmortgageField( game, - fieldToSellBranch, - socket.jwtPayload.sub - ); - this.server - .to(game.id) - .emit('updateGameData', { fields, game: updatedGame }); - } - - @SubscribeMessage('pledgeField') - async pledgeField( - @ConnectedSocket() - socket: Socket & { game: Partial }, - @MessageBody('index') index: number - ) { - const game = socket.game; - const { player, fields } = await this.playerService.pledgeField( - game, - index - ); - this.server - .to(game.id) - .emit('updateGameData', { fields, game: player.game }); - } - - @UseGuards(TurnGuard) - @SubscribeMessage('payRedemptionForField') - async payRedemptionForField( - @ConnectedSocket() - socket: Socket & { game: Partial }, - @MessageBody('index') index: number - ) { - const game = socket.game; - const { player, fields } = await this.playerService.payRedemptionForField( - game, - index + index, + userId ); this.server.to(game.id).emit('updateGameData', { fields, game: player.game, }); } - - @UseGuards(TurnGuard) - @SubscribeMessage('offerTrade') - async offerTrade( - @ConnectedSocket() - socket: Socket & { game: Partial; jwtPayload: JwtPayload }, - @MessageBody() - data: OfferTradeDto - ) { - const game = socket.game; - const userId = socket.jwtPayload.sub; - if (this.auctionService.getAuction(game.id)) - throw new WsException('Cannot offer trade while auction'); - await this.playerService.validateTradeData(game, data); - const trade = { ...data, fromUserId: userId } as Trade; - this.playerService.setTrade(game.id, trade); - const fromPlayer = game.players.find( - (player) => player.userId === trade.fromUserId - ); - const toPlayer = game.players.find( - (player) => player.userId === trade.toUserId - ); - const message = await this.chatService.onNewMessage(game.turnOfUserId, { - text: `${fromPlayer.user.nickname} запропонував ${toPlayer.user.nickname} угоду!`, - chatId: game.chat.id, - }); - this.server.to(game.id).emit('gameChatMessage', message); - this.eventService.emitGameEvent('offerTrade', { game, trade }); - } - - @SubscribeMessage('refuseFromTrade') - async refuseFromTrade( - @ConnectedSocket() - socket: Socket & { game: Partial; jwtPayload: JwtPayload } - ) { - const game = socket.game; - this.playerService.refuseFromTrade(game); - } - - @SubscribeMessage('acceptTrade') - async acceptTrade( - @ConnectedSocket() - socket: Socket & { game: Partial; jwtPayload: JwtPayload } - ) { - const game = socket.game; - const trade = this.playerService.getTrade(game.id); - const { updatedGame, fields } = await this.playerService.acceptTrade( - game, - trade, - socket.jwtPayload.sub - ); - const data = { fields }; - - const { updatedGame: secondTimeUpdatedGame } = - await this.gameService.passTurnToUser({ - game: updatedGame, - toUserId: trade.fromUserId, - }); - if (secondTimeUpdatedGame) { - data['game'] = secondTimeUpdatedGame; - } - const fromPlayer = game.players.find( - (player) => player.userId === trade.fromUserId - ); - const toPlayer = game.players.find( - (player) => player.userId === trade.toUserId - ); - const message = await this.chatService.onNewMessage(game.turnOfUserId, { - text: `Угода між ${fromPlayer.user.nickname} та ${toPlayer.user.nickname} підписана!`, - chatId: game.chat.id, - }); - this.server.to(game.id).emit('gameChatMessage', message); - this.server.to(game.id).emit('updateGameData', data); - } - - @SubscribeMessage('surrender') - async surrender( - @ConnectedSocket() - socket: Socket & { game: Partial; jwtPayload: JwtPayload } - ) { - const userId = socket.jwtPayload.sub; - const gameId = socket.game.id; - const fields = await this.gameService.getGameFields(socket.game.id); - const { updatedPlayer, updatedFields } = await this.playerService.loseGame( - userId, - gameId, - fields - ); - - this.server.to(gameId).emit('playerSurrendered', { - game: updatedPlayer.game, - fields: updatedFields, - }); - } } diff --git a/src/player/player.module.ts b/src/player/player.module.ts index db78718..13f3f7e 100644 --- a/src/player/player.module.ts +++ b/src/player/player.module.ts @@ -1,36 +1,12 @@ import { forwardRef, Module } from '@nestjs/common'; -import { PlayerService } from './player.service'; +import { FieldModule } from 'src/field/field.module'; import { PlayerGateway } from './player.gateway'; import { PlayerRepository } from './player.repository'; -import { UserModule } from 'src/user/user.module'; +import { PlayerService } from './player.service'; import { JwtModule } from '@nestjs/jwt'; import { ConfigModule } from '@nestjs/config'; -import { GameModule } from 'src/game/game.module'; -import { EventModule } from 'src/event/event.module'; -import { WebSocketServerModule } from 'src/webSocketServer/webSocketServer.module'; -import { ChatModule } from 'src/chat/chat.module'; -import { MongooseModule } from '@nestjs/mongoose'; -import { Field, FieldSchema } from 'src/schema/Field.schema'; -import { AuctionModule } from 'src/auction/auction.module'; -import { TimerModule } from 'src/timer/timers.module'; @Module({ - imports: [ - forwardRef(() => GameModule), - UserModule, - JwtModule, - ConfigModule, - EventModule, - WebSocketServerModule, - ChatModule, - forwardRef(() => AuctionModule), - MongooseModule.forFeature([ - { - name: Field.name, - schema: FieldSchema, - }, - ]), - TimerModule, - ], + imports: [FieldModule, JwtModule, ConfigModule], providers: [PlayerGateway, PlayerService, PlayerRepository], exports: [PlayerService], }) diff --git a/src/player/player.service.ts b/src/player/player.service.ts index 61ca403..c9a4c4d 100644 --- a/src/player/player.service.ts +++ b/src/player/player.service.ts @@ -1,35 +1,19 @@ -import { forwardRef, Inject, Injectable } from '@nestjs/common'; -import { CreatePlayerDto } from './dto/create-player.dto'; -import { PlayerPayload, PlayerRepository } from './player.repository'; +import { Injectable } from '@nestjs/common'; +import { WsException } from '@nestjs/websockets'; import { Prisma } from '@prisma/client'; +import { FieldService } from 'src/field/field.service'; import { GamePayload } from 'src/game/game.repository'; -import { WsException } from '@nestjs/websockets'; -import { OfferTradeDto } from './dto/offer-trade.dto'; -import { Trade } from 'src/game/types/trade.type'; -import { GameService } from 'src/game/game.service'; -import { WebSocketServerService } from 'src/webSocketServer/webSocketServer.service'; -import { ChatService } from 'src/chat/chat.service'; -import { EventService } from 'src/event/event.service'; -import { Field, FieldDocument } from 'src/schema/Field.schema'; -import { InjectModel } from '@nestjs/mongoose'; -import { Model } from 'mongoose'; -import { TimerService } from 'src/timer/timers.service'; +import { FieldDocument } from 'src/schema/Field.schema'; +import { CreatePlayerDto } from './dto/create-player.dto'; +import { PlayerPayload, PlayerRepository } from './player.repository'; @Injectable() export class PlayerService { constructor( - @Inject(forwardRef(() => GameService)) - private readonly gameService: GameService, private playerRepository: PlayerRepository, - private webSocketServerService: WebSocketServerService, - private chatService: ChatService, - private eventService: EventService, - @InjectModel(Field.name) private fieldModel: Model, - private timerService: TimerService - ) { - this.refuseFromTrade = this.refuseFromTrade.bind(this); - } - trades: Map = new Map(); + private fieldService: FieldService + ) {} + readonly COLORS = ['blue', 'yellow', 'green', 'purple', 'red']; create(createPlayerDto: CreatePlayerDto) { return this.playerRepository.create({ @@ -184,10 +168,6 @@ export class PlayerService { return player.money + potentialAmountToPledge; } - findPlayerFieldByIndex(fields: FieldDocument[], indexOfField: number) { - return fields.find((field) => field.index === indexOfField); - } - findPlayerWithTurn(game: Partial) { const player = game.players.find( (player) => player.userId === game.turnOfUserId @@ -195,19 +175,27 @@ export class PlayerService { return player; } + findPlayerWithUserId(game: Partial, userId: string) { + const player = game.players.find((player) => player.userId === userId); + return player; + } + async checkWhetherPlayerHasAllGroup( game: Partial, index: number, userId?: string, buying: boolean = true ) { - const fields = await this.gameService.getGameFields(game.id); + const fields = await this.fieldService.getGameFields(game.id); const playerUserId = userId ? userId : game.turnOfUserId; const userFields = fields.filter((field) => field.ownedBy === playerUserId); const userFieldsIndexes = userFields.map((field) => field.index); if (!userFieldsIndexes.includes(index)) throw new WsException('You dont have this field'); - const fieldToBuyBranch = this.findPlayerFieldByIndex(fields, index); + const fieldToBuyBranch = this.fieldService.findPlayerFieldByIndex( + fields, + index + ); const groupFields = fields.filter( (f) => f.group === fieldToBuyBranch.group ); @@ -271,68 +259,17 @@ export class PlayerService { } } - async buyBranch( - game: Partial, - fieldToBuyBranch: FieldDocument, - userId: string - ) { - const playerToPay = game.players.find((player) => player.userId === userId); - if (playerToPay.money < fieldToBuyBranch.branchPrice) { - throw new WsException('You dont have enough money to buy branch'); - } - const player = await this.decrementMoneyWithUserAndGameId( - userId, - game.id, - fieldToBuyBranch.branchPrice - ); - fieldToBuyBranch.amountOfBranches++; - let updatedGame = null; - if (fieldToBuyBranch.amountOfBranches === 5) { - await this.gameService.decreaseHotels(player.game.id, 1); - updatedGame = await this.gameService.increaseHouses(player.game.id, 4); - } else { - updatedGame = await this.gameService.decreaseHouses(player.game.id, 1); - } - await this.fieldModel.updateOne( - { _id: fieldToBuyBranch._id }, - { $set: { amountOfBranches: fieldToBuyBranch.amountOfBranches } } - ); - return updatedGame; - } - - async sellBranch( - game: Partial, - fieldToBuyBranch: FieldDocument, - userId: string - ) { - const player = await this.incrementMoneyWithUserAndGameId( - userId, - game.id, - fieldToBuyBranch.sellBranchPrice - ); - fieldToBuyBranch.amountOfBranches--; - let updatedGame = null; - if (fieldToBuyBranch.amountOfBranches === 4) { - await this.gameService.increaseHotels(player.game.id, 1); - updatedGame = await this.gameService.decreaseHouses(player.game.id, 4); - } else { - updatedGame = await this.gameService.increaseHouses(player.game.id, 1); - } - await this.fieldModel.updateOne( - { _id: fieldToBuyBranch._id }, - { $set: { amountOfBranches: fieldToBuyBranch.amountOfBranches } } - ); - return updatedGame; - } - async pledgeField( game: Partial, index: number, userId?: string ) { - const fields = await this.gameService.getGameFields(game.id); + const fields = await this.fieldService.getGameFields(game.id); const playerUserId = userId ? userId : game.turnOfUserId; - const fieldToPledge = this.findPlayerFieldByIndex(fields, index); + const fieldToPledge = this.fieldService.findPlayerFieldByIndex( + fields, + index + ); if (fieldToPledge.isPledged) { throw new WsException('Field is already pledged'); } @@ -348,159 +285,43 @@ export class PlayerService { ); fieldToPledge.isPledged = true; fieldToPledge.turnsToUnpledge = game.turnsToUnpledge; - await this.fieldModel.updateOne( - { _id: fieldToPledge._id }, - { $set: { isPledged: true, turnsToUnpledge: game.turnsToUnpledge } } - ); + await this.fieldService.updateById(fieldToPledge._id, { + isPledged: true, + turnsToUnpledge: game.turnsToUnpledge, + }); return { player, fields }; } - async payRedemptionForField( + async unmortgageField( game: Partial, index: number, userId?: string ) { const playerUserId = userId ? userId : game.turnOfUserId; - const fields = await this.gameService.getGameFields(game.id); - const fieldToPayRedemption = this.findPlayerFieldByIndex(fields, index); - if (!fieldToPayRedemption.isPledged) { + const fields = await this.fieldService.getGameFields(game.id); + const fieldToUnmortgage = this.fieldService.findPlayerFieldByIndex( + fields, + index + ); + if (!fieldToUnmortgage.isPledged) { throw new WsException('Field is not pledged'); } - fieldToPayRedemption.isPledged = false; - fieldToPayRedemption.turnsToUnpledge = null; - await this.fieldModel.updateOne( - { _id: fieldToPayRedemption._id }, - { $set: { isPledged: false, turnsToUnpledge: null } } - ); + fieldToUnmortgage.isPledged = false; + fieldToUnmortgage.turnsToUnpledge = null; + await this.fieldService.updateById(fieldToUnmortgage._id, { + isPledged: false, + turnsToUnpledge: null, + }); const player = await this.decrementMoneyWithUserAndGameId( playerUserId, game.id, - fieldToPayRedemption.redemptionPrice + fieldToUnmortgage.redemptionPrice ); return { player, fields }; } - async validateTradeData(game: Partial, data: OfferTradeDto) { - if ( - data.offerFieldsIndexes.length === 0 && - data.wantedFieldsIndexes.length === 0 && - data.offeredMoney <= 0 && - data.wantedMoney <= 0 - ) { - throw new WsException('You must offer something'); - } - const fields = await this.gameService.getGameFields(game.id); - if (data.offerFieldsIndexes.length > 0) { - const userFields = fields.filter( - (field) => field.ownedBy === game.turnOfUserId - ); - const userFieldsIndexes = userFields.map((field) => field.index); - const hasAllOfferFields = data.offerFieldsIndexes.every((index) => - userFieldsIndexes.includes(index) - ); - if (!hasAllOfferFields) { - throw new WsException('You dont have all offer fields'); - } - } - if (data.wantedFieldsIndexes.length > 0) { - const otherUserFields = fields.filter( - (field) => field.ownedBy === data.toUserId - ); - const otherUserFieldsIndexes = otherUserFields.map( - (field) => field.index - ); - const hasAllWantedFields = data.wantedFieldsIndexes.every((index) => - otherUserFieldsIndexes.includes(index) - ); - if (!hasAllWantedFields) { - throw new WsException('Other player doesnt have all wanted fields'); - } - } - } - setTrade(gameId: string, trade: Trade) { - this.trades.set(gameId, trade); - } - getTrade(gameId: string) { - return this.trades.get(gameId); - } - async acceptTrade(game: Partial, trade: Trade, userId: string) { - if (!trade) throw new WsException('There is no trade to accept'); - if (trade.toUserId !== userId) - throw new WsException('You cant accept this trade'); - this.timerService.clear(game.id); - const fields = await this.gameService.getGameFields(game.id); - if (trade.offerFieldsIndexes.length > 0) { - trade.offerFieldsIndexes.forEach((index) => { - const field = this.findPlayerFieldByIndex(fields, index); - field.ownedBy = trade.toUserId; - }); - } - if (trade.wantedFieldsIndexes.length > 0) { - trade.wantedFieldsIndexes.forEach((index) => { - const field = this.findPlayerFieldByIndex(fields, index); - field.ownedBy = trade.fromUserId; - }); - } - if (trade.offerFieldsIndexes.length || trade.wantedFieldsIndexes) { - await this.gameService.updateFields(fields, ['ownedBy']); - } - let player = null; - if (trade.offeredMoney) { - this.decrementMoneyWithUserAndGameId( - trade.fromUserId, - game.id, - trade.offeredMoney - ); - player = await this.incrementMoneyWithUserAndGameId( - trade.toUserId, - game.id, - trade.offeredMoney - ); - } - if (trade.wantedMoney) { - this.decrementMoneyWithUserAndGameId( - trade.toUserId, - game.id, - trade.wantedMoney - ); - player = await this.incrementMoneyWithUserAndGameId( - trade.fromUserId, - game.id, - trade.wantedMoney - ); - } - this.setTrade(game.id, null); - return { fields, updatedGame: player?.game ? player.game : game }; - } - - async refuseFromTrade(game: Partial) { - const trade = this.getTrade(game.id); - if (!trade) throw new WsException('There is no trade to refuse'); - this.setTrade(game.id, null); - - const { updatedGame } = await this.gameService.passTurnToUser({ - game, - toUserId: trade.fromUserId, - turnTime: game.timeOfTurn, - }); - this.webSocketServerService.server - .to(game.id) - .emit('updateGameData', { game: updatedGame }); - const toPlayer = game.players.find( - (player) => player.userId === trade.toUserId - ); - const message = await this.chatService.onNewMessage(game.turnOfUserId, { - text: `${toPlayer.user.nickname} відхилив угоду!`, - chatId: game.chat.id, - }); - this.webSocketServerService.server - .to(game.id) - .emit('gameChatMessage', message); - } - - async loseGame(userId: string, gameId: string, fields: FieldDocument[]) { - this.timerService.clear(gameId); - const updatedPlayer = await this.update({ + async updateLostGame(userId: string, gameId: string) { + return this.update({ where: { userId_gameId: { userId: userId, @@ -523,36 +344,15 @@ export class PlayerService { }, }, }); - const updatedGame = await this.gameService.updateById(gameId, { - dices: 'playerLost', - }); - fields.forEach((field) => { - if (field.ownedBy === updatedPlayer.userId) { - field.ownedBy = null; - field.amountOfBranches = 0; - field.isPledged = false; - field.turnsToUnpledge = null; - } - }); - await this.gameService.updateFields(fields, ['ownedBy']); - if (this.gameService.hasWinner(updatedPlayer.game)) { - this.timerService.clear(updatedPlayer.game.id); - const game = await this.gameService.updateById(updatedPlayer.game.id, { - status: 'FINISHED', - }); - this.webSocketServerService.server - .to(game.id) - .emit('playerWon', { game }); - return { updatedPlayer, fields }; - } - this.eventService.emitGameEvent('passTurnToNext', { - game: updatedGame, - }); - return { updatedPlayer, updatedFields: fields }; } validatePlayerMoney(player: Partial, moneyNeeded: number) { if (!player) throw new WsException('No such player'); if (player.money <= moneyNeeded) throw new WsException('Not enough money'); } + + choseRandomPlayer(players: Partial) { + const randomIndex = Math.floor(Math.random() * players.length); + return players[randomIndex]; + } } diff --git a/src/secret/secret.module.ts b/src/secret/secret.module.ts index cdf57b5..cee860b 100644 --- a/src/secret/secret.module.ts +++ b/src/secret/secret.module.ts @@ -1,7 +1,19 @@ -import { Module } from '@nestjs/common'; +import { forwardRef, Module } from '@nestjs/common'; import { SecretService } from './secret.service'; +import { GameModule } from 'src/game/game.module'; +import { PlayerModule } from 'src/player/player.module'; +import { ChatModule } from 'src/chat/chat.module'; +import { FieldModule } from 'src/field/field.module'; +import { PaymentModule } from 'src/payment/payment.module'; @Module({ + imports: [ + forwardRef(() => GameModule), + PlayerModule, + ChatModule, + FieldModule, + forwardRef(() => PaymentModule), + ], providers: [SecretService], exports: [SecretService], }) diff --git a/src/secret/secret.service.ts b/src/secret/secret.service.ts index 094a9b6..65ecd4a 100644 --- a/src/secret/secret.service.ts +++ b/src/secret/secret.service.ts @@ -1,22 +1,52 @@ -import { Injectable } from '@nestjs/common'; +import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { WsException } from '@nestjs/websockets'; import { Player } from '@prisma/client'; +import { ChatService } from 'src/chat/chat.service'; +import { FieldService } from 'src/field/field.service'; import { GamePayload } from 'src/game/game.repository'; +import { GameService } from 'src/game/game.service'; +import { TwoPlayersInvolvedHandler } from 'src/game/handlers/twoPlayersInvolved.handler'; import { SecretInfo } from 'src/game/types/secretInfo.type'; -import { SecretType } from 'src/utils/fields/secretFields'; +import { PaymentService } from 'src/payment/payment.service'; +import { PlayerService } from 'src/player/player.service'; +import secretFields, { SecretType } from 'src/utils/fields/secretFields'; +import { SecretAnalyzer } from './secretAnalyzer'; +import { HandlerChain } from 'src/common/handlerChain'; +import { OnePlayerInvolvedHandler } from 'src/game/handlers/onePlayerInvolved.handler'; +import { AllPlayersInvolvedHandler } from 'src/game/handlers/allPlayersInvolved.handler'; @Injectable() export class SecretService { readonly secrets: Map = new Map(); - constructor() {} + constructor( + private chatService: ChatService, + private fieldService: FieldService, + private playerService: PlayerService, + @Inject(forwardRef(() => PaymentService)) + private paymentService: PaymentService + ) {} getRandomPlayersUserId(players: Partial) { const randomIndex = Math.floor(Math.random() * players.length); return players[randomIndex].userId; } + choseRandomSecret() { + const randomSecretIndex = Math.floor(Math.random() * secretFields.length); + return secretFields[randomSecretIndex]; + } + + findIndexOfUserIdInSecretInfo(secretInfo: SecretInfo, userId: string) { + return secretInfo.users.findIndex( + (userIdInSecret) => userIdInSecret === userId + ); + } + async parseAndSaveSecret(secret: SecretType, game: Partial) { if (secret.numOfPlayersInvolved === 'one') { const secretInfo = { amounts: secret.amounts, users: [game.turnOfUserId], + text: secret.text, + numOfPlayersInvolved: secret.numOfPlayersInvolved as 'one', }; this.secrets.set(game.id, secretInfo); return secretInfo; @@ -27,7 +57,9 @@ export class SecretService { const randomUserId = this.getRandomPlayersUserId(playersWithoutActive); const secretInfo = { amounts: secret.amounts, - users: [game.turnOfUserId, randomUserId], + users: [randomUserId, game.turnOfUserId], + text: secret.text, + numOfPlayersInvolved: secret.numOfPlayersInvolved as 'two', }; this.secrets.set(game.id, secretInfo); return secretInfo; @@ -45,9 +77,183 @@ export class SecretService { return ''; }), ], + text: secret.text, + numOfPlayersInvolved: secret.numOfPlayersInvolved as 'all', }; this.secrets.set(game.id, secretInfo); return secretInfo; } } + + async payToUserForSecret(game: Partial, userId: string) { + let secretInfo = this.secrets.get(game.id); + if (!secretInfo.users.includes(userId)) + throw new WsException('You cant pay for that secret'); + const amount = secretInfo.amounts[1]; + if (amount > 0) + throw new WsException('You dont have to pay for this secret field'); + const userToGetId = secretInfo.users[0]; + const indexOfUser = secretInfo.users.indexOf(userId); + const player = game.players.find((player) => player.userId === userId); + const fields = await this.fieldService.getGameFields(game.id); + let updatedPlayer = null; + let loseGame = false; + if (player.money < amount) { + loseGame = true; + updatedPlayer = await this.playerService.incrementMoneyWithUserAndGameId( + userToGetId, + game.id, + this.playerService.estimateAssets(player, fields) + ); + } else { + updatedPlayer = await this.playerService.incrementMoneyWithUserAndGameId( + userToGetId, + game.id, + amount + ); + await this.playerService.decrementMoneyWithUserAndGameId( + userId, + game.id, + amount + ); + } + secretInfo.users.splice(indexOfUser, 1, ''); + if ( + secretInfo.users.every((userId, index) => { + if (secretInfo.amounts[index] > 0) return true; + return userId === ''; + }) + ) { + secretInfo = null; + } + + return { game: updatedPlayer.game, secretInfo, loseGame }; + } + + async payAllforSecret(game: Partial) { + const secretInfo = this.secrets.get(game.id); + let updatedPlayer = null; + for (const userId of secretInfo.users) { + const firstUser = secretInfo.users[0]; + if (userId && userId !== firstUser) { + if (secretInfo.amounts.length === 2) { + updatedPlayer = await this.payToUserForSecret(game, userId); + } + + if (secretInfo.amounts.length === 1) { + const { playerWhoPayed } = await this.paymentService.transferWithBank( + game, + userId, + secretInfo.amounts[0] + ); + updatedPlayer = playerWhoPayed; + } + } + } + this.secrets.delete(game.id); + return updatedPlayer.game || game; + } + + async handleSecretWithMessage(game: Partial) { + const secret = this.choseRandomSecret(); + const secretInfo = await this.parseAndSaveSecret(secret, game); + if (secretInfo.text.includes('$RANDOM_PLAYER$')) { + const randomPlayer = this.playerService.choseRandomPlayer(game.players); + secretInfo.text = secretInfo.text.replace( + '$RANDOM_PLAYER$', + randomPlayer?.user.nickname + ); + } + const message = await this.chatService.onNewMessage(game.turnOfUserId, { + text: secretInfo.text, + chatId: game.chat.id, + }); + return { message, secretInfo }; + } + + async resolveTwoUsers(game: Partial) { + let secretInfo = this.secrets.get(game.id); + const firstPay = secretInfo.amounts[0] < 1; + let updatedGameToReturn: null | Partial = null; + const fields = await this.fieldService.getGameFields(game.id); + if (firstPay) { + const userId = secretInfo.users[0]; + if (userId) { + const player = game.players.find((player) => player.userId === userId); + if ( + this.playerService.estimateAssets(player, fields) < + secretInfo.amounts[0] + ) { + return { loseGame: true, userId: player.userId, fields }; + } + const { updatedGame } = await this.paymentService.transferWithBank( + game, + userId, + secretInfo.amounts[0] + ); + updatedGameToReturn = updatedGame; + } + if (secretInfo.users[1]) { + const { updatedGame } = await this.paymentService.transferWithBank( + game, + secretInfo.users[1], + secretInfo.amounts[1] + ); + updatedGameToReturn = updatedGame; + } + } else { + const userId = secretInfo.users[1]; + if (userId) { + const player = game.players.find((player) => player.userId === userId); + if ( + this.playerService.estimateAssets(player, fields) < + secretInfo.amounts[1] + ) { + return { loseGame: true, userId: player.userId, fields }; + } + const { updatedGame } = await this.paymentService.transferWithBank( + game, + userId, + secretInfo.amounts[1] + ); + updatedGameToReturn = updatedGame; + } + if (secretInfo.users[0]) { + const { updatedGame } = await this.paymentService.transferWithBank( + game, + secretInfo.users[0], + secretInfo.amounts[0] + ); + updatedGameToReturn = updatedGame; + } + } + secretInfo = null; + return { + loseGame: false, + fields, + updatedGame: updatedGameToReturn, + }; + } + + async payToBankForSecret(game: Partial, userId: string) { + const secretInfo = this.secrets.get(game.id); + if (!secretInfo) throw new WsException('No secret found'); + if (!secretInfo.users.includes(userId)) { + throw new WsException( + 'You cant pay to bank because no user in secretInfo' + ); + } + const secretAnalyzer = new SecretAnalyzer(secretInfo, userId); + const chain = new HandlerChain(); + chain.addHandlers( + new OnePlayerInvolvedHandler(secretAnalyzer), + new TwoPlayersInvolvedHandler(secretAnalyzer, this), + new AllPlayersInvolvedHandler(secretAnalyzer) + ); + chain.process(); + const lastAmount = secretInfo.amounts[secretInfo.amounts.length - 1]; + const indexOfUser = this.findIndexOfUserIdInSecretInfo(secretInfo, userId); + secretInfo.users[indexOfUser] = ''; + return lastAmount; + } } diff --git a/src/timer/timers.service.ts b/src/timer/timers.service.ts index 408c96a..31d204a 100644 --- a/src/timer/timers.service.ts +++ b/src/timer/timers.service.ts @@ -23,7 +23,7 @@ export class TimerService { } catch (err: unknown) { const error = err instanceof Error ? err : new Error(String(err)); console.log('In Timer'); - console.log(error.message); + console.log(error); reject(error); } }, time); diff --git a/src/player/dto/offer-trade.dto.ts b/src/trade/dto/offer-trade.dto.ts similarity index 100% rename from src/player/dto/offer-trade.dto.ts rename to src/trade/dto/offer-trade.dto.ts diff --git a/src/trade/trade.gateway.ts b/src/trade/trade.gateway.ts new file mode 100644 index 0000000..586da95 --- /dev/null +++ b/src/trade/trade.gateway.ts @@ -0,0 +1,111 @@ +import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; +import { + ConnectedSocket, + MessageBody, + SubscribeMessage, + WebSocketGateway, + WsException, +} from '@nestjs/websockets'; +import { Socket } from 'socket.io'; +import { ValidPlayerGuard, TurnGuard } from 'src/auth/guard'; +import { JwtPayload } from 'src/auth/types/jwtPayloadType.type'; +import { GamePayload } from 'src/game/game.repository'; +import { OfferTradeDto } from './dto/offer-trade.dto'; +import { WebsocketExceptionsFilter } from 'src/utils/exceptions/websocket-exceptions.filter'; +import { WsValidationPipe } from 'src/pipes/wsValidation.pipe'; +import { WsGuard } from 'src/auth/guard/jwt.ws.guard'; +import { AuctionService } from 'src/auction/auction.service'; +import { TradeService } from './trade.service'; +import { WebSocketProvider } from 'src/webSocketProvider/webSocketProvider.service'; +import { ChatService } from 'src/chat/chat.service'; +import { Trade } from 'src/game/types/trade.type'; + +@WebSocketGateway({ + cors: { + origin: + process.env.NODE_ENV === 'development' + ? process.env.FRONTEND_URL_DEV + : process.env.FRONTEND_URL_PROD, + methods: ['GET', 'POST'], + credentials: true, + }, +}) +@UseFilters(WebsocketExceptionsFilter) +@UsePipes(new WsValidationPipe()) +@UseGuards(WsGuard, ValidPlayerGuard) +export class TradeGateway { + constructor( + private auctionService: AuctionService, + private tradeService: TradeService, + private webSocketProvider: WebSocketProvider, + private chatService: ChatService + ) {} + @UseGuards(TurnGuard) + @SubscribeMessage('offerTrade') + async offerTrade( + @ConnectedSocket() + socket: Socket & { game: Partial; jwtPayload: JwtPayload }, + @MessageBody() + dataArray: [OfferTradeDto, null] + ) { + const data = dataArray[0]; + const game = socket.game; + const userId = socket.jwtPayload.sub; + if (this.auctionService.getAuction(game.id)) + throw new WsException('Cannot offer trade while auction'); + await this.tradeService.validateTradeData(game, data); + const trade = { ...data, fromUserId: userId } as Trade; + this.tradeService.setTrade(game.id, trade); + const fromPlayer = game.players.find( + (player) => player.userId === trade.fromUserId + ); + const toPlayer = game.players.find( + (player) => player.userId === trade.toUserId + ); + const message = await this.chatService.onNewMessage(game.turnOfUserId, { + text: `${fromPlayer.user.nickname} запропонував ${toPlayer.user.nickname} угоду!`, + chatId: game.chat.id, + }); + this.webSocketProvider.server.to(game.id).emit('gameChatMessage', message); + this.tradeService.handleOfferTrade({ game, trade }); + } + + @SubscribeMessage('refuseFromTrade') + async refuseFromTrade( + @ConnectedSocket() + socket: Socket & { game: Partial; jwtPayload: JwtPayload } + ) { + const game = socket.game; + this.tradeService.refuseFromTrade(game); + } + + @SubscribeMessage('acceptTrade') + async acceptTrade( + @ConnectedSocket() + socket: Socket & { game: Partial; jwtPayload: JwtPayload } + ) { + const game = socket.game; + const trade = this.tradeService.getTrade(game.id); + const { updatedGame, fields } = await this.tradeService.acceptTrade( + game, + trade, + socket.jwtPayload.sub + ); + const data = { fields }; + if (updatedGame) { + data['game'] = updatedGame; + } + const fromPlayer = game.players.find( + (player) => player.userId === trade.fromUserId + ); + const toPlayer = game.players.find( + (player) => player.userId === trade.toUserId + ); + const message = await this.chatService.onNewMessage(game.turnOfUserId, { + text: `Угода між ${fromPlayer.user.nickname} та ${toPlayer.user.nickname} підписана!`, + chatId: game.chat.id, + }); + this.webSocketProvider.server.to(game.id).emit('gameChatMessage', message); + this.webSocketProvider.server.to(game.id).emit('updateGameData', data); + } +} diff --git a/src/trade/trade.module.ts b/src/trade/trade.module.ts new file mode 100644 index 0000000..4f35460 --- /dev/null +++ b/src/trade/trade.module.ts @@ -0,0 +1,28 @@ +import { Module } from '@nestjs/common'; +import { TradeService } from './trade.service'; +import { TradeGateway } from './trade.gateway'; +import { GameModule } from 'src/game/game.module'; +import { WebSocketProviderModule } from 'src/webSocketProvider/webSocketProvider.module'; +import { FieldModule } from 'src/field/field.module'; +import { ChatModule } from 'src/chat/chat.module'; +import { TimerModule } from 'src/timer/timers.module'; +import { PlayerModule } from 'src/player/player.module'; +import { AuctionModule } from 'src/auction/auction.module'; +import { JwtModule } from '@nestjs/jwt'; +import { ConfigModule } from '@nestjs/config'; + +@Module({ + imports: [ + GameModule, + WebSocketProviderModule, + FieldModule, + ChatModule, + TimerModule, + PlayerModule, + AuctionModule, + JwtModule, + ConfigModule, + ], + providers: [TradeService, TradeGateway], +}) +export class TradeModule {} diff --git a/src/trade/trade.service.ts b/src/trade/trade.service.ts new file mode 100644 index 0000000..e3b8263 --- /dev/null +++ b/src/trade/trade.service.ts @@ -0,0 +1,166 @@ +import { Injectable } from '@nestjs/common'; +import { GamePayload } from 'src/game/game.repository'; +import { GameService } from 'src/game/game.service'; +import { Trade } from 'src/game/types/trade.type'; +import { OfferTradeDto } from './dto/offer-trade.dto'; +import { WebSocketProvider } from 'src/webSocketProvider/webSocketProvider.service'; +import { FieldService } from 'src/field/field.service'; +import { ChatService } from 'src/chat/chat.service'; +import { TimerService } from 'src/timer/timers.service'; +import { WsException } from '@nestjs/websockets'; +import { PlayerService } from 'src/player/player.service'; + +@Injectable() +export class TradeService { + trades: Map = new Map(); + constructor( + private gameService: GameService, + private webSocketProvider: WebSocketProvider, + private fieldService: FieldService, + private chatService: ChatService, + private timerService: TimerService, + private playerService: PlayerService + ) { + this.refuseFromTrade = this.refuseFromTrade.bind(this); + } + setTrade(gameId: string, trade: Trade) { + this.trades.set(gameId, trade); + } + getTrade(gameId: string) { + return this.trades.get(gameId); + } + + async validateTradeData(game: Partial, data: OfferTradeDto) { + if ( + data.offerFieldsIndexes.length === 0 && + data.wantedFieldsIndexes.length === 0 && + data.offeredMoney <= 0 && + data.wantedMoney <= 0 + ) { + throw new WsException('You must offer something'); + } + const fields = await this.fieldService.getGameFields(game.id); + if (data.offerFieldsIndexes.length > 0) { + const userFields = fields.filter( + (field) => field.ownedBy === game.turnOfUserId + ); + const userFieldsIndexes = userFields.map((field) => field.index); + const hasAllOfferFields = data.offerFieldsIndexes.every((index) => + userFieldsIndexes.includes(index) + ); + if (!hasAllOfferFields) { + throw new WsException('You dont have all offer fields'); + } + } + if (data.wantedFieldsIndexes.length > 0) { + const otherUserFields = fields.filter( + (field) => field.ownedBy === data.toUserId + ); + const otherUserFieldsIndexes = otherUserFields.map( + (field) => field.index + ); + const hasAllWantedFields = data.wantedFieldsIndexes.every((index) => + otherUserFieldsIndexes.includes(index) + ); + if (!hasAllWantedFields) { + throw new WsException('Other player doesnt have all wanted fields'); + } + } + } + + async handleOfferTrade(data: { game: Partial; trade: Trade }) { + const { updatedGame } = await this.gameService.passTurnToUser({ + game: data.game, + toUserId: data.trade.toUserId, + }); + this.webSocketProvider.server + .to(data.game.id) + .emit('updateGameData', { game: updatedGame }); + this.webSocketProvider.server + .to(data.trade.toUserId) + .emit('tradeOffered', { trade: data.trade }); + this.timerService.set( + updatedGame.id, + 10000, + updatedGame, + this.refuseFromTrade + ); + } + + async acceptTrade(game: Partial, trade: Trade, userId: string) { + if (!trade) throw new WsException('There is no trade to accept'); + if (trade.toUserId !== userId) + throw new WsException('You cant accept this trade'); + this.timerService.clear(game.id); + const fields = await this.fieldService.getGameFields(game.id); + if (trade.offerFieldsIndexes.length > 0) { + trade.offerFieldsIndexes.forEach((index) => { + const field = this.fieldService.findPlayerFieldByIndex(fields, index); + field.ownedBy = trade.toUserId; + }); + } + if (trade.wantedFieldsIndexes.length > 0) { + trade.wantedFieldsIndexes.forEach((index) => { + const field = this.fieldService.findPlayerFieldByIndex(fields, index); + field.ownedBy = trade.fromUserId; + }); + } + if (trade.offerFieldsIndexes.length || trade.wantedFieldsIndexes) { + await this.fieldService.updateFields(fields, ['ownedBy']); + } + let player = null; + if (trade.offeredMoney) { + this.playerService.decrementMoneyWithUserAndGameId( + trade.fromUserId, + game.id, + trade.offeredMoney + ); + player = await this.playerService.incrementMoneyWithUserAndGameId( + trade.toUserId, + game.id, + trade.offeredMoney + ); + } + if (trade.wantedMoney) { + this.playerService.decrementMoneyWithUserAndGameId( + trade.toUserId, + game.id, + trade.wantedMoney + ); + player = await this.playerService.incrementMoneyWithUserAndGameId( + trade.fromUserId, + game.id, + trade.wantedMoney + ); + } + this.setTrade(game.id, null); + const { updatedGame } = await this.gameService.passTurnToUser({ + game: player?.game ? player.game : game, + toUserId: trade.fromUserId, + }); + return { fields, updatedGame }; + } + + async refuseFromTrade(game: Partial) { + const trade = this.getTrade(game.id); + if (!trade) throw new WsException('There is no trade to refuse'); + this.setTrade(game.id, null); + + const { updatedGame } = await this.gameService.passTurnToUser({ + game, + toUserId: trade.fromUserId, + turnTime: game.timeOfTurn, + }); + this.webSocketProvider.server + .to(game.id) + .emit('updateGameData', { game: updatedGame }); + const toPlayer = game.players.find( + (player) => player.userId === trade.toUserId + ); + const message = await this.chatService.onNewMessage(game.turnOfUserId, { + text: `${toPlayer.user.nickname} відхилив угоду!`, + chatId: game.chat.id, + }); + this.webSocketProvider.server.to(game.id).emit('gameChatMessage', message); + } +} diff --git a/src/utils/fields/fields.ts b/src/utils/fields/fields.ts index a8be6e5..fe89a82 100644 --- a/src/utils/fields/fields.ts +++ b/src/utils/fields/fields.ts @@ -10,27 +10,16 @@ export const DEFAULT_FIELDS = [ imageUrl: 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/start.svg?t=2025-01-01T16%3A46%3A10.529Z', }, - { - positionForGrid: 1, + positionForGrid: 2, index: 2, - name: 'Укр Пошта', - price: 1000, - pledgePrice: 600, - redemptionPrice: 650, - branchPrice: 500, - sellBranchPrice: 450, - amountOfBranches: 0, - group: 'post', - specialField: false, + name: 'Невідомість', + specialField: true, + secret: true, line: 'horizontal-top', ownedBy: null, - color: 'yellow', - income: [100, 150, 420, 1100, 1735, 3000], - isPledged: false, - turnsToUnpledge: null, imageUrl: - 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/ukr_poshta.svg?t=2024-12-26T13%3A54%3A43.654Z', + 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/que1.svg', }, { positionForGrid: 2, @@ -44,102 +33,62 @@ export const DEFAULT_FIELDS = [ 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/que1.svg', }, { - positionForGrid: 3, + positionForGrid: 2, index: 4, - name: 'Meest', - price: 1200, - group: 'post', - pledgePrice: 720, - redemptionPrice: 800, - branchPrice: 500, - sellBranchPrice: 450, - amountOfBranches: 0, - specialField: false, + name: 'Невідомість', + specialField: true, + secret: true, line: 'horizontal-top', ownedBy: null, - color: 'yellow', - income: [120, 175, 500, 1250, 1880, 3400], - isPledged: false, - turnsToUnpledge: null, imageUrl: - 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/Meest.svg?t=2025-01-01T16%3A43%3A57.691Z', + 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/que1.svg', }, { - positionForGrid: 4, + positionForGrid: 2, index: 5, - name: 'Нова Пошта', - price: 1400, - group: 'post', - pledgePrice: 840, - redemptionPrice: 850, - branchPrice: 500, - sellBranchPrice: 450, - amountOfBranches: 0, - specialField: false, + name: 'Невідомість', + specialField: true, + secret: true, line: 'horizontal-top', ownedBy: null, - color: 'yellow', - income: [140, 200, 635, 1400, 2000, 4000], - isPledged: false, - turnsToUnpledge: null, imageUrl: - 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/nova_poshta.svg?t=2024-12-26T13%3A47%3A58.811Z', + 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/que1.svg', }, { - positionForGrid: 5, + positionForGrid: 2, index: 6, - name: 'ВДНХ', + name: 'Невідомість', specialField: true, - toPay: -3000, + secret: true, line: 'horizontal-top', ownedBy: null, imageUrl: - 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/vdng.svg?t=2024-12-26T13%3A55%3A02.015Z', + 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/que1.svg', }, { - positionForGrid: 6, + positionForGrid: 2, index: 7, - name: 'Батьківщина', - price: 1800, - pledgePrice: 1080, - redemptionPrice: 1200, - branchPrice: 600, - sellBranchPrice: 550, - amountOfBranches: 0, - group: 'political-party', - specialField: false, + name: 'Невідомість', + specialField: true, + secret: true, line: 'horizontal-top', ownedBy: null, - color: 'blue', - income: [180, 250, 715, 1500, 2275, 4850], - isPledged: false, - turnsToUnpledge: null, imageUrl: - 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/tumoshenko.svg?t=2024-12-26T13%3A54%3A08.461Z', + 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/que1.svg', }, { - positionForGrid: 7, + positionForGrid: 2, index: 8, - name: 'Європейська солідарність', - price: 2000, - pledgePrice: 1200, - redemptionPrice: 1300, - branchPrice: 600, - sellBranchPrice: 550, - amountOfBranches: 0, - group: 'political-party', - specialField: false, + name: 'Невідомість', + specialField: true, + secret: true, line: 'horizontal-top', ownedBy: null, - color: 'blue', - income: [200, 275, 760, 1710, 2400, 6660], - isPledged: false, - turnsToUnpledge: null, imageUrl: - 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/poroshenkoEC.svg?t=2025-01-01T16%3A44%3A54.823Z', + 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/que1.svg', }, { - positionForGrid: 8, + positionForGrid: 2, index: 9, name: 'Невідомість', specialField: true, @@ -150,37 +99,26 @@ export const DEFAULT_FIELDS = [ 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/que1.svg', }, { - positionForGrid: 9, + positionForGrid: 2, index: 10, - name: 'Слуга Народу', - price: 2200, - pledgePrice: 1320, - redemptionPrice: 1450, - branchPrice: 600, - sellBranchPrice: 550, - amountOfBranches: 0, - group: 'political-party', - specialField: false, + name: 'Невідомість', + specialField: true, + secret: true, line: 'horizontal-top', ownedBy: null, - color: 'blue', - income: [220, 300, 820, 2100, 4575, 7700], - isPledged: false, - turnsToUnpledge: null, imageUrl: - 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/sluga_naroda.svg?t=2024-12-26T13%3A53%3A35.907Z', + 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/que1.svg', }, - { - positionForGrid: 10, + positionForGrid: 2, index: 11, - name: 'Білоцерківський Строгач', + name: 'Невідомість', specialField: true, - large: true, - line: 'vertical-right', + secret: true, + line: 'horizontal-top', ownedBy: null, imageUrl: - 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/prison.svg?t=2025-01-01T16%3A45%3A15.880Z', + 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/que1.svg', }, { positionForGrid: 12, diff --git a/src/utils/fields/secretFields.ts b/src/utils/fields/secretFields.ts index 2aa7a62..086e928 100644 --- a/src/utils/fields/secretFields.ts +++ b/src/utils/fields/secretFields.ts @@ -6,7 +6,7 @@ const secretFields = [ }, { text: '$RANDOM_PLAYER$ reported him/her to NABU for undeclared property that he/she did not list in financial documents. Pays a fine of 1500mm. The reporter receives a reward of 3500mm.', - amounts: [-1500, 3500], + amounts: [3500, -1500], numOfPlayersInvolved: 'two', }, { diff --git a/src/webSocketProvider/webSocketProvider.module.ts b/src/webSocketProvider/webSocketProvider.module.ts new file mode 100644 index 0000000..e9d52e8 --- /dev/null +++ b/src/webSocketProvider/webSocketProvider.module.ts @@ -0,0 +1,8 @@ +import { Module } from '@nestjs/common'; +import { WebSocketProvider } from './webSocketProvider.service'; + +@Module({ + providers: [WebSocketProvider], + exports: [WebSocketProvider], +}) +export class WebSocketProviderModule {} diff --git a/src/webSocketProvider/webSocketProvider.service.ts b/src/webSocketProvider/webSocketProvider.service.ts new file mode 100644 index 0000000..2070d5e --- /dev/null +++ b/src/webSocketProvider/webSocketProvider.service.ts @@ -0,0 +1,18 @@ +import { Injectable } from '@nestjs/common'; +import { Server } from 'socket.io'; + +@Injectable() +export class WebSocketProvider { + private _server: Server | null = null; + + setServer(server: Server) { + this._server = server; + } + + get server(): Server { + if (!this._server) { + throw new Error('WebSocket server is not initialized'); + } + return this._server; + } +} diff --git a/src/webSocketServer/webSocketServer.module.ts b/src/webSocketServer/webSocketServer.module.ts deleted file mode 100644 index e04a3f8..0000000 --- a/src/webSocketServer/webSocketServer.module.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Module } from '@nestjs/common'; -import { WebSocketServerService } from './webSocketServer.service'; - -@Module({ - providers: [WebSocketServerService], - exports: [WebSocketServerService], -}) -export class WebSocketServerModule {} diff --git a/src/webSocketServer/webSocketServer.service.ts b/src/webSocketServer/webSocketServer.service.ts deleted file mode 100644 index b864ecb..0000000 --- a/src/webSocketServer/webSocketServer.service.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { Server } from 'socket.io'; - -@Injectable() -export class WebSocketServerService { - public server: Server; - - setServer(server: Server) { - this.server = server; - } -} From 7210dc4deb0e3b1d735feb6fc61095fb2c67298d Mon Sep 17 00:00:00 2001 From: Taras Yurchenko Date: Thu, 29 May 2025 15:00:20 +0300 Subject: [PATCH 09/27] feat: added chain of responsibility pattern --- src/common/IHandler.ts | 4 + src/common/base.handler.ts | 15 + src/common/handlerChain.ts | 17 + src/field/FieldAnalyzer.ts | 47 + src/game/game.gateway.ts | 836 ++++-------------- .../handlers/AllPlayersInvolved.handler.ts | 11 + .../handlers/onePlayerInvolved.handler.ts | 16 + src/game/handlers/passTurn.handler.ts | 11 + src/game/handlers/processSpecial.handler.ts | 7 + src/game/handlers/putUpForAuction.handler.ts | 7 + src/game/handlers/steppedOnPrivate.handler.ts | 7 + .../handlers/twoPlayersInvolved.handler.ts | 27 + src/secret/secretAnalyzer.ts | 29 + 13 files changed, 351 insertions(+), 683 deletions(-) create mode 100644 src/common/IHandler.ts create mode 100644 src/common/base.handler.ts create mode 100644 src/common/handlerChain.ts create mode 100644 src/field/FieldAnalyzer.ts create mode 100644 src/game/handlers/AllPlayersInvolved.handler.ts create mode 100644 src/game/handlers/onePlayerInvolved.handler.ts create mode 100644 src/game/handlers/passTurn.handler.ts create mode 100644 src/game/handlers/processSpecial.handler.ts create mode 100644 src/game/handlers/putUpForAuction.handler.ts create mode 100644 src/game/handlers/steppedOnPrivate.handler.ts create mode 100644 src/game/handlers/twoPlayersInvolved.handler.ts create mode 100644 src/secret/secretAnalyzer.ts diff --git a/src/common/IHandler.ts b/src/common/IHandler.ts new file mode 100644 index 0000000..933fbd2 --- /dev/null +++ b/src/common/IHandler.ts @@ -0,0 +1,4 @@ +export interface IHandler { + canHandle(): boolean | void; + handle(): void; +} diff --git a/src/common/base.handler.ts b/src/common/base.handler.ts new file mode 100644 index 0000000..bc09763 --- /dev/null +++ b/src/common/base.handler.ts @@ -0,0 +1,15 @@ +import { IHandler } from './IHandler'; + +export abstract class BaseHandler implements IHandler { + constructor( + protected analyzer: T, + private handler?: () => void + ) {} + + abstract canHandle(): boolean | void; + handle() { + if (this.handler) { + this.handler(); + } + } +} diff --git a/src/common/handlerChain.ts b/src/common/handlerChain.ts new file mode 100644 index 0000000..fcf2df2 --- /dev/null +++ b/src/common/handlerChain.ts @@ -0,0 +1,17 @@ +import { IHandler } from './IHandler'; + +export class HandlerChain { + private handlers: IHandler[] = []; + addHandlers(...handlers: IHandler[]): void { + this.handlers.push(...handlers); + } + + process(): unknown | Promise { + for (const handler of this.handlers) { + if (handler.canHandle()) { + return handler.handle(); + } + } + console.log('No handler found for this data'); + } +} diff --git a/src/field/FieldAnalyzer.ts b/src/field/FieldAnalyzer.ts new file mode 100644 index 0000000..a9f8e09 --- /dev/null +++ b/src/field/FieldAnalyzer.ts @@ -0,0 +1,47 @@ +import { GamePayload } from 'src/game/game.repository'; +import { PlayerPayload } from 'src/player/player.repository'; +import { PlayerService } from 'src/player/player.service'; +import { FieldDocument } from 'src/schema/Field.schema'; + +export class FieldAnalyzer { + readonly currentPlayer: Partial; + + constructor( + readonly field: FieldDocument, + readonly game: Partial, + private playerService: PlayerService + ) { + this.currentPlayer = this.playerService.findPlayerWithTurn(game); + } + isOwnedByCurrentUser(): boolean { + return ( + this.field.ownedBy === this.currentPlayer.userId && this.field.price > 0 + ); + } + isOwnedByOtherAndNotPledged(): boolean { + return ( + this.field.ownedBy && + this.field.ownedBy !== this.currentPlayer.userId && + !this.field.isPledged + ); + } + isNotOwned(): boolean { + return !this.field.ownedBy && this.field.price > 0; + } + isAffordableForSomeone(): boolean { + return this.game.players.some( + (player) => + player.userId !== this.currentPlayer.userId && + player.money > this.field.price + ); + } + isSpecialField(): boolean { + return !this.field.price; + } + isSkipable(): boolean { + return ( + (this.field?.specialField && !this.field.secret && !this.field.toPay) || + this.field.isPledged + ); + } +} diff --git a/src/game/game.gateway.ts b/src/game/game.gateway.ts index fc32878..326f553 100644 --- a/src/game/game.gateway.ts +++ b/src/game/game.gateway.ts @@ -1,6 +1,5 @@ import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; -import { OnEvent } from '@nestjs/event-emitter'; import { JwtService } from '@nestjs/jwt'; import { ConnectedSocket, @@ -8,31 +7,19 @@ import { SubscribeMessage, WebSocketGateway, WebSocketServer, - WsException, } from '@nestjs/websockets'; import { parse } from 'cookie'; import { Server, Socket } from 'socket.io'; -import { HasLostGuard } from 'src/auth/guard'; -import { ActiveGameGuard } from 'src/auth/guard/activeGame.guard'; -import { WsGuard } from 'src/auth/guard/jwt.ws.guard'; -import { TurnGuard } from 'src/auth/guard/turn.guard'; +import { AuctionService } from 'src/auction/auction.service'; +import { TurnGuard, ValidPlayerGuard, WsGuard } from 'src/auth/guard'; import { JwtPayload } from 'src/auth/types/jwtPayloadType.type'; import { WsValidationPipe } from 'src/pipes/wsValidation.pipe'; -import { PlayerPayload } from 'src/player/player.repository'; -import { PlayerService } from 'src/player/player.service'; +import { TimerService } from 'src/timer/timers.service'; import { WebsocketExceptionsFilter } from 'src/utils/exceptions/websocket-exceptions.filter'; -import { WebSocketServerService } from 'src/webSocketServer/webSocketServer.service'; -import { ChatService } from './../chat/chat.service'; +import { WebSocketProvider } from 'src/webSocketProvider/webSocketProvider.service'; import { GetGameId } from './decorators/getGameCookieWs'; import { GamePayload } from './game.repository'; import { GameService } from './game.service'; -import { Auction } from './types/auction.type'; -import { Trade } from './types/trade.type'; -import { FieldDocument } from 'src/schema/Field.schema'; -import { AuctionService } from 'src/auction/auction.service'; -import { TimerService } from 'src/timer/timers.service'; -import { SecretService } from 'src/secret/secret.service'; -import { FieldValidator } from 'src/field/FieldValidator'; @WebSocketGateway({ cors: { @@ -50,32 +37,17 @@ import { FieldValidator } from 'src/field/FieldValidator'; export class GameGateway { constructor( private gameService: GameService, - private playerService: PlayerService, + private webSocketProvider: WebSocketProvider, private jwtService: JwtService, private configService: ConfigService, - private chatService: ChatService, - private webSocketServer: WebSocketServerService, private auctionService: AuctionService, - private timerService: TimerService, - private secretService: SecretService - ) { - this.rollDice = this.rollDice.bind(this); - this.putUpForAuction = this.putUpForAuction.bind(this); - this.passTurnToNext = this.passTurnToNext.bind(this); - this.winAuction = this.winAuction.bind(this); - this.payForField = this.payForField.bind(this); - this.payToBank = this.payToBank.bind(this); - this.payAll = this.payAll.bind(this); - this.resolveTwoUsers = this.resolveTwoUsers.bind(this); - } - + private timerService: TimerService + ) {} @WebSocketServer() - private server: Server; - + readonly server: Server; afterInit(server: Server) { - this.webSocketServer.setServer(server); + this.webSocketProvider.setServer(server); } - async handleConnection(socket: Socket & { jwtPayload: JwtPayload }) { try { const cookies = await this.extractCookies(socket); @@ -85,7 +57,7 @@ export class GameGateway { if (!userId) return; socket.join(userId); const game = await this.gameService.getGame(gameId); - if (!game || game.status !== 'ACTIVE') return; + if (game.status !== 'ACTIVE') return; const timers = this.timerService.timers; if (timers.has(gameId)) { this.rejoinGame(socket, gameId); @@ -95,21 +67,16 @@ export class GameGateway { this.rejoinGame(socket, gameId); const currentField = await this.gameService.findCurrentFieldFromGame(game); - let timerCallback: (args: unknown) => Promise; if (game.dices) { - timerCallback = currentField?.price - ? this.putUpForAuction - : this.passTurnToNext; + this.gameService.processRolledDices(game, currentField); } else { - timerCallback = this.rollDice; + this.timerService.set( + gameId, + updatedGame.timeOfTurn, + updatedGame, + this.gameService.rollDice + ); } - - this.timerService.set( - gameId, - updatedGame.timeOfTurn, - updatedGame, - timerCallback - ); } catch (err) { console.log(err); } @@ -146,61 +113,51 @@ export class GameGateway { } @SubscribeMessage('getVisibleGames') - async getVisibleGames() { + async onGetVisibleGames() { const games = await this.gameService.getVisibleGames(); return games; } @SubscribeMessage('getGameData') - async getGameData(@GetGameId() gameId: string) { - const game = await this.gameService.getGame(gameId); - const auction = this.auctionService.auctions.get(game.id); - const secretInfo = this.secretService.secrets.get(game.id); - const fields = await this.gameService.getGameFields(game.id); - this.server.emit('gameData', { game, fields, auction, secretInfo }); + async onGetGameData(@GetGameId() gameId: string) { + const { game, fields, auction, secretInfo } = + await this.gameService.getGameData(gameId); + this.server + .to(gameId) + .emit('gameData', { game, fields, auction, secretInfo }); + } + + @SubscribeMessage('createGame') + async createGame(socket: Socket & { jwtPayload: JwtPayload }) { + const createdGameWithPlayer = await this.gameService.createGame( + socket.jwtPayload.sub + ); + socket.join(createdGameWithPlayer.id); + return this.server.emit('newGameCreated', createdGameWithPlayer); } @SubscribeMessage('joinGame') async onJoinGame( socket: Socket & { jwtPayload: JwtPayload }, - data: { id: string } + dataArray: [{ id: string }, null] ) { - const { game, shouldStart } = await this.gameService.onJoinGame( + const data = dataArray[0]; + const game = await this.gameService.joinGame( data.id, socket.jwtPayload.sub ); - if (!game) { - this.server - .to(socket.id) - .emit('error', { message: 'Could not join game' }); - return; - } this.leaveAllRoomsExceptInitial(socket); socket.join(data.id); this.server.emit('onParticipateGame', game); - if (shouldStart) { - this.startGame(game); - } - } - - private startGame(game: Partial) { - // We can setTimeout here for some countdown on frontend - this.server.emit('clearStartedGame', { - gameId: game.id, - }); - this.server.to(game.id).emit('startGame', { - game, - chatId: game.chat.id, - }); - this.timerService.set(game.id, game.timeOfTurn, game, this.rollDice); } @SubscribeMessage('leaveGame') async onLeaveGame( socket: Socket & { jwtPayload: JwtPayload }, - data: { id: string } + dataArray: [{ id: string }, null] ) { - const game = await this.gameService.onLeaveGame( + const data = dataArray[0]; + const game = await this.gameService.leaveGame( data.id, socket.jwtPayload.sub ); @@ -213,683 +170,196 @@ export class GameGateway { } } - @UseGuards(ActiveGameGuard, TurnGuard, HasLostGuard) + @UseGuards(ValidPlayerGuard, TurnGuard) @SubscribeMessage('rollDice') async onRollDice( @ConnectedSocket() socket: Socket & { jwtPayload: JwtPayload; game: Partial } ) { - if (socket.game.dices) - throw new WsException('You have already rolled dices'); - this.timerService.clear(socket.game.id); - await this.rollDice(socket.game); - } - - async rollDice(game: Partial) { - const { updatedGame, playerNextField, hasOwner, currentPlayer, fields } = - await this.gameService.makeTurn(game); - this.server.to(game.id).emit('rolledDice', { - fields, - game: updatedGame, - }); - const fieldValidator = new FieldValidator( - playerNextField, - updatedGame, - this.playerService - ); - if (fieldValidator.isOwnedByCurrentUser()) { - this.timerService.set(game.id, 2500, updatedGame, this.passTurnToNext); - } - if (fieldValidator.isOwnedByOtherAndNotPledged()) { - this.steppedOnPrivateField(currentPlayer, playerNextField, updatedGame); - return; - } - if (fieldValidator.isNotOwned()) { - if (fieldValidator.isAffordableForSomeone()) { - this.timerService.set( - game.id, - game.timeOfTurn, - updatedGame, - this.putUpForAuction - ); - } else { - this.timerService.set(game.id, 2500, updatedGame, this.passTurnToNext); - } - } - - if (fieldValidator.isSpecialField()) { - this.processSpecialField(updatedGame, playerNextField); - } - if (fieldValidator.isSkipable()) { - this.timerService.set(game.id, 2500, updatedGame, this.passTurnToNext); - } + await this.gameService.rollDice(socket.game); } - async processSpecialField( - game: Partial, - playerNextField: FieldDocument + @UseGuards(ValidPlayerGuard) + @SubscribeMessage('payToBankForSpecialField') + async onPayToBankForSpecialField( + @ConnectedSocket() + socket: Socket & { jwtPayload: JwtPayload; game: Partial } ) { - if (playerNextField.toPay) { - this.timerService.set( - game.id, - game.timeOfTurn, - { game, userId: game.turnOfUserId, amount: playerNextField.toPay }, - this.payToBank - ); - } - if (playerNextField.secret) { - const secret = this.gameService.getRandomSecret(); - const secretInfo = await this.secretService.parseAndSaveSecret( - secret, - game - ); - const randomPlayer = game.players.find( - (player) => player.userId === secretInfo.users[1] - ); - if (secret.text.includes('$RANDOM_PLAYER$')) { - secret.text = secret.text.replace( - '$RANDOM_PLAYER$', - randomPlayer?.user.nickname - ); - } - const message = await this.chatService.onNewMessage(game.turnOfUserId, { - text: secret.text, - chatId: game.chat.id, - }); - this.server.to(game.id).emit('gameChatMessage', message); - this.server.to(game.id).emit('secret', secretInfo); - secret.text = secret.text.replace( - randomPlayer?.user.nickname, - '$RANDOM_PLAYER$' - ); - if (secretInfo.users.length === 1) { - if (secretInfo.amounts[0] < 0) { - this.timerService.set( - game.id, - game.timeOfTurn, - { game, userId: game.turnOfUserId, amount: secretInfo.amounts[0] }, - this.payToBank - ); - } else { - this.timerService.set( - game.id, - 3500, - { game, userId: game.turnOfUserId, amount: secretInfo.amounts[0] }, - this.payToBank - ); - } - } else if (secretInfo.users.length === 2) { - this.timerService.set( - game.id, - game.timeOfTurn, - game, - this.resolveTwoUsers - ); - } else if (secretInfo.users.length > 2) { - this.timerService.set(game.id, game.timeOfTurn, game, this.payAll); - } - } - } - - async payAll(game: Partial) { - const secretInfo = this.secretService.secrets.get(game.id); - let updatedPlayer = null; - for (const userId of secretInfo.users) { - const firstUser = secretInfo.users[0]; - if (userId && userId !== firstUser) { - if (secretInfo.amounts.length === 2) { - updatedPlayer = await this.payToUser({ - game, - userId, - userToPayId: firstUser, - amount: secretInfo.amounts[1], - }); - } - - if (secretInfo.amounts.length === 1) { - const { playerWhoPayed } = await this.gameService.payToBank( - game, - userId, - secretInfo.amounts[0] - ); - updatedPlayer = playerWhoPayed; - } - } - } - this.secretService.secrets.delete(game.id); - this.passTurnToNext(updatedPlayer?.game || game); - this.server.to(game.id).emit('updatePlayers', { - game: updatedPlayer?.game, - }); - } - - async resolveTwoUsers(game: Partial) { - let secretInfo = this.secretService.secrets.get(game.id); - const firstPay = secretInfo.amounts[0] < 1; - let updatedGameToReturn: null | Partial = null; - const fields = await this.gameService.getGameFields(game.id); - if (firstPay) { - const userId = secretInfo.users[0]; - if (userId) { - const player = game.players.find((player) => player.userId === userId); - if ( - this.playerService.estimateAssets(player, fields) < - secretInfo.amounts[0] - ) { - await this.playerService.loseGame(player.userId, game.id, fields); - return; - } - const { updatedGame } = await this.gameService.payToBank( - game, - userId, - secretInfo.amounts[0] - ); - updatedGameToReturn = updatedGame; - } - if (secretInfo.users[1]) { - const { updatedGame } = await this.gameService.payToBank( - game, - secretInfo.users[1], - secretInfo.amounts[1] - ); - updatedGameToReturn = updatedGame; - } - } else { - const userId = secretInfo.users[1]; - if (userId) { - const player = game.players.find((player) => player.userId === userId); - if ( - this.playerService.estimateAssets(player, fields) < - secretInfo.amounts[1] - ) { - await this.playerService.loseGame(player.userId, game.id, fields); - return; - } - const { updatedGame } = await this.gameService.payToBank( - game, - userId, - secretInfo.amounts[1] - ); - updatedGameToReturn = updatedGame; - } - if (secretInfo.users[0]) { - const { updatedGame } = await this.gameService.payToBank( - game, - secretInfo.users[0], - secretInfo.amounts[0] - ); - updatedGameToReturn = updatedGame; - } - } - secretInfo = null; - this.passTurnToNext(updatedGameToReturn); + await this.gameService.payToBankForSpecialField( + socket.jwtPayload.sub, + socket.game + ); } - @UseGuards(ActiveGameGuard, HasLostGuard) - @SubscribeMessage('payToUser') - async onPayToUser( + @UseGuards(ValidPlayerGuard) + @SubscribeMessage('payToUserForSecret') + async onPayToUserForSecret( @ConnectedSocket() socket: Socket & { jwtPayload: JwtPayload; game: Partial } ) { const game = socket.game; const userId = socket.jwtPayload.sub; - const secretInfo = this.secretService.secrets.get(game.id); - return this.payToUser({ - game, - userId, - userToPayId: secretInfo.users[0], - amount: secretInfo.amounts[1], - }); - } - - async payToUser({ - game, - userId, - userToPayId, - amount, - }: { - game: Partial; - userId: string; - userToPayId: string; - amount: number; - }) { - let secretInfo = this.secretService.secrets.get(game.id); - if (!secretInfo.users.includes(userId)) - throw new WsException('You cant pay for that secret'); - const indexOfUser = secretInfo.users.indexOf(userId); - if (amount > 0) - throw new WsException('You dont have to pay for this secret field'); - const player = game.players.find((player) => player.userId === userId); - const fields = await this.gameService.getGameFields(game.id); - let updatedPlayer = null; - if (player.money < amount) { - const userToPay = game.players.find( - (player) => player.userId === userToPayId - ); - updatedPlayer = await this.playerService.incrementMoneyWithUserAndGameId( - userToPayId, - game.id, - this.playerService.estimateAssets(userToPay, fields) - ); - await this.playerService.loseGame(player.userId, game.id, fields); - } else { - await this.playerService.incrementMoneyWithUserAndGameId( - userId, - game.id, - amount - ); - updatedPlayer = await this.playerService.incrementMoneyWithUserAndGameId( - userToPayId, - game.id, - -amount - ); - } - secretInfo.users.splice(indexOfUser, 1, ''); - if ( - secretInfo.users.every((userId, index) => { - if (secretInfo.amounts[index] > 0) return true; - return userId === ''; - }) - ) { - secretInfo = null; - } + const { game: updatedGame, secretInfo } = + await this.gameService.payToUserForSecret(game, userId); this.server.to(game.id).emit('updatePlayers', { - game: updatedPlayer.game, + game: updatedGame, secretInfo, }); - return updatedPlayer; } - @UseGuards(ActiveGameGuard, HasLostGuard) - @SubscribeMessage('payToBank') - async onPayToBank( + @UseGuards(ValidPlayerGuard) + @SubscribeMessage('payToBankForSecret') + async onPayToBankForSecret( @ConnectedSocket() socket: Socket & { jwtPayload: JwtPayload; game: Partial } ) { - const currentField = await this.gameService.findCurrentFieldWithUserId( - socket.game - ); - const secretInfo = this.secretService.secrets.get(socket.game.id); - if (!socket.game.dices && !currentField.toPay && !secretInfo) - throw new WsException( - 'You cant pay for that field because smt is missing' - ); - let amountToPay = 0; - if (secretInfo) { - if (!secretInfo.users.includes(socket.jwtPayload.sub)) { - throw new WsException( - 'You cant pay to bank because no user in secretInfo' - ); - } - if (secretInfo.users.length === 1) { - if (secretInfo.amounts[0] > 0) { - throw new WsException( - 'You cant pay to bank because one user and he doesnt have to pay' - ); - } else { - amountToPay = secretInfo.amounts[0]; - } - } else if (secretInfo.users.length === 2) { - const index = secretInfo.users.findIndex( - (userId) => userId === socket.jwtPayload.sub - ); - if (secretInfo.amounts[index] > 0) { - throw new WsException( - 'You cant pay to bank two users and the one wants to pay dont have to' - ); - } else { - amountToPay = secretInfo.amounts[0]; - } - } else if ( - secretInfo.users.length > 2 && - socket.jwtPayload.sub !== secretInfo.users[0] - ) { - if (secretInfo.amounts.length === 2) { - if (secretInfo.amounts[1] > 0) { - throw new WsException( - 'You cant pay to bank more then 2 and this doesnt lya lya' - ); - } else { - amountToPay = secretInfo.amounts[0]; - } - } - - if (secretInfo.amounts.length === 1) { - if (secretInfo.amounts[0] > 0) { - throw new WsException( - 'You cant pay to bank more than 2 one amount and dont have t' - ); - } else { - amountToPay = secretInfo.amounts[0]; - } - } - if ( - secretInfo.users.every((userId, index) => { - if (secretInfo.amounts[index] > 0) return true; - return userId === ''; - }) - ) { - throw new WsException( - 'You cant pay to bank every user is empty string' - ); - } - } - } - if (currentField.toPay) { - this.payToBank({ - game: socket.game, - userId: socket.jwtPayload.sub, - amount: currentField.toPay, - }); - } - if (secretInfo) { - this.payToBank({ - game: socket.game, - userId: socket.jwtPayload.sub, - amount: amountToPay, - }); - const indexOfUser = secretInfo.users.findIndex( - (userId) => userId === socket.jwtPayload.sub - ); - secretInfo.users[indexOfUser] = ''; - } - } - - async payToBank(argsObj: { - game: Partial; - amount: number; - userId: string; - }) { - const { updatedGame } = await this.gameService.payToBank( - argsObj.game, - argsObj.userId, - argsObj.amount - ); - const secretInfo = this.secretService.secrets.get(updatedGame.id); - if (!secretInfo) { - await this.passTurnToNext(updatedGame); - } else { - this.server.to(updatedGame.id).emit('updatePlayers', { - game: updatedGame, - secretInfo, - }); - } - } - - async steppedOnPrivateField( - player: Partial, - field: FieldDocument, - game: Partial - ) { - const fields = await this.gameService.getGameFields(game.id); - if ( - this.playerService.estimateAssets(player, fields) >= - field.income[field.amountOfBranches] - ) { - this.timerService.set( - game.id, - game.timeOfTurn, - { game, field: field }, - this.payForField - ); - return; - } - const { updatedPlayer } = await this.playerService.loseGame( - player.userId, - game.id, - fields - ); - - await this.passTurnToNext(updatedPlayer.game); + const game = socket.game; + const userId = socket.jwtPayload.sub; + await this.gameService.payToBankForSecret(game, userId); } - @UseGuards(ActiveGameGuard, TurnGuard, HasLostGuard) - @SubscribeMessage('payForField') - async onPayForField( + @UseGuards(ValidPlayerGuard, TurnGuard) + @SubscribeMessage('payForPrivateField') + async onPayForPrivateField( @ConnectedSocket() socket: Socket & { jwtPayload: JwtPayload; game: Partial } ) { - const currentField = await this.gameService.findCurrentFieldWithUserId( - socket.game - ); - if (!socket.game.dices || !currentField.ownedBy) - throw new WsException('You cant pay for that field'); - this.timerService.clear(socket.game.id); - await this.payForField({ game: socket.game, field: currentField }); - } - - async payForField({ - game, - field, - }: { - game: Partial; - field: FieldDocument; - }) { - const { updatedGame, fields: updatedFields } = - await this.gameService.payForField(game, field); - this.server.to(game.id).emit('payedForField', { - game: updatedGame, - fields: updatedFields, - }); - this.passTurnToNext(updatedGame); + await this.gameService.payForPrivateField(socket.game); } - @UseGuards(ActiveGameGuard, TurnGuard, HasLostGuard) + @UseGuards(ValidPlayerGuard, TurnGuard) @SubscribeMessage('putUpForAuction') async onPutUpForAuction( @ConnectedSocket() socket: Socket & { jwtPayload: JwtPayload; game: Partial } ) { - await this.putUpForAuction(socket.game); + await this.gameService.putUpForAuction(socket.game); } - async putUpForAuction(game: Partial) { - const auction = await this.auctionService.putUpForAuction(game); - - const updatedGame = await this.gameService.updateGameWithNewTurn( - game, - 15000 - ); - this.server - .to(game.id) - .emit('hasPutUpForAuction', { game: updatedGame, auction }); - this.timerService.set(game.id, 15000, updatedGame, this.passTurnToNext); - } - - @SubscribeMessage('createGame') - async createGame(socket: Socket & { jwtPayload: JwtPayload }) { - const createdGameWithPlayer = await this.gameService.createGame( - socket.jwtPayload.sub - ); - - if (!createdGameWithPlayer) { - return socket.emit('error', { - message: - 'Ви вже знаходитесь в кімнаті, покиньте її щоб приєднатись до іншої', - }); - } else { - socket.join(createdGameWithPlayer.id); - } - - return this.server.emit('newGameCreated', createdGameWithPlayer); - } - - @UseGuards(ActiveGameGuard, HasLostGuard) + @UseGuards(ValidPlayerGuard) @SubscribeMessage('raisePrice') async raisePrice( @ConnectedSocket() socket: Socket & { jwtPayload: JwtPayload }, @GetGameId() gameId: string, - @MessageBody('raiseBy') raiseBy: number, - @MessageBody('bidAmount') bidAmount: number + @MessageBody() dataArray: [{ raiseBy: number; bidAmount: number }, null] ) { + const data = dataArray[0]; const userId = socket.jwtPayload.sub; - const auction = await this.auctionService.raisePrice( + await this.auctionService.raisePrice( gameId, userId, - raiseBy, - bidAmount + data.raiseBy, + data.bidAmount ); - - if (auction) { - this.server.to(gameId).emit('raisedPrice', { auction }); - this.timerService.set( - gameId, - 15000, - { ...auction, gameId }, - this.winAuction - ); - } else { - this.server.to(userId).emit('error', { - message: 'Хтось одночасно поставив з вами і перебив вашу ставку', - }); - } } - @UseGuards(ActiveGameGuard, HasLostGuard) + @UseGuards(ValidPlayerGuard) @SubscribeMessage('refuseAuction') async refuseAuction( @ConnectedSocket() socket: Socket & { jwtPayload: JwtPayload }, @GetGameId() gameId: string ) { const userId = socket.jwtPayload.sub; - const { auction, hasWinner, finished, game } = - await this.auctionService.refuseAuction(gameId, userId); - if (finished) { - if (hasWinner) { - this.winAuction({ ...auction, gameId }); - } else { - this.passTurnToNext(game); - } - } else { - this.server.to(gameId).emit('refusedFromAuction', { auction }); - } - } - - async winAuction(auction: Auction & { gameId: string }) { - const { updatedPlayer, fields } = - await this.auctionService.winAuction(auction); - this.server - .to(auction.gameId) - .emit('wonAuction', { auction, game: updatedPlayer.game, fields }); - const game = await this.gameService.getGame(auction.gameId); - this.passTurnToNext(game); + await this.auctionService.refuseAuction(gameId, userId); } - @UseGuards(ActiveGameGuard, TurnGuard, HasLostGuard) + @UseGuards(ValidPlayerGuard, TurnGuard) @SubscribeMessage('buyField') async onBuyField( @ConnectedSocket() socket: Socket & { jwtPayload: JwtPayload; game: Partial } ) { - await this.buyField(socket.game); - } - - async buyField(game: Partial) { - await this.gameService.buyField(game); - this.passTurnToNext(game); + await this.gameService.buyField(socket.game); } - @UseGuards(ActiveGameGuard, TurnGuard, HasLostGuard) + @UseGuards(ValidPlayerGuard, TurnGuard) @SubscribeMessage('passTurn') async onPassTurn( @ConnectedSocket() socket: Socket & { jwtPayload: JwtPayload; game: Partial } ) { - const fields = await this.gameService.getGameFields(socket.game.id); - const currentPlayer = this.playerService.findPlayerWithTurn(socket.game); - const currentField = this.gameService.findPlayerFieldByIndex( - fields, - currentPlayer.currentFieldIndex - ); - if (!currentField.large && currentField.ownedBy !== socket.jwtPayload.sub) { - throw new WsException('You cant pass turn with that field'); - } - await this.passTurnToNext(socket.game); + await this.gameService.passTurn(socket.game); } - async passTurnToNext(game: Partial) { - const { updatedGame } = await this.gameService.passTurnToNext(game); - const fields = await this.gameService.getGameFields(game.id); + @UseGuards(ValidPlayerGuard) + @SubscribeMessage('buyBranch') + async onBuyBranch( + @ConnectedSocket() + socket: Socket & { game: Partial; jwtPayload: JwtPayload }, + @MessageBody() dataArray: [{ index: number }, null] + ) { + const data = dataArray[0]; + const index = data.index; + const game = socket.game; + const userId = socket.jwtPayload.sub; + const { updatedGame, fields } = await this.gameService.buyBranch( + game, + index, + userId + ); this.server .to(game.id) - .emit('passTurnToNext', { game: updatedGame, fields }); - this.timerService.set(game.id, game.timeOfTurn, updatedGame, this.rollDice); + .emit('updateGameData', { fields, game: updatedGame }); } - @OnEvent('passTurnToNext') - async handlePassTurnToNext(data: { game: Partial }) { - this.passTurnToNext(data.game); - } - @OnEvent('offerTrade') - async handleOfferTrade(data: { game: Partial; trade: Trade }) { - const { updatedGame } = await this.gameService.passTurnToUser({ - game: data.game, - toUserId: data.trade.toUserId, - }); - this.server.to(data.game.id).emit('updateGameData', { game: updatedGame }); - this.server - .to(data.trade.toUserId) - .emit('tradeOffered', { trade: data.trade }); - this.timerService.set( - updatedGame.id, - 10000, - updatedGame, - this.playerService.refuseFromTrade + + @UseGuards(ValidPlayerGuard) + @SubscribeMessage('sellBranch') + async onSellBranch( + @ConnectedSocket() + socket: Socket & { game: Partial; jwtPayload: JwtPayload }, + @MessageBody() dataArray: [{ index: number }, null] + ) { + const data = dataArray[0]; + const index = data.index; + const game = socket.game; + const userId = socket.jwtPayload.sub; + const { updatedGame, fields } = await this.gameService.sellBranch( + game, + index, + userId ); + this.server + .to(game.id) + .emit('updateGameData', { fields, game: updatedGame }); } - @OnEvent('setRollDiceTimer') - async handleSetRollDiceTimer(game: Partial) { - this.timerService.set(game.id, game.timeOfTurn, game, this.rollDice); + + @UseGuards(ValidPlayerGuard) + @SubscribeMessage('surrender') + async surrender( + @ConnectedSocket() + socket: Socket & { game: Partial; jwtPayload: JwtPayload } + ) { + const userId = socket.jwtPayload.sub; + const gameId = socket.game.id; + + const { updatedPlayer, updatedFields } = await this.gameService.loseGame( + userId, + gameId + ); + + this.server.to(gameId).emit('playerSurrendered', { + game: updatedPlayer.game, + fields: updatedFields, + }); } - @OnEvent('setAfterRolledDiceTimer') - async handleSetAfterRolledDiceTimer(updatedGame: Partial) { - const currentPlayer = this.playerService.findPlayerWithTurn(updatedGame); - const fields = await this.gameService.getGameFields(updatedGame.id); - const playerNextField = this.gameService.findPlayerFieldByIndex( - fields, - currentPlayer.currentFieldIndex + + @UseGuards(ValidPlayerGuard) + @SubscribeMessage('mortgageField') + async onMortgageField( + @ConnectedSocket() + socket: Socket & { game: Partial; jwtPayload: JwtPayload }, + @MessageBody() dataArray: [{ index: number }, null] + ) { + const data = dataArray[0]; + const index = data.index; + const game = socket.game; + const { player, fields } = await this.gameService.mortgageField( + game, + index, + socket.jwtPayload.sub ); - if ( - playerNextField.price && - playerNextField.ownedBy === updatedGame?.turnOfUserId - ) { - this.timerService.set( - updatedGame.id, - 2500, - updatedGame, - this.passTurnToNext - ); - } - if ( - playerNextField.ownedBy && - playerNextField.ownedBy !== currentPlayer.userId - ) { - this.steppedOnPrivateField(currentPlayer, playerNextField, updatedGame); - return; - } - if (playerNextField.price && !playerNextField.ownedBy) { - this.timerService.set( - updatedGame.id, - updatedGame.timeOfTurn, - updatedGame, - this.putUpForAuction - ); - } - if (!playerNextField.price) { - this.processSpecialField(updatedGame, playerNextField); - } - const currentField = - await this.gameService.findCurrentFieldWithUserId(updatedGame); - if ( - currentField?.specialField && - !currentField.secret && - !currentField.toPay - ) { - this.timerService.set( - updatedGame.id, - 12000, - updatedGame, - this.passTurnToNext - ); - } + this.server + .to(game.id) + .emit('updateGameData', { fields, game: player.game }); } } diff --git a/src/game/handlers/AllPlayersInvolved.handler.ts b/src/game/handlers/AllPlayersInvolved.handler.ts new file mode 100644 index 0000000..967601d --- /dev/null +++ b/src/game/handlers/AllPlayersInvolved.handler.ts @@ -0,0 +1,11 @@ +import { SecretAnalyzer } from 'src/secret/secretAnalyzer'; +import { BaseHandler } from '../../common/base.handler'; +import { WsException } from '@nestjs/websockets'; +export class AllPlayersInvolvedHandler extends BaseHandler { + throw() { + throw new WsException('You get money from all u dont have to pay'); + } + canHandle() { + return this.analyzer.isAllPlayersInvolved() || this.throw(); + } +} diff --git a/src/game/handlers/onePlayerInvolved.handler.ts b/src/game/handlers/onePlayerInvolved.handler.ts new file mode 100644 index 0000000..3127835 --- /dev/null +++ b/src/game/handlers/onePlayerInvolved.handler.ts @@ -0,0 +1,16 @@ +import { SecretAnalyzer } from 'src/secret/secretAnalyzer'; +import { BaseHandler } from '../../common/base.handler'; +import { WsException } from '@nestjs/websockets'; +export class OnePlayerInvolvedHandler extends BaseHandler { + canHandle() { + return this.analyzer.inOnePlayerInvolved(); + } + + handle() { + if (this.analyzer.secretInfo.amounts[0] > 0) { + throw new WsException( + 'You cant pay to bank because one user and he doesnt have to pay' + ); + } + } +} diff --git a/src/game/handlers/passTurn.handler.ts b/src/game/handlers/passTurn.handler.ts new file mode 100644 index 0000000..9949e7a --- /dev/null +++ b/src/game/handlers/passTurn.handler.ts @@ -0,0 +1,11 @@ +import { FieldAnalyzer } from 'src/field/FieldAnalyzer'; +import { BaseHandler } from '../../common/base.handler'; +export class PassTurnHandler extends BaseHandler { + canHandle() { + return ( + this.analyzer.isOwnedByCurrentUser() || + (this.analyzer.isNotOwned() && !this.analyzer.isAffordableForSomeone()) || + this.analyzer.isSkipable() + ); + } +} diff --git a/src/game/handlers/processSpecial.handler.ts b/src/game/handlers/processSpecial.handler.ts new file mode 100644 index 0000000..8fd218e --- /dev/null +++ b/src/game/handlers/processSpecial.handler.ts @@ -0,0 +1,7 @@ +import { FieldAnalyzer } from 'src/field/FieldAnalyzer'; +import { BaseHandler } from '../../common/base.handler'; +export class ProcessSpecialHandler extends BaseHandler { + canHandle() { + return this.analyzer.isSpecialField(); + } +} diff --git a/src/game/handlers/putUpForAuction.handler.ts b/src/game/handlers/putUpForAuction.handler.ts new file mode 100644 index 0000000..48dd086 --- /dev/null +++ b/src/game/handlers/putUpForAuction.handler.ts @@ -0,0 +1,7 @@ +import { FieldAnalyzer } from 'src/field/FieldAnalyzer'; +import { BaseHandler } from '../../common/base.handler'; +export class PutUpForAuctionHandler extends BaseHandler { + canHandle() { + return this.analyzer.isNotOwned() && this.analyzer.isAffordableForSomeone(); + } +} diff --git a/src/game/handlers/steppedOnPrivate.handler.ts b/src/game/handlers/steppedOnPrivate.handler.ts new file mode 100644 index 0000000..cf48e15 --- /dev/null +++ b/src/game/handlers/steppedOnPrivate.handler.ts @@ -0,0 +1,7 @@ +import { FieldAnalyzer } from 'src/field/FieldAnalyzer'; +import { BaseHandler } from '../../common/base.handler'; +export class SteppedOnPrivateHandler extends BaseHandler { + canHandle() { + return this.analyzer.isOwnedByOtherAndNotPledged(); + } +} diff --git a/src/game/handlers/twoPlayersInvolved.handler.ts b/src/game/handlers/twoPlayersInvolved.handler.ts new file mode 100644 index 0000000..92ebf91 --- /dev/null +++ b/src/game/handlers/twoPlayersInvolved.handler.ts @@ -0,0 +1,27 @@ +import { SecretAnalyzer } from 'src/secret/secretAnalyzer'; +import { BaseHandler } from '../../common/base.handler'; +import { WsException } from '@nestjs/websockets'; +import { SecretService } from 'src/secret/secret.service'; +export class TwoPlayersInvolvedHandler extends BaseHandler { + constructor( + analyzer: SecretAnalyzer, + private secretService: SecretService + ) { + super(analyzer); + } + canHandle() { + return this.analyzer.isTwoPlayersInvolved(); + } + + handle(): void { + const index = this.secretService.findIndexOfUserIdInSecretInfo( + this.analyzer.secretInfo, + this.analyzer.userId + ); + if (this.analyzer.secretInfo.amounts[index] > 0) { + throw new WsException( + 'You cant pay to bank two users and the one wants to pay dont have to' + ); + } + } +} diff --git a/src/secret/secretAnalyzer.ts b/src/secret/secretAnalyzer.ts new file mode 100644 index 0000000..7b77606 --- /dev/null +++ b/src/secret/secretAnalyzer.ts @@ -0,0 +1,29 @@ +import { SecretInfo } from 'src/game/types/secretInfo.type'; + +export class SecretAnalyzer { + constructor( + public readonly secretInfo: SecretInfo, + public readonly userId: string + ) {} + isOneUserHaveToPay() { + return this.secretInfo.users.length === 1 && this.secretInfo.amounts[0] < 0; + } + isOneUserHaveToReceive() { + return this.secretInfo.users.length === 1 && this.secretInfo.amounts[0] > 0; + } + inOnePlayerInvolved() { + return this.secretInfo.numOfPlayersInvolved === 'one'; + } + isTwoPlayersInvolved() { + return this.secretInfo.numOfPlayersInvolved === 'two'; + } + isAllPlayersInvolved() { + return this.secretInfo.numOfPlayersInvolved === 'all'; + } + isFirstAndShouldGet() { + return ( + this.secretInfo.users[0] === this.userId && + this.secretInfo.amounts[0] === null + ); + } +} From e8bf5cfd55812dcace04736b333ed06e71c08ef8 Mon Sep 17 00:00:00 2001 From: Taras Yurchenko Date: Mon, 2 Jun 2025 14:56:08 +0300 Subject: [PATCH 10/27] fix: fixed not starting game for first --- src/game/game.gateway.ts | 4 ++++ src/game/game.service.ts | 4 +--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/game/game.gateway.ts b/src/game/game.gateway.ts index 326f553..9af3b5c 100644 --- a/src/game/game.gateway.ts +++ b/src/game/game.gateway.ts @@ -148,6 +148,10 @@ export class GameGateway { ); this.leaveAllRoomsExceptInitial(socket); socket.join(data.id); + if (game.status === 'ACTIVE') { + this.gameService.startGame(game); + } + this.server.emit('onParticipateGame', game); } diff --git a/src/game/game.service.ts b/src/game/game.service.ts index 4f0003a..eadd014 100644 --- a/src/game/game.service.ts +++ b/src/game/game.service.ts @@ -6,7 +6,6 @@ import { HandlerChain } from 'src/common/handlerChain'; import { FieldService } from 'src/field/field.service'; import { FieldAnalyzer } from 'src/field/FieldAnalyzer'; import { PaymentService } from 'src/payment/payment.service'; -import { PlayerPayload } from 'src/player/player.repository'; import { PlayerService } from 'src/player/player.service'; import { FieldDocument } from 'src/schema/Field.schema'; import { SecretService } from 'src/secret/secret.service'; @@ -166,7 +165,7 @@ export class GameService { (player) => player.userId === userId ); if (alreadyJoined || game.status !== 'LOBBY') - return { game: null, shouldStart: false }; + throw new WsException('You cannot join this game'); const color = this.playerService.COLORS[game.players.length]; const player = await this.playerService.create({ color, @@ -216,7 +215,6 @@ export class GameService { }, }, }); - this.startGame(startedGame); return startedGame; } return gameWithCreatedPlayer; From cc76b65092629a268d56a866d86c4a8483a03d23 Mon Sep 17 00:00:00 2001 From: Taras Yurchenko Date: Wed, 4 Jun 2025 15:37:03 +0300 Subject: [PATCH 11/27] feat: fixed join/leave game logic --- src/game/game.gateway.ts | 2 +- src/game/game.service.ts | 20 +++++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/game/game.gateway.ts b/src/game/game.gateway.ts index 9af3b5c..3e0d297 100644 --- a/src/game/game.gateway.ts +++ b/src/game/game.gateway.ts @@ -169,7 +169,7 @@ export class GameGateway { this.leaveAllRoomsExceptInitial(socket); this.server.emit('onParticipateGame', { id: data.id, - players: game.players, + ...game, }); } } diff --git a/src/game/game.service.ts b/src/game/game.service.ts index eadd014..d723fb3 100644 --- a/src/game/game.service.ts +++ b/src/game/game.service.ts @@ -146,6 +146,18 @@ export class GameService { } async joinGame(gameId: string, userId: string) { + const playerWithUserId = await this.playerService.findFirst({ + where: { + userId, + game: { + status: { + in: ['LOBBY', 'ACTIVE'], + }, + }, + }, + }); + if (playerWithUserId) + throw new WsException('You already in game, leave it first'); const game = await this.gameRepository.findFirst({ where: { id: gameId }, include: { @@ -240,7 +252,13 @@ export class GameService { await this.playerService.deleteById(player.id); - const game = await this.getGame(gameId); + let game = await this.getGame(gameId); + if (!game.players.length) { + game = await this.gameRepository.delete({ + where: { id: gameId }, + include: { players: true }, + }); + } return game; } From 623aed6987e69ea13f6423ad7c794b6a82923972 Mon Sep 17 00:00:00 2001 From: Taras Yurchenko Date: Fri, 6 Jun 2025 18:18:46 +0300 Subject: [PATCH 12/27] feat: changed cookie settings --- src/auth/auth.controller.ts | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts index 51fd4c2..28f63fe 100644 --- a/src/auth/auth.controller.ts +++ b/src/auth/auth.controller.ts @@ -24,9 +24,8 @@ import { GetCurrentUser } from './decorator/get-current-user.decorator'; import { GetCurrentUserId } from './decorator/get-current-user-id.decorator'; import { Public } from './decorator/public.decorator'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; -import { ConfigService } from '@nestjs/config'; import { AccessCookieAttributes, RefreshCookieAttributes } from './types'; -import { JwtRtGuard } from './guard'; +import { JwtGuard, JwtRtGuard } from './guard'; import { GetUser } from './decorator'; import { JwtPayloadWithRt } from './types/jwtPayloadWithRt.type'; @@ -36,20 +35,23 @@ export class AuthController { constructor(private authService: AuthService) {} static readonly ACCESS_COOKIES_ATTRIBUTES: AccessCookieAttributes = { httpOnly: true, + path: '/', + maxAge: 1000 * 60 * 15, sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax', secure: process.env.NODE_ENV === 'production', ...(process.env.NODE_ENV === 'production' && { - domain: process.env.DOMAIN_NAME_PROD, + domain: `.${process.env.DOMAIN_NAME_PROD}`, }), }; static readonly REFRESH_COOKIES_ATTRIBUTES: RefreshCookieAttributes = { httpOnly: true, - path: '/', + path: '/auth/refresh', + maxAge: 1000 * 60 * 60 * 24 * 7, sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax', secure: process.env.NODE_ENV === 'production', ...(process.env.NODE_ENV === 'production' && { - domain: process.env.DOMAIN_NAME_PROD, + domain: `.${process.env.DOMAIN_NAME_PROD}`, }), }; @@ -101,20 +103,9 @@ export class AuthController { .send({ status: 'success', message: 'Logged in successfully', user }); } - @Public() - @UseGuards(JwtRtGuard) @Get('local/me') async me(@GetUser() user: JwtPayloadWithRt, @Res() res: Response) { const result = await this.authService.me(user); - - this.setCookie( - res, - 'access_token', - result.access_token, - AuthController.ACCESS_COOKIES_ATTRIBUTES - ); - delete result.access_token; - return res.status(HttpStatus.OK).send(result); } From 7d738dc88faab8a48ca909e79d04e8b40a77134c Mon Sep 17 00:00:00 2001 From: Taras Yurchenko Date: Fri, 6 Jun 2025 18:19:01 +0300 Subject: [PATCH 13/27] fix: fixed me route --- src/auth/auth.service.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index 82f0cf5..5b29e14 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -91,16 +91,7 @@ export class AuthService { async me(user: JwtPayloadWithRt) { const userFromDB = await this.userRepository.findByEmail(user.email); if (!userFromDB) throw new UnauthorizedException('User does not exist'); - const access_token = await this.jwtService.signAsync( - { sub: userFromDB.id, email: userFromDB.email }, - { - expiresIn: '1d', - privateKey: this.configService.get('ACCESS_TOKEN_PRIV_KEY'), - algorithm: 'RS256', - } - ); return { - access_token, nickname: userFromDB.nickname, email: userFromDB.email, id: userFromDB.id, From 68fa4aa2f70f2d87b44cf25e4e0a71ab3e911b4f Mon Sep 17 00:00:00 2001 From: Taras Yurchenko Date: Fri, 6 Jun 2025 18:20:35 +0300 Subject: [PATCH 14/27] fixed cookies attributes --- src/auth/types/cookiesAttributes.type.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/auth/types/cookiesAttributes.type.ts b/src/auth/types/cookiesAttributes.type.ts index 03f9197..68d78ae 100644 --- a/src/auth/types/cookiesAttributes.type.ts +++ b/src/auth/types/cookiesAttributes.type.ts @@ -3,11 +3,14 @@ export type AccessCookieAttributes = { sameSite: 'lax' | 'strict' | 'none'; secure?: boolean; domain: string; + path: string; + maxAge: number; }; export type RefreshCookieAttributes = { httpOnly: boolean; secure?: boolean; + maxAge: number; sameSite: 'lax' | 'strict' | 'none'; path: string; domain: string; From a974e22cbb8294bc43da3b1559d5c758a7c1c40d Mon Sep 17 00:00:00 2001 From: Taras Yurchenko Date: Fri, 6 Jun 2025 18:52:20 +0300 Subject: [PATCH 15/27] feat: made that guard doesn't decrypt cookie every time but just check it's connection data --- src/auth/guard/jwt.ws.guard.ts | 7 ++-- src/auth/guard/turn.guard.ts | 2 +- src/auth/guard/validPlayer.guard.ts | 31 +++++++++++++--- src/chat/chat.gateway.ts | 5 +-- src/game/game.gateway.ts | 55 +++++++++++++++-------------- src/game/game.service.ts | 2 +- src/player/player.gateway.ts | 6 ++-- src/trade/trade.gateway.ts | 10 +++--- 8 files changed, 73 insertions(+), 45 deletions(-) diff --git a/src/auth/guard/jwt.ws.guard.ts b/src/auth/guard/jwt.ws.guard.ts index f7172a6..797c647 100644 --- a/src/auth/guard/jwt.ws.guard.ts +++ b/src/auth/guard/jwt.ws.guard.ts @@ -16,13 +16,16 @@ export class WsGuard implements CanActivate { if (!client.handshake.headers.cookie) { throw new WsException('Unauthorized'); } - + const userId = client.data.jwtPayload?.sub; + if (userId) { + return true; + } const { access_token } = parse(client.handshake.headers.cookie); try { const decoded = await this.jwtService.verify(access_token, { publicKey: this.configService.get('ACCESS_TOKEN_PUB_KEY'), }); - client.jwtPayload = decoded; + client.data.jwtPayload = decoded; return decoded; } catch (ex) { throw new WsException(ex.message); diff --git a/src/auth/guard/turn.guard.ts b/src/auth/guard/turn.guard.ts index 58b94be..4d8b2e1 100644 --- a/src/auth/guard/turn.guard.ts +++ b/src/auth/guard/turn.guard.ts @@ -7,7 +7,7 @@ export class TurnGuard implements CanActivate { async canActivate(context: any): Promise { const client = context.switchToWs().getClient(); - const userId = client.jwtPayload.sub; + const userId = client.data.jwtPayload.sub; try { if ( client.game?.turnOfUserId !== userId && diff --git a/src/auth/guard/validPlayer.guard.ts b/src/auth/guard/validPlayer.guard.ts index eed73a9..3a718ac 100644 --- a/src/auth/guard/validPlayer.guard.ts +++ b/src/auth/guard/validPlayer.guard.ts @@ -2,18 +2,41 @@ import { CanActivate, Injectable } from '@nestjs/common'; import { WsException } from '@nestjs/websockets'; import { PlayerService } from 'src/player/player.service'; import { parse } from 'cookie'; +import { JwtService } from '@nestjs/jwt'; +import { ConfigService } from '@nestjs/config'; @Injectable() export class ValidPlayerGuard implements CanActivate { - constructor(private playerService: PlayerService) {} + constructor( + private playerService: PlayerService, + private jwtService: JwtService, + private configService: ConfigService + ) {} async canActivate(context: any): Promise { const client = context.switchToWs().getClient(); - const userId = client.jwtPayload.sub; - const { gameId } = parse(client.handshake.headers.cookie); + const userId = client.data.jwtPayload.sub; + if (!userId) { + if (!client.handshake.headers.cookie) { + throw new WsException('Unauthorized'); + } + const { access_token } = parse(client.handshake.headers.cookie); + try { + const decoded = await this.jwtService.verify(access_token, { + publicKey: this.configService.get('ACCESS_TOKEN_PUB_KEY'), + }); + client.data.jwtPayload = decoded; + } catch (ex) { + throw new WsException(ex.message); + } + } + let gameId = client.data.gameId; + if (!gameId) { + gameId = parse(client.handshake.headers.cookie); + } try { const player = await this.playerService.findByUserAndGameId( - userId, + userId || client.data.jwtPayload.sub, gameId ); if (player?.game?.status !== 'ACTIVE') diff --git a/src/chat/chat.gateway.ts b/src/chat/chat.gateway.ts index e10b8c1..98d2087 100644 --- a/src/chat/chat.gateway.ts +++ b/src/chat/chat.gateway.ts @@ -46,13 +46,14 @@ export class ChatGateway implements OnModuleInit { ) { const data = dataArray[0]; const message = await this.chatService.onNewMessage( - socket.jwtPayload.sub, + socket.data.jwtPayload.sub, data ); this.server.emit('onMessage', message); } + @UseGuards(WsGuard) @SubscribeMessage('newGameMessage') async onNewGameMessage( socket: Socket & { jwtPayload: JwtPayload }, @@ -60,7 +61,7 @@ export class ChatGateway implements OnModuleInit { ) { const data = dataArray[0]; const message = await this.chatService.onNewMessage( - socket.jwtPayload.sub, + socket.data.jwtPayload.sub, data ); if (Array.from(socket.rooms).includes(data.gameId)) diff --git a/src/game/game.gateway.ts b/src/game/game.gateway.ts index 3e0d297..df479e6 100644 --- a/src/game/game.gateway.ts +++ b/src/game/game.gateway.ts @@ -33,7 +33,6 @@ import { GameService } from './game.service'; }) @UseFilters(WebsocketExceptionsFilter) @UsePipes(new WsValidationPipe()) -@UseGuards(WsGuard) export class GameGateway { constructor( private gameService: GameService, @@ -48,13 +47,15 @@ export class GameGateway { afterInit(server: Server) { this.webSocketProvider.setServer(server); } - async handleConnection(socket: Socket & { jwtPayload: JwtPayload }) { + async handleConnection( + socket: Socket & { jwtPayload: JwtPayload; gameId: string } + ) { try { - const cookies = await this.extractCookies(socket); - if (!cookies) return; - const { gameId, userId } = cookies; - if (!gameId) return; + await this.extractCookies(socket); + const userId = socket.data.jwtPayload?.sub; + const gameId = socket.gameId; if (!userId) return; + if (!gameId) return; socket.join(userId); const game = await this.gameService.getGame(gameId); if (game.status !== 'ACTIVE') return; @@ -82,7 +83,7 @@ export class GameGateway { } } - private async extractCookies(socket: Socket & { jwtPayload: JwtPayload }) { + private async extractCookies(socket: Socket) { const cookies = socket.handshake.headers.cookie ? parse(socket.handshake.headers.cookie) : null; @@ -93,7 +94,9 @@ export class GameGateway { const decoded = await this.jwtService.verify(access_token, { publicKey: this.configService.get('ACCESS_TOKEN_PUB_KEY'), }); - return { gameId, userId: decoded.sub }; + socket['jwtPayload'] = decoded; + socket['gameId'] = gameId; + socket.data.jwtPayload = decoded; } private rejoinGame(socket: Socket, gameId: string) { @@ -127,24 +130,23 @@ export class GameGateway { .emit('gameData', { game, fields, auction, secretInfo }); } + @UseGuards(WsGuard) @SubscribeMessage('createGame') - async createGame(socket: Socket & { jwtPayload: JwtPayload }) { + async createGame(socket: Socket) { const createdGameWithPlayer = await this.gameService.createGame( - socket.jwtPayload.sub + socket.data.jwtPayload.sub ); socket.join(createdGameWithPlayer.id); return this.server.emit('newGameCreated', createdGameWithPlayer); } + @UseGuards(WsGuard) @SubscribeMessage('joinGame') - async onJoinGame( - socket: Socket & { jwtPayload: JwtPayload }, - dataArray: [{ id: string }, null] - ) { + async onJoinGame(socket: Socket, dataArray: [{ id: string }, null]) { const data = dataArray[0]; const game = await this.gameService.joinGame( data.id, - socket.jwtPayload.sub + socket.data.jwtPayload.sub ); this.leaveAllRoomsExceptInitial(socket); socket.join(data.id); @@ -155,15 +157,16 @@ export class GameGateway { this.server.emit('onParticipateGame', game); } + @UseGuards(WsGuard) @SubscribeMessage('leaveGame') async onLeaveGame( - socket: Socket & { jwtPayload: JwtPayload }, + socket: Socket & { jwtPayload: JwtPayload; user: JwtPayload }, dataArray: [{ id: string }, null] ) { const data = dataArray[0]; const game = await this.gameService.leaveGame( data.id, - socket.jwtPayload.sub + socket.data.jwtPayload.sub ); if (game) { this.leaveAllRoomsExceptInitial(socket); @@ -190,7 +193,7 @@ export class GameGateway { socket: Socket & { jwtPayload: JwtPayload; game: Partial } ) { await this.gameService.payToBankForSpecialField( - socket.jwtPayload.sub, + socket.data.jwtPayload.sub, socket.game ); } @@ -202,7 +205,7 @@ export class GameGateway { socket: Socket & { jwtPayload: JwtPayload; game: Partial } ) { const game = socket.game; - const userId = socket.jwtPayload.sub; + const userId = socket.data.jwtPayload.sub; const { game: updatedGame, secretInfo } = await this.gameService.payToUserForSecret(game, userId); this.server.to(game.id).emit('updatePlayers', { @@ -218,7 +221,7 @@ export class GameGateway { socket: Socket & { jwtPayload: JwtPayload; game: Partial } ) { const game = socket.game; - const userId = socket.jwtPayload.sub; + const userId = socket.data.jwtPayload.sub; await this.gameService.payToBankForSecret(game, userId); } @@ -248,7 +251,7 @@ export class GameGateway { @MessageBody() dataArray: [{ raiseBy: number; bidAmount: number }, null] ) { const data = dataArray[0]; - const userId = socket.jwtPayload.sub; + const userId = socket.data.jwtPayload.sub; await this.auctionService.raisePrice( gameId, userId, @@ -263,7 +266,7 @@ export class GameGateway { @ConnectedSocket() socket: Socket & { jwtPayload: JwtPayload }, @GetGameId() gameId: string ) { - const userId = socket.jwtPayload.sub; + const userId = socket.data.jwtPayload.sub; await this.auctionService.refuseAuction(gameId, userId); } @@ -295,7 +298,7 @@ export class GameGateway { const data = dataArray[0]; const index = data.index; const game = socket.game; - const userId = socket.jwtPayload.sub; + const userId = socket.data.jwtPayload.sub; const { updatedGame, fields } = await this.gameService.buyBranch( game, index, @@ -316,7 +319,7 @@ export class GameGateway { const data = dataArray[0]; const index = data.index; const game = socket.game; - const userId = socket.jwtPayload.sub; + const userId = socket.data.jwtPayload.sub; const { updatedGame, fields } = await this.gameService.sellBranch( game, index, @@ -333,7 +336,7 @@ export class GameGateway { @ConnectedSocket() socket: Socket & { game: Partial; jwtPayload: JwtPayload } ) { - const userId = socket.jwtPayload.sub; + const userId = socket.data.jwtPayload.sub; const gameId = socket.game.id; const { updatedPlayer, updatedFields } = await this.gameService.loseGame( @@ -360,7 +363,7 @@ export class GameGateway { const { player, fields } = await this.gameService.mortgageField( game, index, - socket.jwtPayload.sub + socket.data.jwtPayload.sub ); this.server .to(game.id) diff --git a/src/game/game.service.ts b/src/game/game.service.ts index d723fb3..1e50032 100644 --- a/src/game/game.service.ts +++ b/src/game/game.service.ts @@ -248,7 +248,7 @@ export class GameService { const player = await this.playerService.findFirst({ where: { userId, gameId }, }); - if (!player) return null; + if (!player) throw new WsException('You are not in this game'); await this.playerService.deleteById(player.id); diff --git a/src/player/player.gateway.ts b/src/player/player.gateway.ts index 5f6a0c6..6e7d4e2 100644 --- a/src/player/player.gateway.ts +++ b/src/player/player.gateway.ts @@ -8,7 +8,6 @@ import { } from '@nestjs/websockets'; import { Server, Socket } from 'socket.io'; import { TurnGuard, ValidPlayerGuard } from 'src/auth/guard'; -import { WsGuard } from 'src/auth/guard/jwt.ws.guard'; import { GamePayload } from 'src/game/game.repository'; import { WsValidationPipe } from 'src/pipes/wsValidation.pipe'; import { WebsocketExceptionsFilter } from 'src/utils/exceptions/websocket-exceptions.filter'; @@ -27,13 +26,12 @@ import { JwtPayload } from 'src/auth/types/jwtPayloadType.type'; }) @UseFilters(WebsocketExceptionsFilter) @UsePipes(new WsValidationPipe()) -@UseGuards(WsGuard, ValidPlayerGuard) export class PlayerGateway { constructor(private readonly playerService: PlayerService) {} @WebSocketServer() private server: Server; - @UseGuards(TurnGuard, ValidPlayerGuard) + @UseGuards(ValidPlayerGuard, TurnGuard) @SubscribeMessage('unmortgageField') async onUnmortgageField( @ConnectedSocket() @@ -43,7 +41,7 @@ export class PlayerGateway { const data = dataArray[0]; const { index } = data; const game = socket.game; - const userId = socket.jwtPayload.sub; + const userId = socket.data.jwtPayload.sub; const { player, fields } = await this.playerService.unmortgageField( game, index, diff --git a/src/trade/trade.gateway.ts b/src/trade/trade.gateway.ts index 586da95..aff502f 100644 --- a/src/trade/trade.gateway.ts +++ b/src/trade/trade.gateway.ts @@ -13,7 +13,6 @@ import { GamePayload } from 'src/game/game.repository'; import { OfferTradeDto } from './dto/offer-trade.dto'; import { WebsocketExceptionsFilter } from 'src/utils/exceptions/websocket-exceptions.filter'; import { WsValidationPipe } from 'src/pipes/wsValidation.pipe'; -import { WsGuard } from 'src/auth/guard/jwt.ws.guard'; import { AuctionService } from 'src/auction/auction.service'; import { TradeService } from './trade.service'; import { WebSocketProvider } from 'src/webSocketProvider/webSocketProvider.service'; @@ -32,7 +31,6 @@ import { Trade } from 'src/game/types/trade.type'; }) @UseFilters(WebsocketExceptionsFilter) @UsePipes(new WsValidationPipe()) -@UseGuards(WsGuard, ValidPlayerGuard) export class TradeGateway { constructor( private auctionService: AuctionService, @@ -40,7 +38,7 @@ export class TradeGateway { private webSocketProvider: WebSocketProvider, private chatService: ChatService ) {} - @UseGuards(TurnGuard) + @UseGuards(ValidPlayerGuard, TurnGuard) @SubscribeMessage('offerTrade') async offerTrade( @ConnectedSocket() @@ -50,7 +48,7 @@ export class TradeGateway { ) { const data = dataArray[0]; const game = socket.game; - const userId = socket.jwtPayload.sub; + const userId = socket.data.jwtPayload.sub; if (this.auctionService.getAuction(game.id)) throw new WsException('Cannot offer trade while auction'); await this.tradeService.validateTradeData(game, data); @@ -70,6 +68,7 @@ export class TradeGateway { this.tradeService.handleOfferTrade({ game, trade }); } + @UseGuards(ValidPlayerGuard) @SubscribeMessage('refuseFromTrade') async refuseFromTrade( @ConnectedSocket() @@ -79,6 +78,7 @@ export class TradeGateway { this.tradeService.refuseFromTrade(game); } + @UseGuards(ValidPlayerGuard) @SubscribeMessage('acceptTrade') async acceptTrade( @ConnectedSocket() @@ -89,7 +89,7 @@ export class TradeGateway { const { updatedGame, fields } = await this.tradeService.acceptTrade( game, trade, - socket.jwtPayload.sub + socket.data.jwtPayload.sub ); const data = { fields }; if (updatedGame) { From 725c4a5ba42890d9a1e5df06db2b0b83270498a1 Mon Sep 17 00:00:00 2001 From: Taras Yurchenko Date: Fri, 6 Jun 2025 23:36:05 +0300 Subject: [PATCH 16/27] refactor: moved adding of cookies logic to the interceptor --- src/auth/auth.controller.ts | 99 +++++-------------- src/auth/auth.service.ts | 10 +- src/auth/guard/jwt.ws.guard.ts | 4 +- src/auth/guard/validPlayer.guard.ts | 4 +- .../interceptor/authCookies.interceptor.ts | 57 +++++++++++ src/auth/strategy/jwt.at.strategy.ts | 2 +- src/auth/strategy/jwt.rt.strategy.ts | 4 +- src/auth/types/cookiesAttributes.type.ts | 17 ---- src/auth/types/index.ts | 1 - src/auth/types/tokens.types.ts | 4 +- src/game/game.gateway.ts | 4 +- 11 files changed, 99 insertions(+), 107 deletions(-) create mode 100644 src/auth/interceptor/authCookies.interceptor.ts delete mode 100644 src/auth/types/cookiesAttributes.type.ts diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts index 28f63fe..d82800f 100644 --- a/src/auth/auth.controller.ts +++ b/src/auth/auth.controller.ts @@ -9,6 +9,7 @@ import { Post, Res, UseGuards, + UseInterceptors, } from '@nestjs/common'; import { Response } from 'express'; import { AuthService } from './auth.service'; @@ -24,64 +25,14 @@ import { GetCurrentUser } from './decorator/get-current-user.decorator'; import { GetCurrentUserId } from './decorator/get-current-user-id.decorator'; import { Public } from './decorator/public.decorator'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; -import { AccessCookieAttributes, RefreshCookieAttributes } from './types'; -import { JwtGuard, JwtRtGuard } from './guard'; import { GetUser } from './decorator'; import { JwtPayloadWithRt } from './types/jwtPayloadWithRt.type'; +import { TokenCookieInterceptor } from './interceptor/authCookies.interceptor'; @ApiTags('auth') @Controller('auth') export class AuthController { constructor(private authService: AuthService) {} - static readonly ACCESS_COOKIES_ATTRIBUTES: AccessCookieAttributes = { - httpOnly: true, - path: '/', - maxAge: 1000 * 60 * 15, - sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax', - secure: process.env.NODE_ENV === 'production', - ...(process.env.NODE_ENV === 'production' && { - domain: `.${process.env.DOMAIN_NAME_PROD}`, - }), - }; - - static readonly REFRESH_COOKIES_ATTRIBUTES: RefreshCookieAttributes = { - httpOnly: true, - path: '/auth/refresh', - maxAge: 1000 * 60 * 60 * 24 * 7, - sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax', - secure: process.env.NODE_ENV === 'production', - ...(process.env.NODE_ENV === 'production' && { - domain: `.${process.env.DOMAIN_NAME_PROD}`, - }), - }; - - setCookie( - res: Response, - name: string, - value: string, - options: Record - ) { - const cookieOptions = { - ...options, - }; - res.cookie(name, value, cookieOptions); - } - - setTokens(res: Response, accessToken: string, refreshToken: string) { - this.setCookie( - res, - 'access_token', - accessToken, - AuthController.ACCESS_COOKIES_ATTRIBUTES - ); - this.setCookie( - res, - 'refresh_token', - refreshToken, - AuthController.REFRESH_COOKIES_ATTRIBUTES - ); - } - @Public() @Post('/local/signup') @ApiOperation({ summary: 'signup' }) @@ -93,14 +44,16 @@ export class AuthController { @Public() @Post('/local/signin') + @UseInterceptors(TokenCookieInterceptor) @ApiOperation({ summary: 'signin' }) - async signin(@Body() dto: SignInDto, @Res() res: Response) { + async signin(@Body() dto: SignInDto) { const { tokens, user } = await this.authService.signin(dto); - const { access_token: accessToken, refresh_token: refreshToken } = tokens; - this.setTokens(res, accessToken, refreshToken); - return res - .status(HttpStatus.OK) - .send({ status: 'success', message: 'Logged in successfully', user }); + return { + status: 'success', + message: 'Logged in successfully', + user, + ...tokens, + }; } @Get('local/me') @@ -121,34 +74,34 @@ export class AuthController { @UseGuards(AuthGuard('jwt-refresh')) @Post('refresh') @ApiOperation({ summary: 'refreshTokens' }) + @UseInterceptors(TokenCookieInterceptor) async refreshTokens( @GetCurrentUserId() userId: string, - @GetCurrentUser('refreshToken') refreshToken: string, - @Res() res: Response + @GetCurrentUser('refreshToken') refreshTokenOld: string ) { - const { access_token: accessToken, refresh_token: refreshTokenNew } = - await this.authService.refreshTokens(userId, refreshToken); - - this.setTokens(res, accessToken, refreshTokenNew); - - return res - .status(HttpStatus.OK) - .send({ status: 'success', message: 'Refreshed token successfully' }); + const tokens = await this.authService.refreshTokens( + userId, + refreshTokenOld + ); + return { + status: 'success', + message: 'Refreshed token successfully', + ...tokens, + }; } @Public() @Get('confirm-email/:token') @ApiOperation({ summary: 'confirm-email' }) - async confirmEmail(@Param('token') token: string, @Res() res: Response) { + @UseInterceptors(TokenCookieInterceptor) + async confirmEmail(@Param('token') token: string) { const { tokens, user } = await this.authService.confirmEmail(token); - const { access_token: accessToken, refresh_token: refreshToken } = tokens; - this.setTokens(res, accessToken, refreshToken); - - return res.status(HttpStatus.OK).send({ + return { status: 'success', message: 'Confirmed email successfully', user, - }); + ...tokens, + }; } @Patch('change-password') diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index 5b29e14..bf384c6 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -81,7 +81,7 @@ export class AuthService { throw new ForbiddenException('Password is incorrect'); } const tokens = await this.signTokens(user.id, user.email); - await this.updateRtHash(user.id, tokens.refresh_token); + await this.updateRtHash(user.id, tokens.refreshToken); return { tokens, user: { nickname: user.nickname, email: user.email, id: user.id }, @@ -123,7 +123,7 @@ export class AuthService { if (!rtMatches) throw new ForbiddenException('Access Denied'); const tokens = await this.signTokens(user.id, user.email); - await this.updateRtHash(user.id, tokens.refresh_token); + await this.updateRtHash(user.id, tokens.refreshToken); return tokens; } @@ -149,8 +149,8 @@ export class AuthService { ]); return { - access_token: at_token, - refresh_token: rt_token, + accessToken: at_token, + refreshToken: rt_token, }; } @@ -169,7 +169,7 @@ export class AuthService { }); const tokens = await this.signTokens(user.id, user.email); - await this.updateRtHash(user.id, tokens.refresh_token); + await this.updateRtHash(user.id, tokens.refreshToken); return { tokens, user: { nickname: user.nickname, email: user.email } }; } diff --git a/src/auth/guard/jwt.ws.guard.ts b/src/auth/guard/jwt.ws.guard.ts index 797c647..698ad8f 100644 --- a/src/auth/guard/jwt.ws.guard.ts +++ b/src/auth/guard/jwt.ws.guard.ts @@ -20,9 +20,9 @@ export class WsGuard implements CanActivate { if (userId) { return true; } - const { access_token } = parse(client.handshake.headers.cookie); + const { accessToken } = parse(client.handshake.headers.cookie); try { - const decoded = await this.jwtService.verify(access_token, { + const decoded = await this.jwtService.verify(accessToken, { publicKey: this.configService.get('ACCESS_TOKEN_PUB_KEY'), }); client.data.jwtPayload = decoded; diff --git a/src/auth/guard/validPlayer.guard.ts b/src/auth/guard/validPlayer.guard.ts index 3a718ac..bb40483 100644 --- a/src/auth/guard/validPlayer.guard.ts +++ b/src/auth/guard/validPlayer.guard.ts @@ -20,9 +20,9 @@ export class ValidPlayerGuard implements CanActivate { if (!client.handshake.headers.cookie) { throw new WsException('Unauthorized'); } - const { access_token } = parse(client.handshake.headers.cookie); + const { accessToken } = parse(client.handshake.headers.cookie); try { - const decoded = await this.jwtService.verify(access_token, { + const decoded = await this.jwtService.verify(accessToken, { publicKey: this.configService.get('ACCESS_TOKEN_PUB_KEY'), }); client.data.jwtPayload = decoded; diff --git a/src/auth/interceptor/authCookies.interceptor.ts b/src/auth/interceptor/authCookies.interceptor.ts new file mode 100644 index 0000000..1bc602a --- /dev/null +++ b/src/auth/interceptor/authCookies.interceptor.ts @@ -0,0 +1,57 @@ +import { + Injectable, + NestInterceptor, + ExecutionContext, + CallHandler, +} from '@nestjs/common'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { Response } from 'express'; + +interface AuthResponse { + accessToken?: string; + refreshToken?: string; + [key: string]: any; +} + +@Injectable() +export class TokenCookieInterceptor implements NestInterceptor { + intercept(context: ExecutionContext, next: CallHandler): Observable { + return next.handle().pipe( + map((data: AuthResponse) => { + const response = context.switchToHttp().getResponse(); + if (data?.accessToken || data?.refreshToken) { + if (data.accessToken) { + response.cookie('accessToken', data.accessToken, { + httpOnly: true, + path: '/', + maxAge: 1000 * 60 * 15, + sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax', + secure: process.env.NODE_ENV === 'production', + ...(process.env.NODE_ENV === 'production' && { + domain: `.${process.env.DOMAIN_NAME_PROD}`, + }), + }); + delete data.accessToken; + } + + if (data.refreshToken) { + response.cookie('refreshToken', data.refreshToken, { + httpOnly: true, + path: '/auth/refresh', + maxAge: 1000 * 60 * 60 * 24 * 7, + sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax', + secure: process.env.NODE_ENV === 'production', + ...(process.env.NODE_ENV === 'production' && { + domain: `.${process.env.DOMAIN_NAME_PROD}`, + }), + }); + delete data.refreshToken; + } + } + + return data; + }) + ); + } +} diff --git a/src/auth/strategy/jwt.at.strategy.ts b/src/auth/strategy/jwt.at.strategy.ts index b55a737..17f1825 100644 --- a/src/auth/strategy/jwt.at.strategy.ts +++ b/src/auth/strategy/jwt.at.strategy.ts @@ -11,7 +11,7 @@ export class JwtAtStrategy extends PassportStrategy(Strategy, 'jwt') { super({ jwtFromRequest: ExtractJwt.fromExtractors([ (request: Request) => { - const token = request.cookies['access_token']; + const token = request.cookies['accessToken']; return token; }, ]), diff --git a/src/auth/strategy/jwt.rt.strategy.ts b/src/auth/strategy/jwt.rt.strategy.ts index 979439a..b135750 100644 --- a/src/auth/strategy/jwt.rt.strategy.ts +++ b/src/auth/strategy/jwt.rt.strategy.ts @@ -12,7 +12,7 @@ export class JwtRtStrategy extends PassportStrategy(Strategy, 'jwt-refresh') { super({ jwtFromRequest: ExtractJwt.fromExtractors([ (request: Request) => { - const token = request.cookies['refresh_token']; + const token = request.cookies['refreshToken']; return token; }, ]), @@ -22,7 +22,7 @@ export class JwtRtStrategy extends PassportStrategy(Strategy, 'jwt-refresh') { }); } validate(req: Request, payload: JwtPayload): JwtPayloadWithRt { - const refreshToken = req.cookies['refresh_token']; + const refreshToken = req.cookies['refreshToken']; return { ...payload, refreshToken, diff --git a/src/auth/types/cookiesAttributes.type.ts b/src/auth/types/cookiesAttributes.type.ts deleted file mode 100644 index 68d78ae..0000000 --- a/src/auth/types/cookiesAttributes.type.ts +++ /dev/null @@ -1,17 +0,0 @@ -export type AccessCookieAttributes = { - httpOnly: boolean; - sameSite: 'lax' | 'strict' | 'none'; - secure?: boolean; - domain: string; - path: string; - maxAge: number; -}; - -export type RefreshCookieAttributes = { - httpOnly: boolean; - secure?: boolean; - maxAge: number; - sameSite: 'lax' | 'strict' | 'none'; - path: string; - domain: string; -}; diff --git a/src/auth/types/index.ts b/src/auth/types/index.ts index ff69d7f..45bba05 100644 --- a/src/auth/types/index.ts +++ b/src/auth/types/index.ts @@ -1,2 +1 @@ export * from './tokens.types'; -export * from './cookiesAttributes.type'; diff --git a/src/auth/types/tokens.types.ts b/src/auth/types/tokens.types.ts index 1c0a510..bef8723 100644 --- a/src/auth/types/tokens.types.ts +++ b/src/auth/types/tokens.types.ts @@ -1,4 +1,4 @@ export type Tokens = { - access_token: string; - refresh_token: string; + accessToken: string; + refreshToken: string; }; diff --git a/src/game/game.gateway.ts b/src/game/game.gateway.ts index df479e6..3bf0954 100644 --- a/src/game/game.gateway.ts +++ b/src/game/game.gateway.ts @@ -90,8 +90,8 @@ export class GameGateway { if (!cookies?.gameId) return null; - const { gameId, access_token } = cookies; - const decoded = await this.jwtService.verify(access_token, { + const { gameId, accessToken } = cookies; + const decoded = await this.jwtService.verify(accessToken, { publicKey: this.configService.get('ACCESS_TOKEN_PUB_KEY'), }); socket['jwtPayload'] = decoded; From 718b1b71d5d2b98e3f9ecf681d1f1ed11dd1d765 Mon Sep 17 00:00:00 2001 From: Taras Yurchenko Date: Mon, 1 Sep 2025 17:46:46 +0300 Subject: [PATCH 17/27] feat: changed package-lock --- package-lock.json | 3597 ++++++++++++++++++++++++++++++--------------- 1 file changed, 2425 insertions(+), 1172 deletions(-) diff --git a/package-lock.json b/package-lock.json index d0500c7..851f62c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -77,9 +77,9 @@ } }, "node_modules/@angular-devkit/core": { - "version": "17.3.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.3.8.tgz", - "integrity": "sha512-Q8q0voCGudbdCgJ7lXdnyaxKHbNQBARH68zPQV72WT8NWy+Gw/tys870i6L58NWbBaCJEUcIj/kb6KoakSRu+Q==", + "version": "17.3.11", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.3.11.tgz", + "integrity": "sha512-vTNDYNsLIWpYk2I969LMQFH29GTsLzxNk/0cLw5q56ARF0v5sIWfHYwGTS88jdDqIpuuettcSczbxeA7EuAmqQ==", "dev": true, "dependencies": { "ajv": "8.12.0", @@ -103,13 +103,22 @@ } } }, + "node_modules/@angular-devkit/core/node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/@angular-devkit/schematics": { - "version": "17.3.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-17.3.8.tgz", - "integrity": "sha512-QRVEYpIfgkprNHc916JlPuNbLzOgrm9DZalHasnLUz4P6g7pR21olb8YCyM2OTJjombNhya9ZpckcADU5Qyvlg==", + "version": "17.3.11", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-17.3.11.tgz", + "integrity": "sha512-I5wviiIqiFwar9Pdk30Lujk8FczEEc18i22A5c6Z9lbmhPQdTroDnEQdsfXjy404wPe8H62s0I15o4pmMGfTYQ==", "dev": true, "dependencies": { - "@angular-devkit/core": "17.3.8", + "@angular-devkit/core": "17.3.11", "jsonc-parser": "3.2.1", "magic-string": "0.30.8", "ora": "5.4.1", @@ -143,51 +152,6 @@ "yarn": ">= 1.13.0" } }, - "node_modules/@angular-devkit/schematics-cli/node_modules/@angular-devkit/core": { - "version": "17.3.11", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.3.11.tgz", - "integrity": "sha512-vTNDYNsLIWpYk2I969LMQFH29GTsLzxNk/0cLw5q56ARF0v5sIWfHYwGTS88jdDqIpuuettcSczbxeA7EuAmqQ==", - "dev": true, - "dependencies": { - "ajv": "8.12.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.2.1", - "picomatch": "4.0.1", - "rxjs": "7.8.1", - "source-map": "0.7.4" - }, - "engines": { - "node": "^18.13.0 || >=20.9.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^3.5.2" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } - } - }, - "node_modules/@angular-devkit/schematics-cli/node_modules/@angular-devkit/schematics": { - "version": "17.3.11", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-17.3.11.tgz", - "integrity": "sha512-I5wviiIqiFwar9Pdk30Lujk8FczEEc18i22A5c6Z9lbmhPQdTroDnEQdsfXjy404wPe8H62s0I15o4pmMGfTYQ==", - "dev": true, - "dependencies": { - "@angular-devkit/core": "17.3.11", - "jsonc-parser": "3.2.1", - "magic-string": "0.30.8", - "ora": "5.4.1", - "rxjs": "7.8.1" - }, - "engines": { - "node": "^18.13.0 || >=20.9.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, "node_modules/@angular-devkit/schematics-cli/node_modules/chalk": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", @@ -253,6 +217,15 @@ "node": ">=0.12.0" } }, + "node_modules/@angular-devkit/schematics/node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", @@ -268,30 +241,30 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz", - "integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==", + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.5.tgz", + "integrity": "sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", - "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.4.tgz", + "integrity": "sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.0", - "@babel/helper-compilation-targets": "^7.25.2", - "@babel/helper-module-transforms": "^7.25.2", - "@babel/helpers": "^7.25.0", - "@babel/parser": "^7.25.0", - "@babel/template": "^7.25.0", - "@babel/traverse": "^7.25.2", - "@babel/types": "^7.25.2", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.4", + "@babel/parser": "^7.27.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.27.4", + "@babel/types": "^7.27.3", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -316,29 +289,30 @@ } }, "node_modules/@babel/generator": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz", - "integrity": "sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==", + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.5.tgz", + "integrity": "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==", "dev": true, "dependencies": { - "@babel/types": "^7.25.6", + "@babel/parser": "^7.27.5", + "@babel/types": "^7.27.3", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" + "jsesc": "^3.0.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", - "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.25.2", - "@babel/helper-validator-option": "^7.24.8", - "browserslist": "^4.23.1", + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -356,28 +330,27 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", - "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", "dev": true, "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", - "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", + "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7", - "@babel/traverse": "^7.25.2" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.3" }, "engines": { "node": ">=6.9.0" @@ -387,23 +360,10 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", - "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", - "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", "dev": true, - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, "engines": { "node": ">=6.9.0" } @@ -425,31 +385,31 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", - "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.3.tgz", - "integrity": "sha512-h/eKy9agOya1IGuLaZ9tEUgz+uIRXcbtOhRtUyyMf8JFmn1iT13vnl/IGVWSkdOCG/pC57U4S1jnAabAavTMwg==", + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", + "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", "dev": true, "dependencies": { "@babel/template": "^7.27.2", - "@babel/types": "^7.27.3" + "@babel/types": "^7.27.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.3.tgz", - "integrity": "sha512-xyYxRj6+tLNDTWi0KCBcZ9V7yg3/lwL9DWh9Uwh/RIVlIfFidggcgxKX3GCXwCiswwcGRawBKbEg2LG/Y8eJhw==", + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz", + "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==", "dependencies": { "@babel/types": "^7.27.3" }, @@ -512,12 +472,12 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.25.6.tgz", - "integrity": "sha512-sXaDXaJN9SNLymBdlWFA+bjzBhFD617ZaFiY13dGt7TVslVvVgA6fkZOP7Ki3IGElC45lwHdOTrCtKZGVAWeLQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -551,12 +511,12 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz", - "integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -668,12 +628,12 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.4.tgz", - "integrity": "sha512-uMOCoHVU52BsSWxPOMVv5qKRdeSlPuImUCB2dlPuBSU+W2/ROE7/Zg8F2Kepbk+8yBa68LlRKxO+xgEVWorsDg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -682,6 +642,14 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", + "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.27.2", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", @@ -697,16 +665,16 @@ } }, "node_modules/@babel/traverse": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.6.tgz", - "integrity": "sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==", + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.4.tgz", + "integrity": "sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.6", - "@babel/parser": "^7.25.6", - "@babel/template": "^7.25.0", - "@babel/types": "^7.25.6", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.3", + "@babel/parser": "^7.27.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.3", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -724,9 +692,9 @@ } }, "node_modules/@babel/types": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.3.tgz", - "integrity": "sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw==", + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz", + "integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==", "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" @@ -773,25 +741,166 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@css-inline/css-inline": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline/-/css-inline-0.13.0.tgz", + "integrity": "sha512-ZozAXBiW1I8hf6eW5eTNqhxUdNOBxrNNxxUnQRiKQpWcs5ORuGaiWwV5focMBTJ5WXGN+Z8VLP93BOwWFPzCJw==", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@css-inline/css-inline-darwin-arm64": "0.13.0", + "@css-inline/css-inline-darwin-x64": "0.13.0", + "@css-inline/css-inline-linux-arm-gnueabihf": "0.13.0", + "@css-inline/css-inline-linux-arm64-gnu": "0.13.0", + "@css-inline/css-inline-linux-arm64-musl": "0.13.0", + "@css-inline/css-inline-linux-x64-gnu": "0.13.0", + "@css-inline/css-inline-linux-x64-musl": "0.13.0", + "@css-inline/css-inline-win32-x64-msvc": "0.13.0" + } + }, + "node_modules/@css-inline/css-inline-darwin-arm64": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-darwin-arm64/-/css-inline-darwin-arm64-0.13.0.tgz", + "integrity": "sha512-A4QvlZdhp8v+3IHKF/UftRf5GrAVUMEHCGRuk2Dx594xn/UR4ieh+B70aMm5rfONh2hv5mlR9UcoYAkVpEQ99g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@css-inline/css-inline-darwin-x64": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-darwin-x64/-/css-inline-darwin-x64-0.13.0.tgz", + "integrity": "sha512-px9z4ypzeECMyBEtlrNzTMpA1tnw5MmMIiMkBRhb8UGRr2pOBZY3yd/eEIxWzVVSPt0aIjVDwUOJ3+d0Z+BskA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@css-inline/css-inline-linux-arm-gnueabihf": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-arm-gnueabihf/-/css-inline-linux-arm-gnueabihf-0.13.0.tgz", + "integrity": "sha512-+uo0coLQNgk/AKeOB8mXSRd8VIlUg38zRSB9B9q0ior9oBCDPtEdn1HuCSvWxHoOSJ8QNNk+uwbz0zW4CETzFw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@css-inline/css-inline-linux-arm64-gnu": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-arm64-gnu/-/css-inline-linux-arm64-gnu-0.13.0.tgz", + "integrity": "sha512-GVrsFbY5l0Hxyzxsm5S5JPGObvHm/Ybf2wZgnWBsQigxqGtr1FL535HaTwEnq6aHOpH3f08gR5Vx33gB7jG4pw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@css-inline/css-inline-linux-arm64-musl": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-arm64-musl/-/css-inline-linux-arm64-musl-0.13.0.tgz", + "integrity": "sha512-V5h5+CRnE01EgoafI/kyjEcM8zvN+sKLnp17Aq9LqQfsut7mO3i72d8g/xeVC37DCLoGQFLvDCzbze2NbF2dIQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@css-inline/css-inline-linux-x64-gnu": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-x64-gnu/-/css-inline-linux-x64-gnu-0.13.0.tgz", + "integrity": "sha512-vbRV++73MW7dvz/AIbozkv4R68/k/sEp57hno/L6lx034VYxpCwdfqtGN4D0W1TOTzdr2b6qBOGNZ1oLKQZOQQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@css-inline/css-inline-linux-x64-musl": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-x64-musl/-/css-inline-linux-x64-musl-0.13.0.tgz", + "integrity": "sha512-2tCnwU23W/yMs9cGc2/i2jd9y2pjuntx0a5OytqX7s9fvUtmI3nc0Od6wuf51LnmdU+XAU8HLT9pZppsQiwPfQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@css-inline/css-inline-win32-x64-msvc": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-win32-x64-msvc/-/css-inline-win32-x64-msvc-0.13.0.tgz", + "integrity": "sha512-6VFhFSXp4FH+NzJhLd6fFi7jKCPvIRW+vq0tV+CPuiQ3zPzMfC9nIk8sB/1VJR8EcvBAjMV53YnacuDjRFRT9g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", "dev": true, "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "node_modules/@eslint-community/regexpp": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", - "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" @@ -934,7 +1043,6 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -951,7 +1059,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, "engines": { "node": ">=12" }, @@ -963,7 +1070,6 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, "engines": { "node": ">=12" }, @@ -974,14 +1080,12 @@ "node_modules/@isaacs/cliui/node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -998,7 +1102,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -1013,7 +1116,6 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -1455,15 +1557,10 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jonkemp/package-utils": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@jonkemp/package-utils/-/package-utils-1.0.8.tgz", - "integrity": "sha512-bIcKnH5YmtTYr7S6J3J86dn/rFiklwRpOqbTOQ9C0WMmR9FKHVb3bxs2UYfqEmNb93O4nbA97sb6rtz33i9SyA==" - }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", "dev": true, "dependencies": { "@jridgewell/set-array": "^1.2.1", @@ -1539,83 +1636,44 @@ } }, "node_modules/@microsoft/tsdoc": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.0.tgz", - "integrity": "sha512-HZpPoABogPvjeJOdzCOSJsXeL/SMCBgBZMVC3X3d7YYp2gf31MfxhUoYUNwf1ERPJOnQc0wkFn9trqI6ZEdZuA==" + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.1.tgz", + "integrity": "sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==" }, "node_modules/@mongodb-js/saslprep": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.2.0.tgz", - "integrity": "sha512-+ywrb0AqkfaYuhHs6LxKWgqbh3I72EpEgESCw37o+9qPx9WTCkgDm2B+eMrwehGtHBWHFU4GXvnSCNiFhhausg==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.2.2.tgz", + "integrity": "sha512-EB0O3SCSNRUFk66iRCpI+cXzIjdswfCs7F6nOC3RAGJ7xr5YhaicvsRwJ9eyzYvYRlCSDUO/c7g4yNulxKC1WA==", "dependencies": { "sparse-bitfield": "^3.0.3" } }, "node_modules/@nestjs-modules/mailer": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@nestjs-modules/mailer/-/mailer-1.6.1.tgz", - "integrity": "sha512-ZfJFB3sIQyYr52g7HRUr3VOyZZSPBI+89hcm3hvLvP3flTJs/g98QbZxWR/mH2BzQVHdCAzlP98Qw/KQULPM4w==", + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@nestjs-modules/mailer/-/mailer-1.11.2.tgz", + "integrity": "sha512-k07wyKbtCzxWMm6IqGwcGIisnXD/6sneGvUR8rBBZbxtLn1HE1FLGyiaXBrPui/0K7W41aS9x9jAIhfTawtlUg==", "dependencies": { - "glob": "7.1.7", - "inline-css": "3.0.0", - "preview-email": "3.0.4" + "@css-inline/css-inline": "0.13.0", + "glob": "10.3.10", + "mjml": "4.15.3", + "preview-email": "3.0.19" }, "optionalDependencies": { - "@types/ejs": "^3.0.3", - "@types/pug": "2.0.5", - "ejs": "^3.1.2", - "handlebars": "^4.7.6", - "pug": "^3.0.1" + "@types/ejs": "^3.1.5", + "@types/pug": "^2.0.10", + "ejs": "^3.1.9", + "handlebars": "^4.7.8", + "pug": "^3.0.2" }, "peerDependencies": { - "@nestjs/common": "^7.0.9 || ^8.0.0", - "@nestjs/core": "^7.0.9 || ^8.0.0", - "@types/ejs": "^3.0.3", - "@types/pug": "2.0.5", - "ejs": "^3.1.2", - "handlebars": "^4.7.6", - "nodemailer": "^6.4.6", - "pug": "^2.0.4" - } - }, - "node_modules/@nestjs-modules/mailer/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@nestjs-modules/mailer/node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@nestjs-modules/mailer/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" + "@nestjs/common": ">=7.0.9", + "@nestjs/core": ">=7.0.9", + "@types/ejs": ">=3.0.3", + "@types/pug": ">=2.0.6", + "ejs": ">=3.1.2", + "handlebars": ">=4.7.6", + "nodemailer": ">=6.4.6", + "pug": ">=3.0.1" } }, "node_modules/@nestjs/cli": { @@ -1663,58 +1721,130 @@ } } }, - "node_modules/@nestjs/cli/node_modules/@angular-devkit/core": { - "version": "17.3.11", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.3.11.tgz", - "integrity": "sha512-vTNDYNsLIWpYk2I969LMQFH29GTsLzxNk/0cLw5q56ARF0v5sIWfHYwGTS88jdDqIpuuettcSczbxeA7EuAmqQ==", + "node_modules/@nestjs/cli/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, "dependencies": { - "ajv": "8.12.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.2.1", - "picomatch": "4.0.1", - "rxjs": "7.8.1", - "source-map": "0.7.4" + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" }, "engines": { - "node": "^18.13.0 || >=20.9.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^3.5.2" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } + "node": ">=8.0.0" } }, - "node_modules/@nestjs/cli/node_modules/@angular-devkit/schematics": { - "version": "17.3.11", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-17.3.11.tgz", - "integrity": "sha512-I5wviiIqiFwar9Pdk30Lujk8FczEEc18i22A5c6Z9lbmhPQdTroDnEQdsfXjy404wPe8H62s0I15o4pmMGfTYQ==", + "node_modules/@nestjs/cli/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@nestjs/cli/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, "dependencies": { - "@angular-devkit/core": "17.3.11", - "jsonc-parser": "3.2.1", - "magic-string": "0.30.8", - "ora": "5.4.1", - "rxjs": "7.8.1" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@nestjs/cli/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/@nestjs/cli/node_modules/typescript": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": "^18.13.0 || >=20.9.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" + "node": ">=14.17" + } + }, + "node_modules/@nestjs/cli/node_modules/webpack": { + "version": "5.97.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz", + "integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.1", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } } }, "node_modules/@nestjs/common": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.4.3.tgz", - "integrity": "sha512-4hbLd3XIJubHSylYd/1WSi4VQvG68KM/ECYpMDqA3k3J1/T17SAg40sDoq3ZoO5OZgU0xuNyjuISdOTjs11qVg==", + "version": "10.4.19", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.4.19.tgz", + "integrity": "sha512-0TZJ8H+7qtaqZt6YfZJkDRp0e+v6jjo5/pevPAjUy0WYxaTy16bNNQxFPRKLMe/v1hUr2oGV9imvL2477zNt5g==", "dependencies": { + "file-type": "20.4.1", "iterare": "1.2.1", - "tslib": "2.7.0", + "tslib": "2.8.1", "uid": "2.0.2" }, "funding": { @@ -1737,9 +1867,9 @@ } }, "node_modules/@nestjs/config": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-3.2.3.tgz", - "integrity": "sha512-p6yv/CvoBewJ72mBq4NXgOAi2rSQNWx3a+IMJLVKS2uiwFCOQQuiIatGwq6MRjXV3Jr+B41iUO8FIf4xBrZ4/w==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-3.3.0.tgz", + "integrity": "sha512-pdGTp8m9d0ZCrjTpjkUbZx6gyf2IKf+7zlkrPNMsJzYZ4bFRRTpXrnj+556/5uiI6AfL5mMrJc2u7dB6bvM+VA==", "dependencies": { "dotenv": "16.4.5", "dotenv-expand": "10.0.0", @@ -1751,16 +1881,16 @@ } }, "node_modules/@nestjs/core": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.4.3.tgz", - "integrity": "sha512-6OQz+5C8mT8yRtfvE5pPCq+p6w5jDot+oQku1KzQ24ABn+lay1KGuJwcKZhdVNuselx+8xhdMxknZTA8wrGLIg==", + "version": "10.4.19", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.4.19.tgz", + "integrity": "sha512-gahghu0y4Rn4gn/xPjTgNHFMpUM8TxfhdeMowVWTGVnYMZtGeEGbIXMFhJS0Dce3E4VKyqAglzgO9ecAZd4Ong==", "hasInstallScript": true, "dependencies": { "@nuxtjs/opencollective": "0.3.2", "fast-safe-stringify": "2.1.1", "iterare": "1.2.1", "path-to-regexp": "3.3.0", - "tslib": "2.7.0", + "tslib": "2.8.1", "uid": "2.0.2" }, "funding": { @@ -1788,9 +1918,9 @@ } }, "node_modules/@nestjs/event-emitter": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@nestjs/event-emitter/-/event-emitter-3.0.0.tgz", - "integrity": "sha512-WbvzQQ9BGnj27onh2qSLND2+4iA6Pfp4K+HLlqunB0Uz0614O8lGMtcveSss2IOxsox8EhSI54WAvuAsDrX1hA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@nestjs/event-emitter/-/event-emitter-3.0.1.tgz", + "integrity": "sha512-0Ln/x+7xkU6AJFOcQI9tIhUMXVF7D5itiaQGOyJbXtlAfAIt8gzDdJm+Im7cFzKoWkiW5nCXCPh6GSvdQd/3Dw==", "dependencies": { "eventemitter2": "6.4.9" }, @@ -1812,11 +1942,11 @@ } }, "node_modules/@nestjs/mapped-types": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.5.tgz", - "integrity": "sha512-bSJv4pd6EY99NX9CjBIyn4TVDoSit82DUZlL4I3bqNfy5Gt+gXTa86i3I/i0iIV9P4hntcGM5GyO+FhZAhxtyg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.1.0.tgz", + "integrity": "sha512-W+n+rM69XsFdwORF11UqJahn4J3xi4g/ZEOlJNL6KoW5ygWSmBB2p0S2BZ4FQeS/NDH72e6xIcu35SfJnE8bXw==", "peerDependencies": { - "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "@nestjs/common": "^10.0.0 || ^11.0.0", "class-transformer": "^0.4.0 || ^0.5.0", "class-validator": "^0.13.0 || ^0.14.0", "reflect-metadata": "^0.1.12 || ^0.2.0" @@ -1831,9 +1961,9 @@ } }, "node_modules/@nestjs/mongoose": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/@nestjs/mongoose/-/mongoose-11.0.1.tgz", - "integrity": "sha512-Nsw/eW5ZptIgwv3gPumKe6UtZu/0HZqixwkkO8Hqn6wZyrLeaTpBFi/z3DGY36dp66uPw14huradDY7OTPDYJA==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@nestjs/mongoose/-/mongoose-11.0.3.tgz", + "integrity": "sha512-tg7bbKD4MnNMPaiDLXK/JUyTNQxIn3rNnI+oYU1HorLpNiR2E8vPraWVvfptpIj+zferpT6LkrHMvtqvuIKNPw==", "peerDependencies": { "@nestjs/common": "^10.0.0 || ^11.0.0", "@nestjs/core": "^10.0.0 || ^11.0.0", @@ -1851,14 +1981,14 @@ } }, "node_modules/@nestjs/platform-express": { - "version": "10.4.18", - "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.4.18.tgz", - "integrity": "sha512-v+W+Pu5NOVK/bSG5A5mOnXyoVwN5mJUe4o0j0UJ9Ig9JMmjVxg+Zw2ydTfpOQ+R82lRYWJUjjv3dvqKaFW2z7w==", + "version": "10.4.19", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.4.19.tgz", + "integrity": "sha512-IeQkBZUtPeJoO4E0QqSLwkB+60KcThw8/s4gGvAwIRJ5ViuXoxnwU59eBDy84PUuVbNe4VdKjfAF9fuQOEh11Q==", "dependencies": { "body-parser": "1.20.3", "cors": "2.8.5", "express": "4.21.2", - "multer": "2.0.0", + "multer": "2.0.1", "tslib": "2.8.1" }, "funding": { @@ -1870,15 +2000,10 @@ "@nestjs/core": "^10.0.0" } }, - "node_modules/@nestjs/platform-express/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" - }, "node_modules/@nestjs/platform-socket.io": { - "version": "10.4.18", - "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-10.4.18.tgz", - "integrity": "sha512-+X/c4I5rYJU/X5ZRhrd3jJ/bL86zWWTc6FIdhNfTvVyLneYkIH9+snCyi4+7lu0auhDXqA90zNzmaUg9Iwno1Q==", + "version": "10.4.19", + "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-10.4.19.tgz", + "integrity": "sha512-Pfz9dBcXaoUFdVpA/I96NoOTiTQ7eYfDNhQ2sZdQzne3oISEvts5G2SWyQNouoyjqkUPt6X5r/CQ++M4AzSlEQ==", "dependencies": { "socket.io": "4.8.1", "tslib": "2.8.1" @@ -1893,20 +2018,15 @@ "rxjs": "^7.1.0" } }, - "node_modules/@nestjs/platform-socket.io/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" - }, "node_modules/@nestjs/schematics": { - "version": "10.1.4", - "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-10.1.4.tgz", - "integrity": "sha512-QpY8ez9cTvXXPr3/KBrtSgXQHMSV6BkOUYy2c2TTe6cBqriEdGnCYqGl8cnfrQl3632q3lveQPaZ/c127dHsEw==", + "version": "10.2.3", + "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-10.2.3.tgz", + "integrity": "sha512-4e8gxaCk7DhBxVUly2PjYL4xC2ifDFexCqq1/u4TtivLGXotVk0wHdYuPYe1tHTHuR1lsOkRbfOCpkdTnigLVg==", "dev": true, "dependencies": { - "@angular-devkit/core": "17.3.8", - "@angular-devkit/schematics": "17.3.8", - "comment-json": "4.2.3", + "@angular-devkit/core": "17.3.11", + "@angular-devkit/schematics": "17.3.11", + "comment-json": "4.2.5", "jsonc-parser": "3.3.1", "pluralize": "8.0.0" }, @@ -1952,13 +2072,32 @@ } } }, + "node_modules/@nestjs/swagger/node_modules/@nestjs/mapped-types": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.5.tgz", + "integrity": "sha512-bSJv4pd6EY99NX9CjBIyn4TVDoSit82DUZlL4I3bqNfy5Gt+gXTa86i3I/i0iIV9P4hntcGM5GyO+FhZAhxtyg==", + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "class-transformer": "^0.4.0 || ^0.5.0", + "class-validator": "^0.13.0 || ^0.14.0", + "reflect-metadata": "^0.1.12 || ^0.2.0" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, "node_modules/@nestjs/testing": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.4.3.tgz", - "integrity": "sha512-SBNWrMU51YAlYmW86wyjlGZ2uLnASNiOPD0lBcNIlxxei0b05/aI3nh7OPuxbXQUdedUJfPq2d2jZj4TRG4S0w==", + "version": "10.4.19", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.4.19.tgz", + "integrity": "sha512-YfzkjTmwEcoWqo8xr8YiTZMC4FjBEOg4uRTAPI2p6iGLWu+27tYau1CtAKFHY0uSAK3FzgtsAuYoxBSlfr9mWA==", "dev": true, "dependencies": { - "tslib": "2.7.0" + "tslib": "2.8.1" }, "funding": { "type": "opencollective", @@ -1980,13 +2119,13 @@ } }, "node_modules/@nestjs/websockets": { - "version": "10.4.7", - "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-10.4.7.tgz", - "integrity": "sha512-ajuoptYLYm+l3+KtaA9Ed+cO9yB34PtBE8UObavRT8Euh/f7QfeJiKcrU3+BQSAiTWM3nF2qfuV4CfEkP9uKuw==", + "version": "10.4.19", + "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-10.4.19.tgz", + "integrity": "sha512-3HhNZU40/ozB64ZZJ1W1bOOYSbxGuJwmM5Ui8y1uPwbzpL1Uov3wOG3eRp1IflSBK4Ia0wb8Iv3ChpFSTzrNiA==", "dependencies": { "iterare": "1.2.1", "object-hash": "3.0.0", - "tslib": "2.7.0" + "tslib": "2.8.1" }, "peerDependencies": { "@nestjs/common": "^10.0.0", @@ -2065,6 +2204,11 @@ "npm": ">=5.0.0" } }, + "node_modules/@one-ini/wasm": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", + "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==" + }, "node_modules/@paralleldrive/cuid2": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz", @@ -2086,28 +2230,27 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, "optional": true, "engines": { "node": ">=14" } }, "node_modules/@pkgr/core": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", - "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.7.tgz", + "integrity": "sha512-YLT9Zo3oNPJoBjBc4q8G2mjU4tqIbf5CEOORbUUr48dCD9q3umJ3IPlVqOqDakPfd2HuwccBaqlGhN4Gmr5OWg==", "dev": true, "engines": { "node": "^12.20.0 || ^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/unts" + "url": "https://opencollective.com/pkgr" } }, "node_modules/@prisma/client": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.20.0.tgz", - "integrity": "sha512-CLv55ZuMuUawMsxoqxGtLT3bEZoa2W8L3Qnp6rDIFWy+ZBrUcOFKdoeGPSnbBqxc3SkdxJrF+D1veN/WNynZYA==", + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.22.0.tgz", + "integrity": "sha512-M0SVXfyHnQREBKxCgyo7sffrKttwE6R8PMq330MIUF0pTwjUhLbW84pFDlf06B27XyCR++VtjugEnIHdr07SVA==", "hasInstallScript": true, "engines": { "node": ">=16.13" @@ -2122,43 +2265,43 @@ } }, "node_modules/@prisma/debug": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.20.0.tgz", - "integrity": "sha512-oCx79MJ4HSujokA8S1g0xgZUGybD4SyIOydoHMngFYiwEwYDQ5tBQkK5XoEHuwOYDKUOKRn/J0MEymckc4IgsQ==" + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.22.0.tgz", + "integrity": "sha512-AUt44v3YJeggO2ZU5BkXI7M4hu9BF2zzH2iF2V5pyXT/lRTyWiElZ7It+bRH1EshoMRxHgpYg4VB6rCM+mG5jQ==" }, "node_modules/@prisma/engines": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.20.0.tgz", - "integrity": "sha512-DtqkP+hcZvPEbj8t8dK5df2b7d3B8GNauKqaddRRqQBBlgkbdhJkxhoJTrOowlS3vaRt2iMCkU0+CSNn0KhqAQ==", + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.22.0.tgz", + "integrity": "sha512-UNjfslWhAt06kVL3CjkuYpHAWSO6L4kDCVPegV6itt7nD1kSJavd3vhgAEhjglLJJKEdJ7oIqDJ+yHk6qO8gPA==", "hasInstallScript": true, "dependencies": { - "@prisma/debug": "5.20.0", - "@prisma/engines-version": "5.20.0-12.06fc58a368dc7be9fbbbe894adf8d445d208c284", - "@prisma/fetch-engine": "5.20.0", - "@prisma/get-platform": "5.20.0" + "@prisma/debug": "5.22.0", + "@prisma/engines-version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", + "@prisma/fetch-engine": "5.22.0", + "@prisma/get-platform": "5.22.0" } }, "node_modules/@prisma/engines-version": { - "version": "5.20.0-12.06fc58a368dc7be9fbbbe894adf8d445d208c284", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.20.0-12.06fc58a368dc7be9fbbbe894adf8d445d208c284.tgz", - "integrity": "sha512-Lg8AS5lpi0auZe2Mn4gjuCg081UZf88k3cn0RCwHgR+6cyHHpttPZBElJTHf83ZGsRNAmVCZCfUGA57WB4u4JA==" + "version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2.tgz", + "integrity": "sha512-2PTmxFR2yHW/eB3uqWtcgRcgAbG1rwG9ZriSvQw+nnb7c4uCr3RAcGMb6/zfE88SKlC1Nj2ziUvc96Z379mHgQ==" }, "node_modules/@prisma/fetch-engine": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.20.0.tgz", - "integrity": "sha512-JVcaPXC940wOGpCOwuqQRTz6I9SaBK0c1BAyC1pcz9xBi+dzFgUu3G/p9GV1FhFs9OKpfSpIhQfUJE9y00zhqw==", + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.22.0.tgz", + "integrity": "sha512-bkrD/Mc2fSvkQBV5EpoFcZ87AvOgDxbG99488a5cexp5Ccny+UM6MAe/UFkUC0wLYD9+9befNOqGiIJhhq+HbA==", "dependencies": { - "@prisma/debug": "5.20.0", - "@prisma/engines-version": "5.20.0-12.06fc58a368dc7be9fbbbe894adf8d445d208c284", - "@prisma/get-platform": "5.20.0" + "@prisma/debug": "5.22.0", + "@prisma/engines-version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", + "@prisma/get-platform": "5.22.0" } }, "node_modules/@prisma/get-platform": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.20.0.tgz", - "integrity": "sha512-8/+CehTZZNzJlvuryRgc77hZCWrUDYd/PmlZ7p2yNXtmf2Una4BWnTbak3us6WVdqoz5wmptk6IhsXdG2v5fmA==", + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.22.0.tgz", + "integrity": "sha512-pHhpQdr1UPFpt+zFfnPazhulaZYCUqeIcPpJViYoq9R+D/yw4fjE+CtnsnKzPYm0ddUbeXUzjGVGIRVgPDCk4Q==", "dependencies": { - "@prisma/debug": "5.20.0" + "@prisma/debug": "5.22.0" } }, "node_modules/@selderee/plugin-htmlparser2": { @@ -2173,31 +2316,6 @@ "url": "https://ko-fi.com/killymxi" } }, - "node_modules/@selderee/plugin-htmlparser2/node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/@selderee/plugin-htmlparser2/node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -2227,6 +2345,28 @@ "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==" }, + "node_modules/@tokenizer/inflate": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz", + "integrity": "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==", + "dependencies": { + "debug": "^4.4.0", + "fflate": "^0.8.2", + "token-types": "^6.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" + }, "node_modules/@tsconfig/node10": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", @@ -2265,9 +2405,9 @@ } }, "node_modules/@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", "dev": true, "dependencies": { "@babel/types": "^7.0.0" @@ -2284,18 +2424,18 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", - "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", + "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", "dev": true, "dependencies": { "@babel/types": "^7.20.7" } }, "node_modules/@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", "dev": true, "dependencies": { "@types/connect": "*", @@ -2312,11 +2452,11 @@ } }, "node_modules/@types/cookie-parser": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.7.tgz", - "integrity": "sha512-Fvuyi354Z+uayxzIGCwYTayFKocfV7TuDYZClCdIP9ckhvAu/ixDtCB6qx2TT0FKjPLf1f3P/J1rgf6lPs64mw==", + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.9.tgz", + "integrity": "sha512-tGZiZ2Gtc4m3wIdLkZ8mkj1T6CEHb35+VApbL2T14Dew8HA7c+04dmKqsKRNC+8RJPm16JEK0tFSwdZqubfc4g==", "dev": true, - "dependencies": { + "peerDependencies": { "@types/express": "*" } }, @@ -2327,9 +2467,9 @@ "dev": true }, "node_modules/@types/cors": { - "version": "2.8.18", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.18.tgz", - "integrity": "sha512-nX3d0sxJW41CqQvfOzVG1NCTXfFDrDWIghCZncpHeWlVFd81zxB/DLhg7avFg6eHLCRX7ckBmoIIcqa++upvJA==", + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", "dependencies": { "@types/node": "*" } @@ -2361,15 +2501,15 @@ } }, "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true }, "node_modules/@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.23.tgz", + "integrity": "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==", "dev": true, "dependencies": { "@types/body-parser": "*", @@ -2379,9 +2519,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.19.5", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.5.tgz", - "integrity": "sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==", + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", "dev": true, "dependencies": { "@types/node": "*", @@ -2400,9 +2540,9 @@ } }, "node_modules/@types/http-errors": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", "dev": true }, "node_modules/@types/istanbul-lib-coverage": { @@ -2430,9 +2570,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.13", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.13.tgz", - "integrity": "sha512-wd+MVEZCHt23V0/L642O5APvspWply/rGY5BcW4SUETo2UzPU3Z26qr8jC2qxpimI2jjx9h7+2cj2FwIr01bXg==", + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", "dev": true, "dependencies": { "expect": "^29.0.0", @@ -2466,17 +2606,17 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.16.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.5.tgz", - "integrity": "sha512-VwYCweNo3ERajwy0IUlqqcyZ8/A7Zwa9ZP3MnENWcB11AejO+tLy3pu850goUW2FC/IJMdZUfKpX/yxL1gymCA==", + "version": "20.19.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.0.tgz", + "integrity": "sha512-hfrc+1tud1xcdVTABC2JiomZJEklMcXYNTVtZLAeqTVWD+qL5jkHKT+1lOtqDdGxt+mB53DTtiz673vfjU8D1Q==", "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~6.21.0" } }, "node_modules/@types/passport": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.16.tgz", - "integrity": "sha512-FD0qD5hbPWQzaM0wHUnJ/T0BBCJBxCeemtnCwc/ThhTg3x9jfrAcRUmj5Dopza+MfFS9acTe3wk7rcVnRIp/0A==", + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.17.tgz", + "integrity": "sha512-aciLyx+wDwT2t2/kJGJR2AEeBz0nJU4WuRX04Wu9Dqc5lSUtwu0WERPHYsLhF9PtseiAMPBGNUOtFjxZ56prsg==", "dev": true, "dependencies": { "@types/express": "*" @@ -2503,15 +2643,15 @@ } }, "node_modules/@types/pug": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.5.tgz", - "integrity": "sha512-LOnASQoeNZMkzexRuyqcBBDZ6rS+rQxUMkmj5A0PkhhiSZivLIuz6Hxyr1mkGoEZEkk66faROmpMi4fFkrKsBA==", + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz", + "integrity": "sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==", "optional": true }, "node_modules/@types/qs": { - "version": "6.9.16", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz", - "integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", "dev": true }, "node_modules/@types/range-parser": { @@ -2521,9 +2661,9 @@ "dev": true }, "node_modules/@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "version": "0.17.5", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", + "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", "dev": true, "dependencies": { "@types/mime": "^1", @@ -2531,9 +2671,9 @@ } }, "node_modules/@types/serve-static": { - "version": "1.15.7", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", - "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz", + "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==", "dev": true, "dependencies": { "@types/http-errors": "*", @@ -2560,9 +2700,9 @@ } }, "node_modules/@types/supertest": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-6.0.2.tgz", - "integrity": "sha512-137ypx2lk/wTQbW6An6safu9hXmajAifU/s7szAHLN/FeIm5w7yR0Wkl9fdJMRSHwOn4HLAI0DaB2TOORuhPDg==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-6.0.3.tgz", + "integrity": "sha512-8WzXq62EXFhJ7QsH3Ocb/iKQ/Ty9ZVWnVzoTKc9tyyFRRF3a74Tk2+TLFgaFFw364Ere+npzHKEJ6ga2LzIL7w==", "dev": true, "dependencies": { "@types/methods": "^1.1.4", @@ -2570,9 +2710,9 @@ } }, "node_modules/@types/validator": { - "version": "13.12.2", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.12.2.tgz", - "integrity": "sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA==" + "version": "13.15.1", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.1.tgz", + "integrity": "sha512-9gG6ogYcoI2mCMLdcO0NYI0AYrbxIjv0MDmy/5Ywo6CpWWrqYayc+mmgxRsCgtcGJm9BSbXkMsmxGah1iGHAAQ==" }, "node_modules/@types/webidl-conversions": { "version": "7.0.3", @@ -2788,9 +2928,9 @@ } }, "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "dev": true }, "node_modules/@webassemblyjs/ast": { @@ -2951,6 +3091,14 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true }, + "node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -2964,9 +3112,9 @@ } }, "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -3029,11 +3177,54 @@ } } }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/alce": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/alce/-/alce-1.2.0.tgz", + "integrity": "sha512-XppPf2S42nO2WhvKzlwzlfcApcXHzjlod30pKmcWjRgLOtqoe5DMuqdiYoM6AgyXksc6A6pV4v1L/WW217e57w==", + "dependencies": { + "esprima": "^1.2.0", + "estraverse": "^1.5.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/alce/node_modules/esprima": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.5.tgz", + "integrity": "sha512-S9VbPDU0adFErpDai3qDkjq8+G05ONtKzcyNrPKg/ZKa+tf879nX2KexNU95b31UoTJjRLInNBHHHjFPoCd7lQ==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/alce/node_modules/estraverse": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", + "integrity": "sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, "engines": { "node": ">=6" } @@ -3069,7 +3260,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "engines": { "node": ">=8" } @@ -3092,7 +3282,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -3105,7 +3294,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "engines": { "node": ">=8.6" }, @@ -3190,17 +3378,8 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "node_modules/axios": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", - "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true }, "node_modules/babel-jest": { "version": "29.7.0", @@ -3365,16 +3544,10 @@ "node": "^4.5.0 || >= 5.9" } }, - "node_modules/batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" - }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, "engines": { "node": ">=8" }, @@ -3438,7 +3611,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "devOptional": true, "dependencies": { "balanced-match": "^1.0.0" } @@ -3447,7 +3619,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, "dependencies": { "fill-range": "^7.1.1" }, @@ -3456,9 +3627,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz", + "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==", "dev": true, "funding": [ { @@ -3475,10 +3646,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", + "caniuse-lite": "^1.0.30001718", + "electron-to-chromium": "^1.5.160", "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" + "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" @@ -3509,9 +3680,9 @@ } }, "node_modules/bson": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.2.tgz", - "integrity": "sha512-5afhLTjqDSA3akH56E+/2J6kTDuSIlBxyXPdQslj9hcIgOUE378xdOfZvC/9q3LifJNI6KR/juZ+d0NRNYBwXg==", + "version": "6.10.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz", + "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==", "engines": { "node": ">=16.20.1" } @@ -3573,6 +3744,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", @@ -3622,6 +3794,15 @@ "node": ">=6" } }, + "node_modules/camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==", + "dependencies": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, "node_modules/camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -3632,9 +3813,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001700", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001700.tgz", - "integrity": "sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ==", + "version": "1.0.30001721", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001721.tgz", + "integrity": "sha512-cOuvmUVtKrtEaoKiO0rSc29jcjwMwX5tOHDy4MgVFEWiUXj4uBMJkwI8MDySkgXidpMiHUcviogAvFi4pA2hDQ==", "dev": true, "funding": [ { @@ -3690,36 +3871,45 @@ "dev": true }, "node_modules/cheerio": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", - "integrity": "sha512-8/MzidM6G/TgRelkzDG13y3Y9LxBjCb+8yOEZ9+wwq5gVF2w2pV0wmHvjfT0RvuxGyR7UEuK36r+yYMbT4uKgA==", - "dependencies": { - "css-select": "~1.2.0", - "dom-serializer": "~0.1.0", - "entities": "~1.1.1", - "htmlparser2": "^3.9.1", - "lodash.assignin": "^4.0.9", - "lodash.bind": "^4.1.4", - "lodash.defaults": "^4.0.1", - "lodash.filter": "^4.4.0", - "lodash.flatten": "^4.2.0", - "lodash.foreach": "^4.3.0", - "lodash.map": "^4.4.0", - "lodash.merge": "^4.4.0", - "lodash.pick": "^4.2.1", - "lodash.reduce": "^4.4.0", - "lodash.reject": "^4.4.0", - "lodash.some": "^4.4.0" + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" }, "engines": { - "node": ">= 0.6" + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" } }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -3752,7 +3942,6 @@ "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, "funding": [ { "type": "github", @@ -3764,9 +3953,9 @@ } }, "node_modules/cjs-module-lexer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", - "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", "dev": true }, "node_modules/class-transformer": { @@ -3775,15 +3964,34 @@ "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==" }, "node_modules/class-validator": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.1.tgz", - "integrity": "sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ==", + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.2.tgz", + "integrity": "sha512-3kMVRF2io8N8pY1IFIXlho9r8IPUUIfHe2hYVtiebvAzU2XeQFXTv+XI4WX+TnXmtwXMDcjngcpkiPM0O9PvLw==", "dependencies": { "@types/validator": "^13.11.8", - "libphonenumber-js": "^1.10.53", + "libphonenumber-js": "^1.11.1", "validator": "^13.9.0" } }, + "node_modules/clean-css": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz", + "integrity": "sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==", + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/clean-css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -3836,7 +4044,6 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -3850,7 +4057,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -3908,6 +4114,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -3925,9 +4132,9 @@ } }, "node_modules/comment-json": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.2.3.tgz", - "integrity": "sha512-SsxdiOf064DWoZLH799Ata6u7iV658A11PlWtZATDlXPpKGJnbJZ5Z24ybixAi+LUUqJ/GKowAejtC5GFUG7Tw==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.2.5.tgz", + "integrity": "sha512-bKw/r35jR3HGt5PEPm1ljsQQGyCrR8sFGNiN5L+ykDHdpO8Smxkrkla9Yi6NkQyUrb8V54PGhfMs6NrIwtxtdw==", "dev": true, "dependencies": { "array-timsort": "^1.0.3", @@ -3952,41 +4159,32 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "devOptional": true }, "node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", "engines": [ - "node >= 0.8" + "node >= 6.0" ], "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", - "readable-stream": "^2.2.2", + "readable-stream": "^3.0.2", "typedarray": "^0.0.6" } }, - "node_modules/concat-stream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "ini": "^1.3.4", + "proto-list": "~1.2.1" } }, - "node_modules/concat-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, "node_modules/consola": { "version": "2.15.3", "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", @@ -4030,7 +4228,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", - "license": "MIT", "engines": { "node": ">=18" } @@ -4069,7 +4266,8 @@ "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true }, "node_modules/cors": { "version": "2.8.5", @@ -4140,7 +4338,6 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -4150,47 +4347,36 @@ "node": ">= 8" } }, - "node_modules/css-rules": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/css-rules/-/css-rules-1.1.0.tgz", - "integrity": "sha512-7L6krLIRwAEVCaVKyCEL6PQjQXUmf8DM9bWYKutlZd0DqOe0SiKIGQOkFb59AjDBb+3If7SDp3X8UlzDAgYSow==", - "dependencies": { - "cssom": "^0.5.0" - } - }, "node_modules/css-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha512-dUQOBoqdR7QwV90WysXPLXG5LO7nhYBgiWVfxF80DKPF8zx1t/pUd2FYy73emg3zrjtM6dzmYgbHKfV2rxiHQA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", "dependencies": { - "boolbase": "~1.0.0", - "css-what": "2.1", - "domutils": "1.5.1", - "nth-check": "~1.0.1" + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" } }, "node_modules/css-what": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", - "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", "engines": { - "node": "*" + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" } }, - "node_modules/cssom": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", - "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==" - }, - "node_modules/dayjs": { - "version": "1.11.13", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", - "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==" - }, "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "dependencies": { "ms": "^2.1.3" }, @@ -4204,9 +4390,9 @@ } }, "node_modules/dedent": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", - "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", + "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", "dev": true, "peerDependencies": { "babel-plugin-macros": "^3.1.0" @@ -4217,6 +4403,14 @@ } } }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -4247,6 +4441,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -4263,6 +4458,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, "engines": { "node": ">=0.4.0" } @@ -4284,15 +4480,27 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-indent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "engines": { + "node": ">=8" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, "engines": { "node": ">=8" } }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" + }, "node_modules/dezalgo": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", @@ -4333,6 +4541,18 @@ "node": ">=8" } }, + "node_modules/display-notification": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/display-notification/-/display-notification-2.0.0.tgz", + "integrity": "sha512-TdmtlAcdqy1NU+j7zlkDdMnCL878zriLaBmoD9quOoq1ySSSGv03l0hXK5CvIFZlIfFI/hizqdQuW+Num7xuhw==", + "dependencies": { + "escape-string-applescript": "^1.0.0", + "run-applescript": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -4351,34 +4571,54 @@ "integrity": "sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ==" }, "node_modules/dom-serializer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", - "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "dependencies": { - "domelementtype": "^1.3.0", - "entities": "^1.1.1" + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" } }, "node_modules/domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] }, "node_modules/domhandler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", "dependencies": { - "domelementtype": "1" + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" } }, "node_modules/domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", "dependencies": { - "dom-serializer": "0", - "domelementtype": "1" + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" } }, "node_modules/dotenv": { @@ -4416,8 +4656,7 @@ "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", @@ -4427,6 +4666,45 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/editorconfig": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", + "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", + "dependencies": { + "@one-ini/wasm": "0.1.1", + "commander": "^10.0.0", + "minimatch": "9.0.1", + "semver": "^7.5.3" + }, + "bin": { + "editorconfig": "bin/editorconfig" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/editorconfig/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "engines": { + "node": ">=14" + } + }, + "node_modules/editorconfig/node_modules/minimatch": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -4448,9 +4726,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.102", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.102.tgz", - "integrity": "sha512-eHhqaja8tE/FNpIiBrvBjFV/SSKpyWHLvxuR9dPTdo+3V9ppdLmFB7ZZQ98qNovcngPLYIz0oOBF9P0FfZef5Q==", + "version": "1.5.165", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.165.tgz", + "integrity": "sha512-naiMx1Z6Nb2TxPU6fiFrUrDTjyPMLdTtaOd2oLmG8zVSg2hCWGkhPyxwk+qRmZ1ytwVqUv0u7ZcDA5+ALhaUtw==", "dev": true }, "node_modules/emittery": { @@ -4468,8 +4746,7 @@ "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/encodeurl": { "version": "2.0.0", @@ -4522,10 +4799,26 @@ "node": ">= 0.6" } }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/enhanced-resolve": { - "version": "5.17.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", - "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", "dev": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -4536,9 +4829,15 @@ } }, "node_modules/entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } }, "node_modules/error-ex": { "version": "1.3.2", @@ -4566,9 +4865,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", - "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", "dev": true }, "node_modules/es-object-atoms": { @@ -4582,20 +4881,53 @@ "node": ">= 0.4" } }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, "engines": { "node": ">=6" } }, + "node_modules/escape-goat": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-3.0.0.tgz", + "integrity": "sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, + "node_modules/escape-string-applescript": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/escape-string-applescript/-/escape-string-applescript-1.0.0.tgz", + "integrity": "sha512-4/hFwoYaC6TkpDn9A3pTC52zQPArFeXuIfhUtCGYdauTzXVP9H3BDr3oO/QzQehMpLDC7srvYgfwvImPFGfvBA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -4612,6 +4944,7 @@ "version": "8.57.1", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", @@ -4676,13 +5009,13 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", - "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.4.1.tgz", + "integrity": "sha512-9dF+KuU/Ilkq27A8idRP7N2DH8iUR6qXcjF3FR2wETY21PZdBrIjwCau8oboyGj9b7etWmTGEeM8e7oOed6ZWg==", "dev": true, "dependencies": { "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.9.1" + "synckit": "^0.11.7" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -4693,7 +5026,7 @@ "peerDependencies": { "@types/eslint": ">=8.0.0", "eslint": ">=8.0.0", - "eslint-config-prettier": "*", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", "prettier": ">=3.0.0" }, "peerDependenciesMeta": { @@ -5008,6 +5341,11 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" }, + "node_modules/extend-object": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/extend-object/-/extend-object-1.0.0.tgz", + "integrity": "sha512-0dHDIXC7y7LDmCh/lp1oYkmv73K25AMugQI07r8eFopkW6f7Ufn1q+ETMsJjnV9Am14SlElkqy3O92r6xEaxPw==" + }, "node_modules/external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -5022,17 +5360,6 @@ "node": ">=4" } }, - "node_modules/extract-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-css/-/extract-css-2.0.1.tgz", - "integrity": "sha512-DX3+27l6NIVYNidJjBJ4VU3Z5tk/0aEO/JJ6XEJIRPFum9kyA1PifPjnEa8Ztnv4DHNQg5EF27aypGz6s/fMdw==", - "dependencies": { - "batch": "^0.6.1", - "href-content": "^2.0.1", - "list-stylesheets": "^1.2.10", - "style-data": "^1.4.8" - } - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -5046,16 +5373,16 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -5079,9 +5406,9 @@ "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" }, "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "dev": true, "dependencies": { "reusify": "^1.0.4" @@ -5096,6 +5423,11 @@ "bser": "2.1.1" } }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" + }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -5132,6 +5464,23 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-type": { + "version": "20.4.1", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-20.4.1.tgz", + "integrity": "sha512-hw9gNZXUfZ02Jo0uafWLaFVPter5/k2rfcrjFJJHX/77xtSDOfJuEFb6oKlFV86FLP1SuyHMW1PSk0U9M5tKkQ==", + "dependencies": { + "@tokenizer/inflate": "^0.2.6", + "strtok3": "^10.2.0", + "token-types": "^6.0.0", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, "node_modules/filelist": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", @@ -5157,7 +5506,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -5211,6 +5559,34 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/fixpack": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fixpack/-/fixpack-4.0.0.tgz", + "integrity": "sha512-5SM1+H2CcuJ3gGEwTiVo/+nd/hYpNj9Ch3iMDOQ58ndY+VGQ2QdvaUTkd3otjZvYnd/8LF/HkJ5cx7PBq0orCQ==", + "dependencies": { + "alce": "1.2.0", + "chalk": "^3.0.0", + "detect-indent": "^6.0.0", + "detect-newline": "^3.1.0", + "extend-object": "^1.0.0", + "rc": "^1.2.8" + }, + "bin": { + "fixpack": "bin/fixpack" + } + }, + "node_modules/fixpack/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/flat-cache": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", @@ -5225,43 +5601,18 @@ "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/flat-util": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/flat-util/-/flat-util-1.1.11.tgz", - "integrity": "sha512-h9ho3lHr5hDTQZKLqFDqIliDV/A8yCyP7UoSIBT4U3d7VfA/EeqsC8cxWJGIr5oCxZzMD/3BEx3SLYFX6hD8ng==" - }, "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, "node_modules/foreground-child": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", - "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", - "dev": true, + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dependencies": { - "cross-spawn": "^7.0.0", + "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" }, "engines": { @@ -5322,12 +5673,15 @@ } }, "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", + "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", + "dev": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -5390,7 +5744,8 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true }, "node_modules/fsevents": { "version": "2.3.3", @@ -5426,7 +5781,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -5463,6 +5817,17 @@ "node": ">=8.0.0" } }, + "node_modules/get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", @@ -5488,21 +5853,22 @@ } }, "node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" }, "bin": { "glob": "dist/esm/bin.mjs" }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, "funding": { "url": "https://github.com/sponsors/isaacs" } @@ -5511,7 +5877,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -5634,6 +5999,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, "dependencies": { "es-define-property": "^1.0.0" }, @@ -5685,20 +6051,37 @@ "he": "bin/he" } }, - "node_modules/href-content": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/href-content/-/href-content-2.0.3.tgz", - "integrity": "sha512-ikrAoI1l5ihN5Be2cR9nozFfivVJxPQDpa4+sb6PLt/uaNE/a7A05rHbnJEUduoHddbB3GyT5tdqzXMUmPgJYA==", - "dependencies": { - "remote-content": "^4.0.0" - } - }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, + "node_modules/html-minifier": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-4.0.0.tgz", + "integrity": "sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==", + "dependencies": { + "camel-case": "^3.0.0", + "clean-css": "^4.2.1", + "commander": "^2.19.0", + "he": "^1.2.0", + "param-case": "^2.1.1", + "relateurl": "^0.2.7", + "uglify-js": "^3.5.1" + }, + "bin": { + "html-minifier": "cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/html-minifier/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, "node_modules/html-to-text": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz", @@ -5714,69 +6097,7 @@ "node": ">=14" } }, - "node_modules/html-to-text/node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/html-to-text/node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/html-to-text/node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/html-to-text/node_modules/domutils": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", - "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/html-to-text/node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/html-to-text/node_modules/htmlparser2": { + "node_modules/htmlparser2": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", @@ -5794,19 +6115,6 @@ "entities": "^4.4.0" } }, - "node_modules/htmlparser2": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", - "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", - "dependencies": { - "domelementtype": "^1.3.1", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^3.1.1" - } - }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -5846,7 +6154,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, "funding": [ { "type": "github", @@ -5872,9 +6179,9 @@ } }, "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "dependencies": { "parent-module": "^1.0.0", @@ -5920,6 +6227,7 @@ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -5930,22 +6238,10 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "node_modules/inline-css": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/inline-css/-/inline-css-3.0.0.tgz", - "integrity": "sha512-a+IE7oLaQqeVr3hMviekDDk94LA0+oZX8JEfJuXOm20diZAkOFrq/f/QZCEXpMK6qIbYr0nQNpsuioXQN1ZgXA==", - "dependencies": { - "cheerio": "^0.22.0", - "css-rules": "^1.0.9", - "extract-css": "^2.0.0", - "flat-util": "^1.1.6", - "pick-util": "^1.1.3", - "slick": "^1.12.2", - "specificity": "^0.4.1" - }, - "engines": { - "node": ">=8" - } + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, "node_modules/inquirer": { "version": "8.2.6", @@ -5991,7 +6287,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, "dependencies": { "binary-extensions": "^2.0.0" }, @@ -6000,9 +6295,9 @@ } }, "node_modules/is-core-module": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", - "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dependencies": { "hasown": "^2.0.2" }, @@ -6051,7 +6346,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -6060,7 +6354,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "engines": { "node": ">=8" } @@ -6078,7 +6371,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -6099,7 +6391,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "engines": { "node": ">=0.12.0" } @@ -6170,16 +6461,10 @@ "node": ">=8" } }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", @@ -6265,13 +6550,15 @@ } }, "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", "dependencies": { "@isaacs/cliui": "^8.0.2" }, + "engines": { + "node": ">=14" + }, "funding": { "url": "https://github.com/sponsors/isaacs" }, @@ -6991,6 +7278,67 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/js-beautify": { + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz", + "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==", + "dependencies": { + "config-chain": "^1.1.13", + "editorconfig": "^1.0.4", + "glob": "^10.4.2", + "js-cookie": "^3.0.5", + "nopt": "^7.2.1" + }, + "bin": { + "css-beautify": "js/bin/css-beautify.js", + "html-beautify": "js/bin/html-beautify.js", + "js-beautify": "js/bin/js-beautify.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/js-beautify/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-beautify/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "engines": { + "node": ">=14" + } + }, "node_modules/js-stringify": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", @@ -7014,15 +7362,15 @@ } }, "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, "bin": { "jsesc": "bin/jsesc" }, "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/json-buffer": { @@ -7109,12 +7457,38 @@ "promise": "^7.0.1" } }, + "node_modules/juice": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/juice/-/juice-10.0.1.tgz", + "integrity": "sha512-ZhJT1soxJCkOiO55/mz8yeBKTAJhRzX9WBO+16ZTqNTONnnVlUPyVBIzQ7lDRjaBdTbid+bAnyIon/GM3yp4cA==", + "dependencies": { + "cheerio": "1.0.0-rc.12", + "commander": "^6.1.0", + "mensch": "^0.3.4", + "slick": "^1.12.2", + "web-resource-inliner": "^6.0.1" + }, + "bin": { + "juice": "bin/juice" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/juice/node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "engines": { + "node": ">= 6" + } + }, "node_modules/jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", "dependencies": { - "buffer-equal-constant-time": "1.0.1", + "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } @@ -7212,9 +7586,9 @@ } }, "node_modules/libphonenumber-js": { - "version": "1.11.11", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.11.11.tgz", - "integrity": "sha512-mF3KaORjJQR6JBNcOkluDcJKhtoQT4VTLRMrX1v/wlBayL4M8ybwEDeryyPcrSEJmD0rVwHUbBarpZwN5NfPFQ==" + "version": "1.12.9", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.9.tgz", + "integrity": "sha512-VWwAdNeJgN7jFOD+wN4qx83DTPMVPPAUyx9/TUkBXKLiNkuWWk6anV0439tgdtwaJDrEdqkvdN22iA6J4bUCZg==" }, "node_modules/libqp": { "version": "2.1.1", @@ -7235,15 +7609,6 @@ "uc.micro": "^2.0.0" } }, - "node_modules/list-stylesheets": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/list-stylesheets/-/list-stylesheets-1.2.10.tgz", - "integrity": "sha512-F85Yx9GKLJwDr1T9U34FST5m6iIXhPzqD+MRDsljZsdmoEZwllZBDbkAVaa+EpLKrr6de+P4SRGNHwrWv6zMZA==", - "dependencies": { - "cheerio": "^0.22.0", - "pick-util": "^1.1.4" - } - }, "node_modules/loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", @@ -7273,36 +7638,6 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "node_modules/lodash.assignin": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", - "integrity": "sha512-yX/rx6d/UTVh7sSVWVSIMjfnz95evAgDFdb1ZozC35I9mSFCkmzptOzevxjgbQUsc78NR44LVHWjsoMQXy9FDg==" - }, - "node_modules/lodash.bind": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", - "integrity": "sha512-lxdsn7xxlCymgLYo1gGvVrfHmkjDiyqVv62FAeF2i5ta72BipE1SLxw8hPEPLhD4/247Ijw07UQH7Hq/chT5LA==" - }, - "node_modules/lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" - }, - "node_modules/lodash.filter": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", - "integrity": "sha512-pXYUy7PR8BCLwX5mgJ/aNtyOvuJTdZAo9EQFUvMIYugqmJxnrYaANvTbgndOzHSCSR0wnlBBfRXJL5SbWxo3FQ==" - }, - "node_modules/lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" - }, - "node_modules/lodash.foreach": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", - "integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==" - }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -7333,11 +7668,6 @@ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" }, - "node_modules/lodash.map": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", - "integrity": "sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==" - }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -7347,34 +7677,14 @@ "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true }, "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" }, - "node_modules/lodash.pick": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", - "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==", - "deprecated": "This package is deprecated. Use destructuring assignment syntax instead." - }, - "node_modules/lodash.reduce": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", - "integrity": "sha512-6raRe2vxCYBhpBu+B+TtNGUzah+hQjVdu3E17wfusjyrXBka2nBS8OH/gjVZ5PvHOhWmIZTYri09Z6n/QfnNMw==" - }, - "node_modules/lodash.reject": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz", - "integrity": "sha512-qkTuvgEzYdyhiJBx42YPzPo71R1aEr0z79kAv7Ixg8wPFEjgRgJdUsGMG3Hf3OYSF/kHI79XhNlt+5Ar6OzwxQ==" - }, - "node_modules/lodash.some": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", - "integrity": "sha512-j7MJE+TuT51q9ggt4fSgVqro163BEFjAt3u97IqU+JA2DkWl80nFTrowzLpZ/BnpN7rrl0JA/593NAdd8p/scQ==" - }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -7391,6 +7701,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==" + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -7440,14 +7755,6 @@ "node": ">=0.10.0" } }, - "node_modules/mailparser/node_modules/nodemailer": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.3.tgz", - "integrity": "sha512-Ajq6Sz1x7cIK3pN6KesGTah+1gnwMnx5gKl3piQlQQE/PwyJ4Mbc8is2psWYxK3RJTVeqsDaCv8ZzXLCDHMTZw==", - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/mailsplit": { "version": "5.4.3", "resolved": "https://registry.npmjs.org/mailsplit/-/mailsplit-5.4.3.tgz", @@ -7504,14 +7811,6 @@ "node": ">= 0.6" } }, - "node_modules/mediaquery-text": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mediaquery-text/-/mediaquery-text-1.2.0.tgz", - "integrity": "sha512-cJyRqgYQi+hsYhRkyd5le0s4LsEPvOB7r+6X3jdEELNqVlM9mRIgyUPg9BzF+PuTqQH1ZekgIjYVOeWSXWq35Q==", - "dependencies": { - "cssom": "^0.5.0" - } - }, "node_modules/memfs": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", @@ -7529,6 +7828,11 @@ "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" }, + "node_modules/mensch": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/mensch/-/mensch-0.3.4.tgz", + "integrity": "sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==" + }, "node_modules/merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", @@ -7628,7 +7932,6 @@ "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -7651,44 +7954,435 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, "engines": { "node": ">=16 || 14 >=14.17" } }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "node_modules/mjml": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml/-/mjml-4.15.3.tgz", + "integrity": "sha512-bW2WpJxm6HS+S3Yu6tq1DUPFoTxU9sPviUSmnL7Ua+oVO3WA5ILFWqvujUlz+oeuM+HCwEyMiP5xvKNPENVjYA==", "dependencies": { - "minimist": "^1.2.6" + "@babel/runtime": "^7.23.9", + "mjml-cli": "4.15.3", + "mjml-core": "4.15.3", + "mjml-migrate": "4.15.3", + "mjml-preset-core": "4.15.3", + "mjml-validator": "4.15.3" }, "bin": { - "mkdirp": "bin/cmd.js" + "mjml": "bin/mjml" } }, - "node_modules/mongodb": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.13.0.tgz", - "integrity": "sha512-KeESYR5TEaFxOuwRqkOm3XOsMqCSkdeDMjaW5u2nuKfX7rqaofp7JQGoi7sVqQcNJTKuveNbzZtWMstb8ABP6Q==", + "node_modules/mjml-accordion": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-accordion/-/mjml-accordion-4.15.3.tgz", + "integrity": "sha512-LPNVSj1LyUVYT9G1gWwSw3GSuDzDsQCu0tPB2uDsq4VesYNnU6v3iLCQidMiR6azmIt13OEozG700ygAUuA6Ng==", "dependencies": { - "@mongodb-js/saslprep": "^1.1.9", - "bson": "^6.10.1", - "mongodb-connection-string-url": "^3.0.0" - }, - "engines": { - "node": ">=16.20.1" - }, - "peerDependencies": { - "@aws-sdk/credential-providers": "^3.188.0", - "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", - "gcp-metadata": "^5.2.0", - "kerberos": "^2.0.1", - "mongodb-client-encryption": ">=6.0.0 <7", - "snappy": "^7.2.2", - "socks": "^2.7.1" - }, - "peerDependenciesMeta": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-body": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-body/-/mjml-body-4.15.3.tgz", + "integrity": "sha512-7pfUOVPtmb0wC+oUOn4xBsAw4eT5DyD6xqaxj/kssu6RrFXOXgJaVnDPAI9AzIvXJ/5as9QrqRGYAddehwWpHQ==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-button": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-button/-/mjml-button-4.15.3.tgz", + "integrity": "sha512-79qwn9AgdGjJR1vLnrcm2rq2AsAZkKC5JPwffTMG+Nja6zGYpTDZFZ56ekHWr/r1b5WxkukcPj2PdevUug8c+Q==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-carousel": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-carousel/-/mjml-carousel-4.15.3.tgz", + "integrity": "sha512-3ju6I4l7uUhPRrJfN3yK9AMsfHvrYbRkcJ1GRphFHzUj37B2J6qJOQUpzA547Y4aeh69TSb7HFVf1t12ejQxVw==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-cli": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-cli/-/mjml-cli-4.15.3.tgz", + "integrity": "sha512-+V2TDw3tXUVEptFvLSerz125C2ogYl8klIBRY1m5BHd4JvGVf3yhx8N3PngByCzA6PGcv/eydGQN+wy34SHf0Q==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "chokidar": "^3.0.0", + "glob": "^10.3.10", + "html-minifier": "^4.0.0", + "js-beautify": "^1.6.14", + "lodash": "^4.17.21", + "minimatch": "^9.0.3", + "mjml-core": "4.15.3", + "mjml-migrate": "4.15.3", + "mjml-parser-xml": "4.15.3", + "mjml-validator": "4.15.3", + "yargs": "^17.7.2" + }, + "bin": { + "mjml-cli": "bin/mjml" + } + }, + "node_modules/mjml-column": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-column/-/mjml-column-4.15.3.tgz", + "integrity": "sha512-hYdEFdJGHPbZJSEysykrevEbB07yhJGSwfDZEYDSbhQQFjV2tXrEgYcFD5EneMaowjb55e3divSJxU4c5q4Qgw==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-core": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-core/-/mjml-core-4.15.3.tgz", + "integrity": "sha512-Dmwk+2cgSD9L9GmTbEUNd8QxkTZtW9P7FN/ROZW/fGZD6Hq6/4TB0zEspg2Ow9eYjZXO2ofOJ3PaQEEShKV0kQ==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "cheerio": "1.0.0-rc.12", + "detect-node": "^2.0.4", + "html-minifier": "^4.0.0", + "js-beautify": "^1.6.14", + "juice": "^10.0.0", + "lodash": "^4.17.21", + "mjml-migrate": "4.15.3", + "mjml-parser-xml": "4.15.3", + "mjml-validator": "4.15.3" + } + }, + "node_modules/mjml-divider": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-divider/-/mjml-divider-4.15.3.tgz", + "integrity": "sha512-vh27LQ9FG/01y0b9ntfqm+GT5AjJnDSDY9hilss2ixIUh0FemvfGRfsGVeV5UBVPBKK7Ffhvfqc7Rciob9Spzw==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-group": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-group/-/mjml-group-4.15.3.tgz", + "integrity": "sha512-HSu/rKnGZVKFq3ciT46vi1EOy+9mkB0HewO4+P6dP/Y0UerWkN6S3UK11Cxsj0cAp0vFwkPDCdOeEzRdpFEkzA==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-head": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-head/-/mjml-head-4.15.3.tgz", + "integrity": "sha512-o3mRuuP/MB5fZycjD3KH/uXsnaPl7Oo8GtdbJTKtH1+O/3pz8GzGMkscTKa97l03DAG2EhGrzzLcU2A6eshwFw==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-head-attributes": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-head-attributes/-/mjml-head-attributes-4.15.3.tgz", + "integrity": "sha512-2ISo0r5ZKwkrvJgDou9xVPxxtXMaETe2AsAA02L89LnbB2KC0N5myNsHV0sEysTw9+CfCmgjAb0GAI5QGpxKkQ==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-head-breakpoint": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-head-breakpoint/-/mjml-head-breakpoint-4.15.3.tgz", + "integrity": "sha512-Eo56FA5C2v6ucmWQL/JBJ2z641pLOom4k0wP6CMZI2utfyiJ+e2Uuinj1KTrgDcEvW4EtU9HrfAqLK9UosLZlg==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-head-font": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-head-font/-/mjml-head-font-4.15.3.tgz", + "integrity": "sha512-CzV2aDPpiNIIgGPHNcBhgyedKY4SX3BJoTwOobSwZVIlEA6TAWB4Z9WwFUmQqZOgo1AkkiTHPZQvGcEhFFXH6g==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-head-html-attributes": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-head-html-attributes/-/mjml-head-html-attributes-4.15.3.tgz", + "integrity": "sha512-MDNDPMBOgXUZYdxhosyrA2kudiGO8aogT0/cODyi2Ed9o/1S7W+je11JUYskQbncqhWKGxNyaP4VWa+6+vUC/g==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-head-preview": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-head-preview/-/mjml-head-preview-4.15.3.tgz", + "integrity": "sha512-J2PxCefUVeFwsAExhrKo4lwxDevc5aKj888HBl/wN4EuWOoOg06iOGCxz4Omd8dqyFsrqvbBuPqRzQ+VycGmaA==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-head-style": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-head-style/-/mjml-head-style-4.15.3.tgz", + "integrity": "sha512-9J+JuH+mKrQU65CaJ4KZegACUgNIlYmWQYx3VOBR/tyz+8kDYX7xBhKJCjQ1I4wj2Tvga3bykd89Oc2kFZ5WOw==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-head-title": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-head-title/-/mjml-head-title-4.15.3.tgz", + "integrity": "sha512-IM59xRtsxID4DubQ0iLmoCGXguEe+9BFG4z6y2xQDrscIa4QY3KlfqgKGT69ojW+AVbXXJPEVqrAi4/eCsLItQ==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-hero": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-hero/-/mjml-hero-4.15.3.tgz", + "integrity": "sha512-9cLAPuc69yiuzNrMZIN58j+HMK1UWPaq2i3/Fg2ZpimfcGFKRcPGCbEVh0v+Pb6/J0+kf8yIO0leH20opu3AyQ==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-image": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-image/-/mjml-image-4.15.3.tgz", + "integrity": "sha512-g1OhSdofIytE9qaOGdTPmRIp7JsCtgO0zbsn1Fk6wQh2gEL55Z40j/VoghslWAWTgT2OHFdBKnMvWtN6U5+d2Q==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-migrate": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-migrate/-/mjml-migrate-4.15.3.tgz", + "integrity": "sha512-sr/+35RdxZroNQVegjpfRHJ5hda9XCgaS4mK2FGO+Mb1IUevKfeEPII3F/cHDpNwFeYH3kAgyqQ22ClhGLWNBA==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "js-beautify": "^1.6.14", + "lodash": "^4.17.21", + "mjml-core": "4.15.3", + "mjml-parser-xml": "4.15.3", + "yargs": "^17.7.2" + }, + "bin": { + "migrate": "lib/cli.js" + } + }, + "node_modules/mjml-navbar": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-navbar/-/mjml-navbar-4.15.3.tgz", + "integrity": "sha512-VsKH/Jdlf8Yu3y7GpzQV5n7JMdpqvZvTSpF6UQXL0PWOm7k6+LX+sCZimOfpHJ+wCaaybpxokjWZ71mxOoCWoA==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-parser-xml": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-parser-xml/-/mjml-parser-xml-4.15.3.tgz", + "integrity": "sha512-Tz0UX8/JVYICLjT+U8J1f/TFxIYVYjzZHeh4/Oyta0pLpRLeZlxEd71f3u3kdnulCKMP4i37pFRDmyLXAlEuLw==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "detect-node": "2.1.0", + "htmlparser2": "^9.1.0", + "lodash": "^4.17.15" + } + }, + "node_modules/mjml-parser-xml/node_modules/htmlparser2": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" + } + }, + "node_modules/mjml-preset-core": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-preset-core/-/mjml-preset-core-4.15.3.tgz", + "integrity": "sha512-1zZS8P4O0KweWUqNS655+oNnVMPQ1Rq1GaZq5S9JfwT1Vh/m516lSmiTW9oko6gGHytt5s6Yj6oOeu5Zm8FoLw==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "mjml-accordion": "4.15.3", + "mjml-body": "4.15.3", + "mjml-button": "4.15.3", + "mjml-carousel": "4.15.3", + "mjml-column": "4.15.3", + "mjml-divider": "4.15.3", + "mjml-group": "4.15.3", + "mjml-head": "4.15.3", + "mjml-head-attributes": "4.15.3", + "mjml-head-breakpoint": "4.15.3", + "mjml-head-font": "4.15.3", + "mjml-head-html-attributes": "4.15.3", + "mjml-head-preview": "4.15.3", + "mjml-head-style": "4.15.3", + "mjml-head-title": "4.15.3", + "mjml-hero": "4.15.3", + "mjml-image": "4.15.3", + "mjml-navbar": "4.15.3", + "mjml-raw": "4.15.3", + "mjml-section": "4.15.3", + "mjml-social": "4.15.3", + "mjml-spacer": "4.15.3", + "mjml-table": "4.15.3", + "mjml-text": "4.15.3", + "mjml-wrapper": "4.15.3" + } + }, + "node_modules/mjml-raw": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-raw/-/mjml-raw-4.15.3.tgz", + "integrity": "sha512-IGyHheOYyRchBLiAEgw3UM11kFNmBSMupu2BDdejC6ZiDhEAdG+tyERlsCwDPYtXanvFpGWULIu3XlsUPc+RZw==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-section": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-section/-/mjml-section-4.15.3.tgz", + "integrity": "sha512-JfVPRXH++Hd933gmQfG8JXXCBCR6fIzC3DwiYycvanL/aW1cEQ2EnebUfQkt5QzlYjOkJEH+JpccAsq3ln6FZQ==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-social": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-social/-/mjml-social-4.15.3.tgz", + "integrity": "sha512-7sD5FXrESOxpT9Z4Oh36bS6u/geuUrMP1aCg2sjyAwbPcF1aWa2k9OcatQfpRf6pJEhUZ18y6/WBBXmMVmSzXg==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-spacer": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-spacer/-/mjml-spacer-4.15.3.tgz", + "integrity": "sha512-3B7Qj+17EgDdAtZ3NAdMyOwLTX1jfmJuY7gjyhS2HtcZAmppW+cxqHUBwCKfvSRgTQiccmEvtNxaQK+tfyrZqA==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-table": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-table/-/mjml-table-4.15.3.tgz", + "integrity": "sha512-FLx7DcRKTdKdcOCbMyBaeudeHaHpwPveRrBm6WyQe3LXx6FfdmOh59i71/16LFQMgBOD3N4/UJkzxLzlTJzMqQ==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-text": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-text/-/mjml-text-4.15.3.tgz", + "integrity": "sha512-+C0hxCmw9kg0XzT6vhE5mFkK6y225nC8UEQcN94K0fBCjPKkM+HqZMwGX205fzdGRi+Bxa55b/VhrIVwdv+8vw==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3" + } + }, + "node_modules/mjml-validator": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-validator/-/mjml-validator-4.15.3.tgz", + "integrity": "sha512-Xb72KdqRwjv/qM2rJpV22syyP2N3cRQ9VVDrN6u2FSzLq02buFNxmSPJ7CKhat3PrUNdVHU75KZwOf/tz4UEhA==", + "dependencies": { + "@babel/runtime": "^7.23.9" + } + }, + "node_modules/mjml-wrapper": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-wrapper/-/mjml-wrapper-4.15.3.tgz", + "integrity": "sha512-ditsCijeHJrmBmObtJmQ18ddLxv5oPyMTdPU8Di8APOnD2zPk7Z4UAuJSl7HXB45oFiivr3MJf4koFzMUSZ6Gg==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "lodash": "^4.17.21", + "mjml-core": "4.15.3", + "mjml-section": "4.15.3" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mongodb": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.16.0.tgz", + "integrity": "sha512-D1PNcdT0y4Grhou5Zi/qgipZOYeWrhLEpk33n3nm6LGtz61jvO88WlrWCK/bigMjpnOdAUKKQwsGIl0NtWMyYw==", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.9", + "bson": "^6.10.3", + "mongodb-connection-string-url": "^3.0.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { "@aws-sdk/credential-providers": { "optional": true }, @@ -7721,45 +8415,14 @@ "whatwg-url": "^14.1.0 || ^13.0.0" } }, - "node_modules/mongodb-connection-string-url/node_modules/tr46": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", - "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", - "dependencies": { - "punycode": "^2.3.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/mongodb-connection-string-url/node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "engines": { - "node": ">=12" - } - }, - "node_modules/mongodb-connection-string-url/node_modules/whatwg-url": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.1.tgz", - "integrity": "sha512-mDGf9diDad/giZ/Sm9Xi2YcyzaFpbdLpJPr+E9fSkyQ7KpQD4SdFcugkRQYzhmfI4KeV4Qpnn2sKPdo+kmsgRQ==", - "dependencies": { - "tr46": "^5.0.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/mongoose": { - "version": "8.10.1", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.10.1.tgz", - "integrity": "sha512-5beTeBZnJNndRXU9rxPol0JmTWZMAtgkPbooROkGilswvrZALDERY4cJrGZmgGwDS9dl0mxiB7si+Mv9Yms2fg==", + "version": "8.15.1", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.15.1.tgz", + "integrity": "sha512-RhQ4DzmBi5BNGcS0w4u1vdMRIKcteXTCNzDt1j7XRcdWYBz1MjMjulBhPaeC5jBCHOD1yinuOFTTSOWLLGexWw==", "dependencies": { - "bson": "^6.10.1", + "bson": "^6.10.3", "kareem": "2.6.3", - "mongodb": "~6.13.0", + "mongodb": "~6.16.0", "mpath": "0.9.0", "mquery": "5.0.0", "ms": "2.1.3", @@ -7798,17 +8461,17 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/multer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.0.tgz", - "integrity": "sha512-bS8rPZurbAuHGAnApbM9d4h1wSoYqrOqkE+6a64KLMK9yWU7gJXBDDVklKQ3TPi9DRb85cRs6yXaC0+cjxRtRg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.1.tgz", + "integrity": "sha512-Ug8bXeTIUlxurg8xLTEskKShvcKDZALo1THEX5E41pYCD2sCVub5/kIRIGqWNoqV6szyLyQKV6mD4QUrWE5GCQ==", "dependencies": { "append-field": "^1.0.0", - "busboy": "^1.0.0", - "concat-stream": "^1.5.2", - "mkdirp": "^0.5.4", + "busboy": "^1.6.0", + "concat-stream": "^2.0.0", + "mkdirp": "^0.5.6", "object-assign": "^4.1.1", - "type-is": "^1.6.4", - "xtend": "^4.0.0" + "type-is": "^1.6.18", + "xtend": "^4.0.2" }, "engines": { "node": ">= 10.16.0" @@ -7840,6 +8503,19 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "devOptional": true }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, + "node_modules/no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "dependencies": { + "lower-case": "^1.1.1" + } + }, "node_modules/node-abort-controller": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", @@ -7847,9 +8523,9 @@ "dev": true }, "node_modules/node-addon-api": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.1.0.tgz", - "integrity": "sha512-yBY+qqWSv3dWKGODD6OGE6GnTX7Q2r+4+DfpqxHSHh8x0B4EKP9+wVGLS6U/AM1vxSNNmUEuIV5EGhYwPpfOwQ==", + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.3.1.tgz", + "integrity": "sha512-lytcDEdxKjGJPTLEfW4mYMigRezMlyJY8W4wxJK8zE533Jlb8L8dRuObJFWg2P+AuOIxoCgKF+2Oq4d4Zd0OUA==", "engines": { "node": "^18 || ^20 || >= 21" } @@ -7882,10 +8558,29 @@ } } }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/node-gyp-build": { - "version": "4.8.2", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.2.tgz", - "integrity": "sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==", + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", @@ -7905,18 +8600,31 @@ "dev": true }, "node_modules/nodemailer": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.1.tgz", - "integrity": "sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.3.tgz", + "integrity": "sha512-Ajq6Sz1x7cIK3pN6KesGTah+1gnwMnx5gKl3piQlQQE/PwyJ4Mbc8is2psWYxK3RJTVeqsDaCv8ZzXLCDHMTZw==", "engines": { "node": ">=6.0.0" } }, + "node_modules/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -7934,11 +8642,14 @@ } }, "node_modules/nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", "dependencies": { - "boolbase": "~1.0.0" + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" } }, "node_modules/object-assign": { @@ -7958,9 +8669,9 @@ } }, "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "engines": { "node": ">= 0.4" }, @@ -7983,6 +8694,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, "dependencies": { "wrappy": "1" } @@ -8066,6 +8778,28 @@ "node": ">=0.10.0" } }, + "node_modules/p-event": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz", + "integrity": "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==", + "dependencies": { + "p-timeout": "^3.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "engines": { + "node": ">=4" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -8096,6 +8830,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -8105,11 +8850,32 @@ "node": ">=6" } }, + "node_modules/p-wait-for": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-wait-for/-/p-wait-for-3.2.0.tgz", + "integrity": "sha512-wpgERjNkLrBiFmkMEjuZJEWKKDrNfHCKA1OhyN1wg1FrLkULbviEy6py1AyJUgZ72YWFbZ38FIpnqvVqAlDUwA==", + "dependencies": { + "p-timeout": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/package-json-from-dist": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", - "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", - "dev": true + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" + }, + "node_modules/param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==", + "dependencies": { + "no-case": "^2.2.0" + } }, "node_modules/parent-module": { "version": "1.0.1", @@ -8141,6 +8907,40 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", + "dependencies": { + "domhandler": "^5.0.3", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/parseley": { "version": "0.12.1", "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.1.tgz", @@ -8208,6 +9008,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -8216,7 +9017,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "engines": { "node": ">=8" } @@ -8230,7 +9030,6 @@ "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -8245,8 +9044,7 @@ "node_modules/path-scurry/node_modules/lru-cache": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" }, "node_modules/path-to-regexp": { "version": "3.3.0", @@ -8275,14 +9073,6 @@ "url": "https://ko-fi.com/killymxi" } }, - "node_modules/pick-util": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/pick-util/-/pick-util-1.1.5.tgz", - "integrity": "sha512-H0MaM8T7wpQ/azvB12ChZw7kpSFzjsgv3Z+N7fUWnL1McTGSEeroCngcK4eOPiFQq08rAyKX3hadcAB1kUqfXA==", - "dependencies": { - "@jonkemp/package-utils": "^1.0.8" - } - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -8302,9 +9092,9 @@ } }, "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", "dev": true, "engines": { "node": ">= 6" @@ -8393,9 +9183,9 @@ } }, "node_modules/prettier": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", - "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -8446,30 +9236,41 @@ } }, "node_modules/preview-email": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/preview-email/-/preview-email-3.0.4.tgz", - "integrity": "sha512-g9jbnFHI8QfQAcKeCsZpSzMJT/CeGuJoV311R/NLS6PTsalJkMKkUeirSJJgMJBUYOGJLrhM7MsNVWgk1b13BA==", - "deprecated": "We just released outbound SMTP support! Try it out at @ https://forwardemail.net/docs/how-to-javascript-contact-forms-node-js 🚀 ✉️ 👽", - "dependencies": { - "dayjs": "^1.10.4", - "debug": "^4.3.1", - "mailparser": "^3.1.0", - "nodemailer": "^6.5.0", + "version": "3.0.19", + "resolved": "https://registry.npmjs.org/preview-email/-/preview-email-3.0.19.tgz", + "integrity": "sha512-DBS3Nir18YtKc8loYCCOGitmiaQ0vTdahPoiXxwNweJDpmVZo+w3tppufOhoK0m8skpRxT56llYLs3VrORnmNQ==", + "dependencies": { + "ci-info": "^3.8.0", + "display-notification": "2.0.0", + "fixpack": "^4.0.0", + "get-port": "5.1.1", + "mailparser": "^3.6.4", + "nodemailer": "^6.9.2", "open": "7", + "p-event": "4.2.0", + "p-wait-for": "3.2.0", "pug": "^3.0.2", - "uuid": "^8.3.2" + "uuid": "^9.0.0" }, "engines": { - "node": ">=10" + "node": ">=14" + } + }, + "node_modules/preview-email/node_modules/nodemailer": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.1.tgz", + "integrity": "sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA==", + "engines": { + "node": ">=6.0.0" } }, "node_modules/prisma": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.20.0.tgz", - "integrity": "sha512-6obb3ucKgAnsGS9x9gLOe8qa51XxvJ3vLQtmyf52CTey1Qcez3A6W6ROH5HIz5Q5bW+0VpmZb8WBohieMFGpig==", + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.22.0.tgz", + "integrity": "sha512-vtpjW3XuYCSnMsNVBjLMNkTj6OZbudcPPTPYHqX0CJfpcdWciI1dM8uHETwmDxxiqEwCIE6WvXucWUetJgfu/A==", "hasInstallScript": true, "dependencies": { - "@prisma/engines": "5.20.0" + "@prisma/engines": "5.22.0" }, "bin": { "prisma": "build/index.js" @@ -8481,11 +9282,6 @@ "fsevents": "2.3.3" } }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, "node_modules/promise": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", @@ -8507,6 +9303,11 @@ "node": ">= 6" } }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==" + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -8519,11 +9320,6 @@ "node": ">= 0.10" } }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, "node_modules/pug": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/pug/-/pug-3.0.3.tgz", @@ -8733,6 +9529,28 @@ "node": ">= 0.8" } }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", @@ -8756,7 +9574,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, "dependencies": { "picomatch": "^2.2.1" }, @@ -8768,7 +9585,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "engines": { "node": ">=8.6" }, @@ -8781,13 +9597,12 @@ "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==" }, - "node_modules/remote-content": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/remote-content/-/remote-content-4.0.1.tgz", - "integrity": "sha512-W2lDnjK4k1vAJg7UZArH/rkNYJqZuteHkX1jS7tO9TJUiLhDcu2Ejvj97gK/XbZNDhzld0sn11OW8vihin4cAg==", - "dependencies": { - "axios": "^1.7.9", - "proxy-from-env": "^1.1.0" + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "engines": { + "node": ">= 0.10" } }, "node_modules/repeat-string": { @@ -8803,7 +9618,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -8818,17 +9632,20 @@ } }, "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "dependencies": { - "is-core-module": "^2.13.0", + "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -8864,9 +9681,9 @@ } }, "node_modules/resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", "dev": true, "engines": { "node": ">=10" @@ -8892,9 +9709,9 @@ "dev": true }, "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "dev": true, "engines": { "iojs": ">=1.0.0", @@ -8960,6 +9777,127 @@ "node": "*" } }, + "node_modules/run-applescript": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-3.2.0.tgz", + "integrity": "sha512-Ep0RsvAjnRcBX1p5vogbaBdAGu/8j/ewpvGqnQYunnLd9SM0vWcPJewPKNnWFggf0hF0pwIgwV5XK7qQ7UZ8Qg==", + "dependencies": { + "execa": "^0.10.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/run-applescript/node_modules/cross-spawn": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/run-applescript/node_modules/execa": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", + "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", + "dependencies": { + "cross-spawn": "^6.0.0", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/run-applescript/node_modules/get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/run-applescript/node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/run-applescript/node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/run-applescript/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/run-applescript/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/run-applescript/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/run-applescript/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/run-applescript/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "node_modules/run-applescript/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -8993,9 +9931,9 @@ } }, "node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "dependencies": { "tslib": "^2.1.0" } @@ -9085,9 +10023,9 @@ } }, "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "bin": { "semver": "bin/semver.js" }, @@ -9166,6 +10104,7 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -9187,7 +10126,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -9199,20 +10137,70 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "engines": { "node": ">=8" } }, "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dependencies": { - "call-bind": "^1.0.7", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -9230,7 +10218,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, "engines": { "node": ">=14" }, @@ -9287,6 +10274,22 @@ "ws": "~8.17.1" } }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/socket.io-parser": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", @@ -9299,6 +10302,38 @@ "node": ">=10.0.0" } }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/source-map": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", @@ -9335,14 +10370,6 @@ "memory-pager": "^1.0.2" } }, - "node_modules/specificity": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/specificity/-/specificity-0.4.1.tgz", - "integrity": "sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg==", - "bin": { - "specificity": "bin/specificity" - } - }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -9387,18 +10414,13 @@ } }, "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dependencies": { - "safe-buffer": "~5.1.0" + "safe-buffer": "~5.2.0" } }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -9416,7 +10438,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -9431,7 +10452,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -9445,7 +10465,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -9458,7 +10477,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -9475,6 +10493,14 @@ "node": ">=8" } }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", @@ -9496,20 +10522,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/style-data": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/style-data/-/style-data-1.4.8.tgz", - "integrity": "sha512-RBJD+YQef4PzYKqC4PQEjDvyX709mwEClYg9u0A5EPXqdSkN2BtMnF/tW7EtS9Q0FnBF+lrWsK5+bEKqA+++Dg==", + "node_modules/strtok3": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.1.tgz", + "integrity": "sha512-3JWEZM6mfix/GCJBBUrkA8p2Id2pBkyTkVCJKto55w080QBKZ+8R171fGrbiSp+yMO/u6F8/yUh7K4V9K+YCnw==", "dependencies": { - "cheerio": "^0.22.0", - "mediaquery-text": "^1.2.0", - "pick-util": "^1.1.4" + "@tokenizer/token": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" } }, "node_modules/superagent": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-9.0.2.tgz", - "integrity": "sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w==", + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.1.tgz", + "integrity": "sha512-O+PCv11lgTNJUzy49teNAWLjBZfc+A1enOwTpLlH6/rsvKcTwcdTT8m9azGkVqM7HBl5jpyZ7KTPhHweokBcdg==", "dev": true, "dependencies": { "component-emitter": "^1.3.0", @@ -9517,7 +10548,7 @@ "debug": "^4.3.4", "fast-safe-stringify": "^2.1.1", "form-data": "^4.0.0", - "formidable": "^3.5.1", + "formidable": "^3.5.4", "methods": "^1.1.2", "mime": "2.6.0", "qs": "^6.11.0" @@ -9539,13 +10570,13 @@ } }, "node_modules/supertest": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.0.0.tgz", - "integrity": "sha512-qlsr7fIC0lSddmA3tzojvzubYxvlGtzumcdHgPwbFWMISQwL22MhM2Y3LNt+6w9Yyx7559VW5ab70dgphm8qQA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.1.tgz", + "integrity": "sha512-aI59HBTlG9e2wTjxGJV+DygfNLgnWbGdZxiA/sgrnNNikIW8lbDvCtF6RnhZoJ82nU7qv7ZLjrvWqCEm52fAmw==", "dev": true, "dependencies": { "methods": "^1.1.2", - "superagent": "^9.0.1" + "superagent": "^10.2.1" }, "engines": { "node": ">=14.18.0" @@ -9588,38 +10619,37 @@ } }, "node_modules/synckit": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz", - "integrity": "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.8.tgz", + "integrity": "sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==", "dev": true, "dependencies": { - "@pkgr/core": "^0.1.0", - "tslib": "^2.6.2" + "@pkgr/core": "^0.2.4" }, "engines": { "node": "^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/unts" + "url": "https://opencollective.com/synckit" } }, "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/terser": { - "version": "5.33.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.33.0.tgz", - "integrity": "sha512-JuPVaB7s1gdFKPKTelwUyRq5Sid2A3Gko2S0PncwdBq7kN9Ti9HPWDQ06MPsEDGsZeVESjKEnyGy68quBk1w6g==", + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.41.0.tgz", + "integrity": "sha512-H406eLPXpZbAX14+B8psIuvIr8+3c+2hkuYzpMkoE0ij+NdsVATbA78vb8neA/eqrj7rywa2pIkdmWRsXW6wmw==", "dev": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", + "acorn": "^8.14.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -9631,16 +10661,16 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.10", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", - "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "version": "5.3.14", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", + "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", "dev": true, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.20", + "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.26.0" + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" }, "engines": { "node": ">= 10.13.0" @@ -9678,6 +10708,25 @@ "node": ">= 10.13.0" } }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/terser-webpack-plugin/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -9798,7 +10847,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -9819,10 +10867,32 @@ "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz", "integrity": "sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg==" }, + "node_modules/token-types": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.0.0.tgz", + "integrity": "sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA==", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } }, "node_modules/tree-kill": { "version": "1.2.2", @@ -9834,9 +10904,9 @@ } }, "node_modules/ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", "dev": true, "engines": { "node": ">=16" @@ -9846,9 +10916,9 @@ } }, "node_modules/ts-jest": { - "version": "29.2.5", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz", - "integrity": "sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==", + "version": "29.3.4", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.3.4.tgz", + "integrity": "sha512-Iqbrm8IXOmV+ggWHOTEbjwyCf2xZlUMv5npExksXohL+tk8va4Fjhb+X2+Rt9NBmgO7bJ8WpnMLOwih/DnMlFA==", "dev": true, "dependencies": { "bs-logger": "^0.2.6", @@ -9858,7 +10928,8 @@ "json5": "^2.2.3", "lodash.memoize": "^4.1.2", "make-error": "^1.3.6", - "semver": "^7.6.3", + "semver": "^7.7.2", + "type-fest": "^4.41.0", "yargs-parser": "^21.1.1" }, "bin": { @@ -9893,10 +10964,22 @@ } } }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ts-loader": { - "version": "9.5.1", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", - "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==", + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.2.tgz", + "integrity": "sha512-Qo4piXvOTWcMGIgRiuFa6nHNm+54HbYaZCKqc9eeZCLRy3XqafQgwX2F7mofrbJG3g7EEb+lkiR+z2Lic2s3Zw==", "dev": true, "dependencies": { "chalk": "^4.1.0", @@ -9995,9 +11078,9 @@ } }, "node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/type-check": { "version": "0.4.0", @@ -10050,9 +11133,9 @@ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, "node_modules/typescript": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -10071,7 +11154,6 @@ "version": "3.19.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", - "optional": true, "bin": { "uglifyjs": "bin/uglifyjs" }, @@ -10090,10 +11172,21 @@ "node": ">=8" } }, + "node_modules/uint8array-extras": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.4.0.tgz", + "integrity": "sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" }, "node_modules/universalify": { "version": "2.0.1", @@ -10113,9 +11206,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", - "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", "dev": true, "funding": [ { @@ -10142,6 +11235,11 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==" + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -10165,9 +11263,13 @@ } }, "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "bin": { "uuid": "dist/bin/uuid" } @@ -10192,10 +11294,18 @@ "node": ">=10.12.0" } }, + "node_modules/valid-data-url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/valid-data-url/-/valid-data-url-3.0.1.tgz", + "integrity": "sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA==", + "engines": { + "node": ">=10" + } + }, "node_modules/validator": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", - "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", + "version": "13.15.15", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.15.tgz", + "integrity": "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==", "engines": { "node": ">= 0.10" } @@ -10226,9 +11336,9 @@ } }, "node_modules/watchpack": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", - "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", + "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", "dev": true, "dependencies": { "glob-to-regexp": "^0.4.1", @@ -10247,19 +11357,141 @@ "defaults": "^1.0.3" } }, + "node_modules/web-resource-inliner": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/web-resource-inliner/-/web-resource-inliner-6.0.1.tgz", + "integrity": "sha512-kfqDxt5dTB1JhqsCUQVFDj0rmY+4HLwGQIsLPbyrsN9y9WV/1oFDSx3BQ4GfCv9X+jVeQ7rouTqwK53rA/7t8A==", + "dependencies": { + "ansi-colors": "^4.1.1", + "escape-goat": "^3.0.0", + "htmlparser2": "^5.0.0", + "mime": "^2.4.6", + "node-fetch": "^2.6.0", + "valid-data-url": "^3.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/web-resource-inliner/node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/dom-serializer/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/domhandler": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz", + "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==", + "dependencies": { + "domelementtype": "^2.0.1" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/domutils/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/htmlparser2": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-5.0.1.tgz", + "integrity": "sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^3.3.0", + "domutils": "^2.4.2", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/fb55/htmlparser2?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } }, "node_modules/webpack": { - "version": "5.97.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz", - "integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==", + "version": "5.99.9", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.9.tgz", + "integrity": "sha512-brOPwM3JnmOa+7kd3NsmOUOwbDAj8FT9xDsG3IW0MgbN9yZV7Oi/s/+MNQ/EcSMqw7qfoRyXPoeEWT8zLVdVGg==", "dev": true, + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", @@ -10276,9 +11508,9 @@ "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", + "schema-utils": "^4.3.2", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.10", + "terser-webpack-plugin": "^5.3.11", "watchpack": "^2.4.1", "webpack-sources": "^3.2.3" }, @@ -10308,9 +11540,9 @@ } }, "node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.2.tgz", + "integrity": "sha512-ykKKus8lqlgXX/1WjudpIEjqsafjOTcOJqxnAbMLAu/KCsDCJ6GBtvscewvTkrn24HsnvFwrSCbenFrhtcCsAA==", "dev": true, "engines": { "node": ">=10.13.0" @@ -10321,6 +11553,7 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, + "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -10334,24 +11567,47 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, + "peer": true, "engines": { "node": ">=4.0" } }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "dev": true, + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" } }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -10410,7 +11666,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -10426,7 +11681,8 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true }, "node_modules/write-file-atomic": { "version": "4.0.2", @@ -10479,7 +11735,6 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, "engines": { "node": ">=10" } @@ -10494,7 +11749,6 @@ "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -10512,7 +11766,6 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, "engines": { "node": ">=12" } From caff89974618c1fa7b9170d54ba9c0e575da5416 Mon Sep 17 00:00:00 2001 From: Taras Yurchenko Date: Fri, 12 Sep 2025 23:56:58 +0300 Subject: [PATCH 18/27] fix: fixed validPlayer guard --- src/auth/auth.controller.ts | 40 +++-- src/auth/auth.module.ts | 9 +- src/auth/auth.service.ts | 38 ++--- src/auth/guard/validPlayer.guard.ts | 2 +- .../interceptor/authCookies.interceptor.ts | 10 +- src/mail/mail.service.ts | 12 +- src/sse/sse.module.ts | 5 + src/sse/sse.service.ts | 42 ++++++ src/utils/fields/fields.ts | 142 +++++++++++++----- 9 files changed, 215 insertions(+), 85 deletions(-) create mode 100644 src/sse/sse.module.ts create mode 100644 src/sse/sse.service.ts diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts index d82800f..19f5477 100644 --- a/src/auth/auth.controller.ts +++ b/src/auth/auth.controller.ts @@ -8,11 +8,19 @@ import { Patch, Post, Res, + Sse, UseGuards, UseInterceptors, } from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { Response } from 'express'; +import { Observable } from 'rxjs'; +import { SseService } from 'src/sse/sse.service'; import { AuthService } from './auth.service'; +import { GetUser } from './decorator'; +import { GetCurrentUserId } from './decorator/get-current-user-id.decorator'; +import { GetCurrentUser } from './decorator/get-current-user.decorator'; +import { Public } from './decorator/public.decorator'; import { ChangePasswordDto, ForgotPasswordDto, @@ -20,26 +28,24 @@ import { SignInDto, SignUpDto, } from './dto'; -import { AuthGuard } from '@nestjs/passport'; -import { GetCurrentUser } from './decorator/get-current-user.decorator'; -import { GetCurrentUserId } from './decorator/get-current-user-id.decorator'; -import { Public } from './decorator/public.decorator'; -import { ApiOperation, ApiTags } from '@nestjs/swagger'; -import { GetUser } from './decorator'; -import { JwtPayloadWithRt } from './types/jwtPayloadWithRt.type'; +import { JwtRtGuard } from './guard'; import { TokenCookieInterceptor } from './interceptor/authCookies.interceptor'; +import { JwtPayloadWithRt } from './types/jwtPayloadWithRt.type'; @ApiTags('auth') @Controller('auth') export class AuthController { - constructor(private authService: AuthService) {} + constructor( + private authService: AuthService, + private sseService: SseService + ) {} @Public() @Post('/local/signup') @ApiOperation({ summary: 'signup' }) async signup(@Body() dto: SignUpDto) { - await this.authService.signup(dto); + const user = await this.authService.signup(dto); - return { status: 'success', message: 'Confirm you email' }; + return { status: 'success', message: 'Confirm you email', user }; } @Public() @@ -71,7 +77,7 @@ export class AuthController { } @Public() - @UseGuards(AuthGuard('jwt-refresh')) + @UseGuards(JwtRtGuard) @Post('refresh') @ApiOperation({ summary: 'refreshTokens' }) @UseInterceptors(TokenCookieInterceptor) @@ -104,6 +110,18 @@ export class AuthController { }; } + @Public() + @Sse('sse/:userId') + sse(@Param('userId') userId: string): Observable { + return this.sseService.getEventStream(null, userId); + } + + @Public() + @Sse('sse') + sseAll(): Observable { + return this.sseService.getEventStream(null, null); + } + @Patch('change-password') @ApiOperation({ summary: 'change-password' }) async changePassword( diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts index f269d18..ee2a21f 100644 --- a/src/auth/auth.module.ts +++ b/src/auth/auth.module.ts @@ -6,9 +6,16 @@ import { JwtAtStrategy, JwtRtStrategy } from './strategy'; import { ConfigModule } from '@nestjs/config'; import { MailModule } from 'src/mail/mail.module'; import { UserModule } from 'src/user/user.module'; +import { SseModule } from 'src/sse/sse.module'; @Module({ - imports: [JwtModule.register({}), ConfigModule, MailModule, UserModule], + imports: [ + JwtModule.register({}), + ConfigModule, + MailModule, + UserModule, + SseModule, + ], controllers: [AuthController], providers: [AuthService, JwtRtStrategy, JwtAtStrategy], }) diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index bf384c6..a3fe140 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -3,18 +3,17 @@ import { ForbiddenException, Injectable, UnauthorizedException, - UseGuards, } from '@nestjs/common'; -import { SignInDto, SignUpDto } from './dto'; -import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library'; -import { JwtService } from '@nestjs/jwt'; import { ConfigService } from '@nestjs/config'; -import { Tokens } from './types'; +import { JwtService } from '@nestjs/jwt'; +import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library'; import * as argon2 from 'argon2'; -import { JwtRtGuard } from './guard'; import { MailService } from 'src/mail/mail.service'; -import { JwtPayload } from './types/jwtPayloadType.type'; +import { SseService } from 'src/sse/sse.service'; import { UserRepository } from 'src/user/user.repository'; +import { SignInDto, SignUpDto } from './dto'; +import { Tokens } from './types'; +import { JwtPayload } from './types/jwtPayloadType.type'; import { JwtPayloadWithRt } from './types/jwtPayloadWithRt.type'; @Injectable() @@ -23,7 +22,8 @@ export class AuthService { private userRepository: UserRepository, private jwtService: JwtService, private configService: ConfigService, - private emailService: MailService + private emailService: MailService, + private sseService: SseService ) {} async signup(dto: SignUpDto) { @@ -41,11 +41,11 @@ export class AuthService { hashPasswordPromise, emailConfirmationTokenPromise, ]); - await this.userRepository.create({ + const user = await this.userRepository.create({ data: { + nickname: dto.nickname, email: dto.email, hash, - nickname: dto.nickname, emailConfirmationToken, }, }); @@ -53,6 +53,12 @@ export class AuthService { dto.email, emailConfirmationToken ); + return { + email: user.email, + id: user.id, + nickname: user.nickname, + isEmailConfirmed: user.isEmailConfirmed, + }; } catch (error) { if (error instanceof PrismaClientKnownRequestError) { if (error.code === 'P2002') { @@ -84,7 +90,7 @@ export class AuthService { await this.updateRtHash(user.id, tokens.refreshToken); return { tokens, - user: { nickname: user.nickname, email: user.email, id: user.id }, + user: { email: user.email, id: user.id }, }; } @@ -92,7 +98,6 @@ export class AuthService { const userFromDB = await this.userRepository.findByEmail(user.email); if (!userFromDB) throw new UnauthorizedException('User does not exist'); return { - nickname: userFromDB.nickname, email: userFromDB.email, id: userFromDB.id, isEmailConfirmed: userFromDB.isEmailConfirmed, @@ -112,15 +117,14 @@ export class AuthService { }, }); } - @UseGuards(JwtRtGuard) + async refreshTokens(id: string, rt: string) { const user = await this.userRepository.findById(id); - if (!user) throw new ForbiddenException('Access Denied'); if (!user.hashedRt) throw new ForbiddenException('Access Denied'); const rtMatches = await argon2.verify(user.hashedRt, rt); - if (!rtMatches) throw new ForbiddenException('Access Denied'); + if (!rtMatches) throw new ForbiddenException('Rt token does not match'); const tokens = await this.signTokens(user.id, user.email); await this.updateRtHash(user.id, tokens.refreshToken); @@ -170,7 +174,8 @@ export class AuthService { const tokens = await this.signTokens(user.id, user.email); await this.updateRtHash(user.id, tokens.refreshToken); - return { tokens, user: { nickname: user.nickname, email: user.email } }; + this.sseService.sendToUser(user.id, { type: 'email_verified' }); + return { tokens, user: { email: user.email } }; } async changePassword( @@ -259,7 +264,6 @@ export class AuthService { async updateRtHash(id: string, rt: string) { const hash = await argon2.hash(rt); - await this.userRepository.update({ where: { id, diff --git a/src/auth/guard/validPlayer.guard.ts b/src/auth/guard/validPlayer.guard.ts index bb40483..8f43824 100644 --- a/src/auth/guard/validPlayer.guard.ts +++ b/src/auth/guard/validPlayer.guard.ts @@ -32,7 +32,7 @@ export class ValidPlayerGuard implements CanActivate { } let gameId = client.data.gameId; if (!gameId) { - gameId = parse(client.handshake.headers.cookie); + gameId = parse(client.handshake.headers.cookie).gameId; } try { const player = await this.playerService.findByUserAndGameId( diff --git a/src/auth/interceptor/authCookies.interceptor.ts b/src/auth/interceptor/authCookies.interceptor.ts index 1bc602a..99caa97 100644 --- a/src/auth/interceptor/authCookies.interceptor.ts +++ b/src/auth/interceptor/authCookies.interceptor.ts @@ -24,13 +24,9 @@ export class TokenCookieInterceptor implements NestInterceptor { if (data.accessToken) { response.cookie('accessToken', data.accessToken, { httpOnly: true, - path: '/', maxAge: 1000 * 60 * 15, sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax', secure: process.env.NODE_ENV === 'production', - ...(process.env.NODE_ENV === 'production' && { - domain: `.${process.env.DOMAIN_NAME_PROD}`, - }), }); delete data.accessToken; } @@ -38,20 +34,16 @@ export class TokenCookieInterceptor implements NestInterceptor { if (data.refreshToken) { response.cookie('refreshToken', data.refreshToken, { httpOnly: true, - path: '/auth/refresh', maxAge: 1000 * 60 * 60 * 24 * 7, sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax', secure: process.env.NODE_ENV === 'production', - ...(process.env.NODE_ENV === 'production' && { - domain: `.${process.env.DOMAIN_NAME_PROD}`, - }), }); delete data.refreshToken; } } return data; - }) + }), ); } } diff --git a/src/mail/mail.service.ts b/src/mail/mail.service.ts index c674b70..daddeec 100644 --- a/src/mail/mail.service.ts +++ b/src/mail/mail.service.ts @@ -11,9 +11,9 @@ export class MailService { async sendVerificationEmail(email: string, token: string) { const confirmationUrl = `${ - process.env.NODE_ENV === 'development' - ? process.env.FRONTEND_URL_DEV - : process.env.FRONTEND_URL_PROD + this.configService.get('NODE_ENV') === 'development' + ? this.configService.get('FRONTEND_URL_DEV') + : this.configService.get('FRONTEND_URL_PROD') }/auth/confirm-email/${token}`; await this.mailerService.sendMail({ to: email, @@ -25,9 +25,9 @@ export class MailService { async sendForgotPasswordEmail(email: string, token: string) { const forgotPasswordUrl = `${ - process.env.NODE_ENV === 'development' - ? process.env.FRONTEND_URL_DEV - : process.env.FRONTEND_URL_PROD + this.configService.get('NODE_ENV') === 'development' + ? this.configService.get('FRONTEND_URL_DEV') + : this.configService.get('FRONTEND_URL_PROD') }/auth/reset-password/${token}`; await this.mailerService.sendMail({ to: email, diff --git a/src/sse/sse.module.ts b/src/sse/sse.module.ts new file mode 100644 index 0000000..81848a6 --- /dev/null +++ b/src/sse/sse.module.ts @@ -0,0 +1,5 @@ +import { Module } from '@nestjs/common'; +import { SseService } from './sse.service'; + +@Module({ providers: [SseService], exports: [SseService] }) +export class SseModule {} diff --git a/src/sse/sse.service.ts b/src/sse/sse.service.ts new file mode 100644 index 0000000..20f9253 --- /dev/null +++ b/src/sse/sse.service.ts @@ -0,0 +1,42 @@ +import { Injectable } from '@nestjs/common'; +import { Subject, Observable } from 'rxjs'; +import { filter, map } from 'rxjs/operators'; + +interface SseEvent { + room?: string; + userId?: string; + data: any; +} + +@Injectable() +export class SseService { + private eventSubject = new Subject(); + onModuleInit() { + setInterval(() => { + this.sendToAll({ + data: { hello: 'world', time: new Date().toISOString() }, + }); + }, 2000); + } + sendToRoom(room: string, data: any) { + this.eventSubject.next({ room, data }); + } + + sendToUser(userId: string, data: any) { + this.eventSubject.next({ userId, data }); + } + + sendToAll(data: any) { + this.eventSubject.next({ data }); + } + + getEventStream(room?: string, userId?: string): Observable { + return this.eventSubject.asObservable().pipe( + filter((event) => { + if (room && event.room !== room) return false; + if (userId && event.userId !== userId) return false; + return true; + }), + ); + } +} diff --git a/src/utils/fields/fields.ts b/src/utils/fields/fields.ts index fe89a82..a8be6e5 100644 --- a/src/utils/fields/fields.ts +++ b/src/utils/fields/fields.ts @@ -10,16 +10,27 @@ export const DEFAULT_FIELDS = [ imageUrl: 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/start.svg?t=2025-01-01T16%3A46%3A10.529Z', }, + { - positionForGrid: 2, + positionForGrid: 1, index: 2, - name: 'Невідомість', - specialField: true, - secret: true, + name: 'Укр Пошта', + price: 1000, + pledgePrice: 600, + redemptionPrice: 650, + branchPrice: 500, + sellBranchPrice: 450, + amountOfBranches: 0, + group: 'post', + specialField: false, line: 'horizontal-top', ownedBy: null, + color: 'yellow', + income: [100, 150, 420, 1100, 1735, 3000], + isPledged: false, + turnsToUnpledge: null, imageUrl: - 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/que1.svg', + 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/ukr_poshta.svg?t=2024-12-26T13%3A54%3A43.654Z', }, { positionForGrid: 2, @@ -33,62 +44,102 @@ export const DEFAULT_FIELDS = [ 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/que1.svg', }, { - positionForGrid: 2, + positionForGrid: 3, index: 4, - name: 'Невідомість', - specialField: true, - secret: true, + name: 'Meest', + price: 1200, + group: 'post', + pledgePrice: 720, + redemptionPrice: 800, + branchPrice: 500, + sellBranchPrice: 450, + amountOfBranches: 0, + specialField: false, line: 'horizontal-top', ownedBy: null, + color: 'yellow', + income: [120, 175, 500, 1250, 1880, 3400], + isPledged: false, + turnsToUnpledge: null, imageUrl: - 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/que1.svg', + 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/Meest.svg?t=2025-01-01T16%3A43%3A57.691Z', }, { - positionForGrid: 2, + positionForGrid: 4, index: 5, - name: 'Невідомість', - specialField: true, - secret: true, + name: 'Нова Пошта', + price: 1400, + group: 'post', + pledgePrice: 840, + redemptionPrice: 850, + branchPrice: 500, + sellBranchPrice: 450, + amountOfBranches: 0, + specialField: false, line: 'horizontal-top', ownedBy: null, + color: 'yellow', + income: [140, 200, 635, 1400, 2000, 4000], + isPledged: false, + turnsToUnpledge: null, imageUrl: - 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/que1.svg', + 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/nova_poshta.svg?t=2024-12-26T13%3A47%3A58.811Z', }, { - positionForGrid: 2, + positionForGrid: 5, index: 6, - name: 'Невідомість', + name: 'ВДНХ', specialField: true, - secret: true, + toPay: -3000, line: 'horizontal-top', ownedBy: null, imageUrl: - 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/que1.svg', + 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/vdng.svg?t=2024-12-26T13%3A55%3A02.015Z', }, { - positionForGrid: 2, + positionForGrid: 6, index: 7, - name: 'Невідомість', - specialField: true, - secret: true, + name: 'Батьківщина', + price: 1800, + pledgePrice: 1080, + redemptionPrice: 1200, + branchPrice: 600, + sellBranchPrice: 550, + amountOfBranches: 0, + group: 'political-party', + specialField: false, line: 'horizontal-top', ownedBy: null, + color: 'blue', + income: [180, 250, 715, 1500, 2275, 4850], + isPledged: false, + turnsToUnpledge: null, imageUrl: - 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/que1.svg', + 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/tumoshenko.svg?t=2024-12-26T13%3A54%3A08.461Z', }, { - positionForGrid: 2, + positionForGrid: 7, index: 8, - name: 'Невідомість', - specialField: true, - secret: true, + name: 'Європейська солідарність', + price: 2000, + pledgePrice: 1200, + redemptionPrice: 1300, + branchPrice: 600, + sellBranchPrice: 550, + amountOfBranches: 0, + group: 'political-party', + specialField: false, line: 'horizontal-top', ownedBy: null, + color: 'blue', + income: [200, 275, 760, 1710, 2400, 6660], + isPledged: false, + turnsToUnpledge: null, imageUrl: - 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/que1.svg', + 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/poroshenkoEC.svg?t=2025-01-01T16%3A44%3A54.823Z', }, { - positionForGrid: 2, + positionForGrid: 8, index: 9, name: 'Невідомість', specialField: true, @@ -99,26 +150,37 @@ export const DEFAULT_FIELDS = [ 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/que1.svg', }, { - positionForGrid: 2, + positionForGrid: 9, index: 10, - name: 'Невідомість', - specialField: true, - secret: true, + name: 'Слуга Народу', + price: 2200, + pledgePrice: 1320, + redemptionPrice: 1450, + branchPrice: 600, + sellBranchPrice: 550, + amountOfBranches: 0, + group: 'political-party', + specialField: false, line: 'horizontal-top', ownedBy: null, + color: 'blue', + income: [220, 300, 820, 2100, 4575, 7700], + isPledged: false, + turnsToUnpledge: null, imageUrl: - 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/que1.svg', + 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/sluga_naroda.svg?t=2024-12-26T13%3A53%3A35.907Z', }, + { - positionForGrid: 2, + positionForGrid: 10, index: 11, - name: 'Невідомість', + name: 'Білоцерківський Строгач', specialField: true, - secret: true, - line: 'horizontal-top', + large: true, + line: 'vertical-right', ownedBy: null, imageUrl: - 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/que1.svg', + 'https://dfwunfrhdvqnydwwytsw.supabase.co/storage/v1/object/public/MonopolyFields/prison.svg?t=2025-01-01T16%3A45%3A15.880Z', }, { positionForGrid: 12, From 834b791697a571645c07cfbf570302391d7af71c Mon Sep 17 00:00:00 2001 From: Taras Yurchenko Date: Tue, 30 Sep 2025 14:52:29 +0300 Subject: [PATCH 19/27] feat: added throwing objects not just string --- src/auth/guard/jwt.ws.guard.ts | 4 ++-- src/auth/guard/validPlayer.guard.ts | 8 ++++++++ src/game/decorators/getGameCookieWs.ts | 9 ++++++++- src/utils/exceptions/websocket-exceptions.filter.ts | 2 +- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/auth/guard/jwt.ws.guard.ts b/src/auth/guard/jwt.ws.guard.ts index 698ad8f..bebc2f0 100644 --- a/src/auth/guard/jwt.ws.guard.ts +++ b/src/auth/guard/jwt.ws.guard.ts @@ -14,7 +14,7 @@ export class WsGuard implements CanActivate { async canActivate(context: any): Promise { const client = context.switchToWs().getClient(); if (!client.handshake.headers.cookie) { - throw new WsException('Unauthorized'); + throw new WsException({ message: 'Unauthorized' }); } const userId = client.data.jwtPayload?.sub; if (userId) { @@ -28,7 +28,7 @@ export class WsGuard implements CanActivate { client.data.jwtPayload = decoded; return decoded; } catch (ex) { - throw new WsException(ex.message); + throw new WsException(ex); } } } diff --git a/src/auth/guard/validPlayer.guard.ts b/src/auth/guard/validPlayer.guard.ts index 8f43824..2eeb78f 100644 --- a/src/auth/guard/validPlayer.guard.ts +++ b/src/auth/guard/validPlayer.guard.ts @@ -34,6 +34,14 @@ export class ValidPlayerGuard implements CanActivate { if (!gameId) { gameId = parse(client.handshake.headers.cookie).gameId; } + if (!gameId) + throw new WsException({ + code: 'USER_NOT_IN_GAME', + message: 'You must join a game before performing this action', + details: { + action: 'ValidPlayerGuard', + }, + }); try { const player = await this.playerService.findByUserAndGameId( userId || client.data.jwtPayload.sub, diff --git a/src/game/decorators/getGameCookieWs.ts b/src/game/decorators/getGameCookieWs.ts index e745858..0e661a3 100644 --- a/src/game/decorators/getGameCookieWs.ts +++ b/src/game/decorators/getGameCookieWs.ts @@ -6,7 +6,14 @@ export const GetGameId = createParamDecorator( (_: undefined, context: ExecutionContext): string => { const client = context.switchToWs().getClient(); const { gameId } = parse(client.handshake.headers.cookie); - if (!gameId) throw new WsException('You are not playing'); + if (!gameId) + throw new WsException({ + code: 'USER_NOT_IN_GAME', + message: 'You must join a game before performing this action', + details: { + action: 'GetGameId', + }, + }); return gameId; } ); diff --git a/src/utils/exceptions/websocket-exceptions.filter.ts b/src/utils/exceptions/websocket-exceptions.filter.ts index b1c8d24..d1f1d8a 100644 --- a/src/utils/exceptions/websocket-exceptions.filter.ts +++ b/src/utils/exceptions/websocket-exceptions.filter.ts @@ -9,6 +9,6 @@ export class WebsocketExceptionsFilter implements WsExceptionFilter { const client: Socket = ctx.getClient(); const error = exception.getError(); const details = typeof error === 'string' ? { message: error } : error; - client.emit('error', { status: 'error', ...details }); + client.emit('error', { type: 'error', ...details }); } } From 3bc474b4794f817e4702be4b7be0a6b635f28380 Mon Sep 17 00:00:00 2001 From: Taras Yurchenko Date: Tue, 30 Sep 2025 14:55:37 +0300 Subject: [PATCH 20/27] refactor: changed getGameData to getAllGameData --- src/game/game.gateway.ts | 6 +++--- src/game/game.service.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/game/game.gateway.ts b/src/game/game.gateway.ts index 3bf0954..a630c46 100644 --- a/src/game/game.gateway.ts +++ b/src/game/game.gateway.ts @@ -121,10 +121,10 @@ export class GameGateway { return games; } - @SubscribeMessage('getGameData') - async onGetGameData(@GetGameId() gameId: string) { + @SubscribeMessage('getAllGameData') + async onGetAllGameData(@GetGameId() gameId: string) { const { game, fields, auction, secretInfo } = - await this.gameService.getGameData(gameId); + await this.gameService.getAllGameData(gameId); this.server .to(gameId) .emit('gameData', { game, fields, auction, secretInfo }); diff --git a/src/game/game.service.ts b/src/game/game.service.ts index 1e50032..d1c4f68 100644 --- a/src/game/game.service.ts +++ b/src/game/game.service.ts @@ -65,7 +65,7 @@ export class GameService { }); } - async getGameData(gameId: string) { + async getAllGameData(gameId: string) { const game = await this.getGame(gameId); const auction = this.auctionService.auctions.get(game.id); const secretInfo = this.secretService.secrets.get(game.id); From 45b9ed0b9f2fb61c3847c0bc4870c54a19999463 Mon Sep 17 00:00:00 2001 From: Taras Yurchenko Date: Wed, 1 Oct 2025 16:06:50 +0300 Subject: [PATCH 21/27] feat: added right ex --- src/auth/guard/jwt.ws.guard.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/auth/guard/jwt.ws.guard.ts b/src/auth/guard/jwt.ws.guard.ts index bebc2f0..4a95c07 100644 --- a/src/auth/guard/jwt.ws.guard.ts +++ b/src/auth/guard/jwt.ws.guard.ts @@ -14,7 +14,13 @@ export class WsGuard implements CanActivate { async canActivate(context: any): Promise { const client = context.switchToWs().getClient(); if (!client.handshake.headers.cookie) { - throw new WsException({ message: 'Unauthorized' }); + throw new WsException({ + code: 'USER_NOT_AUTHENTICATED', + message: 'You must log in before performing this action', + details: { + action: 'WsGuard', + }, + }); } const userId = client.data.jwtPayload?.sub; if (userId) { From 951a8146a13e5ac134304ac55030591aa9bbee80 Mon Sep 17 00:00:00 2001 From: Taras Yurchenko Date: Wed, 1 Oct 2025 17:19:56 +0300 Subject: [PATCH 22/27] feat: deleted wrong implementation of messageBody --- src/game/game.gateway.ts | 14 +++++--------- src/game/game.service.ts | 6 +++--- src/player/player.gateway.ts | 3 +-- src/trade/trade.gateway.ts | 3 +-- 4 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/game/game.gateway.ts b/src/game/game.gateway.ts index a630c46..8225238 100644 --- a/src/game/game.gateway.ts +++ b/src/game/game.gateway.ts @@ -248,9 +248,8 @@ export class GameGateway { async raisePrice( @ConnectedSocket() socket: Socket & { jwtPayload: JwtPayload }, @GetGameId() gameId: string, - @MessageBody() dataArray: [{ raiseBy: number; bidAmount: number }, null] + @MessageBody() data: { raiseBy: number; bidAmount: number } ) { - const data = dataArray[0]; const userId = socket.data.jwtPayload.sub; await this.auctionService.raisePrice( gameId, @@ -293,9 +292,8 @@ export class GameGateway { async onBuyBranch( @ConnectedSocket() socket: Socket & { game: Partial; jwtPayload: JwtPayload }, - @MessageBody() dataArray: [{ index: number }, null] + @MessageBody() data: { index: number } ) { - const data = dataArray[0]; const index = data.index; const game = socket.game; const userId = socket.data.jwtPayload.sub; @@ -314,9 +312,8 @@ export class GameGateway { async onSellBranch( @ConnectedSocket() socket: Socket & { game: Partial; jwtPayload: JwtPayload }, - @MessageBody() dataArray: [{ index: number }, null] + @MessageBody() data: { index: number } ) { - const data = dataArray[0]; const index = data.index; const game = socket.game; const userId = socket.data.jwtPayload.sub; @@ -344,7 +341,7 @@ export class GameGateway { gameId ); - this.server.to(gameId).emit('playerSurrendered', { + this.server.to(gameId).emit('updateGameData', { game: updatedPlayer.game, fields: updatedFields, }); @@ -355,9 +352,8 @@ export class GameGateway { async onMortgageField( @ConnectedSocket() socket: Socket & { game: Partial; jwtPayload: JwtPayload }, - @MessageBody() dataArray: [{ index: number }, null] + @MessageBody() data: { index: number } ) { - const data = dataArray[0]; const index = data.index; const game = socket.game; const { player, fields } = await this.gameService.mortgageField( diff --git a/src/game/game.service.ts b/src/game/game.service.ts index d1c4f68..5e39cde 100644 --- a/src/game/game.service.ts +++ b/src/game/game.service.ts @@ -234,13 +234,13 @@ export class GameService { startGame(game: Partial) { // We can setTimeout here for some countdown on frontend - this.webSocketProvider.server.emit('clearStartedGame', { - gameId: game.id, - }); this.webSocketProvider.server.to(game.id).emit('startGame', { game, chatId: game.chat.id, }); + this.webSocketProvider.server.emit('clearStartedGame', { + gameId: game.id, + }); this.timerService.set(game.id, game.timeOfTurn, game, this.rollDice); } diff --git a/src/player/player.gateway.ts b/src/player/player.gateway.ts index 6e7d4e2..9deafbd 100644 --- a/src/player/player.gateway.ts +++ b/src/player/player.gateway.ts @@ -36,9 +36,8 @@ export class PlayerGateway { async onUnmortgageField( @ConnectedSocket() socket: Socket & { game: Partial; jwtPayload: JwtPayload }, - @MessageBody() dataArray: [{ index: number }, null] + @MessageBody() data: { index: number } ) { - const data = dataArray[0]; const { index } = data; const game = socket.game; const userId = socket.data.jwtPayload.sub; diff --git a/src/trade/trade.gateway.ts b/src/trade/trade.gateway.ts index aff502f..9aed142 100644 --- a/src/trade/trade.gateway.ts +++ b/src/trade/trade.gateway.ts @@ -44,9 +44,8 @@ export class TradeGateway { @ConnectedSocket() socket: Socket & { game: Partial; jwtPayload: JwtPayload }, @MessageBody() - dataArray: [OfferTradeDto, null] + data: OfferTradeDto ) { - const data = dataArray[0]; const game = socket.game; const userId = socket.data.jwtPayload.sub; if (this.auctionService.getAuction(game.id)) From 7b844e4b89d46b34a5cadec234eb1242a3a8f25d Mon Sep 17 00:00:00 2001 From: Taras Yurchenko Date: Sat, 4 Oct 2025 23:54:32 +0300 Subject: [PATCH 23/27] feat: changed emitting of events that acknowledged by other event --- src/auction/auction.service.ts | 86 +++++--- src/auth/guard/jwt.ws.guard.ts | 8 +- src/chat/chat.gateway.ts | 18 +- src/game/game.gateway.ts | 156 +++++++++----- src/game/game.service.ts | 201 ++++++++++++------ .../handlers/AllPlayersInvolved.handler.ts | 5 +- .../handlers/onePlayerInvolved.handler.ts | 8 +- .../handlers/twoPlayersInvolved.handler.ts | 8 +- src/player/player.gateway.ts | 6 +- src/player/player.service.ts | 98 ++++++--- src/secret/secret.service.ts | 34 ++- src/secret/secretAnalyzer.ts | 3 +- src/trade/trade.gateway.ts | 39 ++-- src/trade/trade.service.ts | 58 +++-- 14 files changed, 502 insertions(+), 226 deletions(-) diff --git a/src/auction/auction.service.ts b/src/auction/auction.service.ts index 0658768..76d7c76 100644 --- a/src/auction/auction.service.ts +++ b/src/auction/auction.service.ts @@ -27,8 +27,8 @@ export class AuctionService { BID_TIME: number = 10000; MIN_RAISE: number = 100; auctions: Map = new Map(); - async putUpForAuction(game: Partial) { - const player = await this.getCurrentPlayerOrThrow(game); + async putUpForAuction(game: Partial, requestId: string) { + const player = await this.getCurrentPlayerOrThrow(game, requestId); const field = await this.getAuctionableFieldOrThrow( game.id, player.currentFieldIndex @@ -44,21 +44,31 @@ export class AuctionService { }); return this.getAuction(game.id); } - private async getCurrentPlayerOrThrow(game: Partial) { + private async getCurrentPlayerOrThrow( + game: Partial, + requestId?: string + ) { const player = await this.playerService.findByUserAndGameId( game.turnOfUserId, game.id ); if (!player) { - throw new WsException('No such player'); + throw new WsException({ message: 'No such player', requestId }); } return player; } - private async getAuctionableFieldOrThrow(gameId: string, fieldIndex: number) { + private async getAuctionableFieldOrThrow( + gameId: string, + fieldIndex: number, + requestId?: string + ) { const fields = await this.fieldService.getGameFields(gameId); const field = this.fieldService.findPlayerFieldByIndex(fields, fieldIndex); if (!field.price) { - throw new WsException('You cant put this field to auction'); + throw new WsException({ + message: 'This field is not auctionable', + requestId, + }); } return field; } @@ -83,19 +93,31 @@ export class AuctionService { userId: string, raiseBy: number, bidAmount: number, - lastBid: number + lastBid: number, + requestId: string ) { const lastAcceptedBidderOrFirstBid = this.findLastAcceptedBidder(auction) || auction.bidders[0]; + console.log({ bidAmount, lastAcceptedBidderOrFirstBid }); if (bidAmount !== lastAcceptedBidderOrFirstBid.bid) - throw new WsException('Bid amount is not correct'); + throw new WsException({ + message: 'Bid amount is not correct', + requestId, + }); if (lastAcceptedBidderOrFirstBid.bid + raiseBy <= lastBid) - throw new WsException('Bid is not high enough'); - if (!auction) throw new WsException('Auction wasn’t started'); + throw new WsException({ + message: 'Your raise is not big enough', + requestId, + }); + if (!auction) + throw new WsException({ message: 'Auction wasn’t started', requestId }); if (raiseBy < this.MIN_RAISE) - throw new WsException('Raise is not big enough'); + throw new WsException({ message: 'Raise is too low', requestId }); if (auction.usersRefused.includes(userId)) - throw new WsException('You refused to auction'); + throw new WsException({ + message: 'You already refused to auction', + requestId, + }); } findLastAcceptedBidder(auction: Auction) { @@ -112,24 +134,26 @@ export class AuctionService { gameId: string, userId: string, raiseBy: number, - bidAmount: number + bidAmount: number, + requestId: string ) { const player = await this.playerService.findByUserAndGameId(userId, gameId); const auction = this.getAuction(gameId); const lastBidder = this.getLastBidder(auction); - this.playerService.validatePlayerMoney(player, lastBidder.bid); + this.playerService.validatePlayerMoney(player, lastBidder.bid, requestId); this.validateRaisePrice( auction, userId, raiseBy, bidAmount, - lastBidder.bid + lastBidder.bid, + requestId ); this.timerService.clear(gameId); this.setBidderOnAuction(gameId, userId, raiseBy, false); const auctionWithAcceptedBid = await this.timerService.set( gameId, - 2000, + 200, { gameId, userId, raiseBy }, this.hightestInQueue ); @@ -137,15 +161,16 @@ export class AuctionService { if (auctionWithAcceptedBid) { this.webSocketProvider.server .to(gameId) - .emit('raisedPrice', { auction: auctionWithAcceptedBid }); + .emit('raisedPrice', { auction: auctionWithAcceptedBid, requestId }); this.timerService.set( gameId, - 15000, + 10000, { ...auctionWithAcceptedBid, gameId }, this.winAuction ); } else { this.webSocketProvider.server.to(userId).emit('error', { + requestId, message: 'Хтось одночасно поставив з вами і перебив вашу ставку', }); } @@ -171,15 +196,24 @@ export class AuctionService { return auction; } - validateRefuseAuction(auction: Auction, player: Partial) { - if (!player) throw new WsException('No such player'); - if (!auction) throw new WsException('Auction wasn’t started'); + validateRefuseAuction( + auction: Auction, + player: Partial, + requestId: string + ) { + if (!player) + throw new WsException({ message: 'No such player', requestId }); + if (!auction) + throw new WsException({ message: 'No such auction', requestId }); const lastBidder = this.getLastBidder(auction); if (player.userId === lastBidder.userId) { - throw new WsException('You are the last bidder'); + throw new WsException({ message: 'You are the last bidder', requestId }); } if (auction.usersRefused.includes(player.userId)) - throw new WsException('You already refused to auction'); + throw new WsException({ + message: 'You already refused to auction', + requestId, + }); } private addPlayerRefusal(auction: Auction, userId: string): void { @@ -191,11 +225,11 @@ export class AuctionService { return game.players.filter((player) => !player.lost); } - async refuseAuction(gameId: string, userId: string) { + async refuseAuction(gameId: string, userId: string, requestId: string) { const player = await this.playerService.findByUserAndGameId(userId, gameId); const auction = this.getAuction(gameId); const game = player.game; - this.validateRefuseAuction(auction, player); + this.validateRefuseAuction(auction, player, requestId); this.addPlayerRefusal(auction, userId); const activePlayers = this.getActivePlayers(game); const isAuctionComplete = @@ -211,7 +245,7 @@ export class AuctionService { } this.webSocketProvider.server .to(gameId) - .emit('refusedFromAuction', { auction }); + .emit('refusedFromAuction', { auction, requestId }); } hightestInQueue(args: { gameId: string; userId: string; raiseBy: number }) { diff --git a/src/auth/guard/jwt.ws.guard.ts b/src/auth/guard/jwt.ws.guard.ts index 4a95c07..d29100b 100644 --- a/src/auth/guard/jwt.ws.guard.ts +++ b/src/auth/guard/jwt.ws.guard.ts @@ -34,7 +34,13 @@ export class WsGuard implements CanActivate { client.data.jwtPayload = decoded; return decoded; } catch (ex) { - throw new WsException(ex); + throw new WsException({ + code: 'USER_NOT_AUTHENTICATED', + message: ex.message, + details: { + action: 'WsGuard', + }, + }); } } } diff --git a/src/chat/chat.gateway.ts b/src/chat/chat.gateway.ts index 98d2087..4df3410 100644 --- a/src/chat/chat.gateway.ts +++ b/src/chat/chat.gateway.ts @@ -42,30 +42,30 @@ export class ChatGateway implements OnModuleInit { @SubscribeMessage('newMessage') async onNewMessage( socket: Socket & { jwtPayload: JwtPayload }, - dataArray: [NewMessagePayloadDto, null] + data: { newMessage: NewMessagePayloadDto; requestId: string } ) { - const data = dataArray[0]; const message = await this.chatService.onNewMessage( socket.data.jwtPayload.sub, - data + data.newMessage ); - this.server.emit('onMessage', message); + this.server.emit('onMessage', { ...message, requestId: data.requestId }); } @UseGuards(WsGuard) @SubscribeMessage('newGameMessage') async onNewGameMessage( socket: Socket & { jwtPayload: JwtPayload }, - dataArray: [NewGameMessageDto, null] + data: { newMessage: NewGameMessageDto; requestId: string } ) { - const data = dataArray[0]; const message = await this.chatService.onNewMessage( socket.data.jwtPayload.sub, - data + data.newMessage ); - if (Array.from(socket.rooms).includes(data.gameId)) - this.server.to(data.gameId).emit('gameChatMessage', message); + if (Array.from(socket.rooms).includes(data.newMessage.gameId)) + this.server + .to(data.newMessage.gameId) + .emit('gameChatMessage', { ...message, requestId: data.requestId }); } @SubscribeMessage('chatData') diff --git a/src/game/game.gateway.ts b/src/game/game.gateway.ts index 8225238..2d709e0 100644 --- a/src/game/game.gateway.ts +++ b/src/game/game.gateway.ts @@ -122,31 +122,49 @@ export class GameGateway { } @SubscribeMessage('getAllGameData') - async onGetAllGameData(@GetGameId() gameId: string) { + async onGetAllGameData( + @GetGameId() gameId: string, + @MessageBody() data: { requestId: string } + ) { const { game, fields, auction, secretInfo } = - await this.gameService.getAllGameData(gameId); - this.server - .to(gameId) - .emit('gameData', { game, fields, auction, secretInfo }); + await this.gameService.getAllGameData(gameId, data.requestId); + this.server.to(gameId).emit('gameData', { + game, + fields, + auction, + secretInfo, + requestId: data.requestId, + }); } @UseGuards(WsGuard) @SubscribeMessage('createGame') - async createGame(socket: Socket) { + async createGame( + @ConnectedSocket() socket: Socket, + @MessageBody() data: { requestId: string } + ) { const createdGameWithPlayer = await this.gameService.createGame( - socket.data.jwtPayload.sub + socket.data.jwtPayload.sub, + data.requestId ); socket.join(createdGameWithPlayer.id); - return this.server.emit('newGameCreated', createdGameWithPlayer); + return this.server.emit('newGameCreated', { + game: createdGameWithPlayer, + requestId: data.requestId, + }); } @UseGuards(WsGuard) @SubscribeMessage('joinGame') - async onJoinGame(socket: Socket, dataArray: [{ id: string }, null]) { - const data = dataArray[0]; + async onJoinGame( + @ConnectedSocket() socket: Socket, + @MessageBody() data: { id: string; requestId: string } + ) { + console.log({ data }); const game = await this.gameService.joinGame( data.id, - socket.data.jwtPayload.sub + socket.data.jwtPayload.sub, + data.requestId ); this.leaveAllRoomsExceptInitial(socket); socket.join(data.id); @@ -154,25 +172,28 @@ export class GameGateway { this.gameService.startGame(game); } - this.server.emit('onParticipateGame', game); + this.server.emit('onParticipateGame', { + game, + requestId: data.requestId, + }); } @UseGuards(WsGuard) @SubscribeMessage('leaveGame') async onLeaveGame( socket: Socket & { jwtPayload: JwtPayload; user: JwtPayload }, - dataArray: [{ id: string }, null] + data: { id: string; requestId: string } ) { - const data = dataArray[0]; const game = await this.gameService.leaveGame( data.id, - socket.data.jwtPayload.sub + socket.data.jwtPayload.sub, + data.requestId ); if (game) { this.leaveAllRoomsExceptInitial(socket); this.server.emit('onParticipateGame', { - id: data.id, - ...game, + game, + requestId: data.requestId, }); } } @@ -181,20 +202,23 @@ export class GameGateway { @SubscribeMessage('rollDice') async onRollDice( @ConnectedSocket() - socket: Socket & { jwtPayload: JwtPayload; game: Partial } + socket: Socket & { jwtPayload: JwtPayload; game: Partial }, + @MessageBody() data: { requestId: string } ) { - await this.gameService.rollDice(socket.game); + await this.gameService.rollDice(socket.game, data.requestId); } @UseGuards(ValidPlayerGuard) @SubscribeMessage('payToBankForSpecialField') async onPayToBankForSpecialField( @ConnectedSocket() - socket: Socket & { jwtPayload: JwtPayload; game: Partial } + socket: Socket & { jwtPayload: JwtPayload; game: Partial }, + @MessageBody() data: { requestId: string } ) { await this.gameService.payToBankForSpecialField( socket.data.jwtPayload.sub, - socket.game + socket.game, + data.requestId ); } @@ -202,15 +226,17 @@ export class GameGateway { @SubscribeMessage('payToUserForSecret') async onPayToUserForSecret( @ConnectedSocket() - socket: Socket & { jwtPayload: JwtPayload; game: Partial } + socket: Socket & { jwtPayload: JwtPayload; game: Partial }, + @MessageBody() data: { requestId: string } ) { const game = socket.game; const userId = socket.data.jwtPayload.sub; const { game: updatedGame, secretInfo } = - await this.gameService.payToUserForSecret(game, userId); + await this.gameService.payToUserForSecret(game, userId, data.requestId); this.server.to(game.id).emit('updatePlayers', { game: updatedGame, secretInfo, + requestId: data.requestId, }); } @@ -218,29 +244,32 @@ export class GameGateway { @SubscribeMessage('payToBankForSecret') async onPayToBankForSecret( @ConnectedSocket() - socket: Socket & { jwtPayload: JwtPayload; game: Partial } + socket: Socket & { jwtPayload: JwtPayload; game: Partial }, + @MessageBody() data: { requestId: string } ) { const game = socket.game; const userId = socket.data.jwtPayload.sub; - await this.gameService.payToBankForSecret(game, userId); + await this.gameService.payToBankForSecret(game, userId, data.requestId); } @UseGuards(ValidPlayerGuard, TurnGuard) @SubscribeMessage('payForPrivateField') async onPayForPrivateField( @ConnectedSocket() - socket: Socket & { jwtPayload: JwtPayload; game: Partial } + socket: Socket & { jwtPayload: JwtPayload; game: Partial }, + @MessageBody() data: { requestId: string } ) { - await this.gameService.payForPrivateField(socket.game); + await this.gameService.payForPrivateField(socket.game, data.requestId); } @UseGuards(ValidPlayerGuard, TurnGuard) @SubscribeMessage('putUpForAuction') async onPutUpForAuction( @ConnectedSocket() - socket: Socket & { jwtPayload: JwtPayload; game: Partial } + socket: Socket & { jwtPayload: JwtPayload; game: Partial }, + @MessageBody() data: { requestId: string } ) { - await this.gameService.putUpForAuction(socket.game); + await this.gameService.putUpForAuction(socket.game, data.requestId); } @UseGuards(ValidPlayerGuard) @@ -248,14 +277,17 @@ export class GameGateway { async raisePrice( @ConnectedSocket() socket: Socket & { jwtPayload: JwtPayload }, @GetGameId() gameId: string, - @MessageBody() data: { raiseBy: number; bidAmount: number } + @MessageBody() + data: { raiseBy: number; bidAmount: number; requestId: string } ) { + console.log({ data }); const userId = socket.data.jwtPayload.sub; await this.auctionService.raisePrice( gameId, userId, data.raiseBy, - data.bidAmount + data.bidAmount, + data.requestId ); } @@ -263,28 +295,31 @@ export class GameGateway { @SubscribeMessage('refuseAuction') async refuseAuction( @ConnectedSocket() socket: Socket & { jwtPayload: JwtPayload }, - @GetGameId() gameId: string + @GetGameId() gameId: string, + @MessageBody() data: { requestId: string } ) { const userId = socket.data.jwtPayload.sub; - await this.auctionService.refuseAuction(gameId, userId); + await this.auctionService.refuseAuction(gameId, userId, data.requestId); } @UseGuards(ValidPlayerGuard, TurnGuard) @SubscribeMessage('buyField') async onBuyField( @ConnectedSocket() - socket: Socket & { jwtPayload: JwtPayload; game: Partial } + socket: Socket & { jwtPayload: JwtPayload; game: Partial }, + @MessageBody() data: { requestId: string } ) { - await this.gameService.buyField(socket.game); + await this.gameService.buyField(socket.game, data.requestId); } @UseGuards(ValidPlayerGuard, TurnGuard) @SubscribeMessage('passTurn') async onPassTurn( @ConnectedSocket() - socket: Socket & { jwtPayload: JwtPayload; game: Partial } + socket: Socket & { jwtPayload: JwtPayload; game: Partial }, + @MessageBody() data: { requestId: string } ) { - await this.gameService.passTurn(socket.game); + await this.gameService.passTurn(socket.game, data.requestId); } @UseGuards(ValidPlayerGuard) @@ -292,7 +327,7 @@ export class GameGateway { async onBuyBranch( @ConnectedSocket() socket: Socket & { game: Partial; jwtPayload: JwtPayload }, - @MessageBody() data: { index: number } + @MessageBody() data: { index: number; requestId: string } ) { const index = data.index; const game = socket.game; @@ -300,11 +335,14 @@ export class GameGateway { const { updatedGame, fields } = await this.gameService.buyBranch( game, index, - userId + userId, + data.requestId ); - this.server - .to(game.id) - .emit('updateGameData', { fields, game: updatedGame }); + this.server.to(game.id).emit('updateGameData', { + fields, + game: updatedGame, + requestId: data.requestId, + }); } @UseGuards(ValidPlayerGuard) @@ -312,7 +350,7 @@ export class GameGateway { async onSellBranch( @ConnectedSocket() socket: Socket & { game: Partial; jwtPayload: JwtPayload }, - @MessageBody() data: { index: number } + @MessageBody() data: { index: number; requestId: string } ) { const index = data.index; const game = socket.game; @@ -320,30 +358,37 @@ export class GameGateway { const { updatedGame, fields } = await this.gameService.sellBranch( game, index, - userId + userId, + data.requestId ); - this.server - .to(game.id) - .emit('updateGameData', { fields, game: updatedGame }); + this.server.to(game.id).emit('updateGameData', { + fields, + game: updatedGame, + requestId: data.requestId, + }); } @UseGuards(ValidPlayerGuard) @SubscribeMessage('surrender') async surrender( @ConnectedSocket() - socket: Socket & { game: Partial; jwtPayload: JwtPayload } + socket: Socket & { game: Partial; jwtPayload: JwtPayload }, + @MessageBody() data: { requestId: string } ) { const userId = socket.data.jwtPayload.sub; const gameId = socket.game.id; const { updatedPlayer, updatedFields } = await this.gameService.loseGame( userId, - gameId + gameId, + null, + data.requestId ); this.server.to(gameId).emit('updateGameData', { game: updatedPlayer.game, fields: updatedFields, + requestId: data.requestId, }); } @@ -352,17 +397,20 @@ export class GameGateway { async onMortgageField( @ConnectedSocket() socket: Socket & { game: Partial; jwtPayload: JwtPayload }, - @MessageBody() data: { index: number } + @MessageBody() data: { index: number; requestId: string } ) { const index = data.index; const game = socket.game; const { player, fields } = await this.gameService.mortgageField( game, index, - socket.data.jwtPayload.sub + socket.data.jwtPayload.sub, + data.requestId ); - this.server - .to(game.id) - .emit('updateGameData', { fields, game: player.game }); + this.server.to(game.id).emit('updateGameData', { + fields, + game: player.game, + requestId: data.requestId, + }); } } diff --git a/src/game/game.service.ts b/src/game/game.service.ts index 5e39cde..035252e 100644 --- a/src/game/game.service.ts +++ b/src/game/game.service.ts @@ -65,15 +65,19 @@ export class GameService { }); } - async getAllGameData(gameId: string) { - const game = await this.getGame(gameId); - const auction = this.auctionService.auctions.get(game.id); - const secretInfo = this.secretService.secrets.get(game.id); - const fields = await this.fieldService.getGameFields(game.id); - return { game, auction, secretInfo, fields }; + async getAllGameData(gameId: string, requestId: string) { + try { + const game = await this.getGame(gameId); + const auction = this.auctionService.auctions.get(game.id); + const secretInfo = this.secretService.secrets.get(game.id); + const fields = await this.fieldService.getGameFields(game.id); + return { game, auction, secretInfo, fields }; + } catch (error) { + throw new WsException({ message: 'Coundlt get game data', requestId }); + } } - async createGame(userId: string) { + async createGame(userId: string, requestId?: string) { const activePlayer = await this.gameRepository.findFirst({ where: { OR: [{ status: 'LOBBY' }, { status: 'ACTIVE' }], @@ -85,7 +89,11 @@ export class GameService { }, }); - if (activePlayer) throw new WsException('You already have active game'); + if (activePlayer) + throw new WsException({ + message: 'You already have active game', + requestId, + }); const newGame = await this.gameRepository.create({ data: { playersCapacity: 4, // TODO change players capacity to dynamic number @@ -145,7 +153,7 @@ export class GameService { }); } - async joinGame(gameId: string, userId: string) { + async joinGame(gameId: string, userId: string, requestId: string) { const playerWithUserId = await this.playerService.findFirst({ where: { userId, @@ -157,7 +165,10 @@ export class GameService { }, }); if (playerWithUserId) - throw new WsException('You already in game, leave it first'); + throw new WsException({ + message: 'You already in game, leave it first', + requestId, + }); const game = await this.gameRepository.findFirst({ where: { id: gameId }, include: { @@ -177,7 +188,10 @@ export class GameService { (player) => player.userId === userId ); if (alreadyJoined || game.status !== 'LOBBY') - throw new WsException('You cannot join this game'); + throw new WsException({ + message: 'You cannot join this game', + requestId, + }); const color = this.playerService.COLORS[game.players.length]; const player = await this.playerService.create({ color, @@ -244,11 +258,12 @@ export class GameService { this.timerService.set(game.id, game.timeOfTurn, game, this.rollDice); } - async leaveGame(gameId: string, userId: string) { + async leaveGame(gameId: string, userId: string, requestId: string) { const player = await this.playerService.findFirst({ where: { userId, gameId }, }); - if (!player) throw new WsException('You are not in this game'); + if (!player) + throw new WsException({ message: 'You are not in this game', requestId }); await this.playerService.deleteById(player.id); @@ -292,11 +307,20 @@ export class GameService { return `${firstDice}:${secondDice}`; } - async rollDice(game: Partial) { - if (game.dices) throw new WsException('You have already rolled dices'); + async rollDice(game: Partial, requestId?: string) { + if (game.dices) + throw new WsException({ + message: 'You have already rolled dices', + requestId, + }); this.timerService.clear(game.id); const { playerNextField, fields, updatedGame } = await this.makeTurn(game); - await this.processRolledDices(updatedGame, playerNextField, fields); + await this.processRolledDices( + updatedGame, + playerNextField, + fields, + requestId + ); } private async handlePaymentField( @@ -337,7 +361,8 @@ export class GameService { async processRolledDices( game: Partial, playerNextField?: FieldDocument, - fields?: FieldDocument[] + fields?: FieldDocument[], + requestId?: string ) { if (!playerNextField) { playerNextField = await this.findCurrentFieldFromGame(game); @@ -349,6 +374,7 @@ export class GameService { ); this.webSocketProvider.server.to(game.id).emit('rolledDice', { ...(fields !== undefined && { fields }), + requestId, game, }); const chain = new HandlerChain(); @@ -441,6 +467,7 @@ export class GameService { game: Partial; amount: number; userId: string; + requestId?: string; }) { const { updatedGame } = await this.paymentService.transferWithBank( argsObj.game, @@ -449,25 +476,32 @@ export class GameService { ); const secretInfo = this.secretService.secrets.get(updatedGame.id); if (!secretInfo) { - await this.passTurn(updatedGame); + await this.passTurn(updatedGame, argsObj.requestId); } else { this.webSocketProvider.server.to(updatedGame.id).emit('updatePlayers', { game: updatedGame, secretInfo, + requestId: argsObj.requestId, }); } } - async payToBankForSpecialField(userId: string, game: Partial) { + async payToBankForSpecialField( + userId: string, + game: Partial, + requestId?: string + ) { const currentField = await this.findCurrentFieldWithUserId(game); if (!game.dices && !currentField.toPay) - throw new WsException( - 'You cant pay for that field because its not special field' - ); + throw new WsException({ + message: 'You cant pay for that field because its not special field', + requestId, + }); return this.transferWithBank({ game: game, userId, amount: currentField.toPay, + requestId, }); } @@ -493,12 +527,12 @@ export class GameService { await this.passTurn(updatedPlayer.game); } - async putUpForAuction(game: Partial) { - const auction = await this.auctionService.putUpForAuction(game); + async putUpForAuction(game: Partial, requestId?: string) { + const auction = await this.auctionService.putUpForAuction(game, requestId); const updatedGame = await this.updateGameWithNewTurn(game, 15000); this.webSocketProvider.server .to(game.id) - .emit('hasPutUpForAuction', { game: updatedGame, auction }); + .emit('hasPutUpForAuction', { game: updatedGame, auction, requestId }); this.timerService.set(game.id, 15000, updatedGame, this.passTurn); } @@ -647,12 +681,12 @@ export class GameService { ); } - async buyField(game: Partial) { - await this.processBuyField(game); - this.passTurn(game); + async buyField(game: Partial, requestId: string) { + await this.processBuyField(game, requestId); + this.passTurn(game, requestId); } - async processBuyField(game: Partial) { + async processBuyField(game: Partial, requestId: string) { const player = this.playerService.findPlayerWithTurn(game); const fields = await this.fieldService.getGameFields(game.id); const field = this.fieldService.findPlayerFieldByIndex( @@ -660,7 +694,10 @@ export class GameService { player.currentFieldIndex ); if (field.ownedBy) { - throw new WsException('Field is already owned'); + throw new WsException({ + message: 'This field is already owned', + requestId, + }); } if ( !field.price || @@ -668,7 +705,7 @@ export class GameService { field.ownedBy === player.userId || this.auctionService.getAuction(game.id) ) { - throw new WsException('You cant buy this field'); + throw new WsException({ message: 'You cant buy this field', requestId }); } this.timerService.clear(game.id); field.ownedBy = game.turnOfUserId; @@ -686,23 +723,14 @@ export class GameService { return game.players.filter((player) => !player.lost); } - async passTurn(game: Partial, fromEvent: boolean = false) { + async passTurn(game: Partial, requestId?: string) { const fields = await this.fieldService.getGameFields(game.id); - const currentPlayer = this.playerService.findPlayerWithTurn(game); - const currentField = this.fieldService.findPlayerFieldByIndex( - fields, - currentPlayer.currentFieldIndex - ); - if ( - fromEvent && - !currentField.large && - currentField.ownedBy !== game.turnOfUserId - ) { - throw new WsException('You cant pass turn with that field'); - } if (!game.dices) { - throw new WsException('You have to roll dices first'); + throw new WsException({ + message: 'You have to roll dices first', + requestId, + }); } const dices = ''; this.auctionService.setAuction(game.id, null); @@ -727,18 +755,22 @@ export class GameService { }); this.webSocketProvider.server .to(game.id) - .emit('passTurnToNext', { game: updatedGame, fields }); + .emit('passTurnToNext', { game: updatedGame, fields, requestId }); this.timerService.set(game.id, game.timeOfTurn, updatedGame, this.rollDice); } - async payForPrivateField(game: Partial) { + async payForPrivateField(game: Partial, requestId?: string) { const currentField = await this.findCurrentFieldWithUserId(game); if (!game.dices || !currentField.ownedBy) - throw new WsException('You cant pay for that field'); + throw new WsException({ + message: 'You cant pay for that field', + requestId, + }); this.timerService.clear(game.id); const { updatedGame, fields: updatedFields } = await this.processPayingForField(game, currentField); this.webSocketProvider.server.to(game.id).emit('payedForField', { + requestId, game: updatedGame, fields: updatedFields, }); @@ -886,17 +918,27 @@ export class GameService { return { updatedGame }; } - async buyBranch(game: Partial, index: number, userId: string) { + async buyBranch( + game: Partial, + index: number, + userId: string, + requestId: string + ) { const fieldToBuyBranch = await this.playerService.checkWhetherPlayerHasAllGroup( game, index, - userId + userId, + true, + requestId ); - this.playerService.checkFieldHasMaxBranches(fieldToBuyBranch); + this.playerService.checkFieldHasMaxBranches(fieldToBuyBranch, requestId); const playerToPay = game.players.find((player) => player.userId === userId); if (playerToPay.money < fieldToBuyBranch.branchPrice) { - throw new WsException('You dont have enough money to buy branch'); + throw new WsException({ + message: 'You dont have enough money to buy branch', + requestId, + }); } const player = await this.playerService.decrementMoneyWithUserAndGameId( userId, @@ -918,15 +960,21 @@ export class GameService { return { updatedGame, fields }; } - async sellBranch(game: Partial, index: number, userId: string) { + async sellBranch( + game: Partial, + index: number, + userId: string, + requestId: string + ) { const fieldToSellBranch = await this.playerService.checkWhetherPlayerHasAllGroup( game, index, userId, - false + false, + requestId ); - this.playerService.checkFieldHasBranches(fieldToSellBranch); + this.playerService.checkFieldHasBranches(fieldToSellBranch, requestId); const player = await this.playerService.incrementMoneyWithUserAndGameId( userId, game.id, @@ -947,7 +995,12 @@ export class GameService { return { updatedGame, fields }; } - async loseGame(userId: string, gameId: string, fields?: FieldDocument[]) { + async loseGame( + userId: string, + gameId: string, + fields?: FieldDocument[], + requestId?: string + ) { if (!fields) { fields = await this.fieldService.getGameFields(gameId); } @@ -981,26 +1034,36 @@ export class GameService { this.webSocketProvider.server.to(game.id).emit('playerWon', { game }); return { updatedPlayer, fields }; } - this.passTurn(updatedGame); + this.passTurn(updatedGame, requestId); return { updatedPlayer, updatedFields: fields }; } - mortgageField(game: Partial, index: number, userId?: string) { + mortgageField( + game: Partial, + index: number, + userId: string, + requestId?: string + ) { const auction = this.auctionService.auctions.get(game.id); const secretInfo = this.secretService.secrets.get(game.id); if (auction || secretInfo) - throw new WsException( - 'You cant pledge field while auction or secret is active' - ); - return this.playerService.pledgeField(game, index, userId); + throw new WsException({ + message: 'You cannot pledge field right now', + requestId, + }); + return this.playerService.pledgeField(game, index, userId, requestId); } - async payToUserForSecret(game: Partial, userId: string) { + async payToUserForSecret( + game: Partial, + userId: string, + requestId?: string + ) { const { game: updatedGame, secretInfo, loseGame, - } = await this.secretService.payToUserForSecret(game, userId); + } = await this.secretService.payToUserForSecret(game, userId, requestId); let gameAfterLoss = null; if (loseGame) { gameAfterLoss = await this.loseGame(userId, game.id); @@ -1008,15 +1071,21 @@ export class GameService { return { game: loseGame ? gameAfterLoss : updatedGame, secretInfo }; } - async payToBankForSecret(game: Partial, userId: string) { + async payToBankForSecret( + game: Partial, + userId: string, + requestId?: string + ) { const amountToPay = await this.secretService.payToBankForSecret( game, - userId + userId, + requestId ); await this.transferWithBank({ game: game, userId, amount: amountToPay, + requestId, }); } } diff --git a/src/game/handlers/AllPlayersInvolved.handler.ts b/src/game/handlers/AllPlayersInvolved.handler.ts index 967601d..cecba3c 100644 --- a/src/game/handlers/AllPlayersInvolved.handler.ts +++ b/src/game/handlers/AllPlayersInvolved.handler.ts @@ -3,7 +3,10 @@ import { BaseHandler } from '../../common/base.handler'; import { WsException } from '@nestjs/websockets'; export class AllPlayersInvolvedHandler extends BaseHandler { throw() { - throw new WsException('You get money from all u dont have to pay'); + throw new WsException({ + message: 'You get money from all u dont have to pay', + requestId: this.analyzer.requestId, + }); } canHandle() { return this.analyzer.isAllPlayersInvolved() || this.throw(); diff --git a/src/game/handlers/onePlayerInvolved.handler.ts b/src/game/handlers/onePlayerInvolved.handler.ts index 3127835..580e2f9 100644 --- a/src/game/handlers/onePlayerInvolved.handler.ts +++ b/src/game/handlers/onePlayerInvolved.handler.ts @@ -8,9 +8,11 @@ export class OnePlayerInvolvedHandler extends BaseHandler { handle() { if (this.analyzer.secretInfo.amounts[0] > 0) { - throw new WsException( - 'You cant pay to bank because one user and he doesnt have to pay' - ); + throw new WsException({ + message: + 'You cant pay to bank because one user and he doesnt have to pay', + requestId: this.analyzer.requestId, + }); } } } diff --git a/src/game/handlers/twoPlayersInvolved.handler.ts b/src/game/handlers/twoPlayersInvolved.handler.ts index 92ebf91..c3439e9 100644 --- a/src/game/handlers/twoPlayersInvolved.handler.ts +++ b/src/game/handlers/twoPlayersInvolved.handler.ts @@ -19,9 +19,11 @@ export class TwoPlayersInvolvedHandler extends BaseHandler { this.analyzer.userId ); if (this.analyzer.secretInfo.amounts[index] > 0) { - throw new WsException( - 'You cant pay to bank two users and the one wants to pay dont have to' - ); + throw new WsException({ + message: + 'You cant pay to bank two users and the one wants to pay dont have to', + requestId: this.analyzer.requestId, + }); } } } diff --git a/src/player/player.gateway.ts b/src/player/player.gateway.ts index 9deafbd..3ef7a88 100644 --- a/src/player/player.gateway.ts +++ b/src/player/player.gateway.ts @@ -36,7 +36,7 @@ export class PlayerGateway { async onUnmortgageField( @ConnectedSocket() socket: Socket & { game: Partial; jwtPayload: JwtPayload }, - @MessageBody() data: { index: number } + @MessageBody() data: { index: number; requestId: string } ) { const { index } = data; const game = socket.game; @@ -44,10 +44,12 @@ export class PlayerGateway { const { player, fields } = await this.playerService.unmortgageField( game, index, - userId + userId, + data.requestId ); this.server.to(game.id).emit('updateGameData', { fields, + requestId: data.requestId, game: player.game, }); } diff --git a/src/player/player.service.ts b/src/player/player.service.ts index c9a4c4d..5d463f4 100644 --- a/src/player/player.service.ts +++ b/src/player/player.service.ts @@ -183,15 +183,19 @@ export class PlayerService { async checkWhetherPlayerHasAllGroup( game: Partial, index: number, - userId?: string, - buying: boolean = true + userId: string, + buying: boolean = true, + requestId: string ) { const fields = await this.fieldService.getGameFields(game.id); const playerUserId = userId ? userId : game.turnOfUserId; const userFields = fields.filter((field) => field.ownedBy === playerUserId); const userFieldsIndexes = userFields.map((field) => field.index); if (!userFieldsIndexes.includes(index)) - throw new WsException('You dont have this field'); + throw new WsException({ + message: 'You do not own this field', + requestId, + }); const fieldToBuyBranch = this.fieldService.findPlayerFieldByIndex( fields, index @@ -203,35 +207,56 @@ export class PlayerService { (f) => f.group === fieldToBuyBranch.group ); if (groupFields.length !== userGroupFields.length) - throw new WsException('You dont have all group fields'); + throw new WsException({ + message: 'You must own all fields in group to buy branches', + requestId, + }); const isBuyingHotel = buying && fieldToBuyBranch.amountOfBranches === 4; const isBuyingHouse = buying && fieldToBuyBranch.amountOfBranches < 4; const isSellingHotel = !buying && fieldToBuyBranch.amountOfBranches === 5; if (isBuyingHotel && game.hotelsQty <= 0) { - throw new WsException('You must have at least 1 hotel in the bank'); + throw new WsException({ + message: 'You must have at least 1 hotel in the bank', + requestId, + }); } if (isBuyingHouse && game.housesQty <= 0) { - throw new WsException('You must have at least 1 house in the bank'); + throw new WsException({ + message: 'You must have at least 1 house in the bank', + requestId, + }); } if (isSellingHotel && game.housesQty < 4) { - throw new WsException('You must have at least 4 houses in the bank'); + throw new WsException({ + message: 'There must be at least 4 houses in the bank to sell a hotel', + requestId, + }); } if (buying && fieldToBuyBranch.isPledged) { - throw new WsException('You cannot buy a branch for a pledged field'); + throw new WsException({ + message: 'You cant buy branches on a pledged field', + requestId, + }); } - this.checkBuyingOrSellingEvenly(groupFields, fieldToBuyBranch, buying); + this.checkBuyingOrSellingEvenly( + groupFields, + fieldToBuyBranch, + buying, + requestId + ); return fieldToBuyBranch; } checkBuyingOrSellingEvenly( groupOfFields: FieldDocument[], field: FieldDocument, - buying: boolean + buying: boolean, + requestId: string ) { const buyingEvenly = groupOfFields.every((groupField) => { const probableDifferenceOfBranches = Math.abs( @@ -243,26 +268,39 @@ export class PlayerService { ); }); if (!buyingEvenly) - throw new WsException('You must buy/sell branches evenly in group'); + throw new WsException({ + message: 'You must buy/sell branches evenly in group', + requestId, + }); } - checkFieldHasMaxBranches(field: FieldDocument) { + checkFieldHasMaxBranches(field: FieldDocument, requestId: string) { if (field.amountOfBranches >= 5) - throw new WsException('This field has max amount of branches'); + throw new WsException({ + message: 'This field has max amount of branches', + requestId, + }); } - checkFieldHasBranches(field: FieldDocument) { + checkFieldHasBranches(field: FieldDocument, requestId: string) { if (field.amountOfBranches >= 6) - throw new WsException('This field has max amount of branches'); + throw new WsException({ + message: 'This field has no branches to buy', + requestId, + }); if (field.amountOfBranches <= 0) { - throw new WsException('You do not have any branches to sell'); + throw new WsException({ + message: 'This field has no branches to sell', + requestId, + }); } } async pledgeField( game: Partial, index: number, - userId?: string + userId: string, + requestId: string ) { const fields = await this.fieldService.getGameFields(game.id); const playerUserId = userId ? userId : game.turnOfUserId; @@ -271,12 +309,13 @@ export class PlayerService { index ); if (fieldToPledge.isPledged) { - throw new WsException('Field is already pledged'); + throw new WsException({ message: 'Field is already pledged', requestId }); } if (fieldToPledge.amountOfBranches > 0) { - throw new WsException( - 'You must have some brances so you cant pledge the field' - ); + throw new WsException({ + message: 'You must sell all branches before pledging', + requestId, + }); } const player = await this.incrementMoneyWithUserAndGameId( playerUserId, @@ -295,7 +334,8 @@ export class PlayerService { async unmortgageField( game: Partial, index: number, - userId?: string + userId: string, + requestId: string ) { const playerUserId = userId ? userId : game.turnOfUserId; const fields = await this.fieldService.getGameFields(game.id); @@ -304,7 +344,7 @@ export class PlayerService { index ); if (!fieldToUnmortgage.isPledged) { - throw new WsException('Field is not pledged'); + throw new WsException({ message: 'Field is not pledged', requestId }); } fieldToUnmortgage.isPledged = false; fieldToUnmortgage.turnsToUnpledge = null; @@ -346,9 +386,15 @@ export class PlayerService { }); } - validatePlayerMoney(player: Partial, moneyNeeded: number) { - if (!player) throw new WsException('No such player'); - if (player.money <= moneyNeeded) throw new WsException('Not enough money'); + validatePlayerMoney( + player: Partial, + moneyNeeded: number, + requestId: string + ) { + if (!player) + throw new WsException({ message: 'No such player', requestId }); + if (player.money <= moneyNeeded) + throw new WsException({ message: 'Not enough money', requestId }); } choseRandomPlayer(players: Partial) { diff --git a/src/secret/secret.service.ts b/src/secret/secret.service.ts index 65ecd4a..9867a5e 100644 --- a/src/secret/secret.service.ts +++ b/src/secret/secret.service.ts @@ -85,13 +85,23 @@ export class SecretService { } } - async payToUserForSecret(game: Partial, userId: string) { + async payToUserForSecret( + game: Partial, + userId: string, + requestId?: string + ) { let secretInfo = this.secrets.get(game.id); if (!secretInfo.users.includes(userId)) - throw new WsException('You cant pay for that secret'); + throw new WsException({ + message: 'You cant pay for that secret', + requestId, + }); const amount = secretInfo.amounts[1]; if (amount > 0) - throw new WsException('You dont have to pay for this secret field'); + throw new WsException({ + message: 'You dont have to pay for this secret field', + requestId, + }); const userToGetId = secretInfo.users[0]; const indexOfUser = secretInfo.users.indexOf(userId); const player = game.players.find((player) => player.userId === userId); @@ -235,15 +245,21 @@ export class SecretService { }; } - async payToBankForSecret(game: Partial, userId: string) { + async payToBankForSecret( + game: Partial, + userId: string, + requestId?: string + ) { const secretInfo = this.secrets.get(game.id); - if (!secretInfo) throw new WsException('No secret found'); + if (!secretInfo) + throw new WsException({ message: 'No secret found', requestId }); if (!secretInfo.users.includes(userId)) { - throw new WsException( - 'You cant pay to bank because no user in secretInfo' - ); + throw new WsException({ + message: 'You cant pay to bank because no user in secretInfo', + requestId, + }); } - const secretAnalyzer = new SecretAnalyzer(secretInfo, userId); + const secretAnalyzer = new SecretAnalyzer(secretInfo, userId, requestId); const chain = new HandlerChain(); chain.addHandlers( new OnePlayerInvolvedHandler(secretAnalyzer), diff --git a/src/secret/secretAnalyzer.ts b/src/secret/secretAnalyzer.ts index 7b77606..8643ed2 100644 --- a/src/secret/secretAnalyzer.ts +++ b/src/secret/secretAnalyzer.ts @@ -3,7 +3,8 @@ import { SecretInfo } from 'src/game/types/secretInfo.type'; export class SecretAnalyzer { constructor( public readonly secretInfo: SecretInfo, - public readonly userId: string + public readonly userId: string, + public readonly requestId?: string ) {} isOneUserHaveToPay() { return this.secretInfo.users.length === 1 && this.secretInfo.amounts[0] < 0; diff --git a/src/trade/trade.gateway.ts b/src/trade/trade.gateway.ts index 9aed142..f35282c 100644 --- a/src/trade/trade.gateway.ts +++ b/src/trade/trade.gateway.ts @@ -44,14 +44,21 @@ export class TradeGateway { @ConnectedSocket() socket: Socket & { game: Partial; jwtPayload: JwtPayload }, @MessageBody() - data: OfferTradeDto + data: { tradeOffer: OfferTradeDto; requestId: string } ) { const game = socket.game; const userId = socket.data.jwtPayload.sub; if (this.auctionService.getAuction(game.id)) - throw new WsException('Cannot offer trade while auction'); - await this.tradeService.validateTradeData(game, data); - const trade = { ...data, fromUserId: userId } as Trade; + throw new WsException({ + message: 'Cannot offer trade while auction', + requestId: data.requestId, + }); + await this.tradeService.validateTradeData( + game, + data.tradeOffer, + data.requestId + ); + const trade = { ...data.tradeOffer, fromUserId: userId } as Trade; this.tradeService.setTrade(game.id, trade); const fromPlayer = game.players.find( (player) => player.userId === trade.fromUserId @@ -64,35 +71,43 @@ export class TradeGateway { chatId: game.chat.id, }); this.webSocketProvider.server.to(game.id).emit('gameChatMessage', message); - this.tradeService.handleOfferTrade({ game, trade }); + this.tradeService.handleOfferTrade({ + game, + trade, + requestId: data.requestId, + }); } @UseGuards(ValidPlayerGuard) @SubscribeMessage('refuseFromTrade') async refuseFromTrade( @ConnectedSocket() - socket: Socket & { game: Partial; jwtPayload: JwtPayload } + socket: Socket & { game: Partial; jwtPayload: JwtPayload }, + @MessageBody() data: { requestId: string } ) { const game = socket.game; - this.tradeService.refuseFromTrade(game); + this.tradeService.refuseFromTrade(game, data.requestId); } @UseGuards(ValidPlayerGuard) @SubscribeMessage('acceptTrade') async acceptTrade( @ConnectedSocket() - socket: Socket & { game: Partial; jwtPayload: JwtPayload } + socket: Socket & { game: Partial; jwtPayload: JwtPayload }, + @MessageBody() data: { requestId: string } ) { const game = socket.game; const trade = this.tradeService.getTrade(game.id); const { updatedGame, fields } = await this.tradeService.acceptTrade( game, trade, - socket.data.jwtPayload.sub + socket.data.jwtPayload.sub, + data.requestId ); - const data = { fields }; + const gameData = { fields }; + gameData['requestId'] = data.requestId; if (updatedGame) { - data['game'] = updatedGame; + gameData['game'] = updatedGame; } const fromPlayer = game.players.find( (player) => player.userId === trade.fromUserId @@ -105,6 +120,6 @@ export class TradeGateway { chatId: game.chat.id, }); this.webSocketProvider.server.to(game.id).emit('gameChatMessage', message); - this.webSocketProvider.server.to(game.id).emit('updateGameData', data); + this.webSocketProvider.server.to(game.id).emit('updateGameData', gameData); } } diff --git a/src/trade/trade.service.ts b/src/trade/trade.service.ts index e3b8263..c216884 100644 --- a/src/trade/trade.service.ts +++ b/src/trade/trade.service.ts @@ -30,14 +30,18 @@ export class TradeService { return this.trades.get(gameId); } - async validateTradeData(game: Partial, data: OfferTradeDto) { + async validateTradeData( + game: Partial, + data: OfferTradeDto, + requestId: string + ) { if ( data.offerFieldsIndexes.length === 0 && data.wantedFieldsIndexes.length === 0 && data.offeredMoney <= 0 && data.wantedMoney <= 0 ) { - throw new WsException('You must offer something'); + throw new WsException({ message: 'Invalid trade data', requestId }); } const fields = await this.fieldService.getGameFields(game.id); if (data.offerFieldsIndexes.length > 0) { @@ -49,7 +53,10 @@ export class TradeService { userFieldsIndexes.includes(index) ); if (!hasAllOfferFields) { - throw new WsException('You dont have all offer fields'); + throw new WsException({ + message: 'You dont have all offer fields', + requestId, + }); } } if (data.wantedFieldsIndexes.length > 0) { @@ -63,22 +70,29 @@ export class TradeService { otherUserFieldsIndexes.includes(index) ); if (!hasAllWantedFields) { - throw new WsException('Other player doesnt have all wanted fields'); + throw new WsException({ + message: 'The other player does not have all wanted fields', + requestId, + }); } } } - async handleOfferTrade(data: { game: Partial; trade: Trade }) { + async handleOfferTrade(data: { + game: Partial; + trade: Trade; + requestId: string; + }) { const { updatedGame } = await this.gameService.passTurnToUser({ game: data.game, toUserId: data.trade.toUserId, }); this.webSocketProvider.server .to(data.game.id) - .emit('updateGameData', { game: updatedGame }); + .emit('updateGameData', { game: updatedGame, requestId: data.requestId }); this.webSocketProvider.server .to(data.trade.toUserId) - .emit('tradeOffered', { trade: data.trade }); + .emit('tradeOffered', { trade: data.trade, requestId: data.requestId }); this.timerService.set( updatedGame.id, 10000, @@ -87,10 +101,22 @@ export class TradeService { ); } - async acceptTrade(game: Partial, trade: Trade, userId: string) { - if (!trade) throw new WsException('There is no trade to accept'); + async acceptTrade( + game: Partial, + trade: Trade, + userId: string, + requestId: string + ) { + if (!trade) + throw new WsException({ + message: 'There is no trade to accept', + requestId, + }); if (trade.toUserId !== userId) - throw new WsException('You cant accept this trade'); + throw new WsException({ + message: 'You are not allowed to accept this trade', + requestId, + }); this.timerService.clear(game.id); const fields = await this.fieldService.getGameFields(game.id); if (trade.offerFieldsIndexes.length > 0) { @@ -141,9 +167,13 @@ export class TradeService { return { fields, updatedGame }; } - async refuseFromTrade(game: Partial) { + async refuseFromTrade(game: Partial, requestId?: string) { const trade = this.getTrade(game.id); - if (!trade) throw new WsException('There is no trade to refuse'); + if (!trade) + throw new WsException({ + message: 'There is no trade to refuse', + requestId, + }); this.setTrade(game.id, null); const { updatedGame } = await this.gameService.passTurnToUser({ @@ -161,6 +191,8 @@ export class TradeService { text: `${toPlayer.user.nickname} відхилив угоду!`, chatId: game.chat.id, }); - this.webSocketProvider.server.to(game.id).emit('gameChatMessage', message); + this.webSocketProvider.server + .to(game.id) + .emit('gameChatMessage', { requestId, ...message }); } } From de7952186b620c2d065382cd54eae2789bf843b5 Mon Sep 17 00:00:00 2001 From: Taras Yurchenko Date: Sun, 5 Oct 2025 13:43:09 +0300 Subject: [PATCH 24/27] fix: auction --- src/auction/auction.service.ts | 1 + src/game/types/auction.type.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/auction/auction.service.ts b/src/auction/auction.service.ts index 76d7c76..bd37ef1 100644 --- a/src/auction/auction.service.ts +++ b/src/auction/auction.service.ts @@ -37,6 +37,7 @@ export class AuctionService { const bidder = this.createInitialBidder(field.price); const turnEnds = this.timerService.calculateFutureTime(this.BID_TIME); this.setAuction(game.id, { + bidTimeSec: this.BID_TIME / 1000, fieldIndex: field.index, bidders: [bidder], turnEnds, diff --git a/src/game/types/auction.type.ts b/src/game/types/auction.type.ts index 1f20be3..955a303 100644 --- a/src/game/types/auction.type.ts +++ b/src/game/types/auction.type.ts @@ -5,6 +5,7 @@ export interface Bidder { } export interface Auction { + bidTimeSec: number; fieldIndex: number; bidders: Bidder[]; turnEnds: string; From 9c31966f719e4c6b5c8324d17c5b95fd22200bdc Mon Sep 17 00:00:00 2001 From: Taras Yurchenko <115026707+TarasBlatnoi@users.noreply.github.com> Date: Mon, 13 Oct 2025 17:32:21 +0300 Subject: [PATCH 25/27] Update README.md --- README.md | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/README.md b/README.md index 00a13b1..89ac359 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,3 @@ -

- Nest Logo -

- -[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456 -[circleci-url]: https://circleci.com/gh/nestjs/nest - -

A progressive Node.js framework for building efficient and scalable server-side applications.

-

-NPM Version -Package License -NPM Downloads -CircleCI -Coverage -Discord -Backers on Open Collective -Sponsors on Open Collective - - Support us - -

- - -## Description - -[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository. - ## Installation ```bash From 46b34beba4a2c5b222b8cfc39a9b2bf0d4ae34f2 Mon Sep 17 00:00:00 2001 From: Taras Yurchenko <115026707+TarasBlatnoi@users.noreply.github.com> Date: Mon, 13 Oct 2025 17:32:51 +0300 Subject: [PATCH 26/27] Update README.md --- README.md | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/README.md b/README.md index 89ac359..bab9a34 100644 --- a/README.md +++ b/README.md @@ -29,17 +29,3 @@ $ npm run test:e2e # test coverage $ npm run test:cov ``` - -## Support - -Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support). - -## Stay in touch - -- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com) -- Website - [https://nestjs.com](https://nestjs.com/) -- Twitter - [@nestframework](https://twitter.com/nestframework) - -## License - -Nest is [MIT licensed](LICENSE). From 2d8e93c12d9383d6018b475de2d3341e0afadea2 Mon Sep 17 00:00:00 2001 From: Illia Date: Mon, 13 Oct 2025 22:22:13 +0300 Subject: [PATCH 27/27] refactor: move queries logic to the repository layer - created repository v2 - move data logic to the repository --- src/game/game.module.ts | 38 ++--- src/game/game.repository.v2.ts | 209 ++++++++++++++++++++++++++++ src/game/game.service.ts | 244 ++++----------------------------- src/game/types/game.types.ts | 68 +++++++++ 4 files changed, 313 insertions(+), 246 deletions(-) create mode 100644 src/game/game.repository.v2.ts create mode 100644 src/game/types/game.types.ts diff --git a/src/game/game.module.ts b/src/game/game.module.ts index 5156890..29ecae6 100644 --- a/src/game/game.module.ts +++ b/src/game/game.module.ts @@ -1,36 +1,18 @@ -import { forwardRef, Module } from '@nestjs/common'; +import { Module } from '@nestjs/common'; import { GameService } from './game.service'; import { GameGateway } from './game.gateway'; -import { GameRepository } from './game.repository'; -import { UserModule } from 'src/user/user.module'; -import { JwtModule } from '@nestjs/jwt'; -import { ConfigModule } from '@nestjs/config'; -import { PlayerModule } from 'src/player/player.module'; -import { ChatModule } from 'src/chat/chat.module'; -import { WebSocketProviderModule } from 'src/webSocketProvider/webSocketProvider.module'; import { GameController } from './game.controller'; -import { AuctionModule } from 'src/auction/auction.module'; -import { TimerModule } from 'src/timer/timers.module'; -import { SecretModule } from 'src/secret/secret.module'; -import { PaymentModule } from 'src/payment/payment.module'; -import { FieldModule } from 'src/field/field.module'; +import { GameRepository } from './game.repository'; +import { GameRepositoryV2 } from './game.repository.v2'; @Module({ - controllers: [GameController], - imports: [ - UserModule, - JwtModule, - ConfigModule, - ChatModule, - forwardRef(() => PlayerModule), - forwardRef(() => AuctionModule), - TimerModule, - forwardRef(() => SecretModule), - PaymentModule, - FieldModule, - WebSocketProviderModule, + providers: [ + GameService, + GameGateway, + GameRepository, + GameRepositoryV2, ], - providers: [GameGateway, GameService, GameRepository], - exports: [GameService], + controllers: [GameController], + exports: [GameService, GameRepository, GameRepositoryV2], }) export class GameModule {} diff --git a/src/game/game.repository.v2.ts b/src/game/game.repository.v2.ts new file mode 100644 index 0000000..5638aa0 --- /dev/null +++ b/src/game/game.repository.v2.ts @@ -0,0 +1,209 @@ +import { Injectable, Logger, NotFoundException } from '@nestjs/common'; +import { PrismaService } from 'src/prisma/prisma.service'; +import { + GameWithRelations, + GameWithPlayers, + CreateGameData, + StartGameData, + GameInclude, +} from './types/game.types'; +import { Game, Player, User, ChatType, Prisma } from '@prisma/client'; + +@Injectable() +export class GameRepositoryV2 { + private readonly logger = new Logger(GameRepositoryV2.name); + private readonly gameInclude: GameInclude = { + players: { + include: { + user: { + select: { + id: true, + nickname: true, + }, + }, + }, + orderBy: { + createdAt: 'asc', + }, + }, + properties: true, + currentPlayer: true, + winner: true, + chat: { + select: { + id: true, + }, + }, + }; + + constructor(private readonly prisma: PrismaService) {} + + async getVisibleGames(): Promise { + try { + return (await this.prisma.game.findMany({ + where: { status: 'LOBBY' }, + include: this.gameInclude, + })) as unknown as GameWithRelations[]; + } catch (error) { + this.logger.error('Failed to fetch visible games', error); + throw new Error('Failed to fetch visible games'); + } + } + + async createGameWithPlayer( + userId: string, + playersCapacity = 4 + ): Promise { + try { + const data: CreateGameData = { + playersCapacity, + players: { + create: { + userId, + color: '#000000', + }, + }, + turnEnds: '10000', + }; + + return (await this.prisma.game.create({ + data, + include: this.gameInclude, + })) as unknown as GameWithRelations; + } catch (error) { + this.logger.error('Failed to create game with player', error); + throw new Error('Failed to create game'); + } + } + + async getGameById(gameId: string): Promise { + try { + const game = (await this.prisma.game.findUnique({ + where: { id: gameId }, + include: this.gameInclude, + })) as unknown as GameWithRelations; + + if (!game) { + throw new NotFoundException(`Game with ID ${gameId} not found`); + } + + return game; + } catch (error) { + this.logger.error(`Failed to fetch game ${gameId}`, error); + throw error instanceof NotFoundException + ? error + : new Error('Failed to fetch game'); + } + } + + async startGame( + gameId: string, + turnEnds: string + ): Promise { + return this.prisma.$transaction(async (tx) => { + const gameWithPlayers = await tx.game.findUnique({ + where: { id: gameId }, + include: { + players: { + orderBy: { createdAt: 'asc' }, + }, + }, + }); + + if (!gameWithPlayers) { + throw new NotFoundException(`Game with ID ${gameId} not found`); + } + + if (gameWithPlayers.players.length < 2) { + throw new Error('At least 2 players are required to start the game'); + } + + const firstPlayer = + gameWithPlayers.players[gameWithPlayers.players.length - 1]; + + const updateData: StartGameData = { + status: 'ACTIVE', + turnOfUserId: firstPlayer.userId, + turnEnds, + chat: { + create: { + type: ChatType.GAME, + participants: { + createMany: { + data: gameWithPlayers.players.map((player) => ({ + userId: player.userId, + })), + }, + }, + }, + }, + }; + + return (await tx.game.update({ + where: { id: gameId }, + data: updateData, + include: this.gameInclude, + })) as unknown as GameWithRelations; + }); + } + + async updateGame( + gameId: string, + data: Prisma.GameUpdateInput + ): Promise { + try { + return (await this.prisma.game.update({ + where: { id: gameId }, + data, + include: this.gameInclude, + })) as unknown as GameWithRelations; + } catch (error) { + this.logger.error(`Failed to update game ${gameId}`, error); + throw new Error(`Failed to update game: ${error.message}`); + } + } + + async increaseHouses( + gameId: string, + quantity: number + ): Promise { + return this.prisma.game.update({ + where: { id: gameId }, + data: { housesQty: { increment: quantity } }, + include: this.gameInclude, + }) as unknown as GameWithRelations; + } + + async decreaseHotels( + gameId: string, + quantity: number + ): Promise { + return this.prisma.game.update({ + where: { id: gameId }, + data: { hotelsQty: { decrement: quantity } }, + include: this.gameInclude, + }) as unknown as GameWithRelations; + } + + async increaseHotels( + gameId: string, + quantity: number + ): Promise { + return this.prisma.game.update({ + where: { id: gameId }, + data: { hotelsQty: { increment: quantity } }, + include: this.gameInclude, + }) as unknown as GameWithRelations; + } + + async decreaseHouses( + gameId: string, + quantity: number + ): Promise { + return this.prisma.game.update({ + where: { id: gameId }, + data: { housesQty: { decrement: quantity } }, + include: this.gameInclude, + }) as unknown as GameWithRelations; + } +} diff --git a/src/game/game.service.ts b/src/game/game.service.ts index 035252e..a9652cb 100644 --- a/src/game/game.service.ts +++ b/src/game/game.service.ts @@ -1,6 +1,6 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import { WsException } from '@nestjs/websockets'; -import { ChatType, Prisma } from '@prisma/client'; +import { Prisma } from '@prisma/client'; import { AuctionService } from 'src/auction/auction.service'; import { HandlerChain } from 'src/common/handlerChain'; import { FieldService } from 'src/field/field.service'; @@ -14,10 +14,12 @@ import { TimerService } from 'src/timer/timers.service'; import { DEFAULT_FIELDS } from 'src/utils/fields'; import { WebSocketProvider } from 'src/webSocketProvider/webSocketProvider.service'; import { GamePayload, GameRepository } from './game.repository'; -import { PassTurnHandler } from './handlers/PassTurn.handler'; -import { ProcessSpecialHandler } from './handlers/ProcessSpecial.handler'; -import { PutUpForAuctionHandler } from './handlers/PutUpForAuction.handler'; -import { SteppedOnPrivateHandler } from './handlers/SteppedOnPrivate.handler'; +import { GameRepositoryV2 } from 'src/game/game.repository.v2'; +import { PutUpForAuctionHandler } from 'src/game/handlers/putUpForAuction.handler'; +import { PassTurnHandler } from 'src/game/handlers/passTurn.handler'; +import { ProcessSpecialHandler } from 'src/game/handlers/processSpecial.handler'; +import { SteppedOnPrivateHandler } from 'src/game/handlers/steppedOnPrivate.handler'; + @Injectable() export class GameService { constructor( @@ -31,7 +33,8 @@ export class GameService { private fieldService: FieldService, @Inject(forwardRef(() => SecretService)) private secretService: SecretService, - private paymentService: PaymentService + private paymentService: PaymentService, + private gameRepositoryV2: GameRepositoryV2 ) { this.passTurnToUser = this.passTurnToUser.bind(this); this.rollDice = this.rollDice.bind(this); @@ -47,22 +50,7 @@ export class GameService { readonly PLAYING_FIELDS_QUANTITY = 40; async getVisibleGames() { - return this.gameRepository.findMany({ - where: { status: 'LOBBY' }, - include: { - players: { - include: { user: { select: { nickname: true } } }, - orderBy: { - createdAt: 'asc', - }, - }, - chat: { - select: { - id: true, - }, - }, - }, - }); + return this.gameRepositoryV2.getVisibleGames(); } async getAllGameData(gameId: string, requestId: string) { @@ -94,37 +82,8 @@ export class GameService { message: 'You already have active game', requestId, }); - const newGame = await this.gameRepository.create({ - data: { - playersCapacity: 4, // TODO change players capacity to dynamic number - players: { - create: { - userId, - color: this.playerService.COLORS[0], - }, - }, - turnEnds: '10000', - }, - include: { - players: { - include: { - user: { - select: { - nickname: true, - }, - }, - }, - orderBy: { - createdAt: 'asc', - }, - }, - chat: { - select: { - id: true, - }, - }, - }, - }); + const newGame = await this.gameRepositoryV2.createGameWithPlayer(userId); + const gameFields = DEFAULT_FIELDS.map((field) => ({ ...field, gameId: newGame.id, @@ -135,22 +94,7 @@ export class GameService { } async getGame(gameId: string) { - return this.gameRepository.findUnique({ - where: { id: gameId }, - include: { - players: { - include: { user: { select: { nickname: true, id: true } } }, - orderBy: { - createdAt: 'asc', - }, - }, - chat: { - select: { - id: true, - }, - }, - }, - }); + return this.gameRepositoryV2.getGameById(gameId); } async joinGame(gameId: string, userId: string, requestId: string) { @@ -204,44 +148,7 @@ export class GameService { gameWithCreatedPlayer.players.length ) { const turnEnds = this.timerService.calculateFutureTime(game.timeOfTurn); - const startedGame = await this.gameRepository.updateById(gameId, { - data: { - status: 'ACTIVE', - turnOfUserId: - gameWithCreatedPlayer.players[ - gameWithCreatedPlayer.players.length - 1 - ].userId, - turnEnds, - chat: { - create: { - type: ChatType.GAME, - participants: { - createMany: { - data: [ - ...gameWithCreatedPlayer.players.map((player) => ({ - userId: player.userId, - })), - ], - }, - }, - }, - }, - }, - include: { - players: { - include: { user: { select: { nickname: true, id: true } } }, - orderBy: { - createdAt: 'asc', - }, - }, - chat: { - select: { - id: true, - }, - }, - }, - }); - return startedGame; + return this.gameRepositoryV2.startGame(gameId, turnEnds); } return gameWithCreatedPlayer; } @@ -267,9 +174,9 @@ export class GameService { await this.playerService.deleteById(player.id); - let game = await this.getGame(gameId); + const game = await this.getGame(gameId); if (!game.players.length) { - game = await this.gameRepository.delete({ + await this.gameRepository.delete({ where: { id: gameId }, include: { players: true }, }); @@ -277,30 +184,6 @@ export class GameService { return game; } - async findGameWithPlayers(gameId: string) { - const game = await this.gameRepository.findUnique({ - where: { id: gameId }, - include: { - players: { - include: { user: { select: { nickname: true } } }, - orderBy: { - createdAt: 'asc', - }, - }, - chat: { - select: { - id: true, - }, - }, - }, - }); - return game; - } - - findById(gameId: string) { - return this.gameRepository.findById(gameId); - } - getRandomDicesString() { const firstDice = Math.ceil(Math.random() * 6); const secondDice = Math.ceil(Math.random() * 6); @@ -575,23 +458,8 @@ export class GameService { async updateById( gameId: string, fieldsToUpdate: Partial - ) { - return this.gameRepository.updateById(gameId, { - data: fieldsToUpdate, - include: { - players: { - include: { user: { select: { nickname: true } } }, - orderBy: { - createdAt: 'asc', - }, - }, - chat: { - select: { - id: true, - }, - }, - }, - }); + ): Promise { + return this.gameRepositoryV2.updateGame(gameId, fieldsToUpdate); } parseDicesToArr(dices: string) { @@ -811,80 +679,20 @@ export class GameService { return { updatedGame: received.game }; } - decreaseHouses(gameId: string, quantity: number) { - return this.gameRepository.updateById(gameId, { - data: { housesQty: { decrement: quantity } }, - include: { - players: { - include: { user: { select: { nickname: true } } }, - orderBy: { - createdAt: 'asc', - }, - }, - chat: { - select: { - id: true, - }, - }, - }, - }); + async decreaseHouses(gameId: string, quantity: number) { + return this.gameRepositoryV2.decreaseHouses(gameId, quantity); } - increaseHouses(gameId: string, quantity: number) { - return this.gameRepository.updateById(gameId, { - data: { housesQty: { increment: quantity } }, - include: { - players: { - include: { user: { select: { nickname: true } } }, - orderBy: { - createdAt: 'asc', - }, - }, - chat: { - select: { - id: true, - }, - }, - }, - }); + async increaseHouses(gameId: string, quantity: number) { + return this.gameRepositoryV2.increaseHouses(gameId, quantity); } - decreaseHotels(gameId: string, quantity: number) { - return this.gameRepository.updateById(gameId, { - data: { hotelsQty: { decrement: quantity } }, - include: { - players: { - include: { user: { select: { nickname: true } } }, - orderBy: { - createdAt: 'asc', - }, - }, - chat: { - select: { - id: true, - }, - }, - }, - }); + async decreaseHotels(gameId: string, quantity: number) { + return this.gameRepositoryV2.decreaseHotels(gameId, quantity); } - increaseHotels(gameId: string, quantity: number) { - return this.gameRepository.updateById(gameId, { - data: { hotelsQty: { increment: quantity } }, - include: { - players: { - include: { user: { select: { nickname: true } } }, - orderBy: { - createdAt: 'asc', - }, - }, - chat: { - select: { - id: true, - }, - }, - }, - }); + async increaseHotels(gameId: string, quantity: number) { + return this.gameRepositoryV2.increaseHotels(gameId, quantity); } hasWinner(game: Partial) { diff --git a/src/game/types/game.types.ts b/src/game/types/game.types.ts new file mode 100644 index 0000000..f3c646c --- /dev/null +++ b/src/game/types/game.types.ts @@ -0,0 +1,68 @@ +import { Game, Player, User, GameStatus, ChatType } from '@prisma/client'; + +export type GameWithRelations = Game & { + players: (Player & { + user: Pick; + })[]; + currentPlayer: Player | null; + winner: User | null; + chat?: { + id: string; + }; +}; + +export type GameWithPlayers = Game & { + players: Player[]; + currentPlayer: Player | null; +}; + +export type CreateGameData = { + playersCapacity: number; + players: { + create: { + userId: string; + color: string; + }; + }; + turnEnds: string; +}; + +export type StartGameData = { + status: GameStatus; + turnOfUserId: string; + turnEnds: string; + chat: { + create: { + type: ChatType; + participants: { + createMany: { + data: Array<{ userId: string }>; + }; + }; + }; + }; +}; + +export type GameInclude = { + players: { + include: { + user: { + select: { + id: boolean; + nickname: boolean; + }; + }; + }; + orderBy: { + createdAt: 'asc'; + }; + }; + properties?: boolean; + currentPlayer?: boolean; + winner?: boolean; + chat?: { + select: { + id: boolean; + }; + }; +};