From 8d44b66dd323684745812b61df011ff945efc90c Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 17 Jun 2025 22:02:08 -0600 Subject: [PATCH 01/86] feat: add BookableSpace entity, DTO, repository, and integrate with Space entity --- libs/common/src/database/database.module.ts | 2 + .../booking/booking.repository.module.ts | 11 ++++ .../booking/dtos/bookable-space.dto.ts | 23 +++++++++ libs/common/src/modules/booking/dtos/index.ts | 1 + .../booking/entities/bookable-space.entity.ts | 51 +++++++++++++++++++ .../src/modules/booking/entities/index.ts | 1 + .../repositories/booking.repository.ts | 10 ++++ .../src/modules/booking/repositories/index.ts | 1 + .../modules/space/entities/space.entity.ts | 4 ++ 9 files changed, 104 insertions(+) create mode 100644 libs/common/src/modules/booking/booking.repository.module.ts create mode 100644 libs/common/src/modules/booking/dtos/bookable-space.dto.ts create mode 100644 libs/common/src/modules/booking/dtos/index.ts create mode 100644 libs/common/src/modules/booking/entities/bookable-space.entity.ts create mode 100644 libs/common/src/modules/booking/entities/index.ts create mode 100644 libs/common/src/modules/booking/repositories/booking.repository.ts create mode 100644 libs/common/src/modules/booking/repositories/index.ts diff --git a/libs/common/src/database/database.module.ts b/libs/common/src/database/database.module.ts index 2196901..921844f 100644 --- a/libs/common/src/database/database.module.ts +++ b/libs/common/src/database/database.module.ts @@ -59,6 +59,7 @@ import { } from '../modules/user/entities'; import { VisitorPasswordEntity } from '../modules/visitor-password/entities'; import { SpaceDailyOccupancyDurationEntity } from '../modules/occupancy/entities'; +import { BookableSpaceEntity } from '../modules/booking/entities'; @Module({ imports: [ TypeOrmModule.forRootAsync({ @@ -119,6 +120,7 @@ import { SpaceDailyOccupancyDurationEntity } from '../modules/occupancy/entities PresenceSensorDailySpaceEntity, AqiSpaceDailyPollutantStatsEntity, SpaceDailyOccupancyDurationEntity, + BookableSpaceEntity, ], namingStrategy: new SnakeNamingStrategy(), synchronize: Boolean(JSON.parse(configService.get('DB_SYNC'))), diff --git a/libs/common/src/modules/booking/booking.repository.module.ts b/libs/common/src/modules/booking/booking.repository.module.ts new file mode 100644 index 0000000..fb5601b --- /dev/null +++ b/libs/common/src/modules/booking/booking.repository.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { BookableSpaceEntity } from './entities/bookable-space.entity'; + +@Module({ + providers: [], + exports: [], + controllers: [], + imports: [TypeOrmModule.forFeature([BookableSpaceEntity])], +}) +export class BookableRepositoryModule {} diff --git a/libs/common/src/modules/booking/dtos/bookable-space.dto.ts b/libs/common/src/modules/booking/dtos/bookable-space.dto.ts new file mode 100644 index 0000000..8c7c41a --- /dev/null +++ b/libs/common/src/modules/booking/dtos/bookable-space.dto.ts @@ -0,0 +1,23 @@ +import { DaysEnum } from '@app/common/constants/days.enum'; +import { IsArray, IsEnum, IsInt, IsNotEmpty, IsString } from 'class-validator'; + +export class BookableSpaceDto { + @IsString() + @IsNotEmpty() + public uuid: string; + + @IsArray() + @IsEnum(DaysEnum, { each: true }) + daysAvailable: DaysEnum[]; + + @IsString() + @IsNotEmpty() + startTime: string; + + @IsString() + @IsNotEmpty() + endTime: string; + + @IsInt() + points: number; +} diff --git a/libs/common/src/modules/booking/dtos/index.ts b/libs/common/src/modules/booking/dtos/index.ts new file mode 100644 index 0000000..29762ae --- /dev/null +++ b/libs/common/src/modules/booking/dtos/index.ts @@ -0,0 +1 @@ +export * from './bookable-space.dto'; diff --git a/libs/common/src/modules/booking/entities/bookable-space.entity.ts b/libs/common/src/modules/booking/entities/bookable-space.entity.ts new file mode 100644 index 0000000..d5a12ac --- /dev/null +++ b/libs/common/src/modules/booking/entities/bookable-space.entity.ts @@ -0,0 +1,51 @@ +import { + Entity, + Column, + ManyToOne, + CreateDateColumn, + UpdateDateColumn, + Unique, +} from 'typeorm'; +import { SpaceEntity } from '../../space/entities/space.entity'; +import { DaysEnum } from '@app/common/constants/days.enum'; +import { AbstractEntity } from '../../abstract/entities/abstract.entity'; +import { BookableSpaceDto } from '../dtos'; + +@Entity('bookable-space') +@Unique(['space']) +export class BookableSpaceEntity extends AbstractEntity { + @Column({ + type: 'uuid', + default: () => 'gen_random_uuid()', + nullable: false, + }) + public uuid: string; + + @ManyToOne(() => SpaceEntity, (space) => space.bookableConfigs, { + onDelete: 'CASCADE', + }) + space: SpaceEntity; + + @Column({ + type: 'enum', + enum: DaysEnum, + array: true, + nullable: false, + }) + daysAvailable: DaysEnum[]; + + @Column({ type: 'time' }) + startTime: string; + + @Column({ type: 'time' }) + endTime: string; + + @Column({ type: 'int' }) + points: number; + + @CreateDateColumn() + createdAt: Date; + + @UpdateDateColumn() + updatedAt: Date; +} diff --git a/libs/common/src/modules/booking/entities/index.ts b/libs/common/src/modules/booking/entities/index.ts new file mode 100644 index 0000000..7a921ce --- /dev/null +++ b/libs/common/src/modules/booking/entities/index.ts @@ -0,0 +1 @@ +export * from './bookable-space.entity'; diff --git a/libs/common/src/modules/booking/repositories/booking.repository.ts b/libs/common/src/modules/booking/repositories/booking.repository.ts new file mode 100644 index 0000000..87a1c49 --- /dev/null +++ b/libs/common/src/modules/booking/repositories/booking.repository.ts @@ -0,0 +1,10 @@ +import { DataSource, Repository } from 'typeorm'; +import { Injectable } from '@nestjs/common'; +import { BookableSpaceEntity } from '../entities/bookable-space.entity'; + +@Injectable() +export class BookableSpaceEntityRepository extends Repository { + constructor(private dataSource: DataSource) { + super(BookableSpaceEntity, dataSource.createEntityManager()); + } +} diff --git a/libs/common/src/modules/booking/repositories/index.ts b/libs/common/src/modules/booking/repositories/index.ts new file mode 100644 index 0000000..ab4b40c --- /dev/null +++ b/libs/common/src/modules/booking/repositories/index.ts @@ -0,0 +1 @@ +export * from './booking.repository'; diff --git a/libs/common/src/modules/space/entities/space.entity.ts b/libs/common/src/modules/space/entities/space.entity.ts index 56b5d4f..2b0db0f 100644 --- a/libs/common/src/modules/space/entities/space.entity.ts +++ b/libs/common/src/modules/space/entities/space.entity.ts @@ -13,6 +13,7 @@ import { SubspaceEntity } from './subspace/subspace.entity'; import { PresenceSensorDailySpaceEntity } from '../../presence-sensor/entities'; import { AqiSpaceDailyPollutantStatsEntity } from '../../aqi/entities'; import { SpaceDailyOccupancyDurationEntity } from '../../occupancy/entities'; +import { BookableSpaceEntity } from '../../booking/entities'; @Entity({ name: 'space' }) export class SpaceEntity extends AbstractEntity { @@ -126,6 +127,9 @@ export class SpaceEntity extends AbstractEntity { ) occupancyDaily: SpaceDailyOccupancyDurationEntity[]; + @OneToMany(() => BookableSpaceEntity, (bookable) => bookable.space) + bookableConfigs: BookableSpaceEntity[]; + constructor(partial: Partial) { super(); Object.assign(this, partial); From 332b2f5851d9054d3a0f0af1592a404f2a1acdc1 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 17 Jun 2025 22:02:13 -0600 Subject: [PATCH 02/86] feat: implement Booking module with BookableSpace entity, controller, service, and DTOs for managing bookable spaces --- libs/common/src/constants/controller-route.ts | 9 ++ libs/common/src/helper/timeToMinutes.ts | 5 + src/app.module.ts | 2 + src/booking/booking.module.ts | 16 +++ .../controllers/bookable-space.controller.ts | 30 ++++ src/booking/controllers/index.ts | 1 + src/booking/dtos/create-bookable-space.dto.ts | 75 ++++++++++ src/booking/dtos/index.ts | 1 + src/booking/index.ts | 1 + .../services/bookable-space.service.ts | 135 ++++++++++++++++++ src/booking/services/index.ts | 1 + 11 files changed, 276 insertions(+) create mode 100644 libs/common/src/helper/timeToMinutes.ts create mode 100644 src/booking/booking.module.ts create mode 100644 src/booking/controllers/bookable-space.controller.ts create mode 100644 src/booking/controllers/index.ts create mode 100644 src/booking/dtos/create-bookable-space.dto.ts create mode 100644 src/booking/dtos/index.ts create mode 100644 src/booking/index.ts create mode 100644 src/booking/services/bookable-space.service.ts create mode 100644 src/booking/services/index.ts diff --git a/libs/common/src/constants/controller-route.ts b/libs/common/src/constants/controller-route.ts index cc37eee..353a9fb 100644 --- a/libs/common/src/constants/controller-route.ts +++ b/libs/common/src/constants/controller-route.ts @@ -69,7 +69,16 @@ export class ControllerRoute { 'Retrieve the list of all regions registered in Syncrow.'; }; }; + static BOOKABLE_SPACES = class { + public static readonly ROUTE = 'bookable-spaces'; + static ACTIONS = class { + public static readonly ADD_BOOKABLE_SPACES_SUMMARY = + 'Add new bookable spaces'; + public static readonly ADD_BOOKABLE_SPACES_DESCRIPTION = + 'This endpoint allows you to add new bookable spaces by providing the required details.'; + }; + }; static COMMUNITY = class { public static readonly ROUTE = '/projects/:projectUuid/communities'; static ACTIONS = class { diff --git a/libs/common/src/helper/timeToMinutes.ts b/libs/common/src/helper/timeToMinutes.ts new file mode 100644 index 0000000..8bf0e9b --- /dev/null +++ b/libs/common/src/helper/timeToMinutes.ts @@ -0,0 +1,5 @@ +// Convert time string (HH:mm) to minutes +export function timeToMinutes(time: string): number { + const [hours, minutes] = time.split(':').map(Number); + return hours * 60 + minutes; +} diff --git a/src/app.module.ts b/src/app.module.ts index ce64932..6bc32bd 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -39,6 +39,7 @@ import { winstonLoggerOptions } from '../libs/common/src/logger/services/winston import { AqiModule } from './aqi/aqi.module'; import { OccupancyModule } from './occupancy/occupancy.module'; import { WeatherModule } from './weather/weather.module'; +import { BookingModule } from './booking'; @Module({ imports: [ ConfigModule.forRoot({ @@ -82,6 +83,7 @@ import { WeatherModule } from './weather/weather.module'; OccupancyModule, WeatherModule, AqiModule, + BookingModule, ], providers: [ { diff --git a/src/booking/booking.module.ts b/src/booking/booking.module.ts new file mode 100644 index 0000000..6ce9c9a --- /dev/null +++ b/src/booking/booking.module.ts @@ -0,0 +1,16 @@ +import { Global, Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { BookableSpaceController } from './controllers'; +import { BookableSpaceService } from './services'; +import { BookableSpaceEntityRepository } from '@app/common/modules/booking/repositories'; +import { BookableSpaceEntity } from '@app/common/modules/booking/entities'; +import { SpaceEntity } from '@app/common/modules/space/entities/space.entity'; + +@Global() +@Module({ + imports: [TypeOrmModule.forFeature([BookableSpaceEntity, SpaceEntity])], + controllers: [BookableSpaceController], + providers: [BookableSpaceService, BookableSpaceEntityRepository], + exports: [BookableSpaceService], +}) +export class BookingModule {} diff --git a/src/booking/controllers/bookable-space.controller.ts b/src/booking/controllers/bookable-space.controller.ts new file mode 100644 index 0000000..08b5179 --- /dev/null +++ b/src/booking/controllers/bookable-space.controller.ts @@ -0,0 +1,30 @@ +import { Body, Controller, Post, UseGuards } from '@nestjs/common'; +import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger'; +import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; +import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; +import { BaseResponseDto } from '@app/common/dto/base.response.dto'; +import { ControllerRoute } from '@app/common/constants/controller-route'; + +import { CreateBookableSpaceDto } from '../dtos'; +import { BookableSpaceService } from '../services'; + +@ApiTags('Booking Module') +@Controller({ + version: EnableDisableStatusEnum.ENABLED, + path: ControllerRoute.BOOKABLE_SPACES.ROUTE, +}) +export class BookableSpaceController { + constructor(private readonly bookableSpaceService: BookableSpaceService) {} + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Post() + @ApiOperation({ + summary: + ControllerRoute.BOOKABLE_SPACES.ACTIONS.ADD_BOOKABLE_SPACES_SUMMARY, + description: + ControllerRoute.BOOKABLE_SPACES.ACTIONS.ADD_BOOKABLE_SPACES_DESCRIPTION, + }) + async create(@Body() dto: CreateBookableSpaceDto): Promise { + return this.bookableSpaceService.create(dto); + } +} diff --git a/src/booking/controllers/index.ts b/src/booking/controllers/index.ts new file mode 100644 index 0000000..d239862 --- /dev/null +++ b/src/booking/controllers/index.ts @@ -0,0 +1 @@ +export * from './bookable-space.controller'; diff --git a/src/booking/dtos/create-bookable-space.dto.ts b/src/booking/dtos/create-bookable-space.dto.ts new file mode 100644 index 0000000..a8d88ba --- /dev/null +++ b/src/booking/dtos/create-bookable-space.dto.ts @@ -0,0 +1,75 @@ +// dtos/bookable-space.dto.ts +import { DaysEnum } from '@app/common/constants/days.enum'; +import { ApiProperty } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; +import { + IsArray, + IsEnum, + IsNotEmpty, + IsString, + IsUUID, + IsInt, + ValidateNested, + ArrayMinSize, + ArrayMaxSize, + Min, + Max, + Matches, +} from 'class-validator'; + +export class BookingSlotDto { + @ApiProperty({ + enum: DaysEnum, + isArray: true, + example: [DaysEnum.MON, DaysEnum.WED, DaysEnum.FRI], + }) + @IsArray() + @ArrayMinSize(1, { message: 'At least one day must be selected' }) + @ArrayMaxSize(7, { message: 'Cannot select more than 7 days' }) + @IsEnum(DaysEnum, { each: true, message: 'Invalid day provided' }) + daysAvailable: DaysEnum[]; + + @ApiProperty({ example: '09:00' }) + @IsString() + @IsNotEmpty({ message: 'Start time cannot be empty' }) + @Matches(/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/, { + message: 'Start time must be in HH:mm format (24-hour)', + }) + startTime: string; + + @ApiProperty({ example: '17:00' }) + @IsString() + @IsNotEmpty({ message: 'End time cannot be empty' }) + @Matches(/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/, { + message: 'End time must be in HH:mm format (24-hour)', + }) + endTime: string; + + @ApiProperty({ example: 10, minimum: 0 }) + @IsInt() + @Min(0, { message: 'Points cannot be negative' }) + @Max(1000, { message: 'Points cannot exceed 1000' }) + points: number; +} + +export class CreateBookableSpaceDto { + @ApiProperty({ + type: 'string', + isArray: true, + example: [ + '3fa85f64-5717-4562-b3fc-2c963f66afa6', + '4fa85f64-5717-4562-b3fc-2c963f66afa7', + ], + }) + @IsArray() + @ArrayMinSize(1, { message: 'At least one space must be selected' }) + @IsUUID('all', { each: true, message: 'Invalid space UUID provided' }) + spaceUuids: string[]; + + @ApiProperty({ type: BookingSlotDto, isArray: true }) + @IsArray() + @ArrayMinSize(1, { message: 'At least one booking slot must be provided' }) + @ValidateNested({ each: true }) + @Type(() => BookingSlotDto) + slots: BookingSlotDto[]; +} diff --git a/src/booking/dtos/index.ts b/src/booking/dtos/index.ts new file mode 100644 index 0000000..c56d66c --- /dev/null +++ b/src/booking/dtos/index.ts @@ -0,0 +1 @@ +export * from './create-bookable-space.dto'; diff --git a/src/booking/index.ts b/src/booking/index.ts new file mode 100644 index 0000000..770d226 --- /dev/null +++ b/src/booking/index.ts @@ -0,0 +1 @@ +export * from './booking.module'; diff --git a/src/booking/services/bookable-space.service.ts b/src/booking/services/bookable-space.service.ts new file mode 100644 index 0000000..57b573a --- /dev/null +++ b/src/booking/services/bookable-space.service.ts @@ -0,0 +1,135 @@ +// services/bookable-space.service.ts +import { + Injectable, + NotFoundException, + ConflictException, + BadRequestException, +} from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository, In } from 'typeorm'; +import { BookingSlotDto, CreateBookableSpaceDto } from '../dtos'; +import { BaseResponseDto } from '@app/common/dto/base.response.dto'; +import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; +import { BookableSpaceEntity } from '@app/common/modules/booking/entities'; +import { SpaceEntity } from '@app/common/modules/space/entities/space.entity'; +import { timeToMinutes } from '@app/common/helper/timeToMinutes'; + +@Injectable() +export class BookableSpaceService { + constructor( + @InjectRepository(BookableSpaceEntity) + private bookableRepo: Repository, + + @InjectRepository(SpaceEntity) + private spaceRepo: Repository, + ) {} + + async create(dto: CreateBookableSpaceDto): Promise { + // Validate time slots first + this.validateTimeSlots(dto.slots); + + // Validate spaces exist + const spaces = await this.validateSpacesExist(dto.spaceUuids); + + // Validate no duplicate bookable configurations + await this.validateNoDuplicateBookableConfigs(dto.spaceUuids); + + // Create and save bookable spaces + return this.createBookableSpaces(spaces, dto.slots); + } + + /** + * Validate that all specified spaces exist + */ + private async validateSpacesExist( + spaceUuids: string[], + ): Promise { + const spaces = await this.spaceRepo.find({ + where: { uuid: In(spaceUuids) }, + }); + + if (spaces.length !== spaceUuids.length) { + const foundUuids = spaces.map((s) => s.uuid); + const missingUuids = spaceUuids.filter( + (uuid) => !foundUuids.includes(uuid), + ); + throw new NotFoundException( + `Spaces not found: ${missingUuids.join(', ')}`, + ); + } + + return spaces; + } + + /** + * Validate there are no existing bookable configurations for these spaces + */ + private async validateNoDuplicateBookableConfigs( + spaceUuids: string[], + ): Promise { + const existingBookables = await this.bookableRepo.find({ + where: { space: { uuid: In(spaceUuids) } }, + relations: ['space'], + }); + + if (existingBookables.length > 0) { + const existingUuids = [ + ...new Set(existingBookables.map((b) => b.space.uuid)), + ]; + throw new ConflictException( + `Bookable configuration already exists for spaces: ${existingUuids.join(', ')}`, + ); + } + } + + /** + * Validate time slots have valid format and logical times + */ + private validateTimeSlots(slots: BookingSlotDto[]): void { + slots.forEach((slot) => { + const start = timeToMinutes(slot.startTime); + const end = timeToMinutes(slot.endTime); + + if (start >= end) { + throw new BadRequestException( + `End time must be after start time for slot: ${slot.startTime}-${slot.endTime}`, + ); + } + }); + } + + /** + * Create bookable space entries after all validations pass + */ + private async createBookableSpaces( + spaces: SpaceEntity[], + slots: BookingSlotDto[], + ): Promise { + try { + const entries = spaces.flatMap((space) => + slots.map((slot) => + this.bookableRepo.create({ + space, + daysAvailable: slot.daysAvailable, + startTime: slot.startTime, + endTime: slot.endTime, + points: slot.points, + }), + ), + ); + + const data = await this.bookableRepo.save(entries); + return new SuccessResponseDto({ + data, + message: 'Successfully added new bookable spaces', + }); + } catch (error) { + if (error.code === '23505') { + throw new ConflictException( + 'Duplicate bookable space configuration detected', + ); + } + throw error; + } + } +} diff --git a/src/booking/services/index.ts b/src/booking/services/index.ts new file mode 100644 index 0000000..f8a6199 --- /dev/null +++ b/src/booking/services/index.ts @@ -0,0 +1 @@ +export * from './bookable-space.service'; From 689a38ee0cddd35c36cada9ff09306d3dd8e0499 Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Wed, 18 Jun 2025 10:34:29 +0300 Subject: [PATCH 03/86] Revamp/space management (#409) * task: add getCommunitiesV2 * task: update getOneSpace API to match revamp structure * refactor: implement modifications to pace management APIs * refactor: remove space link --- .eslintrc.js | 1 + libs/common/src/database/database.module.ts | 4 +- libs/common/src/dto/pagination.request.dto.ts | 44 +--- .../space/entities/space-link.entity.ts | 31 +-- .../modules/space/entities/space.entity.ts | 25 +-- .../space/repositories/space.repository.ts | 7 +- src/community/community.module.ts | 1 + .../controllers/community.controller.ts | 25 ++- src/community/services/community.service.ts | 75 ++++++- src/invite-user/invite-user.module.ts | 2 - src/power-clamp/power-clamp.module.ts | 4 +- src/project/project.module.ts | 2 - src/project/services/project.service.ts | 11 +- .../subspace/subspace-model.service.ts | 8 +- src/space-model/space-model.module.ts | 2 - src/space/dtos/add.space.dto.ts | 77 ++----- src/space/dtos/create-allocations.dto.ts | 4 +- .../dtos/create-product-allocation.dto.ts | 36 ++++ src/space/dtos/index.ts | 8 +- src/space/dtos/subspace/add.subspace.dto.ts | 7 +- src/space/dtos/subspace/index.ts | 7 +- .../dtos/subspace/modify.subspace.dto.ts | 14 -- .../dtos/subspace/update.subspace.dto.ts | 24 +-- src/space/dtos/update.space.dto.ts | 21 +- src/space/handlers/disable-space.handler.ts | 10 +- .../product-allocation.service.ts | 26 +-- .../services/space-link/space-link.service.ts | 123 +---------- .../services/space-validation.service.ts | 27 ++- src/space/services/space.service.ts | 198 ++++++++++-------- .../subspace-product-allocation.service.ts | 177 +++------------- .../services/subspace/subspace.service.ts | 84 ++++---- src/space/space.module.ts | 2 - src/tags/services/tags.service.ts | 52 +++-- src/users/dtos/add.space.dto.ts | 35 +--- 34 files changed, 467 insertions(+), 707 deletions(-) create mode 100644 src/space/dtos/create-product-allocation.dto.ts delete mode 100644 src/space/dtos/subspace/modify.subspace.dto.ts diff --git a/.eslintrc.js b/.eslintrc.js index 34440e3..60ac5b0 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -21,6 +21,7 @@ module.exports = { '@typescript-eslint/explicit-function-return-type': 'off', '@typescript-eslint/explicit-module-boundary-types': 'off', '@typescript-eslint/no-explicit-any': 'off', + "@typescript-eslint/no-unused-vars": 'warn', }, settings: { 'import/resolver': { diff --git a/libs/common/src/database/database.module.ts b/libs/common/src/database/database.module.ts index 2196901..373041c 100644 --- a/libs/common/src/database/database.module.ts +++ b/libs/common/src/database/database.module.ts @@ -25,6 +25,7 @@ import { InviteUserEntity, InviteUserSpaceEntity, } from '../modules/Invite-user/entities'; +import { SpaceDailyOccupancyDurationEntity } from '../modules/occupancy/entities'; import { PowerClampDailyEntity, PowerClampHourlyEntity, @@ -46,7 +47,6 @@ import { SubspaceModelProductAllocationEntity, } from '../modules/space-model/entities'; import { InviteSpaceEntity } from '../modules/space/entities/invite-space.entity'; -import { SpaceLinkEntity } from '../modules/space/entities/space-link.entity'; import { SpaceProductAllocationEntity } from '../modules/space/entities/space-product-allocation.entity'; import { SpaceEntity } from '../modules/space/entities/space.entity'; import { SubspaceProductAllocationEntity } from '../modules/space/entities/subspace/subspace-product-allocation.entity'; @@ -58,7 +58,6 @@ import { UserSpaceEntity, } from '../modules/user/entities'; import { VisitorPasswordEntity } from '../modules/visitor-password/entities'; -import { SpaceDailyOccupancyDurationEntity } from '../modules/occupancy/entities'; @Module({ imports: [ TypeOrmModule.forRootAsync({ @@ -87,7 +86,6 @@ import { SpaceDailyOccupancyDurationEntity } from '../modules/occupancy/entities PermissionTypeEntity, CommunityEntity, SpaceEntity, - SpaceLinkEntity, SubspaceEntity, UserSpaceEntity, DeviceUserPermissionEntity, diff --git a/libs/common/src/dto/pagination.request.dto.ts b/libs/common/src/dto/pagination.request.dto.ts index e20501a..467fb61 100644 --- a/libs/common/src/dto/pagination.request.dto.ts +++ b/libs/common/src/dto/pagination.request.dto.ts @@ -1,10 +1,9 @@ -import { IsBoolean, IsDate, IsOptional } from 'class-validator'; -import { IsPageRequestParam } from '../validators/is-page-request-param.validator'; import { ApiProperty } from '@nestjs/swagger'; -import { IsSizeRequestParam } from '../validators/is-size-request-param.validator'; import { Transform } from 'class-transformer'; -import { parseToDate } from '../util/parseToDate'; +import { IsBoolean, IsOptional } from 'class-validator'; import { BooleanValues } from '../constants/boolean-values.enum'; +import { IsPageRequestParam } from '../validators/is-page-request-param.validator'; +import { IsSizeRequestParam } from '../validators/is-size-request-param.validator'; export class PaginationRequestGetListDto { @ApiProperty({ @@ -19,6 +18,7 @@ export class PaginationRequestGetListDto { return value.obj.includeSpaces === BooleanValues.TRUE; }) public includeSpaces?: boolean = false; + @IsOptional() @IsPageRequestParam({ message: 'Page must be bigger than 0', @@ -40,40 +40,4 @@ export class PaginationRequestGetListDto { description: 'Size request', }) size?: number; - - @IsOptional() - @ApiProperty({ - name: 'name', - required: false, - description: 'Name to be filtered', - }) - name?: string; - - @ApiProperty({ - name: 'from', - required: false, - type: Number, - description: `Start time in UNIX timestamp format to filter`, - example: 1674172800000, - }) - @IsOptional() - @Transform(({ value }) => parseToDate(value)) - @IsDate({ - message: `From must be in UNIX timestamp format in order to parse to Date instance`, - }) - from?: Date; - - @ApiProperty({ - name: 'to', - required: false, - type: Number, - description: `End time in UNIX timestamp format to filter`, - example: 1674259200000, - }) - @IsOptional() - @Transform(({ value }) => parseToDate(value)) - @IsDate({ - message: `To must be in UNIX timestamp format in order to parse to Date instance`, - }) - to?: Date; } diff --git a/libs/common/src/modules/space/entities/space-link.entity.ts b/libs/common/src/modules/space/entities/space-link.entity.ts index da11eb7..7223591 100644 --- a/libs/common/src/modules/space/entities/space-link.entity.ts +++ b/libs/common/src/modules/space/entities/space-link.entity.ts @@ -1,32 +1,3 @@ -import { Column, Entity, JoinColumn, ManyToOne } from 'typeorm'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; -import { SpaceEntity } from './space.entity'; -import { Direction } from '@app/common/constants/direction.enum'; -@Entity({ name: 'space-link' }) -export class SpaceLinkEntity extends AbstractEntity { - @ManyToOne(() => SpaceEntity, { nullable: false, onDelete: 'CASCADE' }) - @JoinColumn({ name: 'start_space_id' }) - public startSpace: SpaceEntity; - - @ManyToOne(() => SpaceEntity, { nullable: false, onDelete: 'CASCADE' }) - @JoinColumn({ name: 'end_space_id' }) - public endSpace: SpaceEntity; - - @Column({ - nullable: false, - default: false, - }) - public disabled: boolean; - - @Column({ - nullable: false, - enum: Object.values(Direction), - }) - direction: string; - - constructor(partial: Partial) { - super(); - Object.assign(this, partial); - } -} +export class SpaceLinkEntity extends AbstractEntity {} diff --git a/libs/common/src/modules/space/entities/space.entity.ts b/libs/common/src/modules/space/entities/space.entity.ts index 56b5d4f..5341613 100644 --- a/libs/common/src/modules/space/entities/space.entity.ts +++ b/libs/common/src/modules/space/entities/space.entity.ts @@ -1,18 +1,17 @@ import { Column, Entity, JoinColumn, ManyToOne, OneToMany } from 'typeorm'; -import { SpaceDto } from '../dtos'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; -import { UserSpaceEntity } from '../../user/entities'; -import { DeviceEntity } from '../../device/entities'; +import { AqiSpaceDailyPollutantStatsEntity } from '../../aqi/entities'; import { CommunityEntity } from '../../community/entities'; -import { SpaceLinkEntity } from './space-link.entity'; +import { DeviceEntity } from '../../device/entities'; +import { InviteUserSpaceEntity } from '../../Invite-user/entities'; +import { SpaceDailyOccupancyDurationEntity } from '../../occupancy/entities'; +import { PresenceSensorDailySpaceEntity } from '../../presence-sensor/entities'; import { SceneEntity } from '../../scene/entities'; import { SpaceModelEntity } from '../../space-model'; -import { InviteUserSpaceEntity } from '../../Invite-user/entities'; +import { UserSpaceEntity } from '../../user/entities'; +import { SpaceDto } from '../dtos'; import { SpaceProductAllocationEntity } from './space-product-allocation.entity'; import { SubspaceEntity } from './subspace/subspace.entity'; -import { PresenceSensorDailySpaceEntity } from '../../presence-sensor/entities'; -import { AqiSpaceDailyPollutantStatsEntity } from '../../aqi/entities'; -import { SpaceDailyOccupancyDurationEntity } from '../../occupancy/entities'; @Entity({ name: 'space' }) export class SpaceEntity extends AbstractEntity { @@ -75,16 +74,6 @@ export class SpaceEntity extends AbstractEntity { ) devices: DeviceEntity[]; - @OneToMany(() => SpaceLinkEntity, (connection) => connection.startSpace, { - nullable: true, - }) - public outgoingConnections: SpaceLinkEntity[]; - - @OneToMany(() => SpaceLinkEntity, (connection) => connection.endSpace, { - nullable: true, - }) - public incomingConnections: SpaceLinkEntity[]; - @Column({ nullable: true, type: 'text', diff --git a/libs/common/src/modules/space/repositories/space.repository.ts b/libs/common/src/modules/space/repositories/space.repository.ts index a89a314..36cc548 100644 --- a/libs/common/src/modules/space/repositories/space.repository.ts +++ b/libs/common/src/modules/space/repositories/space.repository.ts @@ -1,7 +1,6 @@ import { Injectable } from '@nestjs/common'; import { DataSource, Repository } from 'typeorm'; import { InviteSpaceEntity } from '../entities/invite-space.entity'; -import { SpaceLinkEntity } from '../entities/space-link.entity'; import { SpaceProductAllocationEntity } from '../entities/space-product-allocation.entity'; import { SpaceEntity } from '../entities/space.entity'; @@ -13,11 +12,7 @@ export class SpaceRepository extends Repository { } @Injectable() -export class SpaceLinkRepository extends Repository { - constructor(private dataSource: DataSource) { - super(SpaceLinkEntity, dataSource.createEntityManager()); - } -} +export class SpaceLinkRepository {} @Injectable() export class InviteSpaceRepository extends Repository { diff --git a/src/community/community.module.ts b/src/community/community.module.ts index ea69401..0567ffb 100644 --- a/src/community/community.module.ts +++ b/src/community/community.module.ts @@ -78,6 +78,7 @@ import { AqiDataService } from '@app/common/helper/services/aqi.data.service'; ProjectRepository, SpaceService, InviteSpaceRepository, + // Todo: find out why this is needed SpaceLinkService, SubSpaceService, ValidationService, diff --git a/src/community/controllers/community.controller.ts b/src/community/controllers/community.controller.ts index a6b269f..a2b0ef2 100644 --- a/src/community/controllers/community.controller.ts +++ b/src/community/controllers/community.controller.ts @@ -1,4 +1,3 @@ -import { CommunityService } from '../services/community.service'; import { Body, Controller, @@ -10,17 +9,18 @@ import { Query, UseGuards, } from '@nestjs/common'; -import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger'; +import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; import { AddCommunityDto } from '../dtos/add.community.dto'; import { GetCommunityParams } from '../dtos/get.community.dto'; import { UpdateCommunityNameDto } from '../dtos/update.community.dto'; +import { CommunityService } from '../services/community.service'; // import { CheckUserCommunityGuard } from 'src/guards/user.community.guard'; import { ControllerRoute } from '@app/common/constants/controller-route'; import { BaseResponseDto } from '@app/common/dto/base.response.dto'; -import { ProjectParam } from '../dtos'; -import { PermissionsGuard } from 'src/guards/permissions.guard'; -import { Permissions } from 'src/decorators/permissions.decorator'; import { PaginationRequestWithSearchGetListDto } from '@app/common/dto/pagination-with-search.request.dto'; +import { Permissions } from 'src/decorators/permissions.decorator'; +import { PermissionsGuard } from 'src/guards/permissions.guard'; +import { ProjectParam } from '../dtos'; @ApiTags('Community Module') @Controller({ @@ -45,6 +45,21 @@ export class CommunityController { return await this.communityService.createCommunity(param, addCommunityDto); } + @ApiBearerAuth() + @UseGuards(PermissionsGuard) + @Permissions('COMMUNITY_VIEW') + @ApiOperation({ + summary: ControllerRoute.COMMUNITY.ACTIONS.LIST_COMMUNITY_SUMMARY, + description: ControllerRoute.COMMUNITY.ACTIONS.LIST_COMMUNITY_DESCRIPTION, + }) + @Get('v2') + async getCommunitiesV2( + @Param() param: ProjectParam, + @Query() query: PaginationRequestWithSearchGetListDto, + ): Promise { + return this.communityService.getCommunitiesV2(param, query); + } + @ApiBearerAuth() @UseGuards(PermissionsGuard) @Permissions('COMMUNITY_VIEW') diff --git a/src/community/services/community.service.ts b/src/community/services/community.service.ts index fc33011..5de34fa 100644 --- a/src/community/services/community.service.ts +++ b/src/community/services/community.service.ts @@ -1,4 +1,7 @@ -import { ORPHAN_COMMUNITY_NAME } from '@app/common/constants/orphan-constant'; +import { + ORPHAN_COMMUNITY_NAME, + ORPHAN_SPACE_NAME, +} from '@app/common/constants/orphan-constant'; import { BaseResponseDto } from '@app/common/dto/base.response.dto'; import { PageResponse } from '@app/common/dto/pagination.response.dto'; import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; @@ -22,7 +25,7 @@ import { NotFoundException, } from '@nestjs/common'; import { SpaceService } from 'src/space/services'; -import { SelectQueryBuilder } from 'typeorm'; +import { QueryRunner, SelectQueryBuilder } from 'typeorm'; import { AddCommunityDto, GetCommunityParams, ProjectParam } from '../dtos'; import { UpdateCommunityNameDto } from '../dtos/update.community.dto'; @@ -69,12 +72,18 @@ export class CommunityService { } } - async getCommunityById(params: GetCommunityParams): Promise { + async getCommunityById( + params: GetCommunityParams, + queryRunner?: QueryRunner, + ): Promise { const { communityUuid, projectUuid } = params; await this.validateProject(projectUuid); - const community = await this.communityRepository.findOneBy({ + const communityRepository = + queryRunner?.manager.getRepository(CommunityEntity) || + this.communityRepository; + const community = await communityRepository.findOneBy({ uuid: communityUuid, }); @@ -161,6 +170,64 @@ export class CommunityService { } } + async getCommunitiesV2( + { projectUuid }: ProjectParam, + { + search, + includeSpaces, + ...pageable + }: Partial, + ) { + try { + const project = await this.validateProject(projectUuid); + + let qb: undefined | SelectQueryBuilder = undefined; + + qb = this.communityRepository + .createQueryBuilder('c') + .where('c.project = :projectUuid', { projectUuid }) + .andWhere(`c.name != '${ORPHAN_COMMUNITY_NAME}-${project.name}'`) + .distinct(true); + + if (includeSpaces) { + qb.leftJoinAndSelect('c.spaces', 'space', 'space.disabled = false') + .leftJoinAndSelect('space.parent', 'parent') + .leftJoinAndSelect( + 'space.children', + 'children', + 'children.disabled = :disabled', + { disabled: false }, + ) + // .leftJoinAndSelect('space.spaceModel', 'spaceModel') + .andWhere('space.spaceName != :orphanSpaceName', { + orphanSpaceName: ORPHAN_SPACE_NAME, + }) + .andWhere('space.disabled = :disabled', { disabled: false }); + } + + if (search) { + qb.andWhere( + `c.name ILIKE '%${search}%' ${includeSpaces ? "OR space.space_name ILIKE '%" + search + "%'" : ''}`, + ); + } + + const customModel = TypeORMCustomModel(this.communityRepository); + + const { baseResponseDto, paginationResponseDto } = + await customModel.findAll({ ...pageable, modelName: 'community' }, qb); + return new PageResponse( + baseResponseDto, + paginationResponseDto, + ); + } catch (error) { + // Generic error handling + throw new HttpException( + error.message || 'An error occurred while fetching communities.', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async updateCommunity( params: GetCommunityParams, updateCommunityDto: UpdateCommunityNameDto, diff --git a/src/invite-user/invite-user.module.ts b/src/invite-user/invite-user.module.ts index d28ff10..31e3a9b 100644 --- a/src/invite-user/invite-user.module.ts +++ b/src/invite-user/invite-user.module.ts @@ -71,7 +71,6 @@ import { import { SpaceModelProductAllocationService } from 'src/space-model/services/space-model-product-allocation.service'; import { SubspaceModelProductAllocationService } from 'src/space-model/services/subspace/subspace-model-product-allocation.service'; import { - SpaceLinkService, SpaceService, SpaceUserService, SubspaceDeviceService, @@ -115,7 +114,6 @@ import { UserService, UserSpaceService } from 'src/users/services'; TimeZoneRepository, SpaceService, InviteSpaceRepository, - SpaceLinkService, SubSpaceService, ValidationService, NewTagService, diff --git a/src/power-clamp/power-clamp.module.ts b/src/power-clamp/power-clamp.module.ts index 6a36393..120e368 100644 --- a/src/power-clamp/power-clamp.module.ts +++ b/src/power-clamp/power-clamp.module.ts @@ -1,4 +1,5 @@ import { DeviceStatusFirebaseService } from '@app/common/firebase/devices-status/services/devices-status.service'; +import { AqiDataService } from '@app/common/helper/services/aqi.data.service'; import { OccupancyService } from '@app/common/helper/services/occupancy.service'; import { PowerClampService } from '@app/common/helper/services/power.clamp.service'; import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service'; @@ -49,7 +50,6 @@ import { SpaceModelProductAllocationService } from 'src/space-model/services/spa import { SubspaceModelProductAllocationService } from 'src/space-model/services/subspace/subspace-model-product-allocation.service'; import { SpaceDeviceService, - SpaceLinkService, SpaceService, SubspaceDeviceService, SubSpaceService, @@ -60,7 +60,6 @@ import { SubspaceProductAllocationService } from 'src/space/services/subspace/su import { TagService } from 'src/tags/services'; import { PowerClampController } from './controllers'; import { PowerClampService as PowerClamp } from './services/power-clamp.service'; -import { AqiDataService } from '@app/common/helper/services/aqi.data.service'; @Module({ imports: [ConfigModule], controllers: [PowerClampController], @@ -90,7 +89,6 @@ import { AqiDataService } from '@app/common/helper/services/aqi.data.service'; SceneRepository, AutomationRepository, InviteSpaceRepository, - SpaceLinkService, SubSpaceService, TagService, SpaceModelService, diff --git a/src/project/project.module.ts b/src/project/project.module.ts index 2434fde..7c7f7d3 100644 --- a/src/project/project.module.ts +++ b/src/project/project.module.ts @@ -54,7 +54,6 @@ import { import { SpaceModelProductAllocationService } from 'src/space-model/services/space-model-product-allocation.service'; import { SubspaceModelProductAllocationService } from 'src/space-model/services/subspace/subspace-model-product-allocation.service'; import { - SpaceLinkService, SpaceService, SubspaceDeviceService, SubSpaceService, @@ -87,7 +86,6 @@ const CommandHandlers = [CreateOrphanSpaceHandler]; UserRepository, SpaceService, InviteSpaceRepository, - SpaceLinkService, SubSpaceService, ValidationService, TagService, diff --git a/src/project/services/project.service.ts b/src/project/services/project.service.ts index 645554e..6aed066 100644 --- a/src/project/services/project.service.ts +++ b/src/project/services/project.service.ts @@ -24,6 +24,7 @@ import { SpaceService } from 'src/space/services'; import { PassThrough } from 'stream'; import { CreateOrphanSpaceCommand } from '../command/create-orphan-space-command'; import { CreateProjectDto, GetProjectParam } from '../dto'; +import { QueryRunner } from 'typeorm'; @Injectable() export class ProjectService { @@ -212,8 +213,14 @@ export class ProjectService { } } - async findOne(uuid: string): Promise { - const project = await this.projectRepository.findOne({ where: { uuid } }); + async findOne( + uuid: string, + queryRunner?: QueryRunner, + ): Promise { + const projectRepository = queryRunner + ? queryRunner.manager.getRepository(ProjectEntity) + : this.projectRepository; + const project = await projectRepository.findOne({ where: { uuid } }); if (!project) { throw new HttpException( `Invalid project with uuid ${uuid}`, diff --git a/src/space-model/services/subspace/subspace-model.service.ts b/src/space-model/services/subspace/subspace-model.service.ts index 4001040..011b5dc 100644 --- a/src/space-model/services/subspace/subspace-model.service.ts +++ b/src/space-model/services/subspace/subspace-model.service.ts @@ -51,8 +51,12 @@ export class SubSpaceModelService { for (const [index, dto] of dtos.entries()) { const subspaceModel = savedSubspaces[index]; - const processedTags = await this.tagService.processTags( - dto.tags, + const processedTags = await this.tagService.upsertTags( + dto.tags.map((tag) => ({ + tagName: tag.name, + productUuid: tag.productUuid, + tagUuid: tag.uuid, + })), spaceModel.project.uuid, queryRunner, ); diff --git a/src/space-model/space-model.module.ts b/src/space-model/space-model.module.ts index 3d868c6..4cd6435 100644 --- a/src/space-model/space-model.module.ts +++ b/src/space-model/space-model.module.ts @@ -46,7 +46,6 @@ import { CommunityService } from 'src/community/services'; import { DeviceService } from 'src/device/services'; import { SceneService } from 'src/scene/services'; import { - SpaceLinkService, SpaceService, SubspaceDeviceService, SubSpaceService, @@ -92,7 +91,6 @@ const CommandHandlers = [ DeviceRepository, TuyaService, CommunityRepository, - SpaceLinkService, SpaceLinkRepository, InviteSpaceRepository, NewTagService, diff --git a/src/space/dtos/add.space.dto.ts b/src/space/dtos/add.space.dto.ts index c40eef7..b3b4fcc 100644 --- a/src/space/dtos/add.space.dto.ts +++ b/src/space/dtos/add.space.dto.ts @@ -3,7 +3,8 @@ import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { ArrayUnique, - IsBoolean, + IsArray, + IsMongoId, IsNotEmpty, IsNumber, IsOptional, @@ -12,7 +13,7 @@ import { NotEquals, ValidateNested, } from 'class-validator'; -import { ProcessTagDto } from 'src/tags/dtos'; +import { CreateProductAllocationDto } from './create-product-allocation.dto'; import { AddSubspaceDto } from './subspace'; export class AddSpaceDto { @@ -47,14 +48,6 @@ export class AddSpaceDto { @IsOptional() public icon?: string; - @ApiProperty({ - description: 'Indicates whether the space is private or public', - example: false, - default: false, - }) - @IsBoolean() - isPrivate: boolean; - @ApiProperty({ description: 'X position on canvas', example: 120 }) @IsNumber() x: number; @@ -64,23 +57,19 @@ export class AddSpaceDto { y: number; @ApiProperty({ - description: 'UUID of the Space', + description: 'UUID of the Space Model', example: 'd290f1ee-6c54-4b01-90e6-d701748f0851', }) - @IsString() + @IsMongoId() @IsOptional() spaceModelUuid?: string; - @ApiProperty({ description: 'Y position on canvas', example: 200 }) - @IsString() - @IsOptional() - direction?: string; - @ApiProperty({ description: 'List of subspaces included in the model', type: [AddSubspaceDto], }) @IsOptional() + @IsArray() @ValidateNested({ each: true }) @ArrayUnique((subspace) => subspace.subspaceName, { message(validationArguments) { @@ -100,51 +89,21 @@ export class AddSpaceDto { subspaces?: AddSubspaceDto[]; @ApiProperty({ - description: 'List of tags associated with the space model', - type: [ProcessTagDto], + description: 'List of allocations associated with the space', + type: [CreateProductAllocationDto], }) + @IsOptional() + @IsArray() @ValidateNested({ each: true }) - @Type(() => ProcessTagDto) - tags?: ProcessTagDto[]; -} + @Type(() => CreateProductAllocationDto) + productAllocations?: CreateProductAllocationDto[]; -export class AddUserSpaceDto { @ApiProperty({ - description: 'spaceUuid', - required: true, + description: 'List of children spaces associated with the space', + type: [AddSpaceDto], }) - @IsString() - @IsNotEmpty() - public spaceUuid: string; - @ApiProperty({ - description: 'userUuid', - required: true, - }) - @IsString() - @IsNotEmpty() - public userUuid: string; - constructor(dto: Partial) { - Object.assign(this, dto); - } -} - -export class AddUserSpaceUsingCodeDto { - @ApiProperty({ - description: 'userUuid', - required: true, - }) - @IsString() - @IsNotEmpty() - public userUuid: string; - @ApiProperty({ - description: 'inviteCode', - required: true, - }) - @IsString() - @IsNotEmpty() - public inviteCode: string; - - constructor(dto: Partial) { - Object.assign(this, dto); - } + @IsOptional() + @ValidateNested({ each: true }) + @Type(() => AddSpaceDto) + children?: AddSpaceDto[]; } diff --git a/src/space/dtos/create-allocations.dto.ts b/src/space/dtos/create-allocations.dto.ts index 4fb7a2a..ed86b5d 100644 --- a/src/space/dtos/create-allocations.dto.ts +++ b/src/space/dtos/create-allocations.dto.ts @@ -1,14 +1,14 @@ import { SpaceEntity } from '@app/common/modules/space/entities/space.entity'; import { SubspaceEntity } from '@app/common/modules/space/entities/subspace/subspace.entity'; -import { ProcessTagDto } from 'src/tags/dtos'; import { QueryRunner } from 'typeorm'; +import { CreateProductAllocationDto } from './create-product-allocation.dto'; export enum AllocationsOwnerType { SPACE = 'space', SUBSPACE = 'subspace', } export class BaseCreateAllocationsDto { - tags: ProcessTagDto[]; + productAllocations: CreateProductAllocationDto[]; projectUuid: string; queryRunner: QueryRunner; type: AllocationsOwnerType; diff --git a/src/space/dtos/create-product-allocation.dto.ts b/src/space/dtos/create-product-allocation.dto.ts new file mode 100644 index 0000000..f0472cf --- /dev/null +++ b/src/space/dtos/create-product-allocation.dto.ts @@ -0,0 +1,36 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { + IsNotEmpty, + IsOptional, + IsString, + IsUUID, + ValidateIf, +} from 'class-validator'; + +export class CreateProductAllocationDto { + @ApiProperty({ + description: 'The name of the tag (if creating a new tag)', + example: 'New Tag', + }) + @IsString() + @IsNotEmpty() + @ValidateIf((o) => !o.tagUuid) + tagName: string; + + @ApiProperty({ + description: 'UUID of the tag (if selecting an existing tag)', + example: '123e4567-e89b-12d3-a456-426614174000', + }) + @IsUUID() + @IsNotEmpty() + @ValidateIf((o) => !o.tagName) + tagUuid: string; + + @ApiProperty({ + description: 'UUID of the product', + example: '550e8400-e29b-41d4-a716-446655440000', + }) + @IsUUID() + @IsOptional() + productUuid: string; +} diff --git a/src/space/dtos/index.ts b/src/space/dtos/index.ts index 506efa9..c72e2b0 100644 --- a/src/space/dtos/index.ts +++ b/src/space/dtos/index.ts @@ -1,8 +1,10 @@ export * from './add.space.dto'; export * from './community-space.param'; +export * from './create-allocations.dto'; +export * from './create-product-allocation.dto'; export * from './get.space.param'; -export * from './user-space.param'; -export * from './subspace'; export * from './project.param.dto'; -export * from './update.space.dto'; +export * from './subspace'; export * from './tag'; +export * from './update.space.dto'; +export * from './user-space.param'; diff --git a/src/space/dtos/subspace/add.subspace.dto.ts b/src/space/dtos/subspace/add.subspace.dto.ts index 79dc395..d015a7c 100644 --- a/src/space/dtos/subspace/add.subspace.dto.ts +++ b/src/space/dtos/subspace/add.subspace.dto.ts @@ -1,4 +1,5 @@ import { ApiProperty } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; import { IsArray, IsNotEmpty, @@ -6,8 +7,8 @@ import { IsString, ValidateNested, } from 'class-validator'; -import { Type } from 'class-transformer'; import { ProcessTagDto } from 'src/tags/dtos'; +import { CreateProductAllocationDto } from '../create-product-allocation.dto'; export class AddSubspaceDto { @ApiProperty({ @@ -24,7 +25,7 @@ export class AddSubspaceDto { }) @IsArray() @ValidateNested({ each: true }) - @Type(() => ProcessTagDto) + @Type(() => CreateProductAllocationDto) @IsOptional() - tags?: ProcessTagDto[]; + productAllocations?: CreateProductAllocationDto[]; } diff --git a/src/space/dtos/subspace/index.ts b/src/space/dtos/subspace/index.ts index 0071b44..8ce87d0 100644 --- a/src/space/dtos/subspace/index.ts +++ b/src/space/dtos/subspace/index.ts @@ -1,6 +1,5 @@ -export * from './add.subspace.dto'; -export * from './get.subspace.param'; export * from './add.subspace-device.param'; -export * from './update.subspace.dto'; +export * from './add.subspace.dto'; export * from './delete.subspace.dto'; -export * from './modify.subspace.dto'; +export * from './get.subspace.param'; +export * from './update.subspace.dto'; diff --git a/src/space/dtos/subspace/modify.subspace.dto.ts b/src/space/dtos/subspace/modify.subspace.dto.ts deleted file mode 100644 index 7f32aca..0000000 --- a/src/space/dtos/subspace/modify.subspace.dto.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ApiPropertyOptional, PartialType } from '@nestjs/swagger'; -import { IsOptional, IsUUID } from 'class-validator'; -import { AddSubspaceDto } from './add.subspace.dto'; - -export class ModifySubspaceDto extends PartialType(AddSubspaceDto) { - @ApiPropertyOptional({ - description: - 'UUID of the subspace (will present if updating an existing subspace)', - example: '123e4567-e89b-12d3-a456-426614174000', - }) - @IsOptional() - @IsUUID() - uuid?: string; -} diff --git a/src/space/dtos/subspace/update.subspace.dto.ts b/src/space/dtos/subspace/update.subspace.dto.ts index 0931d9e..4ff2397 100644 --- a/src/space/dtos/subspace/update.subspace.dto.ts +++ b/src/space/dtos/subspace/update.subspace.dto.ts @@ -1,16 +1,14 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsString } from 'class-validator'; +import { ApiPropertyOptional, PartialType } from '@nestjs/swagger'; +import { IsOptional, IsUUID } from 'class-validator'; +import { AddSubspaceDto } from './add.subspace.dto'; -export class UpdateSubspaceDto { - @ApiProperty({ - description: 'Name of the subspace', - example: 'Living Room', +export class UpdateSubspaceDto extends PartialType(AddSubspaceDto) { + @ApiPropertyOptional({ + description: + 'UUID of the subspace (will present if updating an existing subspace)', + example: '123e4567-e89b-12d3-a456-426614174000', }) - @IsNotEmpty() - @IsString() - subspaceName?: string; - - @IsNotEmpty() - @IsString() - subspaceUuid: string; + @IsOptional() + @IsUUID() + uuid?: string; } diff --git a/src/space/dtos/update.space.dto.ts b/src/space/dtos/update.space.dto.ts index 310f695..93ff377 100644 --- a/src/space/dtos/update.space.dto.ts +++ b/src/space/dtos/update.space.dto.ts @@ -9,8 +9,8 @@ import { NotEquals, ValidateNested, } from 'class-validator'; -import { ModifySubspaceDto } from './subspace'; -import { ModifyTagDto } from './tag/modify-tag.dto'; +import { CreateProductAllocationDto } from './create-product-allocation.dto'; +import { UpdateSubspaceDto } from './subspace'; export class UpdateSpaceDto { @ApiProperty({ @@ -46,25 +46,24 @@ export class UpdateSpaceDto { y?: number; @ApiPropertyOptional({ - description: 'List of subspace modifications (add/update/delete)', - type: [ModifySubspaceDto], + description: 'List of subspace modifications', + type: [UpdateSubspaceDto], }) @IsOptional() @IsArray() @ValidateNested({ each: true }) - @Type(() => ModifySubspaceDto) - subspaces?: ModifySubspaceDto[]; + @Type(() => UpdateSubspaceDto) + subspaces?: UpdateSubspaceDto[]; @ApiPropertyOptional({ - description: - 'List of tag modifications (add/update/delete) for the space model', - type: [ModifyTagDto], + description: 'List of allocations modifications', + type: [CreateProductAllocationDto], }) @IsOptional() @IsArray() @ValidateNested({ each: true }) - @Type(() => ModifyTagDto) - tags?: ModifyTagDto[]; + @Type(() => CreateProductAllocationDto) + productAllocations?: CreateProductAllocationDto[]; @ApiProperty({ description: 'UUID of the Space', diff --git a/src/space/handlers/disable-space.handler.ts b/src/space/handlers/disable-space.handler.ts index 089ab76..45d3f96 100644 --- a/src/space/handlers/disable-space.handler.ts +++ b/src/space/handlers/disable-space.handler.ts @@ -5,11 +5,7 @@ import { DeviceService } from 'src/device/services'; import { UserSpaceService } from 'src/users/services'; import { DataSource } from 'typeorm'; import { DisableSpaceCommand } from '../commands'; -import { - SpaceLinkService, - SpaceSceneService, - SubSpaceService, -} from '../services'; +import { SpaceSceneService, SubSpaceService } from '../services'; @CommandHandler(DisableSpaceCommand) export class DisableSpaceHandler @@ -19,7 +15,6 @@ export class DisableSpaceHandler private readonly subSpaceService: SubSpaceService, private readonly userService: UserSpaceService, private readonly deviceService: DeviceService, - private readonly spaceLinkService: SpaceLinkService, private readonly sceneService: SpaceSceneService, private readonly dataSource: DataSource, ) {} @@ -39,8 +34,6 @@ export class DisableSpaceHandler 'subspaces', 'parent', 'devices', - 'outgoingConnections', - 'incomingConnections', 'scenes', 'children', 'userSpaces', @@ -79,7 +72,6 @@ export class DisableSpaceHandler orphanSpace, queryRunner, ), - this.spaceLinkService.deleteSpaceLink(space, queryRunner), this.sceneService.deleteScenes(space, queryRunner), ]; diff --git a/src/space/services/product-allocation/product-allocation.service.ts b/src/space/services/product-allocation/product-allocation.service.ts index 7950f9f..87190cb 100644 --- a/src/space/services/product-allocation/product-allocation.service.ts +++ b/src/space/services/product-allocation/product-allocation.service.ts @@ -16,10 +16,10 @@ export class ProductAllocationService { ) {} async createAllocations(dto: CreateAllocationsDto): Promise { - const { projectUuid, queryRunner, tags, type } = dto; + const { projectUuid, queryRunner, productAllocations, type } = dto; - const allocationsData = await this.tagService.processTags( - tags, + const allocationsData = await this.tagService.upsertTags( + productAllocations, projectUuid, queryRunner, ); @@ -29,15 +29,17 @@ export class ProductAllocationService { const createdTagsByName = new Map(allocationsData.map((t) => [t.name, t])); // Create the product-tag mapping based on the processed tags - const productTagMapping = tags.map(({ uuid, name, productUuid }) => { - const inputTag = uuid - ? createdTagsByUUID.get(uuid) - : createdTagsByName.get(name); - return { - tag: inputTag?.uuid, - product: productUuid, - }; - }); + const productTagMapping = productAllocations.map( + ({ tagUuid, tagName, productUuid }) => { + const inputTag = tagUuid + ? createdTagsByUUID.get(tagUuid) + : createdTagsByName.get(tagName); + return { + tag: inputTag?.uuid, + product: productUuid, + }; + }, + ); switch (type) { case AllocationsOwnerType.SPACE: { diff --git a/src/space/services/space-link/space-link.service.ts b/src/space/services/space-link/space-link.service.ts index 4801fe6..af84c2b 100644 --- a/src/space/services/space-link/space-link.service.ts +++ b/src/space/services/space-link/space-link.service.ts @@ -1,121 +1,6 @@ -import { SpaceLinkEntity } from '@app/common/modules/space/entities/space-link.entity'; -import { SpaceEntity } from '@app/common/modules/space/entities/space.entity'; -import { SpaceLinkRepository } from '@app/common/modules/space/repositories'; -import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; -import { QueryRunner } from 'typeorm'; +import { Injectable } from '@nestjs/common'; +// todo: find out why we need to import this +// in community module in order for the whole system to work @Injectable() -export class SpaceLinkService { - constructor(private readonly spaceLinkRepository: SpaceLinkRepository) {} - - async saveSpaceLink( - startSpaceId: string, - endSpaceId: string, - direction: string, - queryRunner: QueryRunner, - ): Promise { - try { - // Check if a link between the startSpace and endSpace already exists - const existingLink = await queryRunner.manager.findOne(SpaceLinkEntity, { - where: { - startSpace: { uuid: startSpaceId }, - endSpace: { uuid: endSpaceId }, - disabled: false, - }, - }); - - if (existingLink) { - // Update the direction if the link exists - existingLink.direction = direction; - await queryRunner.manager.save(SpaceLinkEntity, existingLink); - return; - } - - const existingEndSpaceLink = await queryRunner.manager.findOne( - SpaceLinkEntity, - { - where: { endSpace: { uuid: endSpaceId } }, - }, - ); - - if ( - existingEndSpaceLink && - existingEndSpaceLink.startSpace.uuid !== startSpaceId - ) { - throw new Error( - `Space with ID ${endSpaceId} is already an endSpace in another link and cannot be reused.`, - ); - } - - // Find start space - const startSpace = await queryRunner.manager.findOne(SpaceEntity, { - where: { uuid: startSpaceId }, - }); - - if (!startSpace) { - throw new HttpException( - `Start space with ID ${startSpaceId} not found.`, - HttpStatus.NOT_FOUND, - ); - } - - // Find end space - const endSpace = await queryRunner.manager.findOne(SpaceEntity, { - where: { uuid: endSpaceId }, - }); - - if (!endSpace) { - throw new HttpException( - `End space with ID ${endSpaceId} not found.`, - HttpStatus.NOT_FOUND, - ); - } - - // Create and save the space link - const spaceLink = this.spaceLinkRepository.create({ - startSpace, - endSpace, - direction, - }); - - await queryRunner.manager.save(SpaceLinkEntity, spaceLink); - } catch (error) { - throw new HttpException( - error.message || - `Failed to save space link. Internal Server Error: ${error.message}`, - error.status || HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - async deleteSpaceLink( - space: SpaceEntity, - queryRunner: QueryRunner, - ): Promise { - try { - const spaceLinks = await queryRunner.manager.find(SpaceLinkEntity, { - where: [ - { startSpace: space, disabled: false }, - { endSpace: space, disabled: false }, - ], - }); - - if (spaceLinks.length === 0) { - return; - } - - const linkIds = spaceLinks.map((link) => link.uuid); - - await queryRunner.manager - .createQueryBuilder() - .update(SpaceLinkEntity) - .set({ disabled: true }) - .whereInIds(linkIds) - .execute(); - } catch (error) { - throw new HttpException( - `Failed to disable space links for the given space: ${error.message}`, - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } -} +export class SpaceLinkService {} diff --git a/src/space/services/space-validation.service.ts b/src/space/services/space-validation.service.ts index 0eac0d2..efbddeb 100644 --- a/src/space/services/space-validation.service.ts +++ b/src/space/services/space-validation.service.ts @@ -16,7 +16,7 @@ import { Inject, Injectable, } from '@nestjs/common'; -import { In } from 'typeorm'; +import { In, QueryRunner } from 'typeorm'; import { CommunityService } from '../../community/services'; import { ProjectService } from '../../project/services'; import { ProjectParam } from '../dtos'; @@ -69,12 +69,17 @@ export class ValidationService { async validateCommunityAndProject( communityUuid: string, projectUuid: string, + queryRunner?: QueryRunner, ) { - const project = await this.projectService.findOne(projectUuid); - const community = await this.communityService.getCommunityById({ - communityUuid, - projectUuid, - }); + const project = await this.projectService.findOne(projectUuid, queryRunner); + + const community = await this.communityService.getCommunityById( + { + communityUuid, + projectUuid, + }, + queryRunner, + ); return { community: community.data, project: project }; } @@ -170,8 +175,14 @@ export class ValidationService { return space; } - async validateSpaceModel(spaceModelUuid: string): Promise { - const queryBuilder = this.spaceModelRepository + async validateSpaceModel( + spaceModelUuid: string, + queryRunner?: QueryRunner, + ): Promise { + const queryBuilder = ( + queryRunner.manager.getRepository(SpaceModelEntity) || + this.spaceModelRepository + ) .createQueryBuilder('spaceModel') .leftJoinAndSelect( 'spaceModel.subspaceModels', diff --git a/src/space/services/space.service.ts b/src/space/services/space.service.ts index 1312030..fb08b59 100644 --- a/src/space/services/space.service.ts +++ b/src/space/services/space.service.ts @@ -22,7 +22,6 @@ import { import { CommandBus } from '@nestjs/cqrs'; import { DeviceService } from 'src/device/services'; import { SpaceModelService } from 'src/space-model/services'; -import { ProcessTagDto } from 'src/tags/dtos'; import { TagService } from 'src/tags/services/tags.service'; import { DataSource, In, Not, QueryRunner } from 'typeorm'; import { DisableSpaceCommand } from '../commands'; @@ -32,9 +31,9 @@ import { GetSpaceParam, UpdateSpaceDto, } from '../dtos'; +import { CreateProductAllocationDto } from '../dtos/create-product-allocation.dto'; import { GetSpaceDto } from '../dtos/get.space.dto'; import { SpaceWithParentsDto } from '../dtos/space.parents.dto'; -import { SpaceLinkService } from './space-link'; import { SpaceProductAllocationService } from './space-product-allocation.service'; import { ValidationService } from './space-validation.service'; import { SubSpaceService } from './subspace'; @@ -44,7 +43,6 @@ export class SpaceService { private readonly dataSource: DataSource, private readonly spaceRepository: SpaceRepository, private readonly inviteSpaceRepository: InviteSpaceRepository, - private readonly spaceLinkService: SpaceLinkService, private readonly subSpaceService: SubSpaceService, private readonly validationService: ValidationService, private readonly tagService: TagService, @@ -57,50 +55,72 @@ export class SpaceService { async createSpace( addSpaceDto: AddSpaceDto, params: CommunitySpaceParam, + queryRunner?: QueryRunner, + recursiveCallParentEntity?: SpaceEntity, ): Promise { - const { parentUuid, direction, spaceModelUuid, subspaces, tags } = - addSpaceDto; + const isRecursiveCall = !!queryRunner; + + const { + parentUuid, + spaceModelUuid, + subspaces, + productAllocations, + children, + } = addSpaceDto; const { communityUuid, projectUuid } = params; - const queryRunner = this.dataSource.createQueryRunner(); - - await queryRunner.connect(); - await queryRunner.startTransaction(); + if (!queryRunner) { + queryRunner = this.dataSource.createQueryRunner(); + await queryRunner.connect(); + await queryRunner.startTransaction(); + } const { community } = await this.validationService.validateCommunityAndProject( communityUuid, projectUuid, + queryRunner, ); - this.validateSpaceCreationCriteria({ spaceModelUuid, subspaces, tags }); + this.validateSpaceCreationCriteria({ + spaceModelUuid, + subspaces, + productAllocations, + }); - const parent = parentUuid - ? await this.validationService.validateSpace(parentUuid) - : null; + const parent = + parentUuid && !isRecursiveCall + ? await this.validationService.validateSpace(parentUuid) + : null; const spaceModel = spaceModelUuid ? await this.validationService.validateSpaceModel(spaceModelUuid) : null; - try { const space = queryRunner.manager.create(SpaceEntity, { - ...addSpaceDto, + // todo: find a better way to handle this instead of naming every key + spaceName: addSpaceDto.spaceName, + icon: addSpaceDto.icon, + x: addSpaceDto.x, + y: addSpaceDto.y, spaceModel, - parent: parentUuid ? parent : null, + parent: isRecursiveCall + ? recursiveCallParentEntity + : parentUuid + ? parent + : null, community, }); const newSpace = await queryRunner.manager.save(space); - - const subspaceTags = - subspaces?.flatMap((subspace) => subspace.tags || []) || []; - - this.checkDuplicateTags([...tags, ...subspaceTags]); + this.checkDuplicateTags([ + ...(productAllocations || []), + ...(subspaces?.flatMap( + (subspace) => subspace.productAllocations || [], + ) || []), + ]); if (spaceModelUuid) { - // no need to check for existing dependencies here as validateSpaceCreationCriteria - // ensures no tags or subspaces are present along with spaceModelUuid await this.spaceModelService.linkToSpace( newSpace, spaceModel, @@ -109,15 +129,6 @@ export class SpaceService { } await Promise.all([ - // todo: remove this logic as we are not using space links anymore - direction && parent - ? this.spaceLinkService.saveSpaceLink( - parent.uuid, - newSpace.uuid, - direction, - queryRunner, - ) - : Promise.resolve(), subspaces?.length ? this.subSpaceService.createSubspacesFromDto( subspaces, @@ -126,12 +137,32 @@ export class SpaceService { projectUuid, ) : Promise.resolve(), - tags?.length - ? this.createAllocations(tags, projectUuid, queryRunner, newSpace) + productAllocations?.length + ? this.createAllocations( + productAllocations, + projectUuid, + queryRunner, + newSpace, + ) : Promise.resolve(), ]); - await queryRunner.commitTransaction(); + if (children?.length) { + await Promise.all( + children.map((child) => + this.createSpace( + { ...child, parentUuid: newSpace.uuid }, + { communityUuid, projectUuid }, + queryRunner, + newSpace, + ), + ), + ); + } + + if (!isRecursiveCall) { + await queryRunner.commitTransaction(); + } return new SuccessResponseDto({ statusCode: HttpStatus.CREATED, @@ -139,34 +170,34 @@ export class SpaceService { message: 'Space created successfully', }); } catch (error) { - await queryRunner.rollbackTransaction(); + !isRecursiveCall ? await queryRunner.rollbackTransaction() : null; if (error instanceof HttpException) { throw error; } throw new HttpException(error.message, HttpStatus.INTERNAL_SERVER_ERROR); } finally { - await queryRunner.release(); + !isRecursiveCall ? await queryRunner.release() : null; } } - private checkDuplicateTags(allTags: ProcessTagDto[]) { + private checkDuplicateTags(allocations: CreateProductAllocationDto[]) { const tagUuidSet = new Set(); const tagNameProductSet = new Set(); - for (const tag of allTags) { - if (tag.uuid) { - if (tagUuidSet.has(tag.uuid)) { + for (const allocation of allocations) { + if (allocation.tagUuid) { + if (tagUuidSet.has(allocation.tagUuid)) { throw new HttpException( - `Duplicate tag UUID found: ${tag.uuid}`, + `Duplicate tag UUID found: ${allocation.tagUuid}`, HttpStatus.BAD_REQUEST, ); } - tagUuidSet.add(tag.uuid); + tagUuidSet.add(allocation.tagUuid); } else { - const tagKey = `${tag.name}-${tag.productUuid}`; + const tagKey = `${allocation.tagName}-${allocation.productUuid}`; if (tagNameProductSet.has(tagKey)) { throw new HttpException( - `Duplicate tag found with name "${tag.name}" and product "${tag.productUuid}".`, + `Duplicate tag found with name "${allocation.tagName}" and product "${allocation.productUuid}".`, HttpStatus.BAD_REQUEST, ); } @@ -195,12 +226,7 @@ export class SpaceService { 'children.disabled = :disabled', { disabled: false }, ) - .leftJoinAndSelect( - 'space.incomingConnections', - 'incomingConnections', - 'incomingConnections.disabled = :incomingConnectionDisabled', - { incomingConnectionDisabled: false }, - ) + .leftJoinAndSelect('space.productAllocations', 'productAllocations') .leftJoinAndSelect('productAllocations.tag', 'tag') .leftJoinAndSelect('productAllocations.product', 'product') @@ -271,7 +297,6 @@ export class SpaceService { } } - // todo refactor this method to eliminate wrong use of tags async findOne(params: GetSpaceParam): Promise { const { communityUuid, spaceUuid, projectUuid } = params; try { @@ -282,19 +307,6 @@ export class SpaceService { const queryBuilder = this.spaceRepository .createQueryBuilder('space') - .leftJoinAndSelect('space.parent', 'parent') - .leftJoinAndSelect( - 'space.children', - 'children', - 'children.disabled = :disabled', - { disabled: false }, - ) - .leftJoinAndSelect( - 'space.incomingConnections', - 'incomingConnections', - 'incomingConnections.disabled = :incomingConnectionDisabled', - { incomingConnectionDisabled: false }, - ) .leftJoinAndSelect('space.productAllocations', 'productAllocations') .leftJoinAndSelect('productAllocations.tag', 'spaceTag') .leftJoinAndSelect('productAllocations.product', 'spaceProduct') @@ -423,7 +435,7 @@ export class SpaceService { const queryRunner = this.dataSource.createQueryRunner(); const hasSubspace = updateSpaceDto.subspaces?.length > 0; - const hasTags = updateSpaceDto.tags?.length > 0; + const hasAllocations = updateSpaceDto.productAllocations?.length > 0; try { await queryRunner.connect(); @@ -448,7 +460,7 @@ export class SpaceService { await this.updateSpaceProperties(space, updateSpaceDto, queryRunner); - if (hasSubspace || hasTags) { + if (hasSubspace || hasAllocations) { await queryRunner.manager.update(SpaceEntity, space.uuid, { spaceModel: null, }); @@ -492,7 +504,7 @@ export class SpaceService { await this.subSpaceService.unlinkModels(space.subspaces, queryRunner); } - if (hasTags && space.productAllocations && space.spaceModel) { + if (hasAllocations && space.productAllocations && space.spaceModel) { await this.spaceProductAllocationService.unlinkModels( space, queryRunner, @@ -508,13 +520,13 @@ export class SpaceService { ); } - if (updateSpaceDto.tags) { + if (updateSpaceDto.productAllocations) { await queryRunner.manager.delete(SpaceProductAllocationEntity, { space: { uuid: space.uuid }, tag: { uuid: Not( In( - updateSpaceDto.tags + updateSpaceDto.productAllocations .filter((tag) => tag.tagUuid) .map((tag) => tag.tagUuid), ), @@ -522,11 +534,7 @@ export class SpaceService { }, }); await this.createAllocations( - updateSpaceDto.tags.map((tag) => ({ - name: tag.name, - uuid: tag.tagUuid, - productUuid: tag.productUuid, - })), + updateSpaceDto.productAllocations, projectUuid, queryRunner, space, @@ -702,13 +710,17 @@ export class SpaceService { private validateSpaceCreationCriteria({ spaceModelUuid, - tags, + productAllocations, subspaces, - }: Pick): void { - const hasTagsOrSubspaces = - (tags && tags.length > 0) || (subspaces && subspaces.length > 0); + }: Pick< + AddSpaceDto, + 'spaceModelUuid' | 'productAllocations' | 'subspaces' + >): void { + const hasProductsOrSubspaces = + (productAllocations && productAllocations.length > 0) || + (subspaces && subspaces.length > 0); - if (spaceModelUuid && hasTagsOrSubspaces) { + if (spaceModelUuid && hasProductsOrSubspaces) { throw new HttpException( 'For space creation choose either space model or products and subspace', HttpStatus.CONFLICT, @@ -717,13 +729,13 @@ export class SpaceService { } private async createAllocations( - tags: ProcessTagDto[], + productAllocations: CreateProductAllocationDto[], projectUuid: string, queryRunner: QueryRunner, space: SpaceEntity, ): Promise { - const allocationsData = await this.tagService.processTags( - tags, + const allocationsData = await this.tagService.upsertTags( + productAllocations, projectUuid, queryRunner, ); @@ -733,15 +745,17 @@ export class SpaceService { const createdTagsByName = new Map(allocationsData.map((t) => [t.name, t])); // Create the product-tag mapping based on the processed tags - const productTagMapping = tags.map(({ uuid, name, productUuid }) => { - const inputTag = uuid - ? createdTagsByUUID.get(uuid) - : createdTagsByName.get(name); - return { - tag: inputTag?.uuid, - product: productUuid, - }; - }); + const productTagMapping = productAllocations.map( + ({ tagUuid, tagName, productUuid }) => { + const inputTag = tagUuid + ? createdTagsByUUID.get(tagUuid) + : createdTagsByName.get(tagName); + return { + tag: inputTag?.uuid, + product: productUuid, + }; + }, + ); await this.spaceProductAllocationService.createProductAllocations( space, diff --git a/src/space/services/subspace/subspace-product-allocation.service.ts b/src/space/services/subspace/subspace-product-allocation.service.ts index b976ce6..a74823c 100644 --- a/src/space/services/subspace/subspace-product-allocation.service.ts +++ b/src/space/services/subspace/subspace-product-allocation.service.ts @@ -3,7 +3,7 @@ import { SubspaceProductAllocationEntity } from '@app/common/modules/space/entit import { SubspaceEntity } from '@app/common/modules/space/entities/subspace/subspace.entity'; import { SubspaceProductAllocationRepository } from '@app/common/modules/space/repositories/subspace.repository'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; -import { UpdateSpaceAllocationDto } from 'src/space/interfaces/update-subspace-allocation.dto'; +import { UpdateSubspaceDto } from 'src/space/dtos'; import { TagService as NewTagService } from 'src/tags/services'; import { In, Not, QueryRunner } from 'typeorm'; @@ -60,31 +60,46 @@ export class SubspaceProductAllocationService { } async updateSubspaceProductAllocationsV2( - subSpaces: UpdateSpaceAllocationDto[], + subSpaces: UpdateSubspaceDto[], projectUuid: string, queryRunner: QueryRunner, ) { await Promise.all( subSpaces.map(async (subspace) => { await queryRunner.manager.delete(SubspaceProductAllocationEntity, { - subspace: { uuid: subspace.uuid }, - tag: { - uuid: Not( - In( - subspace.tags.filter((tag) => tag.uuid).map((tag) => tag.uuid), - ), - ), - }, + subspace: subspace.uuid ? { uuid: subspace.uuid } : undefined, + tag: subspace.productAllocations + ? { + uuid: Not( + In( + subspace.productAllocations + .filter((allocation) => allocation.tagUuid) + .map((allocation) => allocation.tagUuid), + ), + ), + } + : undefined, + product: subspace.productAllocations + ? { + uuid: Not( + In( + subspace.productAllocations + .filter((allocation) => allocation.productUuid) + .map((allocation) => allocation.productUuid), + ), + ), + } + : undefined, }); + const subspaceEntity = await queryRunner.manager.findOne( SubspaceEntity, { where: { uuid: subspace.uuid }, }, ); - - const processedTags = await this.tagService.processTags( - subspace.tags, + const processedTags = await this.tagService.upsertTags( + subspace.productAllocations, projectUuid, queryRunner, ); @@ -97,11 +112,11 @@ export class SubspaceProductAllocationService { ); // Create the product-tag mapping based on the processed tags - const productTagMapping = subspace.tags.map( - ({ uuid, name, productUuid }) => { - const inputTag = uuid - ? createdTagsByUUID.get(uuid) - : createdTagsByName.get(name); + const productTagMapping = subspace.productAllocations.map( + ({ tagUuid, tagName, productUuid }) => { + const inputTag = tagUuid + ? createdTagsByUUID.get(tagUuid) + : createdTagsByName.get(tagName); return { tag: inputTag?.uuid, product: productUuid, @@ -118,71 +133,6 @@ export class SubspaceProductAllocationService { ); } - // async processDeleteActions(dtos: ModifyTagDto[], queryRunner: QueryRunner) { - // // : Promise - // try { - // // if (!dtos || dtos.length === 0) { - // // throw new Error('No DTOs provided for deletion.'); - // // } - // // const tagUuidsToDelete = dtos - // // .filter((dto) => dto.action === ModifyAction.DELETE && dto.tagUuid) - // // .map((dto) => dto.tagUuid); - // // if (tagUuidsToDelete.length === 0) return []; - // // const allocationsToUpdate = await queryRunner.manager.find( - // // SubspaceProductAllocationEntity, - // // { - // // where: { tag: In(tagUuidsToDelete) }, - // // }, - // // ); - // // if (!allocationsToUpdate || allocationsToUpdate.length === 0) return []; - // // const deletedAllocations: SubspaceProductAllocationEntity[] = []; - // // const allocationUpdates: SubspaceProductAllocationEntity[] = []; - // // for (const allocation of allocationsToUpdate) { - // // const updatedTags = allocation.tags.filter( - // // (tag) => !tagUuidsToDelete.includes(tag.uuid), - // // ); - // // if (updatedTags.length === allocation.tags.length) { - // // continue; - // // } - // // if (updatedTags.length === 0) { - // // deletedAllocations.push(allocation); - // // } else { - // // allocation.tags = updatedTags; - // // allocationUpdates.push(allocation); - // // } - // // } - // // if (allocationUpdates.length > 0) { - // // await queryRunner.manager.save( - // // SubspaceProductAllocationEntity, - // // allocationUpdates, - // // ); - // // } - // // if (deletedAllocations.length > 0) { - // // await queryRunner.manager.remove( - // // SubspaceProductAllocationEntity, - // // deletedAllocations, - // // ); - // // } - // // await queryRunner.manager - // // .createQueryBuilder() - // // .delete() - // // .from('subspace_product_tags') - // // .where( - // // 'subspace_product_allocation_uuid NOT IN ' + - // // queryRunner.manager - // // .createQueryBuilder() - // // .select('allocation.uuid') - // // .from(SubspaceProductAllocationEntity, 'allocation') - // // .getQuery() + - // // ')', - // // ) - // // .execute(); - // // return deletedAllocations; - // } catch (error) { - // throw this.handleError(error, `Failed to delete tags in subspace`); - // } - // } - async unlinkModels( allocations: SubspaceProductAllocationEntity[], queryRunner: QueryRunner, @@ -205,67 +155,6 @@ export class SubspaceProductAllocationService { } } - // private async validateTagWithinSubspace( - // queryRunner: QueryRunner | undefined, - // tag: NewTagEntity & { product: string }, - // subspace: SubspaceEntity, - // spaceAllocationsToExclude?: SpaceProductAllocationEntity[], - // ): Promise { - // // const existingTagInSpace = await (queryRunner - // // ? queryRunner.manager.findOne(SpaceProductAllocationEntity, { - // // where: { - // // product: { uuid: tag.product }, - // // space: { uuid: subspace.space.uuid }, - // // tag: { uuid: tag.uuid }, - // // }, - // // }) - // // : this.spaceProductAllocationRepository.findOne({ - // // where: { - // // product: { uuid: tag.product }, - // // space: { uuid: subspace.space.uuid }, - // // tag: { uuid: tag.uuid }, - // // }, - // // })); - // // const isExcluded = spaceAllocationsToExclude?.some( - // // (excludedAllocation) => - // // excludedAllocation.product.uuid === tag.product && - // // excludedAllocation.tags.some((t) => t.uuid === tag.uuid), - // // ); - // // if (!isExcluded && existingTagInSpace) { - // // throw new HttpException( - // // `Tag ${tag.uuid} (Product: ${tag.product}) is already allocated at the space level (${subspace.space.uuid}). Cannot allocate the same tag in a subspace.`, - // // HttpStatus.BAD_REQUEST, - // // ); - // // } - // // // ?: Check if the tag is already allocated in another "subspace" within the same space - // // const existingTagInSameSpace = await (queryRunner - // // ? queryRunner.manager.findOne(SubspaceProductAllocationEntity, { - // // where: { - // // product: { uuid: tag.product }, - // // subspace: { space: subspace.space }, - // // tag: { uuid: tag.uuid }, - // // }, - // // relations: ['subspace'], - // // }) - // // : this.subspaceProductAllocationRepository.findOne({ - // // where: { - // // product: { uuid: tag.product }, - // // subspace: { space: subspace.space }, - // // tag: { uuid: tag.uuid }, - // // }, - // // relations: ['subspace'], - // // })); - // // if ( - // // existingTagInSameSpace && - // // existingTagInSameSpace.subspace.uuid !== subspace.uuid - // // ) { - // // throw new HttpException( - // // `Tag ${tag.uuid} (Product: ${tag.product}) is already allocated in another subspace (${existingTagInSameSpace.subspace.uuid}) within the same space (${subspace.space.uuid}).`, - // // HttpStatus.BAD_REQUEST, - // // ); - // // } - // } - private createNewSubspaceAllocation( subspace: SubspaceEntity, allocationData: { product: string; tag: string }, diff --git a/src/space/services/subspace/subspace.service.ts b/src/space/services/subspace/subspace.service.ts index d9bcd3d..d1bf83f 100644 --- a/src/space/services/subspace/subspace.service.ts +++ b/src/space/services/subspace/subspace.service.ts @@ -12,7 +12,7 @@ import { AddSubspaceDto, GetSpaceParam, GetSubSpaceParam, - ModifySubspaceDto, + UpdateSubspaceDto, } from '../../dtos'; import { SubspaceModelEntity } from '@app/common/modules/space-model'; @@ -103,13 +103,13 @@ export class SubSpaceService { queryRunner, ); await Promise.all( - addSubspaceDtos.map(async ({ tags }, index) => { + addSubspaceDtos.map(async ({ productAllocations }, index) => { // map the dto to the corresponding subspace const subspace = createdSubspaces[index]; await this.createAllocations({ projectUuid, queryRunner, - tags, + productAllocations, type: AllocationsOwnerType.SUBSPACE, subspace, }); @@ -145,7 +145,7 @@ export class SubSpaceService { space, ); const newSubspace = this.subspaceRepository.create({ - ...addSubspaceDto, + subspaceName: addSubspaceDto.subspaceName, space, }); @@ -305,7 +305,7 @@ export class SubSpaceService { } */ async updateSubspaceInSpace( - subspaceDtos: ModifySubspaceDto[], + subspaceDtos: UpdateSubspaceDto[], queryRunner: QueryRunner, space: SpaceEntity, projectUuid: string, @@ -324,42 +324,52 @@ export class SubSpaceService { disabled: true, }, ); - await queryRunner.manager.delete(SubspaceProductAllocationEntity, { - subspace: { uuid: Not(In(subspaceDtos.map((dto) => dto.uuid))) }, + subspace: { + uuid: Not( + In(subspaceDtos.filter(({ uuid }) => uuid).map(({ uuid }) => uuid)), + ), + }, }); // create or update subspaces provided in the list const newSubspaces = this.subspaceRepository.create( - subspaceDtos.filter((dto) => !dto.uuid), + subspaceDtos + .filter((dto) => !dto.uuid) + .map((dto) => ({ + subspaceName: dto.subspaceName, + space, + })), ); const updatedSubspaces: SubspaceEntity[] = await queryRunner.manager.save( SubspaceEntity, - [...newSubspaces, ...subspaceDtos.filter((dto) => dto.uuid)].map( - (subspace) => ({ ...subspace, space }), - ), + [ + ...newSubspaces, + ...subspaceDtos + .filter((dto) => dto.uuid) + .map((dto) => ({ + subspaceName: dto.subspaceName, + space, + })), + ], ); - // create or update allocations for the subspaces if (updatedSubspaces.length > 0) { await this.subspaceProductAllocationService.updateSubspaceProductAllocationsV2( - subspaceDtos.map((dto) => { - if (!dto.uuid) { - dto.uuid = updatedSubspaces.find( - (subspace) => subspace.subspaceName === dto.subspaceName, - )?.uuid; - } - return { - tags: dto.tags || [], - uuid: dto.uuid, - }; - }), + subspaceDtos.map((dto) => ({ + ...dto, + uuid: + dto.uuid || + updatedSubspaces.find((s) => s.subspaceName === dto.subspaceName) + ?.uuid, + })), projectUuid, queryRunner, ); } } catch (error) { + console.log(error); throw new HttpException( `An error occurred while modifying subspaces: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR, @@ -478,10 +488,10 @@ export class SubSpaceService { } async createAllocations(dto: CreateAllocationsDto): Promise { - const { projectUuid, queryRunner, tags, type } = dto; - - const allocationsData = await this.newTagService.processTags( - tags, + const { projectUuid, queryRunner, productAllocations, type } = dto; + if (!productAllocations) return; + const allocationsData = await this.newTagService.upsertTags( + productAllocations, projectUuid, queryRunner, ); @@ -491,15 +501,17 @@ export class SubSpaceService { const createdTagsByName = new Map(allocationsData.map((t) => [t.name, t])); // Create the product-tag mapping based on the processed tags - const productTagMapping = tags.map(({ uuid, name, productUuid }) => { - const inputTag = uuid - ? createdTagsByUUID.get(uuid) - : createdTagsByName.get(name); - return { - tag: inputTag?.uuid, - product: productUuid, - }; - }); + const productTagMapping = productAllocations.map( + ({ tagUuid, tagName, productUuid }) => { + const inputTag = tagUuid + ? createdTagsByUUID.get(tagUuid) + : createdTagsByName.get(tagName); + return { + tag: inputTag?.uuid, + product: productUuid, + }; + }, + ); switch (type) { case AllocationsOwnerType.SUBSPACE: { diff --git a/src/space/space.module.ts b/src/space/space.module.ts index 58f54a5..8229879 100644 --- a/src/space/space.module.ts +++ b/src/space/space.module.ts @@ -79,7 +79,6 @@ import { SpaceValidationController } from './controllers/space-validation.contro import { DisableSpaceHandler } from './handlers'; import { SpaceDeviceService, - SpaceLinkService, SpaceSceneService, SpaceService, SpaceUserService, @@ -110,7 +109,6 @@ export const CommandHandlers = [DisableSpaceHandler]; ProductRepository, SubSpaceService, SpaceDeviceService, - SpaceLinkService, SubspaceDeviceService, SpaceRepository, SubspaceRepository, diff --git a/src/tags/services/tags.service.ts b/src/tags/services/tags.service.ts index b82ebc2..e43438d 100644 --- a/src/tags/services/tags.service.ts +++ b/src/tags/services/tags.service.ts @@ -14,8 +14,8 @@ import { Injectable, NotFoundException, } from '@nestjs/common'; +import { CreateProductAllocationDto } from 'src/space/dtos'; import { In, QueryRunner } from 'typeorm'; -import { ProcessTagDto } from '../dtos'; import { CreateTagDto } from '../dtos/tags.dto'; @Injectable() @@ -68,13 +68,13 @@ export class TagService { /** * Processes an array of tag DTOs, creating or updating tags in the database. - * @param tagDtos - The array of tag DTOs to process. + * @param allocationDtos - The array of allocations DTOs to process. * @param projectUuid - The UUID of the project to associate the tags with. * @param queryRunner - Optional TypeORM query runner for transaction management. * @returns An array of the processed tag entities. */ - async processTags( - tagDtos: ProcessTagDto[], + async upsertTags( + allocationDtos: CreateProductAllocationDto[], projectUuid: string, queryRunner?: QueryRunner, ): Promise { @@ -82,20 +82,22 @@ export class TagService { const dbManager = queryRunner ? queryRunner.manager : this.tagRepository.manager; - if (!tagDtos || tagDtos.length === 0) { + if (!allocationDtos || allocationDtos.length === 0) { return []; } - const [tagsWithUuid, tagsWithoutUuid]: [ - Pick[], - Omit[], - ] = this.splitTagsByUuid(tagDtos); + const [allocationsWithTagUuid, allocationsWithoutTagUuid]: [ + Pick[], + Omit[], + ] = this.splitTagsByUuid(allocationDtos); // create a set of unique existing tag names for the project const upsertedTagsByNameResult = await dbManager.upsert( NewTagEntity, Array.from( - new Set(tagsWithoutUuid.map((tag) => tag.name)).values(), + new Set( + allocationsWithoutTagUuid.map((allocation) => allocation.tagName), + ).values(), ).map((name) => ({ name, project: { uuid: projectUuid }, @@ -111,20 +113,22 @@ export class TagService { let foundByUuidTags: NewTagEntity[] = []; // Fetch existing tags using UUIDs - if (tagsWithUuid.length) { + if (allocationsWithTagUuid.length) { foundByUuidTags = await dbManager.find(NewTagEntity, { where: { - uuid: In([...tagsWithUuid.map((tag) => tag.uuid)]), + uuid: In([ + ...allocationsWithTagUuid.map((allocation) => allocation.tagUuid), + ]), project: { uuid: projectUuid }, }, }); } // Ensure all provided UUIDs exist in the database - if (foundByUuidTags.length !== tagsWithUuid.length) { + if (foundByUuidTags.length !== allocationsWithTagUuid.length) { const foundUuids = new Set(foundByUuidTags.map((tag) => tag.uuid)); - const missingUuids = tagsWithUuid.filter( - ({ uuid }) => !foundUuids.has(uuid), + const missingUuids = allocationsWithTagUuid.filter( + ({ tagUuid }) => !foundUuids.has(tagUuid), ); throw new HttpException( @@ -179,20 +183,22 @@ export class TagService { } private splitTagsByUuid( - tagDtos: ProcessTagDto[], - ): [ProcessTagDto[], ProcessTagDto[]] { - return tagDtos.reduce<[ProcessTagDto[], ProcessTagDto[]]>( - ([withUuid, withoutUuid], tag) => { - if (tag.uuid) { - withUuid.push(tag); + allocationsDtos: CreateProductAllocationDto[], + ): [CreateProductAllocationDto[], CreateProductAllocationDto[]] { + return allocationsDtos.reduce< + [CreateProductAllocationDto[], CreateProductAllocationDto[]] + >( + ([withUuid, withoutUuid], allocation) => { + if (allocation.tagUuid) { + withUuid.push(allocation); } else { - if (!tag.name || !tag.productUuid) { + if (!allocation.tagName || !allocation.productUuid) { throw new HttpException( `Tag name or product UUID is missing`, HttpStatus.BAD_REQUEST, ); } - withoutUuid.push(tag); + withoutUuid.push(allocation); } return [withUuid, withoutUuid]; }, diff --git a/src/users/dtos/add.space.dto.ts b/src/users/dtos/add.space.dto.ts index 635cec9..1bd87d7 100644 --- a/src/users/dtos/add.space.dto.ts +++ b/src/users/dtos/add.space.dto.ts @@ -1,38 +1,5 @@ import { ApiProperty } from '@nestjs/swagger'; -import { - IsBoolean, - IsNotEmpty, - IsOptional, - IsString, - IsUUID, -} from 'class-validator'; - -export class AddSpaceDto { - @ApiProperty({ - description: 'Name of the space (e.g., Floor 1, Unit 101)', - example: 'Unit 101', - }) - @IsString() - @IsNotEmpty() - spaceName: string; - - @ApiProperty({ - description: 'UUID of the parent space (if any, for hierarchical spaces)', - example: 'f5d7e9c3-44bc-4b12-88f1-1b3cda84752e', - required: false, - }) - @IsUUID() - @IsOptional() - parentUuid?: string; - - @ApiProperty({ - description: 'Indicates whether the space is private or public', - example: false, - default: false, - }) - @IsBoolean() - isPrivate: boolean; -} +import { IsNotEmpty, IsString } from 'class-validator'; export class AddUserSpaceDto { @ApiProperty({ From 8c34c68ba6a32bcea5d25201b63e29a66ce3f5a3 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Wed, 18 Jun 2025 01:48:35 -0600 Subject: [PATCH 04/86] refactor: remove BookableSpaceDto and its index export --- .../booking/dtos/bookable-space.dto.ts | 23 ------------------- libs/common/src/modules/booking/dtos/index.ts | 1 - 2 files changed, 24 deletions(-) delete mode 100644 libs/common/src/modules/booking/dtos/bookable-space.dto.ts delete mode 100644 libs/common/src/modules/booking/dtos/index.ts diff --git a/libs/common/src/modules/booking/dtos/bookable-space.dto.ts b/libs/common/src/modules/booking/dtos/bookable-space.dto.ts deleted file mode 100644 index 8c7c41a..0000000 --- a/libs/common/src/modules/booking/dtos/bookable-space.dto.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { DaysEnum } from '@app/common/constants/days.enum'; -import { IsArray, IsEnum, IsInt, IsNotEmpty, IsString } from 'class-validator'; - -export class BookableSpaceDto { - @IsString() - @IsNotEmpty() - public uuid: string; - - @IsArray() - @IsEnum(DaysEnum, { each: true }) - daysAvailable: DaysEnum[]; - - @IsString() - @IsNotEmpty() - startTime: string; - - @IsString() - @IsNotEmpty() - endTime: string; - - @IsInt() - points: number; -} diff --git a/libs/common/src/modules/booking/dtos/index.ts b/libs/common/src/modules/booking/dtos/index.ts deleted file mode 100644 index 29762ae..0000000 --- a/libs/common/src/modules/booking/dtos/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './bookable-space.dto'; From df59e9a4a381e2092144d07e62c5867a7f948bf9 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Wed, 18 Jun 2025 01:48:46 -0600 Subject: [PATCH 05/86] refactor: update BookableSpaceEntity relationship to OneToOne with SpaceEntity --- .../booking/entities/bookable-space.entity.ts | 13 +++++-------- .../src/modules/space/entities/space.entity.ts | 13 ++++++++++--- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/libs/common/src/modules/booking/entities/bookable-space.entity.ts b/libs/common/src/modules/booking/entities/bookable-space.entity.ts index d5a12ac..bbe5e90 100644 --- a/libs/common/src/modules/booking/entities/bookable-space.entity.ts +++ b/libs/common/src/modules/booking/entities/bookable-space.entity.ts @@ -1,19 +1,17 @@ import { Entity, Column, - ManyToOne, CreateDateColumn, UpdateDateColumn, - Unique, + OneToOne, + JoinColumn, } from 'typeorm'; import { SpaceEntity } from '../../space/entities/space.entity'; import { DaysEnum } from '@app/common/constants/days.enum'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; -import { BookableSpaceDto } from '../dtos'; @Entity('bookable-space') -@Unique(['space']) -export class BookableSpaceEntity extends AbstractEntity { +export class BookableSpaceEntity extends AbstractEntity { @Column({ type: 'uuid', default: () => 'gen_random_uuid()', @@ -21,9 +19,8 @@ export class BookableSpaceEntity extends AbstractEntity { }) public uuid: string; - @ManyToOne(() => SpaceEntity, (space) => space.bookableConfigs, { - onDelete: 'CASCADE', - }) + @OneToOne(() => SpaceEntity, (space) => space.bookableConfigs) + @JoinColumn({ name: 'space_uuid' }) space: SpaceEntity; @Column({ diff --git a/libs/common/src/modules/space/entities/space.entity.ts b/libs/common/src/modules/space/entities/space.entity.ts index 2b0db0f..5200872 100644 --- a/libs/common/src/modules/space/entities/space.entity.ts +++ b/libs/common/src/modules/space/entities/space.entity.ts @@ -1,4 +1,11 @@ -import { Column, Entity, JoinColumn, ManyToOne, OneToMany } from 'typeorm'; +import { + Column, + Entity, + JoinColumn, + ManyToOne, + OneToMany, + OneToOne, +} from 'typeorm'; import { SpaceDto } from '../dtos'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { UserSpaceEntity } from '../../user/entities'; @@ -127,8 +134,8 @@ export class SpaceEntity extends AbstractEntity { ) occupancyDaily: SpaceDailyOccupancyDurationEntity[]; - @OneToMany(() => BookableSpaceEntity, (bookable) => bookable.space) - bookableConfigs: BookableSpaceEntity[]; + @OneToOne(() => BookableSpaceEntity, (bookable) => bookable.space) + bookableConfigs: BookableSpaceEntity; constructor(partial: Partial) { super(); From 274cdf741fa78bc343e67ae8843caa18fd4e914e Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Wed, 18 Jun 2025 01:49:00 -0600 Subject: [PATCH 06/86] refactor: streamline Booking module and service by removing unused imports and consolidating space validation logic --- src/booking/booking.module.ts | 11 +-- src/booking/dtos/create-bookable-space.dto.ts | 45 ++++------- .../services/bookable-space.service.ts | 74 +++++++++---------- 3 files changed, 55 insertions(+), 75 deletions(-) diff --git a/src/booking/booking.module.ts b/src/booking/booking.module.ts index 6ce9c9a..fc9b2db 100644 --- a/src/booking/booking.module.ts +++ b/src/booking/booking.module.ts @@ -1,16 +1,17 @@ import { Global, Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; import { BookableSpaceController } from './controllers'; import { BookableSpaceService } from './services'; import { BookableSpaceEntityRepository } from '@app/common/modules/booking/repositories'; -import { BookableSpaceEntity } from '@app/common/modules/booking/entities'; -import { SpaceEntity } from '@app/common/modules/space/entities/space.entity'; +import { SpaceRepository } from '@app/common/modules/space'; @Global() @Module({ - imports: [TypeOrmModule.forFeature([BookableSpaceEntity, SpaceEntity])], controllers: [BookableSpaceController], - providers: [BookableSpaceService, BookableSpaceEntityRepository], + providers: [ + BookableSpaceService, + BookableSpaceEntityRepository, + SpaceRepository, + ], exports: [BookableSpaceService], }) export class BookingModule {} diff --git a/src/booking/dtos/create-bookable-space.dto.ts b/src/booking/dtos/create-bookable-space.dto.ts index a8d88ba..6508c36 100644 --- a/src/booking/dtos/create-bookable-space.dto.ts +++ b/src/booking/dtos/create-bookable-space.dto.ts @@ -1,7 +1,6 @@ // dtos/bookable-space.dto.ts import { DaysEnum } from '@app/common/constants/days.enum'; import { ApiProperty } from '@nestjs/swagger'; -import { Type } from 'class-transformer'; import { IsArray, IsEnum, @@ -9,15 +8,26 @@ import { IsString, IsUUID, IsInt, - ValidateNested, ArrayMinSize, - ArrayMaxSize, - Min, Max, + Min, Matches, } from 'class-validator'; -export class BookingSlotDto { +export class CreateBookableSpaceDto { + @ApiProperty({ + type: 'string', + isArray: true, + example: [ + '3fa85f64-5717-4562-b3fc-2c963f66afa6', + '4fa85f64-5717-4562-b3fc-2c963f66afa7', + ], + }) + @IsArray() + @ArrayMinSize(1, { message: 'At least one space must be selected' }) + @IsUUID('all', { each: true, message: 'Invalid space UUID provided' }) + spaceUuids: string[]; + @ApiProperty({ enum: DaysEnum, isArray: true, @@ -25,7 +35,6 @@ export class BookingSlotDto { }) @IsArray() @ArrayMinSize(1, { message: 'At least one day must be selected' }) - @ArrayMaxSize(7, { message: 'Cannot select more than 7 days' }) @IsEnum(DaysEnum, { each: true, message: 'Invalid day provided' }) daysAvailable: DaysEnum[]; @@ -45,31 +54,9 @@ export class BookingSlotDto { }) endTime: string; - @ApiProperty({ example: 10, minimum: 0 }) + @ApiProperty({ example: 10 }) @IsInt() @Min(0, { message: 'Points cannot be negative' }) @Max(1000, { message: 'Points cannot exceed 1000' }) points: number; } - -export class CreateBookableSpaceDto { - @ApiProperty({ - type: 'string', - isArray: true, - example: [ - '3fa85f64-5717-4562-b3fc-2c963f66afa6', - '4fa85f64-5717-4562-b3fc-2c963f66afa7', - ], - }) - @IsArray() - @ArrayMinSize(1, { message: 'At least one space must be selected' }) - @IsUUID('all', { each: true, message: 'Invalid space UUID provided' }) - spaceUuids: string[]; - - @ApiProperty({ type: BookingSlotDto, isArray: true }) - @IsArray() - @ArrayMinSize(1, { message: 'At least one booking slot must be provided' }) - @ValidateNested({ each: true }) - @Type(() => BookingSlotDto) - slots: BookingSlotDto[]; -} diff --git a/src/booking/services/bookable-space.service.ts b/src/booking/services/bookable-space.service.ts index 57b573a..409ee35 100644 --- a/src/booking/services/bookable-space.service.ts +++ b/src/booking/services/bookable-space.service.ts @@ -1,50 +1,46 @@ -// services/bookable-space.service.ts import { Injectable, NotFoundException, ConflictException, BadRequestException, } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository, In } from 'typeorm'; -import { BookingSlotDto, CreateBookableSpaceDto } from '../dtos'; +import { CreateBookableSpaceDto } from '../dtos'; import { BaseResponseDto } from '@app/common/dto/base.response.dto'; import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; -import { BookableSpaceEntity } from '@app/common/modules/booking/entities'; import { SpaceEntity } from '@app/common/modules/space/entities/space.entity'; +import { BookableSpaceEntityRepository } from '@app/common/modules/booking/repositories'; +import { SpaceRepository } from '@app/common/modules/space/repositories/space.repository'; +import { In } from 'typeorm'; import { timeToMinutes } from '@app/common/helper/timeToMinutes'; @Injectable() export class BookableSpaceService { constructor( - @InjectRepository(BookableSpaceEntity) - private bookableRepo: Repository, - - @InjectRepository(SpaceEntity) - private spaceRepo: Repository, + private readonly bookableSpaceEntityRepository: BookableSpaceEntityRepository, + private readonly spaceRepository: SpaceRepository, ) {} async create(dto: CreateBookableSpaceDto): Promise { // Validate time slots first - this.validateTimeSlots(dto.slots); + this.validateTimeSlot(dto.startTime, dto.endTime); - // Validate spaces exist - const spaces = await this.validateSpacesExist(dto.spaceUuids); + // fetch spaces exist + const spaces = await this.getSpacesOrFindMissing(dto.spaceUuids); // Validate no duplicate bookable configurations await this.validateNoDuplicateBookableConfigs(dto.spaceUuids); // Create and save bookable spaces - return this.createBookableSpaces(spaces, dto.slots); + return this.createBookableSpaces(spaces, dto); } /** - * Validate that all specified spaces exist + * Fetch spaces by UUIDs and throw an error if any are missing */ - private async validateSpacesExist( + private async getSpacesOrFindMissing( spaceUuids: string[], ): Promise { - const spaces = await this.spaceRepo.find({ + const spaces = await this.spaceRepository.find({ where: { uuid: In(spaceUuids) }, }); @@ -67,7 +63,7 @@ export class BookableSpaceService { private async validateNoDuplicateBookableConfigs( spaceUuids: string[], ): Promise { - const existingBookables = await this.bookableRepo.find({ + const existingBookables = await this.bookableSpaceEntityRepository.find({ where: { space: { uuid: In(spaceUuids) } }, relations: ['space'], }); @@ -83,19 +79,17 @@ export class BookableSpaceService { } /** - * Validate time slots have valid format and logical times + * Ensure the slot start time is before the end time */ - private validateTimeSlots(slots: BookingSlotDto[]): void { - slots.forEach((slot) => { - const start = timeToMinutes(slot.startTime); - const end = timeToMinutes(slot.endTime); + private validateTimeSlot(startTime: string, endTime: string): void { + const start = timeToMinutes(startTime); + const end = timeToMinutes(endTime); - if (start >= end) { - throw new BadRequestException( - `End time must be after start time for slot: ${slot.startTime}-${slot.endTime}`, - ); - } - }); + if (start >= end) { + throw new BadRequestException( + `End time must be after start time for slot: ${startTime}-${endTime}`, + ); + } } /** @@ -103,22 +97,20 @@ export class BookableSpaceService { */ private async createBookableSpaces( spaces: SpaceEntity[], - slots: BookingSlotDto[], + dto: CreateBookableSpaceDto, ): Promise { try { - const entries = spaces.flatMap((space) => - slots.map((slot) => - this.bookableRepo.create({ - space, - daysAvailable: slot.daysAvailable, - startTime: slot.startTime, - endTime: slot.endTime, - points: slot.points, - }), - ), + const entries = spaces.map((space) => + this.bookableSpaceEntityRepository.create({ + space, + daysAvailable: dto.daysAvailable, + startTime: dto.startTime, + endTime: dto.endTime, + points: dto.points, + }), ); - const data = await this.bookableRepo.save(entries); + const data = await this.bookableSpaceEntityRepository.save(entries); return new SuccessResponseDto({ data, message: 'Successfully added new bookable spaces', From 49cc7629622e278b89d543906ff5530e43953f3e Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Wed, 18 Jun 2025 01:56:51 -0600 Subject: [PATCH 07/86] fix duplication from conflict merge --- libs/common/src/modules/space/entities/space.entity.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libs/common/src/modules/space/entities/space.entity.ts b/libs/common/src/modules/space/entities/space.entity.ts index cfab8ce..d3c84e0 100644 --- a/libs/common/src/modules/space/entities/space.entity.ts +++ b/libs/common/src/modules/space/entities/space.entity.ts @@ -17,12 +17,8 @@ import { PresenceSensorDailySpaceEntity } from '../../presence-sensor/entities'; import { SceneEntity } from '../../scene/entities'; import { SpaceModelEntity } from '../../space-model'; import { UserSpaceEntity } from '../../user/entities'; -import { SpaceDto } from '../dtos'; import { SpaceProductAllocationEntity } from './space-product-allocation.entity'; import { SubspaceEntity } from './subspace/subspace.entity'; -import { PresenceSensorDailySpaceEntity } from '../../presence-sensor/entities'; -import { AqiSpaceDailyPollutantStatsEntity } from '../../aqi/entities'; -import { SpaceDailyOccupancyDurationEntity } from '../../occupancy/entities'; import { BookableSpaceEntity } from '../../booking/entities'; @Entity({ name: 'space' }) From a37d5bb29954abfdb03004357b3f611d5d3d4634 Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Wed, 18 Jun 2025 12:05:53 +0300 Subject: [PATCH 08/86] task: add trust proxy header (#411) * task: add trust proxy header * add logging --- src/main.ts | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/main.ts b/src/main.ts index d337a66..c9256ef 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,15 +1,14 @@ +import { RequestContextMiddleware } from '@app/common/middleware/request-context.middleware'; +import { SeederService } from '@app/common/seed/services/seeder.service'; +import { Logger, ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { json, urlencoded } from 'body-parser'; import rateLimit from 'express-rate-limit'; import helmet from 'helmet'; -import { setupSwaggerAuthentication } from '../libs/common/src/util/user-auth.swagger.utils'; -import { ValidationPipe } from '@nestjs/common'; -import { json, urlencoded } from 'body-parser'; -import { SeederService } from '@app/common/seed/services/seeder.service'; -import { HttpExceptionFilter } from './common/filters/http-exception/http-exception.filter'; -import { Logger } from '@nestjs/common'; import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston'; -import { RequestContextMiddleware } from '@app/common/middleware/request-context.middleware'; +import { setupSwaggerAuthentication } from '../libs/common/src/util/user-auth.swagger.utils'; +import { AppModule } from './app.module'; +import { HttpExceptionFilter } from './common/filters/http-exception/http-exception.filter'; async function bootstrap() { const app = await NestFactory.create(AppModule); @@ -27,9 +26,18 @@ async function bootstrap() { rateLimit({ windowMs: 5 * 60 * 1000, max: 500, + standardHeaders: true, + legacyHeaders: false, }), ); + app.use((req, res, next) => { + console.log('Real IP:', req.ip); + next(); + }); + + app.getHttpAdapter().getInstance().set('trust proxy', 1); + app.use( helmet({ contentSecurityPolicy: false, From 705ceeba293464815951d3d05e83187a3102f151 Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Thu, 19 Jun 2025 09:45:09 +0300 Subject: [PATCH 09/86] Test/prevent server block on rate limit (#414) * task: test rate limits on sever --- src/main.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main.ts b/src/main.ts index c9256ef..0a01fdd 100644 --- a/src/main.ts +++ b/src/main.ts @@ -9,9 +9,10 @@ import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston'; import { setupSwaggerAuthentication } from '../libs/common/src/util/user-auth.swagger.utils'; import { AppModule } from './app.module'; import { HttpExceptionFilter } from './common/filters/http-exception/http-exception.filter'; +import { NestExpressApplication } from '@nestjs/platform-express'; async function bootstrap() { - const app = await NestFactory.create(AppModule); + const app = await NestFactory.create(AppModule); app.enableCors(); @@ -21,11 +22,12 @@ async function bootstrap() { app.useGlobalFilters(new HttpExceptionFilter()); app.use(new RequestContextMiddleware().use); + app.set('trust proxy', true); app.use( rateLimit({ - windowMs: 5 * 60 * 1000, - max: 500, + windowMs: 30 * 1000, + max: 50, standardHeaders: true, legacyHeaders: false, }), @@ -36,7 +38,7 @@ async function bootstrap() { next(); }); - app.getHttpAdapter().getInstance().set('trust proxy', 1); + // app.getHttpAdapter().getInstance().set('trust proxy', 1); app.use( helmet({ From 0e36f32ed668e07df1b7861ace2a8a50f3f13beb Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Thu, 19 Jun 2025 10:15:29 +0300 Subject: [PATCH 10/86] Test/prevent server block on rate limit (#415) * task: increase rate limit timeout --- src/main.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index 0a01fdd..543767f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,6 +2,7 @@ import { RequestContextMiddleware } from '@app/common/middleware/request-context import { SeederService } from '@app/common/seed/services/seeder.service'; import { Logger, ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; +import { NestExpressApplication } from '@nestjs/platform-express'; import { json, urlencoded } from 'body-parser'; import rateLimit from 'express-rate-limit'; import helmet from 'helmet'; @@ -26,7 +27,7 @@ async function bootstrap() { app.use( rateLimit({ - windowMs: 30 * 1000, + windowMs: 2 * 60 * 1000, max: 50, standardHeaders: true, legacyHeaders: false, From 603e74af0962553f4a5b43b16094648105219005 Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Thu, 19 Jun 2025 12:54:59 +0300 Subject: [PATCH 11/86] Test/prevent server block on rate limit (#417) * task: add trust proxy header * add logging * task: test rate limits on sever * task: increase rate limit timeout * fix: merge conflicts --- src/main.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index 543767f..33ce70a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -10,7 +10,6 @@ import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston'; import { setupSwaggerAuthentication } from '../libs/common/src/util/user-auth.swagger.utils'; import { AppModule } from './app.module'; import { HttpExceptionFilter } from './common/filters/http-exception/http-exception.filter'; -import { NestExpressApplication } from '@nestjs/platform-express'; async function bootstrap() { const app = await NestFactory.create(AppModule); From c5dd5e28fd88f3026f3c59b01d8c186f118043d9 Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Thu, 19 Jun 2025 13:54:22 +0300 Subject: [PATCH 12/86] Test/prevent server block on rate limit (#418) --- src/main.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/main.ts b/src/main.ts index 33ce70a..e00dca6 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,7 +2,6 @@ import { RequestContextMiddleware } from '@app/common/middleware/request-context import { SeederService } from '@app/common/seed/services/seeder.service'; import { Logger, ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; -import { NestExpressApplication } from '@nestjs/platform-express'; import { json, urlencoded } from 'body-parser'; import rateLimit from 'express-rate-limit'; import helmet from 'helmet'; @@ -12,7 +11,7 @@ import { AppModule } from './app.module'; import { HttpExceptionFilter } from './common/filters/http-exception/http-exception.filter'; async function bootstrap() { - const app = await NestFactory.create(AppModule); + const app = await NestFactory.create(AppModule); app.enableCors(); @@ -22,14 +21,11 @@ async function bootstrap() { app.useGlobalFilters(new HttpExceptionFilter()); app.use(new RequestContextMiddleware().use); - app.set('trust proxy', true); app.use( rateLimit({ - windowMs: 2 * 60 * 1000, - max: 50, - standardHeaders: true, - legacyHeaders: false, + windowMs: 5 * 60 * 1000, + max: 500, }), ); From aa9e90bf084afabb2788408e2d3716a052df606f Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Thu, 19 Jun 2025 14:34:23 +0300 Subject: [PATCH 13/86] Test/prevent server block on rate limit (#419) * increase DB max connection to 50 --- libs/common/src/database/database.module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/common/src/database/database.module.ts b/libs/common/src/database/database.module.ts index 373041c..f3ec232 100644 --- a/libs/common/src/database/database.module.ts +++ b/libs/common/src/database/database.module.ts @@ -125,7 +125,7 @@ import { VisitorPasswordEntity } from '../modules/visitor-password/entities'; logger: typeOrmLogger, extra: { charset: 'utf8mb4', - max: 20, // set pool max size + max: 50, // set pool max size idleTimeoutMillis: 5000, // close idle clients after 5 second connectionTimeoutMillis: 12_000, // return an error after 11 second if connection could not be established maxUses: 7500, // close (and replace) a connection after it has been used 7500 times (see below for discussion) From 110ed4157a2456aae523b15e274fa3f872dd46c8 Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Mon, 23 Jun 2025 09:34:59 +0300 Subject: [PATCH 14/86] task: add spaces filter to get devices by project (#422) --- .../controllers/device-project.controller.ts | 14 ++++---- src/device/dtos/get.device.dto.ts | 32 +++++++++++++------ src/device/services/device.service.ts | 16 ++++++---- 3 files changed, 38 insertions(+), 24 deletions(-) diff --git a/src/device/controllers/device-project.controller.ts b/src/device/controllers/device-project.controller.ts index e5181dd..1585415 100644 --- a/src/device/controllers/device-project.controller.ts +++ b/src/device/controllers/device-project.controller.ts @@ -1,11 +1,11 @@ -import { DeviceService } from '../services/device.service'; -import { Controller, Get, Param, Query, UseGuards } from '@nestjs/common'; -import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger'; -import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; import { ControllerRoute } from '@app/common/constants/controller-route'; -import { PermissionsGuard } from 'src/guards/permissions.guard'; +import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; +import { Controller, Get, Param, Query, UseGuards } from '@nestjs/common'; +import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; import { Permissions } from 'src/decorators/permissions.decorator'; -import { GetDoorLockDevices, ProjectParam } from '../dtos'; +import { PermissionsGuard } from 'src/guards/permissions.guard'; +import { GetDevicesFilterDto, ProjectParam } from '../dtos'; +import { DeviceService } from '../services/device.service'; @ApiTags('Device Module') @Controller({ @@ -25,7 +25,7 @@ export class DeviceProjectController { }) async getAllDevices( @Param() param: ProjectParam, - @Query() query: GetDoorLockDevices, + @Query() query: GetDevicesFilterDto, ) { return await this.deviceService.getAllDevices(param, query); } diff --git a/src/device/dtos/get.device.dto.ts b/src/device/dtos/get.device.dto.ts index 84c9d64..e34a8b6 100644 --- a/src/device/dtos/get.device.dto.ts +++ b/src/device/dtos/get.device.dto.ts @@ -1,6 +1,7 @@ import { DeviceTypeEnum } from '@app/common/constants/device-type.enum'; import { ApiProperty } from '@nestjs/swagger'; import { + IsArray, IsEnum, IsNotEmpty, IsOptional, @@ -41,16 +42,7 @@ export class GetDeviceLogsDto { @IsOptional() public endTime: string; } -export class GetDoorLockDevices { - @ApiProperty({ - description: 'Device Type', - enum: DeviceTypeEnum, - required: false, - }) - @IsEnum(DeviceTypeEnum) - @IsOptional() - public deviceType: DeviceTypeEnum; -} + export class GetDevicesBySpaceOrCommunityDto { @ApiProperty({ description: 'Device Product Type', @@ -72,3 +64,23 @@ export class GetDevicesBySpaceOrCommunityDto { @IsNotEmpty({ message: 'Either spaceUuid or communityUuid must be provided' }) requireEither?: never; // This ensures at least one of them is provided } + +export class GetDevicesFilterDto { + @ApiProperty({ + description: 'Device Type', + enum: DeviceTypeEnum, + required: false, + }) + @IsEnum(DeviceTypeEnum) + @IsOptional() + public deviceType: DeviceTypeEnum; + @ApiProperty({ + description: 'List of Space IDs to filter devices', + required: false, + example: ['60d21b4667d0d8992e610c85', '60d21b4967d0d8992e610c86'], + }) + @IsOptional() + @IsArray() + @IsUUID('4', { each: true }) + public spaces?: string[]; +} diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index d2ac4e7..793d854 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -53,7 +53,7 @@ import { DeviceSceneParamDto } from '../dtos/device.param.dto'; import { GetDeviceLogsDto, GetDevicesBySpaceOrCommunityDto, - GetDoorLockDevices, + GetDevicesFilterDto, } from '../dtos/get.device.dto'; import { controlDeviceInterface, @@ -955,19 +955,20 @@ export class DeviceService { async getAllDevices( param: ProjectParam, - query: GetDoorLockDevices, + { deviceType, spaces }: GetDevicesFilterDto, ): Promise { try { await this.validateProject(param.projectUuid); - if (query.deviceType === DeviceTypeEnum.DOOR_LOCK) { - return await this.getDoorLockDevices(param.projectUuid); - } else if (!query.deviceType) { + if (deviceType === DeviceTypeEnum.DOOR_LOCK) { + return await this.getDoorLockDevices(param.projectUuid, spaces); + } else if (!deviceType) { const devices = await this.deviceRepository.find({ where: { isActive: true, spaceDevice: { - community: { project: { uuid: param.projectUuid } }, + uuid: spaces && spaces.length ? In(spaces) : undefined, spaceName: Not(ORPHAN_SPACE_NAME), + community: { project: { uuid: param.projectUuid } }, }, }, relations: [ @@ -1563,7 +1564,7 @@ export class DeviceService { } } - async getDoorLockDevices(projectUuid: string) { + async getDoorLockDevices(projectUuid: string, spaces?: string[]) { await this.validateProject(projectUuid); const devices = await this.deviceRepository.find({ @@ -1573,6 +1574,7 @@ export class DeviceService { }, spaceDevice: { spaceName: Not(ORPHAN_SPACE_NAME), + uuid: spaces && spaces.length ? In(spaces) : undefined, community: { project: { uuid: projectUuid, From d3d84da5e3414b22991a32cadb495b9ab832e12e Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Mon, 23 Jun 2025 00:39:29 -0600 Subject: [PATCH 15/86] fix: correct property name from bookableConfigs to bookableConfig in BookableSpaceEntity and SpaceEntity --- .../src/modules/booking/entities/bookable-space.entity.ts | 2 +- libs/common/src/modules/space/entities/space.entity.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/common/src/modules/booking/entities/bookable-space.entity.ts b/libs/common/src/modules/booking/entities/bookable-space.entity.ts index bbe5e90..3db1165 100644 --- a/libs/common/src/modules/booking/entities/bookable-space.entity.ts +++ b/libs/common/src/modules/booking/entities/bookable-space.entity.ts @@ -19,7 +19,7 @@ export class BookableSpaceEntity extends AbstractEntity { }) public uuid: string; - @OneToOne(() => SpaceEntity, (space) => space.bookableConfigs) + @OneToOne(() => SpaceEntity, (space) => space.bookableConfig) @JoinColumn({ name: 'space_uuid' }) space: SpaceEntity; diff --git a/libs/common/src/modules/space/entities/space.entity.ts b/libs/common/src/modules/space/entities/space.entity.ts index d3c84e0..763ffec 100644 --- a/libs/common/src/modules/space/entities/space.entity.ts +++ b/libs/common/src/modules/space/entities/space.entity.ts @@ -124,7 +124,7 @@ export class SpaceEntity extends AbstractEntity { occupancyDaily: SpaceDailyOccupancyDurationEntity[]; @OneToOne(() => BookableSpaceEntity, (bookable) => bookable.space) - bookableConfigs: BookableSpaceEntity; + bookableConfig: BookableSpaceEntity; constructor(partial: Partial) { super(); From 3160773c2a0906a71242b6d7a26de4ba1b2b5288 Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Mon, 23 Jun 2025 10:21:55 +0300 Subject: [PATCH 16/86] fix: spaces structure in communities (#420) --- src/community/services/community.service.ts | 13 ++++++++++++- src/space/services/space.service.ts | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/community/services/community.service.ts b/src/community/services/community.service.ts index 5de34fa..3b213f9 100644 --- a/src/community/services/community.service.ts +++ b/src/community/services/community.service.ts @@ -207,7 +207,8 @@ export class CommunityService { if (search) { qb.andWhere( - `c.name ILIKE '%${search}%' ${includeSpaces ? "OR space.space_name ILIKE '%" + search + "%'" : ''}`, + `c.name ILIKE :search ${includeSpaces ? 'OR space.space_name ILIKE :search' : ''}`, + { search: `%${search}%` }, ); } @@ -215,12 +216,22 @@ export class CommunityService { const { baseResponseDto, paginationResponseDto } = await customModel.findAll({ ...pageable, modelName: 'community' }, qb); + + if (includeSpaces) { + baseResponseDto.data = baseResponseDto.data.map((community) => ({ + ...community, + spaces: this.spaceService.buildSpaceHierarchy(community.spaces || []), + })); + } return new PageResponse( baseResponseDto, paginationResponseDto, ); } catch (error) { // Generic error handling + if (error instanceof HttpException) { + throw error; + } throw new HttpException( error.message || 'An error occurred while fetching communities.', HttpStatus.INTERNAL_SERVER_ERROR, diff --git a/src/space/services/space.service.ts b/src/space/services/space.service.ts index fb08b59..cbbe953 100644 --- a/src/space/services/space.service.ts +++ b/src/space/services/space.service.ts @@ -681,7 +681,7 @@ export class SpaceService { } } - private buildSpaceHierarchy(spaces: SpaceEntity[]): SpaceEntity[] { + buildSpaceHierarchy(spaces: SpaceEntity[]): SpaceEntity[] { const map = new Map(); // Step 1: Create a map of spaces by UUID From fddd06e06d2242b22088cf1c359c6299fc179770 Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Mon, 23 Jun 2025 12:44:19 +0300 Subject: [PATCH 17/86] fix: add space condition to the join operator instead of general query (#423) --- src/community/services/community.service.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/community/services/community.service.ts b/src/community/services/community.service.ts index 3b213f9..d4ff99c 100644 --- a/src/community/services/community.service.ts +++ b/src/community/services/community.service.ts @@ -190,25 +190,26 @@ export class CommunityService { .distinct(true); if (includeSpaces) { - qb.leftJoinAndSelect('c.spaces', 'space', 'space.disabled = false') + qb.leftJoinAndSelect( + 'c.spaces', + 'space', + 'space.disabled = :disabled AND space.spaceName != :orphanSpaceName', + { disabled: false, orphanSpaceName: ORPHAN_SPACE_NAME }, + ) .leftJoinAndSelect('space.parent', 'parent') .leftJoinAndSelect( 'space.children', 'children', 'children.disabled = :disabled', { disabled: false }, - ) - // .leftJoinAndSelect('space.spaceModel', 'spaceModel') - .andWhere('space.spaceName != :orphanSpaceName', { - orphanSpaceName: ORPHAN_SPACE_NAME, - }) - .andWhere('space.disabled = :disabled', { disabled: false }); + ); + // .leftJoinAndSelect('space.spaceModel', 'spaceModel') } if (search) { qb.andWhere( `c.name ILIKE :search ${includeSpaces ? 'OR space.space_name ILIKE :search' : ''}`, - { search: `%${search}%` }, + { search }, ); } @@ -216,7 +217,6 @@ export class CommunityService { const { baseResponseDto, paginationResponseDto } = await customModel.findAll({ ...pageable, modelName: 'community' }, qb); - if (includeSpaces) { baseResponseDto.data = baseResponseDto.data.map((community) => ({ ...community, From 60d2c8330b8245260fb8dead2fc1bc596540cab5 Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Mon, 23 Jun 2025 15:23:53 +0300 Subject: [PATCH 18/86] fix: increase DB max pool size (#425) --- libs/common/src/database/database.module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/common/src/database/database.module.ts b/libs/common/src/database/database.module.ts index f3ec232..dd25da9 100644 --- a/libs/common/src/database/database.module.ts +++ b/libs/common/src/database/database.module.ts @@ -125,7 +125,7 @@ import { VisitorPasswordEntity } from '../modules/visitor-password/entities'; logger: typeOrmLogger, extra: { charset: 'utf8mb4', - max: 50, // set pool max size + max: 100, // set pool max size idleTimeoutMillis: 5000, // close idle clients after 5 second connectionTimeoutMillis: 12_000, // return an error after 11 second if connection could not be established maxUses: 7500, // close (and replace) a connection after it has been used 7500 times (see below for discussion) From a6053b39710776326709fb3608f9537116855d59 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Mon, 23 Jun 2025 06:34:53 -0600 Subject: [PATCH 19/86] refactor: implement query runners for database operations in multiple services --- .../services/devices-status.service.ts | 84 +++++++++++++------ .../src/helper/services/aqi.data.service.ts | 15 +++- .../src/helper/services/occupancy.service.ts | 15 +++- .../helper/services/power.clamp.service.ts | 21 +++-- 4 files changed, 97 insertions(+), 38 deletions(-) diff --git a/libs/common/src/firebase/devices-status/services/devices-status.service.ts b/libs/common/src/firebase/devices-status/services/devices-status.service.ts index 4b0b0f7..1d201d3 100644 --- a/libs/common/src/firebase/devices-status/services/devices-status.service.ts +++ b/libs/common/src/firebase/devices-status/services/devices-status.service.ts @@ -24,6 +24,7 @@ import { PowerClampEnergyEnum } from '@app/common/constants/power.clamp.enargy.e import { PresenceSensorEnum } from '@app/common/constants/presence.sensor.enum'; import { OccupancyService } from '@app/common/helper/services/occupancy.service'; import { AqiDataService } from '@app/common/helper/services/aqi.data.service'; +import { DataSource, QueryRunner } from 'typeorm'; @Injectable() export class DeviceStatusFirebaseService { private tuya: TuyaContext; @@ -35,6 +36,7 @@ export class DeviceStatusFirebaseService { private readonly occupancyService: OccupancyService, private readonly aqiDataService: AqiDataService, private deviceStatusLogRepository: DeviceStatusLogRepository, + private readonly dataSource: DataSource, ) { const accessKey = this.configService.get('auth-config.ACCESS_KEY'); const secretKey = this.configService.get('auth-config.SECRET_KEY'); @@ -79,28 +81,46 @@ export class DeviceStatusFirebaseService { async addDeviceStatusToFirebase( addDeviceStatusDto: AddDeviceStatusDto, ): Promise { + const queryRunner = this.dataSource.createQueryRunner(); + await queryRunner.connect(); + await queryRunner.startTransaction(); try { const device = await this.getDeviceByDeviceTuyaUuid( addDeviceStatusDto.deviceTuyaUuid, + queryRunner, ); if (device?.uuid) { - return await this.createDeviceStatusFirebase({ - deviceUuid: device.uuid, - ...addDeviceStatusDto, - productType: device.productDevice.prodType, - }); + const result = await this.createDeviceStatusFirebase( + { + deviceUuid: device.uuid, + ...addDeviceStatusDto, + productType: device.productDevice.prodType, + }, + queryRunner, + ); + await queryRunner.commitTransaction(); + return result; } // Return null if device not found or no UUID + await queryRunner.rollbackTransaction(); return null; } catch (error) { - // Handle the error silently, perhaps log it internally or ignore it + await queryRunner.rollbackTransaction(); return null; + } finally { + await queryRunner.release(); } } + async getDeviceByDeviceTuyaUuid( + deviceTuyaUuid: string, + queryRunner?: QueryRunner, + ) { + const repo = queryRunner + ? queryRunner.manager.getRepository(this.deviceRepository.target) + : this.deviceRepository; - async getDeviceByDeviceTuyaUuid(deviceTuyaUuid: string) { - return await this.deviceRepository.findOne({ + return await repo.findOne({ where: { deviceTuyaUuid, isActive: true, @@ -108,6 +128,7 @@ export class DeviceStatusFirebaseService { relations: ['productDevice'], }); } + async getDevicesInstructionStatus(deviceUuid: string) { try { const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid); @@ -153,9 +174,14 @@ export class DeviceStatusFirebaseService { } async getDeviceByDeviceUuid( deviceUuid: string, - withProductDevice: boolean = true, + withProductDevice = true, + queryRunner?: QueryRunner, ) { - return await this.deviceRepository.findOne({ + const repo = queryRunner + ? queryRunner.manager.getRepository(this.deviceRepository.target) + : this.deviceRepository; + + return await repo.findOne({ where: { uuid: deviceUuid, isActive: true, @@ -163,21 +189,20 @@ export class DeviceStatusFirebaseService { ...(withProductDevice && { relations: ['productDevice'] }), }); } + async createDeviceStatusFirebase( addDeviceStatusDto: AddDeviceStatusDto, + queryRunner?: QueryRunner, ): Promise { const dataRef = ref( this.firebaseDb, `device-status/${addDeviceStatusDto.deviceUuid}`, ); - // Use a transaction to handle concurrent updates + // Step 1: Update Firebase Realtime Database await runTransaction(dataRef, (existingData) => { - if (!existingData) { - existingData = {}; - } + if (!existingData) existingData = {}; - // Assign default values if fields are not present if (!existingData.deviceTuyaUuid) { existingData.deviceTuyaUuid = addDeviceStatusDto.deviceTuyaUuid; } @@ -191,18 +216,15 @@ export class DeviceStatusFirebaseService { existingData.status = []; } - // Create a map to track existing status codes + // Merge incoming status with existing status const statusMap = new Map( existingData.status.map((item) => [item.code, item.value]), ); - // Update or add status codes - for (const statusItem of addDeviceStatusDto.status) { statusMap.set(statusItem.code, statusItem.value); } - // Convert the map back to an array format existingData.status = Array.from(statusMap, ([code, value]) => ({ code, value, @@ -211,9 +233,9 @@ export class DeviceStatusFirebaseService { return existingData; }); - // Save logs to your repository - const newLogs = addDeviceStatusDto.log.properties.map((property) => { - return this.deviceStatusLogRepository.create({ + // Step 2: Save device status log entries + const newLogs = addDeviceStatusDto.log.properties.map((property) => + this.deviceStatusLogRepository.create({ deviceId: addDeviceStatusDto.deviceUuid, deviceTuyaId: addDeviceStatusDto.deviceTuyaUuid, productId: addDeviceStatusDto.log.productId, @@ -222,10 +244,19 @@ export class DeviceStatusFirebaseService { value: property.value, eventId: addDeviceStatusDto.log.dataId, eventTime: new Date(property.time).toISOString(), - }); - }); - await this.deviceStatusLogRepository.save(newLogs); + }), + ); + if (queryRunner) { + const repo = queryRunner.manager.getRepository( + this.deviceStatusLogRepository.target, + ); + await repo.save(newLogs); + } else { + await this.deviceStatusLogRepository.save(newLogs); + } + + // Step 3: Trigger additional data services if (addDeviceStatusDto.productType === ProductType.PC) { const energyCodes = new Set([ PowerClampEnergyEnum.ENERGY_CONSUMED, @@ -269,7 +300,8 @@ export class DeviceStatusFirebaseService { addDeviceStatusDto.deviceUuid, ); } - // Return the updated data + + // Step 4: Return updated Firebase status const snapshot: DataSnapshot = await get(dataRef); return snapshot.val(); } diff --git a/libs/common/src/helper/services/aqi.data.service.ts b/libs/common/src/helper/services/aqi.data.service.ts index 3e19b6c..c8f5c1a 100644 --- a/libs/common/src/helper/services/aqi.data.service.ts +++ b/libs/common/src/helper/services/aqi.data.service.ts @@ -36,9 +36,18 @@ export class AqiDataService { procedureFileName: string, params: (string | number | null)[], ): Promise { - const query = this.loadQuery(procedureFolderName, procedureFileName); - await this.dataSource.query(query, params); - console.log(`Procedure ${procedureFileName} executed successfully.`); + const queryRunner = this.dataSource.createQueryRunner(); + await queryRunner.connect(); + try { + const query = this.loadQuery(procedureFolderName, procedureFileName); + await queryRunner.query(query, params); + console.log(`Procedure ${procedureFileName} executed successfully.`); + } catch (err) { + console.error(`Failed to execute procedure ${procedureFileName}:`, err); + throw err; + } finally { + await queryRunner.release(); + } } private loadQuery(folderName: string, fileName: string): string { diff --git a/libs/common/src/helper/services/occupancy.service.ts b/libs/common/src/helper/services/occupancy.service.ts index ea99b7c..b3d50cf 100644 --- a/libs/common/src/helper/services/occupancy.service.ts +++ b/libs/common/src/helper/services/occupancy.service.ts @@ -57,9 +57,18 @@ export class OccupancyService { procedureFileName: string, params: (string | number | null)[], ): Promise { - const query = this.loadQuery(procedureFolderName, procedureFileName); - await this.dataSource.query(query, params); - console.log(`Procedure ${procedureFileName} executed successfully.`); + const queryRunner = this.dataSource.createQueryRunner(); + await queryRunner.connect(); + try { + const query = this.loadQuery(procedureFolderName, procedureFileName); + await queryRunner.query(query, params); + console.log(`Procedure ${procedureFileName} executed successfully.`); + } catch (err) { + console.error(`Failed to execute procedure ${procedureFileName}:`, err); + throw err; + } finally { + await queryRunner.release(); + } } private loadQuery(folderName: string, fileName: string): string { diff --git a/libs/common/src/helper/services/power.clamp.service.ts b/libs/common/src/helper/services/power.clamp.service.ts index 7c83208..6cb667b 100644 --- a/libs/common/src/helper/services/power.clamp.service.ts +++ b/libs/common/src/helper/services/power.clamp.service.ts @@ -46,12 +46,21 @@ export class PowerClampService { procedureFileName: string, params: (string | number | null)[], ): Promise { - const query = this.loadQuery( - 'fact_device_energy_consumed', - procedureFileName, - ); - await this.dataSource.query(query, params); - console.log(`Procedure ${procedureFileName} executed successfully.`); + const queryRunner = this.dataSource.createQueryRunner(); + await queryRunner.connect(); + try { + const query = this.loadQuery( + 'fact_device_energy_consumed', + procedureFileName, + ); + await queryRunner.query(query, params); + console.log(`Procedure ${procedureFileName} executed successfully.`); + } catch (err) { + console.error(`Failed to execute procedure ${procedureFileName}:`, err); + throw err; + } finally { + await queryRunner.release(); + } } private loadQuery(folderName: string, fileName: string): string { From 75d03366c27ed7c6947035c74bfb6a45382626fb Mon Sep 17 00:00:00 2001 From: faljawhary Date: Mon, 23 Jun 2025 06:58:57 -0600 Subject: [PATCH 20/86] Revert "SP-1778-be-fix-time-out-connections-in-the-db" --- .../services/devices-status.service.ts | 84 ++++++------------- .../src/helper/services/aqi.data.service.ts | 15 +--- .../src/helper/services/occupancy.service.ts | 15 +--- .../helper/services/power.clamp.service.ts | 21 ++--- 4 files changed, 38 insertions(+), 97 deletions(-) diff --git a/libs/common/src/firebase/devices-status/services/devices-status.service.ts b/libs/common/src/firebase/devices-status/services/devices-status.service.ts index 1d201d3..4b0b0f7 100644 --- a/libs/common/src/firebase/devices-status/services/devices-status.service.ts +++ b/libs/common/src/firebase/devices-status/services/devices-status.service.ts @@ -24,7 +24,6 @@ import { PowerClampEnergyEnum } from '@app/common/constants/power.clamp.enargy.e import { PresenceSensorEnum } from '@app/common/constants/presence.sensor.enum'; import { OccupancyService } from '@app/common/helper/services/occupancy.service'; import { AqiDataService } from '@app/common/helper/services/aqi.data.service'; -import { DataSource, QueryRunner } from 'typeorm'; @Injectable() export class DeviceStatusFirebaseService { private tuya: TuyaContext; @@ -36,7 +35,6 @@ export class DeviceStatusFirebaseService { private readonly occupancyService: OccupancyService, private readonly aqiDataService: AqiDataService, private deviceStatusLogRepository: DeviceStatusLogRepository, - private readonly dataSource: DataSource, ) { const accessKey = this.configService.get('auth-config.ACCESS_KEY'); const secretKey = this.configService.get('auth-config.SECRET_KEY'); @@ -81,46 +79,28 @@ export class DeviceStatusFirebaseService { async addDeviceStatusToFirebase( addDeviceStatusDto: AddDeviceStatusDto, ): Promise { - const queryRunner = this.dataSource.createQueryRunner(); - await queryRunner.connect(); - await queryRunner.startTransaction(); try { const device = await this.getDeviceByDeviceTuyaUuid( addDeviceStatusDto.deviceTuyaUuid, - queryRunner, ); if (device?.uuid) { - const result = await this.createDeviceStatusFirebase( - { - deviceUuid: device.uuid, - ...addDeviceStatusDto, - productType: device.productDevice.prodType, - }, - queryRunner, - ); - await queryRunner.commitTransaction(); - return result; + return await this.createDeviceStatusFirebase({ + deviceUuid: device.uuid, + ...addDeviceStatusDto, + productType: device.productDevice.prodType, + }); } // Return null if device not found or no UUID - await queryRunner.rollbackTransaction(); return null; } catch (error) { - await queryRunner.rollbackTransaction(); + // Handle the error silently, perhaps log it internally or ignore it return null; - } finally { - await queryRunner.release(); } } - async getDeviceByDeviceTuyaUuid( - deviceTuyaUuid: string, - queryRunner?: QueryRunner, - ) { - const repo = queryRunner - ? queryRunner.manager.getRepository(this.deviceRepository.target) - : this.deviceRepository; - return await repo.findOne({ + async getDeviceByDeviceTuyaUuid(deviceTuyaUuid: string) { + return await this.deviceRepository.findOne({ where: { deviceTuyaUuid, isActive: true, @@ -128,7 +108,6 @@ export class DeviceStatusFirebaseService { relations: ['productDevice'], }); } - async getDevicesInstructionStatus(deviceUuid: string) { try { const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid); @@ -174,14 +153,9 @@ export class DeviceStatusFirebaseService { } async getDeviceByDeviceUuid( deviceUuid: string, - withProductDevice = true, - queryRunner?: QueryRunner, + withProductDevice: boolean = true, ) { - const repo = queryRunner - ? queryRunner.manager.getRepository(this.deviceRepository.target) - : this.deviceRepository; - - return await repo.findOne({ + return await this.deviceRepository.findOne({ where: { uuid: deviceUuid, isActive: true, @@ -189,20 +163,21 @@ export class DeviceStatusFirebaseService { ...(withProductDevice && { relations: ['productDevice'] }), }); } - async createDeviceStatusFirebase( addDeviceStatusDto: AddDeviceStatusDto, - queryRunner?: QueryRunner, ): Promise { const dataRef = ref( this.firebaseDb, `device-status/${addDeviceStatusDto.deviceUuid}`, ); - // Step 1: Update Firebase Realtime Database + // Use a transaction to handle concurrent updates await runTransaction(dataRef, (existingData) => { - if (!existingData) existingData = {}; + if (!existingData) { + existingData = {}; + } + // Assign default values if fields are not present if (!existingData.deviceTuyaUuid) { existingData.deviceTuyaUuid = addDeviceStatusDto.deviceTuyaUuid; } @@ -216,15 +191,18 @@ export class DeviceStatusFirebaseService { existingData.status = []; } - // Merge incoming status with existing status + // Create a map to track existing status codes const statusMap = new Map( existingData.status.map((item) => [item.code, item.value]), ); + // Update or add status codes + for (const statusItem of addDeviceStatusDto.status) { statusMap.set(statusItem.code, statusItem.value); } + // Convert the map back to an array format existingData.status = Array.from(statusMap, ([code, value]) => ({ code, value, @@ -233,9 +211,9 @@ export class DeviceStatusFirebaseService { return existingData; }); - // Step 2: Save device status log entries - const newLogs = addDeviceStatusDto.log.properties.map((property) => - this.deviceStatusLogRepository.create({ + // Save logs to your repository + const newLogs = addDeviceStatusDto.log.properties.map((property) => { + return this.deviceStatusLogRepository.create({ deviceId: addDeviceStatusDto.deviceUuid, deviceTuyaId: addDeviceStatusDto.deviceTuyaUuid, productId: addDeviceStatusDto.log.productId, @@ -244,19 +222,10 @@ export class DeviceStatusFirebaseService { value: property.value, eventId: addDeviceStatusDto.log.dataId, eventTime: new Date(property.time).toISOString(), - }), - ); + }); + }); + await this.deviceStatusLogRepository.save(newLogs); - if (queryRunner) { - const repo = queryRunner.manager.getRepository( - this.deviceStatusLogRepository.target, - ); - await repo.save(newLogs); - } else { - await this.deviceStatusLogRepository.save(newLogs); - } - - // Step 3: Trigger additional data services if (addDeviceStatusDto.productType === ProductType.PC) { const energyCodes = new Set([ PowerClampEnergyEnum.ENERGY_CONSUMED, @@ -300,8 +269,7 @@ export class DeviceStatusFirebaseService { addDeviceStatusDto.deviceUuid, ); } - - // Step 4: Return updated Firebase status + // Return the updated data const snapshot: DataSnapshot = await get(dataRef); return snapshot.val(); } diff --git a/libs/common/src/helper/services/aqi.data.service.ts b/libs/common/src/helper/services/aqi.data.service.ts index c8f5c1a..3e19b6c 100644 --- a/libs/common/src/helper/services/aqi.data.service.ts +++ b/libs/common/src/helper/services/aqi.data.service.ts @@ -36,18 +36,9 @@ export class AqiDataService { procedureFileName: string, params: (string | number | null)[], ): Promise { - const queryRunner = this.dataSource.createQueryRunner(); - await queryRunner.connect(); - try { - const query = this.loadQuery(procedureFolderName, procedureFileName); - await queryRunner.query(query, params); - console.log(`Procedure ${procedureFileName} executed successfully.`); - } catch (err) { - console.error(`Failed to execute procedure ${procedureFileName}:`, err); - throw err; - } finally { - await queryRunner.release(); - } + const query = this.loadQuery(procedureFolderName, procedureFileName); + await this.dataSource.query(query, params); + console.log(`Procedure ${procedureFileName} executed successfully.`); } private loadQuery(folderName: string, fileName: string): string { diff --git a/libs/common/src/helper/services/occupancy.service.ts b/libs/common/src/helper/services/occupancy.service.ts index b3d50cf..ea99b7c 100644 --- a/libs/common/src/helper/services/occupancy.service.ts +++ b/libs/common/src/helper/services/occupancy.service.ts @@ -57,18 +57,9 @@ export class OccupancyService { procedureFileName: string, params: (string | number | null)[], ): Promise { - const queryRunner = this.dataSource.createQueryRunner(); - await queryRunner.connect(); - try { - const query = this.loadQuery(procedureFolderName, procedureFileName); - await queryRunner.query(query, params); - console.log(`Procedure ${procedureFileName} executed successfully.`); - } catch (err) { - console.error(`Failed to execute procedure ${procedureFileName}:`, err); - throw err; - } finally { - await queryRunner.release(); - } + const query = this.loadQuery(procedureFolderName, procedureFileName); + await this.dataSource.query(query, params); + console.log(`Procedure ${procedureFileName} executed successfully.`); } private loadQuery(folderName: string, fileName: string): string { diff --git a/libs/common/src/helper/services/power.clamp.service.ts b/libs/common/src/helper/services/power.clamp.service.ts index 6cb667b..7c83208 100644 --- a/libs/common/src/helper/services/power.clamp.service.ts +++ b/libs/common/src/helper/services/power.clamp.service.ts @@ -46,21 +46,12 @@ export class PowerClampService { procedureFileName: string, params: (string | number | null)[], ): Promise { - const queryRunner = this.dataSource.createQueryRunner(); - await queryRunner.connect(); - try { - const query = this.loadQuery( - 'fact_device_energy_consumed', - procedureFileName, - ); - await queryRunner.query(query, params); - console.log(`Procedure ${procedureFileName} executed successfully.`); - } catch (err) { - console.error(`Failed to execute procedure ${procedureFileName}:`, err); - throw err; - } finally { - await queryRunner.release(); - } + const query = this.loadQuery( + 'fact_device_energy_consumed', + procedureFileName, + ); + await this.dataSource.query(query, params); + console.log(`Procedure ${procedureFileName} executed successfully.`); } private loadQuery(folderName: string, fileName: string): string { From c8d691b3800c836a69d30fc051ff0a0f90ad2509 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Mon, 23 Jun 2025 07:02:23 -0600 Subject: [PATCH 21/86] tern off data procedure --- .../services/devices-status.service.ts | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/libs/common/src/firebase/devices-status/services/devices-status.service.ts b/libs/common/src/firebase/devices-status/services/devices-status.service.ts index 4b0b0f7..2138466 100644 --- a/libs/common/src/firebase/devices-status/services/devices-status.service.ts +++ b/libs/common/src/firebase/devices-status/services/devices-status.service.ts @@ -245,30 +245,30 @@ export class DeviceStatusFirebaseService { } } - if ( - addDeviceStatusDto.productType === ProductType.CPS || - addDeviceStatusDto.productType === ProductType.WPS - ) { - const occupancyCodes = new Set([PresenceSensorEnum.PRESENCE_STATE]); + // if ( + // addDeviceStatusDto.productType === ProductType.CPS || + // addDeviceStatusDto.productType === ProductType.WPS + // ) { + // const occupancyCodes = new Set([PresenceSensorEnum.PRESENCE_STATE]); - const occupancyStatus = addDeviceStatusDto?.log?.properties?.find( - (status) => occupancyCodes.has(status.code), - ); + // const occupancyStatus = addDeviceStatusDto?.log?.properties?.find( + // (status) => occupancyCodes.has(status.code), + // ); - if (occupancyStatus) { - await this.occupancyService.updateOccupancySensorHistoricalData( - addDeviceStatusDto.deviceUuid, - ); - await this.occupancyService.updateOccupancySensorHistoricalDurationData( - addDeviceStatusDto.deviceUuid, - ); - } - } - if (addDeviceStatusDto.productType === ProductType.AQI) { - await this.aqiDataService.updateAQISensorHistoricalData( - addDeviceStatusDto.deviceUuid, - ); - } + // if (occupancyStatus) { + // await this.occupancyService.updateOccupancySensorHistoricalData( + // addDeviceStatusDto.deviceUuid, + // ); + // await this.occupancyService.updateOccupancySensorHistoricalDurationData( + // addDeviceStatusDto.deviceUuid, + // ); + // } + // } + // if (addDeviceStatusDto.productType === ProductType.AQI) { + // await this.aqiDataService.updateAQISensorHistoricalData( + // addDeviceStatusDto.deviceUuid, + // ); + // } // Return the updated data const snapshot: DataSnapshot = await get(dataRef); return snapshot.val(); From 04f64407e1a77f29b3643615f74cec40063575d2 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Mon, 23 Jun 2025 07:10:47 -0600 Subject: [PATCH 22/86] turn off some update data points --- .../services/devices-status.service.ts | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/libs/common/src/firebase/devices-status/services/devices-status.service.ts b/libs/common/src/firebase/devices-status/services/devices-status.service.ts index 2138466..477df26 100644 --- a/libs/common/src/firebase/devices-status/services/devices-status.service.ts +++ b/libs/common/src/firebase/devices-status/services/devices-status.service.ts @@ -226,24 +226,24 @@ export class DeviceStatusFirebaseService { }); await this.deviceStatusLogRepository.save(newLogs); - if (addDeviceStatusDto.productType === ProductType.PC) { - const energyCodes = new Set([ - PowerClampEnergyEnum.ENERGY_CONSUMED, - PowerClampEnergyEnum.ENERGY_CONSUMED_A, - PowerClampEnergyEnum.ENERGY_CONSUMED_B, - PowerClampEnergyEnum.ENERGY_CONSUMED_C, - ]); + // if (addDeviceStatusDto.productType === ProductType.PC) { + // const energyCodes = new Set([ + // PowerClampEnergyEnum.ENERGY_CONSUMED, + // PowerClampEnergyEnum.ENERGY_CONSUMED_A, + // PowerClampEnergyEnum.ENERGY_CONSUMED_B, + // PowerClampEnergyEnum.ENERGY_CONSUMED_C, + // ]); - const energyStatus = addDeviceStatusDto?.log?.properties?.find((status) => - energyCodes.has(status.code), - ); + // const energyStatus = addDeviceStatusDto?.log?.properties?.find((status) => + // energyCodes.has(status.code), + // ); - if (energyStatus) { - await this.powerClampService.updateEnergyConsumedHistoricalData( - addDeviceStatusDto.deviceUuid, - ); - } - } + // if (energyStatus) { + // await this.powerClampService.updateEnergyConsumedHistoricalData( + // addDeviceStatusDto.deviceUuid, + // ); + // } + // } // if ( // addDeviceStatusDto.productType === ProductType.CPS || From ff370b2baa648607d10f7bd5da7394ada5920ff2 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Mon, 23 Jun 2025 07:31:58 -0600 Subject: [PATCH 23/86] Implement message queue for TuyaWebSocketService and batch processing --- .../services/tuya.web.socket.service.ts | 57 ++++++++++++++----- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/libs/common/src/helper/services/tuya.web.socket.service.ts b/libs/common/src/helper/services/tuya.web.socket.service.ts index 5a810ab..dcf07b9 100644 --- a/libs/common/src/helper/services/tuya.web.socket.service.ts +++ b/libs/common/src/helper/services/tuya.web.socket.service.ts @@ -9,6 +9,14 @@ export class TuyaWebSocketService { private client: any; private readonly isDevEnv: boolean; + private messageQueue: { + devId: string; + status: any; + logData: any; + }[] = []; + + private isProcessing = false; + constructor( private readonly configService: ConfigService, private readonly deviceStatusFirebaseService: DeviceStatusFirebaseService, @@ -26,12 +34,12 @@ export class TuyaWebSocketService { }); if (this.configService.get('tuya-config.TRUN_ON_TUYA_SOCKET')) { - // Set up event handlers this.setupEventHandlers(); - - // Start receiving messages this.client.start(); } + + // Trigger the queue processor every 2 seconds + setInterval(() => this.processQueue(), 10000); } private setupEventHandlers() { @@ -44,19 +52,13 @@ export class TuyaWebSocketService { try { const { devId, status, logData } = this.extractMessageData(message); - if (this.sosHandlerService.isSosTriggered(status)) { - await this.sosHandlerService.handleSosEvent(devId, logData); - } else { - await this.deviceStatusFirebaseService.addDeviceStatusToFirebase({ - deviceTuyaUuid: devId, - status: status, - log: logData, - }); - } + // Push to internal queue + this.messageQueue.push({ devId, status, logData }); + // Acknowledge the message this.client.ackMessage(message.messageId); } catch (error) { - console.error('Error processing message:', error); + console.error('Error receiving message:', error); } }); @@ -80,6 +82,35 @@ export class TuyaWebSocketService { console.error('WebSocket error:', error); }); } + private async processQueue() { + if (this.isProcessing || this.messageQueue.length === 0) return; + + this.isProcessing = true; + + const batch = [...this.messageQueue]; + this.messageQueue = []; + + try { + for (const item of batch) { + if (this.sosHandlerService.isSosTriggered(item.status)) { + await this.sosHandlerService.handleSosEvent(item.devId, item.logData); + } else { + await this.deviceStatusFirebaseService.addDeviceStatusToFirebase({ + deviceTuyaUuid: item.devId, + status: item.status, + log: item.logData, + }); + } + } + } catch (error) { + console.error('Error processing batch:', error); + // Re-add the batch to the queue for retry + this.messageQueue.unshift(...batch); + } finally { + this.isProcessing = false; + } + } + private extractMessageData(message: any): { devId: string; status: any; From cf19f08dcadcf51d419ab19492a453d2dca072c6 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Mon, 23 Jun 2025 07:33:01 -0600 Subject: [PATCH 24/86] turn on all the updates data points --- .../services/devices-status.service.ts | 76 +++++++++---------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/libs/common/src/firebase/devices-status/services/devices-status.service.ts b/libs/common/src/firebase/devices-status/services/devices-status.service.ts index 477df26..4b0b0f7 100644 --- a/libs/common/src/firebase/devices-status/services/devices-status.service.ts +++ b/libs/common/src/firebase/devices-status/services/devices-status.service.ts @@ -226,49 +226,49 @@ export class DeviceStatusFirebaseService { }); await this.deviceStatusLogRepository.save(newLogs); - // if (addDeviceStatusDto.productType === ProductType.PC) { - // const energyCodes = new Set([ - // PowerClampEnergyEnum.ENERGY_CONSUMED, - // PowerClampEnergyEnum.ENERGY_CONSUMED_A, - // PowerClampEnergyEnum.ENERGY_CONSUMED_B, - // PowerClampEnergyEnum.ENERGY_CONSUMED_C, - // ]); + if (addDeviceStatusDto.productType === ProductType.PC) { + const energyCodes = new Set([ + PowerClampEnergyEnum.ENERGY_CONSUMED, + PowerClampEnergyEnum.ENERGY_CONSUMED_A, + PowerClampEnergyEnum.ENERGY_CONSUMED_B, + PowerClampEnergyEnum.ENERGY_CONSUMED_C, + ]); - // const energyStatus = addDeviceStatusDto?.log?.properties?.find((status) => - // energyCodes.has(status.code), - // ); + const energyStatus = addDeviceStatusDto?.log?.properties?.find((status) => + energyCodes.has(status.code), + ); - // if (energyStatus) { - // await this.powerClampService.updateEnergyConsumedHistoricalData( - // addDeviceStatusDto.deviceUuid, - // ); - // } - // } + if (energyStatus) { + await this.powerClampService.updateEnergyConsumedHistoricalData( + addDeviceStatusDto.deviceUuid, + ); + } + } - // if ( - // addDeviceStatusDto.productType === ProductType.CPS || - // addDeviceStatusDto.productType === ProductType.WPS - // ) { - // const occupancyCodes = new Set([PresenceSensorEnum.PRESENCE_STATE]); + if ( + addDeviceStatusDto.productType === ProductType.CPS || + addDeviceStatusDto.productType === ProductType.WPS + ) { + const occupancyCodes = new Set([PresenceSensorEnum.PRESENCE_STATE]); - // const occupancyStatus = addDeviceStatusDto?.log?.properties?.find( - // (status) => occupancyCodes.has(status.code), - // ); + const occupancyStatus = addDeviceStatusDto?.log?.properties?.find( + (status) => occupancyCodes.has(status.code), + ); - // if (occupancyStatus) { - // await this.occupancyService.updateOccupancySensorHistoricalData( - // addDeviceStatusDto.deviceUuid, - // ); - // await this.occupancyService.updateOccupancySensorHistoricalDurationData( - // addDeviceStatusDto.deviceUuid, - // ); - // } - // } - // if (addDeviceStatusDto.productType === ProductType.AQI) { - // await this.aqiDataService.updateAQISensorHistoricalData( - // addDeviceStatusDto.deviceUuid, - // ); - // } + if (occupancyStatus) { + await this.occupancyService.updateOccupancySensorHistoricalData( + addDeviceStatusDto.deviceUuid, + ); + await this.occupancyService.updateOccupancySensorHistoricalDurationData( + addDeviceStatusDto.deviceUuid, + ); + } + } + if (addDeviceStatusDto.productType === ProductType.AQI) { + await this.aqiDataService.updateAQISensorHistoricalData( + addDeviceStatusDto.deviceUuid, + ); + } // Return the updated data const snapshot: DataSnapshot = await get(dataRef); return snapshot.val(); From d1d4d529a82dac6810fa847c7abf72a03c6c9dbd Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Mon, 23 Jun 2025 08:10:33 -0600 Subject: [PATCH 25/86] Add methods to handle SOS events and device status updates in Firebase and our DB --- .../services/devices-status.service.ts | 32 +++++++++++++++++-- .../helper/services/sos.handler.service.ts | 26 ++++++++++++++- .../services/tuya.web.socket.service.ts | 17 ++++++++-- 3 files changed, 69 insertions(+), 6 deletions(-) diff --git a/libs/common/src/firebase/devices-status/services/devices-status.service.ts b/libs/common/src/firebase/devices-status/services/devices-status.service.ts index 4b0b0f7..695022b 100644 --- a/libs/common/src/firebase/devices-status/services/devices-status.service.ts +++ b/libs/common/src/firebase/devices-status/services/devices-status.service.ts @@ -76,6 +76,28 @@ export class DeviceStatusFirebaseService { ); } } + async addDeviceStatusToOurDb( + addDeviceStatusDto: AddDeviceStatusDto, + ): Promise { + try { + const device = await this.getDeviceByDeviceTuyaUuid( + addDeviceStatusDto.deviceTuyaUuid, + ); + + if (device?.uuid) { + return await this.createDeviceStatusInOurDb({ + deviceUuid: device.uuid, + ...addDeviceStatusDto, + productType: device.productDevice.prodType, + }); + } + // Return null if device not found or no UUID + return null; + } catch (error) { + // Handle the error silently, perhaps log it internally or ignore it + return null; + } + } async addDeviceStatusToFirebase( addDeviceStatusDto: AddDeviceStatusDto, ): Promise { @@ -211,6 +233,13 @@ export class DeviceStatusFirebaseService { return existingData; }); + // Return the updated data + const snapshot: DataSnapshot = await get(dataRef); + return snapshot.val(); + } + async createDeviceStatusInOurDb( + addDeviceStatusDto: AddDeviceStatusDto, + ): Promise { // Save logs to your repository const newLogs = addDeviceStatusDto.log.properties.map((property) => { return this.deviceStatusLogRepository.create({ @@ -269,8 +298,5 @@ export class DeviceStatusFirebaseService { addDeviceStatusDto.deviceUuid, ); } - // Return the updated data - const snapshot: DataSnapshot = await get(dataRef); - return snapshot.val(); } } diff --git a/libs/common/src/helper/services/sos.handler.service.ts b/libs/common/src/helper/services/sos.handler.service.ts index 4e957dc..e5f9df9 100644 --- a/libs/common/src/helper/services/sos.handler.service.ts +++ b/libs/common/src/helper/services/sos.handler.service.ts @@ -16,7 +16,7 @@ export class SosHandlerService { ); } - async handleSosEvent(devId: string, logData: any): Promise { + async handleSosEventFirebase(devId: string, logData: any): Promise { try { await this.deviceStatusFirebaseService.addDeviceStatusToFirebase({ deviceTuyaUuid: devId, @@ -39,4 +39,28 @@ export class SosHandlerService { this.logger.error('Failed to send SOS true value', err); } } + + async handleSosEventOurDb(devId: string, logData: any): Promise { + try { + await this.deviceStatusFirebaseService.addDeviceStatusToOurDb({ + deviceTuyaUuid: devId, + status: [{ code: 'sos', value: true }], + log: logData, + }); + + setTimeout(async () => { + try { + await this.deviceStatusFirebaseService.addDeviceStatusToOurDb({ + deviceTuyaUuid: devId, + status: [{ code: 'sos', value: false }], + log: logData, + }); + } catch (err) { + this.logger.error('Failed to send SOS false value', err); + } + }, 2000); + } catch (err) { + this.logger.error('Failed to send SOS true value', err); + } + } } diff --git a/libs/common/src/helper/services/tuya.web.socket.service.ts b/libs/common/src/helper/services/tuya.web.socket.service.ts index dcf07b9..0c32c04 100644 --- a/libs/common/src/helper/services/tuya.web.socket.service.ts +++ b/libs/common/src/helper/services/tuya.web.socket.service.ts @@ -51,6 +51,16 @@ export class TuyaWebSocketService { this.client.message(async (ws: WebSocket, message: any) => { try { const { devId, status, logData } = this.extractMessageData(message); + if (this.sosHandlerService.isSosTriggered(status)) { + await this.sosHandlerService.handleSosEventFirebase(devId, logData); + } else { + // Firebase real-time update + await this.deviceStatusFirebaseService.addDeviceStatusToFirebase({ + deviceTuyaUuid: devId, + status: status, + log: logData, + }); + } // Push to internal queue this.messageQueue.push({ devId, status, logData }); @@ -93,9 +103,12 @@ export class TuyaWebSocketService { try { for (const item of batch) { if (this.sosHandlerService.isSosTriggered(item.status)) { - await this.sosHandlerService.handleSosEvent(item.devId, item.logData); + await this.sosHandlerService.handleSosEventOurDb( + item.devId, + item.logData, + ); } else { - await this.deviceStatusFirebaseService.addDeviceStatusToFirebase({ + await this.deviceStatusFirebaseService.addDeviceStatusToOurDb({ deviceTuyaUuid: item.devId, status: item.status, log: item.logData, From f337e6c68143543b764cfefbb0d3b28d2cfe0250 Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Tue, 24 Jun 2025 10:55:38 +0300 Subject: [PATCH 26/86] Test/prevent server block on rate limit (#421) --- src/app.module.ts | 18 ++++++++++++------ src/main.ts | 15 ++++++--------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/app.module.ts b/src/app.module.ts index ce64932..2401b0c 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,7 +1,7 @@ import { SeederModule } from '@app/common/seed/seeder.module'; import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; -import { APP_INTERCEPTOR } from '@nestjs/core'; +import { APP_GUARD, APP_INTERCEPTOR } from '@nestjs/core'; import { WinstonModule } from 'nest-winston'; import { AuthenticationModule } from './auth/auth.module'; import { AutomationModule } from './automation/automation.module'; @@ -35,6 +35,8 @@ import { UserNotificationModule } from './user-notification/user-notification.mo import { UserModule } from './users/user.module'; import { VisitorPasswordModule } from './vistor-password/visitor-password.module'; +import { ThrottlerGuard } from '@nestjs/throttler'; +import { ThrottlerModule } from '@nestjs/throttler/dist/throttler.module'; import { winstonLoggerOptions } from '../libs/common/src/logger/services/winston.logger'; import { AqiModule } from './aqi/aqi.module'; import { OccupancyModule } from './occupancy/occupancy.module'; @@ -44,9 +46,13 @@ import { WeatherModule } from './weather/weather.module'; ConfigModule.forRoot({ load: config, }), - /* ThrottlerModule.forRoot({ - throttlers: [{ ttl: 100000, limit: 30 }], - }), */ + ThrottlerModule.forRoot({ + throttlers: [{ ttl: 60000, limit: 30 }], + generateKey: (context) => { + const req = context.switchToHttp().getRequest(); + return req.headers['x-forwarded-for'] || req.ip; + }, + }), WinstonModule.forRoot(winstonLoggerOptions), ClientModule, AuthenticationModule, @@ -88,10 +94,10 @@ import { WeatherModule } from './weather/weather.module'; provide: APP_INTERCEPTOR, useClass: LoggingInterceptor, }, - /* { + { provide: APP_GUARD, useClass: ThrottlerGuard, - }, */ + }, ], }) export class AppModule {} diff --git a/src/main.ts b/src/main.ts index e00dca6..67edc11 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,7 +3,6 @@ import { SeederService } from '@app/common/seed/services/seeder.service'; import { Logger, ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import { json, urlencoded } from 'body-parser'; -import rateLimit from 'express-rate-limit'; import helmet from 'helmet'; import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston'; import { setupSwaggerAuthentication } from '../libs/common/src/util/user-auth.swagger.utils'; @@ -22,15 +21,13 @@ async function bootstrap() { app.use(new RequestContextMiddleware().use); - app.use( - rateLimit({ - windowMs: 5 * 60 * 1000, - max: 500, - }), - ); - app.use((req, res, next) => { - console.log('Real IP:', req.ip); + console.log( + 'Real IP:', + req.ip, + req.headers['x-forwarded-for'], + req.connection.remoteAddress, + ); next(); }); From 0a1ccad12010a7adfbbe83afcc26840e597f90f1 Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Tue, 24 Jun 2025 12:18:15 +0300 Subject: [PATCH 27/86] add check if not space not found (#430) --- src/space/services/space.service.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/space/services/space.service.ts b/src/space/services/space.service.ts index cbbe953..a4e3af7 100644 --- a/src/space/services/space.service.ts +++ b/src/space/services/space.service.ts @@ -333,7 +333,12 @@ export class SpaceService { .andWhere('space.disabled = :disabled', { disabled: false }); const space = await queryBuilder.getOne(); - + if (!space) { + throw new HttpException( + `Space with ID ${spaceUuid} not found`, + HttpStatus.NOT_FOUND, + ); + } return new SuccessResponseDto({ message: `Space with ID ${spaceUuid} successfully fetched`, data: space, @@ -343,7 +348,7 @@ export class SpaceService { throw error; // If it's an HttpException, rethrow it } else { throw new HttpException( - 'An error occurred while deleting the community', + 'An error occurred while fetching the space', HttpStatus.INTERNAL_SERVER_ERROR, ); } From 932a3efd1ce933bdb26701ff8ca6ec914c9361ec Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Tue, 24 Jun 2025 12:18:46 +0300 Subject: [PATCH 28/86] Sp 1780 be configure the curtain module device (#424) * task: add Cur new device configuration --- .../common/src/constants/product-type.enum.ts | 1 + src/device/commands/cur2-commands.ts | 28 ++ src/schedule/constants/device-function-map.ts | 32 ++ src/schedule/services/schedule.service.ts | 356 ++++++++---------- 4 files changed, 224 insertions(+), 193 deletions(-) create mode 100644 src/device/commands/cur2-commands.ts create mode 100644 src/schedule/constants/device-function-map.ts diff --git a/libs/common/src/constants/product-type.enum.ts b/libs/common/src/constants/product-type.enum.ts index 1bb1394..182f43c 100644 --- a/libs/common/src/constants/product-type.enum.ts +++ b/libs/common/src/constants/product-type.enum.ts @@ -15,6 +15,7 @@ export enum ProductType { WL = 'WL', GD = 'GD', CUR = 'CUR', + CUR_2 = 'CUR_2', PC = 'PC', FOUR_S = '4S', SIX_S = '6S', diff --git a/src/device/commands/cur2-commands.ts b/src/device/commands/cur2-commands.ts new file mode 100644 index 0000000..22301f8 --- /dev/null +++ b/src/device/commands/cur2-commands.ts @@ -0,0 +1,28 @@ +interface BaseCommand { + code: string; + value: any; +} +export interface ControlCur2Command extends BaseCommand { + code: 'control'; + value: 'open' | 'close' | 'stop'; +} +export interface ControlCur2PercentCommand extends BaseCommand { + code: 'percent_control'; + value: 10 | 20 | 30 | 40 | 50 | 60 | 70 | 80 | 90 | 100; +} +export interface ControlCur2AccurateCalibrationCommand extends BaseCommand { + code: 'accurate_calibration'; + value: 'start' | 'end'; // Assuming this is a numeric value for calibration +} +export interface ControlCur2TDirectionConCommand extends BaseCommand { + code: 'control_t_direction_con'; + value: 'forward' | 'back'; +} +export interface ControlCur2QuickCalibrationCommand extends BaseCommand { + code: 'tr_timecon'; + value: number; // between 10 and 120 +} +export interface ControlCur2MotorModeCommand extends BaseCommand { + code: 'elec_machinery_mode'; + value: 'strong_power' | 'dry_contact'; +} diff --git a/src/schedule/constants/device-function-map.ts b/src/schedule/constants/device-function-map.ts new file mode 100644 index 0000000..7258f45 --- /dev/null +++ b/src/schedule/constants/device-function-map.ts @@ -0,0 +1,32 @@ +import { + ControlCur2AccurateCalibrationCommand, + ControlCur2Command, + ControlCur2PercentCommand, + ControlCur2QuickCalibrationCommand, + ControlCur2TDirectionConCommand, +} from 'src/device/commands/cur2-commands'; + +export enum ScheduleProductType { + CUR_2 = 'CUR_2', +} +export const DeviceFunctionMap: { + [T in ScheduleProductType]: (body: DeviceFunction[T]) => any; +} = { + [ScheduleProductType.CUR_2]: ({ code, value }) => { + return [ + { + code, + value, + }, + ]; + }, +}; + +type DeviceFunction = { + [ScheduleProductType.CUR_2]: + | ControlCur2Command + | ControlCur2PercentCommand + | ControlCur2AccurateCalibrationCommand + | ControlCur2TDirectionConCommand + | ControlCur2QuickCalibrationCommand; +}; diff --git a/src/schedule/services/schedule.service.ts b/src/schedule/services/schedule.service.ts index ee029cf..e4bd586 100644 --- a/src/schedule/services/schedule.service.ts +++ b/src/schedule/services/schedule.service.ts @@ -1,6 +1,6 @@ -import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; -import { TuyaContext } from '@tuya/tuya-connector-nodejs'; +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; +import { TuyaContext } from '@tuya/tuya-connector-nodejs'; import { AddScheduleDto, EnableScheduleDto, @@ -11,14 +11,14 @@ import { getDeviceScheduleInterface, } from '../interfaces/get.schedule.interface'; -import { convertKeysToCamelCase } from '@app/common/helper/camelCaseConverter'; -import { DeviceRepository } from '@app/common/modules/device/repositories'; import { ProductType } from '@app/common/constants/product-type.enum'; +import { convertKeysToCamelCase } from '@app/common/helper/camelCaseConverter'; import { convertTimestampToDubaiTime } from '@app/common/helper/convertTimestampToDubaiTime'; import { getEnabledDays, getScheduleStatus, } from '@app/common/helper/getScheduleStatus'; +import { DeviceRepository } from '@app/common/modules/device/repositories'; @Injectable() export class ScheduleService { @@ -49,22 +49,11 @@ export class ScheduleService { } // Corrected condition for supported device types - if ( - deviceDetails.productDevice.prodType !== ProductType.THREE_G && - deviceDetails.productDevice.prodType !== ProductType.ONE_G && - deviceDetails.productDevice.prodType !== ProductType.TWO_G && - deviceDetails.productDevice.prodType !== ProductType.WH && - deviceDetails.productDevice.prodType !== ProductType.ONE_1TG && - deviceDetails.productDevice.prodType !== ProductType.TWO_2TG && - deviceDetails.productDevice.prodType !== ProductType.THREE_3TG && - deviceDetails.productDevice.prodType !== ProductType.GD - ) { - throw new HttpException( - 'This device is not supported for schedule', - HttpStatus.BAD_REQUEST, - ); - } - return await this.enableScheduleDeviceInTuya( + this.ensureProductTypeSupportedForSchedule( + ProductType[deviceDetails.productDevice.prodType], + ); + + return this.enableScheduleDeviceInTuya( deviceDetails.deviceTuyaUuid, enableScheduleDto, ); @@ -75,29 +64,6 @@ export class ScheduleService { ); } } - async enableScheduleDeviceInTuya( - deviceId: string, - enableScheduleDto: EnableScheduleDto, - ): Promise { - try { - const path = `/v2.0/cloud/timer/device/${deviceId}/state`; - const response = await this.tuya.request({ - method: 'PUT', - path, - body: { - enable: enableScheduleDto.enable, - timer_id: enableScheduleDto.scheduleId, - }, - }); - - return response as addScheduleDeviceInterface; - } catch (error) { - throw new HttpException( - 'Error while updating schedule from Tuya', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } async deleteDeviceSchedule(deviceUuid: string, scheduleId: string) { try { const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid); @@ -107,21 +73,10 @@ export class ScheduleService { } // Corrected condition for supported device types - if ( - deviceDetails.productDevice.prodType !== ProductType.THREE_G && - deviceDetails.productDevice.prodType !== ProductType.ONE_G && - deviceDetails.productDevice.prodType !== ProductType.TWO_G && - deviceDetails.productDevice.prodType !== ProductType.WH && - deviceDetails.productDevice.prodType !== ProductType.ONE_1TG && - deviceDetails.productDevice.prodType !== ProductType.TWO_2TG && - deviceDetails.productDevice.prodType !== ProductType.THREE_3TG && - deviceDetails.productDevice.prodType !== ProductType.GD - ) { - throw new HttpException( - 'This device is not supported for schedule', - HttpStatus.BAD_REQUEST, - ); - } + this.ensureProductTypeSupportedForSchedule( + ProductType[deviceDetails.productDevice.prodType], + ); + return await this.deleteScheduleDeviceInTuya( deviceDetails.deviceTuyaUuid, scheduleId, @@ -133,25 +88,6 @@ export class ScheduleService { ); } } - async deleteScheduleDeviceInTuya( - deviceId: string, - scheduleId: string, - ): Promise { - try { - const path = `/v2.0/cloud/timer/device/${deviceId}/batch?timer_ids=${scheduleId}`; - const response = await this.tuya.request({ - method: 'DELETE', - path, - }); - - return response as addScheduleDeviceInterface; - } catch (error) { - throw new HttpException( - 'Error while deleting schedule from Tuya', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } async addDeviceSchedule(deviceUuid: string, addScheduleDto: AddScheduleDto) { try { const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid); @@ -160,22 +96,10 @@ export class ScheduleService { throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); } - // Corrected condition for supported device types - if ( - deviceDetails.productDevice.prodType !== ProductType.THREE_G && - deviceDetails.productDevice.prodType !== ProductType.ONE_G && - deviceDetails.productDevice.prodType !== ProductType.TWO_G && - deviceDetails.productDevice.prodType !== ProductType.WH && - deviceDetails.productDevice.prodType !== ProductType.ONE_1TG && - deviceDetails.productDevice.prodType !== ProductType.TWO_2TG && - deviceDetails.productDevice.prodType !== ProductType.THREE_3TG && - deviceDetails.productDevice.prodType !== ProductType.GD - ) { - throw new HttpException( - 'This device is not supported for schedule', - HttpStatus.BAD_REQUEST, - ); - } + this.ensureProductTypeSupportedForSchedule( + ProductType[deviceDetails.productDevice.prodType], + ); + await this.addScheduleDeviceInTuya( deviceDetails.deviceTuyaUuid, addScheduleDto, @@ -187,40 +111,6 @@ export class ScheduleService { ); } } - async addScheduleDeviceInTuya( - deviceId: string, - addScheduleDto: AddScheduleDto, - ): Promise { - try { - const convertedTime = convertTimestampToDubaiTime(addScheduleDto.time); - const loops = getScheduleStatus(addScheduleDto.days); - - const path = `/v2.0/cloud/timer/device/${deviceId}`; - const response = await this.tuya.request({ - method: 'POST', - path, - body: { - time: convertedTime.time, - timezone_id: 'Asia/Dubai', - loops: `${loops}`, - functions: [ - { - code: addScheduleDto.function.code, - value: addScheduleDto.function.value, - }, - ], - category: `category_${addScheduleDto.category}`, - }, - }); - - return response as addScheduleDeviceInterface; - } catch (error) { - throw new HttpException( - 'Error adding schedule from Tuya', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } async getDeviceScheduleByCategory(deviceUuid: string, category: string) { try { const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid); @@ -229,21 +119,10 @@ export class ScheduleService { throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); } // Corrected condition for supported device types - if ( - deviceDetails.productDevice.prodType !== ProductType.THREE_G && - deviceDetails.productDevice.prodType !== ProductType.ONE_G && - deviceDetails.productDevice.prodType !== ProductType.TWO_G && - deviceDetails.productDevice.prodType !== ProductType.WH && - deviceDetails.productDevice.prodType !== ProductType.ONE_1TG && - deviceDetails.productDevice.prodType !== ProductType.TWO_2TG && - deviceDetails.productDevice.prodType !== ProductType.THREE_3TG && - deviceDetails.productDevice.prodType !== ProductType.GD - ) { - throw new HttpException( - 'This device is not supported for schedule', - HttpStatus.BAD_REQUEST, - ); - } + this.ensureProductTypeSupportedForSchedule( + ProductType[deviceDetails.productDevice.prodType], + ); + const schedules = await this.getScheduleDeviceInTuya( deviceDetails.deviceTuyaUuid, category, @@ -270,7 +149,82 @@ export class ScheduleService { ); } } - async getScheduleDeviceInTuya( + async updateDeviceSchedule( + deviceUuid: string, + updateScheduleDto: UpdateScheduleDto, + ) { + try { + const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid); + + if (!deviceDetails || !deviceDetails.deviceTuyaUuid) { + throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); + } + + // Corrected condition for supported device types + this.ensureProductTypeSupportedForSchedule( + ProductType[deviceDetails.productDevice.prodType], + ); + + await this.updateScheduleDeviceInTuya( + deviceDetails.deviceTuyaUuid, + updateScheduleDto, + ); + } catch (error) { + throw new HttpException( + error.message || 'Error While Updating Schedule', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + private getDeviceByDeviceUuid( + deviceUuid: string, + withProductDevice: boolean = true, + ) { + return this.deviceRepository.findOne({ + where: { + uuid: deviceUuid, + isActive: true, + }, + ...(withProductDevice && { relations: ['productDevice'] }), + }); + } + + private async addScheduleDeviceInTuya( + deviceId: string, + addScheduleDto: AddScheduleDto, + ): Promise { + try { + const convertedTime = convertTimestampToDubaiTime(addScheduleDto.time); + const loops = getScheduleStatus(addScheduleDto.days); + + const path = `/v2.0/cloud/timer/device/${deviceId}`; + const response = await this.tuya.request({ + method: 'POST', + path, + body: { + time: convertedTime.time, + timezone_id: 'Asia/Dubai', + loops: `${loops}`, + functions: [ + { + ...addScheduleDto.function, + }, + ], + category: `category_${addScheduleDto.category}`, + }, + }); + + return response as addScheduleDeviceInterface; + } catch (error) { + throw new HttpException( + 'Error adding schedule from Tuya', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + private async getScheduleDeviceInTuya( deviceId: string, category: string, ): Promise { @@ -291,57 +245,8 @@ export class ScheduleService { ); } } - async getDeviceByDeviceUuid( - deviceUuid: string, - withProductDevice: boolean = true, - ) { - return await this.deviceRepository.findOne({ - where: { - uuid: deviceUuid, - isActive: true, - }, - ...(withProductDevice && { relations: ['productDevice'] }), - }); - } - async updateDeviceSchedule( - deviceUuid: string, - updateScheduleDto: UpdateScheduleDto, - ) { - try { - const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid); - if (!deviceDetails || !deviceDetails.deviceTuyaUuid) { - throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); - } - - // Corrected condition for supported device types - if ( - deviceDetails.productDevice.prodType !== ProductType.THREE_G && - deviceDetails.productDevice.prodType !== ProductType.ONE_G && - deviceDetails.productDevice.prodType !== ProductType.TWO_G && - deviceDetails.productDevice.prodType !== ProductType.WH && - deviceDetails.productDevice.prodType !== ProductType.ONE_1TG && - deviceDetails.productDevice.prodType !== ProductType.TWO_2TG && - deviceDetails.productDevice.prodType !== ProductType.THREE_3TG && - deviceDetails.productDevice.prodType !== ProductType.GD - ) { - throw new HttpException( - 'This device is not supported for schedule', - HttpStatus.BAD_REQUEST, - ); - } - await this.updateScheduleDeviceInTuya( - deviceDetails.deviceTuyaUuid, - updateScheduleDto, - ); - } catch (error) { - throw new HttpException( - error.message || 'Error While Updating Schedule', - error.status || HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - async updateScheduleDeviceInTuya( + private async updateScheduleDeviceInTuya( deviceId: string, updateScheduleDto: UpdateScheduleDto, ): Promise { @@ -376,4 +281,69 @@ export class ScheduleService { ); } } + + private async enableScheduleDeviceInTuya( + deviceId: string, + enableScheduleDto: EnableScheduleDto, + ): Promise { + try { + const path = `/v2.0/cloud/timer/device/${deviceId}/state`; + const response = await this.tuya.request({ + method: 'PUT', + path, + body: { + enable: enableScheduleDto.enable, + timer_id: enableScheduleDto.scheduleId, + }, + }); + + return response as addScheduleDeviceInterface; + } catch (error) { + throw new HttpException( + 'Error while updating schedule from Tuya', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + private async deleteScheduleDeviceInTuya( + deviceId: string, + scheduleId: string, + ): Promise { + try { + const path = `/v2.0/cloud/timer/device/${deviceId}/batch?timer_ids=${scheduleId}`; + const response = await this.tuya.request({ + method: 'DELETE', + path, + }); + + return response as addScheduleDeviceInterface; + } catch (error) { + throw new HttpException( + 'Error while deleting schedule from Tuya', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + private ensureProductTypeSupportedForSchedule(deviceType: ProductType): void { + if ( + ![ + ProductType.THREE_G, + ProductType.ONE_G, + ProductType.TWO_G, + ProductType.WH, + ProductType.ONE_1TG, + ProductType.TWO_2TG, + ProductType.THREE_3TG, + ProductType.GD, + ProductType.CUR_2, + ].includes(deviceType) + ) { + throw new HttpException( + 'This device is not supported for schedule', + HttpStatus.BAD_REQUEST, + ); + } + } } From 4e6b6f6ac574f9a2c6992d5f4bc5eae8f0dd8a74 Mon Sep 17 00:00:00 2001 From: Dona Maria Absi <49731027+DonaAbsi@users.noreply.github.com> Date: Tue, 24 Jun 2025 13:04:21 +0300 Subject: [PATCH 29/86] adjusted procedures --- .../fact_daily_device_energy_consumed_procedure.sql | 13 ++++--------- ...fact_hourly_device_energy_consumed_procedure.sql | 12 +----------- ...act_monthly_device_energy_consumed_procedure.sql | 5 ----- 3 files changed, 5 insertions(+), 25 deletions(-) diff --git a/libs/common/src/sql/procedures/fact_device_energy_consumed/fact_daily_device_energy_consumed_procedure.sql b/libs/common/src/sql/procedures/fact_device_energy_consumed/fact_daily_device_energy_consumed_procedure.sql index c549891..ab9e7d2 100644 --- a/libs/common/src/sql/procedures/fact_device_energy_consumed/fact_daily_device_energy_consumed_procedure.sql +++ b/libs/common/src/sql/procedures/fact_device_energy_consumed/fact_daily_device_energy_consumed_procedure.sql @@ -1,6 +1,5 @@ WITH params AS ( SELECT - $1::uuid AS device_id, $2::date AS target_date ), total_energy AS ( @@ -14,8 +13,7 @@ total_energy AS ( MAX(log.value)::integer AS max_value FROM "device-status-log" log, params WHERE log.code = 'EnergyConsumed' - AND log.device_id = params.device_id - AND log.event_time::date = params.target_date + AND log.event_time::date = params.target_date GROUP BY 1,2,3,4,5 ), energy_phase_A AS ( @@ -29,8 +27,7 @@ energy_phase_A AS ( MAX(log.value)::integer AS max_value FROM "device-status-log" log, params WHERE log.code = 'EnergyConsumedA' - AND log.device_id = params.device_id - AND log.event_time::date = params.target_date + AND log.event_time::date = params.target_date GROUP BY 1,2,3,4,5 ), energy_phase_B AS ( @@ -44,8 +41,7 @@ energy_phase_B AS ( MAX(log.value)::integer AS max_value FROM "device-status-log" log, params WHERE log.code = 'EnergyConsumedB' - AND log.device_id = params.device_id - AND log.event_time::date = params.target_date + AND log.event_time::date = params.target_date GROUP BY 1,2,3,4,5 ), energy_phase_C AS ( @@ -59,8 +55,7 @@ energy_phase_C AS ( MAX(log.value)::integer AS max_value FROM "device-status-log" log, params WHERE log.code = 'EnergyConsumedC' - AND log.device_id = params.device_id - AND log.event_time::date = params.target_date + AND log.event_time::date = params.target_date GROUP BY 1,2,3,4,5 ), final_data AS ( diff --git a/libs/common/src/sql/procedures/fact_device_energy_consumed/fact_hourly_device_energy_consumed_procedure.sql b/libs/common/src/sql/procedures/fact_device_energy_consumed/fact_hourly_device_energy_consumed_procedure.sql index ffefc4f..c056a0f 100644 --- a/libs/common/src/sql/procedures/fact_device_energy_consumed/fact_hourly_device_energy_consumed_procedure.sql +++ b/libs/common/src/sql/procedures/fact_device_energy_consumed/fact_hourly_device_energy_consumed_procedure.sql @@ -1,8 +1,6 @@ WITH params AS ( SELECT - $1::uuid AS device_id, - $2::date AS target_date, - $3::text AS target_hour + $2::date AS target_date ), total_energy AS ( SELECT @@ -15,9 +13,7 @@ total_energy AS ( MAX(log.value)::integer AS max_value FROM "device-status-log" log, params WHERE log.code = 'EnergyConsumed' - AND log.device_id = params.device_id AND log.event_time::date = params.target_date - AND EXTRACT(HOUR FROM log.event_time)::text = params.target_hour GROUP BY 1,2,3,4,5 ), energy_phase_A AS ( @@ -31,9 +27,7 @@ energy_phase_A AS ( MAX(log.value)::integer AS max_value FROM "device-status-log" log, params WHERE log.code = 'EnergyConsumedA' - AND log.device_id = params.device_id AND log.event_time::date = params.target_date - AND EXTRACT(HOUR FROM log.event_time)::text = params.target_hour GROUP BY 1,2,3,4,5 ), energy_phase_B AS ( @@ -47,9 +41,7 @@ energy_phase_B AS ( MAX(log.value)::integer AS max_value FROM "device-status-log" log, params WHERE log.code = 'EnergyConsumedB' - AND log.device_id = params.device_id AND log.event_time::date = params.target_date - AND EXTRACT(HOUR FROM log.event_time)::text = params.target_hour GROUP BY 1,2,3,4,5 ), energy_phase_C AS ( @@ -63,9 +55,7 @@ energy_phase_C AS ( MAX(log.value)::integer AS max_value FROM "device-status-log" log, params WHERE log.code = 'EnergyConsumedC' - AND log.device_id = params.device_id AND log.event_time::date = params.target_date - AND EXTRACT(HOUR FROM log.event_time)::text = params.target_hour GROUP BY 1,2,3,4,5 ), final_data AS ( diff --git a/libs/common/src/sql/procedures/fact_device_energy_consumed/fact_monthly_device_energy_consumed_procedure.sql b/libs/common/src/sql/procedures/fact_device_energy_consumed/fact_monthly_device_energy_consumed_procedure.sql index 0e69d60..691de79 100644 --- a/libs/common/src/sql/procedures/fact_device_energy_consumed/fact_monthly_device_energy_consumed_procedure.sql +++ b/libs/common/src/sql/procedures/fact_device_energy_consumed/fact_monthly_device_energy_consumed_procedure.sql @@ -1,6 +1,5 @@ WITH params AS ( SELECT - $1::uuid AS device_id, $2::text AS target_month -- Format should match 'MM-YYYY' ), total_energy AS ( @@ -14,7 +13,6 @@ total_energy AS ( MAX(log.value)::integer AS max_value FROM "device-status-log" log, params WHERE log.code = 'EnergyConsumed' - AND log.device_id = params.device_id AND TO_CHAR(log.event_time, 'MM-YYYY') = params.target_month GROUP BY 1,2,3,4,5 ), @@ -29,7 +27,6 @@ energy_phase_A AS ( MAX(log.value)::integer AS max_value FROM "device-status-log" log, params WHERE log.code = 'EnergyConsumedA' - AND log.device_id = params.device_id AND TO_CHAR(log.event_time, 'MM-YYYY') = params.target_month GROUP BY 1,2,3,4,5 ), @@ -44,7 +41,6 @@ energy_phase_B AS ( MAX(log.value)::integer AS max_value FROM "device-status-log" log, params WHERE log.code = 'EnergyConsumedB' - AND log.device_id = params.device_id AND TO_CHAR(log.event_time, 'MM-YYYY') = params.target_month GROUP BY 1,2,3,4,5 ), @@ -59,7 +55,6 @@ energy_phase_C AS ( MAX(log.value)::integer AS max_value FROM "device-status-log" log, params WHERE log.code = 'EnergyConsumedC' - AND log.device_id = params.device_id AND TO_CHAR(log.event_time, 'MM-YYYY') = params.target_month GROUP BY 1,2,3,4,5 ), From e58d2d4831675e551e89f036f59c6f352ef902c2 Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Tue, 24 Jun 2025 14:56:02 +0300 Subject: [PATCH 30/86] Test/prevent server block on rate limit (#432) --- src/app.module.ts | 8 +++++++- src/main.ts | 12 ------------ 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/app.module.ts b/src/app.module.ts index 2401b0c..712531f 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -37,6 +37,7 @@ import { VisitorPasswordModule } from './vistor-password/visitor-password.module import { ThrottlerGuard } from '@nestjs/throttler'; import { ThrottlerModule } from '@nestjs/throttler/dist/throttler.module'; +import { isArray } from 'class-validator'; import { winstonLoggerOptions } from '../libs/common/src/logger/services/winston.logger'; import { AqiModule } from './aqi/aqi.module'; import { OccupancyModule } from './occupancy/occupancy.module'; @@ -50,7 +51,12 @@ import { WeatherModule } from './weather/weather.module'; throttlers: [{ ttl: 60000, limit: 30 }], generateKey: (context) => { const req = context.switchToHttp().getRequest(); - return req.headers['x-forwarded-for'] || req.ip; + console.log('Real IP:', req.headers['x-forwarded-for']); + return req.headers['x-forwarded-for'] + ? isArray(req.headers['x-forwarded-for']) + ? req.headers['x-forwarded-for'][0].split(':')[0] + : req.headers['x-forwarded-for'].split(':')[0] + : req.ip; }, }), WinstonModule.forRoot(winstonLoggerOptions), diff --git a/src/main.ts b/src/main.ts index 67edc11..28b546f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -21,18 +21,6 @@ async function bootstrap() { app.use(new RequestContextMiddleware().use); - app.use((req, res, next) => { - console.log( - 'Real IP:', - req.ip, - req.headers['x-forwarded-for'], - req.connection.remoteAddress, - ); - next(); - }); - - // app.getHttpAdapter().getInstance().set('trust proxy', 1); - app.use( helmet({ contentSecurityPolicy: false, From d255e6811e89905491b6e02d83ab787408fa061a Mon Sep 17 00:00:00 2001 From: Dona Maria Absi <49731027+DonaAbsi@users.noreply.github.com> Date: Wed, 25 Jun 2025 10:47:37 +0300 Subject: [PATCH 31/86] update procedures --- .../proceduce_update_daily_space_aqi.sql | 8 +++++--- .../procedure_update_daily_space_occupancy_duration.sql | 6 ++---- .../procedure_update_fact_space_occupancy.sql | 6 ++---- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/libs/common/src/sql/procedures/fact_daily_space_aqi/proceduce_update_daily_space_aqi.sql b/libs/common/src/sql/procedures/fact_daily_space_aqi/proceduce_update_daily_space_aqi.sql index 04fb661..aa2fa2a 100644 --- a/libs/common/src/sql/procedures/fact_daily_space_aqi/proceduce_update_daily_space_aqi.sql +++ b/libs/common/src/sql/procedures/fact_daily_space_aqi/proceduce_update_daily_space_aqi.sql @@ -1,7 +1,6 @@ WITH params AS ( SELECT - TO_DATE(NULLIF($1, ''), 'YYYY-MM-DD') AS event_date, - $2::uuid AS space_id + TO_DATE(NULLIF($1, ''), 'YYYY-MM-DD') AS event_date ), -- Query Pipeline Starts Here @@ -277,7 +276,10 @@ SELECT a.daily_avg_ch2o,a.daily_max_ch2o, a.daily_min_ch2o FROM daily_percentages p LEFT JOIN daily_averages a - ON p.space_id = a.space_id AND p.event_date = a.event_date + ON p.space_id = a.space_id + AND p.event_date = a.event_date +JOIN params + ON params.event_date = a.event_date ORDER BY p.space_id, p.event_date) diff --git a/libs/common/src/sql/procedures/fact_daily_space_occupancy_duration/procedure_update_daily_space_occupancy_duration.sql b/libs/common/src/sql/procedures/fact_daily_space_occupancy_duration/procedure_update_daily_space_occupancy_duration.sql index e669864..f2bd3da 100644 --- a/libs/common/src/sql/procedures/fact_daily_space_occupancy_duration/procedure_update_daily_space_occupancy_duration.sql +++ b/libs/common/src/sql/procedures/fact_daily_space_occupancy_duration/procedure_update_daily_space_occupancy_duration.sql @@ -1,7 +1,6 @@ WITH params AS ( SELECT - TO_DATE(NULLIF($1, ''), 'YYYY-MM-DD') AS event_date, - $2::uuid AS space_id + TO_DATE(NULLIF($1, ''), 'YYYY-MM-DD') AS event_date ), presence_logs AS ( @@ -86,8 +85,7 @@ final_data AS ( ROUND(LEAST(raw_occupied_seconds, 86400) / 86400.0 * 100, 2) AS occupancy_percentage FROM summed_intervals s JOIN params p - ON p.space_id = s.space_id - AND p.event_date = s.event_date + ON p.event_date = s.event_date ) INSERT INTO public."space-daily-occupancy-duration" ( diff --git a/libs/common/src/sql/procedures/fact_space_occupancy_count/procedure_update_fact_space_occupancy.sql b/libs/common/src/sql/procedures/fact_space_occupancy_count/procedure_update_fact_space_occupancy.sql index cc727c0..ecf5ffc 100644 --- a/libs/common/src/sql/procedures/fact_space_occupancy_count/procedure_update_fact_space_occupancy.sql +++ b/libs/common/src/sql/procedures/fact_space_occupancy_count/procedure_update_fact_space_occupancy.sql @@ -1,7 +1,6 @@ WITH params AS ( SELECT - TO_DATE(NULLIF($1, ''), 'YYYY-MM-DD') AS event_date, - $2::uuid AS space_id + TO_DATE(NULLIF($1, ''), 'YYYY-MM-DD') AS event_date ), device_logs AS ( @@ -87,8 +86,7 @@ SELECT summary.space_id, count_total_presence_detected FROM summary JOIN params P ON true -where summary.space_id = P.space_id -and (P.event_date IS NULL or summary.event_date::date = P.event_date) +where (P.event_date IS NULL or summary.event_date::date = P.event_date) ORDER BY space_id, event_date) From 43ab0030f0f70338363edfd01d5dacbbf76239c1 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Wed, 25 Jun 2025 03:20:12 -0600 Subject: [PATCH 32/86] refactor: clean up unused services and optimize batch processing in DeviceStatusFirebaseService --- .../devices-status/devices-status.module.ts | 16 -- .../services/devices-status.service.ts | 166 ++++++++---------- .../services/tuya.web.socket.service.ts | 40 ++--- 3 files changed, 95 insertions(+), 127 deletions(-) diff --git a/libs/common/src/firebase/devices-status/devices-status.module.ts b/libs/common/src/firebase/devices-status/devices-status.module.ts index 52f6123..54d5cfa 100644 --- a/libs/common/src/firebase/devices-status/devices-status.module.ts +++ b/libs/common/src/firebase/devices-status/devices-status.module.ts @@ -3,28 +3,12 @@ import { DeviceStatusFirebaseController } from './controllers/devices-status.con import { DeviceStatusFirebaseService } from './services/devices-status.service'; import { DeviceRepository } from '@app/common/modules/device/repositories'; import { DeviceStatusLogRepository } from '@app/common/modules/device-status-log/repositories/device-status.repository'; -import { PowerClampService } from '@app/common/helper/services/power.clamp.service'; -import { - PowerClampHourlyRepository, - PowerClampDailyRepository, - PowerClampMonthlyRepository, -} from '@app/common/modules/power-clamp/repositories'; -import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service'; -import { OccupancyService } from '@app/common/helper/services/occupancy.service'; -import { AqiDataService } from '@app/common/helper/services/aqi.data.service'; @Module({ providers: [ DeviceStatusFirebaseService, DeviceRepository, DeviceStatusLogRepository, - PowerClampService, - PowerClampHourlyRepository, - PowerClampDailyRepository, - PowerClampMonthlyRepository, - SqlLoaderService, - OccupancyService, - AqiDataService, ], controllers: [DeviceStatusFirebaseController], exports: [DeviceStatusFirebaseService, DeviceStatusLogRepository], diff --git a/libs/common/src/firebase/devices-status/services/devices-status.service.ts b/libs/common/src/firebase/devices-status/services/devices-status.service.ts index 695022b..3fae855 100644 --- a/libs/common/src/firebase/devices-status/services/devices-status.service.ts +++ b/libs/common/src/firebase/devices-status/services/devices-status.service.ts @@ -18,12 +18,6 @@ import { runTransaction, } from 'firebase/database'; import { DeviceStatusLogRepository } from '@app/common/modules/device-status-log/repositories'; -import { ProductType } from '@app/common/constants/product-type.enum'; -import { PowerClampService } from '@app/common/helper/services/power.clamp.service'; -import { PowerClampEnergyEnum } from '@app/common/constants/power.clamp.enargy.enum'; -import { PresenceSensorEnum } from '@app/common/constants/presence.sensor.enum'; -import { OccupancyService } from '@app/common/helper/services/occupancy.service'; -import { AqiDataService } from '@app/common/helper/services/aqi.data.service'; @Injectable() export class DeviceStatusFirebaseService { private tuya: TuyaContext; @@ -31,9 +25,6 @@ export class DeviceStatusFirebaseService { constructor( private readonly configService: ConfigService, private readonly deviceRepository: DeviceRepository, - private readonly powerClampService: PowerClampService, - private readonly occupancyService: OccupancyService, - private readonly aqiDataService: AqiDataService, private deviceStatusLogRepository: DeviceStatusLogRepository, ) { const accessKey = this.configService.get('auth-config.ACCESS_KEY'); @@ -76,28 +67,85 @@ export class DeviceStatusFirebaseService { ); } } - async addDeviceStatusToOurDb( - addDeviceStatusDto: AddDeviceStatusDto, - ): Promise { - try { - const device = await this.getDeviceByDeviceTuyaUuid( - addDeviceStatusDto.deviceTuyaUuid, - ); + async addBatchDeviceStatusToOurDb( + batch: { deviceTuyaUuid: string; status: any; log: any }[], + ): Promise { + const allLogs = []; + const deviceMap = new Map(); - if (device?.uuid) { - return await this.createDeviceStatusInOurDb({ - deviceUuid: device.uuid, - ...addDeviceStatusDto, - productType: device.productDevice.prodType, - }); - } - // Return null if device not found or no UUID - return null; - } catch (error) { - // Handle the error silently, perhaps log it internally or ignore it - return null; + console.log( + `🧠 Starting device lookups for batch of ${batch.length} items...`, + ); + + // Step 1: Parallel device fetching + await Promise.all( + batch.map(async (item) => { + if (!deviceMap.has(item.deviceTuyaUuid)) { + const device = await this.getDeviceByDeviceTuyaUuid( + item.deviceTuyaUuid, + ); + device?.uuid && deviceMap.set(item.deviceTuyaUuid, device); + } + }), + ); + + console.log(`🔍 Found ${deviceMap.size} devices from batch`); + + // Step 2: Prepare logs and updates + for (const item of batch) { + const device = deviceMap.get(item.deviceTuyaUuid); + if (!device?.uuid) continue; + + const logs = item.log.properties.map((property) => + this.deviceStatusLogRepository.create({ + deviceId: device.uuid, + deviceTuyaId: item.deviceTuyaUuid, + productId: item.log.productId, + log: item.log, + code: property.code, + value: property.value, + eventId: item.log.dataId, + eventTime: new Date(property.time).toISOString(), + }), + ); + allLogs.push(...logs); } + + console.log(`📝 Total logs to insert: ${allLogs.length}`); + // Step 3: Insert logs in chunks with ON CONFLICT DO NOTHING + const insertLogsPromise = (async () => { + const chunkSize = 300; + let insertedCount = 0; + + for (let i = 0; i < allLogs.length; i += chunkSize) { + const chunk = allLogs.slice(i, i + chunkSize); + try { + const result = await this.deviceStatusLogRepository + .createQueryBuilder() + .insert() + .into('device-status-log') // or use DeviceStatusLogEntity + .values(chunk) + .orIgnore() // skip duplicates + .execute(); + + insertedCount += result.identifiers.length; + console.log( + `✅ Inserted ${result.identifiers.length} / ${chunk.length} logs (chunk)`, + ); + } catch (error) { + console.error('❌ Insert error (skipped chunk):', error.message); + } + } + + console.log( + `✅ Total logs inserted: ${insertedCount} / ${allLogs.length}`, + ); + })(); + + // Step 5: Wait for both insert and post-processing to finish + await Promise.all([insertLogsPromise]); } + async addDeviceStatusToFirebase( addDeviceStatusDto: AddDeviceStatusDto, ): Promise { @@ -237,66 +285,4 @@ export class DeviceStatusFirebaseService { const snapshot: DataSnapshot = await get(dataRef); return snapshot.val(); } - async createDeviceStatusInOurDb( - addDeviceStatusDto: AddDeviceStatusDto, - ): Promise { - // Save logs to your repository - const newLogs = addDeviceStatusDto.log.properties.map((property) => { - return this.deviceStatusLogRepository.create({ - deviceId: addDeviceStatusDto.deviceUuid, - deviceTuyaId: addDeviceStatusDto.deviceTuyaUuid, - productId: addDeviceStatusDto.log.productId, - log: addDeviceStatusDto.log, - code: property.code, - value: property.value, - eventId: addDeviceStatusDto.log.dataId, - eventTime: new Date(property.time).toISOString(), - }); - }); - await this.deviceStatusLogRepository.save(newLogs); - - if (addDeviceStatusDto.productType === ProductType.PC) { - const energyCodes = new Set([ - PowerClampEnergyEnum.ENERGY_CONSUMED, - PowerClampEnergyEnum.ENERGY_CONSUMED_A, - PowerClampEnergyEnum.ENERGY_CONSUMED_B, - PowerClampEnergyEnum.ENERGY_CONSUMED_C, - ]); - - const energyStatus = addDeviceStatusDto?.log?.properties?.find((status) => - energyCodes.has(status.code), - ); - - if (energyStatus) { - await this.powerClampService.updateEnergyConsumedHistoricalData( - addDeviceStatusDto.deviceUuid, - ); - } - } - - if ( - addDeviceStatusDto.productType === ProductType.CPS || - addDeviceStatusDto.productType === ProductType.WPS - ) { - const occupancyCodes = new Set([PresenceSensorEnum.PRESENCE_STATE]); - - const occupancyStatus = addDeviceStatusDto?.log?.properties?.find( - (status) => occupancyCodes.has(status.code), - ); - - if (occupancyStatus) { - await this.occupancyService.updateOccupancySensorHistoricalData( - addDeviceStatusDto.deviceUuid, - ); - await this.occupancyService.updateOccupancySensorHistoricalDurationData( - addDeviceStatusDto.deviceUuid, - ); - } - } - if (addDeviceStatusDto.productType === ProductType.AQI) { - await this.aqiDataService.updateAQISensorHistoricalData( - addDeviceStatusDto.deviceUuid, - ); - } - } } diff --git a/libs/common/src/helper/services/tuya.web.socket.service.ts b/libs/common/src/helper/services/tuya.web.socket.service.ts index 0c32c04..3f56a63 100644 --- a/libs/common/src/helper/services/tuya.web.socket.service.ts +++ b/libs/common/src/helper/services/tuya.web.socket.service.ts @@ -38,8 +38,8 @@ export class TuyaWebSocketService { this.client.start(); } - // Trigger the queue processor every 2 seconds - setInterval(() => this.processQueue(), 10000); + // Trigger the queue processor every 15 seconds + setInterval(() => this.processQueue(), 15000); } private setupEventHandlers() { @@ -93,32 +93,30 @@ export class TuyaWebSocketService { }); } private async processQueue() { - if (this.isProcessing || this.messageQueue.length === 0) return; + if (this.isProcessing) { + console.log('⏳ Skipping: still processing previous batch'); + return; + } + + if (this.messageQueue.length === 0) return; this.isProcessing = true; - const batch = [...this.messageQueue]; this.messageQueue = []; + console.log(`🔁 Processing batch of size: ${batch.length}`); + try { - for (const item of batch) { - if (this.sosHandlerService.isSosTriggered(item.status)) { - await this.sosHandlerService.handleSosEventOurDb( - item.devId, - item.logData, - ); - } else { - await this.deviceStatusFirebaseService.addDeviceStatusToOurDb({ - deviceTuyaUuid: item.devId, - status: item.status, - log: item.logData, - }); - } - } + await this.deviceStatusFirebaseService.addBatchDeviceStatusToOurDb( + batch.map((item) => ({ + deviceTuyaUuid: item.devId, + status: item.status, + log: item.logData, + })), + ); } catch (error) { - console.error('Error processing batch:', error); - // Re-add the batch to the queue for retry - this.messageQueue.unshift(...batch); + console.error('❌ Error processing batch:', error); + this.messageQueue.unshift(...batch); // retry } finally { this.isProcessing = false; } From 9bebcb2f3e1d306124ffac7eb70bcdeef4890414 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Wed, 25 Jun 2025 03:20:25 -0600 Subject: [PATCH 33/86] feat: implement scheduler for periodic data updates and optimize database procedures - Added SchedulerModule and SchedulerService to handle hourly data updates for AQI, occupancy, and energy consumption. - Refactored existing services to remove unused device repository dependencies and streamline procedure execution. - Updated SQL procedures to use correct parameter indexing. - Enhanced error handling and logging for scheduled tasks. - Integrated new repositories for presence sensor and AQI pollutant stats across multiple modules. - Added NestJS schedule package for task scheduling capabilities. --- libs/common/src/database/database.module.ts | 2 +- .../src/helper/services/aqi.data.service.ts | 65 ++++++++----- .../src/helper/services/occupancy.service.ts | 89 +++++++++--------- .../helper/services/power.clamp.service.ts | 90 +++++++++++------- .../helper/services/sos.handler.service.ts | 38 +++----- .../services/tuya.web.socket.service.ts | 2 +- ...daily_device_energy_consumed_procedure.sql | 2 +- ...ourly_device_energy_consumed_procedure.sql | 2 +- ...nthly_device_energy_consumed_procedure.sql | 2 +- package-lock.json | 42 +++++++++ package.json | 1 + src/app.module.ts | 4 + .../commission-device.module.ts | 4 + src/community/community.module.ts | 4 + src/door-lock/door.lock.module.ts | 4 + src/group/group.module.ts | 4 + src/invite-user/invite-user.module.ts | 4 + src/power-clamp/power-clamp.module.ts | 4 + src/project/project.module.ts | 4 + src/scheduler/scheduler.module.ts | 25 +++++ src/scheduler/scheduler.service.ts | 92 +++++++++++++++++++ src/space-model/space-model.module.ts | 4 + src/space/space.module.ts | 4 + .../visitor-password.module.ts | 4 + 24 files changed, 368 insertions(+), 128 deletions(-) create mode 100644 src/scheduler/scheduler.module.ts create mode 100644 src/scheduler/scheduler.service.ts diff --git a/libs/common/src/database/database.module.ts b/libs/common/src/database/database.module.ts index dd25da9..e86ac6e 100644 --- a/libs/common/src/database/database.module.ts +++ b/libs/common/src/database/database.module.ts @@ -126,7 +126,7 @@ import { VisitorPasswordEntity } from '../modules/visitor-password/entities'; extra: { charset: 'utf8mb4', max: 100, // set pool max size - idleTimeoutMillis: 5000, // close idle clients after 5 second + idleTimeoutMillis: 3000, // close idle clients after 5 second connectionTimeoutMillis: 12_000, // return an error after 11 second if connection could not be established maxUses: 7500, // close (and replace) a connection after it has been used 7500 times (see below for discussion) }, diff --git a/libs/common/src/helper/services/aqi.data.service.ts b/libs/common/src/helper/services/aqi.data.service.ts index 3e19b6c..9a18274 100644 --- a/libs/common/src/helper/services/aqi.data.service.ts +++ b/libs/common/src/helper/services/aqi.data.service.ts @@ -1,44 +1,63 @@ -import { DeviceRepository } from '@app/common/modules/device/repositories'; import { Injectable } from '@nestjs/common'; -import { SqlLoaderService } from './sql-loader.service'; import { DataSource } from 'typeorm'; import { SQL_PROCEDURES_PATH } from '@app/common/constants/sql-query-path'; +import { SqlLoaderService } from './sql-loader.service'; @Injectable() export class AqiDataService { constructor( private readonly sqlLoader: SqlLoaderService, private readonly dataSource: DataSource, - private readonly deviceRepository: DeviceRepository, ) {} - async updateAQISensorHistoricalData(deviceUuid: string): Promise { - try { - const now = new Date(); - const dateStr = now.toLocaleDateString('en-CA'); // YYYY-MM-DD - const device = await this.deviceRepository.findOne({ - where: { uuid: deviceUuid }, - relations: ['spaceDevice'], - }); - await this.executeProcedure( - 'fact_daily_space_aqi', - 'proceduce_update_daily_space_aqi', - [dateStr, device.spaceDevice?.uuid], - ); + async updateAQISensorHistoricalData(): Promise { + try { + const { dateStr } = this.getFormattedDates(); + + // Execute all procedures in parallel + await Promise.all([ + this.executeProcedureWithRetry( + 'proceduce_update_daily_space_aqi', + [dateStr], + 'fact_daily_space_aqi', + ), + ]); } catch (err) { - console.error('Failed to insert or update aqi data:', err); + console.error('Failed to update AQI sensor historical data:', err); throw err; } } - - private async executeProcedure( - procedureFolderName: string, + private getFormattedDates(): { dateStr: string } { + const now = new Date(); + return { + dateStr: now.toLocaleDateString('en-CA'), // YYYY-MM-DD + }; + } + private async executeProcedureWithRetry( procedureFileName: string, params: (string | number | null)[], + folderName: string, + retries = 3, ): Promise { - const query = this.loadQuery(procedureFolderName, procedureFileName); - await this.dataSource.query(query, params); - console.log(`Procedure ${procedureFileName} executed successfully.`); + try { + const query = this.loadQuery(folderName, procedureFileName); + await this.dataSource.query(query, params); + console.log(`Procedure ${procedureFileName} executed successfully.`); + } catch (err) { + if (retries > 0) { + const delayMs = 1000 * (4 - retries); // Exponential backoff + console.warn(`Retrying ${procedureFileName} (${retries} retries left)`); + await new Promise((resolve) => setTimeout(resolve, delayMs)); + return this.executeProcedureWithRetry( + procedureFileName, + params, + folderName, + retries - 1, + ); + } + console.error(`Failed to execute ${procedureFileName}:`, err); + throw err; + } } private loadQuery(folderName: string, fileName: string): string { diff --git a/libs/common/src/helper/services/occupancy.service.ts b/libs/common/src/helper/services/occupancy.service.ts index ea99b7c..cec1560 100644 --- a/libs/common/src/helper/services/occupancy.service.ts +++ b/libs/common/src/helper/services/occupancy.service.ts @@ -1,65 +1,68 @@ -import { DeviceRepository } from '@app/common/modules/device/repositories'; import { Injectable } from '@nestjs/common'; -import { SqlLoaderService } from './sql-loader.service'; import { DataSource } from 'typeorm'; import { SQL_PROCEDURES_PATH } from '@app/common/constants/sql-query-path'; +import { SqlLoaderService } from './sql-loader.service'; @Injectable() export class OccupancyService { constructor( private readonly sqlLoader: SqlLoaderService, private readonly dataSource: DataSource, - private readonly deviceRepository: DeviceRepository, ) {} - async updateOccupancySensorHistoricalDurationData( - deviceUuid: string, - ): Promise { - try { - const now = new Date(); - const dateStr = now.toLocaleDateString('en-CA'); // YYYY-MM-DD - const device = await this.deviceRepository.findOne({ - where: { uuid: deviceUuid }, - relations: ['spaceDevice'], - }); - await this.executeProcedure( - 'fact_daily_space_occupancy_duration', - 'procedure_update_daily_space_occupancy_duration', - [dateStr, device.spaceDevice?.uuid], - ); + async updateOccupancyDataProcedures(): Promise { + try { + const { dateStr } = this.getFormattedDates(); + + // Execute all procedures in parallel + await Promise.all([ + this.executeProcedureWithRetry( + 'procedure_update_fact_space_occupancy', + [dateStr], + 'fact_space_occupancy_count', + ), + this.executeProcedureWithRetry( + 'procedure_update_daily_space_occupancy_duration', + [dateStr], + 'fact_daily_space_occupancy_duration', + ), + ]); } catch (err) { - console.error('Failed to insert or update occupancy duration data:', err); + console.error('Failed to update occupancy data:', err); throw err; } } - async updateOccupancySensorHistoricalData(deviceUuid: string): Promise { - try { - const now = new Date(); - const dateStr = now.toLocaleDateString('en-CA'); // YYYY-MM-DD - const device = await this.deviceRepository.findOne({ - where: { uuid: deviceUuid }, - relations: ['spaceDevice'], - }); - - await this.executeProcedure( - 'fact_space_occupancy_count', - 'procedure_update_fact_space_occupancy', - [dateStr, device.spaceDevice?.uuid], - ); - } catch (err) { - console.error('Failed to insert or update occupancy data:', err); - throw err; - } + private getFormattedDates(): { dateStr: string } { + const now = new Date(); + return { + dateStr: now.toLocaleDateString('en-CA'), // YYYY-MM-DD + }; } - - private async executeProcedure( - procedureFolderName: string, + private async executeProcedureWithRetry( procedureFileName: string, params: (string | number | null)[], + folderName: string, + retries = 3, ): Promise { - const query = this.loadQuery(procedureFolderName, procedureFileName); - await this.dataSource.query(query, params); - console.log(`Procedure ${procedureFileName} executed successfully.`); + try { + const query = this.loadQuery(folderName, procedureFileName); + await this.dataSource.query(query, params); + console.log(`Procedure ${procedureFileName} executed successfully.`); + } catch (err) { + if (retries > 0) { + const delayMs = 1000 * (4 - retries); // Exponential backoff + console.warn(`Retrying ${procedureFileName} (${retries} retries left)`); + await new Promise((resolve) => setTimeout(resolve, delayMs)); + return this.executeProcedureWithRetry( + procedureFileName, + params, + folderName, + retries - 1, + ); + } + console.error(`Failed to execute ${procedureFileName}:`, err); + throw err; + } } private loadQuery(folderName: string, fileName: string): string { diff --git a/libs/common/src/helper/services/power.clamp.service.ts b/libs/common/src/helper/services/power.clamp.service.ts index 7c83208..7805dd5 100644 --- a/libs/common/src/helper/services/power.clamp.service.ts +++ b/libs/common/src/helper/services/power.clamp.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; -import { SqlLoaderService } from './sql-loader.service'; import { DataSource } from 'typeorm'; import { SQL_PROCEDURES_PATH } from '@app/common/constants/sql-query-path'; +import { SqlLoaderService } from './sql-loader.service'; @Injectable() export class PowerClampService { @@ -10,48 +10,72 @@ export class PowerClampService { private readonly dataSource: DataSource, ) {} - async updateEnergyConsumedHistoricalData(deviceUuid: string): Promise { + async updateEnergyConsumedHistoricalData(): Promise { try { - const now = new Date(); - const dateStr = now.toLocaleDateString('en-CA'); // YYYY-MM-DD - const hour = now.getHours(); - const monthYear = now - .toLocaleDateString('en-US', { - month: '2-digit', - year: 'numeric', - }) - .replace('/', '-'); // MM-YYYY + const { dateStr, monthYear } = this.getFormattedDates(); - await this.executeProcedure( - 'fact_hourly_device_energy_consumed_procedure', - [deviceUuid, dateStr, hour], - ); - - await this.executeProcedure( - 'fact_daily_device_energy_consumed_procedure', - [deviceUuid, dateStr], - ); - - await this.executeProcedure( - 'fact_monthly_device_energy_consumed_procedure', - [deviceUuid, monthYear], - ); + // Execute all procedures in parallel + await Promise.all([ + this.executeProcedureWithRetry( + 'fact_hourly_device_energy_consumed_procedure', + [dateStr], + 'fact_device_energy_consumed', + ), + this.executeProcedureWithRetry( + 'fact_daily_device_energy_consumed_procedure', + [dateStr], + 'fact_device_energy_consumed', + ), + this.executeProcedureWithRetry( + 'fact_monthly_device_energy_consumed_procedure', + [monthYear], + 'fact_device_energy_consumed', + ), + ]); } catch (err) { - console.error('Failed to insert or update energy data:', err); + console.error('Failed to update energy consumption data:', err); throw err; } } - private async executeProcedure( + private getFormattedDates(): { dateStr: string; monthYear: string } { + const now = new Date(); + return { + dateStr: now.toLocaleDateString('en-CA'), // YYYY-MM-DD + monthYear: now + .toLocaleDateString('en-US', { + month: '2-digit', + year: 'numeric', + }) + .replace('/', '-'), // MM-YYYY + }; + } + + private async executeProcedureWithRetry( procedureFileName: string, params: (string | number | null)[], + folderName: string, + retries = 3, ): Promise { - const query = this.loadQuery( - 'fact_device_energy_consumed', - procedureFileName, - ); - await this.dataSource.query(query, params); - console.log(`Procedure ${procedureFileName} executed successfully.`); + try { + const query = this.loadQuery(folderName, procedureFileName); + await this.dataSource.query(query, params); + console.log(`Procedure ${procedureFileName} executed successfully.`); + } catch (err) { + if (retries > 0) { + const delayMs = 1000 * (4 - retries); // Exponential backoff + console.warn(`Retrying ${procedureFileName} (${retries} retries left)`); + await new Promise((resolve) => setTimeout(resolve, delayMs)); + return this.executeProcedureWithRetry( + procedureFileName, + params, + folderName, + retries - 1, + ); + } + console.error(`Failed to execute ${procedureFileName}:`, err); + throw err; + } } private loadQuery(folderName: string, fileName: string): string { diff --git a/libs/common/src/helper/services/sos.handler.service.ts b/libs/common/src/helper/services/sos.handler.service.ts index e5f9df9..dd69f33 100644 --- a/libs/common/src/helper/services/sos.handler.service.ts +++ b/libs/common/src/helper/services/sos.handler.service.ts @@ -23,6 +23,13 @@ export class SosHandlerService { status: [{ code: 'sos', value: true }], log: logData, }); + await this.deviceStatusFirebaseService.addBatchDeviceStatusToOurDb([ + { + deviceTuyaUuid: devId, + status: [{ code: 'sos', value: true }], + log: logData, + }, + ]); setTimeout(async () => { try { @@ -31,30 +38,13 @@ export class SosHandlerService { status: [{ code: 'sos', value: false }], log: logData, }); - } catch (err) { - this.logger.error('Failed to send SOS false value', err); - } - }, 2000); - } catch (err) { - this.logger.error('Failed to send SOS true value', err); - } - } - - async handleSosEventOurDb(devId: string, logData: any): Promise { - try { - await this.deviceStatusFirebaseService.addDeviceStatusToOurDb({ - deviceTuyaUuid: devId, - status: [{ code: 'sos', value: true }], - log: logData, - }); - - setTimeout(async () => { - try { - await this.deviceStatusFirebaseService.addDeviceStatusToOurDb({ - deviceTuyaUuid: devId, - status: [{ code: 'sos', value: false }], - log: logData, - }); + await this.deviceStatusFirebaseService.addBatchDeviceStatusToOurDb([ + { + deviceTuyaUuid: devId, + status: [{ code: 'sos', value: false }], + log: logData, + }, + ]); } catch (err) { this.logger.error('Failed to send SOS false value', err); } diff --git a/libs/common/src/helper/services/tuya.web.socket.service.ts b/libs/common/src/helper/services/tuya.web.socket.service.ts index 3f56a63..1db1991 100644 --- a/libs/common/src/helper/services/tuya.web.socket.service.ts +++ b/libs/common/src/helper/services/tuya.web.socket.service.ts @@ -108,7 +108,7 @@ export class TuyaWebSocketService { try { await this.deviceStatusFirebaseService.addBatchDeviceStatusToOurDb( - batch.map((item) => ({ + batch?.map((item) => ({ deviceTuyaUuid: item.devId, status: item.status, log: item.logData, diff --git a/libs/common/src/sql/procedures/fact_device_energy_consumed/fact_daily_device_energy_consumed_procedure.sql b/libs/common/src/sql/procedures/fact_device_energy_consumed/fact_daily_device_energy_consumed_procedure.sql index ab9e7d2..233b24d 100644 --- a/libs/common/src/sql/procedures/fact_device_energy_consumed/fact_daily_device_energy_consumed_procedure.sql +++ b/libs/common/src/sql/procedures/fact_device_energy_consumed/fact_daily_device_energy_consumed_procedure.sql @@ -1,6 +1,6 @@ WITH params AS ( SELECT - $2::date AS target_date + $1::date AS target_date ), total_energy AS ( SELECT diff --git a/libs/common/src/sql/procedures/fact_device_energy_consumed/fact_hourly_device_energy_consumed_procedure.sql b/libs/common/src/sql/procedures/fact_device_energy_consumed/fact_hourly_device_energy_consumed_procedure.sql index c056a0f..afe6e4d 100644 --- a/libs/common/src/sql/procedures/fact_device_energy_consumed/fact_hourly_device_energy_consumed_procedure.sql +++ b/libs/common/src/sql/procedures/fact_device_energy_consumed/fact_hourly_device_energy_consumed_procedure.sql @@ -1,6 +1,6 @@ WITH params AS ( SELECT - $2::date AS target_date + $1::date AS target_date ), total_energy AS ( SELECT diff --git a/libs/common/src/sql/procedures/fact_device_energy_consumed/fact_monthly_device_energy_consumed_procedure.sql b/libs/common/src/sql/procedures/fact_device_energy_consumed/fact_monthly_device_energy_consumed_procedure.sql index 691de79..8deddda 100644 --- a/libs/common/src/sql/procedures/fact_device_energy_consumed/fact_monthly_device_energy_consumed_procedure.sql +++ b/libs/common/src/sql/procedures/fact_device_energy_consumed/fact_monthly_device_energy_consumed_procedure.sql @@ -1,6 +1,6 @@ WITH params AS ( SELECT - $2::text AS target_month -- Format should match 'MM-YYYY' + $1::text AS target_month -- Format should match 'MM-YYYY' ), total_energy AS ( SELECT diff --git a/package-lock.json b/package-lock.json index eaf972a..e3305e5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "@nestjs/jwt": "^10.2.0", "@nestjs/passport": "^10.0.3", "@nestjs/platform-express": "^10.0.0", + "@nestjs/schedule": "^6.0.0", "@nestjs/swagger": "^7.3.0", "@nestjs/terminus": "^11.0.0", "@nestjs/throttler": "^6.4.0", @@ -2538,6 +2539,19 @@ "@nestjs/core": "^10.0.0" } }, + "node_modules/@nestjs/schedule": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-6.0.0.tgz", + "integrity": "sha512-aQySMw6tw2nhitELXd3EiRacQRgzUKD9mFcUZVOJ7jPLqIBvXOyvRWLsK9SdurGA+jjziAlMef7iB5ZEFFoQpw==", + "license": "MIT", + "dependencies": { + "cron": "4.3.0" + }, + "peerDependencies": { + "@nestjs/common": "^10.0.0 || ^11.0.0", + "@nestjs/core": "^10.0.0 || ^11.0.0" + } + }, "node_modules/@nestjs/schematics": { "version": "10.2.3", "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-10.2.3.tgz", @@ -3215,6 +3229,12 @@ "@types/node": "*" } }, + "node_modules/@types/luxon": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.6.2.tgz", + "integrity": "sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw==", + "license": "MIT" + }, "node_modules/@types/methods": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", @@ -5426,6 +5446,19 @@ "devOptional": true, "license": "MIT" }, + "node_modules/cron": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/cron/-/cron-4.3.0.tgz", + "integrity": "sha512-ciiYNLfSlF9MrDqnbMdRWFiA6oizSF7kA1osPP9lRzNu0Uu+AWog1UKy7SkckiDY2irrNjeO6qLyKnXC8oxmrw==", + "license": "MIT", + "dependencies": { + "@types/luxon": "~3.6.0", + "luxon": "~3.6.0" + }, + "engines": { + "node": ">=18.x" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -9777,6 +9810,15 @@ "yallist": "^3.0.2" } }, + "node_modules/luxon": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.6.1.tgz", + "integrity": "sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/magic-string": { "version": "0.30.8", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", diff --git a/package.json b/package.json index eaec865..55d546b 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "@nestjs/jwt": "^10.2.0", "@nestjs/passport": "^10.0.3", "@nestjs/platform-express": "^10.0.0", + "@nestjs/schedule": "^6.0.0", "@nestjs/swagger": "^7.3.0", "@nestjs/terminus": "^11.0.0", "@nestjs/throttler": "^6.4.0", diff --git a/src/app.module.ts b/src/app.module.ts index 712531f..a7ae475 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -42,6 +42,8 @@ import { winstonLoggerOptions } from '../libs/common/src/logger/services/winston import { AqiModule } from './aqi/aqi.module'; import { OccupancyModule } from './occupancy/occupancy.module'; import { WeatherModule } from './weather/weather.module'; +import { ScheduleModule as NestScheduleModule } from '@nestjs/schedule'; // ✅ الباكيج الرسمي +import { SchedulerModule } from './scheduler/scheduler.module'; @Module({ imports: [ ConfigModule.forRoot({ @@ -94,6 +96,8 @@ import { WeatherModule } from './weather/weather.module'; OccupancyModule, WeatherModule, AqiModule, + SchedulerModule, + NestScheduleModule.forRoot(), ], providers: [ { diff --git a/src/commission-device/commission-device.module.ts b/src/commission-device/commission-device.module.ts index 3306705..a189f71 100644 --- a/src/commission-device/commission-device.module.ts +++ b/src/commission-device/commission-device.module.ts @@ -30,6 +30,8 @@ import { PowerClampService } from '@app/common/helper/services/power.clamp.servi import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service'; import { OccupancyService } from '@app/common/helper/services/occupancy.service'; import { AqiDataService } from '@app/common/helper/services/aqi.data.service'; +import { PresenceSensorDailySpaceRepository } from '@app/common/modules/presence-sensor/repositories'; +import { AqiSpaceDailyPollutantStatsRepository } from '@app/common/modules/aqi/repositories'; @Module({ imports: [ConfigModule, SpaceRepositoryModule], @@ -59,6 +61,8 @@ import { AqiDataService } from '@app/common/helper/services/aqi.data.service'; SqlLoaderService, OccupancyService, AqiDataService, + PresenceSensorDailySpaceRepository, + AqiSpaceDailyPollutantStatsRepository, ], exports: [], }) diff --git a/src/community/community.module.ts b/src/community/community.module.ts index 0567ffb..e41f78d 100644 --- a/src/community/community.module.ts +++ b/src/community/community.module.ts @@ -64,6 +64,8 @@ import { import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service'; import { OccupancyService } from '@app/common/helper/services/occupancy.service'; import { AqiDataService } from '@app/common/helper/services/aqi.data.service'; +import { PresenceSensorDailySpaceRepository } from '@app/common/modules/presence-sensor/repositories'; +import { AqiSpaceDailyPollutantStatsRepository } from '@app/common/modules/aqi/repositories'; @Module({ imports: [ConfigModule, SpaceRepositoryModule, UserRepositoryModule], @@ -118,6 +120,8 @@ import { AqiDataService } from '@app/common/helper/services/aqi.data.service'; SqlLoaderService, OccupancyService, AqiDataService, + PresenceSensorDailySpaceRepository, + AqiSpaceDailyPollutantStatsRepository, ], exports: [CommunityService, SpacePermissionService], }) diff --git a/src/door-lock/door.lock.module.ts b/src/door-lock/door.lock.module.ts index c2eaad1..407fced 100644 --- a/src/door-lock/door.lock.module.ts +++ b/src/door-lock/door.lock.module.ts @@ -30,6 +30,8 @@ import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service import { OccupancyService } from '@app/common/helper/services/occupancy.service'; import { CommunityRepository } from '@app/common/modules/community/repositories'; import { AqiDataService } from '@app/common/helper/services/aqi.data.service'; +import { PresenceSensorDailySpaceRepository } from '@app/common/modules/presence-sensor/repositories'; +import { AqiSpaceDailyPollutantStatsRepository } from '@app/common/modules/aqi/repositories'; @Module({ imports: [ConfigModule, DeviceRepositoryModule], controllers: [DoorLockController], @@ -58,6 +60,8 @@ import { AqiDataService } from '@app/common/helper/services/aqi.data.service'; OccupancyService, CommunityRepository, AqiDataService, + PresenceSensorDailySpaceRepository, + AqiSpaceDailyPollutantStatsRepository, ], exports: [DoorLockService], }) diff --git a/src/group/group.module.ts b/src/group/group.module.ts index 443ac31..7f9f6ab 100644 --- a/src/group/group.module.ts +++ b/src/group/group.module.ts @@ -28,6 +28,8 @@ import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service import { OccupancyService } from '@app/common/helper/services/occupancy.service'; import { CommunityRepository } from '@app/common/modules/community/repositories'; import { AqiDataService } from '@app/common/helper/services/aqi.data.service'; +import { PresenceSensorDailySpaceRepository } from '@app/common/modules/presence-sensor/repositories'; +import { AqiSpaceDailyPollutantStatsRepository } from '@app/common/modules/aqi/repositories'; @Module({ imports: [ConfigModule, DeviceRepositoryModule], controllers: [GroupController], @@ -55,6 +57,8 @@ import { AqiDataService } from '@app/common/helper/services/aqi.data.service'; OccupancyService, CommunityRepository, AqiDataService, + PresenceSensorDailySpaceRepository, + AqiSpaceDailyPollutantStatsRepository, ], exports: [GroupService], }) diff --git a/src/invite-user/invite-user.module.ts b/src/invite-user/invite-user.module.ts index 31e3a9b..d8655c5 100644 --- a/src/invite-user/invite-user.module.ts +++ b/src/invite-user/invite-user.module.ts @@ -82,6 +82,8 @@ import { SubspaceProductAllocationService } from 'src/space/services/subspace/su import { TagService as NewTagService } from 'src/tags/services'; import { UserDevicePermissionService } from 'src/user-device-permission/services'; import { UserService, UserSpaceService } from 'src/users/services'; +import { PresenceSensorDailySpaceRepository } from '@app/common/modules/presence-sensor/repositories'; +import { AqiSpaceDailyPollutantStatsRepository } from '@app/common/modules/aqi/repositories'; @Module({ imports: [ConfigModule, InviteUserRepositoryModule, CommunityModule], @@ -150,6 +152,8 @@ import { UserService, UserSpaceService } from 'src/users/services'; SqlLoaderService, OccupancyService, AqiDataService, + PresenceSensorDailySpaceRepository, + AqiSpaceDailyPollutantStatsRepository, ], exports: [InviteUserService], }) diff --git a/src/power-clamp/power-clamp.module.ts b/src/power-clamp/power-clamp.module.ts index 120e368..bb8317b 100644 --- a/src/power-clamp/power-clamp.module.ts +++ b/src/power-clamp/power-clamp.module.ts @@ -60,6 +60,8 @@ import { SubspaceProductAllocationService } from 'src/space/services/subspace/su import { TagService } from 'src/tags/services'; import { PowerClampController } from './controllers'; import { PowerClampService as PowerClamp } from './services/power-clamp.service'; +import { PresenceSensorDailySpaceRepository } from '@app/common/modules/presence-sensor/repositories'; +import { AqiSpaceDailyPollutantStatsRepository } from '@app/common/modules/aqi/repositories'; @Module({ imports: [ConfigModule], controllers: [PowerClampController], @@ -109,6 +111,8 @@ import { PowerClampService as PowerClamp } from './services/power-clamp.service' SubspaceModelProductAllocationRepoitory, OccupancyService, AqiDataService, + PresenceSensorDailySpaceRepository, + AqiSpaceDailyPollutantStatsRepository, ], exports: [PowerClamp], }) diff --git a/src/project/project.module.ts b/src/project/project.module.ts index 7c7f7d3..8860e11 100644 --- a/src/project/project.module.ts +++ b/src/project/project.module.ts @@ -67,6 +67,8 @@ import { ProjectUserController } from './controllers/project-user.controller'; import { CreateOrphanSpaceHandler } from './handler'; import { ProjectService } from './services'; import { ProjectUserService } from './services/project-user.service'; +import { PresenceSensorDailySpaceRepository } from '@app/common/modules/presence-sensor/repositories'; +import { AqiSpaceDailyPollutantStatsRepository } from '@app/common/modules/aqi/repositories'; const CommandHandlers = [CreateOrphanSpaceHandler]; @@ -124,6 +126,8 @@ const CommandHandlers = [CreateOrphanSpaceHandler]; SqlLoaderService, OccupancyService, AqiDataService, + PresenceSensorDailySpaceRepository, + AqiSpaceDailyPollutantStatsRepository, ], exports: [ProjectService, CqrsModule], }) diff --git a/src/scheduler/scheduler.module.ts b/src/scheduler/scheduler.module.ts new file mode 100644 index 0000000..f5bbb09 --- /dev/null +++ b/src/scheduler/scheduler.module.ts @@ -0,0 +1,25 @@ +import { DatabaseModule } from '@app/common/database/database.module'; +import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service'; +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { SchedulerService } from './scheduler.service'; +import { ScheduleModule as NestScheduleModule } from '@nestjs/schedule'; // ✅ الباكيج الرسمي +import { AqiDataService } from '@app/common/helper/services/aqi.data.service'; +import { OccupancyService } from '@app/common/helper/services/occupancy.service'; +import { PowerClampService } from '@app/common/helper/services/power.clamp.service'; + +@Module({ + imports: [ + NestScheduleModule.forRoot(), + TypeOrmModule.forFeature([]), + DatabaseModule, + ], + providers: [ + SchedulerService, + SqlLoaderService, + PowerClampService, + OccupancyService, + AqiDataService, + ], +}) +export class SchedulerModule {} diff --git a/src/scheduler/scheduler.service.ts b/src/scheduler/scheduler.service.ts new file mode 100644 index 0000000..e8e4337 --- /dev/null +++ b/src/scheduler/scheduler.service.ts @@ -0,0 +1,92 @@ +import { AqiDataService } from '@app/common/helper/services/aqi.data.service'; +import { Injectable } from '@nestjs/common'; +import { Cron, CronExpression } from '@nestjs/schedule'; +import { OccupancyService } from '@app/common/helper/services/occupancy.service'; +import { PowerClampService } from '@app/common/helper/services/power.clamp.service'; + +@Injectable() +export class SchedulerService { + constructor( + private readonly powerClampService: PowerClampService, + private readonly occupancyService: OccupancyService, + private readonly aqiDataService: AqiDataService, + ) { + console.log('SchedulerService initialized!'); + } + + @Cron(CronExpression.EVERY_HOUR) + async runHourlyProcedures() { + console.log('\n======== Starting Procedures ========'); + console.log(new Date().toISOString(), 'Scheduler running...'); + + try { + const results = await Promise.allSettled([ + this.executeTask( + () => this.powerClampService.updateEnergyConsumedHistoricalData(), + 'Energy Consumption', + ), + this.executeTask( + () => this.occupancyService.updateOccupancyDataProcedures(), + 'Occupancy Data', + ), + this.executeTask( + () => this.aqiDataService.updateAQISensorHistoricalData(), + 'AQI Data', + ), + ]); + + this.logResults(results); + } catch (error) { + console.error('MAIN SCHEDULER ERROR:', error); + if (error.stack) { + console.error('Error stack:', error.stack); + } + } + } + + private async executeTask( + task: () => Promise, + name: string, + ): Promise<{ name: string; status: string }> { + try { + console.log(`[${new Date().toISOString()}] Starting ${name} task...`); + await task(); + console.log( + `[${new Date().toISOString()}] ${name} task completed successfully`, + ); + return { name, status: 'success' }; + } catch (error) { + console.error( + `[${new Date().toISOString()}] ${name} task failed:`, + error.message, + ); + if (error.stack) { + console.error('Task error stack:', error.stack); + } + return { name, status: 'failed' }; + } + } + + private logResults(results: PromiseSettledResult[]) { + const successCount = results.filter((r) => r.status === 'fulfilled').length; + const failedCount = results.length - successCount; + + console.log('\n======== Task Results ========'); + console.log(`Successful tasks: ${successCount}`); + console.log(`Failed tasks: ${failedCount}`); + + if (failedCount > 0) { + console.log('\n======== Failed Tasks Details ========'); + results.forEach((result, index) => { + if (result.status === 'rejected') { + console.error(`Task ${index + 1} failed:`, result.reason); + if (result.reason.stack) { + console.error('Error stack:', result.reason.stack); + } + } + }); + } + + console.log('\n======== Scheduler Completed ========\n'); + } +} diff --git a/src/space-model/space-model.module.ts b/src/space-model/space-model.module.ts index 4cd6435..1b0a576 100644 --- a/src/space-model/space-model.module.ts +++ b/src/space-model/space-model.module.ts @@ -63,6 +63,8 @@ import { import { SpaceModelService, SubSpaceModelService } from './services'; import { SpaceModelProductAllocationService } from './services/space-model-product-allocation.service'; import { SubspaceModelProductAllocationService } from './services/subspace/subspace-model-product-allocation.service'; +import { PresenceSensorDailySpaceRepository } from '@app/common/modules/presence-sensor/repositories'; +import { AqiSpaceDailyPollutantStatsRepository } from '@app/common/modules/aqi/repositories'; const CommandHandlers = [ PropogateUpdateSpaceModelHandler, @@ -120,6 +122,8 @@ const CommandHandlers = [ SqlLoaderService, OccupancyService, AqiDataService, + PresenceSensorDailySpaceRepository, + AqiSpaceDailyPollutantStatsRepository, ], exports: [CqrsModule, SpaceModelService], }) diff --git a/src/space/space.module.ts b/src/space/space.module.ts index 8229879..288706e 100644 --- a/src/space/space.module.ts +++ b/src/space/space.module.ts @@ -88,6 +88,8 @@ import { } from './services'; import { SpaceProductAllocationService } from './services/space-product-allocation.service'; import { SubspaceProductAllocationService } from './services/subspace/subspace-product-allocation.service'; +import { PresenceSensorDailySpaceRepository } from '@app/common/modules/presence-sensor/repositories'; +import { AqiSpaceDailyPollutantStatsRepository } from '@app/common/modules/aqi/repositories'; export const CommandHandlers = [DisableSpaceHandler]; @@ -161,6 +163,8 @@ export const CommandHandlers = [DisableSpaceHandler]; SqlLoaderService, OccupancyService, AqiDataService, + PresenceSensorDailySpaceRepository, + AqiSpaceDailyPollutantStatsRepository, ], exports: [SpaceService], }) diff --git a/src/vistor-password/visitor-password.module.ts b/src/vistor-password/visitor-password.module.ts index c66ba39..b1d927c 100644 --- a/src/vistor-password/visitor-password.module.ts +++ b/src/vistor-password/visitor-password.module.ts @@ -32,6 +32,8 @@ import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service import { OccupancyService } from '@app/common/helper/services/occupancy.service'; import { CommunityRepository } from '@app/common/modules/community/repositories'; import { AqiDataService } from '@app/common/helper/services/aqi.data.service'; +import { PresenceSensorDailySpaceRepository } from '@app/common/modules/presence-sensor/repositories'; +import { AqiSpaceDailyPollutantStatsRepository } from '@app/common/modules/aqi/repositories'; @Module({ imports: [ConfigModule, DeviceRepositoryModule, DoorLockModule], controllers: [VisitorPasswordController], @@ -61,6 +63,8 @@ import { AqiDataService } from '@app/common/helper/services/aqi.data.service'; OccupancyService, CommunityRepository, AqiDataService, + PresenceSensorDailySpaceRepository, + AqiSpaceDailyPollutantStatsRepository, ], exports: [VisitorPasswordService], }) From 27dbe0429940a8a1831e814aae19d9cc1cc9eb75 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Wed, 25 Jun 2025 04:47:38 -0600 Subject: [PATCH 34/86] fix: remove unnecessary comment from ScheduleModule import in scheduler module --- src/app.module.ts | 2 +- src/scheduler/scheduler.module.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app.module.ts b/src/app.module.ts index a7ae475..f0ab9fc 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -42,7 +42,7 @@ import { winstonLoggerOptions } from '../libs/common/src/logger/services/winston import { AqiModule } from './aqi/aqi.module'; import { OccupancyModule } from './occupancy/occupancy.module'; import { WeatherModule } from './weather/weather.module'; -import { ScheduleModule as NestScheduleModule } from '@nestjs/schedule'; // ✅ الباكيج الرسمي +import { ScheduleModule as NestScheduleModule } from '@nestjs/schedule'; import { SchedulerModule } from './scheduler/scheduler.module'; @Module({ imports: [ diff --git a/src/scheduler/scheduler.module.ts b/src/scheduler/scheduler.module.ts index f5bbb09..28cf164 100644 --- a/src/scheduler/scheduler.module.ts +++ b/src/scheduler/scheduler.module.ts @@ -3,7 +3,7 @@ import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { SchedulerService } from './scheduler.service'; -import { ScheduleModule as NestScheduleModule } from '@nestjs/schedule'; // ✅ الباكيج الرسمي +import { ScheduleModule as NestScheduleModule } from '@nestjs/schedule'; import { AqiDataService } from '@app/common/helper/services/aqi.data.service'; import { OccupancyService } from '@app/common/helper/services/occupancy.service'; import { PowerClampService } from '@app/common/helper/services/power.clamp.service'; From 68692b7c8b4feb58452b95b030d11b6eb27f2a2e Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Wed, 25 Jun 2025 13:50:38 +0300 Subject: [PATCH 35/86] increase rate limit to 100 per minute for each IP (#435) --- src/app.module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app.module.ts b/src/app.module.ts index f0ab9fc..3eee546 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -50,7 +50,7 @@ import { SchedulerModule } from './scheduler/scheduler.module'; load: config, }), ThrottlerModule.forRoot({ - throttlers: [{ ttl: 60000, limit: 30 }], + throttlers: [{ ttl: 60000, limit: 100 }], generateKey: (context) => { const req = context.switchToHttp().getRequest(); console.log('Real IP:', req.headers['x-forwarded-for']); From 71f6ccb4db7f17edcd11eea4d7222d9f2f837aaa Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Wed, 25 Jun 2025 05:20:26 -0600 Subject: [PATCH 36/86] fix: add validation for missing properties in device status logs --- .../devices-status/services/devices-status.service.ts | 8 +++++++- .../common/src/helper/services/tuya.web.socket.service.ts | 8 ++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/libs/common/src/firebase/devices-status/services/devices-status.service.ts b/libs/common/src/firebase/devices-status/services/devices-status.service.ts index 3fae855..7bbdd87 100644 --- a/libs/common/src/firebase/devices-status/services/devices-status.service.ts +++ b/libs/common/src/firebase/devices-status/services/devices-status.service.ts @@ -95,7 +95,13 @@ export class DeviceStatusFirebaseService { for (const item of batch) { const device = deviceMap.get(item.deviceTuyaUuid); if (!device?.uuid) continue; - + if (!Array.isArray(item.log?.properties)) { + console.warn( + `🚨 Missing properties for device: ${item.deviceTuyaUuid}`, + ); + console.log('🔍 Problematic item:', JSON.stringify(item, null, 2)); + continue; + } const logs = item.log.properties.map((property) => this.deviceStatusLogRepository.create({ deviceId: device.uuid, diff --git a/libs/common/src/helper/services/tuya.web.socket.service.ts b/libs/common/src/helper/services/tuya.web.socket.service.ts index 1db1991..3bc3bc8 100644 --- a/libs/common/src/helper/services/tuya.web.socket.service.ts +++ b/libs/common/src/helper/services/tuya.web.socket.service.ts @@ -107,6 +107,14 @@ export class TuyaWebSocketService { console.log(`🔁 Processing batch of size: ${batch.length}`); try { + batch.forEach((item, index) => { + if (!Array.isArray(item?.logData?.properties)) { + console.warn( + `❌ Item #${index + 1} has invalid or missing 'properties':`, + ); + console.log(JSON.stringify(item, null, 2)); + } + }); await this.deviceStatusFirebaseService.addBatchDeviceStatusToOurDb( batch?.map((item) => ({ deviceTuyaUuid: item.devId, From a83424f45b0b3517171dde2d1018e07c83a80fe6 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Wed, 25 Jun 2025 05:29:28 -0600 Subject: [PATCH 37/86] fix: remove unnecessary validation for missing properties in device status logs --- .../devices-status/services/devices-status.service.ts | 7 ------- .../src/helper/services/tuya.web.socket.service.ts | 11 +++-------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/libs/common/src/firebase/devices-status/services/devices-status.service.ts b/libs/common/src/firebase/devices-status/services/devices-status.service.ts index 7bbdd87..a4f7aad 100644 --- a/libs/common/src/firebase/devices-status/services/devices-status.service.ts +++ b/libs/common/src/firebase/devices-status/services/devices-status.service.ts @@ -95,13 +95,6 @@ export class DeviceStatusFirebaseService { for (const item of batch) { const device = deviceMap.get(item.deviceTuyaUuid); if (!device?.uuid) continue; - if (!Array.isArray(item.log?.properties)) { - console.warn( - `🚨 Missing properties for device: ${item.deviceTuyaUuid}`, - ); - console.log('🔍 Problematic item:', JSON.stringify(item, null, 2)); - continue; - } const logs = item.log.properties.map((property) => this.deviceStatusLogRepository.create({ deviceId: device.uuid, diff --git a/libs/common/src/helper/services/tuya.web.socket.service.ts b/libs/common/src/helper/services/tuya.web.socket.service.ts index 3bc3bc8..9d56240 100644 --- a/libs/common/src/helper/services/tuya.web.socket.service.ts +++ b/libs/common/src/helper/services/tuya.web.socket.service.ts @@ -51,6 +51,9 @@ export class TuyaWebSocketService { this.client.message(async (ws: WebSocket, message: any) => { try { const { devId, status, logData } = this.extractMessageData(message); + if (!Array.isArray(logData?.properties)) { + return; + } if (this.sosHandlerService.isSosTriggered(status)) { await this.sosHandlerService.handleSosEventFirebase(devId, logData); } else { @@ -107,14 +110,6 @@ export class TuyaWebSocketService { console.log(`🔁 Processing batch of size: ${batch.length}`); try { - batch.forEach((item, index) => { - if (!Array.isArray(item?.logData?.properties)) { - console.warn( - `❌ Item #${index + 1} has invalid or missing 'properties':`, - ); - console.log(JSON.stringify(item, null, 2)); - } - }); await this.deviceStatusFirebaseService.addBatchDeviceStatusToOurDb( batch?.map((item) => ({ deviceTuyaUuid: item.devId, From 324661e1eee6aea203139afde2f4cac040848620 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Wed, 25 Jun 2025 05:30:15 -0600 Subject: [PATCH 38/86] fix: add missing check for device UUID in batch processing logs --- .../firebase/devices-status/services/devices-status.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/common/src/firebase/devices-status/services/devices-status.service.ts b/libs/common/src/firebase/devices-status/services/devices-status.service.ts index a4f7aad..3fae855 100644 --- a/libs/common/src/firebase/devices-status/services/devices-status.service.ts +++ b/libs/common/src/firebase/devices-status/services/devices-status.service.ts @@ -95,6 +95,7 @@ export class DeviceStatusFirebaseService { for (const item of batch) { const device = deviceMap.get(item.deviceTuyaUuid); if (!device?.uuid) continue; + const logs = item.log.properties.map((property) => this.deviceStatusLogRepository.create({ deviceId: device.uuid, From c0a069b4607e91459c07fb07e9d59f7b1a415b8c Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Wed, 25 Jun 2025 08:03:23 -0600 Subject: [PATCH 39/86] fix: enhance device status handling by integrating device cache for improved performance --- .../services/devices-status.service.ts | 105 ++++++++---------- libs/common/src/helper/helper.module.ts | 6 +- .../helper/services/sos.handler.service.ts | 48 +++++--- .../services/tuya.web.socket.service.ts | 53 +++++++-- 4 files changed, 128 insertions(+), 84 deletions(-) diff --git a/libs/common/src/firebase/devices-status/services/devices-status.service.ts b/libs/common/src/firebase/devices-status/services/devices-status.service.ts index 3fae855..3c431e3 100644 --- a/libs/common/src/firebase/devices-status/services/devices-status.service.ts +++ b/libs/common/src/firebase/devices-status/services/devices-status.service.ts @@ -69,32 +69,23 @@ export class DeviceStatusFirebaseService { } async addBatchDeviceStatusToOurDb( batch: { deviceTuyaUuid: string; status: any; log: any }[], + deviceCache: Map, ): Promise { const allLogs = []; - const deviceMap = new Map(); console.log( - `🧠 Starting device lookups for batch of ${batch.length} items...`, + `🧠 Preparing logs from batch of ${batch.length} items using cached devices only...`, ); - // Step 1: Parallel device fetching - await Promise.all( - batch.map(async (item) => { - if (!deviceMap.has(item.deviceTuyaUuid)) { - const device = await this.getDeviceByDeviceTuyaUuid( - item.deviceTuyaUuid, - ); - device?.uuid && deviceMap.set(item.deviceTuyaUuid, device); - } - }), - ); - - console.log(`🔍 Found ${deviceMap.size} devices from batch`); - - // Step 2: Prepare logs and updates for (const item of batch) { - const device = deviceMap.get(item.deviceTuyaUuid); - if (!device?.uuid) continue; + const device = deviceCache.get(item.deviceTuyaUuid); + + if (!device?.uuid) { + console.log( + `⛔ Ignored unknown device in batch: ${item.deviceTuyaUuid}`, + ); + continue; + } const logs = item.log.properties.map((property) => this.deviceStatusLogRepository.create({ @@ -112,59 +103,53 @@ export class DeviceStatusFirebaseService { } console.log(`📝 Total logs to insert: ${allLogs.length}`); - // Step 3: Insert logs in chunks with ON CONFLICT DO NOTHING - const insertLogsPromise = (async () => { - const chunkSize = 300; - let insertedCount = 0; + const chunkSize = 300; + let insertedCount = 0; - for (let i = 0; i < allLogs.length; i += chunkSize) { - const chunk = allLogs.slice(i, i + chunkSize); - try { - const result = await this.deviceStatusLogRepository - .createQueryBuilder() - .insert() - .into('device-status-log') // or use DeviceStatusLogEntity - .values(chunk) - .orIgnore() // skip duplicates - .execute(); + for (let i = 0; i < allLogs.length; i += chunkSize) { + const chunk = allLogs.slice(i, i + chunkSize); + try { + const result = await this.deviceStatusLogRepository + .createQueryBuilder() + .insert() + .into('device-status-log') // or use DeviceStatusLogEntity + .values(chunk) + .orIgnore() // skip duplicates + .execute(); - insertedCount += result.identifiers.length; - console.log( - `✅ Inserted ${result.identifiers.length} / ${chunk.length} logs (chunk)`, - ); - } catch (error) { - console.error('❌ Insert error (skipped chunk):', error.message); - } + insertedCount += result.identifiers.length; + console.log( + `✅ Inserted ${result.identifiers.length} / ${chunk.length} logs (chunk)`, + ); + } catch (error) { + console.error('❌ Insert error (skipped chunk):', error.message); } + } - console.log( - `✅ Total logs inserted: ${insertedCount} / ${allLogs.length}`, - ); - })(); - - // Step 5: Wait for both insert and post-processing to finish - await Promise.all([insertLogsPromise]); + console.log(`✅ Total logs inserted: ${insertedCount} / ${allLogs.length}`); } async addDeviceStatusToFirebase( addDeviceStatusDto: AddDeviceStatusDto, + deviceCache: Map, ): Promise { try { - const device = await this.getDeviceByDeviceTuyaUuid( - addDeviceStatusDto.deviceTuyaUuid, - ); - - if (device?.uuid) { - return await this.createDeviceStatusFirebase({ - deviceUuid: device.uuid, - ...addDeviceStatusDto, - productType: device.productDevice.prodType, - }); + const device = deviceCache.get(addDeviceStatusDto.deviceTuyaUuid); + if (!device?.uuid) { + console.log( + `⛔ Skipping Firebase update for unknown device: ${addDeviceStatusDto.deviceTuyaUuid}`, + ); + return null; } - // Return null if device not found or no UUID - return null; + + // Ensure product info and uuid are attached + addDeviceStatusDto.deviceUuid = device.uuid; + addDeviceStatusDto.productUuid = device.productDevice?.uuid; + addDeviceStatusDto.productType = device.productDevice?.prodType; + + return await this.createDeviceStatusFirebase(addDeviceStatusDto); } catch (error) { - // Handle the error silently, perhaps log it internally or ignore it + console.error('❌ Error in addDeviceStatusToFirebase:', error); return null; } } diff --git a/libs/common/src/helper/helper.module.ts b/libs/common/src/helper/helper.module.ts index df152e2..41992d3 100644 --- a/libs/common/src/helper/helper.module.ts +++ b/libs/common/src/helper/helper.module.ts @@ -8,7 +8,10 @@ import { TuyaWebSocketService } from './services/tuya.web.socket.service'; import { OneSignalService } from './services/onesignal.service'; import { DeviceMessagesService } from './services/device.messages.service'; import { DeviceRepositoryModule } from '../modules/device/device.repository.module'; -import { DeviceNotificationRepository } from '../modules/device/repositories'; +import { + DeviceNotificationRepository, + DeviceRepository, +} from '../modules/device/repositories'; import { DeviceStatusFirebaseModule } from '../firebase/devices-status/devices-status.module'; import { CommunityPermissionService } from './services/community.permission.service'; import { CommunityRepository } from '../modules/community/repositories'; @@ -27,6 +30,7 @@ import { SosHandlerService } from './services/sos.handler.service'; DeviceNotificationRepository, CommunityRepository, SosHandlerService, + DeviceRepository, ], exports: [ HelperHashService, diff --git a/libs/common/src/helper/services/sos.handler.service.ts b/libs/common/src/helper/services/sos.handler.service.ts index dd69f33..e883b62 100644 --- a/libs/common/src/helper/services/sos.handler.service.ts +++ b/libs/common/src/helper/services/sos.handler.service.ts @@ -16,35 +16,53 @@ export class SosHandlerService { ); } - async handleSosEventFirebase(devId: string, logData: any): Promise { + async handleSosEventFirebase( + devId: string, + logData: any, + deviceCache: Map, + ): Promise { try { - await this.deviceStatusFirebaseService.addDeviceStatusToFirebase({ - deviceTuyaUuid: devId, - status: [{ code: 'sos', value: true }], - log: logData, - }); - await this.deviceStatusFirebaseService.addBatchDeviceStatusToOurDb([ + await this.deviceStatusFirebaseService.addDeviceStatusToFirebase( { deviceTuyaUuid: devId, status: [{ code: 'sos', value: true }], log: logData, }, - ]); + deviceCache, + ); + + await this.deviceStatusFirebaseService.addBatchDeviceStatusToOurDb( + [ + { + deviceTuyaUuid: devId, + status: [{ code: 'sos', value: true }], + log: logData, + }, + ], + deviceCache, + ); setTimeout(async () => { try { - await this.deviceStatusFirebaseService.addDeviceStatusToFirebase({ - deviceTuyaUuid: devId, - status: [{ code: 'sos', value: false }], - log: logData, - }); - await this.deviceStatusFirebaseService.addBatchDeviceStatusToOurDb([ + await this.deviceStatusFirebaseService.addDeviceStatusToFirebase( { deviceTuyaUuid: devId, status: [{ code: 'sos', value: false }], log: logData, }, - ]); + deviceCache, + ); + + await this.deviceStatusFirebaseService.addBatchDeviceStatusToOurDb( + [ + { + deviceTuyaUuid: devId, + status: [{ code: 'sos', value: false }], + log: logData, + }, + ], + deviceCache, + ); } catch (err) { this.logger.error('Failed to send SOS false value', err); } diff --git a/libs/common/src/helper/services/tuya.web.socket.service.ts b/libs/common/src/helper/services/tuya.web.socket.service.ts index 9d56240..e0850be 100644 --- a/libs/common/src/helper/services/tuya.web.socket.service.ts +++ b/libs/common/src/helper/services/tuya.web.socket.service.ts @@ -3,6 +3,7 @@ import TuyaWebsocket from '../../config/tuya-web-socket-config'; import { ConfigService } from '@nestjs/config'; import { DeviceStatusFirebaseService } from '@app/common/firebase/devices-status/services/devices-status.service'; import { SosHandlerService } from './sos.handler.service'; +import { DeviceRepository } from '@app/common/modules/device/repositories'; @Injectable() export class TuyaWebSocketService { @@ -16,11 +17,13 @@ export class TuyaWebSocketService { }[] = []; private isProcessing = false; + private deviceCache: Map = new Map(); constructor( private readonly configService: ConfigService, private readonly deviceStatusFirebaseService: DeviceStatusFirebaseService, private readonly sosHandlerService: SosHandlerService, + private readonly deviceRepository: DeviceRepository, ) { this.isDevEnv = this.configService.get('NODE_ENV') === 'development'; @@ -33,6 +36,11 @@ export class TuyaWebSocketService { maxRetryTimes: 100, }); + this.loadAllActiveDevices(); + + // Reload device cache every 1 hour + setInterval(() => this.loadAllActiveDevices(), 60 * 60 * 1000); + if (this.configService.get('tuya-config.TRUN_ON_TUYA_SOCKET')) { this.setupEventHandlers(); this.client.start(); @@ -42,6 +50,22 @@ export class TuyaWebSocketService { setInterval(() => this.processQueue(), 15000); } + private async loadAllActiveDevices(): Promise { + const devices = await this.deviceRepository.find({ + where: { isActive: true }, + relations: ['productDevice'], + }); + + this.deviceCache.clear(); + devices.forEach((device) => { + this.deviceCache.set(device.deviceTuyaUuid, device); + }); + + console.log( + `🔄 Device cache reloaded: ${this.deviceCache.size} active devices at ${new Date().toISOString()}`, + ); + } + private setupEventHandlers() { // Event handlers this.client.open(() => { @@ -51,18 +75,30 @@ export class TuyaWebSocketService { this.client.message(async (ws: WebSocket, message: any) => { try { const { devId, status, logData } = this.extractMessageData(message); - if (!Array.isArray(logData?.properties)) { + if (!Array.isArray(logData?.properties)) return; + + const device = this.deviceCache.get(devId); + if (!device) { + // console.log(`⛔ Ignored unknown device: ${devId}`); return; } + if (this.sosHandlerService.isSosTriggered(status)) { - await this.sosHandlerService.handleSosEventFirebase(devId, logData); + await this.sosHandlerService.handleSosEventFirebase( + devId, + logData, + this.deviceCache, + ); } else { // Firebase real-time update - await this.deviceStatusFirebaseService.addDeviceStatusToFirebase({ - deviceTuyaUuid: devId, - status: status, - log: logData, - }); + await this.deviceStatusFirebaseService.addDeviceStatusToFirebase( + { + deviceTuyaUuid: devId, + status, + log: logData, + }, + this.deviceCache, + ); } // Push to internal queue @@ -111,11 +147,12 @@ export class TuyaWebSocketService { try { await this.deviceStatusFirebaseService.addBatchDeviceStatusToOurDb( - batch?.map((item) => ({ + batch.map((item) => ({ deviceTuyaUuid: item.devId, status: item.status, log: item.logData, })), + this.deviceCache, ); } catch (error) { console.error('❌ Error processing batch:', error); From 68d2d3b53dc9dc1afb227213a17d60c06632839f Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Wed, 25 Jun 2025 08:13:02 -0600 Subject: [PATCH 40/86] fix: improve device retrieval logic in addDeviceStatusToFirebase method --- .../services/devices-status.service.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/libs/common/src/firebase/devices-status/services/devices-status.service.ts b/libs/common/src/firebase/devices-status/services/devices-status.service.ts index 3c431e3..6f83d99 100644 --- a/libs/common/src/firebase/devices-status/services/devices-status.service.ts +++ b/libs/common/src/firebase/devices-status/services/devices-status.service.ts @@ -131,10 +131,19 @@ export class DeviceStatusFirebaseService { async addDeviceStatusToFirebase( addDeviceStatusDto: AddDeviceStatusDto, - deviceCache: Map, + deviceCache?: Map, ): Promise { try { - const device = deviceCache.get(addDeviceStatusDto.deviceTuyaUuid); + let device; + + if (deviceCache) { + device = deviceCache.get(addDeviceStatusDto.deviceTuyaUuid); + } else { + device = await this.getDeviceByDeviceTuyaUuid( + addDeviceStatusDto.deviceTuyaUuid, + ); + } + if (!device?.uuid) { console.log( `⛔ Skipping Firebase update for unknown device: ${addDeviceStatusDto.deviceTuyaUuid}`, @@ -142,7 +151,6 @@ export class DeviceStatusFirebaseService { return null; } - // Ensure product info and uuid are attached addDeviceStatusDto.deviceUuid = device.uuid; addDeviceStatusDto.productUuid = device.productDevice?.uuid; addDeviceStatusDto.productType = device.productDevice?.prodType; From 731819aeaa7dbaccfaaf2879e3480616746384f9 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Wed, 25 Jun 2025 18:37:46 -0600 Subject: [PATCH 41/86] feat: enhance device status handling with caching and batch processing improvements --- .../services/devices-status.service.ts | 60 +++++++++---------- .../helper/services/sos.handler.service.ts | 29 ++++++--- .../services/tuya.web.socket.service.ts | 46 ++++++++++++-- package-lock.json | 22 +++++++ package.json | 1 + 5 files changed, 113 insertions(+), 45 deletions(-) diff --git a/libs/common/src/firebase/devices-status/services/devices-status.service.ts b/libs/common/src/firebase/devices-status/services/devices-status.service.ts index 3fae855..6560276 100644 --- a/libs/common/src/firebase/devices-status/services/devices-status.service.ts +++ b/libs/common/src/firebase/devices-status/services/devices-status.service.ts @@ -68,33 +68,23 @@ export class DeviceStatusFirebaseService { } } async addBatchDeviceStatusToOurDb( - batch: { deviceTuyaUuid: string; status: any; log: any }[], + batch: { + deviceTuyaUuid: string; + status: any; + log: any; + device: any; + }[], ): Promise { const allLogs = []; - const deviceMap = new Map(); - console.log( - `🧠 Starting device lookups for batch of ${batch.length} items...`, - ); + console.log(`🔁 Preparing logs from batch of ${batch.length} items...`); - // Step 1: Parallel device fetching - await Promise.all( - batch.map(async (item) => { - if (!deviceMap.has(item.deviceTuyaUuid)) { - const device = await this.getDeviceByDeviceTuyaUuid( - item.deviceTuyaUuid, - ); - device?.uuid && deviceMap.set(item.deviceTuyaUuid, device); - } - }), - ); - - console.log(`🔍 Found ${deviceMap.size} devices from batch`); - - // Step 2: Prepare logs and updates for (const item of batch) { - const device = deviceMap.get(item.deviceTuyaUuid); - if (!device?.uuid) continue; + const device = item.device; + if (!device?.uuid) { + console.log(`⛔ Skipped unknown device: ${item.deviceTuyaUuid}`); + continue; + } const logs = item.log.properties.map((property) => this.deviceStatusLogRepository.create({ @@ -142,23 +132,24 @@ export class DeviceStatusFirebaseService { ); })(); - // Step 5: Wait for both insert and post-processing to finish - await Promise.all([insertLogsPromise]); + await insertLogsPromise; } async addDeviceStatusToFirebase( - addDeviceStatusDto: AddDeviceStatusDto, + addDeviceStatusDto: AddDeviceStatusDto & { device?: any }, ): Promise { try { - const device = await this.getDeviceByDeviceTuyaUuid( - addDeviceStatusDto.deviceTuyaUuid, - ); - + let device = addDeviceStatusDto.device; + if (!device) { + device = await this.getDeviceByDeviceTuyaUuid( + addDeviceStatusDto.deviceTuyaUuid, + ); + } if (device?.uuid) { return await this.createDeviceStatusFirebase({ deviceUuid: device.uuid, ...addDeviceStatusDto, - productType: device.productDevice.prodType, + productType: device.productDevice?.prodType, }); } // Return null if device not found or no UUID @@ -178,6 +169,15 @@ export class DeviceStatusFirebaseService { relations: ['productDevice'], }); } + async getAllDevices() { + return await this.deviceRepository.find({ + where: { + isActive: true, + }, + relations: ['productDevice'], + }); + } + async getDevicesInstructionStatus(deviceUuid: string) { try { const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid); diff --git a/libs/common/src/helper/services/sos.handler.service.ts b/libs/common/src/helper/services/sos.handler.service.ts index dd69f33..55a0daf 100644 --- a/libs/common/src/helper/services/sos.handler.service.ts +++ b/libs/common/src/helper/services/sos.handler.service.ts @@ -16,33 +16,44 @@ export class SosHandlerService { ); } - async handleSosEventFirebase(devId: string, logData: any): Promise { + async handleSosEventFirebase(device: any, logData: any): Promise { + const sosTrueStatus = [{ code: 'sos', value: true }]; + const sosFalseStatus = [{ code: 'sos', value: false }]; + try { + // ✅ Send true status await this.deviceStatusFirebaseService.addDeviceStatusToFirebase({ - deviceTuyaUuid: devId, - status: [{ code: 'sos', value: true }], + deviceTuyaUuid: device.deviceTuyaUuid, + status: sosTrueStatus, log: logData, + device, }); + await this.deviceStatusFirebaseService.addBatchDeviceStatusToOurDb([ { - deviceTuyaUuid: devId, - status: [{ code: 'sos', value: true }], + deviceTuyaUuid: device.deviceTuyaUuid, + status: sosTrueStatus, log: logData, + device, }, ]); + // ✅ Schedule false status setTimeout(async () => { try { await this.deviceStatusFirebaseService.addDeviceStatusToFirebase({ - deviceTuyaUuid: devId, - status: [{ code: 'sos', value: false }], + deviceTuyaUuid: device.deviceTuyaUuid, + status: sosFalseStatus, log: logData, + device, }); + await this.deviceStatusFirebaseService.addBatchDeviceStatusToOurDb([ { - deviceTuyaUuid: devId, - status: [{ code: 'sos', value: false }], + deviceTuyaUuid: device.deviceTuyaUuid, + status: sosFalseStatus, log: logData, + device, }, ]); } catch (err) { diff --git a/libs/common/src/helper/services/tuya.web.socket.service.ts b/libs/common/src/helper/services/tuya.web.socket.service.ts index 9d56240..be32b1d 100644 --- a/libs/common/src/helper/services/tuya.web.socket.service.ts +++ b/libs/common/src/helper/services/tuya.web.socket.service.ts @@ -1,18 +1,21 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, OnModuleInit } from '@nestjs/common'; import TuyaWebsocket from '../../config/tuya-web-socket-config'; import { ConfigService } from '@nestjs/config'; import { DeviceStatusFirebaseService } from '@app/common/firebase/devices-status/services/devices-status.service'; import { SosHandlerService } from './sos.handler.service'; +import * as NodeCache from 'node-cache'; @Injectable() -export class TuyaWebSocketService { +export class TuyaWebSocketService implements OnModuleInit { private client: any; private readonly isDevEnv: boolean; + private readonly deviceCache = new NodeCache({ stdTTL: 7200 }); // TTL = 2 hour private messageQueue: { devId: string; status: any; logData: any; + device: any; }[] = []; private isProcessing = false; @@ -38,8 +41,29 @@ export class TuyaWebSocketService { this.client.start(); } - // Trigger the queue processor every 15 seconds + // Run the queue processor every 15 seconds setInterval(() => this.processQueue(), 15000); + + // Refresh the cache every 1 hour + setInterval(() => this.initializeDeviceCache(), 30 * 60 * 1000); // 30 minutes + } + + async onModuleInit() { + await this.initializeDeviceCache(); + } + + private async initializeDeviceCache() { + try { + const allDevices = await this.deviceStatusFirebaseService.getAllDevices(); + allDevices.forEach((device) => { + if (device.deviceTuyaUuid) { + this.deviceCache.set(device.deviceTuyaUuid, device); + } + }); + console.log(`✅ Refreshed cache with ${allDevices.length} devices.`); + } catch (error) { + console.error('❌ Failed to initialize device cache:', error); + } } private setupEventHandlers() { @@ -52,6 +76,14 @@ export class TuyaWebSocketService { try { const { devId, status, logData } = this.extractMessageData(message); if (!Array.isArray(logData?.properties)) { + this.client.ackMessage(message.messageId); + return; + } + + const device = this.deviceCache.get(devId); + if (!device) { + // console.warn(`⚠️ Device not found in cache: ${devId}`); + this.client.ackMessage(message.messageId); return; } if (this.sosHandlerService.isSosTriggered(status)) { @@ -60,13 +92,14 @@ export class TuyaWebSocketService { // Firebase real-time update await this.deviceStatusFirebaseService.addDeviceStatusToFirebase({ deviceTuyaUuid: devId, - status: status, + status, log: logData, + device, }); } // Push to internal queue - this.messageQueue.push({ devId, status, logData }); + this.messageQueue.push({ devId, status, logData, device }); // Acknowledge the message this.client.ackMessage(message.messageId); @@ -111,10 +144,11 @@ export class TuyaWebSocketService { try { await this.deviceStatusFirebaseService.addBatchDeviceStatusToOurDb( - batch?.map((item) => ({ + batch.map((item) => ({ deviceTuyaUuid: item.devId, status: item.status, log: item.logData, + device: item.device, })), ); } catch (error) { diff --git a/package-lock.json b/package-lock.json index e3305e5..e8718b3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,6 +39,7 @@ "ioredis": "^5.3.2", "morgan": "^1.10.0", "nest-winston": "^1.10.2", + "node-cache": "^5.1.2", "nodemailer": "^6.9.10", "onesignal-node": "^3.4.0", "passport-jwt": "^4.0.1", @@ -10184,6 +10185,27 @@ "node": "^18 || ^20 || >= 21" } }, + "node_modules/node-cache": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz", + "integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==", + "license": "MIT", + "dependencies": { + "clone": "2.x" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/node-cache/node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, "node_modules/node-emoji": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", diff --git a/package.json b/package.json index 55d546b..6e16079 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "ioredis": "^5.3.2", "morgan": "^1.10.0", "nest-winston": "^1.10.2", + "node-cache": "^5.1.2", "nodemailer": "^6.9.10", "onesignal-node": "^3.4.0", "passport-jwt": "^4.0.1", From f80d097ff88b882e1b82b9dd5603289b75357f7e Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Wed, 25 Jun 2025 18:57:56 -0600 Subject: [PATCH 42/86] refactor: optimize log insertion and clean up device cache handling in TuyaWebSocketService --- .../services/devices-status.service.ts | 41 ++++++++++--------- .../services/tuya.web.socket.service.ts | 22 ++-------- 2 files changed, 24 insertions(+), 39 deletions(-) diff --git a/libs/common/src/firebase/devices-status/services/devices-status.service.ts b/libs/common/src/firebase/devices-status/services/devices-status.service.ts index 3972b2a..b3ef843 100644 --- a/libs/common/src/firebase/devices-status/services/devices-status.service.ts +++ b/libs/common/src/firebase/devices-status/services/devices-status.service.ts @@ -102,28 +102,30 @@ export class DeviceStatusFirebaseService { } console.log(`📝 Total logs to insert: ${allLogs.length}`); - const chunkSize = 300; - let insertedCount = 0; - for (let i = 0; i < allLogs.length; i += chunkSize) { - const chunk = allLogs.slice(i, i + chunkSize); - try { - const result = await this.deviceStatusLogRepository - .createQueryBuilder() - .insert() - .into('device-status-log') // or use DeviceStatusLogEntity - .values(chunk) - .orIgnore() // skip duplicates - .execute(); + const insertLogsPromise = (async () => { + const chunkSize = 300; + let insertedCount = 0; - insertedCount += result.identifiers.length; - console.log( - `✅ Inserted ${result.identifiers.length} / ${chunk.length} logs (chunk)`, - ); - } catch (error) { - console.error('❌ Insert error (skipped chunk):', error.message); + for (let i = 0; i < allLogs.length; i += chunkSize) { + const chunk = allLogs.slice(i, i + chunkSize); + try { + const result = await this.deviceStatusLogRepository + .createQueryBuilder() + .insert() + .into('device-status-log') // or use DeviceStatusLogEntity + .values(chunk) + .orIgnore() // skip duplicates + .execute(); + + insertedCount += result.identifiers.length; + console.log( + `✅ Inserted ${result.identifiers.length} / ${chunk.length} logs (chunk)`, + ); + } catch (error) { + console.error('❌ Insert error (skipped chunk):', error.message); + } } - } console.log( `✅ Total logs inserted: ${insertedCount} / ${allLogs.length}`, @@ -153,7 +155,6 @@ export class DeviceStatusFirebaseService { // Return null if device not found or no UUID return null; } catch (error) { - console.error('❌ Error in addDeviceStatusToFirebase:', error); return null; } } diff --git a/libs/common/src/helper/services/tuya.web.socket.service.ts b/libs/common/src/helper/services/tuya.web.socket.service.ts index 63de80b..d30200f 100644 --- a/libs/common/src/helper/services/tuya.web.socket.service.ts +++ b/libs/common/src/helper/services/tuya.web.socket.service.ts @@ -19,13 +19,11 @@ export class TuyaWebSocketService implements OnModuleInit { }[] = []; private isProcessing = false; - private deviceCache: Map = new Map(); constructor( private readonly configService: ConfigService, private readonly deviceStatusFirebaseService: DeviceStatusFirebaseService, private readonly sosHandlerService: SosHandlerService, - private readonly deviceRepository: DeviceRepository, ) { this.isDevEnv = this.configService.get('NODE_ENV') === 'development'; @@ -38,11 +36,6 @@ export class TuyaWebSocketService implements OnModuleInit { maxRetryTimes: 100, }); - this.loadAllActiveDevices(); - - // Reload device cache every 1 hour - setInterval(() => this.loadAllActiveDevices(), 60 * 60 * 1000); - if (this.configService.get('tuya-config.TRUN_ON_TUYA_SOCKET')) { this.setupEventHandlers(); this.client.start(); @@ -74,7 +67,6 @@ export class TuyaWebSocketService implements OnModuleInit { } private setupEventHandlers() { - // Event handlers this.client.open(() => { console.log('open'); }); @@ -89,19 +81,14 @@ export class TuyaWebSocketService implements OnModuleInit { const device = this.deviceCache.get(devId); if (!device) { - // console.warn(`⚠️ Device not found in cache: ${devId}`); + // console.log(⛔ Unknown device: ${devId}, message ignored.); this.client.ackMessage(message.messageId); return; } if (this.sosHandlerService.isSosTriggered(status)) { - await this.sosHandlerService.handleSosEventFirebase( - devId, - logData, - this.deviceCache, - ); + await this.sosHandlerService.handleSosEventFirebase(devId, logData); } else { - // Firebase real-time update await this.deviceStatusFirebaseService.addDeviceStatusToFirebase({ deviceTuyaUuid: devId, status, @@ -116,10 +103,9 @@ export class TuyaWebSocketService implements OnModuleInit { // Acknowledge the message this.client.ackMessage(message.messageId); } catch (error) { - console.error('Error receiving message:', error); + console.error('❌ Error receiving message:', error); } }); - this.client.reconnect(() => { console.log('reconnect'); }); @@ -156,14 +142,12 @@ export class TuyaWebSocketService implements OnModuleInit { try { await this.deviceStatusFirebaseService.addBatchDeviceStatusToOurDb( - batch.map((item) => ({ batch.map((item) => ({ deviceTuyaUuid: item.devId, status: item.status, log: item.logData, device: item.device, })), - this.deviceCache, ); } catch (error) { console.error('❌ Error processing batch:', error); From c7a4ff119457d4bd749c7d62650dd46528da838a Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Sun, 29 Jun 2025 15:27:55 +0300 Subject: [PATCH 43/86] fix: schedule device types (#441) --- src/schedule/services/schedule.service.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/schedule/services/schedule.service.ts b/src/schedule/services/schedule.service.ts index e4bd586..d88ee20 100644 --- a/src/schedule/services/schedule.service.ts +++ b/src/schedule/services/schedule.service.ts @@ -50,7 +50,7 @@ export class ScheduleService { // Corrected condition for supported device types this.ensureProductTypeSupportedForSchedule( - ProductType[deviceDetails.productDevice.prodType], + deviceDetails.productDevice.prodType as ProductType, ); return this.enableScheduleDeviceInTuya( @@ -74,7 +74,7 @@ export class ScheduleService { // Corrected condition for supported device types this.ensureProductTypeSupportedForSchedule( - ProductType[deviceDetails.productDevice.prodType], + deviceDetails.productDevice.prodType as ProductType, ); return await this.deleteScheduleDeviceInTuya( @@ -97,7 +97,7 @@ export class ScheduleService { } this.ensureProductTypeSupportedForSchedule( - ProductType[deviceDetails.productDevice.prodType], + deviceDetails.productDevice.prodType as ProductType, ); await this.addScheduleDeviceInTuya( @@ -120,9 +120,8 @@ export class ScheduleService { } // Corrected condition for supported device types this.ensureProductTypeSupportedForSchedule( - ProductType[deviceDetails.productDevice.prodType], + deviceDetails.productDevice.prodType as ProductType, ); - const schedules = await this.getScheduleDeviceInTuya( deviceDetails.deviceTuyaUuid, category, @@ -162,7 +161,7 @@ export class ScheduleService { // Corrected condition for supported device types this.ensureProductTypeSupportedForSchedule( - ProductType[deviceDetails.productDevice.prodType], + deviceDetails.productDevice.prodType as ProductType, ); await this.updateScheduleDeviceInTuya( From 82c82d521c88648db3220ba29a99c6de7e50ec31 Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Mon, 30 Jun 2025 08:57:43 +0300 Subject: [PATCH 44/86] add deviceName to handle password API (#442) --- .../services/visitor-password.service.ts | 239 +++++++----------- 1 file changed, 93 insertions(+), 146 deletions(-) diff --git a/src/vistor-password/services/visitor-password.service.ts b/src/vistor-password/services/visitor-password.service.ts index 0bc1b02..d7ce937 100644 --- a/src/vistor-password/services/visitor-password.service.ts +++ b/src/vistor-password/services/visitor-password.service.ts @@ -1,39 +1,39 @@ -import { VisitorPasswordRepository } from './../../../libs/common/src/modules/visitor-password/repositories/visitor-password.repository'; +import { ProductType } from '@app/common/constants/product-type.enum'; +import { DeviceRepository } from '@app/common/modules/device/repositories'; import { - Injectable, + BadRequestException, HttpException, HttpStatus, - BadRequestException, + Injectable, } from '@nestjs/common'; -import { TuyaContext } from '@tuya/tuya-connector-nodejs'; import { ConfigService } from '@nestjs/config'; +import { TuyaContext } from '@tuya/tuya-connector-nodejs'; import { addDeviceObjectInterface, createTickInterface, } from '../interfaces/visitor-password.interface'; -import { DeviceRepository } from '@app/common/modules/device/repositories'; -import { ProductType } from '@app/common/constants/product-type.enum'; +import { VisitorPasswordRepository } from './../../../libs/common/src/modules/visitor-password/repositories/visitor-password.repository'; -import { AddDoorLockTemporaryPasswordDto } from '../dtos'; -import { EmailService } from '@app/common/util/email.service'; -import { PasswordEncryptionService } from 'src/door-lock/services/encryption.services'; -import { DoorLockService } from 'src/door-lock/services'; -import { DeviceService } from 'src/device/services'; -import { DeviceStatuses } from '@app/common/constants/device-status.enum'; import { DaysEnum, EnableDisableStatusEnum, } from '@app/common/constants/days.enum'; -import { PasswordType } from '@app/common/constants/password-type.enum'; +import { DeviceStatuses } from '@app/common/constants/device-status.enum'; import { CommonHourMinutes, CommonHours, } from '@app/common/constants/hours-minutes.enum'; -import { ProjectRepository } from '@app/common/modules/project/repositiories'; +import { ORPHAN_SPACE_NAME } from '@app/common/constants/orphan-constant'; +import { PasswordType } from '@app/common/constants/password-type.enum'; import { VisitorPasswordEnum } from '@app/common/constants/visitor-password.enum'; import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; +import { ProjectRepository } from '@app/common/modules/project/repositiories'; +import { EmailService } from '@app/common/util/email.service'; +import { DeviceService } from 'src/device/services'; +import { DoorLockService } from 'src/door-lock/services'; +import { PasswordEncryptionService } from 'src/door-lock/services/encryption.services'; import { Not } from 'typeorm'; -import { ORPHAN_SPACE_NAME } from '@app/common/constants/orphan-constant'; +import { AddDoorLockTemporaryPasswordDto } from '../dtos'; @Injectable() export class VisitorPasswordService { @@ -57,6 +57,67 @@ export class VisitorPasswordService { secretKey, }); } + + async getPasswords(projectUuid: string) { + await this.validateProject(projectUuid); + + const deviceIds = await this.deviceRepository.find({ + where: { + productDevice: { + prodType: ProductType.DL, + }, + spaceDevice: { + spaceName: Not(ORPHAN_SPACE_NAME), + community: { + project: { + uuid: projectUuid, + }, + }, + }, + isActive: true, + }, + }); + + const data = []; + deviceIds.forEach((deviceId) => { + data.push( + this.doorLockService + .getOnlineTemporaryPasswordsOneTime(deviceId.uuid, true, false) + .catch(() => {}), + this.doorLockService + .getOnlineTemporaryPasswordsOneTime(deviceId.uuid, true, true) + .catch(() => {}), + this.doorLockService + .getOnlineTemporaryPasswordsMultiple(deviceId.uuid, true, false) + .catch(() => {}), + this.doorLockService + .getOnlineTemporaryPasswordsMultiple(deviceId.uuid, true, true) + .catch(() => {}), + this.doorLockService + .getOfflineOneTimeTemporaryPasswords(deviceId.uuid, true, false) + .catch(() => {}), + this.doorLockService + .getOfflineOneTimeTemporaryPasswords(deviceId.uuid, true, true) + .catch(() => {}), + this.doorLockService + .getOfflineMultipleTimeTemporaryPasswords(deviceId.uuid, true, false) + .catch(() => {}), + this.doorLockService + .getOfflineMultipleTimeTemporaryPasswords(deviceId.uuid, true, true) + .catch(() => {}), + ); + }); + const result = (await Promise.all(data)).flat().filter((datum) => { + return datum != null; + }); + + return new SuccessResponseDto({ + message: 'Successfully retrieved temporary passwords', + data: result, + statusCode: HttpStatus.OK, + }); + } + async handleTemporaryPassword( addDoorLockTemporaryPasswordDto: AddDoorLockTemporaryPasswordDto, userUuid: string, @@ -105,7 +166,7 @@ export class VisitorPasswordService { statusCode: HttpStatus.CREATED, }); } - async addOfflineMultipleTimeTemporaryPassword( + private async addOfflineMultipleTimeTemporaryPassword( addDoorLockOfflineMultipleDto: AddDoorLockTemporaryPasswordDto, userUuid: string, projectUuid: string, @@ -169,6 +230,7 @@ export class VisitorPasswordService { success: true, result: createMultipleOfflinePass.result, deviceUuid, + deviceName: deviceDetails.name, }; } catch (error) { return { @@ -231,7 +293,7 @@ export class VisitorPasswordService { } } - async addOfflineOneTimeTemporaryPassword( + private async addOfflineOneTimeTemporaryPassword( addDoorLockOfflineOneTimeDto: AddDoorLockTemporaryPasswordDto, userUuid: string, projectUuid: string, @@ -295,6 +357,7 @@ export class VisitorPasswordService { success: true, result: createOnceOfflinePass.result, deviceUuid, + deviceName: deviceDetails.name, }; } catch (error) { return { @@ -357,7 +420,7 @@ export class VisitorPasswordService { } } - async addOfflineTemporaryPasswordTuya( + private async addOfflineTemporaryPasswordTuya( doorLockUuid: string, type: string, addDoorLockOfflineMultipleDto: AddDoorLockTemporaryPasswordDto, @@ -387,7 +450,7 @@ export class VisitorPasswordService { ); } } - async addOnlineTemporaryPasswordMultipleTime( + private async addOnlineTemporaryPasswordMultipleTime( addDoorLockOnlineMultipleDto: AddDoorLockTemporaryPasswordDto, userUuid: string, projectUuid: string, @@ -448,6 +511,7 @@ export class VisitorPasswordService { success: true, id: createPass.result.id, deviceUuid, + deviceName: passwordData.deviceName, }; } catch (error) { return { @@ -508,67 +572,8 @@ export class VisitorPasswordService { ); } } - async getPasswords(projectUuid: string) { - await this.validateProject(projectUuid); - const deviceIds = await this.deviceRepository.find({ - where: { - productDevice: { - prodType: ProductType.DL, - }, - spaceDevice: { - spaceName: Not(ORPHAN_SPACE_NAME), - community: { - project: { - uuid: projectUuid, - }, - }, - }, - isActive: true, - }, - }); - - const data = []; - deviceIds.forEach((deviceId) => { - data.push( - this.doorLockService - .getOnlineTemporaryPasswordsOneTime(deviceId.uuid, true, false) - .catch(() => {}), - this.doorLockService - .getOnlineTemporaryPasswordsOneTime(deviceId.uuid, true, true) - .catch(() => {}), - this.doorLockService - .getOnlineTemporaryPasswordsMultiple(deviceId.uuid, true, false) - .catch(() => {}), - this.doorLockService - .getOnlineTemporaryPasswordsMultiple(deviceId.uuid, true, true) - .catch(() => {}), - this.doorLockService - .getOfflineOneTimeTemporaryPasswords(deviceId.uuid, true, false) - .catch(() => {}), - this.doorLockService - .getOfflineOneTimeTemporaryPasswords(deviceId.uuid, true, true) - .catch(() => {}), - this.doorLockService - .getOfflineMultipleTimeTemporaryPasswords(deviceId.uuid, true, false) - .catch(() => {}), - this.doorLockService - .getOfflineMultipleTimeTemporaryPasswords(deviceId.uuid, true, true) - .catch(() => {}), - ); - }); - const result = (await Promise.all(data)).flat().filter((datum) => { - return datum != null; - }); - - return new SuccessResponseDto({ - message: 'Successfully retrieved temporary passwords', - data: result, - statusCode: HttpStatus.OK, - }); - } - - async addOnlineTemporaryPasswordOneTime( + private async addOnlineTemporaryPasswordOneTime( addDoorLockOnlineOneTimeDto: AddDoorLockTemporaryPasswordDto, userUuid: string, projectUuid: string, @@ -627,6 +632,7 @@ export class VisitorPasswordService { return { success: true, id: createPass.result.id, + deviceName: passwordData.deviceName, deviceUuid, }; } catch (error) { @@ -688,7 +694,7 @@ export class VisitorPasswordService { ); } } - async getTicketAndEncryptedPassword( + private async getTicketAndEncryptedPassword( doorLockUuid: string, passwordPlan: string, projectUuid: string, @@ -725,6 +731,7 @@ export class VisitorPasswordService { ticketKey: ticketDetails.result.ticket_key, encryptedPassword: decrypted, deviceTuyaUuid: deviceDetails.deviceTuyaUuid, + deviceName: deviceDetails.name, }; } catch (error) { throw new HttpException( @@ -734,7 +741,7 @@ export class VisitorPasswordService { } } - async createDoorLockTicketTuya( + private async createDoorLockTicketTuya( deviceUuid: string, ): Promise { try { @@ -753,7 +760,7 @@ export class VisitorPasswordService { } } - async addOnlineTemporaryPasswordMultipleTuya( + private async addOnlineTemporaryPasswordMultipleTuya( addDeviceObj: addDeviceObjectInterface, doorLockUuid: string, ): Promise { @@ -795,7 +802,7 @@ export class VisitorPasswordService { } } - getWorkingDayValue(days) { + private getWorkingDayValue(days) { // Array representing the days of the week const weekDays = [ DaysEnum.SAT, @@ -827,36 +834,7 @@ export class VisitorPasswordService { return workingDayValue; } - getDaysFromWorkingDayValue(workingDayValue) { - // Array representing the days of the week - const weekDays = [ - DaysEnum.SAT, - DaysEnum.FRI, - DaysEnum.THU, - DaysEnum.WED, - DaysEnum.TUE, - DaysEnum.MON, - DaysEnum.SUN, - ]; - - // Convert the integer to a binary string and pad with leading zeros to ensure 7 bits - const binaryString = workingDayValue - .toString(2) - .padStart(7, EnableDisableStatusEnum.DISABLED); - - // Initialize an array to hold the days of the week - const days = []; - - // Iterate through the binary string and weekDays array - for (let i = 0; i < binaryString.length; i++) { - if (binaryString[i] === EnableDisableStatusEnum.ENABLED) { - days.push(weekDays[i]); - } - } - - return days; - } - timeToMinutes(timeStr) { + private timeToMinutes(timeStr) { try { // Special case for "24:00" if (timeStr === CommonHours.TWENTY_FOUR) { @@ -883,38 +861,7 @@ export class VisitorPasswordService { throw new HttpException(error.message, HttpStatus.INTERNAL_SERVER_ERROR); } } - minutesToTime(totalMinutes) { - try { - if ( - typeof totalMinutes !== 'number' || - totalMinutes < 0 || - totalMinutes > CommonHourMinutes.TWENTY_FOUR - ) { - throw new Error('Invalid minutes value'); - } - - if (totalMinutes === CommonHourMinutes.TWENTY_FOUR) { - return CommonHours.TWENTY_FOUR; - } - - const hours = Math.floor(totalMinutes / 60); - const minutes = totalMinutes % 60; - - const formattedHours = String(hours).padStart( - 2, - EnableDisableStatusEnum.DISABLED, - ); - const formattedMinutes = String(minutes).padStart( - 2, - EnableDisableStatusEnum.DISABLED, - ); - - return `${formattedHours}:${formattedMinutes}`; - } catch (error) { - throw new HttpException(error.message, HttpStatus.INTERNAL_SERVER_ERROR); - } - } - async getDeviceByDeviceUuid( + private async getDeviceByDeviceUuid( deviceUuid: string, withProductDevice: boolean = true, projectUuid: string, @@ -939,7 +886,7 @@ export class VisitorPasswordService { throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); } } - async addOnlineTemporaryPasswordOneTimeTuya( + private async addOnlineTemporaryPasswordOneTimeTuya( addDeviceObj: addDeviceObjectInterface, doorLockUuid: string, ): Promise { From f4f7999ae0a1d2a4c278e2600e23e896ed36c7e3 Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Mon, 30 Jun 2025 09:48:16 +0300 Subject: [PATCH 45/86] add device to firebase & stop moving it from the OEM space (#443) --- src/commission-device/services/commission-device.service.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/commission-device/services/commission-device.service.ts b/src/commission-device/services/commission-device.service.ts index ac2aae5..88d6a03 100644 --- a/src/commission-device/services/commission-device.service.ts +++ b/src/commission-device/services/commission-device.service.ts @@ -3,6 +3,7 @@ import * as fs from 'fs'; import { ProjectParam } from '@app/common/dto/project-param.dto'; import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; +import { DeviceStatusFirebaseService } from '@app/common/firebase/devices-status/services/devices-status.service'; import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service'; import { CommunityRepository } from '@app/common/modules/community/repositories'; import { DeviceRepository } from '@app/common/modules/device/repositories'; @@ -20,6 +21,7 @@ export class DeviceCommissionService { constructor( private readonly tuyaService: TuyaService, private readonly deviceService: DeviceService, + private readonly deviceStatusFirebaseService: DeviceStatusFirebaseService, private readonly communityRepository: CommunityRepository, private readonly spaceRepository: SpaceRepository, private readonly subspaceRepository: SubspaceRepository, @@ -209,6 +211,10 @@ export class DeviceCommissionService { rawDeviceId, tuyaSpaceId, ); + + await this.deviceStatusFirebaseService.addDeviceStatusByDeviceUuid( + rawDeviceId, + ); successCount.value++; console.log( `Device ${rawDeviceId} successfully processed and transferred to Tuya space ${tuyaSpaceId}`, From f4e748d73561b65592bf0431e40de9be8315a060 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Mon, 30 Jun 2025 00:58:30 -0600 Subject: [PATCH 46/86] fix: update role type formatting in user invitation email --- src/invite-user/services/invite-user.service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/invite-user/services/invite-user.service.ts b/src/invite-user/services/invite-user.service.ts index 0cf8e42..4042663 100644 --- a/src/invite-user/services/invite-user.service.ts +++ b/src/invite-user/services/invite-user.service.ts @@ -111,6 +111,7 @@ export class InviteUserService { }); const invitedUser = await queryRunner.manager.save(inviteUser); + const invitedRoleType = await this.getRoleTypeByUuid(roleUuid); // Link user to spaces const spacePromises = validSpaces.map(async (space) => { @@ -128,7 +129,7 @@ export class InviteUserService { await this.emailService.sendEmailWithInvitationTemplate(email, { name: firstName, invitationCode, - role: roleType, + role: invitedRoleType.replace(/_/g, ' '), spacesList: spaceNames, }); From c9d794d98803167dde66984e51eeb52e446bc8d1 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Mon, 30 Jun 2025 01:25:09 -0600 Subject: [PATCH 47/86] fix: update role type formatting in user invitation email --- src/invite-user/services/invite-user.service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/invite-user/services/invite-user.service.ts b/src/invite-user/services/invite-user.service.ts index 0cf8e42..4042663 100644 --- a/src/invite-user/services/invite-user.service.ts +++ b/src/invite-user/services/invite-user.service.ts @@ -111,6 +111,7 @@ export class InviteUserService { }); const invitedUser = await queryRunner.manager.save(inviteUser); + const invitedRoleType = await this.getRoleTypeByUuid(roleUuid); // Link user to spaces const spacePromises = validSpaces.map(async (space) => { @@ -128,7 +129,7 @@ export class InviteUserService { await this.emailService.sendEmailWithInvitationTemplate(email, { name: firstName, invitationCode, - role: roleType, + role: invitedRoleType.replace(/_/g, ' '), spacesList: spaceNames, }); From 13c0f87fc69782a6ecbe3f90eda085e444c23762 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Mon, 30 Jun 2025 04:09:40 -0600 Subject: [PATCH 48/86] fix: filter daily averages by space_id and event_date in update procedure --- .../proceduce_update_daily_space_aqi.sql | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libs/common/src/sql/procedures/fact_daily_space_aqi/proceduce_update_daily_space_aqi.sql b/libs/common/src/sql/procedures/fact_daily_space_aqi/proceduce_update_daily_space_aqi.sql index 04fb661..46c1dd9 100644 --- a/libs/common/src/sql/procedures/fact_daily_space_aqi/proceduce_update_daily_space_aqi.sql +++ b/libs/common/src/sql/procedures/fact_daily_space_aqi/proceduce_update_daily_space_aqi.sql @@ -276,9 +276,11 @@ SELECT p.good_ch2o_percentage, p.moderate_ch2o_percentage, p.unhealthy_sensitive_ch2o_percentage, p.unhealthy_ch2o_percentage, p.very_unhealthy_ch2o_percentage, p.hazardous_ch2o_percentage, a.daily_avg_ch2o,a.daily_max_ch2o, a.daily_min_ch2o FROM daily_percentages p -LEFT JOIN daily_averages a - ON p.space_id = a.space_id AND p.event_date = a.event_date -ORDER BY p.space_id, p.event_date) + LEFT JOIN daily_averages a + ON p.space_id = a.space_id AND p.event_date = a.event_date + WHERE p.space_id = (SELECT space_id FROM params) + AND p.event_date = (SELECT event_date FROM params) + ORDER BY p.space_id, p.event_date) INSERT INTO public."space-daily-pollutant-stats" ( From 5bf44a18e1d45bd55884d89dd802788a07ee1ab7 Mon Sep 17 00:00:00 2001 From: Mhd Zayd Skaff Date: Mon, 30 Jun 2025 14:09:32 +0300 Subject: [PATCH 49/86] add cur2 checks to schedule --- src/schedule/services/schedule.service.ts | 39 +++++++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/src/schedule/services/schedule.service.ts b/src/schedule/services/schedule.service.ts index 065dc55..c4d7b5d 100644 --- a/src/schedule/services/schedule.service.ts +++ b/src/schedule/services/schedule.service.ts @@ -162,6 +162,16 @@ export class ScheduleService { throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); } + if ( + deviceDetails.productDevice.prodType == ProductType.CUR_2 && + addScheduleDto.category != 'Timer' + ) { + throw new HttpException( + 'Invalid category for CUR_2 devices', + HttpStatus.BAD_REQUEST, + ); + } + // Corrected condition for supported device types if ( deviceDetails.productDevice.prodType !== ProductType.THREE_G && @@ -182,6 +192,7 @@ export class ScheduleService { await this.addScheduleDeviceInTuya( deviceDetails.deviceTuyaUuid, addScheduleDto, + deviceDetails.productDevice.prodType as ProductType, ); } catch (error) { throw new HttpException( @@ -193,6 +204,7 @@ export class ScheduleService { async addScheduleDeviceInTuya( deviceId: string, addScheduleDto: AddScheduleDto, + deviceType: ProductType, ): Promise { try { const convertedTime = convertTimestampToDubaiTime(addScheduleDto.time); @@ -212,7 +224,10 @@ export class ScheduleService { value: addScheduleDto.function.value, }, ], - category: `category_${addScheduleDto.category}`, + category: + deviceType == ProductType.CUR_2 + ? addScheduleDto.category + : `category_${addScheduleDto.category}`, }, }); @@ -254,7 +269,10 @@ export class ScheduleService { ); const result = schedules.result.map((schedule: any) => { return { - category: schedule.category.replace('category_', ''), + category: + deviceDetails.productDevice.prodType == ProductType.CUR_2 + ? schedule.category + : schedule.category.replace('category_', ''), enable: schedule.enable, function: { code: schedule.functions[0].code, @@ -318,6 +336,16 @@ export class ScheduleService { throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); } + if ( + deviceDetails.productDevice.prodType == ProductType.CUR_2 && + updateScheduleDto.category != 'Timer' + ) { + throw new HttpException( + 'Invalid category for CUR_2 devices', + HttpStatus.BAD_REQUEST, + ); + } + // Corrected condition for supported device types if ( deviceDetails.productDevice.prodType !== ProductType.THREE_G && @@ -338,6 +366,7 @@ export class ScheduleService { await this.updateScheduleDeviceInTuya( deviceDetails.deviceTuyaUuid, updateScheduleDto, + deviceDetails.productDevice.prodType as ProductType, ); } catch (error) { throw new HttpException( @@ -349,6 +378,7 @@ export class ScheduleService { async updateScheduleDeviceInTuya( deviceId: string, updateScheduleDto: UpdateScheduleDto, + deviceType: ProductType, ): Promise { try { const convertedTime = convertTimestampToDubaiTime(updateScheduleDto.time); @@ -369,7 +399,10 @@ export class ScheduleService { value: updateScheduleDto.function.value, }, ], - category: `category_${updateScheduleDto.category}`, + category: + deviceType == ProductType.CUR_2 + ? updateScheduleDto.category + : `category_${updateScheduleDto.category.replace('category_', '')}`, }, }); From b3f8b928262381aa470f483fb77def98f596571d Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Mon, 30 Jun 2025 15:35:23 +0300 Subject: [PATCH 50/86] ensure Timer is the category value for CUR2 type (#446) --- src/schedule/services/schedule.service.ts | 39 +++++++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/src/schedule/services/schedule.service.ts b/src/schedule/services/schedule.service.ts index d88ee20..e89c6d8 100644 --- a/src/schedule/services/schedule.service.ts +++ b/src/schedule/services/schedule.service.ts @@ -96,6 +96,16 @@ export class ScheduleService { throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); } + if ( + deviceDetails.productDevice.prodType == ProductType.CUR_2 && + addScheduleDto.category != 'Timer' + ) { + throw new HttpException( + 'Invalid category for CUR_2 devices', + HttpStatus.BAD_REQUEST, + ); + } + this.ensureProductTypeSupportedForSchedule( deviceDetails.productDevice.prodType as ProductType, ); @@ -103,6 +113,7 @@ export class ScheduleService { await this.addScheduleDeviceInTuya( deviceDetails.deviceTuyaUuid, addScheduleDto, + deviceDetails.productDevice.prodType as ProductType, ); } catch (error) { throw new HttpException( @@ -128,7 +139,10 @@ export class ScheduleService { ); const result = schedules.result.map((schedule: any) => { return { - category: schedule.category.replace('category_', ''), + category: + deviceDetails.productDevice.prodType == ProductType.CUR_2 + ? schedule.category + : schedule.category.replace('category_', ''), enable: schedule.enable, function: { code: schedule.functions[0].code, @@ -159,6 +173,16 @@ export class ScheduleService { throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); } + if ( + deviceDetails.productDevice.prodType == ProductType.CUR_2 && + updateScheduleDto.category != 'Timer' + ) { + throw new HttpException( + 'Invalid category for CUR_2 devices', + HttpStatus.BAD_REQUEST, + ); + } + // Corrected condition for supported device types this.ensureProductTypeSupportedForSchedule( deviceDetails.productDevice.prodType as ProductType, @@ -167,6 +191,7 @@ export class ScheduleService { await this.updateScheduleDeviceInTuya( deviceDetails.deviceTuyaUuid, updateScheduleDto, + deviceDetails.productDevice.prodType as ProductType, ); } catch (error) { throw new HttpException( @@ -192,6 +217,7 @@ export class ScheduleService { private async addScheduleDeviceInTuya( deviceId: string, addScheduleDto: AddScheduleDto, + deviceType: ProductType, ): Promise { try { const convertedTime = convertTimestampToDubaiTime(addScheduleDto.time); @@ -210,7 +236,10 @@ export class ScheduleService { ...addScheduleDto.function, }, ], - category: `category_${addScheduleDto.category}`, + category: + deviceType == ProductType.CUR_2 + ? addScheduleDto.category + : `category_${addScheduleDto.category}`, }, }); @@ -248,6 +277,7 @@ export class ScheduleService { private async updateScheduleDeviceInTuya( deviceId: string, updateScheduleDto: UpdateScheduleDto, + deviceType: ProductType, ): Promise { try { const convertedTime = convertTimestampToDubaiTime(updateScheduleDto.time); @@ -268,7 +298,10 @@ export class ScheduleService { value: updateScheduleDto.function.value, }, ], - category: `category_${updateScheduleDto.category}`, + category: + deviceType == ProductType.CUR_2 + ? updateScheduleDto.category + : `category_${updateScheduleDto.category}`, }, }); From 0b9eef276e0a8eb1335174b52cee8cfdc8a83e5c Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Mon, 30 Jun 2025 15:52:01 +0300 Subject: [PATCH 51/86] ensure Timer is the category value for CUR2 type (#448) --- src/schedule/services/schedule.service.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/schedule/services/schedule.service.ts b/src/schedule/services/schedule.service.ts index e89c6d8..1296900 100644 --- a/src/schedule/services/schedule.service.ts +++ b/src/schedule/services/schedule.service.ts @@ -136,6 +136,7 @@ export class ScheduleService { const schedules = await this.getScheduleDeviceInTuya( deviceDetails.deviceTuyaUuid, category, + deviceDetails.productDevice.prodType as ProductType, ); const result = schedules.result.map((schedule: any) => { return { @@ -255,9 +256,12 @@ export class ScheduleService { private async getScheduleDeviceInTuya( deviceId: string, category: string, + deviceType: ProductType, ): Promise { try { - const path = `/v2.0/cloud/timer/device/${deviceId}?category=category_${category}`; + const categoryToSent = + deviceType == ProductType.CUR_2 ? category : `category_${category}`; + const path = `/v2.0/cloud/timer/device/${deviceId}?category=${categoryToSent}`; const response = await this.tuya.request({ method: 'GET', path, From 8f9b15f49fb05596c00cb460484fca5b7e0e36f3 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Mon, 30 Jun 2025 07:12:43 -0600 Subject: [PATCH 52/86] fix: adjust category handling for CUR_2 device type in schedule retrieval --- src/schedule/services/schedule.service.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/schedule/services/schedule.service.ts b/src/schedule/services/schedule.service.ts index c4d7b5d..3157a16 100644 --- a/src/schedule/services/schedule.service.ts +++ b/src/schedule/services/schedule.service.ts @@ -266,6 +266,7 @@ export class ScheduleService { const schedules = await this.getScheduleDeviceInTuya( deviceDetails.deviceTuyaUuid, category, + deviceDetails.productDevice.prodType as ProductType, ); const result = schedules.result.map((schedule: any) => { return { @@ -295,9 +296,12 @@ export class ScheduleService { async getScheduleDeviceInTuya( deviceId: string, category: string, + deviceType: ProductType, ): Promise { try { - const path = `/v2.0/cloud/timer/device/${deviceId}?category=category_${category}`; + const categoryToSent = + deviceType == ProductType.CUR_2 ? category : `category_${category}`; + const path = `/v2.0/cloud/timer/device/${deviceId}?category=${categoryToSent}`; const response = await this.tuya.request({ method: 'GET', path, From 5c916ed44505ba78aa2277e888975a45f02039af Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Thu, 3 Jul 2025 00:01:37 -0600 Subject: [PATCH 53/86] add pr template --- .github/pull_request_template.md | 17 +++++++++++++++++ .gitignore | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..b0ec605 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,17 @@ + + +## Jira Ticket + +[SP-0000](https://syncrow.atlassian.net/browse/SP-0000) + +## Description + + + +## How to Test + + diff --git a/.gitignore b/.gitignore index 46ae420..3de8765 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ /build #github -/.github +/.github/workflows # Logs logs From 60bc03cf79641470e9488124f21a30624066478f Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Thu, 3 Jul 2025 01:12:23 -0600 Subject: [PATCH 54/86] fix deployed issue --- src/schedule/services/schedule.service.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/schedule/services/schedule.service.ts b/src/schedule/services/schedule.service.ts index b05296a..64613b7 100644 --- a/src/schedule/services/schedule.service.ts +++ b/src/schedule/services/schedule.service.ts @@ -1,7 +1,5 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; -import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; import { TuyaContext } from '@tuya/tuya-connector-nodejs'; import { AddScheduleDto, @@ -13,7 +11,6 @@ import { getDeviceScheduleInterface, } from '../interfaces/get.schedule.interface'; -import { ProductType } from '@app/common/constants/product-type.enum'; import { ProductType } from '@app/common/constants/product-type.enum'; import { convertKeysToCamelCase } from '@app/common/helper/camelCaseConverter'; import { convertTimestampToDubaiTime } from '@app/common/helper/convertTimestampToDubaiTime'; @@ -22,7 +19,6 @@ import { getScheduleStatus, } from '@app/common/helper/getScheduleStatus'; import { DeviceRepository } from '@app/common/modules/device/repositories'; -import { DeviceRepository } from '@app/common/modules/device/repositories'; @Injectable() export class ScheduleService { @@ -128,7 +124,6 @@ export class ScheduleService { deviceDetails.deviceTuyaUuid, addScheduleDto, deviceDetails.productDevice.prodType as ProductType, - deviceDetails.productDevice.prodType as ProductType, ); } catch (error) { throw new HttpException( @@ -152,14 +147,9 @@ export class ScheduleService { deviceDetails.deviceTuyaUuid, category, deviceDetails.productDevice.prodType as ProductType, - deviceDetails.productDevice.prodType as ProductType, ); const result = schedules.result.map((schedule: any) => { return { - category: - deviceDetails.productDevice.prodType == ProductType.CUR_2 - ? schedule.category - : schedule.category.replace('category_', ''), category: deviceDetails.productDevice.prodType == ProductType.CUR_2 ? schedule.category @@ -223,7 +213,6 @@ export class ScheduleService { deviceDetails.deviceTuyaUuid, updateScheduleDto, deviceDetails.productDevice.prodType as ProductType, - deviceDetails.productDevice.prodType as ProductType, ); } catch (error) { throw new HttpException( @@ -313,7 +302,6 @@ export class ScheduleService { deviceId: string, updateScheduleDto: UpdateScheduleDto, deviceType: ProductType, - deviceType: ProductType, ): Promise { try { const convertedTime = convertTimestampToDubaiTime(updateScheduleDto.time); From 807c5b7dd3e08d7df509a84d2616dbfcf3e36433 Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Thu, 3 Jul 2025 16:23:42 +0300 Subject: [PATCH 55/86] add tag uuid to add device API (#450) --- src/device/dtos/add.device.dto.ts | 8 ++++++++ src/device/services/device.service.ts | 14 ++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/device/dtos/add.device.dto.ts b/src/device/dtos/add.device.dto.ts index 564dc78..7a09b1c 100644 --- a/src/device/dtos/add.device.dto.ts +++ b/src/device/dtos/add.device.dto.ts @@ -18,6 +18,14 @@ export class AddDeviceDto { @IsNotEmpty() public spaceUuid: string; + @ApiProperty({ + description: 'tagUuid', + required: true, + }) + @IsString() + @IsNotEmpty() + public tagUuid: string; + @ApiProperty({ description: 'deviceName', required: true, diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index 793d854..a73664b 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -162,10 +162,24 @@ export class DeviceService { if (!device.productUuid) { throw new Error('Product UUID is missing for the device.'); } + const existingConflictingDevice = await this.deviceRepository.exists({ + where: { + spaceDevice: { uuid: addDeviceDto.spaceUuid }, + productDevice: { uuid: device.productUuid }, + tag: { uuid: addDeviceDto.tagUuid }, + }, + }); + if (existingConflictingDevice) { + throw new HttpException( + 'Device with the same product type and tag already exists in this space', + HttpStatus.BAD_REQUEST, + ); + } const deviceSaved = await this.deviceRepository.save({ deviceTuyaUuid: addDeviceDto.deviceTuyaUuid, productDevice: { uuid: device.productUuid }, spaceDevice: { uuid: addDeviceDto.spaceUuid }, + tag: { uuid: addDeviceDto.tagUuid }, name: addDeviceDto.deviceName, }); if (deviceSaved.uuid) { From 25599b9fe2931e9c0248b9ef46870305d59e3b0a Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Sun, 6 Jul 2025 14:14:23 +0300 Subject: [PATCH 56/86] add '%' to search (#452) --- src/community/services/community.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/community/services/community.service.ts b/src/community/services/community.service.ts index e657436..96484c2 100644 --- a/src/community/services/community.service.ts +++ b/src/community/services/community.service.ts @@ -210,7 +210,7 @@ export class CommunityService { if (search) { qb.andWhere( `c.name ILIKE :search ${includeSpaces ? 'OR space.space_name ILIKE :search' : ''}`, - { search }, + { search: `%${search}%` }, ); } From 66391bafd81bb4fb839a6a85c7974d7c825c1d9b Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Mon, 7 Jul 2025 10:38:20 +0300 Subject: [PATCH 57/86] SP-1814: Update out-of-date device-virtual-id (#451) * task: add a function to update device tuya id * Add Tuya const uuid to device --- libs/common/src/constants/controller-route.ts | 5 + .../tuya/services/tuya.service.ts | 12 +- .../modules/device/entities/device.entity.ts | 5 + src/device/controllers/device.controller.ts | 56 +- src/device/services/device.service.ts | 2096 +++++++++-------- 5 files changed, 1143 insertions(+), 1031 deletions(-) diff --git a/libs/common/src/constants/controller-route.ts b/libs/common/src/constants/controller-route.ts index 463d524..6e0b948 100644 --- a/libs/common/src/constants/controller-route.ts +++ b/libs/common/src/constants/controller-route.ts @@ -641,6 +641,11 @@ export class ControllerRoute { 'Delete scenes by device uuid and switch name'; public static readonly DELETE_SCENES_BY_SWITCH_NAME_DESCRIPTION = 'This endpoint deletes all scenes associated with a specific switch device.'; + + public static readonly POPULATE_TUYA_CONST_UUID_SUMMARY = + 'Populate Tuya const UUID'; + public static readonly POPULATE_TUYA_CONST_UUID_DESCRIPTION = + 'This endpoint populates the Tuya const UUID for all devices.'; }; }; static DEVICE_COMMISSION = class { diff --git a/libs/common/src/integrations/tuya/services/tuya.service.ts b/libs/common/src/integrations/tuya/services/tuya.service.ts index 67a4eff..19dc6d8 100644 --- a/libs/common/src/integrations/tuya/services/tuya.service.ts +++ b/libs/common/src/integrations/tuya/services/tuya.service.ts @@ -49,12 +49,12 @@ export class TuyaService { path, }); - if (!response.success) { - throw new HttpException( - `Error fetching device details: ${response.msg}`, - HttpStatus.BAD_REQUEST, - ); - } + // if (!response.success) { + // throw new HttpException( + // `Error fetching device details: ${response.msg}`, + // HttpStatus.BAD_REQUEST, + // ); + // } return response.result; } diff --git a/libs/common/src/modules/device/entities/device.entity.ts b/libs/common/src/modules/device/entities/device.entity.ts index 33f13d4..2b4db9a 100644 --- a/libs/common/src/modules/device/entities/device.entity.ts +++ b/libs/common/src/modules/device/entities/device.entity.ts @@ -28,6 +28,11 @@ export class DeviceEntity extends AbstractEntity { }) deviceTuyaUuid: string; + @Column({ + nullable: true, + }) + deviceTuyaConstUuid: string; + @Column({ nullable: true, default: true, diff --git a/src/device/controllers/device.controller.ts b/src/device/controllers/device.controller.ts index ffc073a..703d0fc 100644 --- a/src/device/controllers/device.controller.ts +++ b/src/device/controllers/device.controller.ts @@ -1,39 +1,41 @@ -import { DeviceService } from '../services/device.service'; +import { ControllerRoute } from '@app/common/constants/controller-route'; +import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; +import { RoleType } from '@app/common/constants/role.type.enum'; +import { BaseResponseDto } from '@app/common/dto/base.response.dto'; import { Body, Controller, - Get, - Post, - Query, - Param, - UseGuards, - Put, Delete, + Get, + Param, + Post, + Put, + Query, Req, + UnauthorizedException, + UseGuards, } from '@nestjs/common'; -import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger'; +import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; +import { Permissions } from 'src/decorators/permissions.decorator'; +import { PermissionsGuard } from 'src/guards/permissions.guard'; +import { CheckRoomGuard } from 'src/guards/room.guard'; +import { CheckFourAndSixSceneDeviceTypeGuard } from 'src/guards/scene.device.type.guard'; import { AddDeviceDto, AddSceneToFourSceneDeviceDto, AssignDeviceToSpaceDto, UpdateDeviceDto, } from '../dtos/add.device.dto'; -import { GetDeviceLogsDto } from '../dtos/get.device.dto'; import { - ControlDeviceDto, BatchControlDevicesDto, BatchStatusDevicesDto, + ControlDeviceDto, GetSceneFourSceneDeviceDto, } from '../dtos/control.device.dto'; -import { CheckRoomGuard } from 'src/guards/room.guard'; -import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; -import { CheckFourAndSixSceneDeviceTypeGuard } from 'src/guards/scene.device.type.guard'; -import { ControllerRoute } from '@app/common/constants/controller-route'; -import { BaseResponseDto } from '@app/common/dto/base.response.dto'; -import { DeviceSceneParamDto } from '../dtos/device.param.dto'; import { DeleteSceneFromSceneDeviceDto } from '../dtos/delete.device.dto'; -import { PermissionsGuard } from 'src/guards/permissions.guard'; -import { Permissions } from 'src/decorators/permissions.decorator'; +import { DeviceSceneParamDto } from '../dtos/device.param.dto'; +import { GetDeviceLogsDto } from '../dtos/get.device.dto'; +import { DeviceService } from '../services/device.service'; @ApiTags('Device Module') @Controller({ @@ -340,4 +342,22 @@ export class DeviceController { projectUuid, ); } + + @ApiBearerAuth() + @UseGuards(PermissionsGuard) + @Permissions('DEVICE_UPDATE') + @Post('/populate-tuya-const-uuids') + @ApiOperation({ + summary: ControllerRoute.DEVICE.ACTIONS.POPULATE_TUYA_CONST_UUID_SUMMARY, + description: + ControllerRoute.DEVICE.ACTIONS.POPULATE_TUYA_CONST_UUID_DESCRIPTION, + }) + async populateTuyaConstUuid(@Req() req: any): Promise { + const userUuid = req['user']?.userUuid; + const userRole = req['user']?.role; + if (!userUuid || (userRole && userRole !== RoleType.SUPER_ADMIN)) { + throw new UnauthorizedException('Unauthorized to perform this action'); + } + return this.deviceService.addTuyaConstUuidToDevices(); + } } diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index a73664b..e4c0da5 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -98,875 +98,6 @@ export class DeviceService { }); } - async getDeviceByDeviceUuid( - deviceUuid: string, - withProductDevice: boolean = true, - projectUuid: string, - ) { - const relations = ['subspace']; - - if (withProductDevice) { - relations.push('productDevice'); - } - - return this.deviceRepository.findOne({ - where: { - uuid: deviceUuid, - spaceDevice: { - community: { project: { uuid: projectUuid } }, - spaceName: Not(ORPHAN_SPACE_NAME), - }, - }, - relations, - }); - } - async deleteDevice( - devices: DeviceEntity[], - orphanSpace: SpaceEntity, - queryRunner: QueryRunner, - ): Promise { - try { - const deviceIds = devices.map((device) => device.uuid); - - if (deviceIds.length > 0) { - await queryRunner.manager - .createQueryBuilder() - .update(DeviceEntity) - .set({ spaceDevice: orphanSpace }) - .whereInIds(deviceIds) - .execute(); - } - } catch (error) { - throw new HttpException( - `Failed to update devices to orphan space: ${error.message}`, - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - - async getDeviceByDeviceTuyaUuid(deviceTuyaUuid: string) { - return await this.deviceRepository.findOne({ - where: { - deviceTuyaUuid, - }, - relations: ['productDevice'], - }); - } - - async addNewDevice(addDeviceDto: AddDeviceDto, projectUuid: string) { - try { - const device = await this.getDeviceDetailsByDeviceIdTuya( - addDeviceDto.deviceTuyaUuid, - ); - - if (!device.productUuid) { - throw new Error('Product UUID is missing for the device.'); - } - const existingConflictingDevice = await this.deviceRepository.exists({ - where: { - spaceDevice: { uuid: addDeviceDto.spaceUuid }, - productDevice: { uuid: device.productUuid }, - tag: { uuid: addDeviceDto.tagUuid }, - }, - }); - if (existingConflictingDevice) { - throw new HttpException( - 'Device with the same product type and tag already exists in this space', - HttpStatus.BAD_REQUEST, - ); - } - const deviceSaved = await this.deviceRepository.save({ - deviceTuyaUuid: addDeviceDto.deviceTuyaUuid, - productDevice: { uuid: device.productUuid }, - spaceDevice: { uuid: addDeviceDto.spaceUuid }, - tag: { uuid: addDeviceDto.tagUuid }, - name: addDeviceDto.deviceName, - }); - if (deviceSaved.uuid) { - const deviceStatus: BaseResponseDto = - await this.getDevicesInstructionStatus(deviceSaved.uuid, projectUuid); - if (deviceStatus.data.productUuid) { - await this.deviceStatusFirebaseService.addDeviceStatusToFirebase({ - deviceUuid: deviceSaved.uuid, - deviceTuyaUuid: addDeviceDto.deviceTuyaUuid, - status: deviceStatus.data.status, - productUuid: deviceStatus.data.productUuid, - productType: deviceStatus.data.productType, - }); - } - } - return new SuccessResponseDto({ - message: `Device added successfully`, - data: deviceSaved, - statusCode: HttpStatus.CREATED, - }); - } catch (error) { - if (error.code === CommonErrorCodes.DUPLICATE_ENTITY) { - throw new HttpException( - 'Device already exists', - HttpStatus.BAD_REQUEST, - ); - } else { - throw new HttpException( - error.message || 'Failed to add device in space', - error.status || HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - } - - async transferDeviceInSpaces( - assignDeviceToSpaceDto: AssignDeviceToSpaceDto, - projectUuid: string, - ) { - try { - await this.deviceRepository.update( - { - uuid: assignDeviceToSpaceDto.deviceUuid, - spaceDevice: { - community: { project: { uuid: projectUuid } }, - spaceName: Not(ORPHAN_SPACE_NAME), - }, - }, - { - spaceDevice: { uuid: assignDeviceToSpaceDto.spaceUuid }, - }, - ); - const device = await this.deviceRepository.findOne({ - where: { - uuid: assignDeviceToSpaceDto.deviceUuid, - }, - relations: ['spaceDevice', 'spaceDevice.parent'], - }); - if (device.spaceDevice.parent.spaceTuyaUuid) { - await this.transferDeviceInSpacesTuya( - device.deviceTuyaUuid, - device.spaceDevice.parent.spaceTuyaUuid, - ); - } - - return new SuccessResponseDto({ - message: `Device transferred successfully to spaceUuid: ${assignDeviceToSpaceDto.spaceUuid}`, - data: { - uuid: device.uuid, - spaceUuid: device.spaceDevice.uuid, - }, - statusCode: HttpStatus.CREATED, - }); - } catch (error) { - throw new HttpException( - 'Failed to add device in space', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - - async transferDeviceInSpacesTuya( - deviceId: string, - spaceId: string, - ): Promise { - try { - const path = `/v2.0/cloud/thing/${deviceId}/transfer`; - const response = await this.tuya.request({ - method: 'POST', - path, - body: { space_id: spaceId }, - }); - - return response as controlDeviceInterface; - } catch (error) { - throw new HttpException( - 'Error transferring device in spaces from Tuya', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - async updateDeviceName(deviceUuid: string, deviceName: string) { - try { - await this.deviceRepository.update( - { uuid: deviceUuid }, - { name: deviceName }, - ); - } catch (error) { - throw new HttpException( - 'Error updating device name', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - async controlDevice( - controlDeviceDto: ControlDeviceDto, - deviceUuid: string, - projectUuid: string, - ) { - try { - const deviceDetails = await this.getDeviceByDeviceUuid( - deviceUuid, - false, - projectUuid, - ); - - if (!deviceDetails || !deviceDetails.deviceTuyaUuid) { - throw new NotFoundException('Device Not Found'); - } - const response = await this.controlDeviceTuya( - deviceDetails.deviceTuyaUuid, - controlDeviceDto, - ); - - if (response.success) { - return new SuccessResponseDto({ - message: `Device controlled successfully`, - data: response, - statusCode: HttpStatus.CREATED, - }); - } else { - throw new HttpException( - response.msg || 'Unknown error', - HttpStatus.BAD_REQUEST, - ); - } - } catch (error) { - throw new HttpException( - error.message || 'Device Not Found', - error.status || HttpStatus.NOT_FOUND, - ); - } - } - - async factoryResetDeviceTuya( - deviceUuid: string, - ): Promise { - try { - const path = `/v2.0/cloud/thing/${deviceUuid}/reset`; - const response = await this.tuya.request({ - method: 'POST', - path, - }); - - return response as controlDeviceInterface; - } catch (error) { - throw new HttpException( - 'Error factory resetting device from Tuya', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - async controlDeviceTuya( - deviceUuid: string, - controlDeviceDto: ControlDeviceDto, - ): Promise { - try { - const path = `/v1.0/iot-03/devices/${deviceUuid}/commands`; - const response = await this.tuya.request({ - method: 'POST', - path, - body: { - commands: [ - { code: controlDeviceDto.code, value: controlDeviceDto.value }, - ], - }, - }); - - return response as controlDeviceInterface; - } catch (error) { - throw new HttpException( - 'Error control device from Tuya', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - - async batchControlDevices( - batchControlDevicesDto: BatchControlDevicesDto, - projectUuid: string, - ) { - const { devicesUuid, operationType } = batchControlDevicesDto; - - if (operationType === BatchDeviceTypeEnum.RESET) { - return await this.batchFactoryResetDevices( - batchControlDevicesDto, - projectUuid, - ); - } else if (operationType === BatchDeviceTypeEnum.COMMAND) { - try { - // Check if all devices have the same product UUID - await this.checkAllDevicesHaveSameProductUuid(devicesUuid); - - // Perform all operations concurrently - const results = await Promise.allSettled( - devicesUuid.map(async (deviceUuid) => { - const deviceDetails = await this.getDeviceByDeviceUuid( - deviceUuid, - true, - projectUuid, - ); - const result = await this.controlDeviceTuya( - deviceDetails.deviceTuyaUuid, - batchControlDevicesDto, - ); - return { deviceUuid, result }; - }), - ); - - // Separate successful and failed operations - const successResults = []; - const failedResults = []; - - for (const result of results) { - if (result.status === DeviceStatuses.FULLFILLED) { - const { deviceUuid, result: operationResult } = result.value; - - if (operationResult.success) { - // Add to success results if operationResult.success is true - successResults.push({ deviceUuid, result: operationResult }); - } else { - // Add to failed results if operationResult.success is false - failedResults.push({ deviceUuid, error: operationResult.msg }); - } - } else { - // Add to failed results if promise is rejected - failedResults.push({ - deviceUuid: devicesUuid[results.indexOf(result)], - error: result.reason.message, - }); - } - } - - return new SuccessResponseDto({ - message: `Devices controlled successfully`, - data: { successResults, failedResults }, - statusCode: HttpStatus.CREATED, - }); - } catch (error) { - throw new HttpException( - error.message || 'Device Not Found', - error.status || HttpStatus.NOT_FOUND, - ); - } - } - } - async batchStatusDevices( - batchStatusDevicesDto: BatchStatusDevicesDto, - projectUuid: string, - ) { - const { devicesUuid } = batchStatusDevicesDto; - const devicesUuidArray = devicesUuid.split(','); - - try { - await this.checkAllDevicesHaveSameProductUuid(devicesUuidArray); - const statuses = await Promise.all( - devicesUuidArray.map(async (deviceUuid) => { - const result = await this.getDevicesInstructionStatus( - deviceUuid, - projectUuid, - ); - return { deviceUuid, result }; - }), - ); - - return new SuccessResponseDto({ - message: `Devices status fetched successfully`, - data: { - status: statuses[0].result.data, - devices: statuses, - }, - statusCode: HttpStatus.OK, - }); - } catch (error) { - throw new HttpException( - error.message || 'Device Not Found', - error.status || HttpStatus.NOT_FOUND, - ); - } - } - async checkAllDevicesHaveSameProductUuid(deviceUuids: string[]) { - const firstDevice = await this.deviceRepository.findOne({ - where: { uuid: deviceUuids[0], isActive: true }, - relations: ['productDevice'], - }); - - if (!firstDevice) { - throw new BadRequestException('First device not found'); - } - - const firstProductType = firstDevice.productDevice.prodType; - - for (let i = 1; i < deviceUuids.length; i++) { - const device = await this.deviceRepository.findOne({ - where: { uuid: deviceUuids[i], isActive: true }, - relations: ['productDevice'], - }); - - if (!device) { - throw new BadRequestException(`Device ${deviceUuids[i]} not found`); - } - - if (device.productDevice.prodType !== firstProductType) { - throw new BadRequestException(`Devices have different product types`); - } - } - } - async batchFactoryResetDevices( - batchFactoryResetDevicesDto: BatchFactoryResetDevicesDto, - projectUuid: string, - ) { - const { devicesUuid } = batchFactoryResetDevicesDto; - - try { - // Check if all devices have the same product UUID - await this.checkAllDevicesHaveSameProductUuid(devicesUuid); - - // Perform all operations concurrently - const results = await Promise.allSettled( - devicesUuid.map(async (deviceUuid) => { - const deviceDetails = await this.getDeviceByDeviceUuid( - deviceUuid, - true, - projectUuid, - ); - const result = await this.factoryResetDeviceTuya( - deviceDetails.deviceTuyaUuid, - ); - return { deviceUuid, result }; - }), - ); - - // Separate successful and failed operations - const successResults = []; - const failedResults = []; - - for (const result of results) { - if (result.status === DeviceStatuses.FULLFILLED) { - const { deviceUuid, result: operationResult } = result.value; - - if (operationResult.success) { - // Add to success results if operationResult.success is true - successResults.push({ deviceUuid, result: operationResult }); - // Update isActive to false in the repository for the successfully reset device - await this.deviceRepository.update( - { uuid: deviceUuid }, - { isActive: false }, - ); - } else { - // Add to failed results if operationResult.success is false - failedResults.push({ deviceUuid, error: operationResult.msg }); - } - } else { - // Add to failed results if promise is rejected - failedResults.push({ - deviceUuid: devicesUuid[results.indexOf(result)], - error: result.reason.message, - }); - } - } - - return { successResults, failedResults }; - } catch (error) { - if (error instanceof HttpException) { - throw error; - } - throw new HttpException( - error.message || 'Device Not Found', - error.status || HttpStatus.NOT_FOUND, - ); - } - } - async getDeviceDetailsByDeviceId(deviceUuid: string, projectUuid: string) { - try { - const deviceDetails = await this.getDeviceByDeviceUuid( - deviceUuid, - true, - projectUuid, - ); - - if (!deviceDetails) { - throw new NotFoundException('Device Not Found'); - } - - const response = await this.getDeviceDetailsByDeviceIdTuya( - deviceDetails.deviceTuyaUuid, - ); - const macAddress = await this.getMacAddressByDeviceIdTuya( - deviceDetails.deviceTuyaUuid, - ); - - return new SuccessResponseDto({ - message: `Device details fetched successfully`, - data: { - ...response, - uuid: deviceDetails.uuid, - productUuid: deviceDetails.productDevice.uuid, - productType: deviceDetails.productDevice.prodType, - macAddress: macAddress.mac, - subspace: deviceDetails.subspace ? deviceDetails.subspace : {}, - }, - statusCode: HttpStatus.OK, - }); - } catch (error) { - throw new HttpException( - error.message || 'Device Not Found', - HttpStatus.NOT_FOUND, - ); - } - } - async updateDevice( - deviceUuid: string, - updateDeviceDto: UpdateDeviceDto, - projectUuid: string, - ) { - try { - const device = await this.getDeviceByDeviceUuid( - deviceUuid, - true, - projectUuid, - ); - if (device.uuid) { - await this.updateDeviceName(deviceUuid, updateDeviceDto.deviceName); - } - - return new SuccessResponseDto({ - message: `Device updated successfully`, - data: { - uuid: device.uuid, - deviceName: updateDeviceDto.deviceName, - }, - statusCode: HttpStatus.CREATED, - }); - } catch (error) { - throw new HttpException( - 'Error updating device', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - async getDeviceDetailsByDeviceIdTuya( - deviceId: string, - ): Promise { - try { - const path = `/v1.1/iot-03/devices/${deviceId}`; - const response = await this.tuya.request({ - method: 'GET', - path, - }); - - // Convert keys to camel case - const camelCaseResponse = convertKeysToCamelCase(response); - const product = await this.productRepository.findOne({ - where: { - prodId: camelCaseResponse.result.productId, - }, - }); - const deviceDetails = await this.deviceRepository.findOne({ - where: { - deviceTuyaUuid: deviceId, - }, - }); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { productId, id, productName, uuid, name, ...rest } = - camelCaseResponse.result; - - return { - ...rest, - productUuid: product.uuid, - name: deviceDetails?.name, - productName: product.name, - } as GetDeviceDetailsInterface; - } catch (error) { - console.log('error', error); - - throw new HttpException( - 'Error fetching device details from Tuya', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - async getMacAddressByDeviceIdTuya( - deviceId: string, - ): Promise { - try { - const path = `/v1.0/devices/factory-infos?device_ids=${deviceId}`; - const response = await this.tuya.request({ - method: 'GET', - path, - }); - - return response.result[0]; - } catch (error) { - throw new HttpException( - 'Error fetching mac address device from Tuya', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - async getDeviceInstructionByDeviceId( - deviceUuid: string, - projectUuid: string, - ): Promise { - const deviceDetails = await this.getDeviceByDeviceUuid( - deviceUuid, - true, - projectUuid, - ); - - if (!deviceDetails) { - throw new NotFoundException('Device Not Found'); - } - try { - const response = await this.getDeviceInstructionByDeviceIdTuya( - deviceDetails.deviceTuyaUuid, - ); - - return new SuccessResponseDto({ - message: `Device instructions fetched successfully`, - data: { - productUuid: deviceDetails.productDevice.uuid, - productType: deviceDetails.productDevice.prodType, - functions: response.result.functions.map((fun: any) => { - return { - code: fun.code, - values: fun.values, - dataType: fun.type, - }; - }), - }, - statusCode: HttpStatus.CREATED, - }); - } catch (error) { - throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); - } - } - - async getDeviceInstructionByDeviceIdTuya( - deviceId: string, - ): Promise { - try { - const path = `/v1.0/iot-03/devices/${deviceId}/functions`; - const response = await this.tuya.request({ - method: 'GET', - path, - }); - return response as GetDeviceDetailsFunctionsInterface; - } catch (error) { - throw new HttpException( - 'Error fetching device functions from Tuya', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - async getDevicesInstructionStatus(deviceUuid: string, projectUuid: string) { - try { - const deviceDetails = await this.getDeviceByDeviceUuid( - deviceUuid, - true, - projectUuid, - ); - - if (!deviceDetails) { - throw new NotFoundException('Device Not Found'); - } - const deviceStatus = await this.getDevicesInstructionStatusTuya( - deviceDetails.deviceTuyaUuid, - ); - - return new SuccessResponseDto({ - message: `Device instructions status fetched successfully`, - data: { - productUuid: deviceDetails.productDevice.uuid, - productType: deviceDetails.productDevice.prodType, - status: deviceStatus.result[0].status, - }, - statusCode: HttpStatus.OK, - }); - } catch (error) { - throw new HttpException( - 'Error fetching device functions status', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - async getDevicesStatus(deviceUuid: string, projectUuid: string) { - try { - const deviceDetails = await this.getDeviceByDeviceUuid( - deviceUuid, - true, - projectUuid, - ); - - if (!deviceDetails) { - throw new NotFoundException('Device Not Found'); - } - if (deviceDetails.productDevice.prodType === ProductType.PC) { - return await this.getPowerClampInstructionStatus(deviceDetails); - } else { - return await this.getDevicesInstructionStatus(deviceUuid, projectUuid); - } - } catch (error) { - throw new HttpException( - 'Error fetching device functions status', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - - async getDevicesInstructionStatusTuya( - deviceUuid: string, - ): Promise { - try { - const path = `/v1.0/iot-03/devices/status`; - const response = await this.tuya.request({ - method: 'GET', - path, - query: { - device_ids: deviceUuid, - }, - }); - return response as GetDeviceDetailsFunctionsStatusInterface; - } catch (error) { - throw new HttpException( - 'Error fetching device functions status from Tuya', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - - async getDevicesInGateway(gatewayUuid: string, projectUuid: string) { - try { - const deviceDetails = await this.getDeviceByDeviceUuid( - gatewayUuid, - true, - projectUuid, - ); - - if (!deviceDetails) { - throw new NotFoundException('Device Not Found'); - } else if (deviceDetails.productDevice.prodType !== ProductType.GW) { - throw new BadRequestException('This is not a gateway device'); - } - - const response = await this.getDevicesInGatewayTuya( - deviceDetails.deviceTuyaUuid, - ); - - const devices = await Promise.all( - response.map(async (device: any) => { - try { - const deviceDetails = await this.getDeviceByDeviceTuyaUuid( - device.id, - ); - if (deviceDetails.deviceTuyaUuid) { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { id, ...rest } = device; - return { - ...rest, - tuyaUuid: deviceDetails.deviceTuyaUuid, - uuid: deviceDetails.uuid, - productUuid: deviceDetails.productDevice.uuid, - productType: deviceDetails.productDevice.prodType, - }; - } - return null; - } catch (error) { - return null; - } - }), - ); - - return new SuccessResponseDto({ - message: `Devices fetched successfully`, - data: { - uuid: deviceDetails.uuid, - productUuid: deviceDetails.productDevice.uuid, - productType: deviceDetails.productDevice.prodType, - devices: devices.filter((device) => device !== null), - }, - statusCode: HttpStatus.OK, - }); - } catch (error) { - throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); - } - } - - async getDevicesInGatewayTuya( - deviceId: string, - ): Promise { - try { - const path = `/v1.0/devices/${deviceId}/sub-devices`; - const response: any = await this.tuya.request({ - method: 'GET', - path, - }); - const camelCaseResponse = response.result.map((device: any) => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { product_id, category, ...rest } = device; - const camelCaseDevice = convertKeysToCamelCase({ ...rest }); - return camelCaseDevice as GetDeviceDetailsInterface[]; - }); - - return camelCaseResponse; - } catch (error) { - throw new HttpException( - 'Error fetching device details from Tuya', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - async updateDeviceFirmware( - deviceUuid: string, - firmwareVersion: number, - projectUuid: string, - ) { - try { - const deviceDetails = await this.getDeviceByDeviceUuid( - deviceUuid, - false, - projectUuid, - ); - - if (!deviceDetails || !deviceDetails.deviceTuyaUuid) { - throw new NotFoundException('Device Not Found'); - } - const response = await this.updateDeviceFirmwareTuya( - deviceDetails.deviceTuyaUuid, - firmwareVersion, - ); - - if (response.success) { - return new SuccessResponseDto({ - message: `Device firmware updated successfully`, - data: response, - statusCode: HttpStatus.CREATED, - }); - } else { - throw new HttpException( - response.msg || 'Unknown error', - HttpStatus.BAD_REQUEST, - ); - } - } catch (error) { - throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); - } - } - async updateDeviceFirmwareTuya( - deviceUuid: string, - firmwareVersion: number, - ): Promise { - try { - const path = `/v2.0/cloud/thing/${deviceUuid}/firmware/${firmwareVersion}`; - const response = await this.tuya.request({ - method: 'POST', - path, - }); - - return response as updateDeviceFirmwareInterface; - } catch (error) { - throw new HttpException( - 'Error updating device firmware from Tuya', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - async getAllDevices( param: ProjectParam, { deviceType, spaces }: GetDevicesFilterDto, @@ -1149,131 +280,620 @@ export class DeviceService { ); } } - async getDeviceLogsTuya( - deviceId: string, - code: string, - startTime: string = (Date.now() - 1 * 60 * 60 * 1000).toString(), - endTime: string = Date.now().toString(), - ): Promise { + + async getDeviceByDeviceUuid( + deviceUuid: string, + withProductDevice: boolean = true, + projectUuid: string, + ) { + const relations = ['subspace']; + + if (withProductDevice) { + relations.push('productDevice'); + } + + return this.deviceRepository.findOne({ + where: { + uuid: deviceUuid, + spaceDevice: { + community: { project: { uuid: projectUuid } }, + spaceName: Not(ORPHAN_SPACE_NAME), + }, + }, + relations, + }); + } + async getDeviceByDeviceTuyaUuid(deviceTuyaUuid: string) { + return await this.deviceRepository.findOne({ + where: { + deviceTuyaUuid, + }, + relations: ['productDevice'], + }); + } + + async addNewDevice(addDeviceDto: AddDeviceDto, projectUuid: string) { try { - const path = `/v2.0/cloud/thing/${deviceId}/report-logs?start_time=${startTime}&end_time=${endTime}&codes=${code}&size=50`; - const response = await this.tuya.request({ - method: 'GET', - path, + const device = await this.getDeviceDetailsByDeviceIdTuya( + addDeviceDto.deviceTuyaUuid, + ); + + if (!device.productUuid) { + throw new Error('Product UUID is missing for the device.'); + } + const existingConflictingDevice = await this.deviceRepository.exists({ + where: { + spaceDevice: { uuid: addDeviceDto.spaceUuid }, + productDevice: { uuid: device.productUuid }, + tag: { uuid: addDeviceDto.tagUuid }, + }, }); - // Convert keys to camel case - const camelCaseResponse = convertKeysToCamelCase(response); - const logs = camelCaseResponse.result.logs ?? []; - return { - startTime, - endTime, - data: logs, - } as getDeviceLogsInterface; + if (existingConflictingDevice) { + throw new HttpException( + 'Device with the same product type and tag already exists in this space', + HttpStatus.BAD_REQUEST, + ); + } + const deviceSaved = await this.deviceRepository.save({ + deviceTuyaUuid: addDeviceDto.deviceTuyaUuid, + productDevice: { uuid: device.productUuid }, + spaceDevice: { uuid: addDeviceDto.spaceUuid }, + tag: { uuid: addDeviceDto.tagUuid }, + name: addDeviceDto.deviceName, + }); + if (deviceSaved.uuid) { + const deviceStatus: BaseResponseDto = + await this.getDevicesInstructionStatus(deviceSaved.uuid, projectUuid); + if (deviceStatus.data.productUuid) { + await this.deviceStatusFirebaseService.addDeviceStatusToFirebase({ + deviceUuid: deviceSaved.uuid, + deviceTuyaUuid: addDeviceDto.deviceTuyaUuid, + status: deviceStatus.data.status, + productUuid: deviceStatus.data.productUuid, + productType: deviceStatus.data.productType, + }); + } + } + return new SuccessResponseDto({ + message: `Device added successfully`, + data: deviceSaved, + statusCode: HttpStatus.CREATED, + }); + } catch (error) { + if (error.code === CommonErrorCodes.DUPLICATE_ENTITY) { + throw new HttpException( + 'Device already exists', + HttpStatus.BAD_REQUEST, + ); + } else { + throw new HttpException( + error.message || 'Failed to add device in space', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + } + + async deleteDevice( + devices: DeviceEntity[], + orphanSpace: SpaceEntity, + queryRunner: QueryRunner, + ): Promise { + try { + const deviceIds = devices.map((device) => device.uuid); + + if (deviceIds.length > 0) { + await queryRunner.manager + .createQueryBuilder() + .update(DeviceEntity) + .set({ spaceDevice: orphanSpace }) + .whereInIds(deviceIds) + .execute(); + } } catch (error) { throw new HttpException( - 'Error fetching device logs from Tuya', + `Failed to update devices to orphan space: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR, ); } } - async getPowerClampInstructionStatus(deviceDetails: any) { + async transferDeviceInSpaces( + assignDeviceToSpaceDto: AssignDeviceToSpaceDto, + projectUuid: string, + ) { try { - const deviceStatus = await this.getPowerClampInstructionStatusTuya( - deviceDetails.deviceTuyaUuid, - ); - const statusList = deviceStatus.result.properties as { - code: string; - value: any; - }[]; - - const groupedStatus = statusList.reduce( - (acc, currentStatus) => { - const { code } = currentStatus; - - if (code.endsWith('A')) { - acc.phaseA.push(currentStatus); - } else if (code.endsWith('B')) { - acc.phaseB.push(currentStatus); - } else if (code.endsWith('C')) { - acc.phaseC.push(currentStatus); - } else { - acc.general.push(currentStatus); - } - return acc; + await this.deviceRepository.update( + { + uuid: assignDeviceToSpaceDto.deviceUuid, + spaceDevice: { + community: { project: { uuid: projectUuid } }, + spaceName: Not(ORPHAN_SPACE_NAME), + }, }, { - phaseA: [] as { code: string; value: any }[], - phaseB: [] as { code: string; value: any }[], - phaseC: [] as { code: string; value: any }[], - general: [] as { code: string; value: any }[], + spaceDevice: { uuid: assignDeviceToSpaceDto.spaceUuid }, }, ); + const device = await this.deviceRepository.findOne({ + where: { + uuid: assignDeviceToSpaceDto.deviceUuid, + }, + relations: ['spaceDevice', 'spaceDevice.parent'], + }); + if (device.spaceDevice.parent.spaceTuyaUuid) { + await this.transferDeviceInSpacesTuya( + device.deviceTuyaUuid, + device.spaceDevice.parent.spaceTuyaUuid, + ); + } + + return new SuccessResponseDto({ + message: `Device transferred successfully to spaceUuid: ${assignDeviceToSpaceDto.spaceUuid}`, + data: { + uuid: device.uuid, + spaceUuid: device.spaceDevice.uuid, + }, + statusCode: HttpStatus.CREATED, + }); + } catch (error) { + throw new HttpException( + 'Failed to add device in space', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async transferDeviceInSpacesTuya( + deviceId: string, + spaceId: string, + ): Promise { + try { + const path = `/v2.0/cloud/thing/${deviceId}/transfer`; + const response = await this.tuya.request({ + method: 'POST', + path, + body: { space_id: spaceId }, + }); + + return response as controlDeviceInterface; + } catch (error) { + throw new HttpException( + 'Error transferring device in spaces from Tuya', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async controlDevice( + controlDeviceDto: ControlDeviceDto, + deviceUuid: string, + projectUuid: string, + ) { + try { + const deviceDetails = await this.getDeviceByDeviceUuid( + deviceUuid, + false, + projectUuid, + ); + + if (!deviceDetails || !deviceDetails.deviceTuyaUuid) { + throw new NotFoundException('Device Not Found'); + } + const response = await this.controlDeviceTuya( + deviceDetails.deviceTuyaUuid, + controlDeviceDto, + ); + + if (response.success) { + return new SuccessResponseDto({ + message: `Device controlled successfully`, + data: response, + statusCode: HttpStatus.CREATED, + }); + } else { + throw new HttpException( + response.msg || 'Unknown error', + HttpStatus.BAD_REQUEST, + ); + } + } catch (error) { + throw new HttpException( + error.message || 'Device Not Found', + error.status || HttpStatus.NOT_FOUND, + ); + } + } + + async factoryResetDeviceTuya( + deviceUuid: string, + ): Promise { + try { + const path = `/v2.0/cloud/thing/${deviceUuid}/reset`; + const response = await this.tuya.request({ + method: 'POST', + path, + }); + + return response as controlDeviceInterface; + } catch (error) { + throw new HttpException( + 'Error factory resetting device from Tuya', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async batchControlDevices( + batchControlDevicesDto: BatchControlDevicesDto, + projectUuid: string, + ) { + const { devicesUuid, operationType } = batchControlDevicesDto; + + if (operationType === BatchDeviceTypeEnum.RESET) { + return await this.batchFactoryResetDevices( + batchControlDevicesDto, + projectUuid, + ); + } else if (operationType === BatchDeviceTypeEnum.COMMAND) { + try { + // Check if all devices have the same product UUID + await this.checkAllDevicesHaveSameProductUuid(devicesUuid); + + // Perform all operations concurrently + const results = await Promise.allSettled( + devicesUuid.map(async (deviceUuid) => { + const deviceDetails = await this.getDeviceByDeviceUuid( + deviceUuid, + true, + projectUuid, + ); + const result = await this.controlDeviceTuya( + deviceDetails.deviceTuyaUuid, + batchControlDevicesDto, + ); + return { deviceUuid, result }; + }), + ); + + // Separate successful and failed operations + const successResults = []; + const failedResults = []; + + for (const result of results) { + if (result.status === DeviceStatuses.FULLFILLED) { + const { deviceUuid, result: operationResult } = result.value; + + if (operationResult.success) { + // Add to success results if operationResult.success is true + successResults.push({ deviceUuid, result: operationResult }); + } else { + // Add to failed results if operationResult.success is false + failedResults.push({ deviceUuid, error: operationResult.msg }); + } + } else { + // Add to failed results if promise is rejected + failedResults.push({ + deviceUuid: devicesUuid[results.indexOf(result)], + error: result.reason.message, + }); + } + } + + return new SuccessResponseDto({ + message: `Devices controlled successfully`, + data: { successResults, failedResults }, + statusCode: HttpStatus.CREATED, + }); + } catch (error) { + throw new HttpException( + error.message || 'Device Not Found', + error.status || HttpStatus.NOT_FOUND, + ); + } + } + } + async batchStatusDevices( + batchStatusDevicesDto: BatchStatusDevicesDto, + projectUuid: string, + ) { + const { devicesUuid } = batchStatusDevicesDto; + const devicesUuidArray = devicesUuid.split(','); + + try { + await this.checkAllDevicesHaveSameProductUuid(devicesUuidArray); + const statuses = await Promise.all( + devicesUuidArray.map(async (deviceUuid) => { + const result = await this.getDevicesInstructionStatus( + deviceUuid, + projectUuid, + ); + return { deviceUuid, result }; + }), + ); return new SuccessResponseDto({ - message: `Power clamp functions status fetched successfully`, + message: `Devices status fetched successfully`, data: { - productUuid: deviceDetails.productDevice.uuid, - productType: deviceDetails.productDevice.prodType, - status: { - phaseA: groupedStatus.phaseA, - phaseB: groupedStatus.phaseB, - phaseC: groupedStatus.phaseC, - general: groupedStatus.general, - }, + status: statuses[0].result.data, + devices: statuses, }, statusCode: HttpStatus.OK, }); } catch (error) { throw new HttpException( - error.message || 'Error fetching power clamp functions status', - error.status || HttpStatus.INTERNAL_SERVER_ERROR, + error.message || 'Device Not Found', + error.status || HttpStatus.NOT_FOUND, ); } } - async getPowerClampInstructionStatusTuya( - deviceUuid: string, - ): Promise { + + async getDeviceDetailsByDeviceId(deviceUuid: string, projectUuid: string) { try { - const path = `/v2.0/cloud/thing/${deviceUuid}/shadow/properties`; - const response = await this.tuya.request({ - method: 'GET', - path, - query: { - device_ids: deviceUuid, + const deviceDetails = await this.getDeviceByDeviceUuid( + deviceUuid, + true, + projectUuid, + ); + + if (!deviceDetails) { + throw new NotFoundException('Device Not Found'); + } + + const response = await this.getDeviceDetailsByDeviceIdTuya( + deviceDetails.deviceTuyaUuid, + ); + const macAddress = await this.getMacAddressByDeviceIdTuya( + deviceDetails.deviceTuyaUuid, + ); + + return new SuccessResponseDto({ + message: `Device details fetched successfully`, + data: { + ...response, + uuid: deviceDetails.uuid, + productUuid: deviceDetails.productDevice.uuid, + productType: deviceDetails.productDevice.prodType, + macAddress: macAddress.mac, + subspace: deviceDetails.subspace ? deviceDetails.subspace : {}, }, + statusCode: HttpStatus.OK, }); - const camelCaseResponse = convertKeysToCamelCase(response); - return camelCaseResponse as GetPowerClampFunctionsStatusInterface; } catch (error) { throw new HttpException( - 'Error fetching power clamp functions status from Tuya', + error.message || 'Device Not Found', + HttpStatus.NOT_FOUND, + ); + } + } + async updateDevice( + deviceUuid: string, + updateDeviceDto: UpdateDeviceDto, + projectUuid: string, + ) { + try { + const device = await this.getDeviceByDeviceUuid( + deviceUuid, + true, + projectUuid, + ); + if (device.uuid) { + await this.updateDeviceName(deviceUuid, updateDeviceDto.deviceName); + } + + return new SuccessResponseDto({ + message: `Device updated successfully`, + data: { + uuid: device.uuid, + deviceName: updateDeviceDto.deviceName, + }, + statusCode: HttpStatus.CREATED, + }); + } catch (error) { + throw new HttpException( + 'Error updating device', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async getDeviceDetailsByDeviceIdTuya( + deviceId: string, + ): Promise { + console.log('fetching device details from Tuya for deviceId:', deviceId); + try { + const deviceDetails = await this.deviceRepository.findOne({ + where: { + deviceTuyaUuid: deviceId, + }, + relations: ['productDevice'], + }); + + let result = await this.tuyaService.getDeviceDetails(deviceId); + + if (!result) { + const updatedDeviceTuyaUuid = ( + await this.updateDeviceTuyaUuidFromTuya(deviceDetails) + ).deviceTuyaUuid; + + // Retry with the updated deviceTuyaUuid + result = await this.tuyaService.getDeviceDetails(updatedDeviceTuyaUuid); + } + // Convert keys to camel case + const camelCaseResponse = convertKeysToCamelCase(result); + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { productId, id, productName, uuid, name, ...rest } = + camelCaseResponse; + + return { + ...rest, + productUuid: deviceDetails.productDevice.uuid, + name: deviceDetails?.name, + productName: deviceDetails.productDevice.name, + } as GetDeviceDetailsInterface; + } catch (error) { + console.log('error', error); + + throw new HttpException( + 'Error fetching device details from Tuya', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async getDeviceInstructionByDeviceId( + deviceUuid: string, + projectUuid: string, + ): Promise { + const deviceDetails = await this.getDeviceByDeviceUuid( + deviceUuid, + true, + projectUuid, + ); + + if (!deviceDetails) { + throw new NotFoundException('Device Not Found'); + } + try { + const response = await this.getDeviceInstructionByDeviceIdTuya( + deviceDetails.deviceTuyaUuid, + ); + + return new SuccessResponseDto({ + message: `Device instructions fetched successfully`, + data: { + productUuid: deviceDetails.productDevice.uuid, + productType: deviceDetails.productDevice.prodType, + functions: response.result.functions.map((fun: any) => { + return { + code: fun.code, + values: fun.values, + dataType: fun.type, + }; + }), + }, + statusCode: HttpStatus.CREATED, + }); + } catch (error) { + throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); + } + } + + async getDevicesStatus(deviceUuid: string, projectUuid: string) { + try { + const deviceDetails = await this.getDeviceByDeviceUuid( + deviceUuid, + true, + projectUuid, + ); + + if (!deviceDetails) { + throw new NotFoundException('Device Not Found'); + } + if (deviceDetails.productDevice.prodType === ProductType.PC) { + return await this.getPowerClampInstructionStatus(deviceDetails); + } else { + return await this.getDevicesInstructionStatus(deviceUuid, projectUuid); + } + } catch (error) { + throw new HttpException( + 'Error fetching device functions status', HttpStatus.INTERNAL_SERVER_ERROR, ); } } - private async fetchAncestors(space: SpaceEntity): Promise { - const ancestors: SpaceEntity[] = []; + async getDevicesInGateway(gatewayUuid: string, projectUuid: string) { + try { + const deviceDetails = await this.getDeviceByDeviceUuid( + gatewayUuid, + true, + projectUuid, + ); - let currentSpace = space; - while (currentSpace && currentSpace.parent) { - // Fetch the parent space - const parent = await this.spaceRepository.findOne({ - where: { uuid: currentSpace.parent.uuid }, - relations: ['parent'], // To continue fetching upwards - }); - - if (parent) { - ancestors.push(parent); - currentSpace = parent; - } else { - currentSpace = null; + if (!deviceDetails) { + throw new NotFoundException('Device Not Found'); + } else if (deviceDetails.productDevice.prodType !== ProductType.GW) { + throw new BadRequestException('This is not a gateway device'); } - } - // Return the ancestors in reverse order to have the root at the start - return ancestors.reverse(); + const response = await this.getDevicesInGatewayTuya( + deviceDetails.deviceTuyaUuid, + ); + + const devices = await Promise.all( + response.map(async (device: any) => { + try { + const deviceDetails = await this.getDeviceByDeviceTuyaUuid( + device.id, + ); + if (deviceDetails.deviceTuyaUuid) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { id, ...rest } = device; + return { + ...rest, + tuyaUuid: deviceDetails.deviceTuyaUuid, + uuid: deviceDetails.uuid, + productUuid: deviceDetails.productDevice.uuid, + productType: deviceDetails.productDevice.prodType, + }; + } + return null; + } catch (error) { + return null; + } + }), + ); + + return new SuccessResponseDto({ + message: `Devices fetched successfully`, + data: { + uuid: deviceDetails.uuid, + productUuid: deviceDetails.productDevice.uuid, + productType: deviceDetails.productDevice.prodType, + devices: devices.filter((device) => device !== null), + }, + statusCode: HttpStatus.OK, + }); + } catch (error) { + throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); + } + } + + async updateDeviceFirmware( + deviceUuid: string, + firmwareVersion: number, + projectUuid: string, + ) { + try { + const deviceDetails = await this.getDeviceByDeviceUuid( + deviceUuid, + false, + projectUuid, + ); + + if (!deviceDetails || !deviceDetails.deviceTuyaUuid) { + throw new NotFoundException('Device Not Found'); + } + const response = await this.updateDeviceFirmwareTuya( + deviceDetails.deviceTuyaUuid, + firmwareVersion, + ); + + if (response.success) { + return new SuccessResponseDto({ + message: `Device firmware updated successfully`, + data: response, + statusCode: HttpStatus.CREATED, + }); + } else { + throw new HttpException( + response.msg || 'Unknown error', + HttpStatus.BAD_REQUEST, + ); + } + } catch (error) { + throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); + } } async addSceneToSceneDevice( @@ -1537,19 +1157,6 @@ export class DeviceService { } } - private async validateProject(uuid: string) { - const project = await this.projectRepository.findOne({ - where: { uuid }, - }); - if (!project) { - throw new HttpException( - `A project with the uuid '${uuid}' doesn't exists.`, - HttpStatus.BAD_REQUEST, - ); - } - return project; - } - async getParentHierarchy( space: SpaceEntity, ): Promise<{ uuid: string; spaceName: string }[]> { @@ -1578,7 +1185,69 @@ export class DeviceService { } } - async getDoorLockDevices(projectUuid: string, spaces?: string[]) { + async addDevicesToOrphanSpace( + space: SpaceEntity, + project: ProjectEntity, + queryRunner: QueryRunner, + ) { + const spaceRepository = queryRunner.manager.getRepository(SpaceEntity); + const deviceRepository = queryRunner.manager.getRepository(DeviceEntity); + try { + const orphanSpace = await spaceRepository.findOne({ + where: { + community: { + name: `${ORPHAN_COMMUNITY_NAME}-${project.name}`, + }, + spaceName: ORPHAN_SPACE_NAME, + }, + }); + + if (!orphanSpace) { + throw new HttpException( + `Orphan space not found in community ${project.name}`, + HttpStatus.NOT_FOUND, + ); + } + + await deviceRepository.update( + { uuid: In(space.devices.map((device) => device.uuid)) }, + { spaceDevice: orphanSpace }, + ); + } catch (error) { + throw new Error( + `Failed to add devices to orphan spaces: ${error.message}`, + ); + } + } + + async addTuyaConstUuidToDevices() { + const devices = await this.deviceRepository.find(); + const updatedDevices = []; + for (const device of devices) { + if (!device.deviceTuyaConstUuid) { + const path = `/v1.1/iot-03/devices/${device.deviceTuyaUuid}`; + const response = await this.tuya.request({ + method: 'GET', + path, + }); + if (!response?.success) { + console.error( + `Failed to fetch Tuya constant UUID for device ${device.deviceTuyaUuid}`, + ); + continue; + } + const camelCaseResponse = convertKeysToCamelCase(response); + const tuyaConstUuid = camelCaseResponse?.result.uuid; + if (tuyaConstUuid) { + device.deviceTuyaConstUuid = tuyaConstUuid; + updatedDevices.push(device); + } + } + } + await this.deviceRepository.save(updatedDevices); + } + + private async getDoorLockDevices(projectUuid: string, spaces?: string[]) { await this.validateProject(projectUuid); const devices = await this.deviceRepository.find({ @@ -1635,6 +1304,452 @@ export class DeviceService { statusCode: HttpStatus.OK, }); } + + private async controlDeviceTuya( + deviceUuid: string, + controlDeviceDto: ControlDeviceDto, + ): Promise { + try { + const path = `/v1.0/iot-03/devices/${deviceUuid}/commands`; + const response = await this.tuya.request({ + method: 'POST', + path, + body: { + commands: [ + { code: controlDeviceDto.code, value: controlDeviceDto.value }, + ], + }, + }); + + return response as controlDeviceInterface; + } catch (error) { + throw new HttpException( + 'Error control device from Tuya', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + private async updateDeviceName(deviceUuid: string, deviceName: string) { + try { + await this.deviceRepository.update( + { uuid: deviceUuid }, + { name: deviceName }, + ); + } catch (error) { + throw new HttpException( + 'Error updating device name', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + private async checkAllDevicesHaveSameProductUuid(deviceUuids: string[]) { + const firstDevice = await this.deviceRepository.findOne({ + where: { uuid: deviceUuids[0], isActive: true }, + relations: ['productDevice'], + }); + + if (!firstDevice) { + throw new BadRequestException('First device not found'); + } + + const firstProductType = firstDevice.productDevice.prodType; + + for (let i = 1; i < deviceUuids.length; i++) { + const device = await this.deviceRepository.findOne({ + where: { uuid: deviceUuids[i], isActive: true }, + relations: ['productDevice'], + }); + + if (!device) { + throw new BadRequestException(`Device ${deviceUuids[i]} not found`); + } + + if (device.productDevice.prodType !== firstProductType) { + throw new BadRequestException(`Devices have different product types`); + } + } + } + private async batchFactoryResetDevices( + batchFactoryResetDevicesDto: BatchFactoryResetDevicesDto, + projectUuid: string, + ) { + const { devicesUuid } = batchFactoryResetDevicesDto; + + try { + // Check if all devices have the same product UUID + await this.checkAllDevicesHaveSameProductUuid(devicesUuid); + + // Perform all operations concurrently + const results = await Promise.allSettled( + devicesUuid.map(async (deviceUuid) => { + const deviceDetails = await this.getDeviceByDeviceUuid( + deviceUuid, + true, + projectUuid, + ); + const result = await this.factoryResetDeviceTuya( + deviceDetails.deviceTuyaUuid, + ); + return { deviceUuid, result }; + }), + ); + + // Separate successful and failed operations + const successResults = []; + const failedResults = []; + + for (const result of results) { + if (result.status === DeviceStatuses.FULLFILLED) { + const { deviceUuid, result: operationResult } = result.value; + + if (operationResult.success) { + // Add to success results if operationResult.success is true + successResults.push({ deviceUuid, result: operationResult }); + // Update isActive to false in the repository for the successfully reset device + await this.deviceRepository.update( + { uuid: deviceUuid }, + { isActive: false }, + ); + } else { + // Add to failed results if operationResult.success is false + failedResults.push({ deviceUuid, error: operationResult.msg }); + } + } else { + // Add to failed results if promise is rejected + failedResults.push({ + deviceUuid: devicesUuid[results.indexOf(result)], + error: result.reason.message, + }); + } + } + + return { successResults, failedResults }; + } catch (error) { + if (error instanceof HttpException) { + throw error; + } + throw new HttpException( + error.message || 'Device Not Found', + error.status || HttpStatus.NOT_FOUND, + ); + } + } + private async updateDeviceTuyaUuidFromTuya(device: DeviceEntity) { + console.log(`looking for device with Tuya UUID: ${device.deviceTuyaUuid}`); + try { + let last_id = null; + let deviceFound = false; + + while (!deviceFound) { + const path = `/v2.0/cloud/thing/device`; + const response = await this.tuya.request({ + method: 'GET', + path, + query: { + product_ids: device.productDevice.prodId, + page_size: 20, // maximum allowed by Tuya + last_id, + }, + }); + if ( + !response.success || + !response.result || + !(response.result as any[]).length + ) { + throw new NotFoundException('Device not found in Tuya'); + } + + const devicesTuya = response.result as any[]; + for (const dev of devicesTuya) { + if (dev.uuid == device.deviceTuyaConstUuid) { + deviceFound = true; + device.deviceTuyaUuid = dev.id; + break; + } + } + if (!deviceFound) { + last_id = devicesTuya[devicesTuya.length - 1].id; + } + } + console.log(`found device with Tuya UUID: ${device.deviceTuyaUuid}`); + return this.deviceRepository.save(device); + } catch (error) { + console.log(error); + throw new HttpException( + 'Error fetching device by product ID from Tuya', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + private async getMacAddressByDeviceIdTuya( + deviceId: string, + ): Promise { + try { + const path = `/v1.0/devices/factory-infos?device_ids=${deviceId}`; + const response = await this.tuya.request({ + method: 'GET', + path, + }); + + return response.result[0]; + } catch (error) { + throw new HttpException( + 'Error fetching mac address device from Tuya', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + private async getDeviceInstructionByDeviceIdTuya( + deviceId: string, + ): Promise { + try { + const path = `/v1.0/iot-03/devices/${deviceId}/functions`; + const response = await this.tuya.request({ + method: 'GET', + path, + }); + return response as GetDeviceDetailsFunctionsInterface; + } catch (error) { + throw new HttpException( + 'Error fetching device functions from Tuya', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + private async getDevicesInstructionStatus( + deviceUuid: string, + projectUuid: string, + ) { + try { + const deviceDetails = await this.getDeviceByDeviceUuid( + deviceUuid, + true, + projectUuid, + ); + + if (!deviceDetails) { + throw new NotFoundException('Device Not Found'); + } + const deviceStatus = await this.getDevicesInstructionStatusTuya( + deviceDetails.deviceTuyaUuid, + ); + + return new SuccessResponseDto({ + message: `Device instructions status fetched successfully`, + data: { + productUuid: deviceDetails.productDevice.uuid, + productType: deviceDetails.productDevice.prodType, + status: deviceStatus.result[0].status, + }, + statusCode: HttpStatus.OK, + }); + } catch (error) { + throw new HttpException( + 'Error fetching device functions status', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + private async getDevicesInstructionStatusTuya( + deviceUuid: string, + ): Promise { + try { + const path = `/v1.0/iot-03/devices/status`; + const response = await this.tuya.request({ + method: 'GET', + path, + query: { + device_ids: deviceUuid, + }, + }); + return response as GetDeviceDetailsFunctionsStatusInterface; + } catch (error) { + throw new HttpException( + 'Error fetching device functions status from Tuya', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + private async getDevicesInGatewayTuya( + deviceId: string, + ): Promise { + try { + const path = `/v1.0/devices/${deviceId}/sub-devices`; + const response: any = await this.tuya.request({ + method: 'GET', + path, + }); + const camelCaseResponse = response.result.map((device: any) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { product_id, category, ...rest } = device; + const camelCaseDevice = convertKeysToCamelCase({ ...rest }); + return camelCaseDevice as GetDeviceDetailsInterface[]; + }); + + return camelCaseResponse; + } catch (error) { + throw new HttpException( + 'Error fetching device details from Tuya', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + private async updateDeviceFirmwareTuya( + deviceUuid: string, + firmwareVersion: number, + ): Promise { + try { + const path = `/v2.0/cloud/thing/${deviceUuid}/firmware/${firmwareVersion}`; + const response = await this.tuya.request({ + method: 'POST', + path, + }); + + return response as updateDeviceFirmwareInterface; + } catch (error) { + throw new HttpException( + 'Error updating device firmware from Tuya', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + private async getDeviceLogsTuya( + deviceId: string, + code: string, + startTime: string = (Date.now() - 1 * 60 * 60 * 1000).toString(), + endTime: string = Date.now().toString(), + ): Promise { + try { + const path = `/v2.0/cloud/thing/${deviceId}/report-logs?start_time=${startTime}&end_time=${endTime}&codes=${code}&size=50`; + const response = await this.tuya.request({ + method: 'GET', + path, + }); + // Convert keys to camel case + const camelCaseResponse = convertKeysToCamelCase(response); + const logs = camelCaseResponse.result.logs ?? []; + return { + startTime, + endTime, + data: logs, + } as getDeviceLogsInterface; + } catch (error) { + throw new HttpException( + 'Error fetching device logs from Tuya', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + private async getPowerClampInstructionStatus(deviceDetails: any) { + try { + const deviceStatus = await this.getPowerClampInstructionStatusTuya( + deviceDetails.deviceTuyaUuid, + ); + const statusList = deviceStatus.result.properties as { + code: string; + value: any; + }[]; + + const groupedStatus = statusList.reduce( + (acc, currentStatus) => { + const { code } = currentStatus; + + if (code.endsWith('A')) { + acc.phaseA.push(currentStatus); + } else if (code.endsWith('B')) { + acc.phaseB.push(currentStatus); + } else if (code.endsWith('C')) { + acc.phaseC.push(currentStatus); + } else { + acc.general.push(currentStatus); + } + return acc; + }, + { + phaseA: [] as { code: string; value: any }[], + phaseB: [] as { code: string; value: any }[], + phaseC: [] as { code: string; value: any }[], + general: [] as { code: string; value: any }[], + }, + ); + + return new SuccessResponseDto({ + message: `Power clamp functions status fetched successfully`, + data: { + productUuid: deviceDetails.productDevice.uuid, + productType: deviceDetails.productDevice.prodType, + status: { + phaseA: groupedStatus.phaseA, + phaseB: groupedStatus.phaseB, + phaseC: groupedStatus.phaseC, + general: groupedStatus.general, + }, + }, + statusCode: HttpStatus.OK, + }); + } catch (error) { + throw new HttpException( + error.message || 'Error fetching power clamp functions status', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + private async getPowerClampInstructionStatusTuya( + deviceUuid: string, + ): Promise { + try { + const path = `/v2.0/cloud/thing/${deviceUuid}/shadow/properties`; + const response = await this.tuya.request({ + method: 'GET', + path, + query: { + device_ids: deviceUuid, + }, + }); + const camelCaseResponse = convertKeysToCamelCase(response); + return camelCaseResponse as GetPowerClampFunctionsStatusInterface; + } catch (error) { + throw new HttpException( + 'Error fetching power clamp functions status from Tuya', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + private async fetchAncestors(space: SpaceEntity): Promise { + const ancestors: SpaceEntity[] = []; + + let currentSpace = space; + while (currentSpace && currentSpace.parent) { + // Fetch the parent space + const parent = await this.spaceRepository.findOne({ + where: { uuid: currentSpace.parent.uuid }, + relations: ['parent'], // To continue fetching upwards + }); + + if (parent) { + ancestors.push(parent); + currentSpace = parent; + } else { + currentSpace = null; + } + } + + // Return the ancestors in reverse order to have the root at the start + return ancestors.reverse(); + } + private async validateProject(uuid: string) { + const project = await this.projectRepository.findOne({ + where: { uuid }, + }); + if (!project) { + throw new HttpException( + `A project with the uuid '${uuid}' doesn't exists.`, + HttpStatus.BAD_REQUEST, + ); + } + return project; + } async getAllDevicesBySpaceOrCommunityWithChild( query: GetDevicesBySpaceOrCommunityDto, ): Promise { @@ -1686,7 +1801,9 @@ export class DeviceService { ); } } - async getAllDevicesBySpace(spaceUuid: string): Promise { + private async getAllDevicesBySpace( + spaceUuid: string, + ): Promise { const space = await this.spaceRepository.findOne({ where: { uuid: spaceUuid }, relations: ['children', 'devices', 'devices.productDevice'], @@ -1720,7 +1837,7 @@ export class DeviceService { return allDevices; } - async getAllDevicesByCommunity( + private async getAllDevicesByCommunity( communityUuid: string, ): Promise { const community = await this.communityRepository.findOne({ @@ -1769,39 +1886,4 @@ export class DeviceService { return allDevices; } - - async addDevicesToOrphanSpace( - space: SpaceEntity, - project: ProjectEntity, - queryRunner: QueryRunner, - ) { - const spaceRepository = queryRunner.manager.getRepository(SpaceEntity); - const deviceRepository = queryRunner.manager.getRepository(DeviceEntity); - try { - const orphanSpace = await spaceRepository.findOne({ - where: { - community: { - name: `${ORPHAN_COMMUNITY_NAME}-${project.name}`, - }, - spaceName: ORPHAN_SPACE_NAME, - }, - }); - - if (!orphanSpace) { - throw new HttpException( - `Orphan space not found in community ${project.name}`, - HttpStatus.NOT_FOUND, - ); - } - - await deviceRepository.update( - { uuid: In(space.devices.map((device) => device.uuid)) }, - { spaceDevice: orphanSpace }, - ); - } catch (error) { - throw new Error( - `Failed to add devices to orphan spaces: ${error.message}`, - ); - } - } } From 18b21d697c5f1c6642df34efc4f6855ce6134dd9 Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Tue, 8 Jul 2025 09:02:24 +0300 Subject: [PATCH 58/86] SP-1753 Feat/booking system (#454) * task: add get all bookable spaces API (#453) * task: add non bookable space API --- libs/common/src/constants/controller-route.ts | 6 + .../booking/entities/bookable-space.entity.ts | 13 +- package-lock.json | 3922 +++++++---------- package.json | 2 +- .../controllers/bookable-space.controller.ts | 62 +- .../dtos/bookable-space-request.dto.ts | 31 + .../dtos/bookable-space-response.dto.ts | 58 + .../services/bookable-space.service.ts | 71 +- 8 files changed, 1818 insertions(+), 2347 deletions(-) create mode 100644 src/booking/dtos/bookable-space-request.dto.ts create mode 100644 src/booking/dtos/bookable-space-response.dto.ts diff --git a/libs/common/src/constants/controller-route.ts b/libs/common/src/constants/controller-route.ts index 6e0b948..dedf0f1 100644 --- a/libs/common/src/constants/controller-route.ts +++ b/libs/common/src/constants/controller-route.ts @@ -77,6 +77,12 @@ export class ControllerRoute { public static readonly ADD_BOOKABLE_SPACES_DESCRIPTION = 'This endpoint allows you to add new bookable spaces by providing the required details.'; + + public static readonly GET_ALL_BOOKABLE_SPACES_SUMMARY = + 'Get all bookable spaces'; + + public static readonly GET_ALL_BOOKABLE_SPACES_DESCRIPTION = + 'This endpoint retrieves all bookable spaces.'; }; }; static COMMUNITY = class { diff --git a/libs/common/src/modules/booking/entities/bookable-space.entity.ts b/libs/common/src/modules/booking/entities/bookable-space.entity.ts index 3db1165..8e53cba 100644 --- a/libs/common/src/modules/booking/entities/bookable-space.entity.ts +++ b/libs/common/src/modules/booking/entities/bookable-space.entity.ts @@ -1,14 +1,14 @@ +import { DaysEnum } from '@app/common/constants/days.enum'; import { - Entity, Column, CreateDateColumn, - UpdateDateColumn, - OneToOne, + Entity, JoinColumn, + OneToOne, + UpdateDateColumn, } from 'typeorm'; -import { SpaceEntity } from '../../space/entities/space.entity'; -import { DaysEnum } from '@app/common/constants/days.enum'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; +import { SpaceEntity } from '../../space/entities/space.entity'; @Entity('bookable-space') export class BookableSpaceEntity extends AbstractEntity { @@ -37,6 +37,9 @@ export class BookableSpaceEntity extends AbstractEntity { @Column({ type: 'time' }) endTime: string; + @Column({ type: Boolean, default: true }) + active: boolean; + @Column({ type: 'int' }) points: number; diff --git a/package-lock.json b/package-lock.json index e8718b3..04c97a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,7 +44,7 @@ "onesignal-node": "^3.4.0", "passport-jwt": "^4.0.1", "pg": "^8.11.3", - "reflect-metadata": "^0.2.0", + "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", "typeorm": "^0.3.20", "winston": "^3.17.0", @@ -77,6 +77,10 @@ "ts-node": "^10.9.1", "tsconfig-paths": "^4.2.0", "typescript": "^5.1.3" + }, + "engines": { + "node": "20.x", + "npm": "10.x" } }, "node_modules/@ampproject/remapping": { @@ -84,7 +88,6 @@ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dev": true, - "license": "Apache-2.0", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -98,7 +101,6 @@ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.3.11.tgz", "integrity": "sha512-vTNDYNsLIWpYk2I969LMQFH29GTsLzxNk/0cLw5q56ARF0v5sIWfHYwGTS88jdDqIpuuettcSczbxeA7EuAmqQ==", "dev": true, - "license": "MIT", "dependencies": { "ajv": "8.12.0", "ajv-formats": "2.1.1", @@ -121,12 +123,20 @@ } } }, + "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.11", "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-17.3.11.tgz", "integrity": "sha512-I5wviiIqiFwar9Pdk30Lujk8FczEEc18i22A5c6Z9lbmhPQdTroDnEQdsfXjy404wPe8H62s0I15o4pmMGfTYQ==", "dev": true, - "license": "MIT", "dependencies": { "@angular-devkit/core": "17.3.11", "jsonc-parser": "3.2.1", @@ -145,7 +155,6 @@ "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-17.3.11.tgz", "integrity": "sha512-kcOMqp+PHAKkqRad7Zd7PbpqJ0LqLaNZdY1+k66lLWmkEBozgq8v4ASn/puPWf9Bo0HpCiK+EzLf0VHE8Z/y6Q==", "dev": true, - "license": "MIT", "dependencies": { "@angular-devkit/core": "17.3.11", "@angular-devkit/schematics": "17.3.11", @@ -168,7 +177,6 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", "dev": true, - "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, @@ -181,7 +189,6 @@ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", "dev": true, - "license": "ISC", "engines": { "node": ">= 12" } @@ -191,7 +198,6 @@ "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.15.tgz", "integrity": "sha512-vI2w4zl/mDluHt9YEQ/543VTCwPKWiHzKtm9dM2V0NdFcqEexDAjUHzO1oA60HRNaVifGXXM1tRRNluLVHa0Kg==", "dev": true, - "license": "MIT", "dependencies": { "@ljharb/through": "^2.3.12", "ansi-escapes": "^4.3.2", @@ -218,7 +224,6 @@ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", "dev": true, - "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -228,53 +233,72 @@ "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.12.0" } }, - "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "node_modules/@angular-devkit/schematics-cli/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", - "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", + "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.9.tgz", - "integrity": "sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", + "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", "dev": true, - "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.9", - "@babel/helper-compilation-targets": "^7.26.5", - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.9", - "@babel/parser": "^7.26.9", - "@babel/template": "^7.26.9", - "@babel/traverse": "^7.26.9", - "@babel/types": "^7.26.9", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.6", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -294,22 +318,20 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/generator": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.9.tgz", - "integrity": "sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", + "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.9", - "@babel/types": "^7.26.9", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", + "@babel/parser": "^7.28.0", + "@babel/types": "^7.28.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" }, "engines": { @@ -317,14 +339,13 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", - "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", + "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, - "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.26.5", - "@babel/helper-validator-option": "^7.25.9", + "@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" @@ -338,35 +359,41 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "license": "ISC", "bin": { "semver": "bin/semver.js" } }, - "node_modules/@babel/helper-module-imports": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", - "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "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, - "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", - "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "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, - "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.3" }, "engines": { "node": ">=6.9.0" @@ -376,67 +403,61 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", - "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "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, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "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==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", - "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "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, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.9.tgz", - "integrity": "sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==", + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", + "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/template": "^7.26.9", - "@babel/types": "^7.26.9" + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz", - "integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", + "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/types": "^7.26.9" + "@babel/types": "^7.28.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -450,7 +471,6 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -463,7 +483,6 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -476,7 +495,6 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.12.13" }, @@ -489,7 +507,6 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -501,13 +518,12 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", - "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", + "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, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -521,7 +537,6 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -534,7 +549,6 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -543,13 +557,12 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", - "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", + "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, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -563,7 +576,6 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -576,7 +588,6 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -589,7 +600,6 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -602,7 +612,6 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -615,7 +624,6 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -628,7 +636,6 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -641,7 +648,6 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -657,7 +663,6 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -669,13 +674,12 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", - "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", + "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, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -685,71 +689,54 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.9.tgz", - "integrity": "sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==", + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", + "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==", "dev": true, - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", - "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/parser": "^7.26.9", - "@babel/types": "^7.26.9" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.9.tgz", - "integrity": "sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", + "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.9", - "@babel/parser": "^7.26.9", - "@babel/template": "^7.26.9", - "@babel/types": "^7.26.9", - "debug": "^4.3.1", - "globals": "^11.1.0" + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.0", + "debug": "^4.3.1" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/types": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz", - "integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.0.tgz", + "integrity": "sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -759,15 +746,13 @@ "version": "0.2.3", "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", "dev": true, - "license": "MIT", "optional": true, "engines": { "node": ">=0.1.90" @@ -778,7 +763,6 @@ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "devOptional": true, - "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -791,7 +775,6 @@ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "devOptional": true, - "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -801,7 +784,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", - "license": "MIT", "dependencies": { "colorspace": "1.1.x", "enabled": "2.0.x", @@ -809,11 +791,10 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", - "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "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, - "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.4.3" }, @@ -832,7 +813,6 @@ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, - "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -842,7 +822,6 @@ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, - "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -866,7 +845,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -879,11 +857,10 @@ } }, "node_modules/@eslint/eslintrc/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==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -893,15 +870,13 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -914,7 +889,6 @@ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, - "license": "MIT", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } @@ -923,7 +897,6 @@ "version": "5.0.2", "resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-5.0.2.tgz", "integrity": "sha512-fRYcWvI8vs0Zxa/8fXd/QlmQYWWkJqKZPAXM+vksnplb3owQFKTPPh9JqOtD0L3flQw/AZjjXdPkD7Kp/uHm8g==", - "license": "MIT", "dependencies": { "lodash.escaperegexp": "^4.1.2", "lodash.isboolean": "^3.0.3", @@ -936,7 +909,6 @@ "version": "0.10.8", "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.8.tgz", "integrity": "sha512-CVnHcS4iRJPqtIDc411+UmFldk0ShSK3OB+D0bKD8Ck5Vro6dbK5+APZpkuWpbfdL359DIQUnAaMLE+zs/PVyA==", - "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.9", "@firebase/installations": "0.6.9", @@ -952,7 +924,6 @@ "version": "0.2.14", "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.14.tgz", "integrity": "sha512-unRVY6SvRqfNFIAA/kwl4vK+lvQAL2HVcgu9zTrUtTyYDmtIt/lOuHJynBMYEgLnKm39YKBDhtqdapP2e++ASw==", - "license": "Apache-2.0", "dependencies": { "@firebase/analytics": "0.10.8", "@firebase/analytics-types": "0.8.2", @@ -967,14 +938,12 @@ "node_modules/@firebase/analytics-types": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.2.tgz", - "integrity": "sha512-EnzNNLh+9/sJsimsA/FGqzakmrAUKLeJvjRHlg8df1f97NLUlFidk9600y0ZgWOp3CAxn6Hjtk+08tixlUOWyw==", - "license": "Apache-2.0" + "integrity": "sha512-EnzNNLh+9/sJsimsA/FGqzakmrAUKLeJvjRHlg8df1f97NLUlFidk9600y0ZgWOp3CAxn6Hjtk+08tixlUOWyw==" }, "node_modules/@firebase/app": { "version": "0.10.13", "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.10.13.tgz", "integrity": "sha512-OZiDAEK/lDB6xy/XzYAyJJkaDqmQ+BCtOEPLqFvxWKUz5JbBmej7IiiRHdtiIOD/twW7O5AxVsfaaGA/V1bNsA==", - "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.9", "@firebase/logger": "0.4.2", @@ -987,7 +956,6 @@ "version": "0.8.8", "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.8.8.tgz", "integrity": "sha512-O49RGF1xj7k6BuhxGpHmqOW5hqBIAEbt2q6POW0lIywx7emYtzPDeQI+ryQpC4zbKX646SoVZ711TN1DBLNSOQ==", - "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.9", "@firebase/logger": "0.4.2", @@ -1002,7 +970,6 @@ "version": "0.3.15", "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.15.tgz", "integrity": "sha512-zFIvIFFNqDXpOT2huorz9cwf56VT3oJYRFjSFYdSbGYEJYEaXjLJbfC79lx/zjx4Fh+yuN8pry3TtvwaevrGbg==", - "license": "Apache-2.0", "dependencies": { "@firebase/app-check": "0.8.8", "@firebase/app-check-types": "0.5.2", @@ -1018,20 +985,17 @@ "node_modules/@firebase/app-check-interop-types": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.2.tgz", - "integrity": "sha512-LMs47Vinv2HBMZi49C09dJxp0QT5LwDzFaVGf/+ITHe3BlIhUiLNttkATSXplc89A2lAaeTqjgqVkiRfUGyQiQ==", - "license": "Apache-2.0" + "integrity": "sha512-LMs47Vinv2HBMZi49C09dJxp0QT5LwDzFaVGf/+ITHe3BlIhUiLNttkATSXplc89A2lAaeTqjgqVkiRfUGyQiQ==" }, "node_modules/@firebase/app-check-types": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.2.tgz", - "integrity": "sha512-FSOEzTzL5bLUbD2co3Zut46iyPWML6xc4x+78TeaXMSuJap5QObfb+rVvZJtla3asN4RwU7elaQaduP+HFizDA==", - "license": "Apache-2.0" + "integrity": "sha512-FSOEzTzL5bLUbD2co3Zut46iyPWML6xc4x+78TeaXMSuJap5QObfb+rVvZJtla3asN4RwU7elaQaduP+HFizDA==" }, "node_modules/@firebase/app-compat": { "version": "0.2.43", "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.43.tgz", "integrity": "sha512-HM96ZyIblXjAC7TzE8wIk2QhHlSvksYkQ4Ukh1GmEenzkucSNUmUX4QvoKrqeWsLEQ8hdcojABeCV8ybVyZmeg==", - "license": "Apache-2.0", "dependencies": { "@firebase/app": "0.10.13", "@firebase/component": "0.6.9", @@ -1043,14 +1007,12 @@ "node_modules/@firebase/app-types": { "version": "0.9.2", "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.2.tgz", - "integrity": "sha512-oMEZ1TDlBz479lmABwWsWjzHwheQKiAgnuKxE0pz0IXCVx7/rtlkx1fQ6GfgK24WCrxDKMplZrT50Kh04iMbXQ==", - "license": "Apache-2.0" + "integrity": "sha512-oMEZ1TDlBz479lmABwWsWjzHwheQKiAgnuKxE0pz0IXCVx7/rtlkx1fQ6GfgK24WCrxDKMplZrT50Kh04iMbXQ==" }, "node_modules/@firebase/auth": { "version": "1.7.9", "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.7.9.tgz", "integrity": "sha512-yLD5095kVgDw965jepMyUrIgDklD6qH/BZNHeKOgvu7pchOKNjVM+zQoOVYJIKWMWOWBq8IRNVU6NXzBbozaJg==", - "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.9", "@firebase/logger": "0.4.2", @@ -1072,7 +1034,6 @@ "version": "0.5.14", "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.14.tgz", "integrity": "sha512-2eczCSqBl1KUPJacZlFpQayvpilg3dxXLy9cSMTKtQMTQSmondUtPI47P3ikH3bQAXhzKLOE+qVxJ3/IRtu9pw==", - "license": "Apache-2.0", "dependencies": { "@firebase/auth": "1.7.9", "@firebase/auth-types": "0.12.2", @@ -1088,14 +1049,12 @@ "node_modules/@firebase/auth-interop-types": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.3.tgz", - "integrity": "sha512-Fc9wuJGgxoxQeavybiuwgyi+0rssr76b+nHpj+eGhXFYAdudMWyfBHvFL/I5fEHniUM/UQdFzi9VXJK2iZF7FQ==", - "license": "Apache-2.0" + "integrity": "sha512-Fc9wuJGgxoxQeavybiuwgyi+0rssr76b+nHpj+eGhXFYAdudMWyfBHvFL/I5fEHniUM/UQdFzi9VXJK2iZF7FQ==" }, "node_modules/@firebase/auth-types": { "version": "0.12.2", "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.12.2.tgz", "integrity": "sha512-qsEBaRMoGvHO10unlDJhaKSuPn4pyoTtlQuP1ghZfzB6rNQPuhp/N/DcFZxm9i4v0SogjCbf9reWupwIvfmH6w==", - "license": "Apache-2.0", "peerDependencies": { "@firebase/app-types": "0.x", "@firebase/util": "1.x" @@ -1105,7 +1064,6 @@ "version": "0.6.9", "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.9.tgz", "integrity": "sha512-gm8EUEJE/fEac86AvHn8Z/QW8BvR56TBw3hMW0O838J/1mThYQXAIQBgUv75EqlCZfdawpWLrKt1uXvp9ciK3Q==", - "license": "Apache-2.0", "dependencies": { "@firebase/util": "1.10.0", "tslib": "^2.1.0" @@ -1115,7 +1073,6 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/@firebase/data-connect/-/data-connect-0.1.0.tgz", "integrity": "sha512-vSe5s8dY13ilhLnfY0eYRmQsdTbH7PUFZtBbqU6JVX/j8Qp9A6G5gG6//ulbX9/1JFOF1IWNOne9c8S/DOCJaQ==", - "license": "Apache-2.0", "dependencies": { "@firebase/auth-interop-types": "0.2.3", "@firebase/component": "0.6.9", @@ -1131,7 +1088,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.8.tgz", "integrity": "sha512-dzXALZeBI1U5TXt6619cv0+tgEhJiwlUtQ55WNZY7vGAjv7Q1QioV969iYwt1AQQ0ovHnEW0YW9TiBfefLvErg==", - "license": "Apache-2.0", "dependencies": { "@firebase/app-check-interop-types": "0.3.2", "@firebase/auth-interop-types": "0.2.3", @@ -1146,7 +1102,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-1.0.8.tgz", "integrity": "sha512-OpeWZoPE3sGIRPBKYnW9wLad25RaWbGyk7fFQe4xnJQKRzlynWeFBSRRAoLE2Old01WXwskUiucNqUUVlFsceg==", - "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.9", "@firebase/database": "1.0.8", @@ -1160,7 +1115,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.5.tgz", "integrity": "sha512-fTlqCNwFYyq/C6W7AJ5OCuq5CeZuBEsEwptnVxlNPkWCo5cTTyukzAHRSO/jaQcItz33FfYrrFk1SJofcu2AaQ==", - "license": "Apache-2.0", "dependencies": { "@firebase/app-types": "0.9.2", "@firebase/util": "1.10.0" @@ -1170,7 +1124,6 @@ "version": "4.7.3", "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.7.3.tgz", "integrity": "sha512-NwVU+JPZ/3bhvNSJMCSzfcBZZg8SUGyzZ2T0EW3/bkUeefCyzMISSt/TTIfEHc8cdyXGlMqfGe3/62u9s74UEg==", - "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.9", "@firebase/logger": "0.4.2", @@ -1192,7 +1145,6 @@ "version": "0.3.38", "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.38.tgz", "integrity": "sha512-GoS0bIMMkjpLni6StSwRJarpu2+S5m346Na7gr9YZ/BZ/W3/8iHGNr9PxC+f0rNZXqS4fGRn88pICjrZEgbkqQ==", - "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.9", "@firebase/firestore": "4.7.3", @@ -1208,7 +1160,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.2.tgz", "integrity": "sha512-wp1A+t5rI2Qc/2q7r2ZpjUXkRVPtGMd6zCLsiWurjsQpqPgFin3AhNibKcIzoF2rnToNa/XYtyWXuifjOOwDgg==", - "license": "Apache-2.0", "peerDependencies": { "@firebase/app-types": "0.x", "@firebase/util": "1.x" @@ -1218,7 +1169,6 @@ "version": "0.11.8", "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.11.8.tgz", "integrity": "sha512-Lo2rTPDn96naFIlSZKVd1yvRRqqqwiJk7cf9TZhUerwnPKgBzXy+aHE22ry+6EjCaQusUoNai6mU6p+G8QZT1g==", - "license": "Apache-2.0", "dependencies": { "@firebase/app-check-interop-types": "0.3.2", "@firebase/auth-interop-types": "0.2.3", @@ -1236,7 +1186,6 @@ "version": "0.3.14", "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.14.tgz", "integrity": "sha512-dZ0PKOKQFnOlMfcim39XzaXonSuPPAVuzpqA4ONTIdyaJK/OnBaIEVs/+BH4faa1a2tLeR+Jy15PKqDRQoNIJw==", - "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.9", "@firebase/functions": "0.11.8", @@ -1251,14 +1200,12 @@ "node_modules/@firebase/functions-types": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.2.tgz", - "integrity": "sha512-0KiJ9lZ28nS2iJJvimpY4nNccV21rkQyor5Iheu/nq8aKXJqtJdeSlZDspjPSBBiHRzo7/GMUttegnsEITqR+w==", - "license": "Apache-2.0" + "integrity": "sha512-0KiJ9lZ28nS2iJJvimpY4nNccV21rkQyor5Iheu/nq8aKXJqtJdeSlZDspjPSBBiHRzo7/GMUttegnsEITqR+w==" }, "node_modules/@firebase/installations": { "version": "0.6.9", "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.9.tgz", "integrity": "sha512-hlT7AwCiKghOX3XizLxXOsTFiFCQnp/oj86zp1UxwDGmyzsyoxtX+UIZyVyH/oBF5+XtblFG9KZzZQ/h+dpy+Q==", - "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.9", "@firebase/util": "1.10.0", @@ -1273,7 +1220,6 @@ "version": "0.2.9", "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.9.tgz", "integrity": "sha512-2lfdc6kPXR7WaL4FCQSQUhXcPbI7ol3wF+vkgtU25r77OxPf8F/VmswQ7sgIkBBWtymn5ZF20TIKtnOj9rjb6w==", - "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.9", "@firebase/installations": "0.6.9", @@ -1289,7 +1235,6 @@ "version": "0.5.2", "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.2.tgz", "integrity": "sha512-que84TqGRZJpJKHBlF2pkvc1YcXrtEDOVGiDjovP/a3s6W4nlbohGXEsBJo0JCeeg/UG9A+DEZVDUV9GpklUzA==", - "license": "Apache-2.0", "peerDependencies": { "@firebase/app-types": "0.x" } @@ -1298,7 +1243,6 @@ "version": "0.4.2", "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.2.tgz", "integrity": "sha512-Q1VuA5M1Gjqrwom6I6NUU4lQXdo9IAQieXlujeHZWvRt1b7qQ0KwBaNAjgxG27jgF9/mUwsNmO8ptBCGVYhB0A==", - "license": "Apache-2.0", "dependencies": { "tslib": "^2.1.0" } @@ -1307,7 +1251,6 @@ "version": "0.12.12", "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.12.tgz", "integrity": "sha512-6q0pbzYBJhZEtUoQx7hnPhZvAbuMNuBXKQXOx2YlWhSrlv9N1m0ZzlNpBbu/ItTzrwNKTibdYzUyaaxdWLg+4w==", - "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.9", "@firebase/installations": "0.6.9", @@ -1324,7 +1267,6 @@ "version": "0.2.12", "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.12.tgz", "integrity": "sha512-pKsiUVZrbmRgdImYqhBNZlkKJbqjlPkVdQRZGRbkTyX4OSGKR0F/oJeCt1a8jEg5UnBp4fdVwSWSp4DuCovvEQ==", - "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.9", "@firebase/messaging": "0.12.12", @@ -1338,14 +1280,12 @@ "node_modules/@firebase/messaging-interop-types": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.2.tgz", - "integrity": "sha512-l68HXbuD2PPzDUOFb3aG+nZj5KA3INcPwlocwLZOzPp9rFM9yeuI9YLl6DQfguTX5eAGxO0doTR+rDLDvQb5tA==", - "license": "Apache-2.0" + "integrity": "sha512-l68HXbuD2PPzDUOFb3aG+nZj5KA3INcPwlocwLZOzPp9rFM9yeuI9YLl6DQfguTX5eAGxO0doTR+rDLDvQb5tA==" }, "node_modules/@firebase/performance": { "version": "0.6.9", "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.6.9.tgz", "integrity": "sha512-PnVaak5sqfz5ivhua+HserxTJHtCar/7zM0flCX6NkzBNzJzyzlH4Hs94h2Il0LQB99roBqoE5QT1JqWqcLJHQ==", - "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.9", "@firebase/installations": "0.6.9", @@ -1361,7 +1301,6 @@ "version": "0.2.9", "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.9.tgz", "integrity": "sha512-dNl95IUnpsu3fAfYBZDCVhXNkASE0uo4HYaEPd2/PKscfTvsgqFAOxfAXzBEDOnynDWiaGUnb5M1O00JQ+3FXA==", - "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.9", "@firebase/logger": "0.4.2", @@ -1377,14 +1316,12 @@ "node_modules/@firebase/performance-types": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.2.tgz", - "integrity": "sha512-gVq0/lAClVH5STrIdKnHnCo2UcPLjJlDUoEB/tB4KM+hAeHUxWKnpT0nemUPvxZ5nbdY/pybeyMe8Cs29gEcHA==", - "license": "Apache-2.0" + "integrity": "sha512-gVq0/lAClVH5STrIdKnHnCo2UcPLjJlDUoEB/tB4KM+hAeHUxWKnpT0nemUPvxZ5nbdY/pybeyMe8Cs29gEcHA==" }, "node_modules/@firebase/remote-config": { "version": "0.4.9", "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.4.9.tgz", "integrity": "sha512-EO1NLCWSPMHdDSRGwZ73kxEEcTopAxX1naqLJFNApp4hO8WfKfmEpmjxmP5TrrnypjIf2tUkYaKsfbEA7+AMmA==", - "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.9", "@firebase/installations": "0.6.9", @@ -1400,7 +1337,6 @@ "version": "0.2.9", "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.9.tgz", "integrity": "sha512-AxzGpWfWFYejH2twxfdOJt5Cfh/ATHONegTd/a0p5flEzsD5JsxXgfkFToop+mypEL3gNwawxrxlZddmDoNxyA==", - "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.9", "@firebase/logger": "0.4.2", @@ -1416,14 +1352,12 @@ "node_modules/@firebase/remote-config-types": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.3.2.tgz", - "integrity": "sha512-0BC4+Ud7y2aPTyhXJTMTFfrGGLqdYXrUB9sJVAB8NiqJswDTc4/2qrE/yfUbnQJhbSi6ZaTTBKyG3n1nplssaA==", - "license": "Apache-2.0" + "integrity": "sha512-0BC4+Ud7y2aPTyhXJTMTFfrGGLqdYXrUB9sJVAB8NiqJswDTc4/2qrE/yfUbnQJhbSi6ZaTTBKyG3n1nplssaA==" }, "node_modules/@firebase/storage": { "version": "0.13.2", "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.13.2.tgz", "integrity": "sha512-fxuJnHshbhVwuJ4FuISLu+/76Aby2sh+44ztjF2ppoe0TELIDxPW6/r1KGlWYt//AD0IodDYYA8ZTN89q8YqUw==", - "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.9", "@firebase/util": "1.10.0", @@ -1438,7 +1372,6 @@ "version": "0.3.12", "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.12.tgz", "integrity": "sha512-hA4VWKyGU5bWOll+uwzzhEMMYGu9PlKQc1w4DWxB3aIErWYzonrZjF0icqNQZbwKNIdh8SHjZlFeB2w6OSsjfg==", - "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.9", "@firebase/storage": "0.13.2", @@ -1454,7 +1387,6 @@ "version": "0.8.2", "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.2.tgz", "integrity": "sha512-0vWu99rdey0g53lA7IShoA2Lol1jfnPovzLDUBuon65K7uKG9G+L5uO05brD9pMw+l4HRFw23ah3GwTGpEav6g==", - "license": "Apache-2.0", "peerDependencies": { "@firebase/app-types": "0.x", "@firebase/util": "1.x" @@ -1464,7 +1396,6 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.10.0.tgz", "integrity": "sha512-xKtx4A668icQqoANRxyDLBLz51TAbDP9KRfpbKGxiCAW346d0BeJe5vN6/hKxxmWwnZ0mautyv39JxviwwQMOQ==", - "license": "Apache-2.0", "dependencies": { "tslib": "^2.1.0" } @@ -1473,7 +1404,6 @@ "version": "0.0.4", "resolved": "https://registry.npmjs.org/@firebase/vertexai-preview/-/vertexai-preview-0.0.4.tgz", "integrity": "sha512-EBSqyu9eg8frQlVU9/HjKtHN7odqbh9MtAcVz3WwHj4gLCLOoN9F/o+oxlq3CxvFrd3CNTZwu6d2mZtVlEInng==", - "license": "Apache-2.0", "dependencies": { "@firebase/app-check-interop-types": "0.3.2", "@firebase/component": "0.6.9", @@ -1492,14 +1422,12 @@ "node_modules/@firebase/webchannel-wrapper": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.1.tgz", - "integrity": "sha512-jmEnr/pk0yVkA7mIlHNnxCi+wWzOFUg0WyIotgkKAb2u1J7fAeDBcVNSTjTihbAYNusCLQdW5s9IJ5qwnEufcQ==", - "license": "Apache-2.0" + "integrity": "sha512-jmEnr/pk0yVkA7mIlHNnxCi+wWzOFUg0WyIotgkKAb2u1J7fAeDBcVNSTjTihbAYNusCLQdW5s9IJ5qwnEufcQ==" }, "node_modules/@grpc/grpc-js": { "version": "1.9.15", "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.15.tgz", "integrity": "sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ==", - "license": "Apache-2.0", "dependencies": { "@grpc/proto-loader": "^0.7.8", "@types/node": ">=12.12.47" @@ -1509,10 +1437,9 @@ } }, "node_modules/@grpc/proto-loader": { - "version": "0.7.13", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", - "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", - "license": "Apache-2.0", + "version": "0.7.15", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz", + "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==", "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", @@ -1532,7 +1459,6 @@ "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", "deprecated": "Use @eslint/config-array instead", "dev": true, - "license": "Apache-2.0", "dependencies": { "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", @@ -1543,11 +1469,10 @@ } }, "node_modules/@humanwhocodes/config-array/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==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1558,7 +1483,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -1571,7 +1495,6 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, - "license": "Apache-2.0", "engines": { "node": ">=12.22" }, @@ -1585,20 +1508,17 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "deprecated": "Use @eslint/object-schema instead", - "dev": true, - "license": "BSD-3-Clause" + "dev": true }, "node_modules/@ioredis/commands": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", - "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==", - "license": "MIT" + "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==" }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "license": "ISC", "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -1615,7 +1535,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "license": "MIT", "engines": { "node": ">=12" }, @@ -1627,7 +1546,6 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "license": "MIT", "engines": { "node": ">=12" }, @@ -1638,14 +1556,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==", - "license": "MIT" + "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==", - "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -1662,7 +1578,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -1677,7 +1592,6 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "license": "MIT", "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -1695,7 +1609,6 @@ "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, - "license": "ISC", "dependencies": { "camelcase": "^5.3.1", "find-up": "^4.1.0", @@ -1712,17 +1625,24 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, - "license": "MIT", "dependencies": { "sprintf-js": "~1.0.2" } }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, - "license": "MIT", "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -1736,7 +1656,6 @@ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, - "license": "MIT", "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -1750,7 +1669,6 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, - "license": "MIT", "dependencies": { "p-locate": "^4.1.0" }, @@ -1763,7 +1681,6 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, - "license": "MIT", "dependencies": { "p-try": "^2.0.0" }, @@ -1779,7 +1696,6 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, - "license": "MIT", "dependencies": { "p-limit": "^2.2.0" }, @@ -1792,7 +1708,6 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -1802,7 +1717,6 @@ "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -1812,7 +1726,6 @@ "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", "dev": true, - "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", @@ -1830,7 +1743,6 @@ "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", "dev": true, - "license": "MIT", "dependencies": { "@jest/console": "^29.7.0", "@jest/reporters": "^29.7.0", @@ -1878,7 +1790,6 @@ "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", "dev": true, - "license": "MIT", "dependencies": { "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", @@ -1894,7 +1805,6 @@ "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", "dev": true, - "license": "MIT", "dependencies": { "expect": "^29.7.0", "jest-snapshot": "^29.7.0" @@ -1908,7 +1818,6 @@ "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", "dev": true, - "license": "MIT", "dependencies": { "jest-get-type": "^29.6.3" }, @@ -1921,7 +1830,6 @@ "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", "dev": true, - "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", "@sinonjs/fake-timers": "^10.0.2", @@ -1939,7 +1847,6 @@ "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", "dev": true, - "license": "MIT", "dependencies": { "@jest/environment": "^29.7.0", "@jest/expect": "^29.7.0", @@ -1955,7 +1862,6 @@ "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", "dev": true, - "license": "MIT", "dependencies": { "@bcoe/v8-coverage": "^0.2.3", "@jest/console": "^29.7.0", @@ -1995,11 +1901,10 @@ } }, "node_modules/@jest/reporters/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==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2011,7 +1916,6 @@ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, - "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -2032,7 +1936,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2045,7 +1948,6 @@ "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, - "license": "MIT", "dependencies": { "@sinclair/typebox": "^0.27.8" }, @@ -2058,7 +1960,6 @@ "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", "dev": true, - "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "^0.3.18", "callsites": "^3.0.0", @@ -2073,7 +1974,6 @@ "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", "dev": true, - "license": "MIT", "dependencies": { "@jest/console": "^29.7.0", "@jest/types": "^29.6.3", @@ -2089,7 +1989,6 @@ "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", "dev": true, - "license": "MIT", "dependencies": { "@jest/test-result": "^29.7.0", "graceful-fs": "^4.2.9", @@ -2105,7 +2004,6 @@ "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", "dev": true, - "license": "MIT", "dependencies": { "@babel/core": "^7.11.6", "@jest/types": "^29.6.3", @@ -2132,7 +2030,6 @@ "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, - "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -2146,18 +2043,13 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", "dev": true, - "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { @@ -2165,45 +2057,31 @@ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", - "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.10.tgz", + "integrity": "sha512-0pPkgz9dY+bijgistcTTJ5mR+ocqRXLuhXHYdzoMmmoJ2C9S46RCm2GMUbatPEUK9Yjy26IrAy8D/M00lLkv+Q==", "dev": true, - "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "devOptional": true, - "license": "MIT" + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "devOptional": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", "dev": true, - "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -2214,7 +2092,6 @@ "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.14.tgz", "integrity": "sha512-ajBvlKpWucBB17FuQYUShqpqy8GRgYEpJW0vWJbUu1CV9lWyrDCapy0lScU8T8Z6qn49sSwJB3+M+evYIdGg+A==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8" }, @@ -2226,7 +2103,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==", - "license": "MIT", "engines": { "node": ">=8" } @@ -2234,14 +2110,12 @@ "node_modules/@microsoft/tsdoc": { "version": "0.15.1", "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.1.tgz", - "integrity": "sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==", - "license": "MIT" + "integrity": "sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==" }, "node_modules/@nestjs/axios": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-4.0.0.tgz", "integrity": "sha512-1cB+Jyltu/uUPNQrpUimRHEQHrnQrpLzVj6dU3dgn6iDDDdahr10TgHFGTmw5VuJ9GzKZsCLDL78VSwJAs/9JQ==", - "license": "MIT", "peerDependencies": { "@nestjs/common": "^10.0.0 || ^11.0.0", "axios": "^1.3.1", @@ -2253,7 +2127,6 @@ "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-10.4.9.tgz", "integrity": "sha512-s8qYd97bggqeK7Op3iD49X2MpFtW4LVNLAwXFkfbRxKME6IYT7X0muNTJ2+QfI8hpbNx9isWkrLWIp+g5FOhiA==", "dev": true, - "license": "MIT", "dependencies": { "@angular-devkit/core": "17.3.11", "@angular-devkit/schematics": "17.3.11", @@ -2299,7 +2172,6 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -2313,17 +2185,36 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, - "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } }, + "node_modules/@nestjs/cli/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@nestjs/cli/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "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, - "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -2337,7 +2228,6 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz", "integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==", "dev": true, - "license": "MIT", "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.6", @@ -2380,11 +2270,11 @@ } }, "node_modules/@nestjs/common": { - "version": "10.4.15", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.4.15.tgz", - "integrity": "sha512-vaLg1ZgwhG29BuLDxPA9OAcIlgqzp9/N8iG0wGapyUNTf4IY4O6zAHgN6QalwLhFxq7nOI021vdRojR1oF3bqg==", - "license": "MIT", + "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.8.1", "uid": "2.0.2" @@ -2412,7 +2302,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-3.3.0.tgz", "integrity": "sha512-pdGTp8m9d0ZCrjTpjkUbZx6gyf2IKf+7zlkrPNMsJzYZ4bFRRTpXrnj+556/5uiI6AfL5mMrJc2u7dB6bvM+VA==", - "license": "MIT", "dependencies": { "dotenv": "16.4.5", "dotenv-expand": "10.0.0", @@ -2424,11 +2313,10 @@ } }, "node_modules/@nestjs/core": { - "version": "10.4.15", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.4.15.tgz", - "integrity": "sha512-UBejmdiYwaH6fTsz2QFBlC1cJHM+3UDeLZN+CiP9I1fRv2KlBZsmozGLbV5eS1JAVWJB4T5N5yQ0gjN8ZvcS2w==", + "version": "10.4.19", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.4.19.tgz", + "integrity": "sha512-gahghu0y4Rn4gn/xPjTgNHFMpUM8TxfhdeMowVWTGVnYMZtGeEGbIXMFhJS0Dce3E4VKyqAglzgO9ecAZd4Ong==", "hasInstallScript": true, - "license": "MIT", "dependencies": { "@nuxtjs/opencollective": "0.3.2", "fast-safe-stringify": "2.1.1", @@ -2465,7 +2353,6 @@ "version": "10.2.8", "resolved": "https://registry.npmjs.org/@nestjs/cqrs/-/cqrs-10.2.8.tgz", "integrity": "sha512-nes4J9duwogme6CzNg8uhF5WVbSKYnNotjhHP+3kJxe6RTzcvJDZN10KpROjWJALEVO5fNmKnkGMecoOfqYzYA==", - "license": "MIT", "dependencies": { "uuid": "11.0.2" }, @@ -2480,7 +2367,6 @@ "version": "10.2.0", "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-10.2.0.tgz", "integrity": "sha512-x8cG90SURkEiLOehNaN2aRlotxT0KZESUliOPKKnjWiyJOcWurkF3w345WOX0P4MgFzUjGoZ1Sy0aZnxeihT0g==", - "license": "MIT", "dependencies": { "@types/jsonwebtoken": "9.0.5", "jsonwebtoken": "9.0.2" @@ -2493,7 +2379,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.5.tgz", "integrity": "sha512-bSJv4pd6EY99NX9CjBIyn4TVDoSit82DUZlL4I3bqNfy5Gt+gXTa86i3I/i0iIV9P4hntcGM5GyO+FhZAhxtyg==", - "license": "MIT", "peerDependencies": { "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", "class-transformer": "^0.4.0 || ^0.5.0", @@ -2513,22 +2398,20 @@ "version": "10.0.3", "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-10.0.3.tgz", "integrity": "sha512-znJ9Y4S8ZDVY+j4doWAJ8EuuVO7SkQN3yOBmzxbGaXbvcSwFDAdGJ+OMCg52NdzIO4tQoN4pYKx8W6M0ArfFRQ==", - "license": "MIT", "peerDependencies": { "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", "passport": "^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0" } }, "node_modules/@nestjs/platform-express": { - "version": "10.4.15", - "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.4.15.tgz", - "integrity": "sha512-63ZZPkXHjoDyO7ahGOVcybZCRa7/Scp6mObQKjcX/fTEq1YJeU75ELvMsuQgc8U2opMGOBD7GVuc4DV0oeDHoA==", - "license": "MIT", + "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": "1.4.4-lts.1", + "multer": "2.0.1", "tslib": "2.8.1" }, "funding": { @@ -2540,11 +2423,247 @@ "@nestjs/core": "^10.0.0" } }, + "node_modules/@nestjs/platform-express/node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@nestjs/platform-express/node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@nestjs/platform-express/node_modules/cookie": { + "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" + } + }, + "node_modules/@nestjs/platform-express/node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/@nestjs/platform-express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/@nestjs/platform-express/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/@nestjs/platform-express/node_modules/express": { + "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.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@nestjs/platform-express/node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@nestjs/platform-express/node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@nestjs/platform-express/node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@nestjs/platform-express/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@nestjs/platform-express/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@nestjs/platform-express/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@nestjs/platform-express/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@nestjs/platform-express/node_modules/path-to-regexp": { + "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/@nestjs/platform-express/node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@nestjs/platform-express/node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@nestjs/platform-express/node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@nestjs/platform-express/node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@nestjs/platform-express/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/@nestjs/schedule": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-6.0.0.tgz", "integrity": "sha512-aQySMw6tw2nhitELXd3EiRacQRgzUKD9mFcUZVOJ7jPLqIBvXOyvRWLsK9SdurGA+jjziAlMef7iB5ZEFFoQpw==", - "license": "MIT", "dependencies": { "cron": "4.3.0" }, @@ -2558,7 +2677,6 @@ "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-10.2.3.tgz", "integrity": "sha512-4e8gxaCk7DhBxVUly2PjYL4xC2ifDFexCqq1/u4TtivLGXotVk0wHdYuPYe1tHTHuR1lsOkRbfOCpkdTnigLVg==", "dev": true, - "license": "MIT", "dependencies": { "@angular-devkit/core": "17.3.11", "@angular-devkit/schematics": "17.3.11", @@ -2574,14 +2692,12 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@nestjs/swagger": { "version": "7.4.2", "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-7.4.2.tgz", "integrity": "sha512-Mu6TEn1M/owIvAx2B4DUQObQXqo2028R2s9rSZ/hJEgBK95+doTwS0DjmVA2wTeZTyVtXOoN7CsoM5pONBzvKQ==", - "license": "MIT", "dependencies": { "@microsoft/tsdoc": "^0.15.0", "@nestjs/mapped-types": "2.0.5", @@ -2614,7 +2730,6 @@ "version": "11.0.0", "resolved": "https://registry.npmjs.org/@nestjs/terminus/-/terminus-11.0.0.tgz", "integrity": "sha512-c55LOo9YGovmQHtFUMa/vDaxGZ2cglMTZejqgHREaApt/GArTfgYYGwhRXPLq8ZwiQQlLuYB+79e9iA8mlDSLA==", - "license": "MIT", "dependencies": { "boxen": "5.1.2", "check-disk-space": "3.4.0" @@ -2681,11 +2796,10 @@ } }, "node_modules/@nestjs/testing": { - "version": "10.4.15", - "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.4.15.tgz", - "integrity": "sha512-eGlWESkACMKti+iZk1hs6FUY/UqObmMaa8HAN9JLnaYkoLf1Jeh+EuHlGnfqo/Rq77oznNLIyaA3PFjrFDlNUg==", + "version": "10.4.19", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.4.19.tgz", + "integrity": "sha512-YfzkjTmwEcoWqo8xr8YiTZMC4FjBEOg4uRTAPI2p6iGLWu+27tYau1CtAKFHY0uSAK3FzgtsAuYoxBSlfr9mWA==", "dev": true, - "license": "MIT", "dependencies": { "tslib": "2.8.1" }, @@ -2712,7 +2826,6 @@ "version": "6.4.0", "resolved": "https://registry.npmjs.org/@nestjs/throttler/-/throttler-6.4.0.tgz", "integrity": "sha512-osL67i0PUuwU5nqSuJjtUJZMkxAnYB4VldgYUMGzvYRJDCqGRFMWbsbzm/CkUtPLRL30I8T74Xgt/OQxnYokiA==", - "license": "MIT", "peerDependencies": { "@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0", "@nestjs/core": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0", @@ -2723,7 +2836,6 @@ "version": "10.0.2", "resolved": "https://registry.npmjs.org/@nestjs/typeorm/-/typeorm-10.0.2.tgz", "integrity": "sha512-H738bJyydK4SQkRCTeh1aFBxoO1E9xdL/HaLGThwrqN95os5mEyAtK7BLADOS+vldP4jDZ2VQPLj4epWwRqCeQ==", - "license": "MIT", "dependencies": { "uuid": "9.0.1" }, @@ -2743,16 +2855,14 @@ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], - "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } }, "node_modules/@nestjs/websockets": { - "version": "10.4.15", - "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-10.4.15.tgz", - "integrity": "sha512-OmCUJwvtagzXfMVko595O98UI3M9zg+URL+/HV7vd3QPMCZ3uGCKSq15YYJ99LHJn9NyK4e4Szm2KnHtUg2QzA==", - "license": "MIT", + "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", @@ -2771,12 +2881,23 @@ } } }, + "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", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, - "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -2790,7 +2911,6 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, - "license": "MIT", "engines": { "node": ">= 8" } @@ -2800,7 +2920,6 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, - "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -2813,7 +2932,6 @@ "version": "0.3.2", "resolved": "https://registry.npmjs.org/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz", "integrity": "sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==", - "license": "MIT", "dependencies": { "chalk": "^4.1.0", "consola": "^2.15.0", @@ -2827,11 +2945,19 @@ "npm": ">=5.0.0" } }, + "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", "resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz", "integrity": "sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==", - "license": "MIT", "engines": { "node": ">=10" } @@ -2840,54 +2966,47 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "license": "MIT", "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, - "license": "MIT", "engines": { "node": "^12.20.0 || ^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/unts" + "url": "https://opencollective.com/pkgr" } }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", - "license": "BSD-3-Clause" + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" }, "node_modules/@protobufjs/base64": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", - "license": "BSD-3-Clause" + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" }, "node_modules/@protobufjs/codegen": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", - "license": "BSD-3-Clause" + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" }, "node_modules/@protobufjs/eventemitter": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", - "license": "BSD-3-Clause" + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" }, "node_modules/@protobufjs/fetch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "license": "BSD-3-Clause", "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" @@ -2896,53 +3015,45 @@ "node_modules/@protobufjs/float": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", - "license": "BSD-3-Clause" + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" }, "node_modules/@protobufjs/inquire": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", - "license": "BSD-3-Clause" + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" }, "node_modules/@protobufjs/path": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", - "license": "BSD-3-Clause" + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" }, "node_modules/@protobufjs/pool": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", - "license": "BSD-3-Clause" + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" }, "node_modules/@protobufjs/utf8": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", - "license": "BSD-3-Clause" + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", - "dev": true, - "license": "MIT" + "dev": true }, "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, - "license": "MIT" + "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, - "license": "BSD-3-Clause", "dependencies": { "type-detect": "4.0.8" } @@ -2952,7 +3063,6 @@ "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "@sinonjs/commons": "^3.0.0" } @@ -2960,42 +3070,58 @@ "node_modules/@sqltools/formatter": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz", - "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==", - "license": "MIT" + "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==" + }, + "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", "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "devOptional": true, - "license": "MIT" + "devOptional": true }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "devOptional": true, - "license": "MIT" + "devOptional": true }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "devOptional": true, - "license": "MIT" + "devOptional": true }, "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "devOptional": true, - "license": "MIT" + "devOptional": true }, "node_modules/@tuya/tuya-connector-nodejs": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/@tuya/tuya-connector-nodejs/-/tuya-connector-nodejs-2.1.2.tgz", "integrity": "sha512-8tM7QlOF1QQrT3iQgcHp4JDNRUdOyi06h8F5ZL7antQZYP67TRQ2/puisoo2uhdXo+n+GT0B605pWaDqr9nPrA==", - "license": "MIT", "dependencies": { "axios": "^0.21.1", "qs": "^6.10.1" @@ -3005,7 +3131,6 @@ "version": "0.21.4", "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", - "license": "MIT", "dependencies": { "follow-redirects": "^1.14.0" } @@ -3015,7 +3140,6 @@ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, - "license": "MIT", "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", @@ -3025,11 +3149,10 @@ } }, "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, - "license": "MIT", "dependencies": { "@babel/types": "^7.0.0" } @@ -3039,18 +3162,16 @@ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, - "license": "MIT", "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "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, - "license": "MIT", "dependencies": { "@babel/types": "^7.20.7" } @@ -3059,15 +3180,13 @@ "version": "2.4.6", "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.6.tgz", "integrity": "sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==", - "dev": true, - "license": "MIT" + "dev": true }, "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, - "license": "MIT", "dependencies": { "@types/connect": "*", "@types/node": "*" @@ -3078,7 +3197,6 @@ "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*" } @@ -3087,22 +3205,19 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@types/crypto-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.2.tgz", "integrity": "sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@types/eslint": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "dev": true, - "license": "MIT", "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -3113,25 +3228,22 @@ "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", "dev": true, - "license": "MIT", "dependencies": { "@types/eslint": "*", "@types/estree": "*" } }, "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==", - "dev": true, - "license": "MIT" + "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, - "license": "MIT", "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", @@ -3144,7 +3256,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, - "license": "MIT", "dependencies": { "@types/node": "*", "@types/qs": "*", @@ -3157,31 +3268,27 @@ "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*" } }, "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==", - "dev": true, - "license": "MIT" + "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": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@types/istanbul-lib-report": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", "dev": true, - "license": "MIT", "dependencies": { "@types/istanbul-lib-coverage": "*" } @@ -3191,7 +3298,6 @@ "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "dev": true, - "license": "MIT", "dependencies": { "@types/istanbul-lib-report": "*" } @@ -3201,7 +3307,6 @@ "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", "dev": true, - "license": "MIT", "dependencies": { "expect": "^29.0.0", "pretty-format": "^29.0.0" @@ -3211,21 +3316,18 @@ "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@types/jsonwebtoken": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz", "integrity": "sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==", - "license": "MIT", "dependencies": { "@types/node": "*" } @@ -3233,80 +3335,70 @@ "node_modules/@types/luxon": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.6.2.tgz", - "integrity": "sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw==", - "license": "MIT" + "integrity": "sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw==" }, "node_modules/@types/methods": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", "integrity": "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@types/multer": { - "version": "1.4.12", - "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.12.tgz", - "integrity": "sha512-pQ2hoqvXiJt2FP9WQVLPRO+AmiIm/ZYkavPlIQnx282u4ZrVdztx0pkh3jjpQt0Kz+YI0YhSG264y08UJKoUQg==", + "version": "1.4.13", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.13.tgz", + "integrity": "sha512-bhhdtPw7JqCiEfC9Jimx5LqX9BDIPJEh2q/fQ4bqbBPtyEZYr3cvF22NwG0DmPZNYA0CAf2CnqDB4KIGGpJcaw==", "dev": true, - "license": "MIT", "dependencies": { "@types/express": "*" } }, "node_modules/@types/node": { - "version": "20.17.19", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.19.tgz", - "integrity": "sha512-LEwC7o1ifqg/6r2gn9Dns0f1rhK+fPFDoMiceTJ6kWmVk6bgXBI/9IOWfVan4WiAavK9pIVWdX0/e3J+eEUh5A==", - "license": "MIT", + "version": "20.19.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.4.tgz", + "integrity": "sha512-OP+We5WV8Xnbuvw0zC2m4qfB/BJvjyCwtNjhHdJxV1639SGSKrLmJkc3fMnp2Qy8nJyHp8RO6umxELN/dS1/EA==", "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~6.21.0" } }, "node_modules/@types/qs": { - "version": "6.9.18", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", - "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==", - "dev": true, - "license": "MIT" + "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": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@types/semver": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", - "dev": true, - "license": "MIT" + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==", + "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, - "license": "MIT", "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "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, - "license": "MIT", "dependencies": { "@types/http-errors": "*", "@types/node": "*", @@ -3317,15 +3409,13 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@types/superagent": { "version": "8.1.9", "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.9.tgz", "integrity": "sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==", "dev": true, - "license": "MIT", "dependencies": { "@types/cookiejar": "^2.1.5", "@types/methods": "^1.1.4", @@ -3334,11 +3424,10 @@ } }, "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, - "license": "MIT", "dependencies": { "@types/methods": "^1.1.4", "@types/superagent": "^8.1.0" @@ -3347,21 +3436,18 @@ "node_modules/@types/triple-beam": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", - "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", - "license": "MIT" + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" }, "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==", - "license": "MIT" + "version": "13.15.2", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.2.tgz", + "integrity": "sha512-y7pa/oEJJ4iGYBxOpfAKn5b9+xuihvzDVnC/OSvlVnGxVg0pOqmjiMafiJ1KVNQEaPZf9HsEp5icEwGg8uIe5Q==" }, "node_modules/@types/yargs": { "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", "dev": true, - "license": "MIT", "dependencies": { "@types/yargs-parser": "*" } @@ -3370,15 +3456,13 @@ "version": "21.0.3", "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.5.1", "@typescript-eslint/scope-manager": "6.21.0", @@ -3414,7 +3498,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", @@ -3443,7 +3526,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", "dev": true, - "license": "MIT", "dependencies": { "@typescript-eslint/types": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0" @@ -3461,7 +3543,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", "dev": true, - "license": "MIT", "dependencies": { "@typescript-eslint/typescript-estree": "6.21.0", "@typescript-eslint/utils": "6.21.0", @@ -3489,7 +3570,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "dev": true, - "license": "MIT", "engines": { "node": "^16.0.0 || >=18.0.0" }, @@ -3503,7 +3583,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "@typescript-eslint/types": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0", @@ -3532,7 +3611,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", @@ -3558,7 +3636,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", "dev": true, - "license": "MIT", "dependencies": { "@typescript-eslint/types": "6.21.0", "eslint-visitor-keys": "^3.4.1" @@ -3575,15 +3652,13 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/@webassemblyjs/ast": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "dev": true, - "license": "MIT", "dependencies": { "@webassemblyjs/helper-numbers": "1.13.2", "@webassemblyjs/helper-wasm-bytecode": "1.13.2" @@ -3593,29 +3668,25 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@webassemblyjs/helper-api-error": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@webassemblyjs/helper-buffer": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@webassemblyjs/helper-numbers": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", "dev": true, - "license": "MIT", "dependencies": { "@webassemblyjs/floating-point-hex-parser": "1.13.2", "@webassemblyjs/helper-api-error": "1.13.2", @@ -3626,15 +3697,13 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@webassemblyjs/helper-wasm-section": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", "dev": true, - "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -3647,7 +3716,6 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", "dev": true, - "license": "MIT", "dependencies": { "@xtuc/ieee754": "^1.2.0" } @@ -3657,7 +3725,6 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", "dev": true, - "license": "Apache-2.0", "dependencies": { "@xtuc/long": "4.2.2" } @@ -3666,15 +3733,13 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@webassemblyjs/wasm-edit": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", "dev": true, - "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -3691,7 +3756,6 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", "dev": true, - "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-wasm-bytecode": "1.13.2", @@ -3705,7 +3769,6 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", "dev": true, - "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -3718,7 +3781,6 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", "dev": true, - "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-api-error": "1.13.2", @@ -3733,7 +3795,6 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", "dev": true, - "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" @@ -3743,35 +3804,32 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true, - "license": "BSD-3-Clause" + "dev": true }, "node_modules/@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true, - "license": "Apache-2.0" + "dev": true }, "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "peer": true, "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" }, "engines": { "node": ">= 0.6" } }, "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==", "devOptional": true, - "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -3784,7 +3842,6 @@ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, - "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -3794,7 +3851,6 @@ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", "devOptional": true, - "license": "MIT", "dependencies": { "acorn": "^8.11.0" }, @@ -3806,7 +3862,6 @@ "version": "7.1.3", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", - "license": "MIT", "engines": { "node": ">= 14" } @@ -3816,7 +3871,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -3833,7 +3887,6 @@ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dev": true, - "license": "MIT", "dependencies": { "ajv": "^8.0.0" }, @@ -3851,7 +3904,6 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3" }, @@ -3863,7 +3915,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "license": "ISC", "dependencies": { "string-width": "^4.1.0" } @@ -3873,7 +3924,6 @@ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } @@ -3883,7 +3933,6 @@ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, - "license": "MIT", "dependencies": { "type-fest": "^0.21.3" }, @@ -3899,7 +3948,6 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, - "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -3911,7 +3959,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", "engines": { "node": ">=8" } @@ -3920,7 +3967,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -3931,18 +3977,19 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "license": "MIT" + "node_modules/ansis": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-3.17.0.tgz", + "integrity": "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==", + "engines": { + "node": ">=14" + } }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, - "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -3956,7 +4003,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, - "license": "MIT", "engines": { "node": ">=8.6" }, @@ -3968,7 +4014,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz", "integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==", - "license": "MIT", "engines": { "node": ">= 6.0.0" } @@ -3976,22 +4021,19 @@ "node_modules/append-field": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", - "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", - "license": "MIT" + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "devOptional": true, - "license": "MIT" + "devOptional": true }, "node_modules/argon2": { "version": "0.40.3", "resolved": "https://registry.npmjs.org/argon2/-/argon2-0.40.3.tgz", "integrity": "sha512-FrSmz4VeM91jwFvvjsQv9GYp6o/kARWoYKjbjDB2U5io1H3e5X67PYGclFDeQff6UXIhUd4aHR3mxCdBbMMuQw==", "hasInstallScript": true, - "license": "MIT", "dependencies": { "@phc/format": "^1.0.0", "node-addon-api": "^8.0.0", @@ -4004,15 +4046,13 @@ "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/array-buffer-byte-length": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" @@ -4027,22 +4067,22 @@ "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "license": "MIT" + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, "node_modules/array-includes": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", - "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "is-string": "^1.0.7" + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -4055,32 +4095,30 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/array.prototype.findlastindex": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", - "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", + "es-abstract": "^1.23.9", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -4094,7 +4132,6 @@ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", @@ -4113,7 +4150,6 @@ "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", @@ -4132,7 +4168,6 @@ "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", "dev": true, - "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.8", @@ -4153,14 +4188,12 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/asn1": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "license": "MIT", "dependencies": { "safer-buffer": "~2.1.0" } @@ -4169,7 +4202,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "license": "MIT", "engines": { "node": ">=0.8" } @@ -4177,15 +4209,13 @@ "node_modules/async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "license": "MIT" + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==" }, "node_modules/async-function": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" } @@ -4193,15 +4223,12 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dev": true, - "license": "MIT", "dependencies": { "possible-typed-array-names": "^1.0.0" }, @@ -4216,7 +4243,6 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", - "license": "Apache-2.0", "engines": { "node": "*" } @@ -4224,14 +4250,12 @@ "node_modules/aws4": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", - "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", - "license": "MIT" + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==" }, "node_modules/axios": { - "version": "1.7.9", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", - "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", - "license": "MIT", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz", + "integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -4243,7 +4267,6 @@ "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", "dev": true, - "license": "MIT", "dependencies": { "@jest/transform": "^29.7.0", "@types/babel__core": "^7.1.14", @@ -4265,7 +4288,6 @@ "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@istanbuljs/load-nyc-config": "^1.0.0", @@ -4282,7 +4304,6 @@ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "@babel/core": "^7.12.3", "@babel/parser": "^7.14.7", @@ -4299,7 +4320,6 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -4309,7 +4329,6 @@ "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", "dev": true, - "license": "MIT", "dependencies": { "@babel/template": "^7.3.3", "@babel/types": "^7.3.3", @@ -4325,7 +4344,6 @@ "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", "dev": true, - "license": "MIT", "dependencies": { "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-bigint": "^7.8.3", @@ -4352,7 +4370,6 @@ "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", "dev": true, - "license": "MIT", "dependencies": { "babel-plugin-jest-hoist": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0" @@ -4367,8 +4384,7 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/base64-js": { "version": "1.5.1", @@ -4387,14 +4403,12 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/basic-auth": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "license": "MIT", "dependencies": { "safe-buffer": "5.1.2" }, @@ -4405,14 +4419,12 @@ "node_modules/basic-auth/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==", - "license": "MIT" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "node_modules/bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "license": "BSD-3-Clause", "dependencies": { "tweetnacl": "^0.14.3" } @@ -4420,14 +4432,12 @@ "node_modules/bcryptjs": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", - "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==", - "license": "MIT" + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" }, "node_modules/bignumber.js": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", - "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", - "license": "MIT", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.0.tgz", + "integrity": "sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==", "engines": { "node": "*" } @@ -4437,7 +4447,6 @@ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" }, @@ -4450,39 +4459,21 @@ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "dev": true, - "license": "MIT", "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "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, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "license": "MIT" + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" }, "node_modules/body-parser": { "version": "1.20.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", - "license": "MIT", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", @@ -4506,7 +4497,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -4514,14 +4504,12 @@ "node_modules/body-parser/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/body-parser/node_modules/qs": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.0.6" }, @@ -4536,7 +4524,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", - "license": "MIT", "dependencies": { "ansi-align": "^3.0.0", "camelcase": "^6.2.0", @@ -4554,40 +4541,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/boxen/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/boxen/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "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==", - "license": "MIT", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dependencies": { "balanced-match": "^1.0.0" } @@ -4597,7 +4554,6 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, - "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -4606,9 +4562,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.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", + "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", "dev": true, "funding": [ { @@ -4624,12 +4580,11 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", + "caniuse-lite": "^1.0.30001726", + "electron-to-chromium": "^1.5.173", "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" + "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" @@ -4643,7 +4598,6 @@ "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", "dev": true, - "license": "MIT", "dependencies": { "fast-json-stable-stringify": "2.x" }, @@ -4656,7 +4610,6 @@ "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", "dev": true, - "license": "Apache-2.0", "dependencies": { "node-int64": "^0.4.0" } @@ -4680,7 +4633,6 @@ "url": "https://feross.org/support" } ], - "license": "MIT", "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -4689,14 +4641,12 @@ "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", - "license": "BSD-3-Clause" + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "license": "MIT" + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, "node_modules/busboy": { "version": "1.6.0", @@ -4713,7 +4663,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", "engines": { "node": ">= 0.8" } @@ -4722,8 +4671,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "dev": true, - "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", @@ -4741,7 +4688,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" @@ -4751,13 +4697,12 @@ } }, "node_modules/call-bound": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", - "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", - "license": "MIT", + "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.1", - "get-intrinsic": "^1.2.6" + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { "node": ">= 0.4" @@ -4771,25 +4716,25 @@ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "license": "MIT", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "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.30001727", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", + "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", "dev": true, "funding": [ { @@ -4804,20 +4749,17 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ], - "license": "CC-BY-4.0" + ] }, "node_modules/caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", - "license": "Apache-2.0" + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -4834,7 +4776,6 @@ "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" } @@ -4843,14 +4784,12 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/check-disk-space": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/check-disk-space/-/check-disk-space-3.4.0.tgz", "integrity": "sha512-drVkSqfwA+TvuEhFipiR1OC9boEGZL5RrWvVsOthdcvQNXyCCuKkEiTOTXZ7qxSf/GLwq4GvzfrQD/Wz325hgw==", - "license": "MIT", "engines": { "node": ">=16" } @@ -4860,7 +4799,6 @@ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, - "license": "MIT", "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -4885,7 +4823,6 @@ "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.0" } @@ -4901,7 +4838,6 @@ "url": "https://github.com/sponsors/sibiraj-s" } ], - "license": "MIT", "engines": { "node": ">=8" } @@ -4910,23 +4846,20 @@ "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, - "license": "MIT" + "dev": true }, "node_modules/class-transformer": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", - "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", - "license": "MIT" + "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==", - "license": "MIT", + "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" } }, @@ -4934,7 +4867,6 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", - "license": "MIT", "engines": { "node": ">=6" }, @@ -4947,7 +4879,6 @@ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, - "license": "MIT", "dependencies": { "restore-cursor": "^3.1.0" }, @@ -4955,88 +4886,11 @@ "node": ">=8" } }, - "node_modules/cli-highlight": { - "version": "2.1.11", - "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", - "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", - "license": "ISC", - "dependencies": { - "chalk": "^4.0.0", - "highlight.js": "^10.7.1", - "mz": "^2.4.0", - "parse5": "^5.1.1", - "parse5-htmlparser2-tree-adapter": "^6.0.0", - "yargs": "^16.0.0" - }, - "bin": { - "highlight": "bin/highlight" - }, - "engines": { - "node": ">=8.0.0", - "npm": ">=5.0.0" - } - }, - "node_modules/cli-highlight/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/cli-highlight/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/cli-highlight/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "license": "MIT", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cli-highlight/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, "node_modules/cli-spinners": { "version": "2.9.2", "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" }, @@ -5049,7 +4903,6 @@ "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", "dev": true, - "license": "MIT", "dependencies": { "string-width": "^4.2.0" }, @@ -5065,7 +4918,6 @@ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", "dev": true, - "license": "ISC", "engines": { "node": ">= 10" } @@ -5074,7 +4926,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==", - "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -5084,29 +4935,10 @@ "node": ">=12" } }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true, - "license": "MIT", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", "engines": { "node": ">=0.8" } @@ -5115,7 +4947,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", - "license": "Apache-2.0", "engines": { "node": ">=0.10.0" } @@ -5125,7 +4956,6 @@ "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", "dev": true, - "license": "MIT", "engines": { "iojs": ">= 1.0.0", "node": ">= 0.12.0" @@ -5135,14 +4965,12 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/color": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", - "license": "MIT", "dependencies": { "color-convert": "^1.9.3", "color-string": "^1.6.0" @@ -5152,7 +4980,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -5163,14 +4990,12 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/color-string": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "license": "MIT", "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" @@ -5180,7 +5005,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "license": "MIT", "dependencies": { "color-name": "1.1.3" } @@ -5188,14 +5012,12 @@ "node_modules/color/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==", - "license": "MIT" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "node_modules/colorspace": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", - "license": "MIT", "dependencies": { "color": "^3.1.3", "text-hex": "1.0.x" @@ -5205,7 +5027,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" }, @@ -5218,7 +5039,6 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 6" } @@ -5228,7 +5048,6 @@ "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.2.5.tgz", "integrity": "sha512-bKw/r35jR3HGt5PEPm1ljsQQGyCrR8sFGNiN5L+ykDHdpO8Smxkrkla9Yi6NkQyUrb8V54PGhfMs6NrIwtxtdw==", "dev": true, - "license": "MIT", "dependencies": { "array-timsort": "^1.0.3", "core-util-is": "^1.0.3", @@ -5245,7 +5064,6 @@ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", "dev": true, - "license": "MIT", "funding": { "url": "https://github.com/sponsors/sindresorhus" } @@ -5254,21 +5072,19 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" + "dev": 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" ], - "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", - "readable-stream": "^2.2.2", + "readable-stream": "^3.0.2", "typedarray": "^0.0.6" } }, @@ -5277,7 +5093,6 @@ "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz", "integrity": "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==", "dev": true, - "license": "MIT", "dependencies": { "chalk": "^4.1.2", "date-fns": "^2.30.0", @@ -5305,7 +5120,6 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -5319,14 +5133,13 @@ "node_modules/consola": { "version": "2.15.3", "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", - "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==", - "license": "MIT" + "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" }, "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "license": "MIT", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "peer": true, "dependencies": { "safe-buffer": "5.2.1" }, @@ -5338,7 +5151,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", "engines": { "node": ">= 0.6" } @@ -5347,42 +5159,42 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", - "license": "MIT", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "peer": true, "engines": { "node": ">= 0.6" } }, "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "license": "MIT" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "peer": true, + "engines": { + "node": ">=6.6.0" + } }, "node_modules/cookiejar": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", - "dev": true, - "license": "MIT" + "dev": true }, "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==", - "license": "MIT" + "dev": true }, "node_modules/cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "license": "MIT", "dependencies": { "object-assign": "^4", "vary": "^1" @@ -5396,7 +5208,6 @@ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", "dev": true, - "license": "MIT", "dependencies": { "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", @@ -5423,7 +5234,6 @@ "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", "dev": true, - "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", "chalk": "^4.0.0", @@ -5444,14 +5254,12 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "devOptional": true, - "license": "MIT" + "devOptional": true }, "node_modules/cron": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/cron/-/cron-4.3.0.tgz", "integrity": "sha512-ciiYNLfSlF9MrDqnbMdRWFiA6oizSF7kA1osPP9lRzNu0Uu+AWog1UKy7SkckiDY2irrNjeO6qLyKnXC8oxmrw==", - "license": "MIT", "dependencies": { "@types/luxon": "~3.6.0", "luxon": "~3.6.0" @@ -5464,7 +5272,6 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -5477,14 +5284,12 @@ "node_modules/crypto-js": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", - "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", - "license": "MIT" + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" }, "node_modules/csv-parser": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/csv-parser/-/csv-parser-3.2.0.tgz", "integrity": "sha512-fgKbp+AJbn1h2dcAHKIdKNSSjfp43BZZykXsCjzALjKy80VXQNHPFJ6T9Afwdzoj24aMkq8GwDS7KGcDPpejrA==", - "license": "MIT", "bin": { "csv-parser": "bin/csv-parser" }, @@ -5496,7 +5301,6 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "license": "MIT", "dependencies": { "assert-plus": "^1.0.0" }, @@ -5509,7 +5313,6 @@ "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", @@ -5527,7 +5330,6 @@ "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", @@ -5545,7 +5347,6 @@ "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -5563,7 +5364,6 @@ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", "dev": true, - "license": "MIT", "dependencies": { "@babel/runtime": "^7.21.0" }, @@ -5578,14 +5378,12 @@ "node_modules/dayjs": { "version": "1.11.13", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", - "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", - "license": "MIT" + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==" }, "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "license": "MIT", + "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" }, @@ -5599,11 +5397,9 @@ } }, "node_modules/dedent": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", - "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", - "dev": true, - "license": "MIT", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", + "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, @@ -5617,15 +5413,13 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/deepmerge": { "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==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -5635,7 +5429,6 @@ "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", "dev": true, - "license": "MIT", "dependencies": { "clone": "^1.0.2" }, @@ -5643,12 +5436,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/defaults/node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, "node_modules/define-data-property": { "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, - "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -5666,7 +5466,6 @@ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, - "license": "MIT", "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", @@ -5683,7 +5482,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", "engines": { "node": ">=0.4.0" } @@ -5692,7 +5490,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", - "license": "Apache-2.0", "engines": { "node": ">=0.10" } @@ -5701,7 +5498,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", "engines": { "node": ">= 0.8" } @@ -5710,7 +5506,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", "engines": { "node": ">= 0.8", "npm": "1.2.8000 || >= 1.4.16" @@ -5721,7 +5516,6 @@ "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -5731,7 +5525,6 @@ "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", "dev": true, - "license": "ISC", "dependencies": { "asap": "^2.0.0", "wrappy": "1" @@ -5742,7 +5535,6 @@ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "devOptional": true, - "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } @@ -5752,7 +5544,6 @@ "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true, - "license": "MIT", "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } @@ -5762,7 +5553,6 @@ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, - "license": "MIT", "dependencies": { "path-type": "^4.0.0" }, @@ -5775,7 +5565,6 @@ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, - "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, @@ -5787,7 +5576,6 @@ "version": "16.4.5", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", - "license": "BSD-2-Clause", "engines": { "node": ">=12" }, @@ -5799,7 +5587,6 @@ "version": "10.0.0", "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz", "integrity": "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==", - "license": "BSD-2-Clause", "engines": { "node": ">=12" } @@ -5808,7 +5595,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", @@ -5821,14 +5607,12 @@ "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "license": "MIT" + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, "node_modules/ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "license": "MIT", "dependencies": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" @@ -5838,7 +5622,6 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "license": "Apache-2.0", "dependencies": { "safe-buffer": "^5.0.1" } @@ -5846,15 +5629,13 @@ "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/ejs": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "dev": true, - "license": "Apache-2.0", "dependencies": { "jake": "^10.8.5" }, @@ -5866,18 +5647,16 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.101", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.101.tgz", - "integrity": "sha512-L0ISiQrP/56Acgu4/i/kfPwWSgrzYZUnQrC0+QPFuhqlLP1Ir7qzPPDVS9BcKIyWTRU8+o6CC8dKw38tSWhYIA==", - "dev": true, - "license": "ISC" + "version": "1.5.179", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.179.tgz", + "integrity": "sha512-UWKi/EbBopgfFsc5k61wFpV7WrnnSlSzW/e2XcBmS6qKYTivZlLtoll5/rdqRTxGglGHkmkW0j0pFNJG10EUIQ==", + "dev": true }, "node_modules/emittery": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=12" }, @@ -5888,30 +5667,26 @@ "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==", - "license": "MIT" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/enabled": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", - "license": "MIT" + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" }, "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/enhanced-resolve": { - "version": "5.18.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", - "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "version": "5.18.2", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.2.tgz", + "integrity": "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==", "dev": true, - "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -5925,34 +5700,32 @@ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, - "license": "MIT", "dependencies": { "is-arrayish": "^0.2.1" } }, "node_modules/es-abstract": { - "version": "1.23.9", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", - "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", + "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", "dev": true, - "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", - "call-bound": "^1.0.3", + "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", + "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.0", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", @@ -5964,21 +5737,24 @@ "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", + "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", - "is-weakref": "^1.1.0", + "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", - "object-inspect": "^1.13.3", + "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", - "regexp.prototype.flags": "^1.5.3", + "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", @@ -5987,7 +5763,7 @@ "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", - "which-typed-array": "^1.1.18" + "which-typed-array": "^1.1.19" }, "engines": { "node": ">= 0.4" @@ -6000,7 +5776,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", "engines": { "node": ">= 0.4" } @@ -6009,23 +5784,20 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/es-module-lexer": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", - "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", - "dev": true, - "license": "MIT" + "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": { "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==", - "license": "MIT", "dependencies": { "es-errors": "^1.3.0" }, @@ -6037,7 +5809,6 @@ "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==", - "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", @@ -6053,7 +5824,6 @@ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", "dev": true, - "license": "MIT", "dependencies": { "hasown": "^2.0.2" }, @@ -6066,7 +5836,6 @@ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", "dev": true, - "license": "MIT", "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", @@ -6083,7 +5852,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "license": "MIT", "engines": { "node": ">=6" } @@ -6091,15 +5859,13 @@ "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==", - "license": "MIT" + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -6113,7 +5879,6 @@ "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -6169,7 +5934,6 @@ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, - "license": "MIT", "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -6182,7 +5946,6 @@ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "dev": true, - "license": "MIT", "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.13.0", @@ -6194,17 +5957,15 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, - "license": "MIT", "dependencies": { "ms": "^2.1.1" } }, "node_modules/eslint-module-utils": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", - "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", + "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", "dev": true, - "license": "MIT", "dependencies": { "debug": "^3.2.7" }, @@ -6222,36 +5983,34 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, - "license": "MIT", "dependencies": { "ms": "^2.1.1" } }, "node_modules/eslint-plugin-import": { - "version": "2.31.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", - "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", + "version": "2.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", + "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, - "license": "MIT", "dependencies": { "@rtsao/scc": "^1.1.0", - "array-includes": "^3.1.8", - "array.prototype.findlastindex": "^1.2.5", - "array.prototype.flat": "^1.3.2", - "array.prototype.flatmap": "^1.3.2", + "array-includes": "^3.1.9", + "array.prototype.findlastindex": "^1.2.6", + "array.prototype.flat": "^1.3.3", + "array.prototype.flatmap": "^1.3.3", "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.12.0", + "eslint-module-utils": "^2.12.1", "hasown": "^2.0.2", - "is-core-module": "^2.15.1", + "is-core-module": "^2.16.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "object.groupby": "^1.0.3", - "object.values": "^1.2.0", + "object.values": "^1.2.1", "semver": "^6.3.1", - "string.prototype.trimend": "^1.0.8", + "string.prototype.trimend": "^1.0.9", "tsconfig-paths": "^3.15.0" }, "engines": { @@ -6262,11 +6021,10 @@ } }, "node_modules/eslint-plugin-import/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==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -6277,7 +6035,6 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, - "license": "MIT", "dependencies": { "ms": "^2.1.1" } @@ -6287,7 +6044,6 @@ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, - "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, @@ -6300,7 +6056,6 @@ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, - "license": "MIT", "dependencies": { "minimist": "^1.2.0" }, @@ -6313,7 +6068,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -6326,7 +6080,6 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -6336,7 +6089,6 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } @@ -6346,7 +6098,6 @@ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", "dev": true, - "license": "MIT", "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", @@ -6355,14 +6106,13 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.3.tgz", - "integrity": "sha512-qJ+y0FfCp/mQYQ/vWQ3s7eUlFEL4PyKfAJxsnYTJ4YT73nsJBWqmEpFryxV9OeUiqmsTsYJ5Y+KDNaeP31wrRw==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.1.tgz", + "integrity": "sha512-dobTkHT6XaEVOo8IO90Q4DOSxnm3Y151QxPJlM/vKC0bVy+d6cVWQZLlFiuZPP0wS6vZwSKeJgKkcS+KfMBlRw==", "dev": true, - "license": "MIT", "dependencies": { "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.9.1" + "synckit": "^0.11.7" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -6373,7 +6123,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": { @@ -6390,7 +6140,6 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -6407,7 +6156,6 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -6420,7 +6168,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -6433,11 +6180,10 @@ } }, "node_modules/eslint/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==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -6448,7 +6194,6 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, - "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, @@ -6460,15 +6205,13 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/eslint/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -6481,7 +6224,6 @@ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", @@ -6499,7 +6241,6 @@ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true, - "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -6513,7 +6254,6 @@ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" }, @@ -6526,7 +6266,6 @@ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -6539,7 +6278,6 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } @@ -6549,7 +6287,6 @@ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, - "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } @@ -6558,7 +6295,6 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", "engines": { "node": ">= 0.6" } @@ -6568,7 +6304,6 @@ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.8.x" } @@ -6578,7 +6313,6 @@ "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, - "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -6601,8 +6335,7 @@ "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/exit": { "version": "0.1.2", @@ -6618,7 +6351,6 @@ "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", "dev": true, - "license": "MIT", "dependencies": { "@jest/expect-utils": "^29.7.0", "jest-get-type": "^29.6.3", @@ -6631,45 +6363,41 @@ } }, "node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", - "license": "MIT", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "peer": true, "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.7.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" }, "engines": { - "node": ">= 0.10.0" + "node": ">= 18" }, "funding": { "type": "opencollective", @@ -6677,10 +6405,9 @@ } }, "node_modules/express-rate-limit": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz", - "integrity": "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==", - "license": "MIT", + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz", + "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==", "engines": { "node": ">= 16" }, @@ -6688,57 +6415,89 @@ "url": "https://github.com/sponsors/express-rate-limit" }, "peerDependencies": { - "express": "^4.11 || 5 || ^5.0.0-beta.1" + "express": ">= 4.11" } }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", + "node_modules/express/node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "peer": true, "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/express/node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", - "license": "MIT" - }, - "node_modules/express/node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.6" + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" }, "engines": { - "node": ">=0.6" + "node": ">=18" + } + }, + "node_modules/express/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==", + "peer": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/express/node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/raw-body": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", + "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "peer": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.6.3", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "peer": true, + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" } }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT" + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, "node_modules/external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", "dev": true, - "license": "MIT", "dependencies": { "chardet": "^0.7.0", "iconv-lite": "^0.4.24", @@ -6754,28 +6513,24 @@ "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", "engines": [ "node >=0.6.0" - ], - "license": "MIT" + ] }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-diff": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "dev": true, - "license": "Apache-2.0" + "dev": true }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, - "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -6790,28 +6545,24 @@ "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "license": "MIT" + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/fast-safe-stringify": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", - "license": "MIT" + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" }, "node_modules/fastq": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", - "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "dev": true, - "license": "ISC", "dependencies": { "reusify": "^1.0.4" } @@ -6820,7 +6571,6 @@ "version": "0.11.4", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", - "license": "Apache-2.0", "dependencies": { "websocket-driver": ">=0.5.1" }, @@ -6833,7 +6583,6 @@ "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", "dev": true, - "license": "Apache-2.0", "dependencies": { "bser": "2.1.1" } @@ -6841,15 +6590,18 @@ "node_modules/fecha": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", - "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", - "license": "MIT" + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" + }, + "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", "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", "dev": true, - "license": "MIT", "dependencies": { "escape-string-regexp": "^1.0.5" }, @@ -6865,7 +6617,6 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.8.0" } @@ -6875,7 +6626,6 @@ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, - "license": "MIT", "dependencies": { "flat-cache": "^3.0.4" }, @@ -6883,12 +6633,28 @@ "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", "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", "dev": true, - "license": "Apache-2.0", "dependencies": { "minimatch": "^5.0.1" } @@ -6898,7 +6664,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -6911,7 +6676,6 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, - "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -6920,44 +6684,27 @@ } }, "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "license": "MIT", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "peer": true, "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" }, "engines": { "node": ">= 0.8" } }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, - "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -6973,7 +6720,6 @@ "version": "10.14.1", "resolved": "https://registry.npmjs.org/firebase/-/firebase-10.14.1.tgz", "integrity": "sha512-0KZxU+Ela9rUCULqFsUUOYYkjh7OM1EWdIfG6///MtXd0t2/uUIf0iNV5i0KariMhRQ5jve/OY985nrAXFaZeQ==", - "license": "Apache-2.0", "dependencies": { "@firebase/analytics": "0.10.8", "@firebase/analytics-compat": "0.2.14", @@ -7010,7 +6756,6 @@ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, - "license": "MIT", "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", @@ -7021,17 +6766,15 @@ } }, "node_modules/flatted": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", - "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", - "dev": true, - "license": "ISC" + "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/fn.name": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", - "license": "MIT" + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" }, "node_modules/follow-redirects": { "version": "1.15.9", @@ -7043,7 +6786,6 @@ "url": "https://github.com/sponsors/RubenVerborgh" } ], - "license": "MIT", "engines": { "node": ">=4.0" }, @@ -7057,8 +6799,6 @@ "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", - "dev": true, - "license": "MIT", "dependencies": { "is-callable": "^1.2.7" }, @@ -7070,12 +6810,11 @@ } }, "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==", - "license": "ISC", + "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": { @@ -7089,7 +6828,6 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", - "license": "Apache-2.0", "engines": { "node": "*" } @@ -7099,7 +6837,6 @@ "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-9.0.2.tgz", "integrity": "sha512-Uochze2R8peoN1XqlSi/rGUkDQpRogtLFocP9+PGu68zk1BDAKXfdeCdyVZpgTk8V8WFVQXdEz426VKjXLO1Gg==", "dev": true, - "license": "MIT", "dependencies": { "@babel/code-frame": "^7.16.7", "chalk": "^4.1.2", @@ -7124,11 +6861,10 @@ } }, "node_modules/fork-ts-checker-webpack-plugin/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==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -7139,7 +6875,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -7148,29 +6883,47 @@ } }, "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", - "license": "MIT", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", + "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", "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": { "node": ">= 6" } }, - "node_modules/formidable": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", - "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", - "dev": true, - "license": "MIT", + "node_modules/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/formidable": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.5.tgz", + "integrity": "sha512-Oz5Hwvwak/DCaXVVUtPn4oLMLLy1CdclLKO1LFgU7XzDpVMUU5UjlSLpGMocyQNNk8F6IJW9M/YdooSn2MRI+Q==", + "dev": true, + "dependencies": { + "@paralleldrive/cuid2": "^2.2.2", "dezalgo": "^1.0.4", - "hexoid": "^1.0.0", "once": "^1.4.0", "qs": "^6.11.0" }, @@ -7182,18 +6935,17 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "peer": true, "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/fs-extra": { @@ -7201,7 +6953,6 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, - "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -7215,21 +6966,32 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==", - "dev": true, - "license": "Unlicense" + "dev": true }, "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 + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, - "license": "ISC" + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -7239,7 +7001,6 @@ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", @@ -7260,7 +7021,6 @@ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, - "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -7269,7 +7029,6 @@ "version": "6.7.1", "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", - "license": "Apache-2.0", "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", @@ -7289,7 +7048,6 @@ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], - "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } @@ -7298,7 +7056,6 @@ "version": "6.1.1", "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", - "license": "Apache-2.0", "dependencies": { "gaxios": "^6.1.1", "google-logging-utils": "^0.0.2", @@ -7313,7 +7070,6 @@ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -7322,23 +7078,21 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } }, "node_modules/get-intrinsic": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", - "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", - "license": "MIT", + "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.1", + "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "get-proto": "^1.0.0", + "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", @@ -7356,7 +7110,6 @@ "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=8.0.0" } @@ -7365,7 +7118,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" @@ -7379,7 +7131,6 @@ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -7392,7 +7143,6 @@ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", @@ -7409,7 +7159,6 @@ "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "license": "MIT", "dependencies": { "assert-plus": "^1.0.0" } @@ -7418,7 +7167,6 @@ "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -7439,7 +7187,6 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, - "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -7451,14 +7198,12 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true, - "license": "BSD-2-Clause" + "dev": true }, "node_modules/glob/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -7474,7 +7219,6 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, - "license": "MIT", "dependencies": { "type-fest": "^0.20.2" }, @@ -7490,7 +7234,6 @@ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, - "license": "MIT", "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" @@ -7507,7 +7250,6 @@ "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, - "license": "MIT", "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", @@ -7527,7 +7269,6 @@ "version": "9.15.1", "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", - "license": "Apache-2.0", "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", @@ -7544,7 +7285,6 @@ "version": "0.0.2", "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz", "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", - "license": "Apache-2.0", "engines": { "node": ">=14" } @@ -7553,7 +7293,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -7565,21 +7304,18 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/gtoken": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", - "license": "MIT", "dependencies": { "gaxios": "^6.0.0", "jws": "^4.0.0" @@ -7592,7 +7328,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", - "license": "ISC", "engines": { "node": ">=4" } @@ -7602,7 +7337,6 @@ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", "deprecated": "this library is no longer supported", - "license": "MIT", "dependencies": { "ajv": "^6.12.3", "har-schema": "^2.0.0" @@ -7615,7 +7349,6 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -7630,15 +7363,13 @@ "node_modules/har-validator/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "license": "MIT" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -7650,7 +7381,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", "engines": { "node": ">=8" } @@ -7660,7 +7390,6 @@ "resolved": "https://registry.npmjs.org/has-own-prop/-/has-own-prop-2.0.0.tgz", "integrity": "sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -7669,8 +7398,6 @@ "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, - "license": "MIT", "dependencies": { "es-define-property": "^1.0.0" }, @@ -7683,7 +7410,6 @@ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", "dev": true, - "license": "MIT", "dependencies": { "dunder-proto": "^1.0.0" }, @@ -7698,7 +7424,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -7710,7 +7435,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" }, @@ -7725,7 +7449,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -7737,42 +7460,20 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/helmet/-/helmet-7.2.0.tgz", "integrity": "sha512-ZRiwvN089JfMXokizgqEPXsl2Guk094yExfoDXR0cBYWxtBbaSww/w+vT4WEJsBW2iTUi1GgZ6swmoug3Oy4Xw==", - "license": "MIT", "engines": { "node": ">=16.0.0" } }, - "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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/highlight.js": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", - "license": "BSD-3-Clause", - "engines": { - "node": "*" - } - }, "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, - "license": "MIT" + "dev": true }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "license": "MIT", "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", @@ -7784,17 +7485,23 @@ "node": ">= 0.8" } }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/http-parser-js": { - "version": "0.5.9", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.9.tgz", - "integrity": "sha512-n1XsPy3rXVxlqxVioEWdC+0+M+SQw0DpJynwtOPo1X+ZlvdzTLtDBIJJlDQTnwZIFJrZSzSGmIOUdP8tu+SgLw==", - "license": "MIT" + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz", + "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==" }, "node_modules/http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", - "license": "MIT", "dependencies": { "assert-plus": "^1.0.0", "jsprim": "^1.2.2", @@ -7809,7 +7516,6 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "license": "MIT", "dependencies": { "agent-base": "^7.1.2", "debug": "4" @@ -7823,7 +7529,6 @@ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true, - "license": "Apache-2.0", "engines": { "node": ">=10.17.0" } @@ -7832,7 +7537,6 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -7843,8 +7547,7 @@ "node_modules/idb": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", - "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", - "license": "ISC" + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==" }, "node_modules/ieee754": { "version": "1.2.1", @@ -7863,15 +7566,13 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "BSD-3-Clause" + ] }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, - "license": "MIT", "engines": { "node": ">= 4" } @@ -7881,7 +7582,6 @@ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, - "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -7898,7 +7598,6 @@ "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", "dev": true, - "license": "MIT", "dependencies": { "pkg-dir": "^4.2.0", "resolve-cwd": "^3.0.0" @@ -7918,7 +7617,6 @@ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.8.19" } @@ -7929,7 +7627,6 @@ "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, - "license": "ISC", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -7938,15 +7635,13 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/inquirer": { "version": "8.2.6", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", "dev": true, - "license": "MIT", "dependencies": { "ansi-escapes": "^4.2.1", "chalk": "^4.1.1", @@ -7968,12 +7663,25 @@ "node": ">=12.0.0" } }, + "node_modules/inquirer/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dev": true, - "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", @@ -7984,10 +7692,9 @@ } }, "node_modules/ioredis": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.5.0.tgz", - "integrity": "sha512-7CutT89g23FfSa8MDoIFs2GYYa0PaNiW/OrT+nRyjRXHDZd17HmIgy+reOQ/yhh72NznNjGuS8kbCAcA4Ro4mw==", - "license": "MIT", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.6.1.tgz", + "integrity": "sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA==", "dependencies": { "@ioredis/commands": "^1.1.1", "cluster-key-slot": "^1.1.0", @@ -8011,7 +7718,6 @@ "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", "engines": { "node": ">= 0.10" } @@ -8021,7 +7727,6 @@ "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", @@ -8038,15 +7743,13 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/is-async-function": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", "dev": true, - "license": "MIT", "dependencies": { "async-function": "^1.0.0", "call-bound": "^1.0.3", @@ -8066,7 +7769,6 @@ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dev": true, - "license": "MIT", "dependencies": { "has-bigints": "^1.0.2" }, @@ -8082,7 +7784,6 @@ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, - "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" }, @@ -8095,7 +7796,6 @@ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" @@ -8111,8 +7811,6 @@ "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -8125,7 +7823,6 @@ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, - "license": "MIT", "dependencies": { "hasown": "^2.0.2" }, @@ -8141,7 +7838,6 @@ "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", @@ -8159,7 +7855,6 @@ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" @@ -8176,7 +7871,6 @@ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -8186,7 +7880,6 @@ "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3" }, @@ -8201,7 +7894,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==", - "license": "MIT", "engines": { "node": ">=8" } @@ -8211,7 +7903,6 @@ "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } @@ -8221,7 +7912,6 @@ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "get-proto": "^1.0.0", @@ -8240,7 +7930,6 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, - "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -8253,7 +7942,6 @@ "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -8263,7 +7951,18 @@ "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "dev": true, - "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -8276,7 +7975,6 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -8286,7 +7984,6 @@ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" @@ -8303,17 +8000,21 @@ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "peer": true + }, "node_modules/is-regex": { "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==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", @@ -8332,7 +8033,6 @@ "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -8345,7 +8045,6 @@ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3" }, @@ -8360,7 +8059,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "license": "MIT", "engines": { "node": ">=8" }, @@ -8373,7 +8071,6 @@ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" @@ -8390,7 +8087,6 @@ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "has-symbols": "^1.1.0", @@ -8407,8 +8103,6 @@ "version": "1.1.15", "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", - "dev": true, - "license": "MIT", "dependencies": { "which-typed-array": "^1.1.16" }, @@ -8422,15 +8116,13 @@ "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "license": "MIT" + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -8443,7 +8135,6 @@ "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -8456,7 +8147,6 @@ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3" }, @@ -8472,7 +8162,6 @@ "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" @@ -8485,29 +8174,25 @@ } }, "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", - "license": "MIT" + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=8" } @@ -8517,7 +8202,6 @@ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "@babel/core": "^7.23.9", "@babel/parser": "^7.23.9", @@ -8534,7 +8218,6 @@ "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "istanbul-lib-coverage": "^3.0.0", "make-dir": "^4.0.0", @@ -8549,7 +8232,6 @@ "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "debug": "^4.1.1", "istanbul-lib-coverage": "^3.0.0", @@ -8564,7 +8246,6 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -8574,7 +8255,6 @@ "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" @@ -8587,7 +8267,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz", "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==", - "license": "ISC", "engines": { "node": ">=6" } @@ -8596,7 +8275,6 @@ "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -8612,7 +8290,6 @@ "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", "dev": true, - "license": "Apache-2.0", "dependencies": { "async": "^3.2.3", "chalk": "^4.0.2", @@ -8627,11 +8304,10 @@ } }, "node_modules/jake/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==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -8642,7 +8318,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -8655,7 +8330,6 @@ "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, - "license": "MIT", "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -8682,7 +8356,6 @@ "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", "dev": true, - "license": "MIT", "dependencies": { "execa": "^5.0.0", "jest-util": "^29.7.0", @@ -8697,7 +8370,6 @@ "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", "dev": true, - "license": "MIT", "dependencies": { "@jest/environment": "^29.7.0", "@jest/expect": "^29.7.0", @@ -8729,7 +8401,6 @@ "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", "dev": true, - "license": "MIT", "dependencies": { "@jest/core": "^29.7.0", "@jest/test-result": "^29.7.0", @@ -8763,7 +8434,6 @@ "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", "dev": true, - "license": "MIT", "dependencies": { "@babel/core": "^7.11.6", "@jest/test-sequencer": "^29.7.0", @@ -8805,11 +8475,10 @@ } }, "node_modules/jest-config/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==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -8821,7 +8490,6 @@ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, - "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -8842,7 +8510,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -8855,7 +8522,6 @@ "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", "dev": true, - "license": "MIT", "dependencies": { "chalk": "^4.0.0", "diff-sequences": "^29.6.3", @@ -8871,7 +8537,6 @@ "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", "dev": true, - "license": "MIT", "dependencies": { "detect-newline": "^3.0.0" }, @@ -8884,7 +8549,6 @@ "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", "dev": true, - "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", "chalk": "^4.0.0", @@ -8901,7 +8565,6 @@ "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", "dev": true, - "license": "MIT", "dependencies": { "@jest/environment": "^29.7.0", "@jest/fake-timers": "^29.7.0", @@ -8919,7 +8582,6 @@ "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", "dev": true, - "license": "MIT", "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } @@ -8929,7 +8591,6 @@ "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", "dev": true, - "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", @@ -8955,7 +8616,6 @@ "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", "dev": true, - "license": "MIT", "dependencies": { "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" @@ -8969,7 +8629,6 @@ "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", "dev": true, - "license": "MIT", "dependencies": { "chalk": "^4.0.0", "jest-diff": "^29.7.0", @@ -8985,7 +8644,6 @@ "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "dev": true, - "license": "MIT", "dependencies": { "@babel/code-frame": "^7.12.13", "@jest/types": "^29.6.3", @@ -9006,7 +8664,6 @@ "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", "dev": true, - "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", @@ -9021,7 +8678,6 @@ "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" }, @@ -9039,7 +8695,6 @@ "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", "dev": true, - "license": "MIT", "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } @@ -9049,7 +8704,6 @@ "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", "dev": true, - "license": "MIT", "dependencies": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", @@ -9070,7 +8724,6 @@ "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", "dev": true, - "license": "MIT", "dependencies": { "jest-regex-util": "^29.6.3", "jest-snapshot": "^29.7.0" @@ -9084,7 +8737,6 @@ "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", "dev": true, - "license": "MIT", "dependencies": { "@jest/console": "^29.7.0", "@jest/environment": "^29.7.0", @@ -9117,7 +8769,6 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -9127,7 +8778,6 @@ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", "dev": true, - "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -9138,7 +8788,6 @@ "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", "dev": true, - "license": "MIT", "dependencies": { "@jest/environment": "^29.7.0", "@jest/fake-timers": "^29.7.0", @@ -9168,11 +8817,10 @@ } }, "node_modules/jest-runtime/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==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -9184,7 +8832,6 @@ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, - "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -9205,7 +8852,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -9218,7 +8864,6 @@ "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", "dev": true, - "license": "MIT", "dependencies": { "@babel/core": "^7.11.6", "@babel/generator": "^7.7.2", @@ -9250,7 +8895,6 @@ "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dev": true, - "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", @@ -9268,7 +8912,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, - "license": "MIT", "engines": { "node": ">=8.6" }, @@ -9281,7 +8924,6 @@ "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", "dev": true, - "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", "camelcase": "^6.2.0", @@ -9294,25 +8936,11 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/jest-watcher": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", "dev": true, - "license": "MIT", "dependencies": { "@jest/test-result": "^29.7.0", "@jest/types": "^29.6.3", @@ -9332,7 +8960,6 @@ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*", "jest-util": "^29.7.0", @@ -9348,7 +8975,6 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -9363,14 +8989,12 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -9381,15 +9005,13 @@ "node_modules/jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "license": "MIT" + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, - "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, @@ -9401,7 +9023,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", - "license": "MIT", "dependencies": { "bignumber.js": "^9.0.0" } @@ -9410,48 +9031,41 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/json-schema": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "license": "(AFL-2.1 OR BSD-3-Clause)" + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "license": "ISC" + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, - "license": "MIT", "bin": { "json5": "lib/cli.js" }, @@ -9463,15 +9077,13 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, - "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -9483,7 +9095,6 @@ "version": "9.0.2", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", - "license": "MIT", "dependencies": { "jws": "^3.2.2", "lodash.includes": "^4.3.0", @@ -9502,12 +9113,11 @@ } }, "node_modules/jsonwebtoken/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==", - "license": "MIT", + "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" } @@ -9516,7 +9126,6 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "license": "MIT", "dependencies": { "jwa": "^1.4.1", "safe-buffer": "^5.0.1" @@ -9526,7 +9135,6 @@ "version": "1.4.2", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "license": "MIT", "dependencies": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", @@ -9538,12 +9146,11 @@ } }, "node_modules/jwa": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", - "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", - "license": "MIT", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", "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" } @@ -9552,7 +9159,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", - "license": "MIT", "dependencies": { "jwa": "^2.0.0", "safe-buffer": "^5.0.1" @@ -9563,7 +9169,6 @@ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, - "license": "MIT", "dependencies": { "json-buffer": "3.0.1" } @@ -9573,7 +9178,6 @@ "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } @@ -9581,15 +9185,13 @@ "node_modules/kuler": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", - "license": "MIT" + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } @@ -9599,7 +9201,6 @@ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, - "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -9609,24 +9210,21 @@ } }, "node_modules/libphonenumber-js": { - "version": "1.11.20", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.11.20.tgz", - "integrity": "sha512-/ipwAMvtSZRdiQBHqW1qxqeYiBMzncOQLVA+62MWYr7N4m7Q2jqpJ0WgT7zlOEOpyLRSqrMXidbJpC0J77AaKA==", - "license": "MIT" + "version": "1.12.9", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.9.tgz", + "integrity": "sha512-VWwAdNeJgN7jFOD+wN4qx83DTPMVPPAUyx9/TUkBXKLiNkuWWk6anV0439tgdtwaJDrEdqkvdN22iA6J4bUCZg==" }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.11.5" } @@ -9636,7 +9234,6 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, - "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -9650,114 +9247,96 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "license": "MIT" + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" }, "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==", - "license": "MIT" + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" }, "node_modules/lodash.escaperegexp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", - "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", - "license": "MIT" + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==" }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", - "license": "MIT" + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" }, "node_modules/lodash.isarguments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", - "license": "MIT" + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==" }, "node_modules/lodash.isboolean": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", - "license": "MIT" + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" }, "node_modules/lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", - "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", - "license": "MIT" + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead." }, "node_modules/lodash.isfunction": { "version": "3.0.9", "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", - "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", - "license": "MIT" + "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==" }, "node_modules/lodash.isinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", - "license": "MIT" + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" }, "node_modules/lodash.isnil": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz", - "integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==", - "license": "MIT" + "integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==" }, "node_modules/lodash.isnumber": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", - "license": "MIT" + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" }, "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "license": "MIT" + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" }, "node_modules/lodash.isstring": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", - "license": "MIT" + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true, - "license": "MIT" + "dev": true }, "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, - "license": "MIT" + "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==", - "license": "MIT" + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, - "license": "MIT", "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" @@ -9773,7 +9352,6 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", - "license": "MIT", "dependencies": { "@colors/colors": "1.6.0", "@types/triple-beam": "^1.3.2", @@ -9790,23 +9368,20 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", - "license": "MIT", "engines": { "node": ">=0.1.90" } }, "node_modules/long": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/long/-/long-5.3.0.tgz", - "integrity": "sha512-5vvY5yF1zF/kXk+L94FRiTDa1Znom46UjPCH6/XbSvS8zBKMFBHTJk8KDMqJ+2J6QezQFi7k1k8v21ClJYHPaw==", - "license": "Apache-2.0" + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==" }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, - "license": "ISC", "dependencies": { "yallist": "^3.0.2" } @@ -9815,7 +9390,6 @@ "version": "3.6.1", "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.6.1.tgz", "integrity": "sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ==", - "license": "MIT", "engines": { "node": ">=12" } @@ -9825,7 +9399,6 @@ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", "dev": true, - "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" }, @@ -9838,7 +9411,6 @@ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, - "license": "MIT", "dependencies": { "semver": "^7.5.3" }, @@ -9853,15 +9425,13 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "devOptional": true, - "license": "ISC" + "devOptional": true }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "tmpl": "1.0.5" } @@ -9870,7 +9440,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", "engines": { "node": ">= 0.4" } @@ -9879,7 +9448,6 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", "engines": { "node": ">= 0.6" } @@ -9889,7 +9457,6 @@ "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", "dev": true, - "license": "Unlicense", "dependencies": { "fs-monkey": "^1.0.4" }, @@ -9898,10 +9465,13 @@ } }, "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "license": "MIT", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "peer": true, + "engines": { + "node": ">=18" + }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } @@ -9910,15 +9480,13 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 8" } @@ -9927,7 +9495,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "license": "MIT", "engines": { "node": ">= 0.6" } @@ -9937,7 +9504,6 @@ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, - "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -9951,7 +9517,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, - "license": "MIT", "engines": { "node": ">=8.6" }, @@ -9960,33 +9525,33 @@ } }, "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "license": "MIT", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, "bin": { "mime": "cli.js" }, "engines": { - "node": ">=4" + "node": ">=4.0.0" } }, "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "peer": true, "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "peer": true, "dependencies": { - "mime-db": "1.52.0" + "mime-db": "^1.54.0" }, "engines": { "node": ">= 0.6" @@ -9997,7 +9562,6 @@ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } @@ -10007,7 +9571,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -10022,7 +9585,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -10031,7 +9593,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" } @@ -10040,7 +9601,6 @@ "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "license": "MIT", "dependencies": { "minimist": "^1.2.6" }, @@ -10052,7 +9612,6 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", - "license": "MIT", "dependencies": { "basic-auth": "~2.0.1", "debug": "2.6.9", @@ -10068,7 +9627,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -10076,14 +9634,12 @@ "node_modules/morgan/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/morgan/node_modules/on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", - "license": "MIT", "dependencies": { "ee-first": "1.1.1" }, @@ -10094,57 +9650,42 @@ "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" + "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==", - "license": "MIT", + "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": ">= 6.0.0" + "node": ">= 10.16.0" } }, "node_modules/mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true, - "license": "ISC" - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } + "dev": true }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "peer": true, "engines": { "node": ">= 0.6" } @@ -10153,14 +9694,12 @@ "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/nest-winston": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/nest-winston/-/nest-winston-1.10.2.tgz", "integrity": "sha512-Z9IzL/nekBOF/TEwBHUJDiDPMaXUcFquUQOFavIRet6xF0EbuWnOzslyN/ksgzG+fITNgXhMdrL/POp9SdaFxA==", - "license": "MIT", "dependencies": { "fast-safe-stringify": "^2.1.1" }, @@ -10173,14 +9712,12 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/node-addon-api": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.3.0.tgz", - "integrity": "sha512-8VOpLHFrOQlAH+qA0ZzuGRlALRA6/LVh8QJldbrC4DY0hXoMP0l4Acq8TzFC018HztWiRqyCEj2aTWY2UvnJUg==", - "license": "MIT", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.4.0.tgz", + "integrity": "sha512-D9DI/gXHvVmjHS08SVch0Em8G5S1P+QWtU31appcKT/8wFSPRcdHadIFSAntdMMVM5zz+/DL+bL/gz3UDppqtg==", "engines": { "node": "^18 || ^20 || >= 21" } @@ -10189,7 +9726,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz", "integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==", - "license": "MIT", "dependencies": { "clone": "2.x" }, @@ -10197,21 +9733,11 @@ "node": ">= 8.0.0" } }, - "node_modules/node-cache/node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, "node_modules/node-emoji": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", "dev": true, - "license": "MIT", "dependencies": { "lodash": "^4.17.21" } @@ -10220,7 +9746,6 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", "dependencies": { "whatwg-url": "^5.0.0" }, @@ -10240,7 +9765,6 @@ "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==", - "license": "MIT", "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", @@ -10251,21 +9775,18 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/node-releases": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/nodemailer": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.0.tgz", - "integrity": "sha512-SQ3wZCExjeSatLE/HBaXS5vqUOQk6GtBdIIKxiFdmm01mOQZX/POJkO3SUX1wDiYcwUOJwT23scFSC9fY2H8IA==", - "license": "MIT-0", + "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" } @@ -10275,7 +9796,6 @@ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -10285,7 +9805,6 @@ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, - "license": "MIT", "dependencies": { "path-key": "^3.0.0" }, @@ -10297,7 +9816,6 @@ "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "license": "Apache-2.0", "engines": { "node": "*" } @@ -10306,7 +9824,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -10315,7 +9832,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "license": "MIT", "engines": { "node": ">= 6" } @@ -10324,7 +9840,6 @@ "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -10337,7 +9852,6 @@ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" } @@ -10347,7 +9861,6 @@ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", @@ -10368,7 +9881,6 @@ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -10387,7 +9899,6 @@ "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -10402,7 +9913,6 @@ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", @@ -10420,7 +9930,6 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", "dependencies": { "ee-first": "1.1.1" }, @@ -10432,7 +9941,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "license": "MIT", "engines": { "node": ">= 0.8" } @@ -10441,8 +9949,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, - "license": "ISC", "dependencies": { "wrappy": "1" } @@ -10451,7 +9957,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "license": "MIT", "dependencies": { "fn.name": "1.x.x" } @@ -10460,7 +9965,6 @@ "version": "3.4.0", "resolved": "https://registry.npmjs.org/onesignal-node/-/onesignal-node-3.4.0.tgz", "integrity": "sha512-9dNpfU5Xp6VhJLkdZT4kVqmOaU36RJOgp+6REQHyv+hLOcgqqa4/FRXxuHbjRCE51x9BK4jIC/gn2Mnw0gQgFQ==", - "license": "MIT", "dependencies": { "request": "^2.88.2", "request-promise": "^4.2.6" @@ -10474,7 +9978,6 @@ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, - "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" }, @@ -10490,7 +9993,6 @@ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, - "license": "MIT", "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -10508,7 +10010,6 @@ "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", "dev": true, - "license": "MIT", "dependencies": { "bl": "^4.1.0", "chalk": "^4.1.0", @@ -10532,7 +10033,6 @@ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -10542,7 +10042,6 @@ "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", "dev": true, - "license": "MIT", "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", @@ -10560,7 +10059,6 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, - "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -10576,7 +10074,6 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, - "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -10592,7 +10089,6 @@ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } @@ -10600,15 +10096,13 @@ "node_modules/package-json-from-dist": { "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==", - "license": "BlueOak-1.0.0" + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, - "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -10621,7 +10115,6 @@ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, - "license": "MIT", "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -10635,32 +10128,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", - "license": "MIT" - }, - "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", - "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", - "license": "MIT", - "dependencies": { - "parse5": "^6.0.1" - } - }, - "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "license": "MIT" - }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", "engines": { "node": ">= 0.8" } @@ -10669,7 +10140,6 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", - "license": "MIT", "peer": true, "dependencies": { "passport-strategy": "1.x.x", @@ -10688,7 +10158,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.1.tgz", "integrity": "sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==", - "license": "MIT", "dependencies": { "jsonwebtoken": "^9.0.0", "passport-strategy": "^1.0.0" @@ -10707,7 +10176,6 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -10717,7 +10185,6 @@ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -10726,7 +10193,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "license": "MIT", "engines": { "node": ">=8" } @@ -10735,14 +10201,12 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" + "dev": true }, "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==", - "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -10757,21 +10221,18 @@ "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==", - "license": "ISC" + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" }, "node_modules/path-to-regexp": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz", - "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==", - "license": "MIT" + "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==" }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -10785,26 +10246,24 @@ "node_modules/performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", - "license": "MIT" + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" }, "node_modules/pg": { - "version": "8.13.3", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.13.3.tgz", - "integrity": "sha512-P6tPt9jXbL9HVu/SSRERNYaYG++MjnscnegFh9pPHihfoBSujsrka0hyuymMzeJKFWrcG8wvCKy8rCe8e5nDUQ==", - "license": "MIT", + "version": "8.16.3", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", + "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", "dependencies": { - "pg-connection-string": "^2.7.0", - "pg-pool": "^3.7.1", - "pg-protocol": "^1.7.1", - "pg-types": "^2.1.0", - "pgpass": "1.x" + "pg-connection-string": "^2.9.1", + "pg-pool": "^3.10.1", + "pg-protocol": "^1.10.3", + "pg-types": "2.2.0", + "pgpass": "1.0.5" }, "engines": { - "node": ">= 8.0.0" + "node": ">= 16.0.0" }, "optionalDependencies": { - "pg-cloudflare": "^1.1.1" + "pg-cloudflare": "^1.2.7" }, "peerDependencies": { "pg-native": ">=3.0.1" @@ -10816,47 +10275,41 @@ } }, "node_modules/pg-cloudflare": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", - "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", - "license": "MIT", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz", + "integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==", "optional": true }, "node_modules/pg-connection-string": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.7.0.tgz", - "integrity": "sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==", - "license": "MIT" + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz", + "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==" }, "node_modules/pg-int8": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", - "license": "ISC", "engines": { "node": ">=4.0.0" } }, "node_modules/pg-pool": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.7.1.tgz", - "integrity": "sha512-xIOsFoh7Vdhojas6q3596mXFsR8nwBQBXX5JiV7p9buEVAGqYL4yFzclON5P9vFrpu1u7Zwl2oriyDa89n0wbw==", - "license": "MIT", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz", + "integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==", "peerDependencies": { "pg": ">=8.0" } }, "node_modules/pg-protocol": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.7.1.tgz", - "integrity": "sha512-gjTHWGYWsEgy9MsY0Gp6ZJxV24IjDqdpTW7Eh0x+WfJLFsm/TJx1MzL6T0D88mBvkpxotCQ6TwW6N+Kko7lhgQ==", - "license": "MIT" + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz", + "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==" }, "node_modules/pg-types": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", - "license": "MIT", "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", @@ -10872,7 +10325,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", - "license": "MIT", "dependencies": { "split2": "^4.1.0" } @@ -10881,15 +10333,13 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/picomatch": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.1.tgz", "integrity": "sha512-xUXwsxNjwTQ8K3GnT4pCJm+xq3RUPQbmkYJTP5aFIfNIvbcc/4MUxgBaaRSZJ6yGJZiGSyYlM6MzwTsRk8SYCg==", "dev": true, - "license": "MIT", "engines": { "node": ">=12" }, @@ -10898,11 +10348,10 @@ } }, "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, - "license": "MIT", "engines": { "node": ">= 6" } @@ -10912,7 +10361,6 @@ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, - "license": "MIT", "dependencies": { "find-up": "^4.0.0" }, @@ -10925,7 +10373,6 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, - "license": "MIT", "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -10939,7 +10386,6 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, - "license": "MIT", "dependencies": { "p-locate": "^4.1.0" }, @@ -10952,7 +10398,6 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, - "license": "MIT", "dependencies": { "p-try": "^2.0.0" }, @@ -10968,7 +10413,6 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, - "license": "MIT", "dependencies": { "p-limit": "^2.2.0" }, @@ -10981,7 +10425,6 @@ "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } @@ -10990,8 +10433,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", - "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" } @@ -11000,7 +10441,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", - "license": "MIT", "engines": { "node": ">=4" } @@ -11009,7 +10449,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -11018,7 +10457,6 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -11027,7 +10465,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", - "license": "MIT", "dependencies": { "xtend": "^4.0.0" }, @@ -11040,17 +10477,15 @@ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8.0" } }, "node_modules/prettier": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.1.tgz", - "integrity": "sha512-hPpFQvHwL3Qv5AdRvBFMhnKo4tYxp0ReXiPn2bxkiohEX6mBeBwEpBSQTkD458RaaDKQMYSp4hX4UtfUTA5wDw==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, - "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" }, @@ -11066,7 +10501,6 @@ "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", "dev": true, - "license": "MIT", "dependencies": { "fast-diff": "^1.1.2" }, @@ -11079,7 +10513,6 @@ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, - "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -11094,7 +10527,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -11102,18 +10534,11 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "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==", - "license": "MIT" - }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", "dev": true, - "license": "MIT", "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" @@ -11123,11 +10548,10 @@ } }, "node_modules/protobufjs": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", - "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.3.tgz", + "integrity": "sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw==", "hasInstallScript": true, - "license": "BSD-3-Clause", "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", @@ -11150,7 +10574,6 @@ "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" @@ -11162,14 +10585,12 @@ "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==", - "license": "MIT" + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, "node_modules/psl": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", - "license": "MIT", "dependencies": { "punycode": "^2.3.1" }, @@ -11181,7 +10602,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "license": "MIT", "engines": { "node": ">=6" } @@ -11200,14 +10620,12 @@ "type": "opencollective", "url": "https://opencollective.com/fast-check" } - ], - "license": "MIT" + ] }, "node_modules/qs": { "version": "6.14.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" }, @@ -11236,15 +10654,13 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, - "license": "MIT", "dependencies": { "safe-buffer": "^5.1.0" } @@ -11253,7 +10669,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", "engines": { "node": ">= 0.6" } @@ -11262,7 +10677,6 @@ "version": "2.5.2", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "license": "MIT", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", @@ -11277,36 +10691,26 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" + "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==", - "license": "MIT", + "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==", - "license": "MIT" - }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, - "license": "MIT", "dependencies": { "picomatch": "^2.2.1" }, @@ -11319,7 +10723,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, - "license": "MIT", "engines": { "node": ">=8.6" }, @@ -11331,7 +10734,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", - "license": "MIT", "engines": { "node": ">=4" } @@ -11340,7 +10742,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", - "license": "MIT", "dependencies": { "redis-errors": "^1.0.0" }, @@ -11351,15 +10752,13 @@ "node_modules/reflect-metadata": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", - "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", - "license": "Apache-2.0" + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==" }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", @@ -11377,19 +10776,11 @@ "url": "https://github.com/sponsors/ljharb" } }, - "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==", - "dev": true, - "license": "MIT" - }, "node_modules/regexp.prototype.flags": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", @@ -11410,7 +10801,6 @@ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10" } @@ -11420,7 +10810,6 @@ "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", - "license": "Apache-2.0", "dependencies": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -11452,7 +10841,6 @@ "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.6.tgz", "integrity": "sha512-HCHI3DJJUakkOr8fNoCc73E5nU5bqITjOYFMDrKHYOXWXrgD/SBaC7LjwuPymUprRyuF06UK7hd/lMHkmUXglQ==", "deprecated": "request-promise has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142", - "license": "ISC", "dependencies": { "bluebird": "^3.5.0", "request-promise-core": "1.1.4", @@ -11470,7 +10858,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", - "license": "ISC", "dependencies": { "lodash": "^4.17.19" }, @@ -11485,7 +10872,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", @@ -11495,11 +10881,29 @@ "node": ">= 0.12" } }, + "node_modules/request/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/request/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/request/node_modules/qs": { "version": "6.5.3", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", - "license": "BSD-3-Clause", "engines": { "node": ">=0.6" } @@ -11509,7 +10913,6 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "license": "MIT", "bin": { "uuid": "bin/uuid" } @@ -11518,7 +10921,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==", - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -11528,7 +10930,6 @@ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -11538,7 +10939,6 @@ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "dev": true, - "license": "MIT", "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", @@ -11559,7 +10959,6 @@ "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, - "license": "MIT", "dependencies": { "resolve-from": "^5.0.0" }, @@ -11572,7 +10971,6 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -11582,7 +10980,6 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } @@ -11592,7 +10989,6 @@ "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" } @@ -11602,7 +10998,6 @@ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, - "license": "MIT", "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" @@ -11615,15 +11010,13 @@ "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" + "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, - "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -11635,7 +11028,6 @@ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, - "license": "ISC", "dependencies": { "glob": "^7.1.3" }, @@ -11647,11 +11039,10 @@ } }, "node_modules/rimraf/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==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -11663,7 +11054,6 @@ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, - "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -11684,7 +11074,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -11692,12 +11081,36 @@ "node": "*" } }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "peer": true, + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/router/node_modules/path-to-regexp": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "peer": true, + "engines": { + "node": ">=16" + } + }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -11721,16 +11134,14 @@ "url": "https://feross.org/support" } ], - "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } }, "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==", - "license": "Apache-2.0", + "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" } @@ -11740,7 +11151,6 @@ "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", @@ -11755,13 +11165,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/safe-array-concat/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true, - "license": "MIT" - }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -11779,15 +11182,13 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/safe-push-apply": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", "dev": true, - "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "isarray": "^2.0.5" @@ -11799,19 +11200,11 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/safe-push-apply/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true, - "license": "MIT" - }, "node_modules/safe-regex-test": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -11828,7 +11221,6 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", - "license": "MIT", "engines": { "node": ">=10" } @@ -11836,15 +11228,13 @@ "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/schema-utils": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, - "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -11863,7 +11253,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -11880,7 +11269,6 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true, - "license": "MIT", "peerDependencies": { "ajv": "^6.9.1" } @@ -11889,14 +11277,12 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", - "license": "ISC", + "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" }, @@ -11905,51 +11291,25 @@ } }, "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", - "license": "MIT", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "peer": true, "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" }, "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "license": "MIT", - "engines": { - "node": ">= 0.8" + "node": ">= 18" } }, "node_modules/serialize-javascript": { @@ -11957,32 +11317,29 @@ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "randombytes": "^2.1.0" } }, "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", - "license": "MIT", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "peer": true, "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 18" } }, "node_modules/set-function-length": { "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, - "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -12000,7 +11357,6 @@ "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dev": true, - "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -12016,7 +11372,6 @@ "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", "dev": true, - "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", @@ -12029,27 +11384,31 @@ "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "node_modules/sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "license": "(MIT AND BSD-3-Clause)", + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", + "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.0" }, "bin": { "sha.js": "bin.js" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -12061,17 +11420,15 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/shell-quote": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", - "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -12083,7 +11440,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", @@ -12102,7 +11458,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" @@ -12118,7 +11473,6 @@ "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==", - "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -12136,7 +11490,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -12155,7 +11508,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "license": "ISC", "engines": { "node": ">=14" }, @@ -12167,7 +11519,6 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "license": "MIT", "dependencies": { "is-arrayish": "^0.3.1" } @@ -12175,22 +11526,19 @@ "node_modules/simple-swizzle/node_modules/is-arrayish": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "license": "MIT" + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -12200,7 +11548,6 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">= 8" } @@ -12210,7 +11557,6 @@ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, - "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -12221,7 +11567,6 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -12236,7 +11581,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "license": "ISC", "engines": { "node": ">= 10.x" } @@ -12245,14 +11589,27 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, - "license": "BSD-3-Clause" + "dev": true + }, + "node_modules/sql-highlight": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/sql-highlight/-/sql-highlight-6.1.0.tgz", + "integrity": "sha512-ed7OK4e9ywpE7pgRMkMQmZDPKSVdm0oX5IEtZiKnFucSF0zu6c80GZBe38UqHuVhTWJ9xsKgSMjCG2bml86KvA==", + "funding": [ + "https://github.com/scriptcoded/sql-highlight?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/scriptcoded" + } + ], + "engines": { + "node": ">=14" + } }, "node_modules/sshpk": { "version": "1.18.0", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", - "license": "MIT", "dependencies": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -12277,7 +11634,6 @@ "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", - "license": "MIT", "engines": { "node": "*" } @@ -12287,7 +11643,6 @@ "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", "dev": true, - "license": "MIT", "dependencies": { "escape-string-regexp": "^2.0.0" }, @@ -12300,7 +11655,6 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -12308,14 +11662,13 @@ "node_modules/standard-as-callback": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", - "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", - "license": "MIT" + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" }, "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "peer": true, "engines": { "node": ">= 0.8" } @@ -12324,11 +11677,23 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", "integrity": "sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==", - "license": "ISC", "engines": { "node": ">=0.10.0" } }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -12338,26 +11703,18 @@ } }, "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==", - "license": "MIT", + "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==", - "license": "MIT" - }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", "dev": true, - "license": "MIT", "dependencies": { "char-regex": "^1.0.2", "strip-ansi": "^6.0.0" @@ -12370,7 +11727,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==", - "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -12385,7 +11741,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==", - "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -12400,7 +11755,6 @@ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", @@ -12422,7 +11776,6 @@ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", @@ -12441,7 +11794,6 @@ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -12458,7 +11810,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -12471,7 +11822,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -12484,7 +11834,6 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -12494,7 +11843,6 @@ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } @@ -12504,7 +11852,6 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" }, @@ -12512,13 +11859,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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": { + "@tokenizer/token": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/superagent": { "version": "8.1.2", "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.1.2.tgz", "integrity": "sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==", "deprecated": "Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net", "dev": true, - "license": "MIT", "dependencies": { "component-emitter": "^1.3.0", "cookiejar": "^2.1.4", @@ -12535,25 +11896,11 @@ "node": ">=6.4.0 <13 || >=14" } }, - "node_modules/superagent/node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true, - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/supertest": { "version": "6.3.4", "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.3.4.tgz", "integrity": "sha512-erY3HFDG0dPnhw4U+udPfrzXa4xhSG+n4rxfRuZWCUvjFWwKl+OxWf/7zk50s84/fAAs7vf5QAb9uRa0cCykxw==", "dev": true, - "license": "MIT", "dependencies": { "methods": "^1.1.2", "superagent": "^8.1.2" @@ -12566,7 +11913,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -12579,7 +11925,6 @@ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -12590,55 +11935,49 @@ "node_modules/swagger-ui-dist": { "version": "5.17.14", "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.17.14.tgz", - "integrity": "sha512-CVbSfaLpstV65OnSjbXfVd6Sta3q3F7Cj/yYuvHMp1P90LztOLs6PfUnKEVAeiIVQt9u2SaPwv0LiH/OyMjHRw==", - "license": "Apache-2.0" + "integrity": "sha512-CVbSfaLpstV65OnSjbXfVd6Sta3q3F7Cj/yYuvHMp1P90LztOLs6PfUnKEVAeiIVQt9u2SaPwv0LiH/OyMjHRw==" }, "node_modules/symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10" } }, "node_modules/synckit": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", - "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.8.tgz", + "integrity": "sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==", "dev": true, - "license": "MIT", "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, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/terser": { - "version": "5.39.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz", - "integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==", + "version": "5.43.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz", + "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==", "dev": true, - "license": "BSD-2-Clause", "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" }, @@ -12650,11 +11989,10 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.11", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.11.tgz", - "integrity": "sha512-RVCsMfuD0+cTt3EwX8hSl2Ks56EbFHWmhluwcqoPKtBnfjiT6olaq7PRIRfhyU8nnC2MrnDrBLfrD/RGE+cVXQ==", + "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, - "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", @@ -12689,7 +12027,6 @@ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -12700,11 +12037,10 @@ } }, "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", - "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", + "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, - "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", @@ -12724,7 +12060,6 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -12739,15 +12074,13 @@ "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==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, - "license": "ISC", "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", @@ -12758,11 +12091,10 @@ } }, "node_modules/test-exclude/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==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -12774,7 +12106,6 @@ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, - "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -12795,7 +12126,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -12806,50 +12136,25 @@ "node_modules/text-hex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", - "license": "MIT" + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true, - "license": "MIT" - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "license": "MIT", - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } + "dev": true }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, - "license": "MIT", "dependencies": { "os-tmpdir": "~1.0.2" }, @@ -12861,15 +12166,26 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true, - "license": "BSD-3-Clause" + "dev": true + }, + "node_modules/to-buffer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.1.tgz", + "integrity": "sha512-tB82LpAIWjhLYbqjx3X4zEeHN6M8CiuOEy2JY8SEQVdYRe3CCHOFaqrBW1doLDrfpWhplcW7BL+bO3/6S3pcDQ==", + "dependencies": { + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1", + "typed-array-buffer": "^1.0.3" + }, + "engines": { + "node": ">= 0.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==", "dev": true, - "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -12881,16 +12197,30 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", "engines": { "node": ">=0.6" } }, + "node_modules/token-types": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.0.3.tgz", + "integrity": "sha512-IKJ6EzuPPWtKtEIEPpIdXv9j5j2LGJEYk0CKY2efgKoYKLBiZdh6iQkLVBow/CB3phyWAWCyk+bZeaimJn6uRQ==", + "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/tough-cookie": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "license": "BSD-3-Clause", "dependencies": { "psl": "^1.1.28", "punycode": "^2.1.1" @@ -12902,15 +12232,13 @@ "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "dev": true, - "license": "MIT", "bin": { "tree-kill": "cli.js" } @@ -12919,7 +12247,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", - "license": "MIT", "engines": { "node": ">= 14.0.0" } @@ -12929,7 +12256,6 @@ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", "dev": true, - "license": "MIT", "engines": { "node": ">=16" }, @@ -12938,20 +12264,19 @@ } }, "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.4.0", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.0.tgz", + "integrity": "sha512-d423TJMnJGu80/eSgfQ5w/R+0zFJvdtTxwtF9KzFFunOpSeD+79lHJQIiAhluJoyGRbvj9NZJsl9WjCUo0ND7Q==", "dev": true, - "license": "MIT", "dependencies": { "bs-logger": "^0.2.6", "ejs": "^3.1.10", "fast-json-stable-stringify": "^2.1.0", - "jest-util": "^29.0.0", "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": { @@ -12962,10 +12287,11 @@ }, "peerDependencies": { "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/transform": "^29.0.0", - "@jest/types": "^29.0.0", - "babel-jest": "^29.0.0", - "jest": "^29.0.0", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", "typescript": ">=4.3 <6" }, "peerDependenciesMeta": { @@ -12983,15 +12309,29 @@ }, "esbuild": { "optional": true + }, + "jest-util": { + "optional": true } } }, + "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.2", "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.2.tgz", "integrity": "sha512-Qo4piXvOTWcMGIgRiuFa6nHNm+54HbYaZCKqc9eeZCLRy3XqafQgwX2F7mofrbJG3g7EEb+lkiR+z2Lic2s3Zw==", "dev": true, - "license": "MIT", "dependencies": { "chalk": "^4.1.0", "enhanced-resolve": "^5.0.0", @@ -13012,7 +12352,6 @@ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "devOptional": true, - "license": "MIT", "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -13056,7 +12395,6 @@ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", "dev": true, - "license": "MIT", "dependencies": { "json5": "^2.2.2", "minimist": "^1.2.6", @@ -13071,7 +12409,6 @@ "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.2.0.tgz", "integrity": "sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==", "dev": true, - "license": "MIT", "dependencies": { "chalk": "^4.1.0", "enhanced-resolve": "^5.7.0", @@ -13087,7 +12424,6 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } @@ -13095,14 +12431,12 @@ "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==", - "license": "0BSD" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "license": "Apache-2.0", "dependencies": { "safe-buffer": "^5.0.1" }, @@ -13113,15 +12447,13 @@ "node_modules/tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "license": "Unlicense" + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, - "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" }, @@ -13134,7 +12466,6 @@ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } @@ -13143,7 +12474,6 @@ "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -13155,7 +12485,6 @@ "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" @@ -13164,12 +12493,29 @@ "node": ">= 0.6" } }, + "node_modules/type-is/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/type-is/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/typed-array-buffer": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", - "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", @@ -13184,7 +12530,6 @@ "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "for-each": "^0.3.3", @@ -13204,7 +12549,6 @@ "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", "dev": true, - "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", @@ -13226,7 +12570,6 @@ "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", @@ -13245,30 +12588,27 @@ "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", - "license": "MIT" + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, "node_modules/typeorm": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.20.tgz", - "integrity": "sha512-sJ0T08dV5eoZroaq9uPKBoNcGslHBR4E4y+EBHs//SiGbblGe7IeduP/IH4ddCcj0qp3PHwDwGnuvqEAnKlq/Q==", - "license": "MIT", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.25.tgz", + "integrity": "sha512-fTKDFzWXKwAaBdEMU4k661seZewbNYET4r1J/z3Jwf+eAvlzMVpTLKAVcAzg75WwQk7GDmtsmkZ5MfkmXCiFWg==", "dependencies": { "@sqltools/formatter": "^1.2.5", + "ansis": "^3.17.0", "app-root-path": "^3.1.0", "buffer": "^6.0.3", - "chalk": "^4.1.2", - "cli-highlight": "^2.1.11", - "dayjs": "^1.11.9", - "debug": "^4.3.4", - "dotenv": "^16.0.3", - "glob": "^10.3.10", - "mkdirp": "^2.1.3", - "reflect-metadata": "^0.2.1", + "dayjs": "^1.11.13", + "debug": "^4.4.0", + "dedent": "^1.6.0", + "dotenv": "^16.4.7", + "glob": "^10.4.5", "sha.js": "^2.4.11", - "tslib": "^2.5.0", - "uuid": "^9.0.0", - "yargs": "^17.6.2" + "sql-highlight": "^6.0.0", + "tslib": "^2.8.1", + "uuid": "^11.1.0", + "yargs": "^17.7.2" }, "bin": { "typeorm": "cli.js", @@ -13282,23 +12622,24 @@ "url": "https://opencollective.com/typeorm" }, "peerDependencies": { - "@google-cloud/spanner": "^5.18.0", + "@google-cloud/spanner": "^5.18.0 || ^6.0.0 || ^7.0.0", "@sap/hana-client": "^2.12.25", - "better-sqlite3": "^7.1.2 || ^8.0.0 || ^9.0.0", + "better-sqlite3": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0", "hdb-pool": "^0.1.6", "ioredis": "^5.0.4", - "mongodb": "^5.8.0", - "mssql": "^9.1.1 || ^10.0.1", + "mongodb": "^5.8.0 || ^6.0.0", + "mssql": "^9.1.1 || ^10.0.1 || ^11.0.1", "mysql2": "^2.2.5 || ^3.0.1", "oracledb": "^6.3.0", "pg": "^8.5.1", "pg-native": "^3.0.0", "pg-query-stream": "^4.0.0", "redis": "^3.1.1 || ^4.0.0", + "reflect-metadata": "^0.1.14 || ^0.2.0", "sql.js": "^1.4.0", "sqlite3": "^5.0.3", "ts-node": "^10.7.0", - "typeorm-aurora-data-api-driver": "^2.0.0" + "typeorm-aurora-data-api-driver": "^2.0.0 || ^3.0.0" }, "peerDependenciesMeta": { "@google-cloud/spanner": { @@ -13372,46 +12713,39 @@ "url": "https://feross.org/support" } ], - "license": "MIT", "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, - "node_modules/typeorm/node_modules/mkdirp": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz", - "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==", - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, + "node_modules/typeorm/node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://dotenvx.com" } }, "node_modules/typeorm/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], - "license": "MIT", "bin": { - "uuid": "dist/bin/uuid" + "uuid": "dist/esm/bin/uuid" } }, "node_modules/typescript": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", - "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "devOptional": true, - "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -13424,7 +12758,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz", "integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==", - "license": "MIT", "dependencies": { "@lukeed/csprng": "^1.0.0" }, @@ -13432,12 +12765,22 @@ "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/unbox-primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", @@ -13455,23 +12798,20 @@ "version": "6.19.7", "resolved": "https://registry.npmjs.org/undici/-/undici-6.19.7.tgz", "integrity": "sha512-HR3W/bMGPSr90i8AAp2C4DM3wChFdJPLrWYpIS++LxS8K+W535qftjt+4MyjNYHeWabMj1nvtmLIi7l++iq91A==", - "license": "MIT", "engines": { "node": ">=18.17" } }, "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==", - "license": "MIT" + "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", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 10.0.0" } @@ -13480,15 +12820,14 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", "engines": { "node": ">= 0.8" } }, "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": [ { @@ -13504,7 +12843,6 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" @@ -13520,7 +12858,6 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } @@ -13528,14 +12865,12 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "license": "MIT", "engines": { "node": ">= 0.4.0" } @@ -13548,7 +12883,6 @@ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], - "license": "MIT", "bin": { "uuid": "dist/esm/bin/uuid" } @@ -13557,15 +12891,13 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "devOptional": true, - "license": "MIT" + "devOptional": true }, "node_modules/v8-to-istanbul": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", "dev": true, - "license": "ISC", "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", @@ -13576,10 +12908,9 @@ } }, "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==", - "license": "MIT", + "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" } @@ -13588,7 +12919,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", "engines": { "node": ">= 0.8" } @@ -13600,7 +12930,6 @@ "engines": [ "node >=0.6.0" ], - "license": "MIT", "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", @@ -13610,25 +12939,22 @@ "node_modules/verror/node_modules/core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "license": "MIT" + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", "dev": true, - "license": "Apache-2.0", "dependencies": { "makeerror": "1.0.12" } }, "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, - "license": "MIT", "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -13642,7 +12968,6 @@ "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", "dev": true, - "license": "MIT", "dependencies": { "defaults": "^1.0.3" } @@ -13650,19 +12975,18 @@ "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==", - "license": "BSD-2-Clause" + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "node_modules/webpack": { - "version": "5.98.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.98.0.tgz", - "integrity": "sha512-UFynvx+gM44Gv9qFgj0acCQK2VE1CtdfwFdimkapco3hlPCJ/zeq73n2yVKimVbtm+TnApIugGhLJnkU6gjYXA==", + "version": "5.99.9", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.9.tgz", + "integrity": "sha512-brOPwM3JnmOa+7kd3NsmOUOwbDAj8FT9xDsG3IW0MgbN9yZV7Oi/s/+MNQ/EcSMqw7qfoRyXPoeEWT8zLVdVGg==", "dev": true, - "license": "MIT", "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", @@ -13679,7 +13003,7 @@ "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^4.3.0", + "schema-utils": "^4.3.2", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.3.11", "watchpack": "^2.4.1", @@ -13706,17 +13030,15 @@ "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz", "integrity": "sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "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.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", + "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", "dev": true, - "license": "MIT", "engines": { "node": ">=10.13.0" } @@ -13726,7 +13048,6 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, - "license": "BSD-2-Clause", "peer": true, "dependencies": { "esrecurse": "^4.3.0", @@ -13741,18 +13062,39 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, - "license": "BSD-2-Clause", "peer": true, "engines": { "node": ">=4.0" } }, - "node_modules/webpack/node_modules/schema-utils": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", - "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", + "node_modules/webpack/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "peer": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "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, - "license": "MIT", "peer": true, "dependencies": { "@types/json-schema": "^7.0.9", @@ -13772,7 +13114,6 @@ "version": "0.7.4", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "license": "Apache-2.0", "dependencies": { "http-parser-js": ">=0.5.1", "safe-buffer": ">=5.1.0", @@ -13786,7 +13127,6 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "license": "Apache-2.0", "engines": { "node": ">=0.8.0" } @@ -13795,7 +13135,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -13805,7 +13144,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -13821,7 +13159,6 @@ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", "dev": true, - "license": "MIT", "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", @@ -13841,7 +13178,6 @@ "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", @@ -13864,19 +13200,11 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/which-builtin-type/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true, - "license": "MIT" - }, "node_modules/which-collection": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, - "license": "MIT", "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", @@ -13891,16 +13219,15 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz", - "integrity": "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==", - "dev": true, - "license": "MIT", + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "for-each": "^0.3.3", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" }, @@ -13915,7 +13242,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "license": "MIT", "dependencies": { "string-width": "^4.0.0" }, @@ -13927,7 +13253,6 @@ "version": "3.17.0", "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==", - "license": "MIT", "dependencies": { "@colors/colors": "^1.6.0", "@dabh/diagnostics": "^2.0.2", @@ -13949,7 +13274,6 @@ "version": "4.9.0", "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", - "license": "MIT", "dependencies": { "logform": "^2.7.0", "readable-stream": "^3.6.2", @@ -13959,66 +13283,37 @@ "node": ">= 12.0.0" } }, - "node_modules/winston-transport/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==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/winston/node_modules/@colors/colors": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", - "license": "MIT", "engines": { "node": ">=0.1.90" } }, - "node_modules/winston/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==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "license": "MIT", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/wrap-ansi-cjs": { @@ -14026,7 +13321,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -14042,16 +13336,13 @@ "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, - "license": "ISC" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/write-file-atomic": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", "dev": true, - "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" @@ -14064,14 +13355,12 @@ "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", - "license": "MIT", + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "engines": { "node": ">=10.0.0" }, @@ -14092,7 +13381,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "license": "MIT", "engines": { "node": ">=0.4" } @@ -14101,7 +13389,6 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "license": "ISC", "engines": { "node": ">=10" } @@ -14110,14 +13397,12 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "license": "MIT", "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -14135,7 +13420,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==", - "license": "ISC", "engines": { "node": ">=12" } @@ -14145,7 +13429,6 @@ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "devOptional": true, - "license": "MIT", "engines": { "node": ">=6" } @@ -14155,7 +13438,6 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, diff --git a/package.json b/package.json index f759b41..b496f90 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "onesignal-node": "^3.4.0", "passport-jwt": "^4.0.1", "pg": "^8.11.3", - "reflect-metadata": "^0.2.0", + "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", "typeorm": "^0.3.20", "winston": "^3.17.0", diff --git a/src/booking/controllers/bookable-space.controller.ts b/src/booking/controllers/bookable-space.controller.ts index 08b5179..fc41dd5 100644 --- a/src/booking/controllers/bookable-space.controller.ts +++ b/src/booking/controllers/bookable-space.controller.ts @@ -1,11 +1,24 @@ -import { Body, Controller, Post, UseGuards } from '@nestjs/common'; -import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger'; -import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; +import { ControllerRoute } from '@app/common/constants/controller-route'; import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; import { BaseResponseDto } from '@app/common/dto/base.response.dto'; -import { ControllerRoute } from '@app/common/constants/controller-route'; +import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; +import { + Body, + Controller, + Get, + Post, + Query, + Req, + UseGuards, +} from '@nestjs/common'; +import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; +import { PageResponse } from '@app/common/dto/pagination.response.dto'; +import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; +import { plainToInstance } from 'class-transformer'; import { CreateBookableSpaceDto } from '../dtos'; +import { BookableSpaceRequestDto } from '../dtos/bookable-space-request.dto'; +import { BookableSpaceResponseDto } from '../dtos/bookable-space-response.dto'; import { BookableSpaceService } from '../services'; @ApiTags('Booking Module') @@ -25,6 +38,45 @@ export class BookableSpaceController { ControllerRoute.BOOKABLE_SPACES.ACTIONS.ADD_BOOKABLE_SPACES_DESCRIPTION, }) async create(@Body() dto: CreateBookableSpaceDto): Promise { - return this.bookableSpaceService.create(dto); + const result = await this.bookableSpaceService.create(dto); + return new SuccessResponseDto({ + data: result, + message: 'Successfully created bookable spaces', + }); + } + + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Get() + @ApiOperation({ + summary: + ControllerRoute.BOOKABLE_SPACES.ACTIONS.GET_ALL_BOOKABLE_SPACES_SUMMARY, + description: + ControllerRoute.BOOKABLE_SPACES.ACTIONS + .GET_ALL_BOOKABLE_SPACES_DESCRIPTION, + }) + async findAll( + @Query() query: BookableSpaceRequestDto, + @Req() req: Request, + ): Promise> { + const project = req['user']?.project?.uuid; + if (!project) { + throw new Error('Project UUID is required in the request'); + } + const { data, pagination } = await this.bookableSpaceService.findAll( + query, + project, + ); + return new PageResponse( + { + data: data.map((space) => + plainToInstance(BookableSpaceResponseDto, space, { + excludeExtraneousValues: true, + }), + ), + message: 'Successfully fetched all bookable spaces', + }, + pagination, + ); } } diff --git a/src/booking/dtos/bookable-space-request.dto.ts b/src/booking/dtos/bookable-space-request.dto.ts new file mode 100644 index 0000000..f0442cd --- /dev/null +++ b/src/booking/dtos/bookable-space-request.dto.ts @@ -0,0 +1,31 @@ +import { BooleanValues } from '@app/common/constants/boolean-values.enum'; +import { PaginationRequestGetListDto } from '@app/common/dto/pagination.request.dto'; +import { ApiProperty, OmitType } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; +import { IsBoolean, IsNotEmpty, IsOptional } from 'class-validator'; + +export class BookableSpaceRequestDto extends OmitType( + PaginationRequestGetListDto, + ['includeSpaces'], +) { + @ApiProperty({ + type: Boolean, + required: false, + }) + @IsBoolean() + @IsOptional() + @Transform(({ obj }) => { + return obj.active === BooleanValues.TRUE; + }) + active?: boolean; + + @ApiProperty({ + type: Boolean, + }) + @IsBoolean() + @IsNotEmpty() + @Transform(({ obj }) => { + return obj.configured === BooleanValues.TRUE; + }) + configured: boolean; +} diff --git a/src/booking/dtos/bookable-space-response.dto.ts b/src/booking/dtos/bookable-space-response.dto.ts new file mode 100644 index 0000000..b637b69 --- /dev/null +++ b/src/booking/dtos/bookable-space-response.dto.ts @@ -0,0 +1,58 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Expose, Type } from 'class-transformer'; +export class BookableSpaceConfigResponseDto { + @ApiProperty() + @Expose() + uuid: string; + + @ApiProperty({ + type: [String], + }) + @Expose() + daysAvailable: string[]; + + @ApiProperty() + @Expose() + startTime: string; + + @ApiProperty() + @Expose() + endTime: string; + + @ApiProperty({ + type: Boolean, + }) + @Expose() + active: boolean; + + @ApiProperty({ + type: Number, + }) + @Expose() + points: number; +} + +export class BookableSpaceResponseDto { + @ApiProperty() + @Expose() + uuid: string; + + @ApiProperty() + @Expose() + spaceUuid: string; + + @ApiProperty() + @Expose() + spaceName: string; + + @ApiProperty() + @Expose() + virtualLocation: string; + + @ApiProperty({ + type: BookableSpaceConfigResponseDto, + }) + @Expose() + @Type(() => BookableSpaceConfigResponseDto) + bookableConfig: BookableSpaceConfigResponseDto; +} diff --git a/src/booking/services/bookable-space.service.ts b/src/booking/services/bookable-space.service.ts index 409ee35..791486e 100644 --- a/src/booking/services/bookable-space.service.ts +++ b/src/booking/services/bookable-space.service.ts @@ -1,17 +1,19 @@ +import { BaseResponseDto } from '@app/common/dto/base.response.dto'; +import { PageResponseDto } from '@app/common/dto/pagination.response.dto'; +import { timeToMinutes } from '@app/common/helper/timeToMinutes'; +import { TypeORMCustomModel } from '@app/common/models/typeOrmCustom.model'; +import { BookableSpaceEntityRepository } from '@app/common/modules/booking/repositories'; +import { SpaceEntity } from '@app/common/modules/space/entities/space.entity'; +import { SpaceRepository } from '@app/common/modules/space/repositories/space.repository'; import { + BadRequestException, + ConflictException, Injectable, NotFoundException, - ConflictException, - BadRequestException, } from '@nestjs/common'; -import { CreateBookableSpaceDto } from '../dtos'; -import { BaseResponseDto } from '@app/common/dto/base.response.dto'; -import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; -import { SpaceEntity } from '@app/common/modules/space/entities/space.entity'; -import { BookableSpaceEntityRepository } from '@app/common/modules/booking/repositories'; -import { SpaceRepository } from '@app/common/modules/space/repositories/space.repository'; import { In } from 'typeorm'; -import { timeToMinutes } from '@app/common/helper/timeToMinutes'; +import { CreateBookableSpaceDto } from '../dtos'; +import { BookableSpaceRequestDto } from '../dtos/bookable-space-request.dto'; @Injectable() export class BookableSpaceService { @@ -20,7 +22,7 @@ export class BookableSpaceService { private readonly spaceRepository: SpaceRepository, ) {} - async create(dto: CreateBookableSpaceDto): Promise { + async create(dto: CreateBookableSpaceDto) { // Validate time slots first this.validateTimeSlot(dto.startTime, dto.endTime); @@ -34,6 +36,47 @@ export class BookableSpaceService { return this.createBookableSpaces(spaces, dto); } + async findAll( + { active, page, size, configured }: BookableSpaceRequestDto, + project: string, + ): Promise<{ + data: BaseResponseDto['data']; + pagination: PageResponseDto; + }> { + let qb = this.spaceRepository + .createQueryBuilder('space') + .leftJoinAndSelect('space.parent', 'parentSpace') + .leftJoinAndSelect('space.community', 'community') + .where('community.project = :project', { project }); + + if (configured) { + qb = qb + .leftJoinAndSelect('space.bookableConfig', 'bookableConfig') + .andWhere('bookableConfig.uuid IS NOT NULL'); + if (active !== undefined) { + qb = qb.andWhere('bookableConfig.active = :active', { active }); + } + } else { + qb = qb + .leftJoinAndSelect('space.bookableConfig', 'bookableConfig') + .andWhere('bookableConfig.uuid IS NULL'); + } + + const customModel = TypeORMCustomModel(this.spaceRepository); + + const { baseResponseDto, paginationResponseDto } = + await customModel.findAll({ page, size, modelName: 'space' }, qb); + return { + data: baseResponseDto.data.map((space) => { + return { + ...space, + virtualLocation: `${space.community?.name} - ${space.parent ? space.parent?.spaceName + ' - ' : ''}${space.spaceName}`, + }; + }), + pagination: paginationResponseDto, + }; + } + /** * Fetch spaces by UUIDs and throw an error if any are missing */ @@ -98,7 +141,7 @@ export class BookableSpaceService { private async createBookableSpaces( spaces: SpaceEntity[], dto: CreateBookableSpaceDto, - ): Promise { + ) { try { const entries = spaces.map((space) => this.bookableSpaceEntityRepository.create({ @@ -110,11 +153,7 @@ export class BookableSpaceService { }), ); - const data = await this.bookableSpaceEntityRepository.save(entries); - return new SuccessResponseDto({ - data, - message: 'Successfully added new bookable spaces', - }); + return this.bookableSpaceEntityRepository.save(entries); } catch (error) { if (error.code === '23505') { throw new ConflictException( From 7a07f39f1692de10bc02c80a6c03eb0ab8f9868c Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Tue, 8 Jul 2025 11:25:15 +0300 Subject: [PATCH 59/86] add communities filter to devices by project API (#455) --- src/device/dtos/get.device.dto.ts | 25 +++++++++++++++++++++++-- src/device/services/device.service.ts | 22 ++++++++++++++++++---- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/src/device/dtos/get.device.dto.ts b/src/device/dtos/get.device.dto.ts index e34a8b6..429273a 100644 --- a/src/device/dtos/get.device.dto.ts +++ b/src/device/dtos/get.device.dto.ts @@ -1,7 +1,7 @@ import { DeviceTypeEnum } from '@app/common/constants/device-type.enum'; import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; import { - IsArray, IsEnum, IsNotEmpty, IsOptional, @@ -74,13 +74,34 @@ export class GetDevicesFilterDto { @IsEnum(DeviceTypeEnum) @IsOptional() public deviceType: DeviceTypeEnum; + @ApiProperty({ description: 'List of Space IDs to filter devices', required: false, example: ['60d21b4667d0d8992e610c85', '60d21b4967d0d8992e610c86'], }) @IsOptional() - @IsArray() + @Transform(({ value }) => { + if (!Array.isArray(value)) { + return [value]; + } + return value; + }) @IsUUID('4', { each: true }) public spaces?: string[]; + + @ApiProperty({ + description: 'List of Community IDs to filter devices', + required: false, + example: ['60d21b4667d0d8992e610c85', '60d21b4967d0d8992e610c86'], + }) + @Transform(({ value }) => { + if (!Array.isArray(value)) { + return [value]; + } + return value; + }) + @IsOptional() + @IsUUID('4', { each: true }) + public communities?: string[]; } diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index e4c0da5..cc6f911 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -100,12 +100,15 @@ export class DeviceService { async getAllDevices( param: ProjectParam, - { deviceType, spaces }: GetDevicesFilterDto, + { deviceType, spaces, communities }: GetDevicesFilterDto, ): Promise { try { await this.validateProject(param.projectUuid); if (deviceType === DeviceTypeEnum.DOOR_LOCK) { - return await this.getDoorLockDevices(param.projectUuid, spaces); + return await this.getDoorLockDevices(param.projectUuid, { + spaces, + communities, + }); } else if (!deviceType) { const devices = await this.deviceRepository.find({ where: { @@ -113,7 +116,13 @@ export class DeviceService { spaceDevice: { uuid: spaces && spaces.length ? In(spaces) : undefined, spaceName: Not(ORPHAN_SPACE_NAME), - community: { project: { uuid: param.projectUuid } }, + community: { + uuid: + communities && communities.length + ? In(communities) + : undefined, + project: { uuid: param.projectUuid }, + }, }, }, relations: [ @@ -1247,7 +1256,10 @@ export class DeviceService { await this.deviceRepository.save(updatedDevices); } - private async getDoorLockDevices(projectUuid: string, spaces?: string[]) { + private async getDoorLockDevices( + projectUuid: string, + { communities, spaces }: { spaces?: string[]; communities?: string[] }, + ) { await this.validateProject(projectUuid); const devices = await this.deviceRepository.find({ @@ -1259,6 +1271,8 @@ export class DeviceService { spaceName: Not(ORPHAN_SPACE_NAME), uuid: spaces && spaces.length ? In(spaces) : undefined, community: { + uuid: + communities && communities.length ? In(communities) : undefined, project: { uuid: projectUuid, }, From 9971fb953d5c82967a645cfb3301363a4d081283 Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Tue, 8 Jul 2025 11:52:22 +0300 Subject: [PATCH 60/86] SP-1812: Task/booking-system/update-api (#456) * add update bookable spaces API * add search to get bookable spaces API --- libs/common/src/constants/controller-route.ts | 6 ++++ .../controllers/bookable-space.controller.ts | 24 +++++++++++++ .../dtos/bookable-space-request.dto.ts | 4 +-- src/booking/dtos/create-bookable-space.dto.ts | 1 - src/booking/dtos/update-bookable-space.dto.ts | 12 +++++++ .../services/bookable-space.service.ts | 34 ++++++++++++++++++- 6 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 src/booking/dtos/update-bookable-space.dto.ts diff --git a/libs/common/src/constants/controller-route.ts b/libs/common/src/constants/controller-route.ts index dedf0f1..1c7ee7c 100644 --- a/libs/common/src/constants/controller-route.ts +++ b/libs/common/src/constants/controller-route.ts @@ -83,6 +83,12 @@ export class ControllerRoute { public static readonly GET_ALL_BOOKABLE_SPACES_DESCRIPTION = 'This endpoint retrieves all bookable spaces.'; + + public static readonly UPDATE_BOOKABLE_SPACES_SUMMARY = + 'Update existing bookable spaces'; + + public static readonly UPDATE_BOOKABLE_SPACES_DESCRIPTION = + 'This endpoint allows you to update existing bookable spaces by providing the required details.'; }; }; static COMMUNITY = class { diff --git a/src/booking/controllers/bookable-space.controller.ts b/src/booking/controllers/bookable-space.controller.ts index fc41dd5..65d7b33 100644 --- a/src/booking/controllers/bookable-space.controller.ts +++ b/src/booking/controllers/bookable-space.controller.ts @@ -6,7 +6,9 @@ import { Body, Controller, Get, + Param, Post, + Put, Query, Req, UseGuards, @@ -19,6 +21,7 @@ import { plainToInstance } from 'class-transformer'; import { CreateBookableSpaceDto } from '../dtos'; import { BookableSpaceRequestDto } from '../dtos/bookable-space-request.dto'; import { BookableSpaceResponseDto } from '../dtos/bookable-space-response.dto'; +import { UpdateBookableSpaceDto } from '../dtos/update-bookable-space.dto'; import { BookableSpaceService } from '../services'; @ApiTags('Booking Module') @@ -79,4 +82,25 @@ export class BookableSpaceController { pagination, ); } + + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Put(':spaceUuid') + @ApiOperation({ + summary: + ControllerRoute.BOOKABLE_SPACES.ACTIONS.UPDATE_BOOKABLE_SPACES_SUMMARY, + description: + ControllerRoute.BOOKABLE_SPACES.ACTIONS + .UPDATE_BOOKABLE_SPACES_DESCRIPTION, + }) + async update( + @Param('spaceUuid') spaceUuid: string, + @Body() dto: UpdateBookableSpaceDto, + ): Promise { + const result = await this.bookableSpaceService.update(spaceUuid, dto); + return new SuccessResponseDto({ + data: result, + message: 'Successfully updated bookable spaces', + }); + } } diff --git a/src/booking/dtos/bookable-space-request.dto.ts b/src/booking/dtos/bookable-space-request.dto.ts index f0442cd..e958aa0 100644 --- a/src/booking/dtos/bookable-space-request.dto.ts +++ b/src/booking/dtos/bookable-space-request.dto.ts @@ -1,11 +1,11 @@ import { BooleanValues } from '@app/common/constants/boolean-values.enum'; -import { PaginationRequestGetListDto } from '@app/common/dto/pagination.request.dto'; +import { PaginationRequestWithSearchGetListDto } from '@app/common/dto/pagination-with-search.request.dto'; import { ApiProperty, OmitType } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; import { IsBoolean, IsNotEmpty, IsOptional } from 'class-validator'; export class BookableSpaceRequestDto extends OmitType( - PaginationRequestGetListDto, + PaginationRequestWithSearchGetListDto, ['includeSpaces'], ) { @ApiProperty({ diff --git a/src/booking/dtos/create-bookable-space.dto.ts b/src/booking/dtos/create-bookable-space.dto.ts index 6508c36..df40ee8 100644 --- a/src/booking/dtos/create-bookable-space.dto.ts +++ b/src/booking/dtos/create-bookable-space.dto.ts @@ -1,4 +1,3 @@ -// dtos/bookable-space.dto.ts import { DaysEnum } from '@app/common/constants/days.enum'; import { ApiProperty } from '@nestjs/swagger'; import { diff --git a/src/booking/dtos/update-bookable-space.dto.ts b/src/booking/dtos/update-bookable-space.dto.ts new file mode 100644 index 0000000..2b7f048 --- /dev/null +++ b/src/booking/dtos/update-bookable-space.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty, OmitType, PartialType } from '@nestjs/swagger'; +import { IsBoolean, IsOptional } from 'class-validator'; +import { CreateBookableSpaceDto } from './create-bookable-space.dto'; + +export class UpdateBookableSpaceDto extends PartialType( + OmitType(CreateBookableSpaceDto, ['spaceUuids']), +) { + @ApiProperty({ type: Boolean }) + @IsOptional() + @IsBoolean() + active?: boolean; +} diff --git a/src/booking/services/bookable-space.service.ts b/src/booking/services/bookable-space.service.ts index 791486e..ae8330b 100644 --- a/src/booking/services/bookable-space.service.ts +++ b/src/booking/services/bookable-space.service.ts @@ -14,6 +14,7 @@ import { import { In } from 'typeorm'; import { CreateBookableSpaceDto } from '../dtos'; import { BookableSpaceRequestDto } from '../dtos/bookable-space-request.dto'; +import { UpdateBookableSpaceDto } from '../dtos/update-bookable-space.dto'; @Injectable() export class BookableSpaceService { @@ -37,7 +38,7 @@ export class BookableSpaceService { } async findAll( - { active, page, size, configured }: BookableSpaceRequestDto, + { active, page, size, configured, search }: BookableSpaceRequestDto, project: string, ): Promise<{ data: BaseResponseDto['data']; @@ -49,6 +50,12 @@ export class BookableSpaceService { .leftJoinAndSelect('space.community', 'community') .where('community.project = :project', { project }); + if (search) { + qb = qb.andWhere( + 'space.spaceName ILIKE :search OR community.name ILIKE :search OR parentSpace.spaceName ILIKE :search', + { search: `%${search}%` }, + ); + } if (configured) { qb = qb .leftJoinAndSelect('space.bookableConfig', 'bookableConfig') @@ -77,6 +84,30 @@ export class BookableSpaceService { }; } + /** + * todo: if updating availability, send to the ones who have access to this space + * todo: if updating other fields, just send emails to all users who's bookings might be affected + */ + async update(spaceUuid: string, dto: UpdateBookableSpaceDto) { + // fetch spaces exist + const space = (await this.getSpacesOrFindMissing([spaceUuid]))[0]; + + if (!space.bookableConfig) { + throw new NotFoundException( + `Bookable configuration not found for space: ${spaceUuid}`, + ); + } + if (dto.startTime || dto.endTime) { + // Validate time slots first + this.validateTimeSlot( + dto.startTime || space.bookableConfig.startTime, + dto.endTime || space.bookableConfig.endTime, + ); + } + Object.assign(space.bookableConfig, dto); + return this.bookableSpaceEntityRepository.save(space.bookableConfig); + } + /** * Fetch spaces by UUIDs and throw an error if any are missing */ @@ -85,6 +116,7 @@ export class BookableSpaceService { ): Promise { const spaces = await this.spaceRepository.find({ where: { uuid: In(spaceUuids) }, + relations: ['bookableConfig'], }); if (spaces.length !== spaceUuids.length) { From 0bb178ed1087aaf44fbab1a20795b5c94ae93e84 Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Tue, 8 Jul 2025 14:50:00 +0300 Subject: [PATCH 61/86] make point nullable (#457) --- .../booking/entities/bookable-space.entity.ts | 4 ++-- src/booking/dtos/bookable-space-response.dto.ts | 3 ++- src/booking/dtos/create-bookable-space.dto.ts | 12 +++++++----- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/libs/common/src/modules/booking/entities/bookable-space.entity.ts b/libs/common/src/modules/booking/entities/bookable-space.entity.ts index 8e53cba..d7ad3ec 100644 --- a/libs/common/src/modules/booking/entities/bookable-space.entity.ts +++ b/libs/common/src/modules/booking/entities/bookable-space.entity.ts @@ -40,8 +40,8 @@ export class BookableSpaceEntity extends AbstractEntity { @Column({ type: Boolean, default: true }) active: boolean; - @Column({ type: 'int' }) - points: number; + @Column({ type: 'int', default: null }) + points?: number; @CreateDateColumn() createdAt: Date; diff --git a/src/booking/dtos/bookable-space-response.dto.ts b/src/booking/dtos/bookable-space-response.dto.ts index b637b69..3e89bd2 100644 --- a/src/booking/dtos/bookable-space-response.dto.ts +++ b/src/booking/dtos/bookable-space-response.dto.ts @@ -27,9 +27,10 @@ export class BookableSpaceConfigResponseDto { @ApiProperty({ type: Number, + nullable: true, }) @Expose() - points: number; + points?: number; } export class BookableSpaceResponseDto { diff --git a/src/booking/dtos/create-bookable-space.dto.ts b/src/booking/dtos/create-bookable-space.dto.ts index df40ee8..f7389f1 100644 --- a/src/booking/dtos/create-bookable-space.dto.ts +++ b/src/booking/dtos/create-bookable-space.dto.ts @@ -1,16 +1,17 @@ import { DaysEnum } from '@app/common/constants/days.enum'; import { ApiProperty } from '@nestjs/swagger'; import { + ArrayMinSize, IsArray, IsEnum, + IsInt, IsNotEmpty, + IsOptional, IsString, IsUUID, - IsInt, - ArrayMinSize, + Matches, Max, Min, - Matches, } from 'class-validator'; export class CreateBookableSpaceDto { @@ -53,9 +54,10 @@ export class CreateBookableSpaceDto { }) endTime: string; - @ApiProperty({ example: 10 }) + @ApiProperty({ example: 10, required: false }) + @IsOptional() @IsInt() @Min(0, { message: 'Points cannot be negative' }) @Max(1000, { message: 'Points cannot exceed 1000' }) - points: number; + points?: number; } From 5cf45c30f43ea118e4ced5109cbc469a19184bbc Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Tue, 8 Jul 2025 16:55:56 +0300 Subject: [PATCH 62/86] fix: check if device not found (#458) --- src/device/services/device.service.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index cc6f911..02210b7 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -717,6 +717,9 @@ export class DeviceService { relations: ['productDevice'], }); + if (!deviceDetails) { + throw new NotFoundException('Device not found'); + } let result = await this.tuyaService.getDeviceDetails(deviceId); if (!result) { From 2589e391ed56f07176eb4350a6942c31854e9fef Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Wed, 9 Jul 2025 11:33:04 +0300 Subject: [PATCH 63/86] fix: add unique validation on subspaces in update space dto (#460) --- src/space/dtos/update.space.dto.ts | 15 +++++++++++++++ src/space/services/space.service.ts | 2 +- .../subspace-product-allocation.service.ts | 4 ++-- src/space/services/subspace/subspace.service.ts | 2 +- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/space/dtos/update.space.dto.ts b/src/space/dtos/update.space.dto.ts index 93ff377..0491aa1 100644 --- a/src/space/dtos/update.space.dto.ts +++ b/src/space/dtos/update.space.dto.ts @@ -2,6 +2,7 @@ import { ORPHAN_SPACE_NAME } from '@app/common/constants/orphan-constant'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { + ArrayUnique, IsArray, IsNumber, IsOptional, @@ -49,6 +50,20 @@ export class UpdateSpaceDto { description: 'List of subspace modifications', type: [UpdateSubspaceDto], }) + @ArrayUnique((subspace) => subspace.subspaceName, { + message(validationArguments) { + const subspaces = validationArguments.value; + const nameCounts = subspaces.reduce((acc, curr) => { + acc[curr.subspaceName] = (acc[curr.subspaceName] || 0) + 1; + return acc; + }, {}); + // Find duplicates + const duplicates = Object.keys(nameCounts).filter( + (name) => nameCounts[name] > 1, + ); + return `Duplicate subspace names found: ${duplicates.join(', ')}`; + }, + }) @IsOptional() @IsArray() @ValidateNested({ each: true }) diff --git a/src/space/services/space.service.ts b/src/space/services/space.service.ts index a4e3af7..f0ea822 100644 --- a/src/space/services/space.service.ts +++ b/src/space/services/space.service.ts @@ -426,7 +426,7 @@ export class SpaceService { } } - async disableSpace(space: SpaceEntity, orphanSpace: SpaceEntity) { + private async disableSpace(space: SpaceEntity, orphanSpace: SpaceEntity) { await this.commandBus.execute( new DisableSpaceCommand({ spaceUuid: space.uuid, orphanSpace }), ); diff --git a/src/space/services/subspace/subspace-product-allocation.service.ts b/src/space/services/subspace/subspace-product-allocation.service.ts index a74823c..4ba7c9c 100644 --- a/src/space/services/subspace/subspace-product-allocation.service.ts +++ b/src/space/services/subspace/subspace-product-allocation.service.ts @@ -23,7 +23,7 @@ export class SubspaceProductAllocationService { // spaceAllocationsToExclude?: SpaceProductAllocationEntity[], ): Promise { try { - if (!allocationsData.length) return; + if (!allocationsData?.length) return; const allocations: SubspaceProductAllocationEntity[] = []; @@ -112,7 +112,7 @@ export class SubspaceProductAllocationService { ); // Create the product-tag mapping based on the processed tags - const productTagMapping = subspace.productAllocations.map( + const productTagMapping = subspace.productAllocations?.map( ({ tagUuid, tagName, productUuid }) => { const inputTag = tagUuid ? createdTagsByUUID.get(tagUuid) diff --git a/src/space/services/subspace/subspace.service.ts b/src/space/services/subspace/subspace.service.ts index d1bf83f..15bda14 100644 --- a/src/space/services/subspace/subspace.service.ts +++ b/src/space/services/subspace/subspace.service.ts @@ -39,7 +39,7 @@ export class SubSpaceService { private readonly subspaceProductAllocationService: SubspaceProductAllocationService, ) {} - async createSubspaces( + private async createSubspaces( subspaceData: Array<{ subspaceName: string; space: SpaceEntity; From 83be80d9f6c94b5e690071e7b88db1035b569a07 Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Wed, 9 Jul 2025 11:44:14 +0300 Subject: [PATCH 64/86] add order space API (#459) --- libs/common/src/constants/controller-route.ts | 5 +++ .../modules/space/entities/space.entity.ts | 10 ++++- src/space/controllers/space.controller.ts | 33 ++++++++++++--- src/space/dtos/order.spaces.dto.ts | 13 ++++++ src/space/services/space.service.ts | 40 ++++++++++++++++++- 5 files changed, 92 insertions(+), 9 deletions(-) create mode 100644 src/space/dtos/order.spaces.dto.ts diff --git a/libs/common/src/constants/controller-route.ts b/libs/common/src/constants/controller-route.ts index 1c7ee7c..af7bda4 100644 --- a/libs/common/src/constants/controller-route.ts +++ b/libs/common/src/constants/controller-route.ts @@ -220,6 +220,11 @@ export class ControllerRoute { public static readonly UPDATE_SPACE_DESCRIPTION = 'Updates a space by its UUID and community ID. You can update the name, parent space, and other properties. If a parent space is provided and not already a parent, its `isParent` flag will be set to true.'; + public static readonly UPDATE_CHILDREN_SPACES_ORDER_OF_A_SPACE_SUMMARY = + 'Update the order of child spaces under a specific parent space'; + public static readonly UPDATE_CHILDREN_SPACES_ORDER_OF_A_SPACE_DESCRIPTION = + 'Updates the order of child spaces under a specific parent space. You can provide a new order for the child spaces.'; + public static readonly GET_HEIRARCHY_SUMMARY = 'Get space hierarchy'; public static readonly GET_HEIRARCHY_DESCRIPTION = 'This endpoint retrieves the hierarchical structure of spaces under a given space ID. It returns all the child spaces nested within the specified space, organized by their parent-child relationships. '; diff --git a/libs/common/src/modules/space/entities/space.entity.ts b/libs/common/src/modules/space/entities/space.entity.ts index 763ffec..6a04865 100644 --- a/libs/common/src/modules/space/entities/space.entity.ts +++ b/libs/common/src/modules/space/entities/space.entity.ts @@ -6,9 +6,9 @@ import { OneToMany, OneToOne, } from 'typeorm'; -import { SpaceDto } from '../dtos'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { AqiSpaceDailyPollutantStatsEntity } from '../../aqi/entities'; +import { BookableSpaceEntity } from '../../booking/entities'; import { CommunityEntity } from '../../community/entities'; import { DeviceEntity } from '../../device/entities'; import { InviteUserSpaceEntity } from '../../Invite-user/entities'; @@ -17,9 +17,9 @@ import { PresenceSensorDailySpaceEntity } from '../../presence-sensor/entities'; import { SceneEntity } from '../../scene/entities'; import { SpaceModelEntity } from '../../space-model'; import { UserSpaceEntity } from '../../user/entities'; +import { SpaceDto } from '../dtos'; import { SpaceProductAllocationEntity } from './space-product-allocation.entity'; import { SubspaceEntity } from './subspace/subspace.entity'; -import { BookableSpaceEntity } from '../../booking/entities'; @Entity({ name: 'space' }) export class SpaceEntity extends AbstractEntity { @@ -64,6 +64,12 @@ export class SpaceEntity extends AbstractEntity { }) public disabled: boolean; + @Column({ + nullable: true, + type: Number, + }) + public order?: number; + @OneToMany(() => SubspaceEntity, (subspace) => subspace.space, { nullable: true, }) diff --git a/src/space/controllers/space.controller.ts b/src/space/controllers/space.controller.ts index ca47488..20660ca 100644 --- a/src/space/controllers/space.controller.ts +++ b/src/space/controllers/space.controller.ts @@ -1,6 +1,5 @@ -import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; -import { SpaceService } from '../services'; import { ControllerRoute } from '@app/common/constants/controller-route'; +import { BaseResponseDto } from '@app/common/dto/base.response.dto'; import { Body, Controller, @@ -12,12 +11,14 @@ import { Query, UseGuards, } from '@nestjs/common'; -import { AddSpaceDto, CommunitySpaceParam, UpdateSpaceDto } from '../dtos'; -import { BaseResponseDto } from '@app/common/dto/base.response.dto'; -import { GetSpaceParam } from '../dtos/get.space.param'; -import { PermissionsGuard } from 'src/guards/permissions.guard'; +import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; import { Permissions } from 'src/decorators/permissions.decorator'; +import { PermissionsGuard } from 'src/guards/permissions.guard'; +import { AddSpaceDto, CommunitySpaceParam, UpdateSpaceDto } from '../dtos'; import { GetSpaceDto } from '../dtos/get.space.dto'; +import { GetSpaceParam } from '../dtos/get.space.param'; +import { OrderSpacesDto } from '../dtos/order.spaces.dto'; +import { SpaceService } from '../services'; @ApiTags('Space Module') @Controller({ @@ -65,6 +66,26 @@ export class SpaceController { ); } + @ApiBearerAuth() + @UseGuards(PermissionsGuard) + @Permissions('SPACE_UPDATE') + @ApiOperation({ + summary: + ControllerRoute.SPACE.ACTIONS + .UPDATE_CHILDREN_SPACES_ORDER_OF_A_SPACE_SUMMARY, + description: + ControllerRoute.SPACE.ACTIONS + .UPDATE_CHILDREN_SPACES_ORDER_OF_A_SPACE_DESCRIPTION, + }) + @Post(':parentSpaceUuid/spaces/order') + async updateSpacesOrder( + @Body() orderSpacesDto: OrderSpacesDto, + @Param() communitySpaceParam: CommunitySpaceParam, + @Param('parentSpaceUuid') parentSpaceUuid: string, + ) { + return this.spaceService.updateSpacesOrder(parentSpaceUuid, orderSpacesDto); + } + @ApiBearerAuth() @UseGuards(PermissionsGuard) @Permissions('SPACE_DELETE') diff --git a/src/space/dtos/order.spaces.dto.ts b/src/space/dtos/order.spaces.dto.ts new file mode 100644 index 0000000..275af9f --- /dev/null +++ b/src/space/dtos/order.spaces.dto.ts @@ -0,0 +1,13 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { ArrayUnique, IsNotEmpty, IsUUID } from 'class-validator'; + +export class OrderSpacesDto { + @ApiProperty({ + description: 'List of children spaces associated with the space', + type: [String], + }) + @IsNotEmpty() + @ArrayUnique() + @IsUUID('4', { each: true, message: 'Invalid space UUID provided' }) + spacesUuids: string[]; +} diff --git a/src/space/services/space.service.ts b/src/space/services/space.service.ts index f0ea822..5ea39cd 100644 --- a/src/space/services/space.service.ts +++ b/src/space/services/space.service.ts @@ -33,6 +33,7 @@ import { } from '../dtos'; import { CreateProductAllocationDto } from '../dtos/create-product-allocation.dto'; import { GetSpaceDto } from '../dtos/get.space.dto'; +import { OrderSpacesDto } from '../dtos/order.spaces.dto'; import { SpaceWithParentsDto } from '../dtos/space.parents.dto'; import { SpaceProductAllocationService } from './space-product-allocation.service'; import { ValidationService } from './space-validation.service'; @@ -355,6 +356,32 @@ export class SpaceService { } } + async updateSpacesOrder( + parentSpaceUuid: string, + { spacesUuids }: OrderSpacesDto, + ) { + try { + await this.spaceRepository.update( + { uuid: In(spacesUuids), parent: { uuid: parentSpaceUuid } }, + { + order: () => + 'CASE ' + + spacesUuids + .map((s, index) => `WHEN uuid = '${s}' THEN ${index + 1}`) + .join(' ') + + ' END', + }, + ); + return true; + } catch (error) { + console.error('Error updating spaces order:', error); + throw new HttpException( + 'An error occurred while updating spaces order', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async delete(params: GetSpaceParam): Promise { const queryRunner = this.dataSource.createQueryRunner(); await queryRunner.connect(); @@ -709,10 +736,21 @@ export class SpaceService { rootSpaces.push(map.get(space.uuid)!); // Push only root spaces } }); - + rootSpaces.forEach(this.sortSpaceChildren.bind(this)); return rootSpaces; } + private sortSpaceChildren(space: SpaceEntity) { + if (space.children && space.children.length > 0) { + space.children.sort((a, b) => { + const aOrder = a.order ?? Infinity; + const bOrder = b.order ?? Infinity; + return aOrder - bOrder; + }); + space.children.forEach(this.sortSpaceChildren.bind(this)); // Recursively sort children of children + } + } + private validateSpaceCreationCriteria({ spaceModelUuid, productAllocations, From 74d3620d0ec01c97ef78efc77c22c6220ab3e421 Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Wed, 9 Jul 2025 14:25:26 +0300 Subject: [PATCH 65/86] Chore/space link tag cleanup (#462) * chore: remove unused imports and dead code for space link * chore: remove unused imports and dead code * chore: remove unused imports and dead code of tag service --- .../src/modules/space/entities/space-link.entity.ts | 3 --- .../src/modules/space/repositories/space.repository.ts | 3 --- src/community/community.module.ts | 6 ------ src/invite-user/invite-user.module.ts | 2 -- src/power-clamp/power-clamp.module.ts | 2 -- src/project/project.module.ts | 2 -- src/space-model/space-model.module.ts | 2 -- src/space/services/index.ts | 1 - src/space/services/space-link/index.ts | 1 - src/space/services/space-link/space-link.service.ts | 6 ------ src/space/services/tag/index.ts | 1 - src/space/services/tag/tag.service.ts | 8 -------- src/space/space.module.ts | 2 -- 13 files changed, 39 deletions(-) delete mode 100644 libs/common/src/modules/space/entities/space-link.entity.ts delete mode 100644 src/space/services/space-link/index.ts delete mode 100644 src/space/services/space-link/space-link.service.ts delete mode 100644 src/space/services/tag/index.ts delete mode 100644 src/space/services/tag/tag.service.ts diff --git a/libs/common/src/modules/space/entities/space-link.entity.ts b/libs/common/src/modules/space/entities/space-link.entity.ts deleted file mode 100644 index 7223591..0000000 --- a/libs/common/src/modules/space/entities/space-link.entity.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { AbstractEntity } from '../../abstract/entities/abstract.entity'; - -export class SpaceLinkEntity extends AbstractEntity {} diff --git a/libs/common/src/modules/space/repositories/space.repository.ts b/libs/common/src/modules/space/repositories/space.repository.ts index 36cc548..6b83e08 100644 --- a/libs/common/src/modules/space/repositories/space.repository.ts +++ b/libs/common/src/modules/space/repositories/space.repository.ts @@ -11,9 +11,6 @@ export class SpaceRepository extends Repository { } } -@Injectable() -export class SpaceLinkRepository {} - @Injectable() export class InviteSpaceRepository extends Repository { constructor(private dataSource: DataSource) { diff --git a/src/community/community.module.ts b/src/community/community.module.ts index e41f78d..d7a7b74 100644 --- a/src/community/community.module.ts +++ b/src/community/community.module.ts @@ -5,7 +5,6 @@ import { ConfigModule } from '@nestjs/config'; import { SpaceRepositoryModule } from '@app/common/modules/space/space.repository.module'; import { InviteSpaceRepository, - SpaceLinkRepository, SpaceProductAllocationRepository, SpaceRepository, } from '@app/common/modules/space/repositories'; @@ -16,14 +15,12 @@ import { CommunityRepository } from '@app/common/modules/community/repositories' import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service'; import { ProjectRepository } from '@app/common/modules/project/repositiories'; import { - SpaceLinkService, SpaceService, SubspaceDeviceService, SubSpaceService, ValidationService, } from 'src/space/services'; import { TagService as NewTagService } from 'src/tags/services'; -import { TagService } from 'src/space/services/tag'; import { SpaceModelService, SubSpaceModelService, @@ -81,16 +78,13 @@ import { AqiSpaceDailyPollutantStatsRepository } from '@app/common/modules/aqi/r SpaceService, InviteSpaceRepository, // Todo: find out why this is needed - SpaceLinkService, SubSpaceService, ValidationService, NewTagService, SpaceModelService, SpaceProductAllocationService, - SpaceLinkRepository, SubspaceRepository, // Todo: find out why this is needed - TagService, SubspaceDeviceService, SubspaceProductAllocationService, SpaceModelRepository, diff --git a/src/invite-user/invite-user.module.ts b/src/invite-user/invite-user.module.ts index d8655c5..36dbbb5 100644 --- a/src/invite-user/invite-user.module.ts +++ b/src/invite-user/invite-user.module.ts @@ -38,7 +38,6 @@ import { } from '@app/common/modules/scene/repositories'; import { InviteSpaceRepository, - SpaceLinkRepository, SpaceProductAllocationRepository, SpaceRepository, } from '@app/common/modules/space'; @@ -121,7 +120,6 @@ import { AqiSpaceDailyPollutantStatsRepository } from '@app/common/modules/aqi/r NewTagService, SpaceModelService, SpaceProductAllocationService, - SpaceLinkRepository, SubspaceRepository, SubspaceDeviceService, SubspaceProductAllocationService, diff --git a/src/power-clamp/power-clamp.module.ts b/src/power-clamp/power-clamp.module.ts index bb8317b..e0da098 100644 --- a/src/power-clamp/power-clamp.module.ts +++ b/src/power-clamp/power-clamp.module.ts @@ -22,7 +22,6 @@ import { } from '@app/common/modules/scene/repositories'; import { InviteSpaceRepository, - SpaceLinkRepository, SpaceProductAllocationRepository, SpaceRepository, } from '@app/common/modules/space'; @@ -96,7 +95,6 @@ import { AqiSpaceDailyPollutantStatsRepository } from '@app/common/modules/aqi/r SpaceModelService, SpaceProductAllocationService, SqlLoaderService, - SpaceLinkRepository, SubspaceRepository, SubspaceDeviceService, SubspaceProductAllocationService, diff --git a/src/project/project.module.ts b/src/project/project.module.ts index 8860e11..58a9a37 100644 --- a/src/project/project.module.ts +++ b/src/project/project.module.ts @@ -23,7 +23,6 @@ import { } from '@app/common/modules/scene/repositories'; import { InviteSpaceRepository, - SpaceLinkRepository, SpaceProductAllocationRepository, SpaceRepository, } from '@app/common/modules/space'; @@ -94,7 +93,6 @@ const CommandHandlers = [CreateOrphanSpaceHandler]; SpaceModelService, DeviceService, SpaceProductAllocationService, - SpaceLinkRepository, SubspaceRepository, SubspaceDeviceService, SubspaceProductAllocationService, diff --git a/src/space-model/space-model.module.ts b/src/space-model/space-model.module.ts index 1b0a576..383a0db 100644 --- a/src/space-model/space-model.module.ts +++ b/src/space-model/space-model.module.ts @@ -22,7 +22,6 @@ import { } from '@app/common/modules/scene/repositories'; import { InviteSpaceRepository, - SpaceLinkRepository, SpaceProductAllocationRepository, SpaceRepository, } from '@app/common/modules/space'; @@ -93,7 +92,6 @@ const CommandHandlers = [ DeviceRepository, TuyaService, CommunityRepository, - SpaceLinkRepository, InviteSpaceRepository, NewTagService, DeviceService, diff --git a/src/space/services/index.ts b/src/space/services/index.ts index 5f86e3d..a092f83 100644 --- a/src/space/services/index.ts +++ b/src/space/services/index.ts @@ -2,6 +2,5 @@ export * from './space.service'; export * from './space-user.service'; export * from './space-device.service'; export * from './subspace'; -export * from './space-link'; export * from './space-scene.service'; export * from './space-validation.service'; diff --git a/src/space/services/space-link/index.ts b/src/space/services/space-link/index.ts deleted file mode 100644 index ad41b9a..0000000 --- a/src/space/services/space-link/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './space-link.service'; diff --git a/src/space/services/space-link/space-link.service.ts b/src/space/services/space-link/space-link.service.ts deleted file mode 100644 index af84c2b..0000000 --- a/src/space/services/space-link/space-link.service.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Injectable } from '@nestjs/common'; - -// todo: find out why we need to import this -// in community module in order for the whole system to work -@Injectable() -export class SpaceLinkService {} diff --git a/src/space/services/tag/index.ts b/src/space/services/tag/index.ts deleted file mode 100644 index 0cbeec4..0000000 --- a/src/space/services/tag/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './tag.service'; diff --git a/src/space/services/tag/tag.service.ts b/src/space/services/tag/tag.service.ts deleted file mode 100644 index 9de3d0a..0000000 --- a/src/space/services/tag/tag.service.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Injectable } from '@nestjs/common'; - -// todo: find out why we need to import this -// in community module in order for the whole system to work -@Injectable() -export class TagService { - constructor() {} -} diff --git a/src/space/space.module.ts b/src/space/space.module.ts index 288706e..8cdfbc7 100644 --- a/src/space/space.module.ts +++ b/src/space/space.module.ts @@ -37,7 +37,6 @@ import { } from '@app/common/modules/space-model'; import { InviteSpaceRepository, - SpaceLinkRepository, SpaceProductAllocationRepository, SpaceRepository, } from '@app/common/modules/space/repositories'; @@ -116,7 +115,6 @@ export const CommandHandlers = [DisableSpaceHandler]; SubspaceRepository, DeviceRepository, CommunityRepository, - SpaceLinkRepository, UserSpaceRepository, UserRepository, SpaceUserService, From 09322c5b800af438d71c74fd214a27de400c8d2b Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Wed, 9 Jul 2025 14:25:42 +0300 Subject: [PATCH 66/86] add booking points to user table (#461) --- libs/common/src/auth/services/auth.service.ts | 9 +++-- .../src/modules/user/entities/user.entity.ts | 6 ++++ src/auth/services/user-auth.service.ts | 36 ++++++++++--------- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/libs/common/src/auth/services/auth.service.ts b/libs/common/src/auth/services/auth.service.ts index db191a5..d662635 100644 --- a/libs/common/src/auth/services/auth.service.ts +++ b/libs/common/src/auth/services/auth.service.ts @@ -1,5 +1,6 @@ import { PlatformType } from '@app/common/constants/platform-type.enum'; import { RoleType } from '@app/common/constants/role.type.enum'; +import { UserEntity } from '@app/common/modules/user/entities'; import { BadRequestException, Injectable, @@ -32,7 +33,7 @@ export class AuthService { pass: string, regionUuid?: string, platform?: PlatformType, - ): Promise { + ): Promise> { const user = await this.userRepository.findOne({ where: { email, @@ -70,8 +71,9 @@ export class AuthService { } // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { password, ...result } = user; - return result; + // const { password, ...result } = user; + delete user.password; + return user; } async createSession(data): Promise { @@ -114,6 +116,7 @@ export class AuthService { hasAcceptedWebAgreement: user.hasAcceptedWebAgreement, hasAcceptedAppAgreement: user.hasAcceptedAppAgreement, project: user?.project, + bookingPoints: user?.bookingPoints, }; if (payload.googleCode) { const profile = await this.getProfile(payload.googleCode); diff --git a/libs/common/src/modules/user/entities/user.entity.ts b/libs/common/src/modules/user/entities/user.entity.ts index 5bf2fe7..87b5e16 100644 --- a/libs/common/src/modules/user/entities/user.entity.ts +++ b/libs/common/src/modules/user/entities/user.entity.ts @@ -82,6 +82,12 @@ export class UserEntity extends AbstractEntity { }) public isActive: boolean; + @Column({ + nullable: true, + type: Number, + }) + public bookingPoints?: number; + @Column({ default: false }) hasAcceptedWebAgreement: boolean; diff --git a/src/auth/services/user-auth.service.ts b/src/auth/services/user-auth.service.ts index 6a73fcc..8f6f56c 100644 --- a/src/auth/services/user-auth.service.ts +++ b/src/auth/services/user-auth.service.ts @@ -1,25 +1,25 @@ -import { UserRepository } from '../../../libs/common/src/modules/user/repositories'; +import { RoleType } from '@app/common/constants/role.type.enum'; +import { differenceInSeconds } from '@app/common/helper/differenceInSeconds'; import { BadRequestException, ForbiddenException, Injectable, } from '@nestjs/common'; -import { UserSignUpDto } from '../dtos/user-auth.dto'; -import { HelperHashService } from '../../../libs/common/src/helper/services'; -import { UserLoginDto } from '../dtos/user-login.dto'; -import { AuthService } from '../../../libs/common/src/auth/services/auth.service'; -import { UserSessionRepository } from '../../../libs/common/src/modules/session/repositories/session.repository'; -import { UserOtpRepository } from '../../../libs/common/src/modules/user/repositories/user.repository'; -import { ForgetPasswordDto, UserOtpDto, VerifyOtpDto } from '../dtos'; -import { EmailService } from '../../../libs/common/src/util/email.service'; -import { OtpType } from '../../../libs/common/src/constants/otp-type.enum'; -import { UserEntity } from '../../../libs/common/src/modules/user/entities/user.entity'; -import * as argon2 from 'argon2'; -import { differenceInSeconds } from '@app/common/helper/differenceInSeconds'; -import { LessThan, MoreThan } from 'typeorm'; import { ConfigService } from '@nestjs/config'; +import * as argon2 from 'argon2'; import { RoleService } from 'src/role/services'; -import { RoleType } from '@app/common/constants/role.type.enum'; +import { LessThan, MoreThan } from 'typeorm'; +import { AuthService } from '../../../libs/common/src/auth/services/auth.service'; +import { OtpType } from '../../../libs/common/src/constants/otp-type.enum'; +import { HelperHashService } from '../../../libs/common/src/helper/services'; +import { UserSessionRepository } from '../../../libs/common/src/modules/session/repositories/session.repository'; +import { UserEntity } from '../../../libs/common/src/modules/user/entities/user.entity'; +import { UserRepository } from '../../../libs/common/src/modules/user/repositories'; +import { UserOtpRepository } from '../../../libs/common/src/modules/user/repositories/user.repository'; +import { EmailService } from '../../../libs/common/src/util/email.service'; +import { ForgetPasswordDto, UserOtpDto, VerifyOtpDto } from '../dtos'; +import { UserSignUpDto } from '../dtos/user-auth.dto'; +import { UserLoginDto } from '../dtos/user-login.dto'; @Injectable() export class UserAuthService { @@ -108,7 +108,7 @@ export class UserAuthService { async userLogin(data: UserLoginDto) { try { - let user; + let user: Omit; if (data.googleCode) { const googleUserData = await this.authService.login({ googleCode: data.googleCode, @@ -145,7 +145,7 @@ export class UserAuthService { } const session = await Promise.all([ await this.sessionRepository.update( - { userId: user.id }, + { userId: user?.['id'] }, { isLoggedOut: true, }, @@ -166,6 +166,7 @@ export class UserAuthService { hasAcceptedAppAgreement: user.hasAcceptedAppAgreement, project: user.project, sessionId: session[1].uuid, + bookingPoints: user.bookingPoints, }); return res; } catch (error) { @@ -347,6 +348,7 @@ export class UserAuthService { userId: user.uuid, uuid: user.uuid, type, + bookingPoints: user.bookingPoints, sessionId, }); await this.authService.updateRefreshToken(user.uuid, tokens.refreshToken); From 3cfed2b45215241af0eea6c42e23182b8c9457c1 Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Thu, 10 Jul 2025 10:56:27 +0300 Subject: [PATCH 67/86] fix: check if subspaces not exists in update space (#463) --- src/space/dtos/update.space.dto.ts | 5 +-- .../services/subspace/subspace.service.ts | 33 ++++++++++++------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/space/dtos/update.space.dto.ts b/src/space/dtos/update.space.dto.ts index 0491aa1..5f1fbcd 100644 --- a/src/space/dtos/update.space.dto.ts +++ b/src/space/dtos/update.space.dto.ts @@ -50,11 +50,12 @@ export class UpdateSpaceDto { description: 'List of subspace modifications', type: [UpdateSubspaceDto], }) - @ArrayUnique((subspace) => subspace.subspaceName, { + @ArrayUnique((subspace) => subspace.subspaceName ?? subspace.uuid, { message(validationArguments) { const subspaces = validationArguments.value; const nameCounts = subspaces.reduce((acc, curr) => { - acc[curr.subspaceName] = (acc[curr.subspaceName] || 0) + 1; + acc[curr.subspaceName ?? curr.uuid] = + (acc[curr.subspaceName ?? curr.uuid] || 0) + 1; return acc; }, {}); // Find duplicates diff --git a/src/space/services/subspace/subspace.service.ts b/src/space/services/subspace/subspace.service.ts index 15bda14..64d56c9 100644 --- a/src/space/services/subspace/subspace.service.ts +++ b/src/space/services/subspace/subspace.service.ts @@ -342,26 +342,37 @@ export class SubSpaceService { })), ); + const existingSubspaces = await this.subspaceRepository.find({ + where: { + uuid: In( + subspaceDtos.filter((dto) => dto.uuid).map((dto) => dto.uuid), + ), + }, + }); + + if ( + existingSubspaces.length !== + subspaceDtos.filter((dto) => dto.uuid).length + ) { + throw new HttpException( + `Some subspaces with provided UUIDs do not exist in the space.`, + HttpStatus.NOT_FOUND, + ); + } + const updatedSubspaces: SubspaceEntity[] = await queryRunner.manager.save( SubspaceEntity, - [ - ...newSubspaces, - ...subspaceDtos - .filter((dto) => dto.uuid) - .map((dto) => ({ - subspaceName: dto.subspaceName, - space, - })), - ], + newSubspaces, ); + const allSubspaces = [...updatedSubspaces, ...existingSubspaces]; // create or update allocations for the subspaces - if (updatedSubspaces.length > 0) { + if (allSubspaces.length > 0) { await this.subspaceProductAllocationService.updateSubspaceProductAllocationsV2( subspaceDtos.map((dto) => ({ ...dto, uuid: dto.uuid || - updatedSubspaces.find((s) => s.subspaceName === dto.subspaceName) + allSubspaces.find((s) => s.subspaceName === dto.subspaceName) ?.uuid, })), projectUuid, From 009deaf40328e9fdda475fb791f467aa1d78a241 Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Thu, 10 Jul 2025 11:11:30 +0300 Subject: [PATCH 68/86] SP-1812: fix: update bookable space (#467) --- src/booking/controllers/bookable-space.controller.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/booking/controllers/bookable-space.controller.ts b/src/booking/controllers/bookable-space.controller.ts index 65d7b33..876976b 100644 --- a/src/booking/controllers/bookable-space.controller.ts +++ b/src/booking/controllers/bookable-space.controller.ts @@ -7,6 +7,7 @@ import { Controller, Get, Param, + ParseUUIDPipe, Post, Put, Query, @@ -94,7 +95,7 @@ export class BookableSpaceController { .UPDATE_BOOKABLE_SPACES_DESCRIPTION, }) async update( - @Param('spaceUuid') spaceUuid: string, + @Param('spaceUuid', ParseUUIDPipe) spaceUuid: string, @Body() dto: UpdateBookableSpaceDto, ): Promise { const result = await this.bookableSpaceService.update(spaceUuid, dto); From 945328c0ce1269b039d11f587c502a469101ed93 Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Thu, 10 Jul 2025 11:33:55 +0300 Subject: [PATCH 69/86] fix: commission device API (#465) --- src/device/services/device.service.ts | 42 ++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index 02210b7..9b3897c 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -323,7 +323,7 @@ export class DeviceService { async addNewDevice(addDeviceDto: AddDeviceDto, projectUuid: string) { try { - const device = await this.getDeviceDetailsByDeviceIdTuya( + const device = await this.getNewDeviceDetailsFromTuya( addDeviceDto.deviceTuyaUuid, ); @@ -349,6 +349,7 @@ export class DeviceService { spaceDevice: { uuid: addDeviceDto.spaceUuid }, tag: { uuid: addDeviceDto.tagUuid }, name: addDeviceDto.deviceName, + deviceTuyaConstUuid: device.uuid, }); if (deviceSaved.uuid) { const deviceStatus: BaseResponseDto = @@ -752,6 +753,45 @@ export class DeviceService { ); } } + + async getNewDeviceDetailsFromTuya( + deviceId: string, + ): Promise { + console.log('fetching device details from Tuya for deviceId:', deviceId); + try { + const result = await this.tuyaService.getDeviceDetails(deviceId); + + if (!result) { + throw new NotFoundException('Device not found'); + } + // Convert keys to camel case + const camelCaseResponse = convertKeysToCamelCase(result); + + const product = await this.productRepository.findOne({ + where: { prodId: camelCaseResponse.productId }, + }); + + if (!product) { + throw new NotFoundException('Product Type is not supported'); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { productId, id, productName, ...rest } = camelCaseResponse; + + return { + ...rest, + productUuid: product.uuid, + productName: product.name, + } as GetDeviceDetailsInterface; + } catch (error) { + console.log('error', error); + + throw new HttpException( + error.message || 'Error fetching device details from Tuya', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } async getDeviceInstructionByDeviceId( deviceUuid: string, projectUuid: string, From 712b7443acb7251ef1e5983ab6d7cd291f40e12d Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Thu, 10 Jul 2025 11:34:50 +0300 Subject: [PATCH 70/86] fix: prevent conditions overlapping by adding parenthesis to search condition (#464) --- src/booking/services/bookable-space.service.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/booking/services/bookable-space.service.ts b/src/booking/services/bookable-space.service.ts index ae8330b..0379214 100644 --- a/src/booking/services/bookable-space.service.ts +++ b/src/booking/services/bookable-space.service.ts @@ -52,7 +52,7 @@ export class BookableSpaceService { if (search) { qb = qb.andWhere( - 'space.spaceName ILIKE :search OR community.name ILIKE :search OR parentSpace.spaceName ILIKE :search', + '(space.spaceName ILIKE :search OR community.name ILIKE :search OR parentSpace.spaceName ILIKE :search)', { search: `%${search}%` }, ); } @@ -68,7 +68,6 @@ export class BookableSpaceService { .leftJoinAndSelect('space.bookableConfig', 'bookableConfig') .andWhere('bookableConfig.uuid IS NULL'); } - const customModel = TypeORMCustomModel(this.spaceRepository); const { baseResponseDto, paginationResponseDto } = From a17a271213cb9e22219fba6a7b9b611fce040081 Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Thu, 10 Jul 2025 14:41:50 +0300 Subject: [PATCH 71/86] add validation checks (#466) --- src/space/controllers/space.controller.ts | 10 ++++++++-- src/space/services/space.service.ts | 24 ++++++++++++++++++++++- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/space/controllers/space.controller.ts b/src/space/controllers/space.controller.ts index 20660ca..776c222 100644 --- a/src/space/controllers/space.controller.ts +++ b/src/space/controllers/space.controller.ts @@ -1,11 +1,13 @@ import { ControllerRoute } from '@app/common/constants/controller-route'; import { BaseResponseDto } from '@app/common/dto/base.response.dto'; +import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; import { Body, Controller, Delete, Get, Param, + ParseUUIDPipe, Post, Put, Query, @@ -81,9 +83,13 @@ export class SpaceController { async updateSpacesOrder( @Body() orderSpacesDto: OrderSpacesDto, @Param() communitySpaceParam: CommunitySpaceParam, - @Param('parentSpaceUuid') parentSpaceUuid: string, + @Param('parentSpaceUuid', ParseUUIDPipe) parentSpaceUuid: string, ) { - return this.spaceService.updateSpacesOrder(parentSpaceUuid, orderSpacesDto); + await this.spaceService.updateSpacesOrder(parentSpaceUuid, orderSpacesDto); + return new SuccessResponseDto({ + statusCode: 200, + message: 'Spaces order updated successfully', + }); } @ApiBearerAuth() diff --git a/src/space/services/space.service.ts b/src/space/services/space.service.ts index 5ea39cd..126a3ec 100644 --- a/src/space/services/space.service.ts +++ b/src/space/services/space.service.ts @@ -360,6 +360,28 @@ export class SpaceService { parentSpaceUuid: string, { spacesUuids }: OrderSpacesDto, ) { + const parentSpace = await this.spaceRepository.findOne({ + where: { uuid: parentSpaceUuid, disabled: false }, + relations: ['children'], + }); + if (!parentSpace) { + throw new HttpException( + `Parent space with ID ${parentSpaceUuid} not found`, + HttpStatus.NOT_FOUND, + ); + } + // ensure that all sent spaces belong to the parent space + const missingSpaces = spacesUuids.filter( + (uuid) => !parentSpace.children.some((child) => child.uuid === uuid), + ); + if (missingSpaces.length > 0) { + throw new HttpException( + `Some spaces with IDs ${missingSpaces.join( + ', ', + )} do not belong to the parent space with ID ${parentSpaceUuid}`, + HttpStatus.BAD_REQUEST, + ); + } try { await this.spaceRepository.update( { uuid: In(spacesUuids), parent: { uuid: parentSpaceUuid } }, @@ -372,7 +394,7 @@ export class SpaceService { ' END', }, ); - return true; + return; } catch (error) { console.error('Error updating spaces order:', error); throw new HttpException( From a9cb1b670449b5ade935c9e1b71cee1b6cb0c90e Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Thu, 10 Jul 2025 15:41:46 +0300 Subject: [PATCH 72/86] SP-1830: add company name to invite user entity (#468) --- .../Invite-user/dtos/Invite-user.dto.ts | 10 +- .../entities/Invite-user.entity.ts | 5 + src/invite-user/dtos/add.invite-user.dto.ts | 16 +- .../dtos/update.invite-user.dto.ts | 80 +- .../services/invite-user.service.ts | 905 ++++++++++-------- src/project/services/project-user.service.ts | 2 + src/users/services/user-space.service.ts | 35 +- 7 files changed, 543 insertions(+), 510 deletions(-) diff --git a/libs/common/src/modules/Invite-user/dtos/Invite-user.dto.ts b/libs/common/src/modules/Invite-user/dtos/Invite-user.dto.ts index 50e826c..0760206 100644 --- a/libs/common/src/modules/Invite-user/dtos/Invite-user.dto.ts +++ b/libs/common/src/modules/Invite-user/dtos/Invite-user.dto.ts @@ -1,6 +1,6 @@ import { RoleType } from '@app/common/constants/role.type.enum'; import { UserStatusEnum } from '@app/common/constants/user-status.enum'; -import { IsEnum, IsNotEmpty, IsString } from 'class-validator'; +import { IsEnum, IsNotEmpty, IsOptional, IsString } from 'class-validator'; export class InviteUserDto { @IsString() @@ -12,8 +12,12 @@ export class InviteUserDto { public email: string; @IsString() - @IsNotEmpty() - public jobTitle: string; + @IsOptional() + public jobTitle?: string; + + @IsString() + @IsOptional() + public companyName?: string; @IsEnum(UserStatusEnum) @IsNotEmpty() diff --git a/libs/common/src/modules/Invite-user/entities/Invite-user.entity.ts b/libs/common/src/modules/Invite-user/entities/Invite-user.entity.ts index c798a70..07b3151 100644 --- a/libs/common/src/modules/Invite-user/entities/Invite-user.entity.ts +++ b/libs/common/src/modules/Invite-user/entities/Invite-user.entity.ts @@ -37,6 +37,11 @@ export class InviteUserEntity extends AbstractEntity { }) jobTitle: string; + @Column({ + nullable: true, + }) + companyName: string; + @Column({ nullable: false, enum: Object.values(UserStatusEnum), diff --git a/src/invite-user/dtos/add.invite-user.dto.ts b/src/invite-user/dtos/add.invite-user.dto.ts index 0d9acbc..689720c 100644 --- a/src/invite-user/dtos/add.invite-user.dto.ts +++ b/src/invite-user/dtos/add.invite-user.dto.ts @@ -5,6 +5,7 @@ import { IsNotEmpty, IsOptional, IsString, + IsUUID, } from 'class-validator'; export class AddUserInvitationDto { @@ -44,6 +45,15 @@ export class AddUserInvitationDto { @IsOptional() public jobTitle?: string; + @ApiProperty({ + description: 'The company name of the user', + example: 'Tech Corp', + required: false, + }) + @IsString() + @IsOptional() + public companyName?: string; + @ApiProperty({ description: 'The phone number of the user', example: '+1234567890', @@ -58,7 +68,7 @@ export class AddUserInvitationDto { example: 'd290f1ee-6c54-4b01-90e6-d701748f0851', required: true, }) - @IsString() + @IsUUID('4') @IsNotEmpty() public roleUuid: string; @ApiProperty({ @@ -66,15 +76,17 @@ export class AddUserInvitationDto { example: 'd290f1ee-6c54-4b01-90e6-d701748f0851', required: true, }) - @IsString() + @IsUUID('4') @IsNotEmpty() public projectUuid: string; + @ApiProperty({ description: 'The array of space UUIDs (at least one required)', example: ['b5f3c9d2-58b7-4377-b3f7-60acb711d5d9'], required: true, }) @IsArray() + @IsUUID('4', { each: true }) @ArrayMinSize(1) public spaceUuids: string[]; constructor(dto: Partial) { diff --git a/src/invite-user/dtos/update.invite-user.dto.ts b/src/invite-user/dtos/update.invite-user.dto.ts index 6890ed1..fb981cd 100644 --- a/src/invite-user/dtos/update.invite-user.dto.ts +++ b/src/invite-user/dtos/update.invite-user.dto.ts @@ -1,78 +1,10 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { - ArrayMinSize, - IsArray, - IsBoolean, - IsNotEmpty, - IsOptional, - IsString, -} from 'class-validator'; +import { ApiProperty, OmitType } from '@nestjs/swagger'; +import { IsBoolean, IsNotEmpty, IsString } from 'class-validator'; +import { AddUserInvitationDto } from './add.invite-user.dto'; -export class UpdateUserInvitationDto { - @ApiProperty({ - description: 'The first name of the user', - example: 'John', - required: true, - }) - @IsString() - @IsNotEmpty() - public firstName: string; - - @ApiProperty({ - description: 'The last name of the user', - example: 'Doe', - required: true, - }) - @IsString() - @IsNotEmpty() - public lastName: string; - - @ApiProperty({ - description: 'The job title of the user', - example: 'Software Engineer', - required: false, - }) - @IsString() - @IsOptional() - public jobTitle?: string; - - @ApiProperty({ - description: 'The phone number of the user', - example: '+1234567890', - required: false, - }) - @IsString() - @IsOptional() - public phoneNumber?: string; - - @ApiProperty({ - description: 'The role uuid of the user', - example: 'd290f1ee-6c54-4b01-90e6-d701748f0851', - required: true, - }) - @IsString() - @IsNotEmpty() - public roleUuid: string; - @ApiProperty({ - description: 'The project uuid of the user', - example: 'd290f1ee-6c54-4b01-90e6-d701748f0851', - required: true, - }) - @IsString() - @IsNotEmpty() - public projectUuid: string; - @ApiProperty({ - description: 'The array of space UUIDs (at least one required)', - example: ['b5f3c9d2-58b7-4377-b3f7-60acb711d5d9'], - required: true, - }) - @IsArray() - @ArrayMinSize(1) - public spaceUuids: string[]; - constructor(dto: Partial) { - Object.assign(this, dto); - } -} +export class UpdateUserInvitationDto extends OmitType(AddUserInvitationDto, [ + 'email', +]) {} export class DisableUserInvitationDto { @ApiProperty({ description: 'The disable status of the user', diff --git a/src/invite-user/services/invite-user.service.ts b/src/invite-user/services/invite-user.service.ts index 4042663..10f1e5e 100644 --- a/src/invite-user/services/invite-user.service.ts +++ b/src/invite-user/services/invite-user.service.ts @@ -8,6 +8,8 @@ import { InviteUserRepository, InviteUserSpaceRepository, } from '@app/common/modules/Invite-user/repositiories'; +import { ProjectEntity } from '@app/common/modules/project/entities'; +import { RoleTypeEntity } from '@app/common/modules/role-type/entities'; import { RoleTypeRepository } from '@app/common/modules/role-type/repositories'; import { SpaceRepository } from '@app/common/modules/space'; import { SpaceEntity } from '@app/common/modules/space/entities/space.entity'; @@ -61,6 +63,7 @@ export class InviteUserService { lastName, email, jobTitle, + companyName, phoneNumber, roleUuid, spaceUuids, @@ -90,6 +93,8 @@ export class InviteUserService { ); } + await this.checkRole(roleUuid, queryRunner); + await this.checkProject(projectUuid, queryRunner); // Validate spaces const validSpaces = await this.validateSpaces( spaceUuids, @@ -102,6 +107,7 @@ export class InviteUserService { lastName, email, jobTitle, + companyName, phoneNumber, roleType: { uuid: roleUuid }, status: UserStatusEnum.INVITED, @@ -157,185 +163,6 @@ export class InviteUserService { await queryRunner.release(); } } - private async validateSpaces( - spaceUuids: string[], - entityManager: EntityManager, - ): Promise { - const spaceRepo = entityManager.getRepository(SpaceEntity); - - const validSpaces = await spaceRepo.find({ - where: { uuid: In(spaceUuids) }, - }); - - if (validSpaces.length !== spaceUuids.length) { - const validSpaceUuids = validSpaces.map((space) => space.uuid); - const invalidSpaceUuids = spaceUuids.filter( - (uuid) => !validSpaceUuids.includes(uuid), - ); - throw new HttpException( - `Invalid space UUIDs: ${invalidSpaceUuids.join(', ')}`, - HttpStatus.BAD_REQUEST, - ); - } - - return validSpaces; - } - - async checkEmailAndProject(dto: CheckEmailDto): Promise { - const { email } = dto; - - try { - const user = await this.userRepository.findOne({ - where: { email }, - relations: ['project'], - }); - this.validateUserOrInvite(user, 'User'); - const invitedUser = await this.inviteUserRepository.findOne({ - where: { email }, - relations: ['project'], - }); - this.validateUserOrInvite(invitedUser, 'Invited User'); - - return new SuccessResponseDto({ - statusCode: HttpStatus.OK, - success: true, - message: 'Valid email', - }); - } catch (error) { - console.error('Error checking email and project:', error); - throw new HttpException( - error.message || - 'An unexpected error occurred while checking the email', - error.status || HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - - private validateUserOrInvite(user: any, userType: string): void { - if (user) { - if (!user.isActive) { - throw new HttpException( - `${userType} is deleted`, - HttpStatus.BAD_REQUEST, - ); - } - if (user.project) { - throw new HttpException( - `${userType} already has a project`, - HttpStatus.BAD_REQUEST, - ); - } - } - } - - async activationCode(dto: ActivateCodeDto): Promise { - const { activationCode, userUuid } = dto; - - try { - const user = await this.getUser(userUuid); - - const invitedUser = await this.inviteUserRepository.findOne({ - where: { - email: user.email, - status: UserStatusEnum.INVITED, - isActive: true, - }, - relations: ['project', 'spaces.space.community', 'roleType'], - }); - - if (invitedUser) { - if (invitedUser.invitationCode !== activationCode) { - throw new HttpException( - 'Invalid activation code', - HttpStatus.BAD_REQUEST, - ); - } - - // Handle invited user with valid activation code - await this.handleInvitedUser(user, invitedUser); - } else { - // Handle case for non-invited user - await this.handleNonInvitedUser(activationCode, userUuid); - } - return new SuccessResponseDto({ - statusCode: HttpStatus.OK, - success: true, - message: 'The code has been successfully activated', - }); - } catch (error) { - console.error('Error activating the code:', error); - throw new HttpException( - error.message || - 'An unexpected error occurred while activating the code', - error.status || HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - - private async getUser(userUuid: string): Promise { - const user = await this.userRepository.findOne({ - where: { uuid: userUuid, isActive: true, isUserVerified: true }, - }); - - if (!user) { - throw new HttpException('User not found', HttpStatus.NOT_FOUND); - } - return user; - } - - private async handleNonInvitedUser( - activationCode: string, - userUuid: string, - ): Promise { - await this.userSpaceService.verifyCodeAndAddUserSpace( - { inviteCode: activationCode }, - userUuid, - ); - } - - private async handleInvitedUser( - user: UserEntity, - invitedUser: InviteUserEntity, - ): Promise { - for (const invitedSpace of invitedUser.spaces) { - try { - const deviceUUIDs = await this.userSpaceService.getDeviceUUIDsForSpace( - invitedSpace.space.uuid, - ); - - await this.userSpaceService.addUserPermissionsToDevices( - user.uuid, - deviceUUIDs, - ); - - await this.spaceUserService.associateUserToSpace({ - communityUuid: invitedSpace.space.community.uuid, - spaceUuid: invitedSpace.space.uuid, - userUuid: user.uuid, - projectUuid: invitedUser.project.uuid, - }); - } catch (spaceError) { - console.error( - `Error processing space ${invitedSpace.space.uuid}:`, - spaceError, - ); - continue; // Skip to the next space - } - } - - // Update invited user and associated user data - await this.inviteUserRepository.update( - { uuid: invitedUser.uuid }, - { status: UserStatusEnum.ACTIVE, user: { uuid: user.uuid } }, - ); - await this.userRepository.update( - { uuid: user.uuid }, - { - project: { uuid: invitedUser.project.uuid }, - roleType: { uuid: invitedUser.roleType.uuid }, - }, - ); - } async updateUserInvitation( dto: UpdateUserInvitationDto, @@ -357,6 +184,9 @@ export class InviteUserService { throw new HttpException('User not found', HttpStatus.NOT_FOUND); } + await this.checkRole(dto.roleUuid, queryRunner); + await this.checkProject(projectUuid, queryRunner); + // Perform update actions if status is 'INVITED' if (userOldData.status === UserStatusEnum.INVITED) { await this.updateWhenUserIsInvite(queryRunner, dto, invitedUserUuid); @@ -439,174 +269,7 @@ export class InviteUserService { await queryRunner.release(); } } - private async getRoleTypeByUuid(roleUuid: string) { - const role = await this.roleTypeRepository.findOne({ - where: { uuid: roleUuid }, - }); - return role.type; - } - - private async updateWhenUserIsInvite( - queryRunner: QueryRunner, - dto: UpdateUserInvitationDto, - invitedUserUuid: string, - ): Promise { - const { firstName, lastName, jobTitle, phoneNumber, roleUuid, spaceUuids } = - dto; - - // Update user invitation details - await queryRunner.manager.update( - this.inviteUserRepository.target, - { uuid: invitedUserUuid }, - { - firstName, - lastName, - jobTitle, - phoneNumber, - roleType: { uuid: roleUuid }, - }, - ); - - // Remove old space associations - await queryRunner.manager.delete(this.inviteUserSpaceRepository.target, { - inviteUser: { uuid: invitedUserUuid }, - }); - - // Save new space associations - const spaceData = spaceUuids.map((spaceUuid) => ({ - inviteUser: { uuid: invitedUserUuid }, - space: { uuid: spaceUuid }, - })); - await queryRunner.manager.save( - this.inviteUserSpaceRepository.target, - spaceData, - ); - } - private async updateWhenUserIsActive( - queryRunner: QueryRunner, - dto: UpdateUserInvitationDto, - invitedUserUuid: string, - ): Promise { - const { - firstName, - lastName, - jobTitle, - phoneNumber, - roleUuid, - spaceUuids, - projectUuid, - } = dto; - - const userData = await this.inviteUserRepository.findOne({ - where: { uuid: invitedUserUuid }, - relations: ['user.userSpaces.space', 'user.userSpaces.space.community'], - }); - - if (!userData) { - throw new HttpException( - `User with invitedUserUuid ${invitedUserUuid} not found`, - HttpStatus.NOT_FOUND, - ); - } - - // Update user details - await queryRunner.manager.update( - this.inviteUserRepository.target, - { uuid: invitedUserUuid }, - { - firstName, - lastName, - jobTitle, - phoneNumber, - roleType: { uuid: roleUuid }, - }, - ); - await this.userRepository.update( - { uuid: userData.user.uuid }, - { - roleType: { uuid: roleUuid }, - }, - ); - // Disassociate the user from all current spaces - const disassociatePromises = userData.user.userSpaces.map((userSpace) => - this.spaceUserService - .disassociateUserFromSpace({ - communityUuid: userSpace.space.community.uuid, - spaceUuid: userSpace.space.uuid, - userUuid: userData.user.uuid, - projectUuid, - }) - .catch((error) => { - console.error( - `Failed to disassociate user from space ${userSpace.space.uuid}:`, - error, - ); - throw error; - }), - ); - - await Promise.allSettled(disassociatePromises); - - // Process new spaces - const associatePromises = spaceUuids.map(async (spaceUuid) => { - try { - // Fetch space details - const spaceDetails = await this.getSpaceByUuid(spaceUuid); - - // Fetch device UUIDs for the space - const deviceUUIDs = - await this.userSpaceService.getDeviceUUIDsForSpace(spaceUuid); - - // Grant permissions to the user for all devices in the space - await this.userSpaceService.addUserPermissionsToDevices( - userData.user.uuid, - deviceUUIDs, - ); - - // Associate the user with the new space - await this.spaceUserService.associateUserToSpace({ - communityUuid: spaceDetails.communityUuid, - spaceUuid: spaceUuid, - userUuid: userData.user.uuid, - projectUuid, - }); - } catch (error) { - console.error(`Failed to process space ${spaceUuid}:`, error); - throw error; - } - }); - - await Promise.all(associatePromises); - } - - async getSpaceByUuid(spaceUuid: string) { - try { - const space = await this.spaceRepository.findOne({ - where: { - uuid: spaceUuid, - }, - relations: ['community'], - }); - if (!space) { - throw new BadRequestException(`Invalid space UUID ${spaceUuid}`); - } - return { - uuid: space.uuid, - createdAt: space.createdAt, - updatedAt: space.updatedAt, - name: space.spaceName, - spaceTuyaUuid: space.community.externalId, - communityUuid: space.community.uuid, - }; - } catch (err) { - if (err instanceof BadRequestException) { - throw err; // Re-throw BadRequestException - } else { - throw new HttpException('Space not found', HttpStatus.NOT_FOUND); - } - } - } async disableUserInvitation( dto: DisableUserInvitationDto, invitedUserUuid: string, @@ -686,74 +349,6 @@ export class InviteUserService { } } - private async updateUserStatus( - invitedUserUuid: string, - projectUuid: string, - isEnabled: boolean, - ) { - await this.inviteUserRepository.update( - { uuid: invitedUserUuid, project: { uuid: projectUuid } }, - { isEnabled }, - ); - } - - private async disassociateUserFromSpaces(user: any, projectUuid: string) { - const disassociatePromises = user.userSpaces.map((userSpace) => - this.spaceUserService.disassociateUserFromSpace({ - communityUuid: userSpace.space.community.uuid, - spaceUuid: userSpace.space.uuid, - userUuid: user.uuid, - projectUuid, - }), - ); - - const results = await Promise.allSettled(disassociatePromises); - - results.forEach((result, index) => { - if (result.status === 'rejected') { - console.error( - `Failed to disassociate user from space ${user.userSpaces[index].space.uuid}:`, - result.reason, - ); - } - }); - } - private async associateUserToSpaces( - user: any, - userData: any, - projectUuid: string, - invitedUserUuid: string, - disable: boolean, - ) { - const spaceUuids = userData.spaces.map((space) => space.space.uuid); - - const associatePromises = spaceUuids.map(async (spaceUuid) => { - try { - const spaceDetails = await this.getSpaceByUuid(spaceUuid); - - const deviceUUIDs = - await this.userSpaceService.getDeviceUUIDsForSpace(spaceUuid); - await this.userSpaceService.addUserPermissionsToDevices( - user.uuid, - deviceUUIDs, - ); - - await this.spaceUserService.associateUserToSpace({ - communityUuid: spaceDetails.communityUuid, - spaceUuid, - userUuid: user.uuid, - projectUuid, - }); - - await this.updateUserStatus(invitedUserUuid, projectUuid, disable); - } catch (error) { - console.error(`Failed to associate user to space ${spaceUuid}:`, error); - } - }); - - await Promise.allSettled(associatePromises); - } - async deleteUserInvitation( invitedUserUuid: string, ): Promise { @@ -824,4 +419,486 @@ export class InviteUserService { await queryRunner.release(); } } + + async activationCode(dto: ActivateCodeDto): Promise { + const { activationCode, userUuid } = dto; + + try { + const user = await this.getUser(userUuid); + + const invitedUser = await this.inviteUserRepository.findOne({ + where: { + email: user.email, + status: UserStatusEnum.INVITED, + isActive: true, + }, + relations: ['project', 'spaces.space.community', 'roleType'], + }); + + if (invitedUser) { + if (invitedUser.invitationCode !== activationCode) { + throw new HttpException( + 'Invalid activation code', + HttpStatus.BAD_REQUEST, + ); + } + + // Handle invited user with valid activation code + await this.handleInvitedUser(user, invitedUser); + } else { + // Handle case for non-invited user + await this.handleNonInvitedUser(activationCode, userUuid); + } + return new SuccessResponseDto({ + statusCode: HttpStatus.OK, + success: true, + message: 'The code has been successfully activated', + }); + } catch (error) { + console.error('Error activating the code:', error); + throw new HttpException( + error.message || + 'An unexpected error occurred while activating the code', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async checkEmailAndProject(dto: CheckEmailDto): Promise { + const { email } = dto; + + try { + const user = await this.userRepository.findOne({ + where: { email }, + relations: ['project'], + }); + this.validateUserOrInvite(user, 'User'); + const invitedUser = await this.inviteUserRepository.findOne({ + where: { email }, + relations: ['project'], + }); + this.validateUserOrInvite(invitedUser, 'Invited User'); + + return new SuccessResponseDto({ + statusCode: HttpStatus.OK, + success: true, + message: 'Valid email', + }); + } catch (error) { + console.error('Error checking email and project:', error); + throw new HttpException( + error.message || + 'An unexpected error occurred while checking the email', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + private async getSpaceByUuid(spaceUuid: string) { + try { + const space = await this.spaceRepository.findOne({ + where: { + uuid: spaceUuid, + }, + relations: ['community'], + }); + if (!space) { + throw new BadRequestException(`Invalid space UUID ${spaceUuid}`); + } + return { + uuid: space.uuid, + createdAt: space.createdAt, + updatedAt: space.updatedAt, + name: space.spaceName, + spaceTuyaUuid: space.community.externalId, + communityUuid: space.community.uuid, + }; + } catch (err) { + if (err instanceof BadRequestException) { + throw err; // Re-throw BadRequestException + } else { + throw new HttpException('Space not found', HttpStatus.NOT_FOUND); + } + } + } + + private async validateSpaces( + spaceUuids: string[], + entityManager: EntityManager, + ): Promise { + const spaceRepo = entityManager.getRepository(SpaceEntity); + + const validSpaces = await spaceRepo.find({ + where: { uuid: In(spaceUuids) }, + }); + + if (validSpaces.length !== spaceUuids.length) { + const validSpaceUuids = validSpaces.map((space) => space.uuid); + const invalidSpaceUuids = spaceUuids.filter( + (uuid) => !validSpaceUuids.includes(uuid), + ); + throw new HttpException( + `Invalid space UUIDs: ${invalidSpaceUuids.join(', ')}`, + HttpStatus.BAD_REQUEST, + ); + } + + return validSpaces; + } + + private async checkRole( + roleUuid: string, + queryRunner: QueryRunner, + ): Promise { + try { + const role = await queryRunner.manager.findOne(RoleTypeEntity, { + where: { uuid: roleUuid }, + }); + + if (!role) { + throw new HttpException('Role not found', HttpStatus.NOT_FOUND); + } + + return new SuccessResponseDto({ + statusCode: HttpStatus.OK, + success: true, + message: 'Valid role', + }); + } catch (error) { + console.error('Error checking role:', error); + throw new HttpException( + error.message || 'An unexpected error occurred while checking the role', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + private async checkProject( + projectUuid: string, + queryRunner: QueryRunner, + ): Promise { + try { + const project = await queryRunner.manager.findOne(ProjectEntity, { + where: { uuid: projectUuid }, + }); + + if (!project) { + throw new HttpException('Project not found', HttpStatus.NOT_FOUND); + } + + return new SuccessResponseDto({ + statusCode: HttpStatus.OK, + success: true, + message: 'Valid project', + }); + } catch (error) { + console.error('Error checking project:', error); + throw new HttpException( + error.message || + 'An unexpected error occurred while checking the project', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + private validateUserOrInvite(user: any, userType: string): void { + if (user) { + if (!user.isActive) { + throw new HttpException( + `${userType} is deleted`, + HttpStatus.BAD_REQUEST, + ); + } + if (user.project) { + throw new HttpException( + `${userType} already has a project`, + HttpStatus.BAD_REQUEST, + ); + } + } + } + + private async getUser(userUuid: string): Promise { + const user = await this.userRepository.findOne({ + where: { uuid: userUuid, isActive: true, isUserVerified: true }, + }); + + if (!user) { + throw new HttpException('User not found', HttpStatus.NOT_FOUND); + } + return user; + } + + private async handleNonInvitedUser( + activationCode: string, + userUuid: string, + ): Promise { + await this.userSpaceService.verifyCodeAndAddUserSpace( + { inviteCode: activationCode }, + userUuid, + ); + } + + private async handleInvitedUser( + user: UserEntity, + invitedUser: InviteUserEntity, + ): Promise { + for (const invitedSpace of invitedUser.spaces) { + try { + const deviceUUIDs = await this.userSpaceService.getDeviceUUIDsForSpace( + invitedSpace.space.uuid, + ); + + await this.userSpaceService.addUserPermissionsToDevices( + user.uuid, + deviceUUIDs, + ); + + await this.spaceUserService.associateUserToSpace({ + communityUuid: invitedSpace.space.community.uuid, + spaceUuid: invitedSpace.space.uuid, + userUuid: user.uuid, + projectUuid: invitedUser.project.uuid, + }); + } catch (spaceError) { + console.error( + `Error processing space ${invitedSpace.space.uuid}:`, + spaceError, + ); + continue; // Skip to the next space + } + } + + // Update invited user and associated user data + await this.inviteUserRepository.update( + { uuid: invitedUser.uuid }, + { status: UserStatusEnum.ACTIVE, user: { uuid: user.uuid } }, + ); + await this.userRepository.update( + { uuid: user.uuid }, + { + project: { uuid: invitedUser.project.uuid }, + roleType: { uuid: invitedUser.roleType.uuid }, + }, + ); + } + + private async getRoleTypeByUuid(roleUuid: string) { + const role = await this.roleTypeRepository.findOne({ + where: { uuid: roleUuid }, + }); + + return role.type; + } + + private async updateWhenUserIsInvite( + queryRunner: QueryRunner, + dto: UpdateUserInvitationDto, + invitedUserUuid: string, + ): Promise { + const { + firstName, + lastName, + jobTitle, + companyName, + phoneNumber, + roleUuid, + spaceUuids, + } = dto; + + // Update user invitation details + await queryRunner.manager.update( + this.inviteUserRepository.target, + { uuid: invitedUserUuid }, + { + firstName, + lastName, + jobTitle, + companyName, + phoneNumber, + roleType: { uuid: roleUuid }, + }, + ); + + // Remove old space associations + await queryRunner.manager.delete(this.inviteUserSpaceRepository.target, { + inviteUser: { uuid: invitedUserUuid }, + }); + + // Save new space associations + const spaceData = spaceUuids.map((spaceUuid) => ({ + inviteUser: { uuid: invitedUserUuid }, + space: { uuid: spaceUuid }, + })); + await queryRunner.manager.save( + this.inviteUserSpaceRepository.target, + spaceData, + ); + } + private async updateWhenUserIsActive( + queryRunner: QueryRunner, + dto: UpdateUserInvitationDto, + invitedUserUuid: string, + ): Promise { + const { + firstName, + lastName, + jobTitle, + companyName, + phoneNumber, + roleUuid, + spaceUuids, + projectUuid, + } = dto; + + const userData = await this.inviteUserRepository.findOne({ + where: { uuid: invitedUserUuid }, + relations: ['user.userSpaces.space', 'user.userSpaces.space.community'], + }); + + if (!userData) { + throw new HttpException( + `User with invitedUserUuid ${invitedUserUuid} not found`, + HttpStatus.NOT_FOUND, + ); + } + + // Update user details + await queryRunner.manager.update( + this.inviteUserRepository.target, + { uuid: invitedUserUuid }, + { + firstName, + lastName, + jobTitle, + companyName, + phoneNumber, + roleType: { uuid: roleUuid }, + }, + ); + await this.userRepository.update( + { uuid: userData.user.uuid }, + { + roleType: { uuid: roleUuid }, + }, + ); + // Disassociate the user from all current spaces + const disassociatePromises = userData.user.userSpaces.map((userSpace) => + this.spaceUserService + .disassociateUserFromSpace({ + communityUuid: userSpace.space.community.uuid, + spaceUuid: userSpace.space.uuid, + userUuid: userData.user.uuid, + projectUuid, + }) + .catch((error) => { + console.error( + `Failed to disassociate user from space ${userSpace.space.uuid}:`, + error, + ); + throw error; + }), + ); + + await Promise.allSettled(disassociatePromises); + + // Process new spaces + const associatePromises = spaceUuids.map(async (spaceUuid) => { + try { + // Fetch space details + const spaceDetails = await this.getSpaceByUuid(spaceUuid); + + // Fetch device UUIDs for the space + const deviceUUIDs = + await this.userSpaceService.getDeviceUUIDsForSpace(spaceUuid); + + // Grant permissions to the user for all devices in the space + await this.userSpaceService.addUserPermissionsToDevices( + userData.user.uuid, + deviceUUIDs, + ); + + // Associate the user with the new space + await this.spaceUserService.associateUserToSpace({ + communityUuid: spaceDetails.communityUuid, + spaceUuid: spaceUuid, + userUuid: userData.user.uuid, + projectUuid, + }); + } catch (error) { + console.error(`Failed to process space ${spaceUuid}:`, error); + throw error; + } + }); + + await Promise.all(associatePromises); + } + + private async updateUserStatus( + invitedUserUuid: string, + projectUuid: string, + isEnabled: boolean, + ) { + await this.inviteUserRepository.update( + { uuid: invitedUserUuid, project: { uuid: projectUuid } }, + { isEnabled }, + ); + } + + private async disassociateUserFromSpaces(user: any, projectUuid: string) { + const disassociatePromises = user.userSpaces.map((userSpace) => + this.spaceUserService.disassociateUserFromSpace({ + communityUuid: userSpace.space.community.uuid, + spaceUuid: userSpace.space.uuid, + userUuid: user.uuid, + projectUuid, + }), + ); + + const results = await Promise.allSettled(disassociatePromises); + + results.forEach((result, index) => { + if (result.status === 'rejected') { + console.error( + `Failed to disassociate user from space ${user.userSpaces[index].space.uuid}:`, + result.reason, + ); + } + }); + } + private async associateUserToSpaces( + user: any, + userData: any, + projectUuid: string, + invitedUserUuid: string, + disable: boolean, + ) { + const spaceUuids = userData.spaces.map((space) => space.space.uuid); + + const associatePromises = spaceUuids.map(async (spaceUuid) => { + try { + const spaceDetails = await this.getSpaceByUuid(spaceUuid); + + const deviceUUIDs = + await this.userSpaceService.getDeviceUUIDsForSpace(spaceUuid); + await this.userSpaceService.addUserPermissionsToDevices( + user.uuid, + deviceUUIDs, + ); + + await this.spaceUserService.associateUserToSpace({ + communityUuid: spaceDetails.communityUuid, + spaceUuid, + userUuid: user.uuid, + projectUuid, + }); + + await this.updateUserStatus(invitedUserUuid, projectUuid, disable); + } catch (error) { + console.error(`Failed to associate user to space ${spaceUuid}:`, error); + } + }); + + await Promise.allSettled(associatePromises); + } } diff --git a/src/project/services/project-user.service.ts b/src/project/services/project-user.service.ts index f6a1dcb..3b1d4a2 100644 --- a/src/project/services/project-user.service.ts +++ b/src/project/services/project-user.service.ts @@ -33,6 +33,7 @@ export class ProjectUserService { 'status', 'phoneNumber', 'jobTitle', + 'companyName', 'invitedBy', 'isEnabled', ], @@ -91,6 +92,7 @@ export class ProjectUserService { 'status', 'phoneNumber', 'jobTitle', + 'companyName', 'invitedBy', 'isEnabled', ], diff --git a/src/users/services/user-space.service.ts b/src/users/services/user-space.service.ts index e0a5b79..7b9cbb5 100644 --- a/src/users/services/user-space.service.ts +++ b/src/users/services/user-space.service.ts @@ -1,28 +1,28 @@ +import { CommonErrorCodes } from '@app/common/constants/error-codes.enum'; +import { PermissionType } from '@app/common/constants/permission-type.enum'; +import { RoleType } from '@app/common/constants/role.type.enum'; +import { UserStatusEnum } from '@app/common/constants/user-status.enum'; +import { BaseResponseDto } from '@app/common/dto/base.response.dto'; +import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; +import { + InviteUserRepository, + InviteUserSpaceRepository, +} from '@app/common/modules/Invite-user/repositiories'; +import { InviteSpaceEntity } from '@app/common/modules/space/entities/invite-space.entity'; +import { + InviteSpaceRepository, + SpaceRepository, +} from '@app/common/modules/space/repositories'; +import { UserSpaceRepository } from '@app/common/modules/user/repositories'; import { BadRequestException, HttpException, HttpStatus, Injectable, } from '@nestjs/common'; -import { UserSpaceRepository } from '@app/common/modules/user/repositories'; -import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; -import { BaseResponseDto } from '@app/common/dto/base.response.dto'; -import { AddUserSpaceDto, AddUserSpaceUsingCodeDto } from '../dtos'; -import { - InviteSpaceRepository, - SpaceRepository, -} from '@app/common/modules/space/repositories'; -import { CommonErrorCodes } from '@app/common/constants/error-codes.enum'; import { UserDevicePermissionService } from 'src/user-device-permission/services'; -import { PermissionType } from '@app/common/constants/permission-type.enum'; -import { InviteSpaceEntity } from '@app/common/modules/space/entities/invite-space.entity'; +import { AddUserSpaceDto, AddUserSpaceUsingCodeDto } from '../dtos'; import { UserService } from './user.service'; -import { RoleType } from '@app/common/constants/role.type.enum'; -import { - InviteUserRepository, - InviteUserSpaceRepository, -} from '@app/common/modules/Invite-user/repositiories'; -import { UserStatusEnum } from '@app/common/constants/user-status.enum'; @Injectable() export class UserSpaceService { @@ -154,6 +154,7 @@ export class UserSpaceService { lastName: user.lastName, email: user.email, jobTitle: null, + companyName: null, phoneNumber: null, roleType: { uuid: user.role.uuid }, status: UserStatusEnum.ACTIVE, From e5970c02c1e455d1d3d3ec7582fd7c6a7f04f56c Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Tue, 15 Jul 2025 10:11:36 +0300 Subject: [PATCH 73/86] SP-1757, SP-1758, SP-1809, SP-1810: Feat/implement booking (#469) * fix: commission device API * task: add create booking API * add get All api for dashboard & mobile * add Find APIs for bookings * implement sending email updates on update bookable space * move email interfaces to separate files --- libs/common/src/common.module.ts | 10 +- libs/common/src/config/email.config.ts | 6 + libs/common/src/constants/controller-route.ts | 19 ++ libs/common/src/constants/mail-trap.ts | 3 - libs/common/src/database/database.module.ts | 4 +- .../booking/booking.repository.module.ts | 5 +- .../booking/entities/booking.entity.ts | 44 ++++ .../src/modules/booking/entities/index.ts | 1 - .../repositories/bookable-space.repository.ts | 10 + .../repositories/booking.repository.ts | 8 +- .../src/modules/booking/repositories/index.ts | 1 - .../modules/space/entities/space.entity.ts | 6 +- .../src/modules/user/entities/user.entity.ts | 4 + .../src/util/email/batch-email.interface.ts | 8 + .../src/util/{ => email}/email.service.ts | 224 +++++++++++------- .../src/util/email/single-email.interface.ts | 7 + .../src/util/time-to-12-hours-convetion.ts | 7 + package-lock.json | 52 ++-- package.json | 4 +- src/auth/auth.module.ts | 16 +- src/auth/services/user-auth.service.ts | 2 +- src/booking/booking.module.ts | 24 +- .../controllers/bookable-space.controller.ts | 4 +- src/booking/controllers/booking.controller.ts | 107 +++++++++ src/booking/controllers/index.ts | 1 - src/booking/dtos/booking-request.dto.ts | 14 ++ src/booking/dtos/booking-response.dto.ts | 88 +++++++ src/booking/dtos/create-booking.dto.ts | 35 +++ src/booking/dtos/index.ts | 1 - src/booking/dtos/my-booking-request.dto.ts | 14 ++ .../services/bookable-space.service.ts | 183 +++++++++++++- src/booking/services/booking.service.ts | 215 +++++++++++++++++ src/booking/services/index.ts | 1 - src/invite-user/invite-user.module.ts | 6 +- .../services/invite-user.service.ts | 2 +- .../services/visitor-password.service.ts | 2 +- .../visitor-password.module.ts | 60 ++--- 37 files changed, 1014 insertions(+), 184 deletions(-) delete mode 100644 libs/common/src/constants/mail-trap.ts create mode 100644 libs/common/src/modules/booking/entities/booking.entity.ts delete mode 100644 libs/common/src/modules/booking/entities/index.ts create mode 100644 libs/common/src/modules/booking/repositories/bookable-space.repository.ts delete mode 100644 libs/common/src/modules/booking/repositories/index.ts create mode 100644 libs/common/src/util/email/batch-email.interface.ts rename libs/common/src/util/{ => email}/email.service.ts (60%) create mode 100644 libs/common/src/util/email/single-email.interface.ts create mode 100644 libs/common/src/util/time-to-12-hours-convetion.ts create mode 100644 src/booking/controllers/booking.controller.ts delete mode 100644 src/booking/controllers/index.ts create mode 100644 src/booking/dtos/booking-request.dto.ts create mode 100644 src/booking/dtos/booking-response.dto.ts create mode 100644 src/booking/dtos/create-booking.dto.ts delete mode 100644 src/booking/dtos/index.ts create mode 100644 src/booking/dtos/my-booking-request.dto.ts create mode 100644 src/booking/services/booking.service.ts delete mode 100644 src/booking/services/index.ts diff --git a/libs/common/src/common.module.ts b/libs/common/src/common.module.ts index 91e5507..325df3d 100644 --- a/libs/common/src/common.module.ts +++ b/libs/common/src/common.module.ts @@ -1,12 +1,11 @@ import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { ErrorMessageService } from 'src/error-message/error-message.service'; +import { AuthModule } from './auth/auth.module'; import { CommonService } from './common.service'; +import config from './config'; import { DatabaseModule } from './database/database.module'; import { HelperModule } from './helper/helper.module'; -import { AuthModule } from './auth/auth.module'; -import { ConfigModule } from '@nestjs/config'; -import config from './config'; -import { EmailService } from './util/email.service'; -import { ErrorMessageService } from 'src/error-message/error-message.service'; import { TuyaService } from './integrations/tuya/services/tuya.service'; import { SceneDeviceRepository } from './modules/scene-device/repositories'; import { SpaceRepository } from './modules/space'; @@ -15,6 +14,7 @@ import { SubspaceModelRepository, } from './modules/space-model'; import { SubspaceRepository } from './modules/space/repositories/subspace.repository'; +import { EmailService } from './util/email/email.service'; @Module({ providers: [ CommonService, diff --git a/libs/common/src/config/email.config.ts b/libs/common/src/config/email.config.ts index 57ae480..e83d044 100644 --- a/libs/common/src/config/email.config.ts +++ b/libs/common/src/config/email.config.ts @@ -10,6 +10,8 @@ export default registerAs( SMTP_USER: process.env.SMTP_USER, SMTP_SENDER: process.env.SMTP_SENDER, SMTP_PASSWORD: process.env.SMTP_PASSWORD, + BATCH_EMAIL_API_URL: process.env.BATCH_EMAIL_API_URL, + SEND_EMAIL_API_URL: process.env.SEND_EMAIL_API_URL, MAILTRAP_API_TOKEN: process.env.MAILTRAP_API_TOKEN, MAILTRAP_INVITATION_TEMPLATE_UUID: process.env.MAILTRAP_INVITATION_TEMPLATE_UUID, @@ -21,5 +23,9 @@ export default registerAs( process.env.MAILTRAP_EDIT_USER_TEMPLATE_UUID, MAILTRAP_SEND_OTP_TEMPLATE_UUID: process.env.MAILTRAP_SEND_OTP_TEMPLATE_UUID, + MAILTRAP_SEND_BOOKING_AVAILABILITY_UPDATE_TEMPLATE_UUID: + process.env.MAILTRAP_SEND_BOOKING_AVAILABILITY_UPDATE_TEMPLATE_UUID, + MAILTRAP_SEND_BOOKING_TIMING_UPDATE_TEMPLATE_UUID: + process.env.MAILTRAP_SEND_BOOKING_TIMING_UPDATE_TEMPLATE_UUID, }), ); diff --git a/libs/common/src/constants/controller-route.ts b/libs/common/src/constants/controller-route.ts index af7bda4..a9d4607 100644 --- a/libs/common/src/constants/controller-route.ts +++ b/libs/common/src/constants/controller-route.ts @@ -91,6 +91,25 @@ export class ControllerRoute { 'This endpoint allows you to update existing bookable spaces by providing the required details.'; }; }; + static BOOKING = class { + public static readonly ROUTE = 'bookings'; + static ACTIONS = class { + public static readonly ADD_BOOKING_SUMMARY = 'Add new booking'; + + public static readonly ADD_BOOKING_DESCRIPTION = + 'This endpoint allows you to add new booking by providing the required details.'; + + public static readonly GET_ALL_BOOKINGS_SUMMARY = 'Get all bookings'; + + public static readonly GET_ALL_BOOKINGS_DESCRIPTION = + 'This endpoint retrieves all bookings.'; + + public static readonly GET_MY_BOOKINGS_SUMMARY = 'Get my bookings'; + + public static readonly GET_MY_BOOKINGS_DESCRIPTION = + 'This endpoint retrieves all bookings for the authenticated user.'; + }; + }; static COMMUNITY = class { public static readonly ROUTE = '/projects/:projectUuid/communities'; static ACTIONS = class { diff --git a/libs/common/src/constants/mail-trap.ts b/libs/common/src/constants/mail-trap.ts deleted file mode 100644 index 6642244..0000000 --- a/libs/common/src/constants/mail-trap.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const SEND_EMAIL_API_URL_PROD = 'https://send.api.mailtrap.io/api/send/'; -export const SEND_EMAIL_API_URL_DEV = - 'https://sandbox.api.mailtrap.io/api/send/2634012'; diff --git a/libs/common/src/database/database.module.ts b/libs/common/src/database/database.module.ts index 3038c35..6aab302 100644 --- a/libs/common/src/database/database.module.ts +++ b/libs/common/src/database/database.module.ts @@ -14,6 +14,8 @@ import { createLogger } from 'winston'; import { winstonLoggerOptions } from '../logger/services/winston.logger'; import { AqiSpaceDailyPollutantStatsEntity } from '../modules/aqi/entities'; import { AutomationEntity } from '../modules/automation/entities'; +import { BookableSpaceEntity } from '../modules/booking/entities/bookable-space.entity'; +import { BookingEntity } from '../modules/booking/entities/booking.entity'; import { ClientEntity } from '../modules/client/entities'; import { CommunityEntity } from '../modules/community/entities'; import { DeviceStatusLogEntity } from '../modules/device-status-log/entities'; @@ -58,7 +60,6 @@ import { UserSpaceEntity, } from '../modules/user/entities'; import { VisitorPasswordEntity } from '../modules/visitor-password/entities'; -import { BookableSpaceEntity } from '../modules/booking/entities'; @Module({ imports: [ TypeOrmModule.forRootAsync({ @@ -119,6 +120,7 @@ import { BookableSpaceEntity } from '../modules/booking/entities'; AqiSpaceDailyPollutantStatsEntity, SpaceDailyOccupancyDurationEntity, BookableSpaceEntity, + BookingEntity, ], namingStrategy: new SnakeNamingStrategy(), synchronize: Boolean(JSON.parse(configService.get('DB_SYNC'))), diff --git a/libs/common/src/modules/booking/booking.repository.module.ts b/libs/common/src/modules/booking/booking.repository.module.ts index fb5601b..7cf3ed6 100644 --- a/libs/common/src/modules/booking/booking.repository.module.ts +++ b/libs/common/src/modules/booking/booking.repository.module.ts @@ -1,11 +1,12 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { BookableSpaceEntity } from './entities/bookable-space.entity'; +import { BookingEntity } from './entities/booking.entity'; @Module({ providers: [], exports: [], controllers: [], - imports: [TypeOrmModule.forFeature([BookableSpaceEntity])], + imports: [TypeOrmModule.forFeature([BookableSpaceEntity, BookingEntity])], }) -export class BookableRepositoryModule {} +export class BookingRepositoryModule {} diff --git a/libs/common/src/modules/booking/entities/booking.entity.ts b/libs/common/src/modules/booking/entities/booking.entity.ts new file mode 100644 index 0000000..2c0e88d --- /dev/null +++ b/libs/common/src/modules/booking/entities/booking.entity.ts @@ -0,0 +1,44 @@ +import { + Column, + CreateDateColumn, + Entity, + ManyToOne, + UpdateDateColumn, +} from 'typeorm'; +import { AbstractEntity } from '../../abstract/entities/abstract.entity'; +import { SpaceEntity } from '../../space/entities/space.entity'; +import { UserEntity } from '../../user/entities'; + +@Entity('booking') +export class BookingEntity extends AbstractEntity { + @Column({ + type: 'uuid', + default: () => 'gen_random_uuid()', + nullable: false, + }) + public uuid: string; + + @ManyToOne(() => SpaceEntity, (space) => space.bookableConfig) + space: SpaceEntity; + + @ManyToOne(() => UserEntity, (user) => user.bookings) + user: UserEntity; + + @Column({ type: Date, nullable: false }) + date: Date; + + @Column({ type: 'time' }) + startTime: string; + + @Column({ type: 'time' }) + endTime: string; + + @Column({ type: 'int', default: null }) + cost?: number; + + @CreateDateColumn() + createdAt: Date; + + @UpdateDateColumn() + updatedAt: Date; +} diff --git a/libs/common/src/modules/booking/entities/index.ts b/libs/common/src/modules/booking/entities/index.ts deleted file mode 100644 index 7a921ce..0000000 --- a/libs/common/src/modules/booking/entities/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './bookable-space.entity'; diff --git a/libs/common/src/modules/booking/repositories/bookable-space.repository.ts b/libs/common/src/modules/booking/repositories/bookable-space.repository.ts new file mode 100644 index 0000000..87a1c49 --- /dev/null +++ b/libs/common/src/modules/booking/repositories/bookable-space.repository.ts @@ -0,0 +1,10 @@ +import { DataSource, Repository } from 'typeorm'; +import { Injectable } from '@nestjs/common'; +import { BookableSpaceEntity } from '../entities/bookable-space.entity'; + +@Injectable() +export class BookableSpaceEntityRepository extends Repository { + constructor(private dataSource: DataSource) { + super(BookableSpaceEntity, dataSource.createEntityManager()); + } +} diff --git a/libs/common/src/modules/booking/repositories/booking.repository.ts b/libs/common/src/modules/booking/repositories/booking.repository.ts index 87a1c49..9b6af87 100644 --- a/libs/common/src/modules/booking/repositories/booking.repository.ts +++ b/libs/common/src/modules/booking/repositories/booking.repository.ts @@ -1,10 +1,10 @@ -import { DataSource, Repository } from 'typeorm'; import { Injectable } from '@nestjs/common'; -import { BookableSpaceEntity } from '../entities/bookable-space.entity'; +import { DataSource, Repository } from 'typeorm'; +import { BookingEntity } from '../entities/booking.entity'; @Injectable() -export class BookableSpaceEntityRepository extends Repository { +export class BookingEntityRepository extends Repository { constructor(private dataSource: DataSource) { - super(BookableSpaceEntity, dataSource.createEntityManager()); + super(BookingEntity, dataSource.createEntityManager()); } } diff --git a/libs/common/src/modules/booking/repositories/index.ts b/libs/common/src/modules/booking/repositories/index.ts deleted file mode 100644 index ab4b40c..0000000 --- a/libs/common/src/modules/booking/repositories/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './booking.repository'; diff --git a/libs/common/src/modules/space/entities/space.entity.ts b/libs/common/src/modules/space/entities/space.entity.ts index 6a04865..f895de9 100644 --- a/libs/common/src/modules/space/entities/space.entity.ts +++ b/libs/common/src/modules/space/entities/space.entity.ts @@ -8,7 +8,7 @@ import { } from 'typeorm'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { AqiSpaceDailyPollutantStatsEntity } from '../../aqi/entities'; -import { BookableSpaceEntity } from '../../booking/entities'; +import { BookableSpaceEntity } from '../../booking/entities/bookable-space.entity'; import { CommunityEntity } from '../../community/entities'; import { DeviceEntity } from '../../device/entities'; import { InviteUserSpaceEntity } from '../../Invite-user/entities'; @@ -20,6 +20,7 @@ import { UserSpaceEntity } from '../../user/entities'; import { SpaceDto } from '../dtos'; import { SpaceProductAllocationEntity } from './space-product-allocation.entity'; import { SubspaceEntity } from './subspace/subspace.entity'; +import { BookingEntity } from '../../booking/entities/booking.entity'; @Entity({ name: 'space' }) export class SpaceEntity extends AbstractEntity { @@ -132,6 +133,9 @@ export class SpaceEntity extends AbstractEntity { @OneToOne(() => BookableSpaceEntity, (bookable) => bookable.space) bookableConfig: BookableSpaceEntity; + @OneToMany(() => BookingEntity, (booking) => booking.space) + bookings: BookingEntity[]; + constructor(partial: Partial) { super(); Object.assign(this, partial); diff --git a/libs/common/src/modules/user/entities/user.entity.ts b/libs/common/src/modules/user/entities/user.entity.ts index 87b5e16..fe6aa27 100644 --- a/libs/common/src/modules/user/entities/user.entity.ts +++ b/libs/common/src/modules/user/entities/user.entity.ts @@ -29,6 +29,7 @@ import { UserOtpDto, UserSpaceDto, } from '../dtos'; +import { BookingEntity } from '../../booking/entities/booking.entity'; @Entity({ name: 'user' }) export class UserEntity extends AbstractEntity { @@ -121,6 +122,9 @@ export class UserEntity extends AbstractEntity { ) deviceUserNotification: DeviceNotificationEntity[]; + @OneToMany(() => BookingEntity, (booking) => booking.user) + bookings: BookingEntity[]; + @ManyToOne(() => RegionEntity, (region) => region.users, { nullable: true }) region: RegionEntity; @ManyToOne(() => TimeZoneEntity, (timezone) => timezone.users, { diff --git a/libs/common/src/util/email/batch-email.interface.ts b/libs/common/src/util/email/batch-email.interface.ts new file mode 100644 index 0000000..834acb0 --- /dev/null +++ b/libs/common/src/util/email/batch-email.interface.ts @@ -0,0 +1,8 @@ +export interface BatchEmailData { + base: { from: { email: string }; template_uuid: string }; + requests: Array<{ + to: { email: string }[]; + template_variables: Record; + }>; + isBatch: true; +} diff --git a/libs/common/src/util/email.service.ts b/libs/common/src/util/email/email.service.ts similarity index 60% rename from libs/common/src/util/email.service.ts rename to libs/common/src/util/email/email.service.ts index 0a9b1f2..e72af59 100644 --- a/libs/common/src/util/email.service.ts +++ b/libs/common/src/util/email/email.service.ts @@ -1,15 +1,17 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import axios from 'axios'; -import * as nodemailer from 'nodemailer'; -import { - SEND_EMAIL_API_URL_DEV, - SEND_EMAIL_API_URL_PROD, -} from '../constants/mail-trap'; +import nodemailer from 'nodemailer'; +import Mail from 'nodemailer/lib/mailer'; +import { BatchEmailData } from './batch-email.interface'; +import { SingleEmailData } from './single-email.interface'; @Injectable() export class EmailService { private smtpConfig: any; + private API_TOKEN: string; + private SEND_EMAIL_API_URL: string; + private BATCH_EMAIL_API_URL: string; constructor(private readonly configService: ConfigService) { this.smtpConfig = { @@ -22,6 +24,15 @@ export class EmailService { pass: this.configService.get('email-config.SMTP_PASSWORD'), }, }; + this.API_TOKEN = this.configService.get( + 'email-config.MAILTRAP_API_TOKEN', + ); + this.SEND_EMAIL_API_URL = this.configService.get( + 'email-config.SEND_EMAIL_API_URL', + ); + this.BATCH_EMAIL_API_URL = this.configService.get( + 'email-config.BATCH_EMAIL_API_URL', + ); } async sendEmail( @@ -31,7 +42,7 @@ export class EmailService { ): Promise { const transporter = nodemailer.createTransport(this.smtpConfig); - const mailOptions = { + const mailOptions: Mail.Options = { from: this.smtpConfig.sender, to: email, subject, @@ -44,13 +55,6 @@ export class EmailService { email: string, emailInvitationData: any, ): Promise { - const isProduction = process.env.NODE_ENV === 'production'; - const API_TOKEN = this.configService.get( - 'email-config.MAILTRAP_API_TOKEN', - ); - const API_URL = isProduction - ? SEND_EMAIL_API_URL_PROD - : SEND_EMAIL_API_URL_DEV; const TEMPLATE_UUID = this.configService.get( 'email-config.MAILTRAP_INVITATION_TEMPLATE_UUID', ); @@ -68,21 +72,12 @@ export class EmailService { template_variables: emailInvitationData, }; - try { - await axios.post(API_URL, emailData, { - headers: { - Authorization: `Bearer ${API_TOKEN}`, - 'Content-Type': 'application/json', - }, - }); - } catch (error) { - throw new HttpException( - error.response?.data?.message || - 'Error sending email using Mailtrap template', - error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR, - ); - } + return this.sendEmailWithTemplateV2({ + ...emailData, + isBatch: false, + }); } + async sendEmailWithTemplate({ email, name, @@ -94,14 +89,6 @@ export class EmailService { isEnable: boolean; isDelete: boolean; }): Promise { - const isProduction = process.env.NODE_ENV === 'production'; - const API_TOKEN = this.configService.get( - 'email-config.MAILTRAP_API_TOKEN', - ); - const API_URL = isProduction - ? SEND_EMAIL_API_URL_PROD - : SEND_EMAIL_API_URL_DEV; - // Determine the template UUID based on the arguments const templateUuid = isDelete ? this.configService.get( @@ -128,32 +115,16 @@ export class EmailService { }, }; - try { - await axios.post(API_URL, emailData, { - headers: { - Authorization: `Bearer ${API_TOKEN}`, - 'Content-Type': 'application/json', - }, - }); - } catch (error) { - throw new HttpException( - error.response?.data?.message || - 'Error sending email using Mailtrap template', - error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR, - ); - } + return this.sendEmailWithTemplateV2({ + ...emailData, + isBatch: false, + }); } + async sendEditUserEmailWithTemplate( email: string, emailEditData: any, ): Promise { - const isProduction = process.env.NODE_ENV === 'production'; - const API_TOKEN = this.configService.get( - 'email-config.MAILTRAP_API_TOKEN', - ); - const API_URL = isProduction - ? SEND_EMAIL_API_URL_PROD - : SEND_EMAIL_API_URL_DEV; const TEMPLATE_UUID = this.configService.get( 'email-config.MAILTRAP_EDIT_USER_TEMPLATE_UUID', ); @@ -171,32 +142,15 @@ export class EmailService { template_variables: emailEditData, }; - try { - await axios.post(API_URL, emailData, { - headers: { - Authorization: `Bearer ${API_TOKEN}`, - 'Content-Type': 'application/json', - }, - }); - } catch (error) { - throw new HttpException( - error.response?.data?.message || - 'Error sending email using Mailtrap template', - error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR, - ); - } + return this.sendEmailWithTemplateV2({ + ...emailData, + isBatch: false, + }); } async sendOtpEmailWithTemplate( email: string, emailEditData: any, ): Promise { - const isProduction = process.env.NODE_ENV === 'production'; - const API_TOKEN = this.configService.get( - 'email-config.MAILTRAP_API_TOKEN', - ); - const API_URL = isProduction - ? SEND_EMAIL_API_URL_PROD - : SEND_EMAIL_API_URL_DEV; const TEMPLATE_UUID = this.configService.get( 'email-config.MAILTRAP_SEND_OTP_TEMPLATE_UUID', ); @@ -214,20 +168,84 @@ export class EmailService { template_variables: emailEditData, }; - try { - await axios.post(API_URL, emailData, { - headers: { - Authorization: `Bearer ${API_TOKEN}`, - 'Content-Type': 'application/json', + return this.sendEmailWithTemplateV2({ + ...emailData, + isBatch: false, + }); + } + + async sendUpdateBookingTimingEmailWithTemplate( + emails: { + email: string; + name: string; + bookings: { + date: string; + start_time: string; + end_time: string; + }[]; + }[], + emailVariables: { + space_name: string; + days: string; + start_time: string; + end_time: string; + }, + ): Promise { + const TEMPLATE_UUID = this.configService.get( + 'email-config.MAILTRAP_SEND_BOOKING_TIMING_UPDATE_TEMPLATE_UUID', + ); + + const emailData = { + base: { + from: { + email: this.smtpConfig.sender, }, - }); - } catch (error) { - throw new HttpException( - error.response?.data?.message || - 'Error sending email using Mailtrap template', - error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR, - ); - } + template_uuid: TEMPLATE_UUID, + }, + requests: emails.map(({ email, name, bookings }) => ({ + to: [{ email }], + template_variables: { ...emailVariables, name, bookings }, + })), + }; + + return this.sendEmailWithTemplateV2({ + ...emailData, + isBatch: true, + }); + } + + async sendUpdateBookingAvailabilityEmailWithTemplate( + emails: { email: string; name: string }[], + emailVariables: { + space_name: string; + availability: string; + isAvailable: boolean; + }, + ): Promise { + const TEMPLATE_UUID = this.configService.get( + 'email-config.MAILTRAP_SEND_BOOKING_AVAILABILITY_UPDATE_TEMPLATE_UUID', + ); + + const emailData = { + base: { + from: { + email: this.smtpConfig.sender, + }, + template_uuid: TEMPLATE_UUID, + }, + requests: emails.map(({ email, name }) => ({ + to: [{ email }], + template_variables: { + ...emailVariables, + name, + }, + })), + }; + + return this.sendEmailWithTemplateV2({ + ...emailData, + isBatch: true, + }); } generateUserChangesEmailBody( addedSpaceNames: string[], @@ -264,4 +282,30 @@ export class EmailService { nameChanged, }; } + + private async sendEmailWithTemplateV2({ + isBatch, + ...emailData + }: BatchEmailData | SingleEmailData): Promise { + try { + await axios.post( + isBatch ? this.BATCH_EMAIL_API_URL : this.SEND_EMAIL_API_URL, + { + ...emailData, + }, + { + headers: { + Authorization: `Bearer ${this.API_TOKEN}`, + 'Content-Type': 'application/json', + }, + }, + ); + } catch (error) { + throw new HttpException( + error.response?.data?.message || + 'Error sending email using Mailtrap template', + error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } } diff --git a/libs/common/src/util/email/single-email.interface.ts b/libs/common/src/util/email/single-email.interface.ts new file mode 100644 index 0000000..a7a14e2 --- /dev/null +++ b/libs/common/src/util/email/single-email.interface.ts @@ -0,0 +1,7 @@ +export interface SingleEmailData { + from: { email: string }; + to: { email: string }[]; + template_uuid: string; + template_variables?: Record; + isBatch: false; +} diff --git a/libs/common/src/util/time-to-12-hours-convetion.ts b/libs/common/src/util/time-to-12-hours-convetion.ts new file mode 100644 index 0000000..0f6ab5f --- /dev/null +++ b/libs/common/src/util/time-to-12-hours-convetion.ts @@ -0,0 +1,7 @@ +import { format, parse } from 'date-fns'; + +export function to12HourFormat(timeString: string): string { + timeString = timeString.padEnd(8, ':00'); + const parsedTime = parse(timeString, 'HH:mm:ss', new Date()); + return format(parsedTime, 'hh:mm a'); +} diff --git a/package-lock.json b/package-lock.json index 04c97a0..39a752b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,6 +32,7 @@ "class-validator": "^0.14.1", "crypto-js": "^4.2.0", "csv-parser": "^3.2.0", + "date-fns": "^4.1.0", "express-rate-limit": "^7.1.5", "firebase": "^10.12.5", "google-auth-library": "^9.14.1", @@ -40,7 +41,7 @@ "morgan": "^1.10.0", "nest-winston": "^1.10.2", "node-cache": "^5.1.2", - "nodemailer": "^6.9.10", + "nodemailer": "^7.0.5", "onesignal-node": "^3.4.0", "passport-jwt": "^4.0.1", "pg": "^8.11.3", @@ -60,6 +61,7 @@ "@types/jest": "^29.5.2", "@types/multer": "^1.4.12", "@types/node": "^20.3.1", + "@types/nodemailer": "^6.4.17", "@types/supertest": "^6.0.0", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", @@ -3366,6 +3368,15 @@ "undici-types": "~6.21.0" } }, + "node_modules/@types/nodemailer": { + "version": "6.4.17", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.17.tgz", + "integrity": "sha512-I9CCaIp6DTldEg7vyUTZi8+9Vo0hi1/T8gv3C89yk1rSAAzoKQ8H8ki/jBYJSFoH/BisgLP8tkZMlQ91CIquww==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/qs": { "version": "6.14.0", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", @@ -5115,6 +5126,22 @@ "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" } }, + "node_modules/concurrently/node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, "node_modules/concurrently/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -5360,19 +5387,12 @@ } }, "node_modules/date-fns": { - "version": "2.30.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", - "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.21.0" - }, - "engines": { - "node": ">=0.11" - }, + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", "funding": { - "type": "opencollective", - "url": "https://opencollective.com/date-fns" + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" } }, "node_modules/dayjs": { @@ -9784,9 +9804,9 @@ "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.5", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.5.tgz", + "integrity": "sha512-nsrh2lO3j4GkLLXoeEksAMgAOqxOv6QumNRVQTJwKH4nuiww6iC2y7GyANs9kRAxCexg3+lTWM3PZ91iLlVjfg==", "engines": { "node": ">=6.0.0" } diff --git a/package.json b/package.json index b496f90..f0bc8cc 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "class-validator": "^0.14.1", "crypto-js": "^4.2.0", "csv-parser": "^3.2.0", + "date-fns": "^4.1.0", "express-rate-limit": "^7.1.5", "firebase": "^10.12.5", "google-auth-library": "^9.14.1", @@ -52,7 +53,7 @@ "morgan": "^1.10.0", "nest-winston": "^1.10.2", "node-cache": "^5.1.2", - "nodemailer": "^6.9.10", + "nodemailer": "^7.0.5", "onesignal-node": "^3.4.0", "passport-jwt": "^4.0.1", "pg": "^8.11.3", @@ -72,6 +73,7 @@ "@types/jest": "^29.5.2", "@types/multer": "^1.4.12", "@types/node": "^20.3.1", + "@types/nodemailer": "^6.4.17", "@types/supertest": "^6.0.0", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts index df23572..3fae71d 100644 --- a/src/auth/auth.module.ts +++ b/src/auth/auth.module.ts @@ -1,15 +1,17 @@ +import { AuthService } from '@app/common/auth/services/auth.service'; +import { RoleTypeRepository } from '@app/common/modules/role-type/repositories'; +import { UserSessionRepository } from '@app/common/modules/session/repositories/session.repository'; +import { + UserOtpRepository, + UserRepository, +} from '@app/common/modules/user/repositories'; +import { EmailService } from '@app/common/util/email/email.service'; import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; -import { UserRepository } from '@app/common/modules/user/repositories'; -import { UserSessionRepository } from '@app/common/modules/session/repositories/session.repository'; -import { UserOtpRepository } from '@app/common/modules/user/repositories'; -import { RoleTypeRepository } from '@app/common/modules/role-type/repositories'; +import { JwtService } from '@nestjs/jwt'; import { RoleService } from 'src/role/services'; import { UserAuthController } from './controllers'; import { UserAuthService } from './services'; -import { AuthService } from '@app/common/auth/services/auth.service'; -import { EmailService } from '@app/common/util/email.service'; -import { JwtService } from '@nestjs/jwt'; @Module({ imports: [ConfigModule], diff --git a/src/auth/services/user-auth.service.ts b/src/auth/services/user-auth.service.ts index 8f6f56c..abf8942 100644 --- a/src/auth/services/user-auth.service.ts +++ b/src/auth/services/user-auth.service.ts @@ -16,7 +16,7 @@ import { UserSessionRepository } from '../../../libs/common/src/modules/session/ import { UserEntity } from '../../../libs/common/src/modules/user/entities/user.entity'; import { UserRepository } from '../../../libs/common/src/modules/user/repositories'; import { UserOtpRepository } from '../../../libs/common/src/modules/user/repositories/user.repository'; -import { EmailService } from '../../../libs/common/src/util/email.service'; +import { EmailService } from '../../../libs/common/src/util/email/email.service'; import { ForgetPasswordDto, UserOtpDto, VerifyOtpDto } from '../dtos'; import { UserSignUpDto } from '../dtos/user-auth.dto'; import { UserLoginDto } from '../dtos/user-login.dto'; diff --git a/src/booking/booking.module.ts b/src/booking/booking.module.ts index fc9b2db..490f96b 100644 --- a/src/booking/booking.module.ts +++ b/src/booking/booking.module.ts @@ -1,17 +1,29 @@ -import { Global, Module } from '@nestjs/common'; -import { BookableSpaceController } from './controllers'; -import { BookableSpaceService } from './services'; -import { BookableSpaceEntityRepository } from '@app/common/modules/booking/repositories'; +import { BookingRepositoryModule } from '@app/common/modules/booking/booking.repository.module'; +import { BookableSpaceEntityRepository } from '@app/common/modules/booking/repositories/bookable-space.repository'; +import { BookingEntityRepository } from '@app/common/modules/booking/repositories/booking.repository'; import { SpaceRepository } from '@app/common/modules/space'; +import { UserRepository } from '@app/common/modules/user/repositories'; +import { EmailService } from '@app/common/util/email/email.service'; +import { Global, Module } from '@nestjs/common'; +import { BookableSpaceController } from './controllers/bookable-space.controller'; +import { BookingController } from './controllers/booking.controller'; +import { BookableSpaceService } from './services/bookable-space.service'; +import { BookingService } from './services/booking.service'; @Global() @Module({ - controllers: [BookableSpaceController], + imports: [BookingRepositoryModule], + controllers: [BookableSpaceController, BookingController], providers: [ BookableSpaceService, + BookingService, + EmailService, BookableSpaceEntityRepository, + BookingEntityRepository, + SpaceRepository, + UserRepository, ], - exports: [BookableSpaceService], + exports: [BookableSpaceService, BookingService], }) export class BookingModule {} diff --git a/src/booking/controllers/bookable-space.controller.ts b/src/booking/controllers/bookable-space.controller.ts index 876976b..99452f6 100644 --- a/src/booking/controllers/bookable-space.controller.ts +++ b/src/booking/controllers/bookable-space.controller.ts @@ -19,11 +19,11 @@ import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; import { PageResponse } from '@app/common/dto/pagination.response.dto'; import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; import { plainToInstance } from 'class-transformer'; -import { CreateBookableSpaceDto } from '../dtos'; import { BookableSpaceRequestDto } from '../dtos/bookable-space-request.dto'; import { BookableSpaceResponseDto } from '../dtos/bookable-space-response.dto'; +import { CreateBookableSpaceDto } from '../dtos/create-bookable-space.dto'; import { UpdateBookableSpaceDto } from '../dtos/update-bookable-space.dto'; -import { BookableSpaceService } from '../services'; +import { BookableSpaceService } from '../services/bookable-space.service'; @ApiTags('Booking Module') @Controller({ diff --git a/src/booking/controllers/booking.controller.ts b/src/booking/controllers/booking.controller.ts new file mode 100644 index 0000000..bdbe516 --- /dev/null +++ b/src/booking/controllers/booking.controller.ts @@ -0,0 +1,107 @@ +import { ControllerRoute } from '@app/common/constants/controller-route'; +import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; +import { BaseResponseDto } from '@app/common/dto/base.response.dto'; +import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; +import { + Body, + Controller, + Get, + Post, + Query, + Req, + UseGuards, +} from '@nestjs/common'; +import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; + +import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; +import { plainToInstance } from 'class-transformer'; +import { AdminRoleGuard } from 'src/guards/admin.role.guard'; +import { BookingRequestDto } from '../dtos/booking-request.dto'; +import { BookingResponseDto } from '../dtos/booking-response.dto'; +import { CreateBookingDto } from '../dtos/create-booking.dto'; +import { MyBookingRequestDto } from '../dtos/my-booking-request.dto'; +import { BookingService } from '../services/booking.service'; + +@ApiTags('Booking Module') +@Controller({ + version: EnableDisableStatusEnum.ENABLED, + path: ControllerRoute.BOOKING.ROUTE, +}) +export class BookingController { + constructor(private readonly bookingService: BookingService) {} + + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Post() + @ApiOperation({ + summary: ControllerRoute.BOOKING.ACTIONS.ADD_BOOKING_SUMMARY, + description: ControllerRoute.BOOKING.ACTIONS.ADD_BOOKING_DESCRIPTION, + }) + async create( + @Body() dto: CreateBookingDto, + @Req() req: Request, + ): Promise { + const userUuid = req['user']?.uuid; + if (!userUuid) { + throw new Error('User UUID is required in the request'); + } + const result = await this.bookingService.create(userUuid, dto); + return new SuccessResponseDto({ + data: result, + message: 'Successfully created booking', + }); + } + + @ApiBearerAuth() + @UseGuards(AdminRoleGuard) + @Get() + @ApiOperation({ + summary: ControllerRoute.BOOKING.ACTIONS.GET_ALL_BOOKINGS_SUMMARY, + description: ControllerRoute.BOOKING.ACTIONS.GET_ALL_BOOKINGS_DESCRIPTION, + }) + async findAll( + @Query() query: BookingRequestDto, + @Req() req: Request, + ): Promise { + const project = req['user']?.project?.uuid; + if (!project) { + throw new Error('Project UUID is required in the request'); + } + const result = await this.bookingService.findAll(query, project); + return new SuccessResponseDto({ + data: plainToInstance(BookingResponseDto, result, { + excludeExtraneousValues: true, + }), + message: 'Successfully fetched all bookings', + }); + } + + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Get('my-bookings') + @ApiOperation({ + summary: ControllerRoute.BOOKING.ACTIONS.GET_MY_BOOKINGS_SUMMARY, + description: ControllerRoute.BOOKING.ACTIONS.GET_MY_BOOKINGS_DESCRIPTION, + }) + async findMyBookings( + @Query() query: MyBookingRequestDto, + @Req() req: Request, + ): Promise { + const userUuid = req['user']?.uuid; + const project = req['user']?.project?.uuid; + if (!project) { + throw new Error('Project UUID is required in the request'); + } + const result = await this.bookingService.findMyBookings( + query, + userUuid, + project, + ); + return new SuccessResponseDto({ + data: plainToInstance(BookingResponseDto, result, { + excludeExtraneousValues: true, + }), + message: 'Successfully fetched all bookings', + }); + } +} diff --git a/src/booking/controllers/index.ts b/src/booking/controllers/index.ts deleted file mode 100644 index d239862..0000000 --- a/src/booking/controllers/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './bookable-space.controller'; diff --git a/src/booking/dtos/booking-request.dto.ts b/src/booking/dtos/booking-request.dto.ts new file mode 100644 index 0000000..dd61d14 --- /dev/null +++ b/src/booking/dtos/booking-request.dto.ts @@ -0,0 +1,14 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, Matches } from 'class-validator'; + +export class BookingRequestDto { + @ApiProperty({ + description: 'Month in MM/YYYY format', + example: '07/2025', + }) + @IsNotEmpty() + @Matches(/^(0[1-9]|1[0-2])\/\d{4}$/, { + message: 'Date must be in MM/YYYY format', + }) + month: string; +} diff --git a/src/booking/dtos/booking-response.dto.ts b/src/booking/dtos/booking-response.dto.ts new file mode 100644 index 0000000..a8a0bc7 --- /dev/null +++ b/src/booking/dtos/booking-response.dto.ts @@ -0,0 +1,88 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Expose, Transform, Type } from 'class-transformer'; + +export class BookingUserResponseDto { + @ApiProperty() + @Expose() + uuid: string; + + @ApiProperty() + @Expose() + firstName: string; + + @ApiProperty() + @Expose() + lastName: string; + + @ApiProperty({ + type: String, + nullable: true, + }) + @Expose() + email: string; + + @ApiProperty({ + type: String, + nullable: true, + }) + @Expose() + @Transform(({ obj }) => obj.inviteUser?.companyName || null) + companyName: string; + + @ApiProperty({ + type: String, + nullable: true, + }) + @Expose() + phoneNumber: string; +} + +export class BookingSpaceResponseDto { + @ApiProperty() + @Expose() + uuid: string; + + @ApiProperty() + @Expose() + spaceName: string; +} + +export class BookingResponseDto { + @ApiProperty() + @Expose() + uuid: string; + + @ApiProperty({ + type: Date, + }) + @Expose() + date: Date; + + @ApiProperty() + @Expose() + startTime: string; + + @ApiProperty() + @Expose() + endTime: string; + + @ApiProperty({ + type: Number, + }) + @Expose() + cost: number; + + @ApiProperty({ + type: BookingUserResponseDto, + }) + @Type(() => BookingUserResponseDto) + @Expose() + user: BookingUserResponseDto; + + @ApiProperty({ + type: BookingSpaceResponseDto, + }) + @Type(() => BookingSpaceResponseDto) + @Expose() + space: BookingSpaceResponseDto; +} diff --git a/src/booking/dtos/create-booking.dto.ts b/src/booking/dtos/create-booking.dto.ts new file mode 100644 index 0000000..4f453b2 --- /dev/null +++ b/src/booking/dtos/create-booking.dto.ts @@ -0,0 +1,35 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsDate, IsNotEmpty, IsString, IsUUID, Matches } from 'class-validator'; + +export class CreateBookingDto { + @ApiProperty({ + type: 'string', + example: '4fa85f64-5717-4562-b3fc-2c963f66afa7', + }) + @IsNotEmpty() + @IsUUID('4', { message: 'Invalid space UUID provided' }) + spaceUuid: string; + + @ApiProperty({ + type: Date, + }) + @IsNotEmpty() + @IsDate() + date: Date; + + @ApiProperty({ example: '09:00' }) + @IsString() + @IsNotEmpty({ message: 'Start time cannot be empty' }) + @Matches(/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/, { + message: 'Start time must be in HH:mm format (24-hour)', + }) + startTime: string; + + @ApiProperty({ example: '17:00' }) + @IsString() + @IsNotEmpty({ message: 'End time cannot be empty' }) + @Matches(/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/, { + message: 'End time must be in HH:mm format (24-hour)', + }) + endTime: string; +} diff --git a/src/booking/dtos/index.ts b/src/booking/dtos/index.ts deleted file mode 100644 index c56d66c..0000000 --- a/src/booking/dtos/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './create-bookable-space.dto'; diff --git a/src/booking/dtos/my-booking-request.dto.ts b/src/booking/dtos/my-booking-request.dto.ts new file mode 100644 index 0000000..402aa36 --- /dev/null +++ b/src/booking/dtos/my-booking-request.dto.ts @@ -0,0 +1,14 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsIn, IsOptional } from 'class-validator'; + +export class MyBookingRequestDto { + @ApiProperty({ + description: 'Filter bookings by time period', + example: 'past', + enum: ['past', 'future'], + required: false, + }) + @IsOptional() + @IsIn(['past', 'future']) + when?: 'past' | 'future'; +} diff --git a/src/booking/services/bookable-space.service.ts b/src/booking/services/bookable-space.service.ts index 0379214..c497f83 100644 --- a/src/booking/services/bookable-space.service.ts +++ b/src/booking/services/bookable-space.service.ts @@ -2,24 +2,30 @@ import { BaseResponseDto } from '@app/common/dto/base.response.dto'; import { PageResponseDto } from '@app/common/dto/pagination.response.dto'; import { timeToMinutes } from '@app/common/helper/timeToMinutes'; import { TypeORMCustomModel } from '@app/common/models/typeOrmCustom.model'; -import { BookableSpaceEntityRepository } from '@app/common/modules/booking/repositories'; +import { BookableSpaceEntityRepository } from '@app/common/modules/booking/repositories/bookable-space.repository'; +import { BookingEntityRepository } from '@app/common/modules/booking/repositories/booking.repository'; import { SpaceEntity } from '@app/common/modules/space/entities/space.entity'; import { SpaceRepository } from '@app/common/modules/space/repositories/space.repository'; +import { EmailService } from '@app/common/util/email/email.service'; +import { to12HourFormat } from '@app/common/util/time-to-12-hours-convetion'; import { BadRequestException, ConflictException, Injectable, NotFoundException, } from '@nestjs/common'; -import { In } from 'typeorm'; -import { CreateBookableSpaceDto } from '../dtos'; +import { format } from 'date-fns'; +import { Brackets, In } from 'typeorm'; import { BookableSpaceRequestDto } from '../dtos/bookable-space-request.dto'; +import { CreateBookableSpaceDto } from '../dtos/create-bookable-space.dto'; import { UpdateBookableSpaceDto } from '../dtos/update-bookable-space.dto'; @Injectable() export class BookableSpaceService { constructor( + private readonly emailService: EmailService, private readonly bookableSpaceEntityRepository: BookableSpaceEntityRepository, + private readonly bookingEntityRepository: BookingEntityRepository, private readonly spaceRepository: SpaceRepository, ) {} @@ -84,8 +90,7 @@ export class BookableSpaceService { } /** - * todo: if updating availability, send to the ones who have access to this space - * todo: if updating other fields, just send emails to all users who's bookings might be affected + * Update bookable space configuration */ async update(spaceUuid: string, dto: UpdateBookableSpaceDto) { // fetch spaces exist @@ -102,11 +107,179 @@ export class BookableSpaceService { dto.startTime || space.bookableConfig.startTime, dto.endTime || space.bookableConfig.endTime, ); + if ( + dto.startTime != space.bookableConfig.startTime || + dto.endTime != space.bookableConfig.endTime || + dto.daysAvailable != space.bookableConfig.daysAvailable + ) { + this.handleTimingUpdate( + { + daysAvailable: + dto.daysAvailable || space.bookableConfig.daysAvailable, + startTime: dto.startTime || space.bookableConfig.startTime, + endTime: dto.endTime || space.bookableConfig.endTime, + }, + space, + ); + } } + + if ( + dto.active !== undefined && + dto.active !== space.bookableConfig.active + ) { + this.handleAvailabilityUpdate(dto.active, space); + } + Object.assign(space.bookableConfig, dto); return this.bookableSpaceEntityRepository.save(space.bookableConfig); } + private async handleTimingUpdate( + dto: UpdateBookableSpaceDto, + space: SpaceEntity, + ): Promise { + const affectedUsers = await this.getAffectedBookings(space.uuid); + if (!affectedUsers.length) return; + + const groupedParams = this.groupBookingsByUser(affectedUsers); + + return this.emailService.sendUpdateBookingTimingEmailWithTemplate( + groupedParams, + { + space_name: space.spaceName, + start_time: to12HourFormat(dto.startTime), + end_time: to12HourFormat(dto.endTime), + days: dto.daysAvailable.join(', '), + }, + ); + } + + private async getAffectedBookings(spaceUuid: string) { + const today = new Date(); + const nowTime = format(today, 'HH:mm'); + + const bookingWithDayCte = this.bookingEntityRepository + .createQueryBuilder('b') + .select('b.*') + .addSelect( + ` + CASE EXTRACT(DOW FROM b.date) + WHEN 0 THEN 'Sun' + WHEN 1 THEN 'Mon' + WHEN 2 THEN 'Tue' + WHEN 3 THEN 'Wed' + WHEN 4 THEN 'Thu' + WHEN 5 THEN 'Fri' + WHEN 6 THEN 'Sat' + END::"bookable-space_days_available_enum" + `, + 'booking_day', + ) + .where( + `(DATE(b.date) > :today OR (DATE(b.date) = :today AND b.startTime >= :nowTime))`, + { today, nowTime }, + ) + .andWhere('b.space_uuid = :spaceUuid', { spaceUuid }); + + const query = this.bookableSpaceEntityRepository + .createQueryBuilder('bs') + .distinct(true) + .addCommonTableExpression(bookingWithDayCte, 'booking_with_day') + .select('u.first_name', 'name') + .addSelect('u.email', 'email') + .addSelect('DATE(bwd.date)', 'date') + .addSelect('bwd.start_time', 'start_time') + .addSelect('bwd.end_time', 'end_time') + .from('booking_with_day', 'bwd') + .innerJoin('user', 'u', 'u.uuid = bwd.user_uuid') + .where('bs.space_uuid = :spaceUuid', { spaceUuid }) + .andWhere( + new Brackets((qb) => { + qb.where('NOT (bwd.booking_day = ANY(bs.days_available))') + .orWhere('bwd.start_time < bs.start_time') + .orWhere('bwd.end_time > bs.end_time'); + }), + ); + + return query.getRawMany<{ + name: string; + email: string; + date: string; + start_time: string; + end_time: string; + }>(); + } + + private groupBookingsByUser( + bookings: { + name: string; + email: string; + date: string; + start_time: string; + end_time: string; + }[], + ): { + name: string; + email: string; + bookings: { date: string; start_time: string; end_time: string }[]; + }[] { + const grouped: Record< + string, + { + name: string; + email: string; + bookings: { date: string; start_time: string; end_time: string }[]; + } + > = {}; + + for (const { name, email, date, start_time, end_time } of bookings) { + const formattedDate = format(new Date(date), 'yyyy-MM-dd'); + const formattedStartTime = to12HourFormat(start_time); + const formattedEndTime = to12HourFormat(end_time); + + if (!grouped[email]) { + grouped[email] = { + name, + email, + bookings: [], + }; + } + + grouped[email].bookings.push({ + date: formattedDate, + start_time: formattedStartTime, + end_time: formattedEndTime, + }); + } + + return Object.values(grouped); + } + + private async handleAvailabilityUpdate( + active: boolean, + space: SpaceEntity, + ): Promise { + space = await this.spaceRepository.findOne({ + where: { uuid: space.uuid }, + relations: ['userSpaces', 'userSpaces.user'], + }); + const emails = space.userSpaces.map((userSpace) => ({ + email: userSpace.user.email, + name: userSpace.user.firstName, + })); + if (!emails.length) return Promise.resolve(); + + return this.emailService.sendUpdateBookingAvailabilityEmailWithTemplate( + emails, + { + availability: active ? 'Available' : 'Unavailable', + space_name: space.spaceName, + isAvailable: active, + }, + ); + } + /** * Fetch spaces by UUIDs and throw an error if any are missing */ diff --git a/src/booking/services/booking.service.ts b/src/booking/services/booking.service.ts new file mode 100644 index 0000000..d11b25a --- /dev/null +++ b/src/booking/services/booking.service.ts @@ -0,0 +1,215 @@ +import { DaysEnum } from '@app/common/constants/days.enum'; +import { timeToMinutes } from '@app/common/helper/timeToMinutes'; +import { BookingEntityRepository } from '@app/common/modules/booking/repositories/booking.repository'; +import { SpaceEntity } from '@app/common/modules/space/entities/space.entity'; +import { SpaceRepository } from '@app/common/modules/space/repositories/space.repository'; +import { UserRepository } from '@app/common/modules/user/repositories/user.repository'; +import { + BadRequestException, + ConflictException, + ForbiddenException, + Injectable, + NotFoundException, +} from '@nestjs/common'; +import { format } from 'date-fns'; +import { Between } from 'typeorm/find-options/operator/Between'; +import { BookingRequestDto } from '../dtos/booking-request.dto'; +import { CreateBookingDto } from '../dtos/create-booking.dto'; +import { MyBookingRequestDto } from '../dtos/my-booking-request.dto'; + +@Injectable() +export class BookingService { + constructor( + private readonly bookingEntityRepository: BookingEntityRepository, + private readonly spaceRepository: SpaceRepository, + private readonly userRepository: UserRepository, + ) {} + + async create(userUuid: string, dto: CreateBookingDto) { + console.log(userUuid); + const user = await this.userRepository.findOne({ + where: { uuid: userUuid }, + relations: ['userSpaces', 'userSpaces.space'], + }); + console.log(user.userSpaces); + if (!user.userSpaces.some(({ space }) => space.uuid === dto.spaceUuid)) { + throw new ForbiddenException( + `User does not have permission to book this space: ${dto.spaceUuid}`, + ); + } + // Validate time slots first + this.validateTimeSlot(dto.startTime, dto.endTime); + + // fetch spaces exist + const space = await this.getSpaceConfigurationAndBookings(dto.spaceUuid); + + // Validate booking availability + this.validateBookingAvailability(space, dto); + + // Create and save booking + return this.createBookings(space, userUuid, dto); + } + + async findAll({ month }: BookingRequestDto, project: string) { + const [monthNumber, year] = month.split('/').map(Number); + const fromDate = new Date(year, monthNumber - 1, 1); + const toDate = new Date(year, monthNumber, 0, 23, 59, 59); + return this.bookingEntityRepository.find({ + where: { + space: { community: { project: { uuid: project } } }, + date: Between(fromDate, toDate), + }, + relations: ['space', 'user', 'user.inviteUser'], + order: { date: 'DESC' }, + }); + } + + async findMyBookings( + { when }: MyBookingRequestDto, + userUuid: string, + project: string, + ) { + const now = new Date(); + const nowTime = format(now, 'HH:mm'); + + const query = this.bookingEntityRepository + .createQueryBuilder('booking') + .leftJoinAndSelect('booking.space', 'space') + .innerJoin( + 'space.community', + 'community', + 'community.project = :project', + { project }, + ) + .leftJoinAndSelect('booking.user', 'user') + .where('user.uuid = :userUuid', { userUuid }); + + if (when === 'past') { + query.andWhere( + `(DATE(booking.date) < :today OR (DATE(booking.date) = :today AND booking.startTime < :nowTime))`, + { today: now, nowTime }, + ); + } else if (when === 'future') { + query.andWhere( + `(DATE(booking.date) > :today OR (DATE(booking.date) = :today AND booking.startTime >= :nowTime))`, + { today: now, nowTime }, + ); + } + + query.orderBy({ + 'DATE(booking.date)': 'DESC', + 'booking.startTime': 'DESC', + }); + + return query.getMany(); + } + + /** + * Fetch space by UUID and throw an error if not found or if not configured for booking + */ + private async getSpaceConfigurationAndBookings( + spaceUuid: string, + ): Promise { + const space = await this.spaceRepository.findOne({ + where: { uuid: spaceUuid }, + relations: ['bookableConfig', 'bookings'], + }); + + if (!space) { + throw new NotFoundException(`Space not found: ${spaceUuid}`); + } + if (!space.bookableConfig) { + throw new NotFoundException( + `This space is not configured for booking: ${spaceUuid}`, + ); + } + + return space; + } + + /** + * Ensure the slot start time is before the end time + */ + private validateTimeSlot(startTime: string, endTime: string): void { + const start = timeToMinutes(startTime); + const end = timeToMinutes(endTime); + + if (start >= end) { + throw new BadRequestException( + `End time must be after start time for slot: ${startTime}-${endTime}`, + ); + } + } + + /** + * check if the space is available for booking on the requested day + * and if the requested time slot is within the available hours + */ + private validateBookingAvailability( + space: SpaceEntity, + dto: CreateBookingDto, + ): void { + // Check if the space is available for booking on the requested day + const availableDays = space.bookableConfig?.daysAvailable || []; + const requestedDay = new Date(dto.date).toLocaleDateString('en-US', { + weekday: 'short', + }) as DaysEnum; + + if (!availableDays.includes(requestedDay)) { + const dayFullName = new Date(dto.date).toLocaleDateString('en-US', { + weekday: 'long', + }); + throw new BadRequestException( + `Space is not available for booking on ${dayFullName}s`, + ); + } + + const dtoStartTimeInMinutes = timeToMinutes(dto.startTime); + const dtoEndTimeInMinutes = timeToMinutes(dto.endTime); + + if ( + dtoStartTimeInMinutes < timeToMinutes(space.bookableConfig.startTime) || + dtoEndTimeInMinutes > timeToMinutes(space.bookableConfig.endTime) + ) { + throw new BadRequestException( + `Booking time must be within the available hours for space: ${space.spaceName}`, + ); + } + + const previousBookings = space.bookings.filter( + (booking) => + timeToMinutes(booking.startTime) < dtoEndTimeInMinutes && + timeToMinutes(booking.endTime) > dtoStartTimeInMinutes && + format(new Date(booking.date), 'yyyy-MM-dd') === + format(new Date(dto.date), 'yyyy-MM-dd'), + ); + + if (previousBookings.length > 0) { + // tell the user what time is unavailable + const unavailableTimes = previousBookings.map((booking) => { + return `${booking.startTime}-${booking.endTime}`; + }); + throw new ConflictException( + `Space is already booked during this times: ${unavailableTimes.join(', ')}`, + ); + } + } + /** + * Create bookable space entries after all validations pass + */ + private async createBookings( + space: SpaceEntity, + user: string, + { spaceUuid, date, ...dto }: CreateBookingDto, + ) { + const entry = this.bookingEntityRepository.create({ + space: { uuid: spaceUuid }, + user: { uuid: user }, + ...dto, + date: new Date(date), + cost: space.bookableConfig?.points || null, + }); + + return this.bookingEntityRepository.save(entry); + } +} diff --git a/src/booking/services/index.ts b/src/booking/services/index.ts deleted file mode 100644 index f8a6199..0000000 --- a/src/booking/services/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './bookable-space.service'; diff --git a/src/invite-user/invite-user.module.ts b/src/invite-user/invite-user.module.ts index 36dbbb5..2b528f6 100644 --- a/src/invite-user/invite-user.module.ts +++ b/src/invite-user/invite-user.module.ts @@ -9,6 +9,7 @@ import { OccupancyService } from '@app/common/helper/services/occupancy.service' import { PowerClampService } from '@app/common/helper/services/power.clamp.service'; import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service'; import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service'; +import { AqiSpaceDailyPollutantStatsRepository } from '@app/common/modules/aqi/repositories'; import { AutomationRepository } from '@app/common/modules/automation/repositories'; import { CommunityRepository } from '@app/common/modules/community/repositories'; import { DeviceStatusLogRepository } from '@app/common/modules/device-status-log/repositories'; @@ -27,6 +28,7 @@ import { PowerClampHourlyRepository, PowerClampMonthlyRepository, } from '@app/common/modules/power-clamp/repositories'; +import { PresenceSensorDailySpaceRepository } from '@app/common/modules/presence-sensor/repositories'; import { ProductRepository } from '@app/common/modules/product/repositories'; import { ProjectRepository } from '@app/common/modules/project/repositiories'; import { RegionRepository } from '@app/common/modules/region/repositories'; @@ -57,7 +59,7 @@ import { UserRepository, UserSpaceRepository, } from '@app/common/modules/user/repositories'; -import { EmailService } from '@app/common/util/email.service'; +import { EmailService } from '@app/common/util/email/email.service'; import { CommunityModule } from 'src/community/community.module'; import { CommunityService } from 'src/community/services'; import { DeviceService } from 'src/device/services'; @@ -81,8 +83,6 @@ import { SubspaceProductAllocationService } from 'src/space/services/subspace/su import { TagService as NewTagService } from 'src/tags/services'; import { UserDevicePermissionService } from 'src/user-device-permission/services'; import { UserService, UserSpaceService } from 'src/users/services'; -import { PresenceSensorDailySpaceRepository } from '@app/common/modules/presence-sensor/repositories'; -import { AqiSpaceDailyPollutantStatsRepository } from '@app/common/modules/aqi/repositories'; @Module({ imports: [ConfigModule, InviteUserRepositoryModule, CommunityModule], diff --git a/src/invite-user/services/invite-user.service.ts b/src/invite-user/services/invite-user.service.ts index 10f1e5e..d9b4b66 100644 --- a/src/invite-user/services/invite-user.service.ts +++ b/src/invite-user/services/invite-user.service.ts @@ -15,7 +15,7 @@ import { SpaceRepository } from '@app/common/modules/space'; import { SpaceEntity } from '@app/common/modules/space/entities/space.entity'; import { UserEntity } from '@app/common/modules/user/entities'; import { UserRepository } from '@app/common/modules/user/repositories'; -import { EmailService } from '@app/common/util/email.service'; +import { EmailService } from '@app/common/util/email/email.service'; import { BadRequestException, HttpException, diff --git a/src/vistor-password/services/visitor-password.service.ts b/src/vistor-password/services/visitor-password.service.ts index d7ce937..267c65f 100644 --- a/src/vistor-password/services/visitor-password.service.ts +++ b/src/vistor-password/services/visitor-password.service.ts @@ -28,7 +28,7 @@ import { PasswordType } from '@app/common/constants/password-type.enum'; import { VisitorPasswordEnum } from '@app/common/constants/visitor-password.enum'; import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; import { ProjectRepository } from '@app/common/modules/project/repositiories'; -import { EmailService } from '@app/common/util/email.service'; +import { EmailService } from '@app/common/util/email/email.service'; import { DeviceService } from 'src/device/services'; import { DoorLockService } from 'src/door-lock/services'; import { PasswordEncryptionService } from 'src/door-lock/services/encryption.services'; diff --git a/src/vistor-password/visitor-password.module.ts b/src/vistor-password/visitor-password.module.ts index b1d927c..6599d45 100644 --- a/src/vistor-password/visitor-password.module.ts +++ b/src/vistor-password/visitor-password.module.ts @@ -1,39 +1,39 @@ -import { Module } from '@nestjs/common'; -import { VisitorPasswordService } from './services/visitor-password.service'; -import { VisitorPasswordController } from './controllers/visitor-password.controller'; -import { ConfigModule } from '@nestjs/config'; -import { DeviceRepositoryModule } from '@app/common/modules/device'; -import { DeviceRepository } from '@app/common/modules/device/repositories'; -import { EmailService } from '@app/common/util/email.service'; -import { PasswordEncryptionService } from 'src/door-lock/services/encryption.services'; -import { DoorLockModule } from 'src/door-lock/door.lock.module'; -import { DeviceService } from 'src/device/services'; -import { ProductRepository } from '@app/common/modules/product/repositories'; import { DeviceStatusFirebaseService } from '@app/common/firebase/devices-status/services/devices-status.service'; -import { SpaceRepository } from '@app/common/modules/space/repositories'; -import { VisitorPasswordRepository } from '@app/common/modules/visitor-password/repositories'; -import { DeviceStatusLogRepository } from '@app/common/modules/device-status-log/repositories'; +import { AqiDataService } from '@app/common/helper/services/aqi.data.service'; +import { OccupancyService } from '@app/common/helper/services/occupancy.service'; +import { PowerClampService } from '@app/common/helper/services/power.clamp.service'; +import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service'; import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service'; -import { SceneService } from 'src/scene/services'; +import { AqiSpaceDailyPollutantStatsRepository } from '@app/common/modules/aqi/repositories'; +import { AutomationRepository } from '@app/common/modules/automation/repositories'; +import { CommunityRepository } from '@app/common/modules/community/repositories'; +import { DeviceRepositoryModule } from '@app/common/modules/device'; +import { DeviceStatusLogRepository } from '@app/common/modules/device-status-log/repositories'; +import { DeviceRepository } from '@app/common/modules/device/repositories'; +import { + PowerClampDailyRepository, + PowerClampHourlyRepository, + PowerClampMonthlyRepository, +} from '@app/common/modules/power-clamp/repositories'; +import { PresenceSensorDailySpaceRepository } from '@app/common/modules/presence-sensor/repositories'; +import { ProductRepository } from '@app/common/modules/product/repositories'; +import { ProjectRepository } from '@app/common/modules/project/repositiories'; +import { SceneDeviceRepository } from '@app/common/modules/scene-device/repositories'; import { SceneIconRepository, SceneRepository, } from '@app/common/modules/scene/repositories'; -import { SceneDeviceRepository } from '@app/common/modules/scene-device/repositories'; -import { AutomationRepository } from '@app/common/modules/automation/repositories'; -import { ProjectRepository } from '@app/common/modules/project/repositiories'; -import { PowerClampService } from '@app/common/helper/services/power.clamp.service'; -import { - PowerClampHourlyRepository, - PowerClampDailyRepository, - PowerClampMonthlyRepository, -} from '@app/common/modules/power-clamp/repositories'; -import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service'; -import { OccupancyService } from '@app/common/helper/services/occupancy.service'; -import { CommunityRepository } from '@app/common/modules/community/repositories'; -import { AqiDataService } from '@app/common/helper/services/aqi.data.service'; -import { PresenceSensorDailySpaceRepository } from '@app/common/modules/presence-sensor/repositories'; -import { AqiSpaceDailyPollutantStatsRepository } from '@app/common/modules/aqi/repositories'; +import { SpaceRepository } from '@app/common/modules/space/repositories'; +import { VisitorPasswordRepository } from '@app/common/modules/visitor-password/repositories'; +import { EmailService } from '@app/common/util/email/email.service'; +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { DeviceService } from 'src/device/services'; +import { DoorLockModule } from 'src/door-lock/door.lock.module'; +import { PasswordEncryptionService } from 'src/door-lock/services/encryption.services'; +import { SceneService } from 'src/scene/services'; +import { VisitorPasswordController } from './controllers/visitor-password.controller'; +import { VisitorPasswordService } from './services/visitor-password.service'; @Module({ imports: [ConfigModule, DeviceRepositoryModule, DoorLockModule], controllers: [VisitorPasswordController], From 036c8bea17e7f67acfb4fd971ff8bd52023ab2ba Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Tue, 15 Jul 2025 13:31:56 +0300 Subject: [PATCH 74/86] task: add space filter to bookings (#471) --- src/booking/dtos/booking-request.dto.ts | 11 ++++++++++- src/booking/services/booking.service.ts | 7 +++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/booking/dtos/booking-request.dto.ts b/src/booking/dtos/booking-request.dto.ts index dd61d14..b76257f 100644 --- a/src/booking/dtos/booking-request.dto.ts +++ b/src/booking/dtos/booking-request.dto.ts @@ -1,5 +1,5 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, Matches } from 'class-validator'; +import { IsNotEmpty, IsOptional, IsUUID, Matches } from 'class-validator'; export class BookingRequestDto { @ApiProperty({ @@ -11,4 +11,13 @@ export class BookingRequestDto { message: 'Date must be in MM/YYYY format', }) month: string; + + @ApiProperty({ + description: 'Space UUID', + example: '550e8400-e29b-41d4-a716-446655440000', + required: false, + }) + @IsOptional() + @IsUUID('4') + space?: string; } diff --git a/src/booking/services/booking.service.ts b/src/booking/services/booking.service.ts index d11b25a..2148300 100644 --- a/src/booking/services/booking.service.ts +++ b/src/booking/services/booking.service.ts @@ -50,13 +50,16 @@ export class BookingService { return this.createBookings(space, userUuid, dto); } - async findAll({ month }: BookingRequestDto, project: string) { + async findAll({ month, space }: BookingRequestDto, project: string) { const [monthNumber, year] = month.split('/').map(Number); const fromDate = new Date(year, monthNumber - 1, 1); const toDate = new Date(year, monthNumber, 0, 23, 59, 59); return this.bookingEntityRepository.find({ where: { - space: { community: { project: { uuid: project } } }, + space: { + community: { project: { uuid: project } }, + uuid: space ? space : undefined, + }, date: Between(fromDate, toDate), }, relations: ['space', 'user', 'user.inviteUser'], From 12579fcd6ea75e061a4ff7d6b1101bf01e6235c0 Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Tue, 15 Jul 2025 16:46:50 +0300 Subject: [PATCH 75/86] fix nodemailer import (#473) --- libs/common/src/util/email/email.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/common/src/util/email/email.service.ts b/libs/common/src/util/email/email.service.ts index e72af59..a7fd95e 100644 --- a/libs/common/src/util/email/email.service.ts +++ b/libs/common/src/util/email/email.service.ts @@ -1,7 +1,7 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import axios from 'axios'; -import nodemailer from 'nodemailer'; +import * as nodemailer from 'nodemailer'; import Mail from 'nodemailer/lib/mailer'; import { BatchEmailData } from './batch-email.interface'; import { SingleEmailData } from './single-email.interface'; From fffa27b6ee44818ebd7de6dce4537422f14e2091 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 15 Jul 2025 18:48:22 -0600 Subject: [PATCH 76/86] Add AI PR Description Action --- .github/workflows/pr-description.yml | 54 ++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 .github/workflows/pr-description.yml diff --git a/.github/workflows/pr-description.yml b/.github/workflows/pr-description.yml new file mode 100644 index 0000000..0106065 --- /dev/null +++ b/.github/workflows/pr-description.yml @@ -0,0 +1,54 @@ +name: 🤖 AI PR Description Generator (with Template) + +on: + pull_request: + types: [opened, edited] + +jobs: + generate-description: + runs-on: ubuntu-latest + + steps: + - name: Checkout Repo + uses: actions/checkout@v4 + + - name: Install GitHub CLI + uses: cli/cli-action@v2 + + - name: Fetch PR Commits + id: fetch_commits + run: | + COMMITS=$(gh pr view ${{ github.event.pull_request.number }} --json commits --jq '.commits[].message' | sed 's/^/- /') + echo "commits<> $GITHUB_ENV + echo "$COMMITS" >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Generate PR Description with OpenAI + id: generate_description + run: | + RESPONSE=$(curl -s https://api.openai.com/v1/chat/completions \ + -H "Authorization: Bearer $OPENAI_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "model": "gpt-4o", + "messages": [{ + "role": "user", + "content": "Given the following commit messages:\n\n'"${commits}"'\n\nFill the following pull request template. Only fill the \"## Description\" section:\n\n\n\n## Jira Ticket\n\n[SP-0000](https://syncrow.atlassian.net/browse/SP-0000)\n\n## Description\n\n\n\n## How to Test\n\n" + }] + }') + + DESCRIPTION=$(echo "$RESPONSE" | jq -r '.choices[0].message.content') + echo "description<> $GITHUB_ENV + echo "$DESCRIPTION" >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + commits: ${{ env.commits }} + + - name: Update PR Body with AI Description + run: | + gh pr edit ${{ github.event.pull_request.number }} --body "${{ env.description }}" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 65d4a56135ace50ada73ca74433df38b4f17e433 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 15 Jul 2025 19:00:23 -0600 Subject: [PATCH 77/86] fix: update GitHub CLI installation method and refine AI PR description prompt --- .github/workflows/pr-description.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr-description.yml b/.github/workflows/pr-description.yml index 0106065..ec52e85 100644 --- a/.github/workflows/pr-description.yml +++ b/.github/workflows/pr-description.yml @@ -13,7 +13,9 @@ jobs: uses: actions/checkout@v4 - name: Install GitHub CLI - uses: cli/cli-action@v2 + run: | + sudo apt-get update + sudo apt-get install gh -y - name: Fetch PR Commits id: fetch_commits @@ -35,7 +37,7 @@ jobs: "model": "gpt-4o", "messages": [{ "role": "user", - "content": "Given the following commit messages:\n\n'"${commits}"'\n\nFill the following pull request template. Only fill the \"## Description\" section:\n\n\n\n## Jira Ticket\n\n[SP-0000](https://syncrow.atlassian.net/browse/SP-0000)\n\n## Description\n\n\n\n## How to Test\n\n" + "content": "Given the following commit messages:\n\n'"${commits}"'\n\nFill only the \"## Description\" section in this pull request template:\n\n\n\n## Jira Ticket\n\n[SP-0000](https://syncrow.atlassian.net/browse/SP-0000)\n\n## Description\n\n\n\n## How to Test\n\n" }] }') From a4095c837bef3b57c518a093d32c5b90678f9d4a Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 15 Jul 2025 23:19:22 -0600 Subject: [PATCH 78/86] fix: rename workflow and update AI description generation method --- .github/workflows/pr-description.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pr-description.yml b/.github/workflows/pr-description.yml index ec52e85..307e117 100644 --- a/.github/workflows/pr-description.yml +++ b/.github/workflows/pr-description.yml @@ -1,4 +1,4 @@ -name: 🤖 AI PR Description Generator (with Template) +name: 🤖 AI PR Description Commenter (Safe Mode) on: pull_request: @@ -37,7 +37,7 @@ jobs: "model": "gpt-4o", "messages": [{ "role": "user", - "content": "Given the following commit messages:\n\n'"${commits}"'\n\nFill only the \"## Description\" section in this pull request template:\n\n\n\n## Jira Ticket\n\n[SP-0000](https://syncrow.atlassian.net/browse/SP-0000)\n\n## Description\n\n\n\n## How to Test\n\n" + "content": "Given the following commit messages:\n\n'"${commits}"'\n\nGenerate a clear and professional pull request description." }] }') @@ -49,8 +49,8 @@ jobs: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} commits: ${{ env.commits }} - - name: Update PR Body with AI Description + - name: Post AI Generated Description as Comment run: | - gh pr edit ${{ github.event.pull_request.number }} --body "${{ env.description }}" + gh pr comment ${{ github.event.pull_request.number }} --body "${{ env.description }}" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 8d265c9105636f711f020acb15688dbfdd8c2191 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 15 Jul 2025 23:38:57 -0600 Subject: [PATCH 79/86] fix: update workflow name and change GitHub token secret --- .github/workflows/pr-description.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pr-description.yml b/.github/workflows/pr-description.yml index 307e117..d9146a3 100644 --- a/.github/workflows/pr-description.yml +++ b/.github/workflows/pr-description.yml @@ -1,4 +1,4 @@ -name: 🤖 AI PR Description Commenter (Safe Mode) +name: 🤖 AI PR Description Commenter on: pull_request: @@ -25,7 +25,7 @@ jobs: echo "$COMMITS" >> $GITHUB_ENV echo "EOF" >> $GITHUB_ENV env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ secrets.GH_PERSONAL_TOKEN }} - name: Generate PR Description with OpenAI id: generate_description @@ -53,4 +53,4 @@ jobs: run: | gh pr comment ${{ github.event.pull_request.number }} --body "${{ env.description }}" env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ secrets.GH_PERSONAL_TOKEN }} From 6d2252a4036f2099384e5d96a1c6e171517ff45a Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 15 Jul 2025 23:43:00 -0600 Subject: [PATCH 80/86] fix: add debug information to AI PR Description workflow --- .github/workflows/pr-description.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pr-description.yml b/.github/workflows/pr-description.yml index d9146a3..2de9e86 100644 --- a/.github/workflows/pr-description.yml +++ b/.github/workflows/pr-description.yml @@ -1,4 +1,4 @@ -name: 🤖 AI PR Description Commenter +name: 🤖 AI PR Description Commenter (With Debug) on: pull_request: @@ -42,6 +42,12 @@ jobs: }') DESCRIPTION=$(echo "$RESPONSE" | jq -r '.choices[0].message.content') + + echo "---------- OpenAI Raw Response ----------" + echo "$RESPONSE" + echo "---------- Extracted Description ----------" + echo "$DESCRIPTION" + echo "description<> $GITHUB_ENV echo "$DESCRIPTION" >> $GITHUB_ENV echo "EOF" >> $GITHUB_ENV From f0556813ac10020e40e3d1220417c6675275ccdf Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 15 Jul 2025 23:45:16 -0600 Subject: [PATCH 81/86] fix: update workflow name and enhance commit message handling in AI PR Description --- .github/workflows/pr-description.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/pr-description.yml b/.github/workflows/pr-description.yml index 2de9e86..7460044 100644 --- a/.github/workflows/pr-description.yml +++ b/.github/workflows/pr-description.yml @@ -1,4 +1,4 @@ -name: 🤖 AI PR Description Commenter (With Debug) +name: 🤖 AI PR Description Commenter (Safe JSON with PAT) on: pull_request: @@ -12,10 +12,10 @@ jobs: - name: Checkout Repo uses: actions/checkout@v4 - - name: Install GitHub CLI + - name: Install GitHub CLI and jq run: | sudo apt-get update - sudo apt-get install gh -y + sudo apt-get install gh jq -y - name: Fetch PR Commits id: fetch_commits @@ -27,9 +27,10 @@ jobs: env: GH_TOKEN: ${{ secrets.GH_PERSONAL_TOKEN }} - - name: Generate PR Description with OpenAI - id: generate_description + - name: Generate PR Description with OpenAI (Safe JSON) run: | + ESCAPED_COMMITS=$(printf '%s\n' "${commits}" | jq -R . | jq -s -c 'join("\n")') + RESPONSE=$(curl -s https://api.openai.com/v1/chat/completions \ -H "Authorization: Bearer $OPENAI_API_KEY" \ -H "Content-Type: application/json" \ @@ -37,7 +38,7 @@ jobs: "model": "gpt-4o", "messages": [{ "role": "user", - "content": "Given the following commit messages:\n\n'"${commits}"'\n\nGenerate a clear and professional pull request description." + "content": "Given the following commit messages:\n\n'"${ESCAPED_COMMITS}"'\n\nGenerate a clear and professional pull request description." }] }') From d62e620828fd384738f5dc01d3ba950e9c993f31 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 15 Jul 2025 23:47:45 -0600 Subject: [PATCH 82/86] fix: update workflow name and enhance OpenAI request handling in AI PR Description --- .github/workflows/pr-description.yml | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/.github/workflows/pr-description.yml b/.github/workflows/pr-description.yml index 7460044..e4514b4 100644 --- a/.github/workflows/pr-description.yml +++ b/.github/workflows/pr-description.yml @@ -1,4 +1,4 @@ -name: 🤖 AI PR Description Commenter (Safe JSON with PAT) +name: 🤖 AI PR Description Commenter (100% Safe with jq) on: pull_request: @@ -27,20 +27,21 @@ jobs: env: GH_TOKEN: ${{ secrets.GH_PERSONAL_TOKEN }} - - name: Generate PR Description with OpenAI (Safe JSON) + - name: Generate PR Description with OpenAI (Safe JSON with jq) run: | - ESCAPED_COMMITS=$(printf '%s\n' "${commits}" | jq -R . | jq -s -c 'join("\n")') + REQUEST_BODY=$(jq -n \ + --arg model "gpt-4o" \ + --arg content "Given the following commit messages:\n\n${commits}\n\nGenerate a clear and professional pull request description." \ + '{ + model: $model, + messages: [{ role: "user", content: $content }] + }' + ) RESPONSE=$(curl -s https://api.openai.com/v1/chat/completions \ -H "Authorization: Bearer $OPENAI_API_KEY" \ -H "Content-Type: application/json" \ - -d '{ - "model": "gpt-4o", - "messages": [{ - "role": "user", - "content": "Given the following commit messages:\n\n'"${ESCAPED_COMMITS}"'\n\nGenerate a clear and professional pull request description." - }] - }') + -d "$REQUEST_BODY") DESCRIPTION=$(echo "$RESPONSE" | jq -r '.choices[0].message.content') From dea942f11e100c6debd8a4fa317b876c1e7306ec Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Wed, 16 Jul 2025 00:05:33 -0600 Subject: [PATCH 83/86] fix: update workflow to use HuggingFace for PR description generation --- .github/workflows/pr-description.yml | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/.github/workflows/pr-description.yml b/.github/workflows/pr-description.yml index e4514b4..7b08458 100644 --- a/.github/workflows/pr-description.yml +++ b/.github/workflows/pr-description.yml @@ -1,4 +1,4 @@ -name: 🤖 AI PR Description Commenter (100% Safe with jq) +name: 🤖 AI PR Description with HuggingFace on: pull_request: @@ -27,25 +27,21 @@ jobs: env: GH_TOKEN: ${{ secrets.GH_PERSONAL_TOKEN }} - - name: Generate PR Description with OpenAI (Safe JSON with jq) + - name: Generate PR Description with HuggingFace (DeepSeek) run: | REQUEST_BODY=$(jq -n \ - --arg model "gpt-4o" \ - --arg content "Given the following commit messages:\n\n${commits}\n\nGenerate a clear and professional pull request description." \ - '{ - model: $model, - messages: [{ role: "user", content: $content }] - }' + --arg inputs "Given the following commit messages:\n\n${commits}\n\nGenerate a clear and professional pull request description." \ + '{ inputs: $inputs }' ) - RESPONSE=$(curl -s https://api.openai.com/v1/chat/completions \ - -H "Authorization: Bearer $OPENAI_API_KEY" \ + RESPONSE=$(curl -s https://api-inference.huggingface.co/models/deepseek-ai/deepseek-coder-6.7b-instruct \ + -H "Authorization: Bearer $HUGGINGFACE_API_KEY" \ -H "Content-Type: application/json" \ -d "$REQUEST_BODY") - DESCRIPTION=$(echo "$RESPONSE" | jq -r '.choices[0].message.content') + DESCRIPTION=$(echo "$RESPONSE" | jq -r '.[0].generated_text') - echo "---------- OpenAI Raw Response ----------" + echo "---------- HuggingFace Raw Response ----------" echo "$RESPONSE" echo "---------- Extracted Description ----------" echo "$DESCRIPTION" @@ -54,7 +50,7 @@ jobs: echo "$DESCRIPTION" >> $GITHUB_ENV echo "EOF" >> $GITHUB_ENV env: - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + HUGGINGFACE_API_KEY: ${{ secrets.HUGGINGFACE_API_KEY }} commits: ${{ env.commits }} - name: Post AI Generated Description as Comment From 61348aa351482edeffaa788004b9f75886cb2747 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Wed, 16 Jul 2025 00:08:08 -0600 Subject: [PATCH 84/86] fix: update workflow to use HuggingFace Falcon model for PR description generation --- .github/workflows/pr-description.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pr-description.yml b/.github/workflows/pr-description.yml index 7b08458..b37a658 100644 --- a/.github/workflows/pr-description.yml +++ b/.github/workflows/pr-description.yml @@ -1,4 +1,4 @@ -name: 🤖 AI PR Description with HuggingFace +name: 🤖 AI PR Description with HuggingFace Falcon on: pull_request: @@ -27,22 +27,23 @@ jobs: env: GH_TOKEN: ${{ secrets.GH_PERSONAL_TOKEN }} - - name: Generate PR Description with HuggingFace (DeepSeek) + - name: Generate PR Description with HuggingFace Falcon run: | REQUEST_BODY=$(jq -n \ --arg inputs "Given the following commit messages:\n\n${commits}\n\nGenerate a clear and professional pull request description." \ '{ inputs: $inputs }' ) - RESPONSE=$(curl -s https://api-inference.huggingface.co/models/deepseek-ai/deepseek-coder-6.7b-instruct \ + RESPONSE=$(curl -s https://api-inference.huggingface.co/models/tiiuae/falcon-7b-instruct \ -H "Authorization: Bearer $HUGGINGFACE_API_KEY" \ -H "Content-Type: application/json" \ -d "$REQUEST_BODY") - DESCRIPTION=$(echo "$RESPONSE" | jq -r '.[0].generated_text') - echo "---------- HuggingFace Raw Response ----------" echo "$RESPONSE" + + DESCRIPTION=$(echo "$RESPONSE" | jq -r 'if type=="array" then .[0].generated_text else .error else "Error: Unexpected response" end') + echo "---------- Extracted Description ----------" echo "$DESCRIPTION" From 7e2c3136cf768dec522fb98a7fad575df9907f13 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Wed, 16 Jul 2025 00:28:20 -0600 Subject: [PATCH 85/86] fix: update workflow to use OpenAI for PR description generation --- .github/workflows/pr-description.yml | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/.github/workflows/pr-description.yml b/.github/workflows/pr-description.yml index b37a658..e4514b4 100644 --- a/.github/workflows/pr-description.yml +++ b/.github/workflows/pr-description.yml @@ -1,4 +1,4 @@ -name: 🤖 AI PR Description with HuggingFace Falcon +name: 🤖 AI PR Description Commenter (100% Safe with jq) on: pull_request: @@ -27,23 +27,26 @@ jobs: env: GH_TOKEN: ${{ secrets.GH_PERSONAL_TOKEN }} - - name: Generate PR Description with HuggingFace Falcon + - name: Generate PR Description with OpenAI (Safe JSON with jq) run: | REQUEST_BODY=$(jq -n \ - --arg inputs "Given the following commit messages:\n\n${commits}\n\nGenerate a clear and professional pull request description." \ - '{ inputs: $inputs }' + --arg model "gpt-4o" \ + --arg content "Given the following commit messages:\n\n${commits}\n\nGenerate a clear and professional pull request description." \ + '{ + model: $model, + messages: [{ role: "user", content: $content }] + }' ) - RESPONSE=$(curl -s https://api-inference.huggingface.co/models/tiiuae/falcon-7b-instruct \ - -H "Authorization: Bearer $HUGGINGFACE_API_KEY" \ + RESPONSE=$(curl -s https://api.openai.com/v1/chat/completions \ + -H "Authorization: Bearer $OPENAI_API_KEY" \ -H "Content-Type: application/json" \ -d "$REQUEST_BODY") - echo "---------- HuggingFace Raw Response ----------" + DESCRIPTION=$(echo "$RESPONSE" | jq -r '.choices[0].message.content') + + echo "---------- OpenAI Raw Response ----------" echo "$RESPONSE" - - DESCRIPTION=$(echo "$RESPONSE" | jq -r 'if type=="array" then .[0].generated_text else .error else "Error: Unexpected response" end') - echo "---------- Extracted Description ----------" echo "$DESCRIPTION" @@ -51,7 +54,7 @@ jobs: echo "$DESCRIPTION" >> $GITHUB_ENV echo "EOF" >> $GITHUB_ENV env: - HUGGINGFACE_API_KEY: ${{ secrets.HUGGINGFACE_API_KEY }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} commits: ${{ env.commits }} - name: Post AI Generated Description as Comment From 85687e7950bb4de40852b02d3de64995b410b281 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Wed, 16 Jul 2025 20:05:58 -0600 Subject: [PATCH 86/86] fix: refactor device status logging and improve property handling in batch processing --- .../services/devices-status.service.ts | 89 +++++++++++-------- .../services/tuya.web.socket.service.ts | 17 ++-- 2 files changed, 63 insertions(+), 43 deletions(-) diff --git a/libs/common/src/firebase/devices-status/services/devices-status.service.ts b/libs/common/src/firebase/devices-status/services/devices-status.service.ts index fd40f8b..027526b 100644 --- a/libs/common/src/firebase/devices-status/services/devices-status.service.ts +++ b/libs/common/src/firebase/devices-status/services/devices-status.service.ts @@ -1,15 +1,13 @@ +import { DeviceStatusLogRepository } from '@app/common/modules/device-status-log/repositories'; +import { DeviceRepository } from '@app/common/modules/device/repositories'; import { HttpException, HttpStatus, Injectable, NotFoundException, } from '@nestjs/common'; -import { AddDeviceStatusDto } from '../dtos/add.devices-status.dto'; -import { DeviceRepository } from '@app/common/modules/device/repositories'; -import { GetDeviceDetailsFunctionsStatusInterface } from 'src/device/interfaces/get.device.interface'; -import { TuyaContext } from '@tuya/tuya-connector-nodejs'; import { ConfigService } from '@nestjs/config'; -import { firebaseDataBase } from '../../firebase.config'; +import { TuyaContext } from '@tuya/tuya-connector-nodejs'; import { Database, DataSnapshot, @@ -17,7 +15,9 @@ import { ref, runTransaction, } from 'firebase/database'; -import { DeviceStatusLogRepository } from '@app/common/modules/device-status-log/repositories'; +import { GetDeviceDetailsFunctionsStatusInterface } from 'src/device/interfaces/get.device.interface'; +import { firebaseDataBase } from '../../firebase.config'; +import { AddDeviceStatusDto } from '../dtos/add.devices-status.dto'; @Injectable() export class DeviceStatusFirebaseService { private tuya: TuyaContext; @@ -79,64 +79,77 @@ export class DeviceStatusFirebaseService { device: any; }[], ): Promise { - const allLogs = []; - console.log(`🔁 Preparing logs from batch of ${batch.length} items...`); + const allLogs = []; + for (const item of batch) { const device = item.device; + if (!device?.uuid) { console.log(`⛔ Skipped unknown device: ${item.deviceTuyaUuid}`); continue; } - const logs = item.log.properties.map((property) => + // Determine properties based on environment + const properties = + this.isDevEnv && Array.isArray(item.log?.properties) + ? item.log.properties + : Array.isArray(item.status) + ? item.status + : null; + + if (!properties) { + console.log( + `⛔ Skipped invalid status/properties for device: ${item.deviceTuyaUuid}`, + ); + continue; + } + + const logs = properties.map((property) => this.deviceStatusLogRepository.create({ deviceId: device.uuid, deviceTuyaId: item.deviceTuyaUuid, - productId: item.log.productId, + productId: device.productDevice?.uuid, log: item.log, code: property.code, value: property.value, - eventId: item.log.dataId, - eventTime: new Date(property.time).toISOString(), + eventId: item.log?.dataId, + eventTime: new Date( + this.isDevEnv ? property.time : property.t, + ).toISOString(), }), ); + allLogs.push(...logs); } console.log(`📝 Total logs to insert: ${allLogs.length}`); - const insertLogsPromise = (async () => { - const chunkSize = 300; - let insertedCount = 0; + const chunkSize = 300; + let insertedCount = 0; - for (let i = 0; i < allLogs.length; i += chunkSize) { - const chunk = allLogs.slice(i, i + chunkSize); - try { - const result = await this.deviceStatusLogRepository - .createQueryBuilder() - .insert() - .into('device-status-log') // or use DeviceStatusLogEntity - .values(chunk) - .orIgnore() // skip duplicates - .execute(); + for (let i = 0; i < allLogs.length; i += chunkSize) { + const chunk = allLogs.slice(i, i + chunkSize); + try { + const result = await this.deviceStatusLogRepository + .createQueryBuilder() + .insert() + .into('device-status-log') + .values(chunk) + .orIgnore() + .execute(); - insertedCount += result.identifiers.length; - console.log( - `✅ Inserted ${result.identifiers.length} / ${chunk.length} logs (chunk)`, - ); - } catch (error) { - console.error('❌ Insert error (skipped chunk):', error.message); - } + insertedCount += result.identifiers.length; + console.log( + `✅ Inserted ${result.identifiers.length} / ${chunk.length} logs (chunk)`, + ); + } catch (error) { + console.error('❌ Insert error (skipped chunk):', error.message); } + } - console.log( - `✅ Total logs inserted: ${insertedCount} / ${allLogs.length}`, - ); - })(); - - await insertLogsPromise; + console.log(`✅ Total logs inserted: ${insertedCount} / ${allLogs.length}`); } async addDeviceStatusToFirebase( diff --git a/libs/common/src/helper/services/tuya.web.socket.service.ts b/libs/common/src/helper/services/tuya.web.socket.service.ts index d30200f..1376343 100644 --- a/libs/common/src/helper/services/tuya.web.socket.service.ts +++ b/libs/common/src/helper/services/tuya.web.socket.service.ts @@ -1,9 +1,9 @@ -import { Injectable, OnModuleInit } from '@nestjs/common'; -import TuyaWebsocket from '../../config/tuya-web-socket-config'; -import { ConfigService } from '@nestjs/config'; import { DeviceStatusFirebaseService } from '@app/common/firebase/devices-status/services/devices-status.service'; -import { SosHandlerService } from './sos.handler.service'; +import { Injectable, OnModuleInit } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; import * as NodeCache from 'node-cache'; +import TuyaWebsocket from '../../config/tuya-web-socket-config'; +import { SosHandlerService } from './sos.handler.service'; @Injectable() export class TuyaWebSocketService implements OnModuleInit { @@ -74,7 +74,12 @@ export class TuyaWebSocketService implements OnModuleInit { this.client.message(async (ws: WebSocket, message: any) => { try { const { devId, status, logData } = this.extractMessageData(message); - if (!Array.isArray(logData?.properties)) { + // console.log( + // `📬 Received message for device: ${devId}, status:`, + // status, + // logData, + // ); + if (!Array.isArray(status)) { this.client.ackMessage(message.messageId); return; } @@ -162,6 +167,8 @@ export class TuyaWebSocketService implements OnModuleInit { status: any; logData: any; } { + // console.log('Received message:', message); + const payloadData = message.payload.data; if (this.isDevEnv) {