From 2a8be3fd82c75beb4a67ca40ad20e33ad7b0de85 Mon Sep 17 00:00:00 2001 From: julian Date: Wed, 27 May 2026 22:36:00 +0000 Subject: [PATCH] feat(api): master-login stub + WorkspacePrismaPool (Frente E) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Prisma 7: prisma.config.ts com datasource.url (API correta); schema gerado em CJS - WorkspacePrismaPool: LRU cache (max 10) de PrismaClient por workspace (ADR 0006) PrismaPg adapter + pg.Pool por workspace; getOrCreate/health/onModuleDestroy - JwtAuthGuard: global APP_GUARD, jose HS256, popula CLS com workspace_id/userId/prisma @Public() decorator marca ping/health/dev-auth como rotas abertas - DevAuthController: POST /auth/dev/token — emite JWT dev (404 em produção) - AuthTokenResponseSchema + DevTokenRequestSchema em @sar/api-interface - WorkspacePoolHealthIndicator: health/ready reporta amostra LRU top-3 (nunca O(N)) - .npmrc: hoist @prisma/client-runtime-utils (requerido pelo Prisma 7 isolated mode) Co-Authored-By: Claude Opus 4.7 --- .npmrc | 1 + apps/api/prisma.config.ts | 17 + apps/api/prisma/schema.prisma | 26 + apps/api/src/app/app.module.ts | 12 +- apps/api/src/app/auth/auth.module.ts | 12 + apps/api/src/app/auth/dev-auth.controller.ts | 46 ++ apps/api/src/app/auth/jwt-auth.guard.ts | 79 ++ apps/api/src/app/auth/jwt.types.ts | 17 + apps/api/src/app/auth/public.decorator.ts | 6 + apps/api/src/app/health/health.controller.ts | 26 +- apps/api/src/app/health/health.module.ts | 5 +- .../health/workspace-pool.health-indicator.ts | 35 + apps/api/src/app/ping/ping.controller.ts | 2 + .../workspace-prisma-pool.service.ts | 80 ++ .../api/src/app/workspace/workspace.module.ts | 19 +- apps/api/src/app/workspace/workspace.types.ts | 12 +- design-artifacts/_progress/00-design-log.md | 32 +- libs/shared/api-interface/src/index.ts | 1 + .../api-interface/src/lib/auth.contract.ts | 23 + package.json | 8 + pnpm-lock.yaml | 782 +++++++++++++++++- pnpm-workspace.yaml | 2 + 22 files changed, 1204 insertions(+), 39 deletions(-) create mode 100644 apps/api/prisma.config.ts create mode 100644 apps/api/prisma/schema.prisma create mode 100644 apps/api/src/app/auth/auth.module.ts create mode 100644 apps/api/src/app/auth/dev-auth.controller.ts create mode 100644 apps/api/src/app/auth/jwt-auth.guard.ts create mode 100644 apps/api/src/app/auth/jwt.types.ts create mode 100644 apps/api/src/app/auth/public.decorator.ts create mode 100644 apps/api/src/app/health/workspace-pool.health-indicator.ts create mode 100644 apps/api/src/app/workspace/workspace-prisma-pool.service.ts create mode 100644 libs/shared/api-interface/src/lib/auth.contract.ts diff --git a/.npmrc b/.npmrc index 610b8d9..af819c6 100644 --- a/.npmrc +++ b/.npmrc @@ -6,4 +6,5 @@ shamefully-hoist=false public-hoist-pattern[]=*types* public-hoist-pattern[]=*eslint* public-hoist-pattern[]=*prettier* +public-hoist-pattern[]=@prisma/client-runtime-utils node-linker=isolated diff --git a/apps/api/prisma.config.ts b/apps/api/prisma.config.ts new file mode 100644 index 0000000..65959c2 --- /dev/null +++ b/apps/api/prisma.config.ts @@ -0,0 +1,17 @@ +// Prisma 7 config — usado pelo CLI (migrate, generate, studio). +// Conexão de runtime fica no WorkspacePrismaPool (adapter por workspace). +// CODING-RULES PGD-DB-001: DATABASE_URL aponta direto ao PG na porta 5432 (sem PgBouncer). + +import path from 'node:path'; +import { defineConfig } from 'prisma/config'; + +export default defineConfig({ + schema: path.join(import.meta.dirname, 'prisma/schema.prisma'), + datasource: { + // Prisma 7: url aqui serve apenas para o CLI (migrate/generate/studio). + // Runtime usa WorkspacePrismaPool → PrismaClient({ adapter: new PrismaPg(pool) }). + url: + process.env['DATABASE_URL'] ?? + 'postgresql://sar:sar_dev_password@localhost:5432/sar_workspace_dev', + }, +}); diff --git a/apps/api/prisma/schema.prisma b/apps/api/prisma/schema.prisma new file mode 100644 index 0000000..6111ea2 --- /dev/null +++ b/apps/api/prisma/schema.prisma @@ -0,0 +1,26 @@ +// SAR — Workspace Database Schema +// Stack canon: Prisma 7 · PostgreSQL 18 · BD-por-workspace (ADR 0006) +// +// Este schema roda em CADA workspace DB (sar_workspace_). +// NÃO há workspaceId/tenantId em nenhum modelo — o isolamento é físico. +// O banco master (sar_master) é gerenciado pelo master-login (IdP JCS), não por este schema. +// +// CODING-RULES PGD-DB-004: moduleFormat = "cjs" (NestJS é CJS) +// CODING-RULES PGD-DB-001: MIGRATION_DATABASE_URL aponta direto ao PG (sem PgBouncer) + +generator client { + provider = "prisma-client-js" + output = "../../../node_modules/.prisma/client" + moduleFormat = "cjs" +} + +// Prisma 7: url foi removida do schema — conexão fica em prisma.config.ts (migrate) +// e no WorkspacePrismaPool via PrismaPg adapter (runtime). +datasource db { + provider = "postgresql" +} + +// ─── Modelos de domínio serão adicionados por feature ────────────────────── +// +// Próximos: Client (C2), Order + OrderItem (C3/C4) — vindos das stories. +// Cada model novo exige: migration versionada + seed de dev atualizado. diff --git a/apps/api/src/app/app.module.ts b/apps/api/src/app/app.module.ts index ac2cefa..6f85564 100644 --- a/apps/api/src/app/app.module.ts +++ b/apps/api/src/app/app.module.ts @@ -1,28 +1,30 @@ import { Module } from '@nestjs/common'; -import { APP_FILTER, APP_PIPE } from '@nestjs/core'; +import { APP_FILTER, APP_GUARD, APP_PIPE } from '@nestjs/core'; import { ZodValidationPipe } from 'nestjs-zod'; import { EnvModule } from './config/env.module'; import { LoggerModule } from './logger/logger.module'; import { WorkspaceModule } from './workspace/workspace.module'; import { HealthModule } from './health/health.module'; import { PingModule } from './ping/ping.module'; +import { AuthModule } from './auth/auth.module'; +import { JwtAuthGuard } from './auth/jwt-auth.guard'; import { ProblemDetailsFilter } from './filters/problem-details.filter'; @Module({ imports: [ - // Ordem importa: Env primeiro (fail-fast), depois Logger, CLS, módulos de domínio. + // Ordem: Env primeiro (fail-fast), depois Logger, CLS, Auth, módulos de domínio. EnvModule, LoggerModule, WorkspaceModule, + AuthModule, HealthModule, PingModule, ], providers: [ - // Pipe global: nestjs-zod converte ZodSchema (via createZodDto) em validação automática. - // CODING-RULES §06: schema é o contrato; DTO é a classe que o expõe. { provide: APP_PIPE, useClass: ZodValidationPipe }, - // Filter global: RFC 9457. Zod → 422. { provide: APP_FILTER, useClass: ProblemDetailsFilter }, + // Guard global — exige JWT em todas as rotas exceto as com @Public(). + { provide: APP_GUARD, useClass: JwtAuthGuard }, ], }) export class AppModule {} diff --git a/apps/api/src/app/auth/auth.module.ts b/apps/api/src/app/auth/auth.module.ts new file mode 100644 index 0000000..aa8a21a --- /dev/null +++ b/apps/api/src/app/auth/auth.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { WorkspaceModule } from '../workspace/workspace.module'; +import { JwtAuthGuard } from './jwt-auth.guard'; +import { DevAuthController } from './dev-auth.controller'; + +@Module({ + imports: [WorkspaceModule], + controllers: [DevAuthController], + providers: [JwtAuthGuard], + exports: [JwtAuthGuard], +}) +export class AuthModule {} diff --git a/apps/api/src/app/auth/dev-auth.controller.ts b/apps/api/src/app/auth/dev-auth.controller.ts new file mode 100644 index 0000000..9046421 --- /dev/null +++ b/apps/api/src/app/auth/dev-auth.controller.ts @@ -0,0 +1,46 @@ +import { Body, Controller, HttpCode, HttpStatus, NotFoundException, Post } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { SignJWT } from 'jose'; +import { createZodDto } from 'nestjs-zod'; +import { DevTokenRequestSchema, type AuthTokenResponse } from '@sar/api-interface'; +import type { Env } from '../config/env.schema'; +import { Public } from './public.decorator'; + +class DevTokenRequestDto extends createZodDto(DevTokenRequestSchema) {} + +// Dev-only stub — emite JWT HS256 para smoke tests locais. +// CODING-RULES PGD-SEC-002: retorna 404 em produção. +// CODING-RULES PGD-AUTHZ-002: workspace_id vem do body aqui APENAS porque +// este endpoint É o gerador do token — nenhum outro handler pode fazer isso. + +@Public() +@Controller({ path: 'auth/dev' }) +export class DevAuthController { + private readonly secret: Uint8Array; + private readonly expiresIn: number; + private readonly isProd: boolean; + + constructor(config: ConfigService) { + this.secret = new TextEncoder().encode(config.get('MASTER_LOGIN_JWT_SECRET', { infer: true })); + this.expiresIn = config.get('JWT_ACCESS_EXPIRATION', { infer: true }); + this.isProd = config.get('NODE_ENV', { infer: true }) === 'production'; + } + + @Post('token') + @HttpCode(HttpStatus.OK) + async token(@Body() dto: DevTokenRequestDto): Promise { + if (this.isProd) throw new NotFoundException(); + + const accessToken = await new SignJWT({ + workspace_id: dto.workspaceId, + role: dto.role, + }) + .setProtectedHeader({ alg: 'HS256' }) + .setSubject(dto.userId) + .setIssuedAt() + .setExpirationTime(`${this.expiresIn}s`) + .sign(this.secret); + + return { accessToken, tokenType: 'Bearer', expiresIn: this.expiresIn }; + } +} diff --git a/apps/api/src/app/auth/jwt-auth.guard.ts b/apps/api/src/app/auth/jwt-auth.guard.ts new file mode 100644 index 0000000..996fb02 --- /dev/null +++ b/apps/api/src/app/auth/jwt-auth.guard.ts @@ -0,0 +1,79 @@ +import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { Reflector } from '@nestjs/core'; +import { ClsService } from 'nestjs-cls'; +import { jwtVerify } from 'jose'; +import type { Request } from 'express'; +import type { Env } from '../config/env.schema'; +import type { WorkspaceClsStore } from '../workspace/workspace.types'; +import { WorkspacePrismaPool } from '../workspace/workspace-prisma-pool.service'; +import type { JwtPayload } from './jwt.types'; +import { IS_PUBLIC_KEY } from './public.decorator'; + +// Guard global (APP_GUARD). Valida Bearer HS256 e atualiza CLS com workspace real. +// CODING-RULES PGD-AUTHZ-002: workspaceId sempre do JWT, nunca de body/param. +// Ordem NestJS: middleware CLS (workspace default) → este guard (workspace real). + +@Injectable() +export class JwtAuthGuard implements CanActivate { + private readonly secret: Uint8Array; + + constructor( + private readonly reflector: Reflector, + private readonly cls: ClsService, + private readonly pool: WorkspacePrismaPool, + config: ConfigService, + ) { + this.secret = new TextEncoder().encode(config.get('MASTER_LOGIN_JWT_SECRET', { infer: true })); + } + + async canActivate(context: ExecutionContext): Promise { + if (this.isPublic(context)) return true; + + const req = context.switchToHttp().getRequest(); + const token = this.extractBearer(req); + + if (!token) { + throw new UnauthorizedException('token ausente'); + } + + try { + const { payload } = await jwtVerify(token, this.secret, { + algorithms: ['HS256'], + }); + + (req as Request & { user: JwtPayload }).user = payload as JwtPayload; + + // Sobrescreve CLS com workspace real do JWT (corre depois do middleware). + const workspaceId = payload.workspace_id; + this.cls.set('workspaceId', workspaceId); + this.cls.set('userId', payload.sub); + + const dbUrl = + process.env['DATABASE_URL'] ?? + `postgresql://sar:sar_dev_password@localhost:5432/sar_workspace_${workspaceId}`; + this.cls.set('prisma', this.pool.getOrCreate(workspaceId, dbUrl)); + + return true; + } catch { + throw new UnauthorizedException('token inválido ou expirado'); + } + } + + private isPublic(ctx: ExecutionContext): boolean { + return ( + this.reflector.getAllAndOverride(IS_PUBLIC_KEY, [ + ctx.getHandler(), + ctx.getClass(), + ]) === true + ); + } + + private extractBearer(req: Request): string | undefined { + const auth = req.headers['authorization']; + if (typeof auth === 'string' && auth.startsWith('Bearer ')) { + return auth.slice(7); + } + return undefined; + } +} diff --git a/apps/api/src/app/auth/jwt.types.ts b/apps/api/src/app/auth/jwt.types.ts new file mode 100644 index 0000000..f74c258 --- /dev/null +++ b/apps/api/src/app/auth/jwt.types.ts @@ -0,0 +1,17 @@ +// Claims do JWT emitido pelo master-login. Fonte da verdade para req.user. +// CODING-RULES PGD-AUTHZ-002: workspace_id vem sempre do token, nunca do body. + +export type JwtRole = 'rep' | 'supervisor' | 'manager' | 'admin'; + +export interface JwtPayload { + sub: string; // userId + workspace_id: string; + role: JwtRole; + iat?: number; + exp?: number; +} + +// Tipo auxiliar para requests autenticados — evita global namespace augmentation. +export interface AuthenticatedRequest { + user: JwtPayload; +} diff --git a/apps/api/src/app/auth/public.decorator.ts b/apps/api/src/app/auth/public.decorator.ts new file mode 100644 index 0000000..b936083 --- /dev/null +++ b/apps/api/src/app/auth/public.decorator.ts @@ -0,0 +1,6 @@ +import { SetMetadata } from '@nestjs/common'; + +export const IS_PUBLIC_KEY = 'isPublic'; + +/** Marca um controller ou handler como público — JwtAuthGuard não exige token. */ +export const Public = () => SetMetadata(IS_PUBLIC_KEY, true); diff --git a/apps/api/src/app/health/health.controller.ts b/apps/api/src/app/health/health.controller.ts index 8a70511..33f0368 100644 --- a/apps/api/src/app/health/health.controller.ts +++ b/apps/api/src/app/health/health.controller.ts @@ -5,22 +5,22 @@ import { HealthCheckService, MemoryHealthIndicator, } from '@nestjs/terminus'; +import { Public } from '../auth/public.decorator'; +import { WorkspacePoolHealthIndicator } from './workspace-pool.health-indicator'; // CODING-RULES §20 (PGD-OBS-003): -// /health/live → liveness só com memory.checkHeap(350MB). -// /health/ready → readiness pinga master-login + amostra LRU (K=3) dos pools -// quentes do WorkspacePrismaPool + Valkey + BullMQ. -// NUNCA percorrer todos os workspaces (O(N) → false negative). -// -// Hoje o "ready" só checa heap, idêntico ao live. Quando master-login, -// WorkspacePrismaPool, Valkey e BullMQ entrarem, cada um adiciona seu indicator -// aqui — sem nunca virar O(N) sobre workspaces. +// /health/live → liveness: memory.checkHeap(350MB). +// /health/ready → readiness: heap + amostra LRU (K=3) do WorkspacePrismaPool. +// Próximos: MasterLoginHealthIndicator, ValkeyHealthIndicator, BullMQHealthIndicator. +// NUNCA percorrer todos os workspaces (O(N)). +@Public() @Controller({ path: 'health' }) export class HealthController { constructor( private readonly health: HealthCheckService, private readonly memory: MemoryHealthIndicator, + private readonly workspacePool: WorkspacePoolHealthIndicator, ) {} @Get('live') @@ -32,11 +32,9 @@ export class HealthController { @Get('ready') @HealthCheck() ready(): Promise { - // Skeleton: por enquanto idêntico ao live. Próximas frentes: - // - MasterLoginHealthIndicator (obrigatório) - // - WorkspacePoolLruHealthIndicator (K=3 amostra) - // - ValkeyHealthIndicator - // - BullMQHealthIndicator - return this.health.check([() => this.memory.checkHeap('heap', 350 * 1024 * 1024)]); + return this.health.check([ + () => this.memory.checkHeap('heap', 350 * 1024 * 1024), + () => this.workspacePool.check('workspace_pool', 3), + ]); } } diff --git a/apps/api/src/app/health/health.module.ts b/apps/api/src/app/health/health.module.ts index 0208ef7..5992fa9 100644 --- a/apps/api/src/app/health/health.module.ts +++ b/apps/api/src/app/health/health.module.ts @@ -1,9 +1,12 @@ import { Module } from '@nestjs/common'; import { TerminusModule } from '@nestjs/terminus'; +import { WorkspaceModule } from '../workspace/workspace.module'; import { HealthController } from './health.controller'; +import { WorkspacePoolHealthIndicator } from './workspace-pool.health-indicator'; @Module({ - imports: [TerminusModule], + imports: [TerminusModule, WorkspaceModule], controllers: [HealthController], + providers: [WorkspacePoolHealthIndicator], }) export class HealthModule {} diff --git a/apps/api/src/app/health/workspace-pool.health-indicator.ts b/apps/api/src/app/health/workspace-pool.health-indicator.ts new file mode 100644 index 0000000..e0c976f --- /dev/null +++ b/apps/api/src/app/health/workspace-pool.health-indicator.ts @@ -0,0 +1,35 @@ +import { Injectable } from '@nestjs/common'; +import { HealthIndicatorResult, HealthIndicator, HealthCheckError } from '@nestjs/terminus'; +import { WorkspacePrismaPool } from '../workspace/workspace-prisma-pool.service'; + +// Amostra os K workspaces mais quentes do LRU — nunca O(N) sobre todos os workspaces. +// CODING-RULES §20 (PGD-OBS-003): readiness não percorre workspaces individualmente. + +@Injectable() +export class WorkspacePoolHealthIndicator extends HealthIndicator { + constructor(private readonly pool: WorkspacePrismaPool) { + super(); + } + + async check(key = 'workspace_pool', k = 3): Promise { + const results = await this.pool.health(k); + + if (results.length === 0) { + return this.getStatus(key, true, { active: 0 }); + } + + const failed = results.filter((r) => !r.ok); + const isHealthy = failed.length === 0; + const detail = { + active: results.length, + healthy: results.length - failed.length, + ...(failed.length > 0 && { failed: failed.map((r) => r.workspaceId) }), + }; + + if (!isHealthy) { + throw new HealthCheckError(`${key} degradado`, this.getStatus(key, false, detail)); + } + + return this.getStatus(key, true, detail); + } +} diff --git a/apps/api/src/app/ping/ping.controller.ts b/apps/api/src/app/ping/ping.controller.ts index f65b4bf..4e204d9 100644 --- a/apps/api/src/app/ping/ping.controller.ts +++ b/apps/api/src/app/ping/ping.controller.ts @@ -2,6 +2,7 @@ import { Controller, Get } from '@nestjs/common'; import { ClsService } from 'nestjs-cls'; import type { PingResponse } from '@sar/api-interface'; import type { WorkspaceClsStore } from '../workspace/workspace.types'; +import { Public } from '../auth/public.decorator'; // Endpoint de verificação de fundação: // - confirma que CLS está populando workspaceId + requestId; @@ -9,6 +10,7 @@ import type { WorkspaceClsStore } from '../workspace/workspace.types'; // - usado pela Web (Frente B) para validar conectividade real. // Contrato: @sar/api-interface · PingResponseSchema (zod). +@Public() @Controller({ path: 'ping' }) export class PingController { constructor(private readonly cls: ClsService) {} diff --git a/apps/api/src/app/workspace/workspace-prisma-pool.service.ts b/apps/api/src/app/workspace/workspace-prisma-pool.service.ts new file mode 100644 index 0000000..368a968 --- /dev/null +++ b/apps/api/src/app/workspace/workspace-prisma-pool.service.ts @@ -0,0 +1,80 @@ +import { Injectable, Logger, OnModuleDestroy } from '@nestjs/common'; +import { PrismaClient } from '@prisma/client'; +import { PrismaPg } from '@prisma/adapter-pg'; +import pg from 'pg'; + +// ADR 0006: BD-por-workspace — um PrismaClient por workspaceId, nunca singleton. +// CODING-RULES PGD-DB-009: callers obtêm o client via CLS, não injetando este serviço. + +const MAX_ENTRIES = 10; // LRU cap; ajustável via env na próxima iteração +const PG_POOL_SIZE = 5; +const noop = (): void => undefined; + +interface PoolEntry { + client: PrismaClient; + pgPool: pg.Pool; +} + +@Injectable() +export class WorkspacePrismaPool implements OnModuleDestroy { + private readonly logger = new Logger(WorkspacePrismaPool.name); + + // Map preserves insertion order → LRU: primeiro = mais antigo, último = mais recente + private readonly cache = new Map(); + + getOrCreate(workspaceId: string, dbUrl: string): PrismaClient { + const hit = this.cache.get(workspaceId); + if (hit) { + // Move para o fim (LRU refresh) + this.cache.delete(workspaceId); + this.cache.set(workspaceId, hit); + return hit.client; + } + + if (this.cache.size >= MAX_ENTRIES) { + this.evictOldest(); + } + + const pgPool = new pg.Pool({ connectionString: dbUrl, max: PG_POOL_SIZE }); + const adapter = new PrismaPg(pgPool); + const client = new PrismaClient({ adapter }); + this.cache.set(workspaceId, { client, pgPool }); + this.logger.log(`pool criado: workspace=${workspaceId} total=${this.cache.size}`); + return client; + } + + async health(k = 3): Promise<{ workspaceId: string; ok: boolean; latencyMs?: number }[]> { + // Verifica os k workspaces mais recentes + const entries = [...this.cache.entries()].slice(-k); + return Promise.all( + entries.map(async ([workspaceId, { pgPool }]) => { + const start = Date.now(); + try { + const conn = await pgPool.connect(); + conn.release(); + return { workspaceId, ok: true, latencyMs: Date.now() - start }; + } catch { + return { workspaceId, ok: false }; + } + }), + ); + } + + async onModuleDestroy(): Promise { + await Promise.allSettled( + [...this.cache.values()].map(({ client, pgPool }) => + client.$disconnect().finally(() => pgPool.end()), + ), + ); + this.cache.clear(); + this.logger.log('pool destruído — todos os clientes desconectados'); + } + + private evictOldest(): void { + const [oldestId, oldest] = this.cache.entries().next().value as [string, PoolEntry]; + void oldest.client.$disconnect().catch(noop); + void oldest.pgPool.end().catch(noop); + this.cache.delete(oldestId); + this.logger.log(`evicted LRU workspace=${oldestId}`); + } +} diff --git a/apps/api/src/app/workspace/workspace.module.ts b/apps/api/src/app/workspace/workspace.module.ts index 3de19c2..a80ea0d 100644 --- a/apps/api/src/app/workspace/workspace.module.ts +++ b/apps/api/src/app/workspace/workspace.module.ts @@ -5,10 +5,13 @@ import { randomUUID } from 'node:crypto'; import type { Request, Response } from 'express'; import type { WorkspaceClsStore } from './workspace.types'; import type { Env } from '../config/env.schema'; +import { WorkspacePrismaPool } from './workspace-prisma-pool.service'; -// CLS popula contexto por request. Hoje: requestId + DEFAULT_WORKSPACE_ID do env. -// Amanhã: workspaceId vem do JWT (PGD-AUTHZ-002); `prisma` é resolvido pelo -// WorkspacePrismaPool e injetado via cls.set('prisma', ...) aqui mesmo. +// CLS middleware roda ANTES dos guards (ordem NestJS). +// Aqui: apenas requestId + workspaceId default. +// JwtAuthGuard atualiza workspaceId, userId e prisma após validar o token. +// CODING-RULES PGD-DB-009: prisma via cls.get('prisma'), nunca singleton. +// CODING-RULES PGD-AUTHZ-002: workspaceId real vem do JWT (guard), não do env. @Module({ imports: [ @@ -21,28 +24,28 @@ import type { Env } from '../config/env.schema'; mount: true, generateId: true, idGenerator: (req: Request) => { - // Prioridade: req.id (pino-http já gerou/leu header) > header bruto > novo UUID. const fromPino = (req as Request & { id?: unknown }).id; if (typeof fromPino === 'string' && fromPino.length > 0) return fromPino; const headerVal = req.headers['x-request-id']; - return typeof headerVal === 'string' && headerVal.length > 0 - ? headerVal - : randomUUID(); + return typeof headerVal === 'string' && headerVal.length > 0 ? headerVal : randomUUID(); }, setup: (cls, req: Request, res: Response) => { const store = cls as unknown as { set: (key: K, value: WorkspaceClsStore[K]) => void; getId: () => string; }; + const requestId = store.getId(); res.setHeader('x-request-id', requestId); store.set('requestId', requestId); + // Fallback para rotas públicas (ping, health). Guard sobrescreve em rotas protegidas. store.set('workspaceId', config.get('DEFAULT_WORKSPACE_ID', { infer: true })); }, }, }), }), ], - exports: [ClsModule], + providers: [WorkspacePrismaPool], + exports: [ClsModule, WorkspacePrismaPool], }) export class WorkspaceModule {} diff --git a/apps/api/src/app/workspace/workspace.types.ts b/apps/api/src/app/workspace/workspace.types.ts index 962aa88..1be2c5a 100644 --- a/apps/api/src/app/workspace/workspace.types.ts +++ b/apps/api/src/app/workspace/workspace.types.ts @@ -1,13 +1,13 @@ import type { ClsStore } from 'nestjs-cls'; +import type { PrismaClient } from '@prisma/client'; -// Forma do CLS store por request — fonte da verdade para qualquer caller -// que faça `cls.get(...)`. Quando o PrismaClient por workspace entrar -// (ADR 0006), `prisma` virará obrigatório aqui — por hora segue opcional. +// Forma do CLS store por request — fonte da verdade para qualquer caller. +// CODING-RULES PGD-DB-009: nunca importe PrismaClient diretamente; use cls.get('prisma'). +// CODING-RULES PGD-AUTHZ-002: workspaceId vem sempre do JWT, nunca de body/param/query. export interface WorkspaceClsStore extends ClsStore { requestId: string; workspaceId: string; - // userId virá quando master-login estiver plugado. - userId?: string; - // prisma: PrismaClient — adicionar quando WorkspacePrismaPool entrar. + userId?: string; // preenchido pelo JwtAuthGuard (M3) + prisma?: PrismaClient; // preenchido pelo WorkspaceModule após WorkspacePrismaPool entrar (M5) } diff --git a/design-artifacts/_progress/00-design-log.md b/design-artifacts/_progress/00-design-log.md index 844abda..4afa460 100644 --- a/design-artifacts/_progress/00-design-log.md +++ b/design-artifacts/_progress/00-design-log.md @@ -384,9 +384,35 @@ - **OQs abertas (6):** OQ-1/OQ-4 são phase-blockers para C2/C4 — dependem do primeiro cliente. OQ-3/OQ-6 são non-blockers. - **Reviewer gate:** 1 revisor subagente — veredito "Aprovado com Ressalvas"; 3 achados incorporados antes do `final`. - **Pendente próxima sessão (ordem atualizada):** - 1. **Master-login stub + WorkspacePrismaPool** — frente arquitetural pesada. PRD está pronto; pode iniciar modelagem de domínio. - 2. **OpenTelemetry SDK** plugar quando entrar no catálogo. - 3. **Design C2/C4** (Consulta de Clientes + Lançamento de Pedido) — após resolver OQ-1 e OQ-4 com o primeiro cliente. + 1. **OpenTelemetry SDK** plugar quando entrar no catálogo. + 2. **Design C2/C4** (Consulta de Clientes + Lançamento de Pedido) — após resolver OQ-1 e OQ-4 com o primeiro cliente. + +### 2026-05-27 — Master-login stub + WorkspacePrismaPool COMPLETO ✅ + +**Entregas (M1–M7):** + +- **M1 — Prisma 7 config corrigida:** `prisma.config.ts` usa `datasource.url` (não `migrate.adapter`) — API correta do Prisma 7. `prisma migrate dev` e `prisma generate` funcionando. Schema vazio (modelos virão com C2/C3). + +- **M2 — WorkspacePrismaPool:** LRU cache (max 10) de `PrismaClient` por `workspaceId`. `getOrCreate(workspaceId, dbUrl)`, `health(k=3)`, `onModuleDestroy`. Usa `@prisma/adapter-pg` + `pg.Pool` por workspace (ADR 0006). + +- **M3 — JwtAuthGuard:** Guard global (`APP_GUARD`) com `jose` HS256. Valida Bearer token, popula `req.user` com `{sub, workspace_id, role}`. Atualiza CLS com `workspaceId`, `userId` e `prisma` após validação. `@Public()` decorator para ping/health/dev-auth. + +- **M4 — Auth dev stub:** `POST /api/v1/auth/dev/token` — emite JWT HS256 com claims `{sub, workspace_id, role}`. Retorna 404 em produção. Contrato `DevTokenRequestSchema` + `AuthTokenResponseSchema` em `@sar/api-interface`. + +- **M5 — WorkspaceModule:** CLS setup simplificado (middleware só define `requestId` + `workspaceId` default). Guard sobrescreve workspace real do JWT. Pool não injetado no middleware (limitação do nestjs-cls `ClsRootModule`). + +- **M6 — Health ready:** `WorkspacePoolHealthIndicator` adicionado ao `/health/ready`. Amostra top-3 LRU — nunca O(N). `active: 0` quando nenhum workspace criado ainda. + +- **M7 — Smoke test:** API sobe limpo. `/health/live` ✓, `/health/ready` ✓ (pool ativo=0), `/ping` público ✓, `POST /auth/dev/token` emite token com claims corretos. + +**Decisões técnicas:** +- `@prisma/client-runtime-utils` adicionado como dependência direta no workspace root (pnpm isolated mode não o hoista automaticamente). +- Guard atualiza CLS depois do middleware (ordem correta NestJS: middleware → guard → handler). +- Pool não injetado no ClsRootAsync devido a limitação de DI do nestjs-cls; guard faz a resolução. + +**Pendente próxima sessão:** +1. **OpenTelemetry SDK** plugar quando entrar no catálogo. +2. **Modelagem C2** — modelo `Client` no Prisma schema + migração + endpoint `GET /clients`. Requer OQ-1/OQ-4 resolvidos com primeiro cliente. --- diff --git a/libs/shared/api-interface/src/index.ts b/libs/shared/api-interface/src/index.ts index c1227ab..fdde956 100644 --- a/libs/shared/api-interface/src/index.ts +++ b/libs/shared/api-interface/src/index.ts @@ -1 +1,2 @@ export * from './lib/ping.contract'; +export * from './lib/auth.contract'; diff --git a/libs/shared/api-interface/src/lib/auth.contract.ts b/libs/shared/api-interface/src/lib/auth.contract.ts new file mode 100644 index 0000000..a0abccc --- /dev/null +++ b/libs/shared/api-interface/src/lib/auth.contract.ts @@ -0,0 +1,23 @@ +import { z } from 'zod'; + +// Contrato do auth dev stub — POST /api/v1/auth/dev/token. +// Endpoint existe APENAS em development/test (NODE_ENV !== 'production'). +// CODING-RULES PGD-SEC-002: never use dev secret in production. + +const JwtRoleSchema = z.enum(['rep', 'supervisor', 'manager', 'admin']); + +export const DevTokenRequestSchema = z.object({ + userId: z.string().min(1), + workspaceId: z.string().min(1), + role: JwtRoleSchema, +}); + +export const AuthTokenResponseSchema = z.object({ + accessToken: z.string().min(1), + tokenType: z.literal('Bearer'), + expiresIn: z.number().int().positive(), +}); + +export type DevTokenRequest = z.infer; +export type AuthTokenResponse = z.infer; +export type JwtRole = z.infer; diff --git a/package.json b/package.json index 48c4e55..33f34dd 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "@nx/webpack": "22.7.4", "@nx/workspace": "^22.7.4", "@playwright/test": "^1.36.0", + "@prisma/client-runtime-utils": "7.8.0", "@swc-node/register": "~1.11.1", "@swc/cli": "~0.8.0", "@swc/core": "~1.15.5", @@ -57,6 +58,7 @@ "@types/express": "^5.0.6", "@types/jest": "~30.0.0", "@types/node": "catalog:", + "@types/pg": "^8.20.0", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", "@vitejs/plugin-react": "^6.0.0", @@ -97,15 +99,21 @@ "@nestjs/core": "^11.0.0", "@nestjs/platform-express": "^11.0.0", "@nestjs/terminus": "^11.1.1", + "@prisma/adapter-pg": "^7.8.0", + "@prisma/client": "^7.8.0", "axios": "^1.6.0", "compression": "^1.8.1", "helmet": "^8.2.0", + "jose": "^6.2.3", + "lru-cache": "^11.5.0", "nestjs-cls": "^5.4.3", "nestjs-pino": "^4.6.1", "nestjs-zod": "^4.3.1", + "pg": "^8.21.0", "pino": "^9.14.0", "pino-http": "^10.5.0", "pino-pretty": "^13.1.3", + "prisma": "^7.8.0", "react": "^19.0.0", "react-dom": "^19.0.0", "reflect-metadata": "^0.1.13", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7e0e38c..dabbdfa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -34,7 +34,13 @@ importers: version: 11.1.24(@nestjs/common@11.1.24(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@11.1.24) '@nestjs/terminus': specifier: ^11.1.1 - version: 11.1.1(@nestjs/common@11.1.24(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@11.1.24)(reflect-metadata@0.1.14)(rxjs@7.8.1) + version: 11.1.1(@nestjs/common@11.1.24(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@11.1.24)(@prisma/client@7.8.0(prisma@7.8.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(magicast@0.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@5.9.3))(typescript@5.9.3))(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@prisma/adapter-pg': + specifier: ^7.8.0 + version: 7.8.0 + '@prisma/client': + specifier: ^7.8.0 + version: 7.8.0(prisma@7.8.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(magicast@0.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@5.9.3))(typescript@5.9.3) axios: specifier: ^1.6.0 version: 1.16.0 @@ -44,6 +50,12 @@ importers: helmet: specifier: ^8.2.0 version: 8.2.0 + jose: + specifier: ^6.2.3 + version: 6.2.3 + lru-cache: + specifier: ^11.5.0 + version: 11.5.0 nestjs-cls: specifier: ^5.4.3 version: 5.4.3(@nestjs/common@11.1.24(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@11.1.24)(reflect-metadata@0.1.14)(rxjs@7.8.1) @@ -53,6 +65,9 @@ importers: nestjs-zod: specifier: ^4.3.1 version: 4.3.1(@nestjs/common@11.1.24(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@11.1.24)(zod@4.4.3) + pg: + specifier: ^8.21.0 + version: 8.21.0 pino: specifier: ^9.14.0 version: 9.14.0 @@ -62,6 +77,9 @@ importers: pino-pretty: specifier: ^13.1.3 version: 13.1.3 + prisma: + specifier: ^7.8.0 + version: 7.8.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(magicast@0.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@5.9.3) react: specifier: ^19.0.0 version: 19.2.6 @@ -141,6 +159,9 @@ importers: '@playwright/test': specifier: ^1.36.0 version: 1.60.0 + '@prisma/client-runtime-utils': + specifier: 7.8.0 + version: 7.8.0 '@swc-node/register': specifier: ~1.11.1 version: 1.11.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@swc/core@1.15.40(@swc/helpers@0.5.21))(@swc/types@0.1.26)(typescript@5.9.3) @@ -171,6 +192,9 @@ importers: '@types/node': specifier: 'catalog:' version: 24.12.4 + '@types/pg': + specifier: ^8.20.0 + version: 8.20.0 '@types/react': specifier: ^19.0.0 version: 19.2.15 @@ -1128,6 +1152,20 @@ packages: resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==} engines: {node: '>=10.0.0'} + '@electric-sql/pglite-socket@0.1.1': + resolution: {integrity: sha512-p2hoXw3Z3LQHwTeikdZNsFBOvXGqKY2hk51BBw+8NKND8eoH+8LFOtW9Z8CQKmTJ2qqGYu82ipqiyFZOTTXNfw==} + hasBin: true + peerDependencies: + '@electric-sql/pglite': 0.4.1 + + '@electric-sql/pglite-tools@0.3.1': + resolution: {integrity: sha512-C+T3oivmy9bpQvSxVqXA1UDY8cB9Eb9vZHL9zxWwEUfDixbXv4G3r2LjoTdR33LD8aomR3O9ZXEO3XEwr/cUCA==} + peerDependencies: + '@electric-sql/pglite': 0.4.1 + + '@electric-sql/pglite@0.4.1': + resolution: {integrity: sha512-mZ9NzzUSYPOCnxHH1oAHPRzoMFJHY472raDKwXl/+6oPbpdJ7g8LsCN4FSaIIfkiCKHhb3iF/Zqo3NYxaIhU7Q==} + '@emnapi/core@1.10.0': resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} @@ -1216,6 +1254,12 @@ packages: '@fortawesome/fontawesome-svg-core': ~6 || ~7 react: ^18.0.0 || ^19.0.0 + '@hono/node-server@1.19.11': + resolution: {integrity: sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g==} + engines: {node: '>=18.14.1'} + peerDependencies: + hono: ^4 + '@hookform/resolvers@5.4.0': resolution: {integrity: sha512-EIsqr/t/qbinPIhGjMdtvutIN1Kk4uwbROE9/UQ93CAVGR7GkA7Y92+fX80OzXi/OB67jVFYwKGO1WzkxmkFZw==} peerDependencies: @@ -2354,6 +2398,143 @@ packages: '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + '@prisma/adapter-pg@7.8.0': + resolution: {integrity: sha512-ygb3UkerK3v8MDpXVgCISdRNDozpxh6+JVJgiIGbSr5KBgz10LLf5ejUskPGoXlsIjxsOu6nuy1JVQr2EKGSlg==} + + '@prisma/client-runtime-utils@7.8.0': + resolution: {integrity: sha512-5NQZztQ0oY/ADFkmd9gPuweH5A1/CCY8YQPorLLO0Mu6a87mY5gsnDkzmFmIHs9NFaLnZojzgddFVN4RpKYrdw==} + + '@prisma/client@7.8.0': + resolution: {integrity: sha512-HFp3Dawv/3sU3JtlPha90IB+48lS7zHiH4LKZPjmcE8YH5P9DOXGPvo8dqOtO7MqLDd1p2hOWMcFlRT1DMblHw==} + engines: {node: ^20.19 || ^22.12 || >=24.0} + peerDependencies: + prisma: '*' + typescript: '>=5.4.0' + peerDependenciesMeta: + prisma: + optional: true + typescript: + optional: true + + '@prisma/config@7.8.0': + resolution: {integrity: sha512-HFESzd9rx2ZQxlK+TL7tu1HPvCqrHiL6LCxYykI2c34mvaUuIVVl3lYuicJD/MNnzgPnyeBEMlK4WTomJCV5jw==} + + '@prisma/debug@7.2.0': + resolution: {integrity: sha512-YSGTiSlBAVJPzX4ONZmMotL+ozJwQjRmZweQNIq/ER0tQJKJynNkRB3kyvt37eOfsbMCXk3gnLF6J9OJ4QWftw==} + + '@prisma/debug@7.8.0': + resolution: {integrity: sha512-p+QZReysDUqXC+mk17q9a+Y/qzh4c2KYliDK30buYUyfrGeTGSyfmc0AIrJRhZJrLHhRiJa9Au/J72h3C+szvA==} + + '@prisma/dev@0.24.3': + resolution: {integrity: sha512-ffHlQuKXZiaDt9Go0OnCTdJZrHxK0k7omJKNV86/VjpsXu5EIHZLK0T7JSWgvNlJwh56kW9JFu9v0qJciFzepg==} + + '@prisma/driver-adapter-utils@7.8.0': + resolution: {integrity: sha512-/Q13o0ZT0rjc1Xk0Q9KhZYwuq2EW/vSbWUBKfgEKkaCuB/Sg6bqnjmTZqC5cD4d6y1vfFAEwBRzfzoSMIVJ55A==} + + '@prisma/engines-version@7.8.0-6.3c6e192761c0362d496ed980de936e2f3cebcd3a': + resolution: {integrity: sha512-fJPQxCkLgA5EayWaW8eArgCvjJ+N+Kz3VyeNKMEeYiQC4alNkxRKFVAGxv/ZUzuJISKqdw+zGeDbS6mn6RCPOA==} + + '@prisma/engines@7.8.0': + resolution: {integrity: sha512-jx3rCnNNrt5uzbkKlegtQ2GZHxSlihMCzutgT/BP6UIDF1r9tDI39hV/0T/cHZgzJ3ELbuQPXlVZy+Y1n0pcgw==} + + '@prisma/fetch-engine@7.8.0': + resolution: {integrity: sha512-gwB0Euiz/DDRyxFRpLXYlK3RfaZUj1c5dAYMuhZYfApg7arknJlcb9bIsOHDppJmbqYaVA+yBIiFMDBfprsNPQ==} + + '@prisma/get-platform@7.2.0': + resolution: {integrity: sha512-k1V0l0Td1732EHpAfi2eySTezyllok9dXb6UQanajkJQzPUGi3vO2z7jdkz67SypFTdmbnyGYxvEvYZdZsMAVA==} + + '@prisma/get-platform@7.8.0': + resolution: {integrity: sha512-WlxgRGnolL8VH2EmkH1R/DkKNr/mVdS3G2h42IZFFZ3eUrH9OT6t73kIOSlkkrv50wG123Iq8d96ufv5LlZktw==} + + '@prisma/query-plan-executor@7.2.0': + resolution: {integrity: sha512-EOZmNzcV8uJ0mae3DhTsiHgoNCuu1J9mULQpGCh62zN3PxPTd+qI9tJvk5jOst8WHKQNwJWR3b39t0XvfBB0WQ==} + + '@prisma/streams-local@0.1.2': + resolution: {integrity: sha512-l49yTxKKF2odFxaAXTmwmkBKL3+bVQ1tFOooGifu4xkdb9NMNLxHj27XAhTylWZod8I+ISGM5erU1xcl/oBCtg==} + engines: {bun: '>=1.3.6', node: '>=22.0.0'} + + '@prisma/studio-core@0.27.3': + resolution: {integrity: sha512-AADjNFPdsrglxHQVTmHFqv6DuKQZ5WY4p5/gVFY017twvNrSwpLJ9lqUbYYxEu2W7nbvVxTZA8deJ8LseNALsw==} + engines: {node: ^20.19 || ^22.12 || >=24.0, pnpm: '8'} + peerDependencies: + '@types/react': ^18.0.0 || ^19.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + + '@radix-ui/primitive@1.1.3': + resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} + + '@radix-ui/react-compose-refs@1.1.2': + resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-primitive@2.1.3': + resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-slot@1.2.3': + resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-toggle@1.1.10': + resolution: {integrity: sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-use-controllable-state@1.2.2': + resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-effect-event@0.0.2': + resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.1': + resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@rc-component/async-validator@5.1.0': resolution: {integrity: sha512-n4HcR5siNUXRX23nDizbZBQPO0ZM/5oTtmKZ6/eqL0L2bo747cklFdZGRN2f+c9qWGICwDzrhW0H7tE9PptdcA==} engines: {node: '>=14.x'} @@ -3421,6 +3602,9 @@ packages: '@types/parse-json@4.0.2': resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + '@types/pg@8.20.0': + resolution: {integrity: sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow==} + '@types/qs@6.15.1': resolution: {integrity: sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==} @@ -4063,6 +4247,10 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} + aws-ssl-profiles@1.1.2: + resolution: {integrity: sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==} + engines: {node: '>= 6.0.0'} + axe-core@4.11.4: resolution: {integrity: sha512-KunSNx+TVpkAw/6ULfhnx+HWRecjqZGTOyquAoWHYLRSdK1tB5Ihce1ZW+UY3fj33bYAFWPu7W/GRSmmrCGuxA==} engines: {node: '>=4'} @@ -4198,6 +4386,9 @@ packages: batch@0.6.1: resolution: {integrity: sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==} + better-result@2.9.2: + resolution: {integrity: sha512-WIFoBPCdnTOdk9inkE1ZRvCZ4P0CpSkAiLlchC65N7n9DcjZ3NhqkBOlafzpOVnO8ixyi37kicmSJ3ENhPZl7Q==} + big.js@5.2.2: resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} @@ -4289,6 +4480,14 @@ packages: resolution: {integrity: sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==} engines: {node: '>=6.0.0'} + c12@3.3.4: + resolution: {integrity: sha512-cM0ApFQSBXuourJejzwv/AuPRvAxordTyParRVcHjjtXirtkzM0uK2L9TTn9s0cXZbG7E55jCivRQzoxYmRAlA==} + peerDependencies: + magicast: '*' + peerDependenciesMeta: + magicast: + optional: true + cacheable-lookup@7.0.0: resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} engines: {node: '>=14.16'} @@ -4499,6 +4698,9 @@ packages: concat-with-sourcemaps@1.1.0: resolution: {integrity: sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==} + confbox@0.2.4: + resolution: {integrity: sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==} + confusing-browser-globals@1.0.11: resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==} @@ -4797,6 +4999,10 @@ packages: deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + deepmerge-ts@7.1.5: + resolution: {integrity: sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==} + engines: {node: '>=16.0.0'} + deepmerge@4.3.1: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} @@ -4828,10 +5034,17 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} + defu@6.1.7: + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} + delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} + denque@2.1.0: + resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} + engines: {node: '>=0.10'} + depd@1.1.2: resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} engines: {node: '>= 0.6'} @@ -4844,6 +5057,9 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} + destr@2.0.5: + resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} + destroy@1.2.0: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} @@ -4926,6 +5142,9 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + effect@3.20.0: + resolution: {integrity: sha512-qMLfDJscrNG8p/aw+IkT9W7fgj50Z4wG5bLBy0Txsxz8iUHjDIkOgO3SV0WZfnQbNG2VJYb0b+rDLMrhM4+Krw==} + ejs@5.0.1: resolution: {integrity: sha512-COqBPFMxuPTPspXl2DkVYaDS3HtrD1GpzOGkNTJ1IYkifq/r9h8SVEFrjA3D9/VJGOEoMQcrlhpntcSUrM8k6A==} engines: {node: '>=0.12.18'} @@ -4951,6 +5170,10 @@ packages: resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} engines: {node: '>= 4'} + empathic@2.0.0: + resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==} + engines: {node: '>=14'} + encodeurl@2.0.0: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} @@ -4981,6 +5204,10 @@ packages: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} + env-paths@3.0.0: + resolution: {integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + envinfo@7.21.0: resolution: {integrity: sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow==} engines: {node: '>=4'} @@ -5248,6 +5475,9 @@ packages: resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} engines: {node: '>= 18'} + exsolve@1.0.8: + resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==} + ext-list@2.2.2: resolution: {integrity: sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==} engines: {node: '>=0.10.0'} @@ -5256,6 +5486,10 @@ packages: resolution: {integrity: sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==} engines: {node: '>=4'} + fast-check@3.23.2: + resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==} + engines: {node: '>=8.0.0'} + fast-copy@4.0.3: resolution: {integrity: sha512-58apWr0GUiDFM8+3afrO6eYwJBn9ZAhDOzG3L+/9llab/haCARS2UIfffmOurYLwbgDRs8n0rfr6qAAPEAuAQw==} @@ -5463,6 +5697,9 @@ packages: functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + generate-function@2.3.1: + resolution: {integrity: sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==} + generator-function@2.0.1: resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} engines: {node: '>= 0.4'} @@ -5490,6 +5727,9 @@ packages: resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} engines: {node: '>=8.0.0'} + get-port-please@3.2.0: + resolution: {integrity: sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A==} + get-proto@1.0.1: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} @@ -5513,6 +5753,10 @@ packages: get-them-args@1.3.2: resolution: {integrity: sha512-LRn8Jlk+DwZE4GTlDbT3Hikd1wSHgLMme/+7ddlqKd7ldwR6LjJgTVWzBnR01wnYGe4KgrXjg287RaI22UHmAw==} + giget@3.2.0: + resolution: {integrity: sha512-GvHTWcykIR/fP8cj8dMpuMMkvaeJfPvYnhq0oW+chSeIr+ldX21ifU2Ms6KBoyKZQZmVaUAAhQ2EZ68KJF8a7A==} + hasBin: true + git-raw-commits@5.0.1: resolution: {integrity: sha512-Y+csSm2GD/PCSh6Isd/WiMjNAydu0VBiG9J7EdQsNA5P9uXvLayqjmTsNlK5Gs9IhblFZqOU0yid5Il5JPoLiQ==} engines: {node: '>=18'} @@ -5588,6 +5832,12 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + grammex@3.1.12: + resolution: {integrity: sha512-6ufJOsSA7LcQehIJNCO7HIBykfM7DXQual0Ny780/DEcJIpBlHRvcqEBWGPYd7hrXL2GJ3oJI1MIhaXjWmLQOQ==} + + graphmatch@1.1.1: + resolution: {integrity: sha512-5ykVn/EXM1hF0XCaWh05VbYvEiOL2lY1kBxZtaYsyvjp7cmWOU1XsAdfQBwClraEofXDT197lFbXOEVMHpvQOg==} + handle-thing@2.0.1: resolution: {integrity: sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==} @@ -5645,6 +5895,10 @@ packages: resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==} engines: {node: '>=0.10.0'} + hono@4.12.23: + resolution: {integrity: sha512-eIaZ9qDgu7XV0pxOCrg7/WhnQ6Ivm22UcxhXx/A3dcbqbbYgBEkc6e/J/s7j2tS96zoB0S9VBdLwQNCWwUo4LA==} + engines: {node: '>=16.9.0'} + hpack.js@2.1.6: resolution: {integrity: sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==} @@ -5698,6 +5952,9 @@ packages: engines: {node: '>=12'} hasBin: true + http-status-codes@2.3.0: + resolution: {integrity: sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==} + http2-wrapper@2.2.1: resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} engines: {node: '>=10.19.0'} @@ -5960,6 +6217,9 @@ packages: is-promise@4.0.0: resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + is-property@1.0.2: + resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==} + is-reference@1.2.1: resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} @@ -6333,6 +6593,9 @@ packages: resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true + jose@6.2.3: + resolution: {integrity: sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==} + joycon@3.1.1: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} @@ -6618,6 +6881,9 @@ packages: long-timeout@0.1.1: resolution: {integrity: sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==} + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -6632,9 +6898,17 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@11.5.0: + resolution: {integrity: sha512-5YgH9UJd7wVb9hIouI2adWpgqrrICkt070Dnj8EUY1+B4B2P9eRLPAkAAo6NICA7CEhOIeBHl46u9zSNpNu7zA==} + engines: {node: 20 || >=22} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lru.min@1.1.4: + resolution: {integrity: sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==} + engines: {bun: '>=1.0.0', deno: '>=1.30.0', node: '>=8.0.0'} + luxon@3.7.2: resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==} engines: {node: '>=12'} @@ -6819,6 +7093,14 @@ packages: resolution: {integrity: sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==} hasBin: true + mysql2@3.15.3: + resolution: {integrity: sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg==} + engines: {node: '>= 8.0'} + + named-placeholders@1.1.6: + resolution: {integrity: sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==} + engines: {node: '>=8.0.0'} + nanoid@3.3.12: resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -6994,6 +7276,9 @@ packages: obug@2.1.1: resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + ohash@2.0.11: + resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} + on-exit-leak-free@2.1.2: resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} engines: {node: '>=14.0.0'} @@ -7172,6 +7457,43 @@ packages: pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + perfect-debounce@2.1.0: + resolution: {integrity: sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==} + + pg-cloudflare@1.4.0: + resolution: {integrity: sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==} + + pg-connection-string@2.13.0: + resolution: {integrity: sha512-EMnU9E2fSULdsbErBbMaXJvFeD9B4+nPcM3f+4lsiCR0BHLPrLVjv3DbyM2hgQQviKJaTWIRRTjKjWlHg3p2ig==} + + pg-int8@1.0.1: + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} + + pg-pool@3.14.0: + resolution: {integrity: sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==} + peerDependencies: + pg: '>=8.0' + + pg-protocol@1.14.0: + resolution: {integrity: sha512-n5taZ1kO3s9ngDTVxsEznOqCyToTgz0FLuPq0B33COy5pPpuWJpY3/2oRBVETuOgzdqRXfWpM9HIhp2LBBT1BA==} + + pg-types@2.2.0: + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} + + pg@8.21.0: + resolution: {integrity: sha512-AUP1EYJuHraQGsVoCQVIcM7TEJVGtDzxWtGFZd8rds9d+CCXlU5Js1rYgfLNvxy9iJrpHjGrRjoi/3BT9fRyiA==} + engines: {node: '>= 16.0.0'} + peerDependencies: + pg-native: '>=3.0.1' + peerDependenciesMeta: + pg-native: + optional: true + + pgpass@1.0.5: + resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -7226,6 +7548,9 @@ packages: resolution: {integrity: sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==} engines: {node: '>=14.16'} + pkg-types@2.3.1: + resolution: {integrity: sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==} + pkijs@3.4.0: resolution: {integrity: sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw==} engines: {node: '>=16.0.0'} @@ -7473,6 +7798,30 @@ packages: resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} engines: {node: ^10 || ^12 || >=14} + postgres-array@2.0.0: + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} + engines: {node: '>=4'} + + postgres-array@3.0.4: + resolution: {integrity: sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ==} + engines: {node: '>=12'} + + postgres-bytea@1.0.1: + resolution: {integrity: sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==} + engines: {node: '>=0.10.0'} + + postgres-date@1.0.7: + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} + engines: {node: '>=0.10.0'} + + postgres-interval@1.2.0: + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} + engines: {node: '>=0.10.0'} + + postgres@3.4.7: + resolution: {integrity: sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==} + engines: {node: '>=12'} + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -7498,6 +7847,19 @@ packages: resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==} engines: {node: '>=18'} + prisma@7.8.0: + resolution: {integrity: sha512-yfN4yrw7HV9kEJhoy1+jgah0jafEIQsf7uWouSsM8MvJtlubsk+kM7AIBWZ8+GJl74Yj3c+nbYqBkMOxtsZ3Lw==} + engines: {node: ^20.19 || ^22.12 || >=24.0} + hasBin: true + peerDependencies: + better-sqlite3: '>=9.0.0' + typescript: '>=5.4.0' + peerDependenciesMeta: + better-sqlite3: + optional: true + typescript: + optional: true + process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} @@ -7507,6 +7869,9 @@ packages: prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + proper-lockfile@4.1.2: + resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} + proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} @@ -7528,6 +7893,9 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + pure-rand@6.1.0: + resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + pure-rand@7.0.1: resolution: {integrity: sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==} @@ -7564,6 +7932,9 @@ packages: resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} engines: {node: '>= 0.10'} + rc9@3.0.1: + resolution: {integrity: sha512-gMDyleLWVE+i6Sgtc0QbbY6pEKqYs97NGi6isHQPqYlLemPoO8dxQ3uGi0f4NiP98c+jMW6cG1Kx9dDwfvqARQ==} + react-chartjs-2@5.3.1: resolution: {integrity: sha512-h5IPXKg9EXpjoBzUfyWJvllMjG2mQ4EiuHQFhms/AjUm0XSZHhyRy2xVmLXHKrtcdrPO4mnGqRtYoD0vp95A0A==} peerDependencies: @@ -7659,6 +8030,9 @@ packages: resolution: {integrity: sha512-dLsljMd9sqwRkby8zhO1gSg3PnJIBFid8f4CQj/sXx+7cKx+E7u0PKhZ+U4wmhx7EfmtvnA318oVaIkAB1lRJw==} hasBin: true + remeda@2.33.4: + resolution: {integrity: sha512-ygHswjlc/opg2VrtiYvUOPLjxjtdKvjGz1/plDhkG66hjNjFr1xmfrs2ClNFo/E6TyUFiwYNh53bKV26oBoMGQ==} + require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -7719,6 +8093,10 @@ packages: resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} engines: {node: '>=18'} + retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + retry@0.13.1: resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} engines: {node: '>= 4'} @@ -8006,6 +8384,9 @@ packages: resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} engines: {node: '>= 18'} + seq-queue@0.0.5: + resolution: {integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==} + serialize-javascript@7.0.5: resolution: {integrity: sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw==} engines: {node: '>=20.0.0'} @@ -8173,6 +8554,10 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + sqlstring@2.3.3: + resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==} + engines: {node: '>= 0.6'} + stack-utils@2.0.6: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} @@ -8188,6 +8573,9 @@ packages: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + std-env@4.1.0: resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} @@ -8768,6 +9156,14 @@ packages: resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} engines: {node: '>=10.12.0'} + valibot@1.2.0: + resolution: {integrity: sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==} + peerDependencies: + typescript: '>=5' + peerDependenciesMeta: + typescript: + optional: true + varint@6.0.0: resolution: {integrity: sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==} @@ -9071,6 +9467,10 @@ packages: xmlchars@2.2.0: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -9123,6 +9523,9 @@ packages: resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} engines: {node: '>=18'} + zeptomatch@2.1.0: + resolution: {integrity: sha512-KiGErG2J0G82LSpniV0CtIzjlJ10E04j02VOudJsPyPwNZgGnRKQy7I1R7GMyg/QswnE4l7ohSGrQbQbjXPPDA==} + zod@4.4.3: resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} @@ -10180,6 +10583,16 @@ snapshots: '@discoveryjs/json-ext@0.5.7': {} + '@electric-sql/pglite-socket@0.1.1(@electric-sql/pglite@0.4.1)': + dependencies: + '@electric-sql/pglite': 0.4.1 + + '@electric-sql/pglite-tools@0.3.1(@electric-sql/pglite@0.4.1)': + dependencies: + '@electric-sql/pglite': 0.4.1 + + '@electric-sql/pglite@0.4.1': {} + '@emnapi/core@1.10.0': dependencies: '@emnapi/wasi-threads': 1.2.1 @@ -10280,6 +10693,10 @@ snapshots: '@fortawesome/fontawesome-svg-core': 7.2.0 react: 19.2.6 + '@hono/node-server@1.19.11(hono@4.12.23)': + dependencies: + hono: 4.12.23 + '@hookform/resolvers@5.4.0(react-hook-form@7.76.1(react@19.2.6))': dependencies: '@standard-schema/utils': 0.3.0 @@ -11143,7 +11560,7 @@ snapshots: transitivePeerDependencies: - chokidar - '@nestjs/terminus@11.1.1(@nestjs/common@11.1.24(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@11.1.24)(reflect-metadata@0.1.14)(rxjs@7.8.1)': + '@nestjs/terminus@11.1.1(@nestjs/common@11.1.24(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@11.1.24)(@prisma/client@7.8.0(prisma@7.8.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(magicast@0.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@5.9.3))(typescript@5.9.3))(reflect-metadata@0.1.14)(rxjs@7.8.1)': dependencies: '@nestjs/common': 11.1.24(reflect-metadata@0.1.14)(rxjs@7.8.1) '@nestjs/core': 11.1.24(@nestjs/common@11.1.24(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@11.1.24)(reflect-metadata@0.1.14)(rxjs@7.8.1) @@ -11151,6 +11568,8 @@ snapshots: check-disk-space: 3.4.0 reflect-metadata: 0.1.14 rxjs: 7.8.1 + optionalDependencies: + '@prisma/client': 7.8.0(prisma@7.8.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(magicast@0.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@5.9.3))(typescript@5.9.3) '@nestjs/testing@11.1.24(@nestjs/common@11.1.24(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@11.1.24)(@nestjs/platform-express@11.1.24)': dependencies: @@ -11930,6 +12349,161 @@ snapshots: '@polka/url@1.0.0-next.29': {} + '@prisma/adapter-pg@7.8.0': + dependencies: + '@prisma/driver-adapter-utils': 7.8.0 + '@types/pg': 8.20.0 + pg: 8.21.0 + postgres-array: 3.0.4 + transitivePeerDependencies: + - pg-native + + '@prisma/client-runtime-utils@7.8.0': {} + + '@prisma/client@7.8.0(prisma@7.8.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(magicast@0.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@5.9.3))(typescript@5.9.3)': + dependencies: + '@prisma/client-runtime-utils': 7.8.0 + optionalDependencies: + prisma: 7.8.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(magicast@0.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + typescript: 5.9.3 + + '@prisma/config@7.8.0(magicast@0.5.3)': + dependencies: + c12: 3.3.4(magicast@0.5.3) + deepmerge-ts: 7.1.5 + effect: 3.20.0 + empathic: 2.0.0 + transitivePeerDependencies: + - magicast + + '@prisma/debug@7.2.0': {} + + '@prisma/debug@7.8.0': {} + + '@prisma/dev@0.24.3(typescript@5.9.3)': + dependencies: + '@electric-sql/pglite': 0.4.1 + '@electric-sql/pglite-socket': 0.1.1(@electric-sql/pglite@0.4.1) + '@electric-sql/pglite-tools': 0.3.1(@electric-sql/pglite@0.4.1) + '@hono/node-server': 1.19.11(hono@4.12.23) + '@prisma/get-platform': 7.2.0 + '@prisma/query-plan-executor': 7.2.0 + '@prisma/streams-local': 0.1.2 + foreground-child: 3.3.1 + get-port-please: 3.2.0 + hono: 4.12.23 + http-status-codes: 2.3.0 + pathe: 2.0.3 + proper-lockfile: 4.1.2 + remeda: 2.33.4 + std-env: 3.10.0 + valibot: 1.2.0(typescript@5.9.3) + zeptomatch: 2.1.0 + transitivePeerDependencies: + - typescript + + '@prisma/driver-adapter-utils@7.8.0': + dependencies: + '@prisma/debug': 7.8.0 + + '@prisma/engines-version@7.8.0-6.3c6e192761c0362d496ed980de936e2f3cebcd3a': {} + + '@prisma/engines@7.8.0': + dependencies: + '@prisma/debug': 7.8.0 + '@prisma/engines-version': 7.8.0-6.3c6e192761c0362d496ed980de936e2f3cebcd3a + '@prisma/fetch-engine': 7.8.0 + '@prisma/get-platform': 7.8.0 + + '@prisma/fetch-engine@7.8.0': + dependencies: + '@prisma/debug': 7.8.0 + '@prisma/engines-version': 7.8.0-6.3c6e192761c0362d496ed980de936e2f3cebcd3a + '@prisma/get-platform': 7.8.0 + + '@prisma/get-platform@7.2.0': + dependencies: + '@prisma/debug': 7.2.0 + + '@prisma/get-platform@7.8.0': + dependencies: + '@prisma/debug': 7.8.0 + + '@prisma/query-plan-executor@7.2.0': {} + + '@prisma/streams-local@0.1.2': + dependencies: + ajv: 8.20.0 + better-result: 2.9.2 + env-paths: 3.0.0 + proper-lockfile: 4.1.2 + + '@prisma/studio-core@0.27.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + dependencies: + '@radix-ui/react-toggle': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@types/react': 19.2.15 + chart.js: 4.5.1 + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + transitivePeerDependencies: + - '@types/react-dom' + + '@radix-ui/primitive@1.1.3': {} + + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.15)(react@19.2.6)': + dependencies: + react: 19.2.6 + optionalDependencies: + '@types/react': 19.2.15 + + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + dependencies: + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.15)(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + optionalDependencies: + '@types/react': 19.2.15 + '@types/react-dom': 19.2.3(@types/react@19.2.15) + + '@radix-ui/react-slot@1.2.3(@types/react@19.2.15)(react@19.2.6)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.15)(react@19.2.6) + react: 19.2.6 + optionalDependencies: + '@types/react': 19.2.15 + + '@radix-ui/react-toggle@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.15)(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + optionalDependencies: + '@types/react': 19.2.15 + '@types/react-dom': 19.2.3(@types/react@19.2.15) + + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.15)(react@19.2.6)': + dependencies: + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.15)(react@19.2.6) + react: 19.2.6 + optionalDependencies: + '@types/react': 19.2.15 + + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.15)(react@19.2.6)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.15)(react@19.2.6) + react: 19.2.6 + optionalDependencies: + '@types/react': 19.2.15 + + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.15)(react@19.2.6)': + dependencies: + react: 19.2.6 + optionalDependencies: + '@types/react': 19.2.15 + '@rc-component/async-validator@5.1.0': dependencies: '@babel/runtime': 7.29.7 @@ -12952,6 +13526,12 @@ snapshots: '@types/parse-json@4.0.2': {} + '@types/pg@8.20.0': + dependencies: + '@types/node': 24.12.4 + pg-protocol: 1.14.0 + pg-types: 2.2.0 + '@types/qs@6.15.1': {} '@types/range-parser@1.2.7': {} @@ -13728,6 +14308,8 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 + aws-ssl-profiles@1.1.2: {} + axe-core@4.11.4: {} axios@1.16.0: @@ -13894,6 +14476,8 @@ snapshots: batch@0.6.1: {} + better-result@2.9.2: {} + big.js@5.2.2: {} binary-extensions@2.3.0: {} @@ -14020,6 +14604,23 @@ snapshots: bytestreamjs@2.0.1: {} + c12@3.3.4(magicast@0.5.3): + dependencies: + chokidar: 5.0.0 + confbox: 0.2.4 + defu: 6.1.7 + dotenv: 17.4.1 + exsolve: 1.0.8 + giget: 3.2.0 + jiti: 2.7.0 + ohash: 2.0.11 + pathe: 2.0.3 + perfect-debounce: 2.1.0 + pkg-types: 2.3.1 + rc9: 3.0.1 + optionalDependencies: + magicast: 0.5.3 + cacheable-lookup@7.0.0: {} cacheable-request@13.0.19: @@ -14226,6 +14827,8 @@ snapshots: dependencies: source-map: 0.6.1 + confbox@0.2.4: {} + confusing-browser-globals@1.0.11: {} connect-history-api-fallback@2.0.0: {} @@ -14505,6 +15108,8 @@ snapshots: deep-is@0.1.4: {} + deepmerge-ts@7.1.5: {} + deepmerge@4.3.1: {} default-browser-id@5.0.1: {} @@ -14534,14 +15139,20 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 + defu@6.1.7: {} + delayed-stream@1.0.0: {} + denque@2.1.0: {} + depd@1.1.2: {} depd@2.0.0: {} dequal@2.0.3: {} + destr@2.0.5: {} + destroy@1.2.0: {} detect-libc@2.1.2: {} @@ -14615,6 +15226,11 @@ snapshots: ee-first@1.1.1: {} + effect@3.20.0: + dependencies: + '@standard-schema/spec': 1.1.0 + fast-check: 3.23.2 + ejs@5.0.1: {} electron-to-chromium@1.5.361: {} @@ -14629,6 +15245,8 @@ snapshots: emojis-list@3.0.0: {} + empathic@2.0.0: {} + encodeurl@2.0.0: {} encoding@0.1.13: @@ -14654,6 +15272,8 @@ snapshots: env-paths@2.2.1: {} + env-paths@3.0.0: {} + envinfo@7.21.0: {} environment@1.1.0: {} @@ -15123,6 +15743,8 @@ snapshots: transitivePeerDependencies: - supports-color + exsolve@1.0.8: {} + ext-list@2.2.2: dependencies: mime-db: 1.54.0 @@ -15132,6 +15754,10 @@ snapshots: ext-list: 2.2.2 sort-keys-length: 1.0.1 + fast-check@3.23.2: + dependencies: + pure-rand: 6.1.0 + fast-copy@4.0.3: {} fast-deep-equal@3.1.3: {} @@ -15345,6 +15971,10 @@ snapshots: functions-have-names@1.2.3: {} + generate-function@2.3.1: + dependencies: + is-property: 1.0.2 + generator-function@2.0.1: {} generic-names@4.0.0: @@ -15372,6 +16002,8 @@ snapshots: get-package-type@0.1.0: {} + get-port-please@3.2.0: {} + get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 @@ -15394,6 +16026,8 @@ snapshots: get-them-args@1.3.2: {} + giget@3.2.0: {} + git-raw-commits@5.0.1(conventional-commits-parser@6.4.0): dependencies: '@conventional-changelog/git-client': 2.7.0(conventional-commits-parser@6.4.0) @@ -15492,6 +16126,10 @@ snapshots: graceful-fs@4.2.11: {} + grammex@3.1.12: {} + + graphmatch@1.1.1: {} + handle-thing@2.0.1: {} handlebars@4.7.9: @@ -15541,6 +16179,8 @@ snapshots: dependencies: parse-passwd: 1.0.0 + hono@4.12.23: {} + hpack.js@2.1.6: dependencies: inherits: 2.0.4 @@ -15634,6 +16274,8 @@ snapshots: - debug - supports-color + http-status-codes@2.3.0: {} + http2-wrapper@2.2.1: dependencies: quick-lru: 5.1.1 @@ -15845,6 +16487,8 @@ snapshots: is-promise@4.0.0: {} + is-property@1.0.2: {} + is-reference@1.2.1: dependencies: '@types/estree': 1.0.9 @@ -16572,6 +17216,8 @@ snapshots: jiti@2.7.0: {} + jose@6.2.3: {} + joycon@3.1.1: {} js-tokens@10.0.0: {} @@ -16846,6 +17492,8 @@ snapshots: long-timeout@0.1.1: {} + long@5.3.2: {} + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -16858,10 +17506,14 @@ snapshots: lru-cache@10.4.3: {} + lru-cache@11.5.0: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 + lru.min@1.1.4: {} + luxon@3.7.2: {} lz-string@1.5.0: {} @@ -17025,6 +17677,22 @@ snapshots: dns-packet: 5.6.1 thunky: 1.1.0 + mysql2@3.15.3: + dependencies: + aws-ssl-profiles: 1.1.2 + denque: 2.1.0 + generate-function: 2.3.1 + iconv-lite: 0.7.2 + long: 5.3.2 + lru.min: 1.1.4 + named-placeholders: 1.1.6 + seq-queue: 0.0.5 + sqlstring: 2.3.3 + + named-placeholders@1.1.6: + dependencies: + lru.min: 1.1.4 + nanoid@3.3.12: {} napi-postinstall@0.3.4: {} @@ -17298,6 +17966,8 @@ snapshots: obug@2.1.1: {} + ohash@2.0.11: {} + on-exit-leak-free@2.1.2: {} on-finished@2.4.1: @@ -17495,6 +18165,43 @@ snapshots: pend@1.2.0: {} + perfect-debounce@2.1.0: {} + + pg-cloudflare@1.4.0: + optional: true + + pg-connection-string@2.13.0: {} + + pg-int8@1.0.1: {} + + pg-pool@3.14.0(pg@8.21.0): + dependencies: + pg: 8.21.0 + + pg-protocol@1.14.0: {} + + pg-types@2.2.0: + dependencies: + pg-int8: 1.0.1 + postgres-array: 2.0.0 + postgres-bytea: 1.0.1 + postgres-date: 1.0.7 + postgres-interval: 1.2.0 + + pg@8.21.0: + dependencies: + pg-connection-string: 2.13.0 + pg-pool: 3.14.0(pg@8.21.0) + pg-protocol: 1.14.0 + pg-types: 2.2.0 + pgpass: 1.0.5 + optionalDependencies: + pg-cloudflare: 1.4.0 + + pgpass@1.0.5: + dependencies: + split2: 4.2.0 + picocolors@1.1.1: {} picomatch@2.3.2: {} @@ -17567,6 +18274,12 @@ snapshots: dependencies: find-up: 6.3.0 + pkg-types@2.3.1: + dependencies: + confbox: 0.2.4 + exsolve: 1.0.8 + pathe: 2.0.3 + pkijs@3.4.0: dependencies: '@noble/hashes': 1.4.0 @@ -17811,6 +18524,20 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + postgres-array@2.0.0: {} + + postgres-array@3.0.4: {} + + postgres-bytea@1.0.1: {} + + postgres-date@1.0.7: {} + + postgres-interval@1.2.0: + dependencies: + xtend: 4.0.2 + + postgres@3.4.7: {} + prelude-ls@1.2.1: {} prettier@3.8.3: {} @@ -17838,6 +18565,23 @@ snapshots: dependencies: parse-ms: 4.0.0 + prisma@7.8.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(magicast@0.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@5.9.3): + dependencies: + '@prisma/config': 7.8.0(magicast@0.5.3) + '@prisma/dev': 0.24.3(typescript@5.9.3) + '@prisma/engines': 7.8.0 + '@prisma/studio-core': 0.27.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + mysql2: 3.15.3 + postgres: 3.4.7 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + - magicast + - react + - react-dom + process-nextick-args@2.0.1: {} process-warning@5.0.0: {} @@ -17848,6 +18592,12 @@ snapshots: object-assign: 4.1.1 react-is: 16.13.1 + proper-lockfile@4.1.2: + dependencies: + graceful-fs: 4.2.11 + retry: 0.12.0 + signal-exit: 3.0.7 + proxy-addr@2.0.7: dependencies: forwarded: 0.2.0 @@ -17869,6 +18619,8 @@ snapshots: punycode@2.3.1: {} + pure-rand@6.1.0: {} + pure-rand@7.0.1: {} pvtsutils@1.3.6: @@ -17903,6 +18655,11 @@ snapshots: iconv-lite: 0.7.2 unpipe: 1.0.0 + rc9@3.0.1: + dependencies: + defu: 6.1.7 + destr: 2.0.5 + react-chartjs-2@5.3.1(chart.js@4.5.1)(react@19.2.6): dependencies: chart.js: 4.5.1 @@ -18006,6 +18763,8 @@ snapshots: dependencies: jsesc: 3.1.0 + remeda@2.33.4: {} + require-directory@2.1.1: {} require-from-string@2.0.2: {} @@ -18065,6 +18824,8 @@ snapshots: onetime: 7.0.0 signal-exit: 4.1.0 + retry@0.12.0: {} + retry@0.13.1: {} rfdc@1.4.1: {} @@ -18378,6 +19139,8 @@ snapshots: transitivePeerDependencies: - supports-color + seq-queue@0.0.5: {} + serialize-javascript@7.0.5: {} seroval-plugins@1.5.4(seroval@1.5.4): @@ -18585,6 +19348,8 @@ snapshots: sprintf-js@1.0.3: {} + sqlstring@2.3.3: {} + stack-utils@2.0.6: dependencies: escape-string-regexp: 2.0.0 @@ -18595,6 +19360,8 @@ snapshots: statuses@2.0.2: {} + std-env@3.10.0: {} + std-env@4.1.0: {} stop-iteration-iterator@1.1.0: @@ -19191,6 +19958,10 @@ snapshots: '@types/istanbul-lib-coverage': 2.0.6 convert-source-map: 2.0.0 + valibot@1.2.0(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 + varint@6.0.0: {} vary@1.1.2: {} @@ -19524,6 +20295,8 @@ snapshots: xmlchars@2.2.0: {} + xtend@4.0.2: {} + y18n@5.0.8: {} yallist@3.1.1: {} @@ -19568,6 +20341,11 @@ snapshots: yoctocolors@2.1.2: {} + zeptomatch@2.1.0: + dependencies: + grammex: 3.1.12 + graphmatch: 1.1.1 + zod@4.4.3: {} zustand@5.0.13(@types/react@19.2.15)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)): diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 264e72f..86cdf4d 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -6,9 +6,11 @@ packages: allowBuilds: '@nestjs/core': true '@parcel/watcher': true + '@prisma/engines': true '@swc/core': true less: true nx: true + prisma: true unrs-resolver: true catalog: