From 00e68dbbf6860b0efb7eaec075be72bddefe1690 Mon Sep 17 00:00:00 2001 From: Sergey Krylov Date: Sun, 19 Apr 2026 08:54:35 +0300 Subject: [PATCH] feat: add hall and seats services --- prisma/schema.prisma | 37 +++++++ src/app.module.ts | 6 +- src/main.ts | 4 +- .../commands/create-hall.usecase.ts | 32 ++++++ .../application/queries/get-hall.usecase.ts | 23 +++++ .../application/queries/list-halls.usecase.ts | 12 +++ .../hall/domain/entities/hall.entity.ts | 9 ++ .../hall/domain/ports/hall.repository.port.ts | 29 ++++++ src/modules/hall/infra/hall.module.ts | 23 +++++ .../infra/prisma/hall.prisma.repository.ts | 97 +++++++++++++++++++ .../hall/interfaces/grpc/hall.controller.ts | 42 ++++++++ .../application/queries/get-seat.usecase.ts | 28 ++++++ .../application/queries/list-seats.usecase.ts | 19 ++++ .../seat/domain/entities/seat.entity.ts | 10 ++ .../seat/domain/ports/seat.repository.port.ts | 6 ++ .../infra/prisma/seat.prisma.repository.ts | 34 +++++++ src/modules/seat/infra/seat.module.ts | 21 ++++ .../seat/interfaces/grpc/seat.controller.ts | 31 ++++++ .../commands/create-theater.usecase.ts | 0 .../queries/get-theater.usecase.ts | 0 .../queries/list-theater.usecase.ts | 0 src/modules/theater/infra/theater.module.ts | 8 +- .../grpc/theater.grpc.controller.ts | 7 +- yarn.lock | 18 ++-- 24 files changed, 475 insertions(+), 21 deletions(-) create mode 100644 src/modules/hall/application/commands/create-hall.usecase.ts create mode 100644 src/modules/hall/application/queries/get-hall.usecase.ts create mode 100644 src/modules/hall/application/queries/list-halls.usecase.ts create mode 100644 src/modules/hall/domain/entities/hall.entity.ts create mode 100644 src/modules/hall/domain/ports/hall.repository.port.ts create mode 100644 src/modules/hall/infra/hall.module.ts create mode 100644 src/modules/hall/infra/prisma/hall.prisma.repository.ts create mode 100644 src/modules/hall/interfaces/grpc/hall.controller.ts create mode 100644 src/modules/seat/application/queries/get-seat.usecase.ts create mode 100644 src/modules/seat/application/queries/list-seats.usecase.ts create mode 100644 src/modules/seat/domain/entities/seat.entity.ts create mode 100644 src/modules/seat/domain/ports/seat.repository.port.ts create mode 100644 src/modules/seat/infra/prisma/seat.prisma.repository.ts create mode 100644 src/modules/seat/infra/seat.module.ts create mode 100644 src/modules/seat/interfaces/grpc/seat.controller.ts rename src/modules/theater/{applications => application}/commands/create-theater.usecase.ts (100%) rename src/modules/theater/{applications => application}/queries/get-theater.usecase.ts (100%) rename src/modules/theater/{applications => application}/queries/list-theater.usecase.ts (100%) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index f3b607e..dcdb58d 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -19,8 +19,45 @@ model Theater { name String address String + halls Hall[] + createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") @@map("theaters") } + +model Hall { + id String @id @default(nanoid()) + + name String + seats Seat[] + + theater Theater @relation(fields: [theaterId], references: [id], onDelete: Cascade) + theaterId String @map("theater_id") + + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + + @@map("halls") +} + +model Seat { + id String @id @default(nanoid()) + + row Int + number Int + x Int + y Int + type String + price Int + + hall Hall @relation(fields: [hallId], references: [id], onDelete: Cascade) + hallId String @map("hall_id") + + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + + @@unique([hallId, row, number]) + @@map("seats") +} diff --git a/src/app.module.ts b/src/app.module.ts index 066c20b..bd8c6c2 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -2,6 +2,8 @@ import { Module } from '@nestjs/common' import { ConfigModule } from '@nestjs/config' import { PrismaModule } from '@/infra/prisma/prisma.module' +import { HallModule } from '@/modules/hall/infra/hall.module' +import { SeatModule } from '@/modules/seat/infra/seat.module' import { TheaterModule } from './modules/theater/infra/theater.module' @@ -11,7 +13,9 @@ import { TheaterModule } from './modules/theater/infra/theater.module' isGlobal: true }), PrismaModule, - TheaterModule + TheaterModule, + HallModule, + SeatModule ] }) export class AppModule {} diff --git a/src/main.ts b/src/main.ts index c2226b8..a3155d0 100644 --- a/src/main.ts +++ b/src/main.ts @@ -17,8 +17,8 @@ async function bootstrap() { transport: Transport.GRPC, options: { url, - package: ['theater.v1'], - protoPath: [PROTO_PATHS.THEATER], + package: ['theater.v1', 'hall.v1', 'seat.v1'], + protoPath: [PROTO_PATHS.THEATER, PROTO_PATHS.HALL, PROTO_PATHS.SEAT], loader: { keepCase: false, longs: String, diff --git a/src/modules/hall/application/commands/create-hall.usecase.ts b/src/modules/hall/application/commands/create-hall.usecase.ts new file mode 100644 index 0000000..b80791e --- /dev/null +++ b/src/modules/hall/application/commands/create-hall.usecase.ts @@ -0,0 +1,32 @@ +import { Injectable } from '@nestjs/common' + +import { PrismaService } from '@/infra/prisma/prisma.service' + +import { + HallRepositoryPort, + RowLayout +} from '../../domain/ports/hall.repository.port' + +@Injectable() +export class CreateHallUsecase { + constructor( + private readonly repository: HallRepositoryPort, + private readonly prismaService: PrismaService + ) {} + + public async execute(data: { + name: string + theaterId: string + layout: RowLayout[] + }) { + return this.prismaService.$transaction(async () => { + const hall = await this.repository.create(data) + await this.repository.createSeats({ + hallId: hall.id, + layout: data.layout + }) + + return { hall } + }) + } +} diff --git a/src/modules/hall/application/queries/get-hall.usecase.ts b/src/modules/hall/application/queries/get-hall.usecase.ts new file mode 100644 index 0000000..cca872a --- /dev/null +++ b/src/modules/hall/application/queries/get-hall.usecase.ts @@ -0,0 +1,23 @@ +import { Injectable } from '@nestjs/common' +import { RpcException } from '@nestjs/microservices' +import { RpcStatus } from '@teacinema/common' + +import { HallRepositoryPort } from '../../domain/ports/hall.repository.port' + +@Injectable() +export class GetHallUsecase { + public constructor(private readonly repository: HallRepositoryPort) {} + + public async execute(id: string) { + const hall = await this.repository.findById(id) + + if (!hall) { + throw new RpcException({ + code: RpcStatus.NOT_FOUND, + message: 'Hall not found' + }) + } + + return { hall } + } +} diff --git a/src/modules/hall/application/queries/list-halls.usecase.ts b/src/modules/hall/application/queries/list-halls.usecase.ts new file mode 100644 index 0000000..37ab4d5 --- /dev/null +++ b/src/modules/hall/application/queries/list-halls.usecase.ts @@ -0,0 +1,12 @@ +import { Injectable } from '@nestjs/common' + +import { HallRepositoryPort } from '@/modules/hall/domain/ports/hall.repository.port' + +@Injectable() +export class ListHallsUseCase { + public constructor(private readonly repository: HallRepositoryPort) {} + + public execute(theaterId: string) { + return this.repository.listByTheater(theaterId) + } +} diff --git a/src/modules/hall/domain/entities/hall.entity.ts b/src/modules/hall/domain/entities/hall.entity.ts new file mode 100644 index 0000000..ec2f431 --- /dev/null +++ b/src/modules/hall/domain/entities/hall.entity.ts @@ -0,0 +1,9 @@ +export class HallEntity { + public constructor( + public readonly id: string, + public readonly name: string, + public readonly theaterId: string, + public readonly createdAt: Date, + public readonly updatedAt: Date + ) {} +} diff --git a/src/modules/hall/domain/ports/hall.repository.port.ts b/src/modules/hall/domain/ports/hall.repository.port.ts new file mode 100644 index 0000000..45e57e9 --- /dev/null +++ b/src/modules/hall/domain/ports/hall.repository.port.ts @@ -0,0 +1,29 @@ +import { HallEntity } from '../entities/hall.entity' + +export interface RowLayout { + row: number + columns: number + type: string + price: number +} + +export abstract class HallRepositoryPort { + public abstract create(data: { + name: string + theaterId: string + layout: RowLayout[] + }): Promise + + public abstract findById(id: string): Promise + + public abstract listByTheater( + theaterId: string + ): Promise<{ halls: HallEntity[] }> + + public abstract createSeats(data: { + hallId: string + layout: RowLayout[] + }): Promise + + public abstract findSeatsByHall(hallId: string) +} diff --git a/src/modules/hall/infra/hall.module.ts b/src/modules/hall/infra/hall.module.ts new file mode 100644 index 0000000..90c5162 --- /dev/null +++ b/src/modules/hall/infra/hall.module.ts @@ -0,0 +1,23 @@ +import { Module } from '@nestjs/common' + +import { HallRepositoryPort } from '@/modules/hall/domain/ports/hall.repository.port' +import { HallPrismaRepository } from '@/modules/hall/infra/prisma/hall.prisma.repository' +import { HallGrpcController } from '@/modules/hall/interfaces/grpc/hall.controller' + +import { CreateHallUsecase } from '../application/commands/create-hall.usecase' +import { GetHallUsecase } from '../application/queries/get-hall.usecase' +import { ListHallsUseCase } from '../application/queries/list-halls.usecase' + +@Module({ + controllers: [HallGrpcController], + providers: [ + { + provide: HallRepositoryPort, + useClass: HallPrismaRepository + }, + CreateHallUsecase, + GetHallUsecase, + ListHallsUseCase + ] +}) +export class HallModule {} diff --git a/src/modules/hall/infra/prisma/hall.prisma.repository.ts b/src/modules/hall/infra/prisma/hall.prisma.repository.ts new file mode 100644 index 0000000..82ad625 --- /dev/null +++ b/src/modules/hall/infra/prisma/hall.prisma.repository.ts @@ -0,0 +1,97 @@ +import { Injectable } from '@nestjs/common' +import { Hall } from '@prisma/generated/client' +import { SeatCreateManyInput } from '@prisma/generated/models/Seat' + +import { PrismaService } from '@/infra/prisma/prisma.service' +import { HallEntity } from '@/modules/hall/domain/entities/hall.entity' +import { + HallRepositoryPort, + RowLayout +} from '@/modules/hall/domain/ports/hall.repository.port' + +@Injectable() +export class HallPrismaRepository implements HallRepositoryPort { + public constructor(private readonly prismaService: PrismaService) {} + + public async create(data: { + name: string + theaterId: string + layout: RowLayout[] + }): Promise { + const { name, theaterId } = data + const hall = await this.prismaService.hall.create({ + data: { + name, + theater: { + connect: { + id: theaterId + } + } + } + }) + + return this.toEntity(hall) + } + + public async findById(id: string): Promise { + const hall = await this.prismaService.hall.findUnique({ + where: { id } + }) + + return hall ? this.toEntity(hall) : null + } + + public async listByTheater( + theaterId: string + ): Promise<{ halls: HallEntity[] }> { + const halls = await this.prismaService.hall.findMany({ + where: { theaterId }, + orderBy: { name: 'asc' } + }) + + return { + halls: halls.map(hall => this.toEntity(hall)) + } + } + + public async createSeats(data: { + hallId: string + layout: RowLayout[] + }): Promise { + const seats: SeatCreateManyInput[] = [] + + for (const rowConfig of data.layout) { + for (let num = 1; num <= rowConfig.columns; num++) { + seats.push({ + row: rowConfig.row, + number: num, + hallId: data.hallId, + type: rowConfig.type, + price: rowConfig.price, + x: num, + y: rowConfig.row + }) + } + } + + await this.prismaService.seat.createMany({ + data: seats + }) + } + + public async findSeatsByHall(hallId: string) { + const hall = await this.prismaService.hall.findUnique({ + where: { id: hallId }, + include: { + seats: true + } + }) + + return hall?.seats + } + + private toEntity(hall: Hall): HallEntity { + const { id, name, theaterId, createdAt, updatedAt } = hall + return new HallEntity(id, name, theaterId, createdAt, updatedAt) + } +} diff --git a/src/modules/hall/interfaces/grpc/hall.controller.ts b/src/modules/hall/interfaces/grpc/hall.controller.ts new file mode 100644 index 0000000..a7ed1e5 --- /dev/null +++ b/src/modules/hall/interfaces/grpc/hall.controller.ts @@ -0,0 +1,42 @@ +import { Controller } from '@nestjs/common' +import { GrpcMethod } from '@nestjs/microservices' +import type { + CreateHallRequest, + CreateHallResponse, + GetHallRequest, + GetHallResponse, + ListHallsByTheaterRequest, + ListHallsByTheaterResponse +} from '@teacinema/contracts/gen/ts/hall' + +import { CreateHallUsecase } from '../../application/commands/create-hall.usecase' +import { GetHallUsecase } from '../../application/queries/get-hall.usecase' +import { ListHallsUseCase } from '../../application/queries/list-halls.usecase' + +@Controller() +export class HallGrpcController { + public constructor( + private readonly getHallUC: GetHallUsecase, + private readonly listHallsUC: ListHallsUseCase, + private readonly createHallsUC: CreateHallUsecase + ) {} + + @GrpcMethod('HallService', 'GetHall') + public async getHall(data: GetHallRequest): Promise { + return this.getHallUC.execute(data.id) + } + + @GrpcMethod('HallService', 'CreateHall') + public async createHall( + data: CreateHallRequest + ): Promise { + return this.createHallsUC.execute(data) + } + + @GrpcMethod('HallService', 'ListHallsByTheater') + public async listHalls( + data: ListHallsByTheaterRequest + ): Promise { + return this.listHallsUC.execute(data.theaterId) + } +} diff --git a/src/modules/seat/application/queries/get-seat.usecase.ts b/src/modules/seat/application/queries/get-seat.usecase.ts new file mode 100644 index 0000000..b81d497 --- /dev/null +++ b/src/modules/seat/application/queries/get-seat.usecase.ts @@ -0,0 +1,28 @@ +import { Injectable } from '@nestjs/common' +import { RpcException } from '@nestjs/microservices' +import { RpcStatus } from '@teacinema/common' + +import { SeatRepositoryPort } from '../../domain/ports/seat.repository.port' + +@Injectable() +export class GetSeatUsecase { + public constructor(private readonly repository: SeatRepositoryPort) {} + + public async execute(id: string) { + const seat = await this.repository.findById(id) + + if (!seat) { + throw new RpcException({ + code: RpcStatus.NOT_FOUND, + message: 'Seat not found' + }) + } + + return { + seat: { + ...seat, + status: 'available' + } + } + } +} diff --git a/src/modules/seat/application/queries/list-seats.usecase.ts b/src/modules/seat/application/queries/list-seats.usecase.ts new file mode 100644 index 0000000..a316c37 --- /dev/null +++ b/src/modules/seat/application/queries/list-seats.usecase.ts @@ -0,0 +1,19 @@ +import { Injectable } from '@nestjs/common' + +import { SeatRepositoryPort } from '../../domain/ports/seat.repository.port' + +@Injectable() +export class ListSeatsUseCase { + public constructor(private readonly repository: SeatRepositoryPort) {} + + public async execute(data: { hallId: string; screeningId: string }) { + const seats = await this.repository.findByHall(data.hallId) + + return { + seats: seats.map(s => ({ + ...s, + status: 'available' + })) + } + } +} diff --git a/src/modules/seat/domain/entities/seat.entity.ts b/src/modules/seat/domain/entities/seat.entity.ts new file mode 100644 index 0000000..96249fa --- /dev/null +++ b/src/modules/seat/domain/entities/seat.entity.ts @@ -0,0 +1,10 @@ +export class SeatEntity { + public constructor( + public readonly id: string, + public readonly row: number, + public readonly number: number, + public readonly price: number, + public readonly type: string, + public readonly hallId: string, + ) {} +} diff --git a/src/modules/seat/domain/ports/seat.repository.port.ts b/src/modules/seat/domain/ports/seat.repository.port.ts new file mode 100644 index 0000000..994725e --- /dev/null +++ b/src/modules/seat/domain/ports/seat.repository.port.ts @@ -0,0 +1,6 @@ +import { SeatEntity } from '../entities/seat.entity' + +export abstract class SeatRepositoryPort { + public abstract findById(id: string): Promise + public abstract findByHall(hallId: string): Promise +} diff --git a/src/modules/seat/infra/prisma/seat.prisma.repository.ts b/src/modules/seat/infra/prisma/seat.prisma.repository.ts new file mode 100644 index 0000000..6d3a989 --- /dev/null +++ b/src/modules/seat/infra/prisma/seat.prisma.repository.ts @@ -0,0 +1,34 @@ +import { Injectable } from '@nestjs/common' +import { Seat } from '@prisma/generated/client' + +import { PrismaService } from '@/infra/prisma/prisma.service' + +import { SeatEntity } from '../../domain/entities/seat.entity' +import { SeatRepositoryPort } from '../../domain/ports/seat.repository.port' + +@Injectable() +export class SeatPrismaRepository implements SeatRepositoryPort { + public constructor(private readonly prismaService: PrismaService) {} + + public async findById(id: string): Promise { + const seat = await this.prismaService.seat.findUnique({ + where: { id } + }) + + return seat ? this.toEntity(seat) : null + } + + public async findByHall(hallId: string) { + const seats = await this.prismaService.seat.findMany({ + where: { hallId }, + orderBy: [{ row: 'asc' }, { number: 'asc' }] + }) + + return seats.map(s => this.toEntity(s)) + } + + private toEntity(seat: Seat): SeatEntity { + const { id, number, price, row, type, hallId } = seat + return new SeatEntity(id, row, number, price, type, hallId) + } +} diff --git a/src/modules/seat/infra/seat.module.ts b/src/modules/seat/infra/seat.module.ts new file mode 100644 index 0000000..f52da52 --- /dev/null +++ b/src/modules/seat/infra/seat.module.ts @@ -0,0 +1,21 @@ +import { Module } from '@nestjs/common' + +import { GetSeatUsecase } from '../application/queries/get-seat.usecase' +import { ListSeatsUseCase } from '../application/queries/list-seats.usecase' +import { SeatRepositoryPort } from '../domain/ports/seat.repository.port' +import { SeatGrpcController } from '../interfaces/grpc/seat.controller' + +import { SeatPrismaRepository } from './prisma/seat.prisma.repository' + +@Module({ + controllers: [SeatGrpcController], + providers: [ + { + provide: SeatRepositoryPort, + useClass: SeatPrismaRepository + }, + GetSeatUsecase, + ListSeatsUseCase + ] +}) +export class SeatModule {} diff --git a/src/modules/seat/interfaces/grpc/seat.controller.ts b/src/modules/seat/interfaces/grpc/seat.controller.ts new file mode 100644 index 0000000..38ed402 --- /dev/null +++ b/src/modules/seat/interfaces/grpc/seat.controller.ts @@ -0,0 +1,31 @@ +import { Controller } from '@nestjs/common' +import { GrpcMethod } from '@nestjs/microservices' +import type { + GetSeatRequest, + GetSeatResponse, + ListSeatsByHallRequest, + ListSeatsByHallResponse +} from '@teacinema/contracts/gen/ts/seat' + +import { GetSeatUsecase } from '../../application/queries/get-seat.usecase' +import { ListSeatsUseCase } from '../../application/queries/list-seats.usecase' + +@Controller() +export class SeatGrpcController { + public constructor( + private readonly getSeatUC: GetSeatUsecase, + private readonly listSeatsUC: ListSeatsUseCase + ) {} + + @GrpcMethod('SeatService', 'GetSeat') + public async getSeat(data: GetSeatRequest): Promise { + return this.getSeatUC.execute(data.id) + } + + @GrpcMethod('SeatService', 'ListSeatsByHall') + public async listSeatsByHall( + data: ListSeatsByHallRequest + ): Promise { + return this.listSeatsUC.execute(data) + } +} diff --git a/src/modules/theater/applications/commands/create-theater.usecase.ts b/src/modules/theater/application/commands/create-theater.usecase.ts similarity index 100% rename from src/modules/theater/applications/commands/create-theater.usecase.ts rename to src/modules/theater/application/commands/create-theater.usecase.ts diff --git a/src/modules/theater/applications/queries/get-theater.usecase.ts b/src/modules/theater/application/queries/get-theater.usecase.ts similarity index 100% rename from src/modules/theater/applications/queries/get-theater.usecase.ts rename to src/modules/theater/application/queries/get-theater.usecase.ts diff --git a/src/modules/theater/applications/queries/list-theater.usecase.ts b/src/modules/theater/application/queries/list-theater.usecase.ts similarity index 100% rename from src/modules/theater/applications/queries/list-theater.usecase.ts rename to src/modules/theater/application/queries/list-theater.usecase.ts diff --git a/src/modules/theater/infra/theater.module.ts b/src/modules/theater/infra/theater.module.ts index 4fff078..debe91f 100644 --- a/src/modules/theater/infra/theater.module.ts +++ b/src/modules/theater/infra/theater.module.ts @@ -1,10 +1,8 @@ import { Module } from '@nestjs/common' -import { PrismaModule } from '@/infra/prisma/prisma.module' - -import { CreateTheaterUsecase } from '../applications/commands/create-theater.usecase' -import { GetTheaterUsecase } from '../applications/queries/get-theater.usecase' -import { ListTheaterUsecase } from '../applications/queries/list-theater.usecase' +import { CreateTheaterUsecase } from '../application/commands/create-theater.usecase' +import { GetTheaterUsecase } from '../application/queries/get-theater.usecase' +import { ListTheaterUsecase } from '../application/queries/list-theater.usecase' import { TheaterRepositoryPort } from '../domain/ports/theater.repository.port' import { TheaterGrpcController } from '../interfaces/grpc/theater.grpc.controller' diff --git a/src/modules/theater/interfaces/grpc/theater.grpc.controller.ts b/src/modules/theater/interfaces/grpc/theater.grpc.controller.ts index 7d7bdbe..a396226 100644 --- a/src/modules/theater/interfaces/grpc/theater.grpc.controller.ts +++ b/src/modules/theater/interfaces/grpc/theater.grpc.controller.ts @@ -8,10 +8,9 @@ import type { ListTheaterResponse } from '@teacinema/contracts/gen/ts/theater' -import { CreateTheaterUsecase } from '@/modules/theater/applications/commands/create-theater.usecase' -import { GetTheaterUsecase } from '@/modules/theater/applications/queries/get-theater.usecase' - -import { ListTheaterUsecase } from '../../applications/queries/list-theater.usecase' +import { CreateTheaterUsecase } from '../../application/commands/create-theater.usecase' +import { GetTheaterUsecase } from '../../application/queries/get-theater.usecase' +import { ListTheaterUsecase } from '../../application/queries/list-theater.usecase' @Controller() export class TheaterGrpcController { diff --git a/yarn.lock b/yarn.lock index 6568975..ba9ec44 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1345,9 +1345,9 @@ integrity sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w== "@teacinema/common@^1.4.0": - version "1.4.0" - resolved "https://git.ksv741.keenetic.pro/api/packages/teacinema/npm/%40teacinema%2Fcommon/-/1.4.0/common-1.4.0.tgz#9523c8a5a54c54b4756f2bc4db5d2cabb60504f2" - integrity sha512-M2c95xPg/lSG5Vz9tkqheuKzrd5m0HDtZdBEpPVehkTjk81GmSywyZr/HMBEl4GU/cQG93Xza//l3yCP7JnaZg== + version "1.5.0" + resolved "https://git.ksv741.keenetic.pro/api/packages/teacinema/npm/%40teacinema%2Fcommon/-/1.5.0/common-1.5.0.tgz#236c1aba248b31773297bf17aad9053116483e93" + integrity sha512-GqGx8jFaMP6ADcZP18ariJtAHbJqLhT9Up80SLwU1fInKUthQb5MzLrTc4ce8jAxbczLVMGmikphIQNYNpvCGg== dependencies: "@nestjs/common" "^11.1.16" "@nestjs/config" "^4.0.3" @@ -1355,9 +1355,9 @@ "@teacinema/contracts" "^1.1.3" "@teacinema/contracts@^1.1.3": - version "1.3.1" - resolved "https://git.ksv741.keenetic.pro/api/packages/teacinema/npm/%40teacinema%2Fcontracts/-/1.3.1/contracts-1.3.1.tgz#f911a6d2a82a373ccf0f9a6223192e8bf3d7f933" - integrity sha512-iDvXw1zwvORps1d9qbvABVMRAx/WuftOciDAB1KG9TWD76nJT6CfCB9aJfovg3dfeeJo2WYc7mRbp8Ns6YeA/w== + version "1.4.0" + resolved "https://git.ksv741.keenetic.pro/api/packages/teacinema/npm/%40teacinema%2Fcontracts/-/1.4.0/contracts-1.4.0.tgz#96f3abb5fa2ddf8c134e040bb8f155cd0829fcaa" + integrity sha512-1bQdII9naDgEiOXA/F2mxw9d62yF7NFv6f0atfUGvWrSTtM+uARX+wwcLHTuPDu3I1y8XrAYc8y4stFZxt2RrQ== dependencies: "@nestjs/microservices" "^11.1.12" protoc "33.4.0" @@ -1365,9 +1365,9 @@ ts-proto "2.11.0" "@teacinema/contracts@^1.3.1": - version "1.3.2" - resolved "https://git.ksv741.keenetic.pro/api/packages/teacinema/npm/%40teacinema%2Fcontracts/-/1.3.2/contracts-1.3.2.tgz#4abd896c2af5030ec049b34f978aa52554e6b03d" - integrity sha512-myXVPXPDOzSxixIAh8LuIMg84ftBHSKht9sySLnztrQfssutg4Hz5590aQIMlgUooHBiC/Gu/6sCUEkfeFOofQ== + version "1.4.1" + resolved "https://git.ksv741.keenetic.pro/api/packages/teacinema/npm/%40teacinema%2Fcontracts/-/1.4.1/contracts-1.4.1.tgz#20feccf1784a41ad6b4b0ebfcc035f991a7473c9" + integrity sha512-CcYU/cUQfBw+KJp8d14RONIZahkbUAYxiLqudHhx/kpEPuoQDO8VMJy6SbUqBM1HKZ1VTtyQWfxm/79PBPMe0g== dependencies: "@nestjs/microservices" "^11.1.12" protoc "33.4.0"