@vritti/api-sdk 0.1.8 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/auth/auth-config.module.ts","../src/request/request.module.ts","../src/request/services/request.service.ts","../src/config/index.ts","../src/auth/guards/vritti-auth.guard.ts","../src/auth/decorators/skip-csrf.decorator.ts","../src/database/services/primary-database.service.ts","../src/database/constants.ts","../src/auth/utils/token-hash.util.ts","../src/auth/decorators/onboarding.decorator.ts","../src/auth/decorators/public.decorator.ts","../src/auth/decorators/user-id.decorator.ts","../src/auth/guards/sse-auth.guard.ts","../src/database/database.module.ts","../src/database/interceptors/message-tenant-context.interceptor.ts","../src/database/services/tenant-context.service.ts","../src/database/interceptors/tenant-context.interceptor.ts","../src/database/services/tenant-database.service.ts","../src/database/decorators/tenant.decorator.ts","../src/database/repositories/primary-base.repository.ts","../src/database/repositories/tenant-base.repository.ts","../src/exceptions/bad-gateway.exception.ts","../src/exceptions/base-field.exception.ts","../src/exceptions/bad-request.exception.ts","../src/exceptions/conflict.exception.ts","../src/exceptions/forbidden.exception.ts","../src/exceptions/gone.exception.ts","../src/exceptions/internal-server-error.exception.ts","../src/exceptions/method-not-allowed.exception.ts","../src/exceptions/not-acceptable.exception.ts","../src/exceptions/not-found.exception.ts","../src/exceptions/not-implemented.exception.ts","../src/exceptions/payload-too-large.exception.ts","../src/exceptions/request-timeout.exception.ts","../src/exceptions/service-unavailable.exception.ts","../src/exceptions/too-many-requests.exception.ts","../src/exceptions/unauthorized.exception.ts","../src/exceptions/unprocessable-entity.exception.ts","../src/exceptions/unsupported-media-type.exception.ts","../src/exceptions/validation.exception.ts","../src/filters/http-exception.filter.ts","../src/utils/phone.utils.ts","../src/logger/interceptors/http-logger.interceptor.ts","../src/logger/services/logger.service.ts","../src/logger/utils/index.ts","../src/logger/logger.module.ts","../src/logger/middleware/correlation-id.middleware.ts"],"sourcesContent":["// Config system\n\n// Core modules\nexport { AuthConfigModule } from './auth/auth-config.module';\nexport { Onboarding } from './auth/decorators/onboarding.decorator';\nexport { Public } from './auth/decorators/public.decorator';\nexport { UserId } from './auth/decorators/user-id.decorator';\n// Guards\nexport { VrittiAuthGuard } from './auth/guards/vritti-auth.guard';\nexport { SseAuthGuard } from './auth/guards/sse-auth.guard';\n// Token hash utilities\nexport { hashToken, verifyTokenHash } from './auth/utils/token-hash.util';\nexport {\n type ApiSdkConfig,\n type CookieConfig,\n configureApiSdk,\n defineConfig,\n type GuardConfig,\n getConfig,\n getJwtExpiry,\n getRefreshCookieOptions,\n type JwtConfig,\n resetConfig,\n} from './config';\nexport { DatabaseModule } from './database/database.module';\n// Decorators\nexport { Tenant } from './database/decorators/tenant.decorator';\n// Interfaces\nexport * from './database/interfaces';\n// Repositories\nexport { PrimaryBaseRepository } from './database/repositories/primary-base.repository';\nexport { TenantBaseRepository } from './database/repositories/tenant-base.repository';\n// Schema Registry (for module augmentation)\nexport type {\n RegisteredSchema,\n TypedDrizzleClient,\n} from './database/schema.registry';\nexport { PrimaryDatabaseService } from './database/services/primary-database.service';\n// Services\nexport { TenantContextService } from './database/services/tenant-context.service';\nexport { TenantDatabaseService } from './database/services/tenant-database.service';\n// Exceptions\nexport * from './exceptions';\n// RFC 7807 Filters (includes HttpExceptionFilter)\nexport * from './filters';\n// Auth decorators (SkipCsrf for webhook endpoints)\nexport { SkipCsrf, SKIP_CSRF_KEY } from './auth/decorators/skip-csrf.decorator';\n// Phone utilities\nexport { extractCountryFromPhone, normalizePhoneNumber } from './utils/phone.utils';\n// Logger utilities\nexport * from './logger';\n// RFC 7807 Types (using named exports to avoid conflicts)\nexport type { ApiErrorResponse, ProblemDetails } from './types';\n","import { type DynamicModule, Global, Module } from '@nestjs/common';\nimport { ConfigModule, ConfigService } from '@nestjs/config';\nimport { APP_GUARD, Reflector } from '@nestjs/core';\nimport { JwtModule } from '@nestjs/jwt';\nimport { RequestModule } from '../request/request.module';\nimport { VrittiAuthGuard } from './guards/vritti-auth.guard';\n\n/**\n * Global authentication configuration module\n *\n * This module provides:\n * - JWT token verification (JwtModule)\n * - Global authentication guard (VrittiAuthGuard)\n * - Support for @Public and @Onboarding decorators\n *\n * ## Features:\n * - Automatically applies VrittiAuthGuard to all routes\n * - Configures JwtModule with JWT_SECRET from environment\n * - Exports JwtModule for token generation in services\n *\n * ## Usage in Application:\n *\n * @example\n * // In app.module.ts\n * @Module({\n * imports: [\n * ConfigModule.forRoot({ isGlobal: true }),\n *\n * // Auth configuration (global guard + JWT)\n * AuthConfigModule.forRootAsync(),\n *\n * // Database configuration (Gateway mode)\n * DatabaseModule.forServer({\n * useFactory: (config: ConfigService) => ({\n * primaryDb: {\n * host: config.get('PRIMARY_DB_HOST'),\n * // ... other config\n * },\n * prismaClientConstructor: PrismaClient,\n * }),\n * inject: [ConfigService],\n * }),\n * ],\n * })\n * export class AppModule {}\n *\n * ## Environment Variables Required:\n * - JWT_SECRET: Secret key to verify access tokens (required)\n * - JWT_REFRESH_SECRET: Secret key for refresh tokens (optional, falls back to JWT_SECRET)\n *\n * ## Bypass Authentication:\n *\n * @example\n * // Skip authentication on specific endpoints\n * @Public()\n * @Post('auth/login')\n * async login() { ... }\n *\n * @example\n * // Onboarding endpoints (only accept onboarding tokens)\n * @Onboarding()\n * @Post('onboarding/verify-email')\n * async verifyEmail(@Request() req) {\n * const userId = req.user.id; // Available from guard\n * ...\n * }\n */\n@Global()\n@Module({})\nexport class AuthConfigModule {\n /**\n * Register the auth module with async configuration\n *\n * This method:\n * 1. Configures JwtModule with JWT_SECRET from ConfigService\n * 2. Provides VrittiAuthGuard globally (applies to all routes)\n * 3. Exports JwtModule for use in other modules (e.g., for signing tokens)\n *\n * @returns Dynamic module configuration\n */\n static forRootAsync(): DynamicModule {\n return {\n module: AuthConfigModule,\n imports: [\n ConfigModule,\n RequestModule,\n JwtModule.registerAsync({\n imports: [ConfigModule],\n inject: [ConfigService],\n useFactory: (config: ConfigService) => ({\n secret: config.get<string>('JWT_SECRET'),\n signOptions: {\n algorithm: 'HS256',\n },\n }),\n }),\n ],\n providers: [\n // Required for external packages - NestJS global Reflector not available\n {\n provide: Reflector,\n useClass: Reflector,\n },\n {\n provide: APP_GUARD,\n useClass: VrittiAuthGuard,\n },\n ],\n exports: [\n JwtModule, // Export for use in other modules (e.g., generating tokens)\n ],\n };\n }\n}\n","import { Global, Module } from '@nestjs/common';\nimport { RequestService } from './services/request.service';\n\n@Global()\n@Module({\n providers: [RequestService],\n exports: [RequestService],\n})\nexport class RequestModule {}\n","import { Inject, Injectable, Scope } from '@nestjs/common';\nimport { REQUEST } from '@nestjs/core';\nimport type { FastifyRequest } from 'fastify';\nimport { getConfig } from '../../config';\n\n@Injectable({ scope: Scope.REQUEST })\nexport class RequestService {\n constructor(@Inject(REQUEST) private readonly request: FastifyRequest) {}\n\n /**\n * Extract tenant identifier from request headers\n * Priority: x-tenant-id > x-subdomain\n * @returns Tenant identifier or null if not found\n */\n getTenantIdentifier(): string | null {\n const getHeader = (key: string) => {\n const value = this.request.headers?.[key];\n return Array.isArray(value) ? value[0] : value;\n };\n\n return getHeader('x-tenant-id') || getHeader('x-subdomain') || null;\n }\n\n /**\n * Extract access token from Authorization header\n * Format: \"Bearer <token>\"\n * @returns Access token or null if not found\n */\n getAccessToken(): string | null {\n const authHeader = this.request.headers?.authorization;\n if (!authHeader) {\n return null;\n }\n const [type, token] = authHeader.split(' ') ?? [];\n return type === 'Bearer' && token ? token : null;\n }\n\n /**\n * Extract refresh token from httpOnly cookie\n * Cookie name is configurable via api-sdk config\n * @returns Refresh token or null if not found\n */\n getRefreshToken(): string | null {\n try {\n const cookies = (this.request as unknown as { cookies?: Record<string, string> }).cookies;\n if (cookies && typeof cookies === 'object') {\n const config = getConfig();\n const refreshToken = cookies[config.cookie.refreshCookieName];\n if (refreshToken) {\n return refreshToken;\n }\n }\n return null;\n } catch (_error: unknown) {\n return null;\n }\n }\n\n /**\n * Get a specific header value\n * @param key Header key\n * @returns Header value (string, array, or undefined)\n */\n getHeader(key: string): string | string[] | undefined {\n return this.request.headers?.[key];\n }\n\n /**\n * Get all headers\n * @returns Record of all headers\n */\n getAllHeaders(): FastifyRequest['headers'] {\n return this.request.headers || {};\n }\n}\n","/**\n * api-sdk Configuration System\n *\n * Similar to quantum-ui's config pattern - provides a type-safe configuration system\n *\n * @example\n * ```typescript\n * // In vritti-api-nexus/src/main.ts\n * import { configureApiSdk } from '@vritti/api-sdk';\n *\n * configureApiSdk({\n * cookie: {\n * refreshCookieName: 'vritti_refresh',\n * refreshCookieMaxAge: 30 * 24 * 60 * 60 * 1000, // 30 days\n * },\n * jwt: {\n * accessTokenExpiry: '15m',\n * refreshTokenExpiry: '30d',\n * validateTokenBinding: true,\n * },\n * guard: {\n * tenantHeaderName: 'x-tenant-id',\n * },\n * });\n * ```\n */\n\n/**\n * Cookie configuration options\n */\nexport interface CookieConfig {\n /**\n * The name of the httpOnly cookie containing the refresh token\n * @default 'vritti_refresh'\n */\n refreshCookieName: string;\n\n /**\n * Max age of the refresh cookie in milliseconds\n * @default 2592000000 (30 days)\n */\n refreshCookieMaxAge: number;\n\n /**\n * Cookie path\n * @default '/'\n */\n refreshCookiePath: string;\n\n /**\n * Whether the cookie is secure (HTTPS only)\n * @default true in production\n */\n refreshCookieSecure: boolean;\n\n /**\n * SameSite attribute for the cookie\n * @default 'strict'\n */\n refreshCookieSameSite: 'strict' | 'lax' | 'none';\n\n /**\n * Cookie domain (e.g., 'localhost' for dev, '.vritti.cloud' for prod)\n * Required for cross-subdomain auth (e.g., cloud.localhost accessing localhost API)\n * @default undefined (uses request domain)\n */\n refreshCookieDomain?: string;\n}\n\n/**\n * JWT token configuration options\n */\nexport interface JwtConfig {\n /**\n * Access token expiry time\n * @default '15m'\n */\n accessTokenExpiry: string;\n\n /**\n * Refresh token expiry time\n * @default '30d'\n */\n refreshTokenExpiry: string;\n\n /**\n * Onboarding token expiry time\n * @default '24h'\n */\n onboardingTokenExpiry: string;\n\n /**\n * Whether to validate refresh token binding (hash in access token)\n * @default true\n */\n validateTokenBinding: boolean;\n}\n\n/**\n * Auth guard configuration options\n */\nexport interface GuardConfig {\n /**\n * Header name for tenant ID\n * @default 'x-tenant-id'\n */\n tenantHeaderName: string;\n\n /**\n * Header name for authorization\n * @default 'authorization'\n */\n authHeaderName: string;\n\n /**\n * Token prefix (e.g., 'Bearer')\n * @default 'Bearer'\n */\n tokenPrefix: string;\n}\n\n/**\n * Complete api-sdk configuration interface\n */\nexport interface ApiSdkConfig {\n /**\n * Cookie configuration\n */\n cookie?: Partial<CookieConfig>;\n\n /**\n * JWT token configuration\n */\n jwt?: Partial<JwtConfig>;\n\n /**\n * Auth guard configuration\n */\n guard?: Partial<GuardConfig>;\n}\n\n/**\n * Full configuration type with all properties required\n */\nexport interface FullConfig {\n cookie: CookieConfig;\n jwt: JwtConfig;\n guard: GuardConfig;\n}\n\n/**\n * Default configuration values\n */\nconst defaultConfig: FullConfig = {\n cookie: {\n refreshCookieName: 'vritti_refresh',\n refreshCookieMaxAge: 30 * 24 * 60 * 60 * 1000, // 30 days\n refreshCookiePath: '/',\n refreshCookieSecure: process.env.NODE_ENV === 'production',\n refreshCookieSameSite: 'strict',\n refreshCookieDomain: 'localhost',\n },\n jwt: {\n accessTokenExpiry: '15m',\n refreshTokenExpiry: '30d',\n onboardingTokenExpiry: '24h',\n validateTokenBinding: true,\n },\n guard: {\n tenantHeaderName: 'x-tenant-id',\n authHeaderName: 'authorization',\n tokenPrefix: 'Bearer',\n },\n};\n\n/**\n * Current active configuration\n */\nlet currentConfig: FullConfig = { ...defaultConfig };\n\n/**\n * Helper function to define configuration with type safety\n * Similar to Tailwind's defineConfig()\n */\nexport function defineConfig(config: ApiSdkConfig): ApiSdkConfig {\n return config;\n}\n\n/**\n * Configure api-sdk with user settings\n * This should be called once in the application's bootstrap (main.ts)\n */\nexport function configureApiSdk(userConfig: ApiSdkConfig): void {\n currentConfig = {\n cookie: {\n ...defaultConfig.cookie,\n ...(userConfig.cookie || {}),\n },\n jwt: {\n ...defaultConfig.jwt,\n ...(userConfig.jwt || {}),\n },\n guard: {\n ...defaultConfig.guard,\n ...(userConfig.guard || {}),\n },\n };\n}\n\n/**\n * Get the current configuration\n */\nexport function getConfig(): FullConfig {\n return currentConfig;\n}\n\n/**\n * Reset configuration to defaults (for testing)\n */\nexport function resetConfig(): void {\n currentConfig = { ...defaultConfig };\n}\n\n/**\n * Get refresh cookie options (convenience method)\n */\nexport function getRefreshCookieOptions() {\n const options: Record<string, unknown> = {\n httpOnly: true,\n secure: currentConfig.cookie.refreshCookieSecure,\n sameSite: currentConfig.cookie.refreshCookieSameSite,\n path: currentConfig.cookie.refreshCookiePath,\n maxAge: currentConfig.cookie.refreshCookieMaxAge,\n };\n\n // Only add domain if specified (needed for cross-subdomain auth like cloud.localhost)\n if (currentConfig.cookie.refreshCookieDomain) {\n options.domain = currentConfig.cookie.refreshCookieDomain;\n }\n\n return options;\n}\n\n/**\n * Get JWT expiry settings (convenience method)\n */\nexport function getJwtExpiry() {\n return {\n access: currentConfig.jwt.accessTokenExpiry,\n refresh: currentConfig.jwt.refreshTokenExpiry,\n onboarding: currentConfig.jwt.onboardingTokenExpiry,\n };\n}\n","import {\n type CanActivate,\n type ExecutionContext,\n ForbiddenException,\n Injectable,\n Logger,\n Scope,\n UnauthorizedException,\n} from '@nestjs/common';\nimport { ConfigService } from '@nestjs/config';\nimport { Reflector } from '@nestjs/core';\nimport { JwtService } from '@nestjs/jwt';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport { SKIP_CSRF_KEY } from '../decorators/skip-csrf.decorator';\nimport { getConfig } from '../../config';\nimport { PrimaryDatabaseService } from '../../database/services/primary-database.service';\nimport { RequestService } from '../../request/services/request.service';\nimport { verifyTokenHash } from '../utils/token-hash.util';\n\n// Type for decoded JWT token\ninterface DecodedToken {\n userId?: string;\n type?: string;\n refreshTokenHash?: string;\n exp?: number;\n nbf?: number;\n iat?: number;\n [key: string]: unknown;\n}\n\n/**\n * Vritti Authentication Guard - Validates JWT access tokens and tenant context\n *\n * This guard performs access token validation and attaches user data to request.\n * NOTE: Refresh tokens are NOT validated here - they are only validated in\n * /auth/token and /auth/refresh endpoints (session.service.ts).\n *\n * Validation Flow:\n * 1. Checks if endpoint is marked with @Public() decorator → skip all validation\n * 2. Checks if endpoint is marked with @Onboarding() decorator:\n * - Requires token type='onboarding'\n * - Validates JWT signature and expiry only\n * - Skips tenant validation\n * - Attaches user data to request.user\n * 3. For regular endpoints (no decorator):\n * - Rejects tokens with type='onboarding'\n * - Validates access token (JWT signature, expiry, nbf)\n * - Validates tenant exists and is ACTIVE\n * - Attaches user data to request.user\n *\n * Token Format:\n * - Access Token: \"Authorization: Bearer <jwt_token>\"\n *\n * Token Types:\n * - type='onboarding': Limited access during registration flow (@Onboarding endpoints only)\n * - type='access': Full access to authenticated endpoints\n *\n * Environment Variables Required:\n * - JWT_SECRET: Secret key to verify access tokens (required)\n *\n * Error Responses:\n * - 401: Invalid/expired access token\n * - 401: Tenant not found or inactive\n * - 401: Tenant identifier not found\n * - 401: Token type mismatch (onboarding token on regular endpoint or vice versa)\n *\n * @example\n * // Automatically registered by AuthConfigModule.forRootAsync()\n * // No manual registration needed\n * //\n * // Internal registration uses useExisting pattern:\n * // providers: [\n * // VrittiAuthGuard,\n * // {\n * // provide: APP_GUARD,\n * // useExisting: VrittiAuthGuard,\n * // },\n * // ]\n *\n * @example\n * // Bypass guard with @Public() decorator\n * @Public()\n * @Post('auth/login')\n * async login(@Body() dto: LoginDto) { ... }\n *\n * @example\n * // Restrict to onboarding tokens with @Onboarding() decorator\n * @Onboarding()\n * @Post('onboarding/verify-email')\n * async verifyEmail(@Request() req) {\n * const userId = req.user.id; // Available from guard\n * ...\n * }\n */\n@Injectable({ scope: Scope.REQUEST })\nexport class VrittiAuthGuard implements CanActivate {\n private readonly logger = new Logger(VrittiAuthGuard.name);\n\n constructor(\n private readonly reflector: Reflector,\n readonly _configService: ConfigService,\n private readonly jwtService: JwtService,\n private readonly primaryDatabase: PrimaryDatabaseService,\n private readonly requestService: RequestService,\n ) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = context.switchToHttp().getRequest<FastifyRequest>();\n const reply = context.switchToHttp().getResponse<FastifyReply>();\n\n // Step 1: Check @SkipCsrf() decorator\n const skipCsrf = this.reflector.getAllAndOverride<boolean>(SKIP_CSRF_KEY, [\n context.getHandler(),\n context.getClass(),\n ]);\n\n // Step 2: Validate CSRF for state-changing methods (unless skipped)\n if (!skipCsrf) {\n await this.validateCsrf(request, reply);\n }\n\n // Step 3: Check if endpoint is marked as @Public()\n const isPublic = this.reflector.getAllAndOverride<boolean>('isPublic', [context.getHandler(), context.getClass()]);\n\n if (isPublic) {\n this.logger.debug('Public endpoint detected, skipping authentication');\n return true;\n }\n\n // Step 4: Check if endpoint is marked as @Onboarding()\n const isOnboarding = this.reflector.getAllAndOverride<boolean>('isOnboarding', [\n context.getHandler(),\n context.getClass(),\n ]);\n\n try {\n // Extract and validate access token\n const accessToken = this.requestService.getAccessToken();\n if (!accessToken) {\n this.logger.warn('Access token not found in Authorization header');\n throw new UnauthorizedException('Access token not found');\n }\n\n // Decode token to check type (without full validation yet)\n const decodedToken = this.jwtService.decode(accessToken) as DecodedToken;\n if (!decodedToken) {\n this.logger.warn('Failed to decode access token');\n throw new UnauthorizedException('Invalid token format');\n }\n\n // Step 3: Handle @Onboarding endpoints\n if (isOnboarding) {\n // Only accept onboarding tokens\n if (decodedToken.type !== 'onboarding') {\n this.logger.warn('Onboarding endpoint requires onboarding token');\n throw new UnauthorizedException('This endpoint requires an onboarding token');\n }\n\n // Validate JWT signature and expiry only\n const validatedToken = this.validateAccessToken(accessToken);\n this.logger.debug('Onboarding token validated successfully');\n\n // Validate refresh token binding if enabled\n this.validateRefreshTokenBinding(context, validatedToken);\n\n // Attach user data to request (use userId field from our tokens, fallback to sub for standard JWT)\n const userId = (validatedToken as any).userId;\n (request as any).user = { id: userId };\n\n return true;\n }\n\n // Step 4: Handle regular endpoints - reject onboarding tokens\n if (decodedToken.type === 'onboarding') {\n this.logger.warn('Regular endpoint accessed with onboarding token');\n throw new UnauthorizedException('Onboarding tokens cannot access this endpoint');\n }\n\n // Step 5: Validate access token\n const validatedToken = this.validateAccessToken(accessToken);\n this.logger.debug('Access token validated successfully');\n\n // Step 5.5: Validate refresh token binding if enabled\n this.validateRefreshTokenBinding(context, validatedToken);\n\n // Step 6: Attach user data to request (use userId field from our tokens)\n const userId = (validatedToken as any).userId;\n (request as any).user = { id: userId };\n\n // Step 7: Extract tenant identifier using RequestService\n const tenantIdentifier = this.requestService.getTenantIdentifier();\n\n if (!tenantIdentifier) {\n this.logger.warn('Tenant identifier not found in request');\n throw new UnauthorizedException('Tenant identifier not found');\n }\n\n this.logger.debug(`Tenant identifier extracted: ${tenantIdentifier}`);\n\n // Step 8: Skip database validation for platform admin (cloud.vritti.com)\n if (tenantIdentifier === 'cloud') {\n this.logger.debug('Platform admin access detected, skipping tenant database validation');\n return true;\n }\n\n // Step 9: Fetch tenant details from primary database\n const tenantInfo = await this.primaryDatabase.getTenantInfo(tenantIdentifier);\n\n if (!tenantInfo) {\n this.logger.warn(`Invalid tenant: ${tenantIdentifier}`);\n throw new UnauthorizedException('Invalid tenant');\n }\n\n // Step 10: Validate tenant is ACTIVE\n if (tenantInfo.status !== 'ACTIVE') {\n this.logger.warn(`Tenant ${tenantIdentifier} has status: ${tenantInfo.status}`);\n throw new UnauthorizedException(`Tenant is ${tenantInfo.status}`);\n }\n\n this.logger.debug(`Tenant validated: ${tenantInfo.subdomain} (${tenantInfo.type})`);\n\n return true;\n } catch (error) {\n if (error instanceof UnauthorizedException) {\n throw error;\n }\n this.logger.error('Unexpected error in auth guard', error);\n throw new UnauthorizedException('Authentication failed');\n }\n }\n\n /**\n * Validate access token with proper expiry checks\n * Throws UnauthorizedException if token is invalid or expired\n */\n private validateAccessToken(token: string): DecodedToken {\n try {\n const decoded = this.jwtService.verify<DecodedToken>(token);\n\n this.logger.debug(`Access token decoded for user: ${(decoded as any).userId}`);\n\n // Check expiry explicitly (JwtService already validates, but we log it)\n if (decoded.exp) {\n const expiryTime = decoded.exp * 1000; // Convert to milliseconds\n const currentTime = Date.now();\n\n const timeRemaining = expiryTime - currentTime;\n this.logger.debug(`Access token valid for ${Math.floor(timeRemaining / 1000)} more seconds`);\n }\n\n return decoded;\n } catch (error: unknown) {\n if (error instanceof UnauthorizedException) {\n throw error;\n }\n\n const jwtError = error as { name?: string; message?: string; expiredAt?: string };\n if (jwtError?.name === 'TokenExpiredError') {\n this.logger.warn(`Access token expired at: ${jwtError?.expiredAt}`);\n throw new UnauthorizedException('Access token has expired');\n }\n\n if (jwtError?.name === 'JsonWebTokenError') {\n this.logger.warn(`Access token verification failed: ${jwtError?.message}`);\n throw new UnauthorizedException('Invalid access token');\n }\n\n if (jwtError?.name === 'NotBeforeError') {\n this.logger.warn('Access token used before valid (nbf claim)');\n throw new UnauthorizedException('Access token not yet valid');\n }\n\n this.logger.error('Unexpected error validating access token', error);\n throw new UnauthorizedException('Access token validation failed');\n }\n }\n\n /**\n * Validate that the access token is bound to the refresh token in the cookie.\n * This prevents token theft - a stolen access token is useless without the\n * corresponding refresh token cookie.\n *\n * @param context - The execution context containing the request\n * @param validatedToken - The decoded and validated JWT token\n * @throws UnauthorizedException if token binding validation fails\n */\n private validateRefreshTokenBinding(context: ExecutionContext, validatedToken: DecodedToken): void {\n const config = getConfig();\n\n // Skip validation if disabled in config\n if (!config.jwt.validateTokenBinding) {\n this.logger.debug('Token binding validation is disabled');\n return;\n }\n\n // Skip validation if token doesn't have refreshTokenHash\n // (backwards compatibility for tokens issued before this feature)\n if (!validatedToken.refreshTokenHash) {\n this.logger.debug('Token does not contain refreshTokenHash, skipping binding validation');\n return;\n }\n\n const request = context.switchToHttp().getRequest<FastifyRequest>();\n const cookies = (request as any).cookies || {};\n const refreshToken = cookies[config.cookie.refreshCookieName];\n\n if (!refreshToken) {\n this.logger.warn('Session validation failed - refresh token cookie not found');\n throw new UnauthorizedException('Session validation failed');\n }\n\n if (!verifyTokenHash(refreshToken, validatedToken.refreshTokenHash)) {\n this.logger.warn('Session validation failed - token binding mismatch');\n throw new UnauthorizedException('Session validation failed');\n }\n\n this.logger.debug('Token binding validated successfully');\n }\n\n /**\n * Validate CSRF token for state-changing requests\n * Uses Fastify's csrf-protection plugin for token validation\n *\n * @param request - Fastify request object\n * @param reply - Fastify reply object\n * @throws ForbiddenException if CSRF validation fails\n */\n private async validateCsrf(request: FastifyRequest, reply: FastifyReply): Promise<void> {\n // Skip CSRF for safe methods (GET, HEAD, OPTIONS)\n const safeMethods = ['GET', 'HEAD', 'OPTIONS'];\n if (safeMethods.includes(request.method)) {\n return;\n }\n\n try {\n const fastifyInstance = request.server as any;\n\n if (!fastifyInstance.csrfProtection) {\n this.logger.error('CSRF protection plugin not found. Ensure @fastify/csrf-protection is registered.');\n throw new ForbiddenException('CSRF protection not configured');\n }\n\n // Call the CSRF protection hook\n await new Promise<void>((resolve, reject) => {\n fastifyInstance.csrfProtection(request, reply, (err?: Error) => {\n if (err) {\n reject(err);\n } else {\n resolve();\n }\n });\n });\n\n this.logger.debug(`CSRF validation successful for ${request.method} ${request.url}`);\n } catch (error) {\n this.logger.warn(\n `CSRF validation failed for ${request.method} ${request.url}: ${error instanceof Error ? error.message : 'Unknown error'}`,\n );\n\n throw new ForbiddenException({\n errors: [\n {\n field: 'csrf',\n message: 'Invalid or missing CSRF token',\n },\n ],\n message: 'CSRF validation failed',\n });\n }\n }\n}\n","import { SetMetadata } from '@nestjs/common';\n\nexport const SKIP_CSRF_KEY = 'skipCsrf';\n\n/**\n * Decorator to skip CSRF validation for specific routes or controllers.\n * Use this for webhook endpoints that receive requests from external services\n * (e.g., WhatsApp, Twilio) which cannot include CSRF tokens.\n *\n * @example\n * // Skip CSRF for entire controller\n * @Controller('webhooks')\n * @SkipCsrf()\n * export class WebhookController { ... }\n *\n * @example\n * // Skip CSRF for specific route\n * @Post()\n * @SkipCsrf()\n * async handleWebhook() { ... }\n */\nexport const SkipCsrf = () => SetMetadata(SKIP_CSRF_KEY, true);\n","import {\n Inject,\n Injectable,\n InternalServerErrorException,\n Logger,\n type OnModuleDestroy,\n type OnModuleInit,\n} from '@nestjs/common';\nimport { eq, or } from 'drizzle-orm';\nimport { drizzle } from 'drizzle-orm/node-postgres';\nimport { Pool } from 'pg';\nimport { DATABASE_MODULE_OPTIONS } from '../constants';\nimport type { DatabaseModuleOptions, TenantInfo } from '../interfaces';\nimport type { TypedDrizzleClient } from '../schema.registry';\n\n/**\n * Schema tables required for tenant resolution.\n *\n * @remarks\n * This service requires the schema to include `tenants` and `tenantDatabaseConfigs` tables.\n * Due to Drizzle's complex type system with generic tables and columns,\n * we use explicit casts when accessing these tables. The result types\n * (TenantRow, TenantDatabaseConfigRow) are properly typed for type safety.\n */\ninterface TenantSchemaRequirement {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n tenants: any;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n tenantDatabaseConfigs: any;\n}\n\n/**\n * Type for the raw join result row from tenant query.\n * Uses string keys since Drizzle returns tables with their snake_case names.\n */\ninterface TenantJoinResultRow {\n tenants: TenantRow;\n tenant_database_configs: TenantDatabaseConfigRow | null;\n}\n\n/**\n * Expected shape of a tenant row from the tenants table.\n */\ninterface TenantRow {\n id: string;\n subdomain: string;\n dbType: 'SHARED' | 'DEDICATED';\n status: string;\n}\n\n/**\n * Expected shape of a tenant database config row.\n */\ninterface TenantDatabaseConfigRow {\n tenantId: string;\n dbSchema: string | null;\n dbName: string | null;\n dbHost: string | null;\n dbPort: number | null;\n dbUsername: string | null;\n dbPassword: string | null;\n dbSslMode: string | null;\n connectionPoolSize: number | null;\n}\n\n/**\n * Service responsible for querying the primary database to resolve tenant configurations\n *\n * This service:\n * - Connects to the primary database (tenant registry)\n * - Queries tenant metadata (database location, credentials, etc.)\n * - Caches tenant configs in memory to reduce database load\n * - Only used in GATEWAY MODE (microservices receive tenant config from messages)\n *\n * @example\n * // In API Gateway\n * const config = await primaryDatabase.getTenantConfig('acme');\n * // Returns: { id, slug, type, databaseHost, databaseName, ... }\n */\n@Injectable()\nexport class PrimaryDatabaseService implements OnModuleInit, OnModuleDestroy {\n private readonly logger = new Logger(PrimaryDatabaseService.name);\n\n /** PostgreSQL connection pool */\n private pool: Pool | null = null;\n\n /** Drizzle database instance */\n private db: TypedDrizzleClient | null = null;\n\n /** In-memory cache: Map<tenantIdentifier, TenantConfig> */\n private readonly tenantConfigCache = new Map<string, TenantInfo>();\n\n /** Cache TTL in milliseconds */\n private readonly cacheTTL: number;\n\n constructor(\n @Inject(DATABASE_MODULE_OPTIONS)\n private readonly options: DatabaseModuleOptions,\n ) {\n this.cacheTTL = options.connectionCacheTTL || 300000; // 5 minutes default\n }\n\n async onModuleInit() {\n // Only initialize if we have primary database config (gateway mode)\n if (this.options.primaryDb) {\n await this.initializeDrizzleClient();\n }\n }\n\n /**\n * Initialize connection to primary database using Drizzle\n */\n private async initializeDrizzleClient(): Promise<void> {\n try {\n const databaseUrl = this.buildPrimaryDbUrl();\n\n this.pool = new Pool({\n connectionString: databaseUrl,\n max: this.options.maxConnections || 10,\n });\n\n // Initialize Drizzle with the schema provided (v2 API)\n // Relations must be passed separately for db.query to work\n this.logger.debug(`Schema keys passed to drizzle: [${Object.keys(this.options.drizzleSchema || {}).join(', ')}]`);\n this.logger.debug(\n `Relations keys passed to drizzle: [${Object.keys(this.options.drizzleRelations || {}).join(', ')}]`,\n );\n this.db = drizzle({\n client: this.pool,\n schema: this.options.drizzleSchema,\n relations: this.options.drizzleRelations,\n }) as TypedDrizzleClient;\n this.logger.debug(`Drizzle query keys after init: [${Object.keys(this.db.query || {}).join(', ')}]`);\n\n // Test connection\n await this.pool.query('SELECT 1');\n this.logger.log('Connected to primary database (tenant registry)');\n } catch (error) {\n this.logger.error('Failed to connect to primary database', error);\n throw new InternalServerErrorException('Failed to initialize tenant registry');\n }\n }\n\n /**\n * Build connection URL from primary database properties\n */\n private buildPrimaryDbUrl(): string {\n if (!this.options.primaryDb) {\n throw new Error('Primary database configuration not provided');\n }\n\n const {\n host,\n port = 5432,\n username,\n password,\n database,\n schema = 'public',\n sslMode = 'require',\n } = this.options.primaryDb;\n\n // Build base URL\n let url = `postgresql://${username}:${encodeURIComponent(password)}@${host}:${port}/${database}`;\n\n // Add query parameters\n const params = new URLSearchParams();\n if (schema) {\n params.set('schema', schema);\n }\n params.set('sslmode', sslMode);\n\n const queryString = params.toString();\n if (queryString) {\n url += `?${queryString}`;\n }\n\n this.logger.debug(`Primary DB connection URL: ${this.maskPassword(url)}`);\n\n return url;\n }\n\n /**\n * Mask password in connection URL for logging\n */\n private maskPassword(url: string): string {\n return url.replace(/:([^@]+)@/, ':****@');\n }\n\n /**\n * Get tenant configuration by identifier (ID or subdomain)\n *\n * @param tenantIdentifier Tenant ID or subdomain\n * @returns Tenant configuration or null if not found\n */\n async getTenantInfo(tenantIdentifier: string): Promise<TenantInfo | null> {\n // Check cache first\n const cached = this.tenantConfigCache.get(tenantIdentifier);\n if (cached) {\n this.logger.debug(`Cache hit for tenant: ${tenantIdentifier}`);\n return cached;\n }\n\n // Query primary database\n try {\n if (!this.db) {\n throw new Error('Primary database client not initialized');\n }\n\n this.logger.debug(`Querying primary database for tenant: ${tenantIdentifier}`);\n\n // Get table references from schema\n // Cast to TenantSchemaRequirement - consumer must provide these tables\n const schema = this.options.drizzleSchema as unknown as TenantSchemaRequirement;\n const { tenants, tenantDatabaseConfigs } = schema;\n\n // Query with left join to get tenant and its database config\n const result = await this.db\n .select()\n .from(tenants)\n .leftJoin(tenantDatabaseConfigs, eq(tenants.id, tenantDatabaseConfigs.tenantId))\n .where(or(eq(tenants.id, tenantIdentifier), eq(tenants.subdomain, tenantIdentifier)))\n .limit(1);\n\n if (!result.length) {\n this.logger.warn(`Tenant not found: ${tenantIdentifier}`);\n return null;\n }\n\n // Cast row to access joined table results using typed interfaces\n const row = result[0] as unknown as TenantJoinResultRow;\n const tenant = row.tenants;\n const config = row.tenant_database_configs;\n\n // Check if tenant is active\n if (tenant.status !== 'ACTIVE') {\n this.logger.warn(`Tenant not active: ${tenantIdentifier}`);\n return null;\n }\n\n // Build info object - map from separated tables\n const info: TenantInfo = {\n id: tenant.id,\n subdomain: tenant.subdomain,\n type: tenant.dbType,\n status: tenant.status,\n // For SHARED tenants: schema name\n schemaName: config?.dbSchema || undefined,\n // For DEDICATED tenants: database configuration from TenantDatabaseConfig table\n databaseName: config?.dbName || undefined,\n databaseHost: config?.dbHost || undefined,\n databasePort: config?.dbPort || undefined,\n databaseUsername: config?.dbUsername ? this.decrypt(config.dbUsername) : undefined,\n databasePassword: config?.dbPassword ? this.decrypt(config.dbPassword) : undefined,\n databaseSslMode: config?.dbSslMode || undefined,\n connectionPoolSize: config?.connectionPoolSize || undefined,\n };\n\n // Cache by both ID and subdomain\n this.cacheInfo(info);\n\n return info;\n } catch (error) {\n this.logger.error(`Failed to fetch tenant info: ${tenantIdentifier}`, error);\n throw new InternalServerErrorException('Failed to resolve tenant');\n }\n }\n\n /**\n * Cache tenant information with TTL\n */\n private cacheInfo(info: TenantInfo): void {\n this.tenantConfigCache.set(info.id, info);\n this.tenantConfigCache.set(info.subdomain, info);\n\n // Set expiration\n setTimeout(() => {\n this.tenantConfigCache.delete(info.id);\n this.tenantConfigCache.delete(info.subdomain);\n this.logger.debug(`Cache expired for tenant: ${info.subdomain}`);\n }, this.cacheTTL);\n }\n\n /**\n * Clear cached tenant information\n *\n * Useful when tenant settings are updated and cache needs to be invalidated\n *\n * @param tenantIdentifier Tenant ID or subdomain\n */\n clearTenantCache(tenantIdentifier: string): void {\n const config = this.tenantConfigCache.get(tenantIdentifier);\n if (config) {\n this.tenantConfigCache.delete(config.id);\n this.tenantConfigCache.delete(config.subdomain);\n this.logger.log(`Cleared cache for tenant: ${tenantIdentifier}`);\n }\n }\n\n /**\n * Clear all cached tenant configurations\n */\n clearAllCaches(): void {\n const size = this.tenantConfigCache.size;\n this.tenantConfigCache.clear();\n this.logger.log(`Cleared ${size} cached tenant configs`);\n }\n\n /**\n * Get the Drizzle database instance for the primary database.\n * This is a synchronous property that returns the initialized Drizzle client.\n *\n * @returns Primary database Drizzle instance\n * @throws Error if primary database client is not initialized\n */\n get drizzleClient(): TypedDrizzleClient {\n if (!this.db) {\n throw new Error('Primary database client not initialized');\n }\n return this.db;\n }\n\n /**\n * Get the Drizzle schema\n */\n get schema(): typeof this.options.drizzleSchema {\n return this.options.drizzleSchema;\n }\n\n /**\n * Decrypt database credentials\n *\n * Override this method to implement your encryption strategy\n *\n * @param encrypted Encrypted value\n * @returns Decrypted value\n */\n private decrypt(encrypted: string): string {\n // TODO: Implement actual decryption using this.options.encryptionKey\n // For now, return as-is (assumes unencrypted or encryption happens elsewhere)\n return encrypted;\n }\n\n async onModuleDestroy() {\n if (this.pool) {\n await this.pool.end();\n this.logger.log('Disconnected from primary database');\n }\n }\n}\n","export const DATABASE_MODULE_OPTIONS = Symbol('DATABASE_MODULE_OPTIONS');\n","import * as crypto from 'node:crypto';\n\n/**\n * Hash a token using SHA-256\n * @param token The token to hash\n * @returns The hex-encoded SHA-256 hash\n */\nexport function hashToken(token: string): string {\n return crypto.createHash('sha256').update(token).digest('hex');\n}\n\n/**\n * Verify a token against its expected hash using constant-time comparison\n * @param token The token to verify\n * @param expectedHash The expected SHA-256 hash\n * @returns true if the token matches the hash\n */\nexport function verifyTokenHash(token: string, expectedHash: string): boolean {\n const computedHash = hashToken(token);\n if (computedHash.length !== expectedHash.length) return false;\n return crypto.timingSafeEqual(Buffer.from(computedHash, 'hex'), Buffer.from(expectedHash, 'hex'));\n}\n","import { SetMetadata } from '@nestjs/common';\n\n/**\n * Onboarding Decorator - Marks endpoints that require onboarding token\n *\n * Use this decorator on controllers or route handlers that should only be\n * accessible during the onboarding flow with JWT tokens containing type='onboarding'.\n *\n * These endpoints:\n * - Accept ONLY tokens with type='onboarding'\n * - Reject regular access tokens (type='access')\n * - Skip tenant validation and refresh token checks\n * - Only validate JWT signature and expiry\n *\n * Useful for:\n * - Email/phone verification during onboarding\n * - Onboarding status checks\n * - Resending OTPs during registration\n *\n * @example\n * // On a controller method\n * @Post('verify-email')\n * @Onboarding()\n * async verifyEmail(@Request() req, @Body() dto: VerifyEmailDto) {\n * const userId = req.user.id; // Available from VrittiAuthGuard\n * return this.service.verifyEmail(userId, dto.otp);\n * }\n *\n * @example\n * // Multiple onboarding endpoints\n * @Controller('onboarding')\n * export class OnboardingController {\n * @Post('verify-email')\n * @Onboarding()\n * async verifyEmail() { ... }\n *\n * @Post('resend-otp')\n * @Onboarding()\n * async resendOtp() { ... }\n * }\n */\nexport const Onboarding = () => SetMetadata('isOnboarding', true);\n","import { SetMetadata } from '@nestjs/common';\n\n/**\n * Public Decorator - Marks endpoints that don't require authentication\n *\n * Use this decorator on controllers or route handlers to bypass VrittiAuthGuard\n * tenant validation. Useful for:\n * - Login/signup endpoints\n * - Health checks\n * - Public documentation endpoints\n * - Webhook endpoints that don't require tenant context\n *\n * @example\n * // On a controller method\n * @Public()\n * @Post('auth/login')\n * async login(@Body() dto: LoginDto) {\n * return this.authService.login(dto);\n * }\n *\n * @example\n * // On an entire controller\n * @Public()\n * @Controller('health')\n * export class HealthController {\n * @Get()\n * check() {\n * return { status: 'ok' };\n * }\n * }\n */\nexport const Public = () => SetMetadata('isPublic', true);\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport type { FastifyRequest } from 'fastify';\n\n/**\n * Parameter decorator to extract user ID from authenticated request\n *\n * This decorator retrieves the user ID from the request object,\n * which is set by authentication guards (JwtAuthGuard, VrittiAuthGuard).\n *\n * @returns The user's ID as a string (UUID)\n *\n * @example\n * @Post('verify-email')\n * @Onboarding()\n * async verifyEmail(@UserId() userId: string) {\n * await this.service.verify(userId);\n * }\n *\n * @example\n * @Post('logout-all')\n * @UseGuards(JwtAuthGuard)\n * async logoutAll(@UserId() userId: string) {\n * await this.authService.logoutAll(userId);\n * }\n */\nexport const UserId = createParamDecorator(\n (_data: unknown, ctx: ExecutionContext): string => {\n const request = ctx.switchToHttp().getRequest<FastifyRequest>();\n const user = (request as any).user;\n\n if (!user?.id) {\n throw new Error('User ID not found on request. Ensure route is protected by auth guard.');\n }\n\n return user.id;\n },\n);\n","import {\n type CanActivate,\n type ExecutionContext,\n Injectable,\n Logger,\n Scope,\n UnauthorizedException,\n} from '@nestjs/common';\nimport { JwtService } from '@nestjs/jwt';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\n\n/**\n * Allowed origins for SSE CORS\n * These must match the frontend origins\n */\nconst SSE_ALLOWED_ORIGINS = [\n 'http://localhost:5173',\n 'http://localhost:3001',\n 'http://localhost:3012',\n 'http://localhost:5174',\n 'http://local.vrittiai.com:3012',\n 'http://cloud.local.vrittiai.com:3012',\n 'https://local.vrittiai.com:3012',\n 'https://cloud.local.vrittiai.com:3012',\n];\n\n// Type for decoded JWT token\ninterface DecodedToken {\n userId?: string;\n type?: string;\n exp?: number;\n [key: string]: unknown;\n}\n\n/**\n * SSE Authentication Guard - For Server-Sent Events endpoints\n *\n * This guard is specifically designed for SSE endpoints where:\n * 1. Browser's EventSource API cannot send custom headers\n * 2. Token must be passed via query parameter\n * 3. CORS headers must be set before any response (including errors)\n *\n * Validation Flow:\n * 1. Set CORS headers FIRST (ensures error responses include CORS)\n * 2. Extract token from query param (?token=<jwt>)\n * 3. Validate token is type='onboarding'\n * 4. Attach user data to request.user\n *\n * Usage:\n * ```typescript\n * @Sse('events')\n * @Public() // Bypass global VrittiAuthGuard\n * @UseGuards(SseAuthGuard)\n * async subscribeToEvents(@UserId() userId: string) { ... }\n * ```\n *\n * Note: Must be used with @Public() to bypass the global VrittiAuthGuard\n * since EventSource cannot send Authorization headers.\n */\n@Injectable({ scope: Scope.REQUEST })\nexport class SseAuthGuard implements CanActivate {\n private readonly logger = new Logger(SseAuthGuard.name);\n\n constructor(private readonly jwtService: JwtService) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = context.switchToHttp().getRequest<FastifyRequest>();\n const response = context.switchToHttp().getResponse<FastifyReply>();\n\n // Step 1: Set CORS headers FIRST (before any validation that might throw)\n // This ensures error responses also have CORS headers\n this.setCorsHeaders(request, response);\n\n // Step 2: Extract token from query param\n const token = (request.query as Record<string, string>)?.token;\n\n if (!token) {\n this.logger.warn('SSE authentication failed: token not found in query params');\n throw new UnauthorizedException('Authentication required');\n }\n\n try {\n // Step 3: Decode and validate token\n const decodedToken = this.jwtService.decode(token) as DecodedToken;\n\n if (!decodedToken) {\n this.logger.warn('SSE authentication failed: invalid token format');\n throw new UnauthorizedException('Invalid token format');\n }\n\n // Step 4: Validate token type is 'onboarding'\n if (decodedToken.type !== 'onboarding') {\n this.logger.warn('SSE authentication failed: endpoint requires onboarding token');\n throw new UnauthorizedException('This endpoint requires an onboarding token');\n }\n\n // Step 5: Verify token signature and expiry\n const validatedToken = this.jwtService.verify<DecodedToken>(token);\n this.logger.debug(`SSE token validated for user: ${validatedToken.userId}`);\n\n // Step 6: Attach user data to request\n (request as any).user = { id: validatedToken.userId };\n\n return true;\n } catch (error) {\n if (error instanceof UnauthorizedException) {\n throw error;\n }\n\n const jwtError = error as { name?: string; message?: string };\n if (jwtError?.name === 'TokenExpiredError') {\n this.logger.warn('SSE authentication failed: token expired');\n throw new UnauthorizedException('Token has expired');\n }\n\n if (jwtError?.name === 'JsonWebTokenError') {\n this.logger.warn(`SSE authentication failed: ${jwtError?.message}`);\n throw new UnauthorizedException('Invalid token');\n }\n\n this.logger.error('Unexpected error in SSE auth guard', error);\n throw new UnauthorizedException('Authentication failed');\n }\n }\n\n /**\n * Set CORS headers for SSE responses\n * Must be called before any potential exceptions\n */\n private setCorsHeaders(request: FastifyRequest, response: FastifyReply): void {\n const origin = request.headers.origin;\n\n if (origin && SSE_ALLOWED_ORIGINS.includes(origin)) {\n response.header('Access-Control-Allow-Origin', origin);\n response.header('Access-Control-Allow-Credentials', 'true');\n this.logger.debug(`CORS headers set for origin: ${origin}`);\n } else if (origin) {\n this.logger.warn(`SSE request from unauthorized origin: ${origin}`);\n }\n }\n}\n","import { type DynamicModule, Global, Module, type Provider } from '@nestjs/common';\nimport { APP_INTERCEPTOR, Reflector } from '@nestjs/core';\nimport { RequestModule } from '../request/request.module';\nimport { DATABASE_MODULE_OPTIONS } from './constants';\nimport { MessageTenantContextInterceptor } from './interceptors/message-tenant-context.interceptor';\nimport { TenantContextInterceptor } from './interceptors/tenant-context.interceptor';\n\nimport type { DatabaseModuleOptions } from './interfaces';\nimport { PrimaryDatabaseService } from './services/primary-database.service';\nimport { TenantContextService } from './services/tenant-context.service';\nimport { TenantDatabaseService } from './services/tenant-database.service';\n\n/**\n * Dynamic module for multi-tenant database management\n *\n * This module provides:\n * - Tenant context management (request-scoped)\n * - Database connection pooling\n * - Dynamic schema/cluster routing\n * - Support for both gateway and microservice modes\n *\n * ## Gateway Mode (API Gateway)\n * - Use DatabaseModule.forServer() method\n * - Automatically extracts tenant from subdomain, falls back to x-tenant-id header\n * - Provide primaryDb configuration and prismaClientConstructor\n * - Automatically queries primary DB for tenant config\n * - Automatically registers TenantContextInterceptor globally\n * - No manual interceptor registration needed\n *\n * ## Microservice Mode (RabbitMQ Workers)\n * - Use DatabaseModule.forMicroservice() method\n * - Only provide prismaClientConstructor\n * - Tenant context comes from RabbitMQ messages\n * - Automatically registers MessageTenantContextInterceptor globally\n * - No manual interceptor registration needed\n *\n * @example\n * // Gateway configuration\n * DatabaseModule.forServer({\n * inject: [ConfigService],\n * useFactory: (config: ConfigService) => ({\n * primaryDb: {\n * host: config.get('PRIMARY_DB_HOST'),\n * port: config.get('PRIMARY_DB_PORT'),\n * username: config.get('PRIMARY_DB_USERNAME'),\n * password: config.get('PRIMARY_DB_PASSWORD'),\n * database: config.get('PRIMARY_DB_DATABASE'),\n * },\n * prismaClientConstructor: PrismaClient,\n * }),\n * })\n *\n * @example\n * // Microservice configuration\n * DatabaseModule.forMicroservice({\n * inject: [ConfigService],\n * useFactory: (config: ConfigService) => ({\n * prismaClientConstructor: PrismaClient,\n * }),\n * })\n */\n@Global()\n@Module({})\nexport class DatabaseModule {\n /**\n * Configure DatabaseModule for Gateway/HTTP mode (multi-tenant web servers)\n *\n * This mode is for API Gateways that handle HTTP requests:\n * - Automatically registers TenantContextInterceptor\n * - Extracts tenant from subdomain or x-tenant-id header\n * - Queries primary database for tenant configuration\n * - Provides PrimaryDatabaseService for tenant lookup\n *\n * @param options Async configuration options\n * @returns Dynamic module configuration with HTTP interceptor\n *\n * @example\n * DatabaseModule.forServer({\n * inject: [ConfigService],\n * useFactory: (config: ConfigService) => ({\n * primaryDb: {\n * host: config.get('PRIMARY_DB_HOST'),\n * port: config.get('PRIMARY_DB_PORT'),\n * username: config.get('PRIMARY_DB_USERNAME'),\n * password: config.get('PRIMARY_DB_PASSWORD'),\n * database: config.get('PRIMARY_DB_DATABASE'),\n * },\n * prismaClientConstructor: PrismaClient,\n * }),\n * })\n */\n static forServer(options: {\n useFactory: (...args: any[]) => Promise<DatabaseModuleOptions> | DatabaseModuleOptions;\n inject?: any[];\n }): DynamicModule {\n return DatabaseModule.createDynamicModule(options, 'server');\n }\n\n /**\n * Configure DatabaseModule for Microservice/Messaging mode (RabbitMQ workers)\n *\n * This mode is for microservices that process messages from queues:\n * - Automatically registers MessageTenantContextInterceptor\n * - Extracts tenant from RabbitMQ message patterns\n * - No primary database needed (tenant comes from message context)\n *\n * @param options Async configuration options\n * @returns Dynamic module configuration with message interceptor\n *\n * @example\n * DatabaseModule.forMicroservice({\n * inject: [ConfigService],\n * useFactory: (config: ConfigService) => ({\n * prismaClientConstructor: PrismaClient,\n * }),\n * })\n */\n static forMicroservice(options: {\n useFactory: (...args: any[]) => Promise<DatabaseModuleOptions> | DatabaseModuleOptions;\n inject?: any[];\n }): DynamicModule {\n return DatabaseModule.createDynamicModule(options, 'microservice');\n }\n\n /**\n * Internal helper to create dynamic module with conditional interceptor registration\n *\n * @param options Configuration options\n * @param mode Mode of operation (gateway or microservice)\n * @returns Dynamic module configuration\n */\n private static createDynamicModule(\n options: {\n useFactory: (...args: any[]) => Promise<DatabaseModuleOptions> | DatabaseModuleOptions;\n inject?: any[];\n },\n mode: 'server' | 'microservice',\n ): DynamicModule {\n const asyncProvider: Provider = {\n provide: DATABASE_MODULE_OPTIONS,\n useFactory: options.useFactory,\n inject: options.inject || [],\n };\n\n const providers: Provider[] = [\n // Required for external packages - NestJS global Reflector not available\n {\n provide: Reflector,\n useClass: Reflector,\n },\n asyncProvider,\n TenantContextService,\n PrimaryDatabaseService,\n TenantDatabaseService,\n ];\n\n // Conditionally add interceptor based on mode\n if (mode === 'server') {\n providers.push({\n provide: APP_INTERCEPTOR,\n useClass: TenantContextInterceptor,\n });\n } else {\n providers.push({\n provide: APP_INTERCEPTOR,\n useClass: MessageTenantContextInterceptor,\n });\n }\n\n return {\n module: DatabaseModule,\n imports: [RequestModule],\n providers,\n exports: [TenantDatabaseService, TenantContextService, PrimaryDatabaseService, asyncProvider],\n };\n }\n}\n","import {\n type CallHandler,\n type ExecutionContext,\n Injectable,\n Logger,\n type NestInterceptor,\n Scope,\n} from '@nestjs/common';\nimport type { Observable } from 'rxjs';\nimport { tap } from 'rxjs/operators';\nimport type { TenantInfo } from '../interfaces';\nimport { TenantContextService } from '../services/tenant-context.service';\n\n/**\n * Interceptor that extracts tenant context from RabbitMQ messages (Microservice Mode)\n *\n * This interceptor:\n * 1. Extracts tenant info from RabbitMQ message payload\n * 2. Sets it in REQUEST-SCOPED TenantContextService\n * 3. Cleans up after message is processed\n *\n * Expected message format:\n * {\n * dto: { ... },\n * tenant: {\n * tenantId: 'abc-123',\n * tenantSlug: 'acme',\n * tenantType: 'ENTERPRISE',\n * databaseHost: 'enterprise-1.aws.com',\n * databaseName: 'acme_db',\n * ...\n * }\n * }\n *\n * @example\n * // Automatically registered by DatabaseModule.forMicroservice()\n * // No manual registration needed\n * DatabaseModule.forMicroservice({\n * inject: [ConfigService],\n * useFactory: (config: ConfigService) => ({\n * prismaClientConstructor: PrismaClient,\n * }),\n * })\n */\n@Injectable({ scope: Scope.REQUEST })\nexport class MessageTenantContextInterceptor implements NestInterceptor {\n private readonly logger = new Logger(MessageTenantContextInterceptor.name);\n\n constructor(private readonly tenantContext: TenantContextService) {}\n\n intercept(context: ExecutionContext, next: CallHandler): Observable<any> {\n const contextType = context.getType();\n\n // Only handle RabbitMQ/microservice messages\n if (contextType === 'rpc') {\n const rpcContext = context.switchToRpc();\n const payload = rpcContext.getData();\n\n // Extract tenant from message payload\n if (payload?.tenant) {\n const tenant = payload.tenant as TenantInfo;\n\n this.logger.debug(`Setting tenant context from message: ${tenant.subdomain}`);\n\n try {\n this.tenantContext.setTenant(tenant);\n this.logger.log(`Tenant context set: ${tenant.subdomain} (${tenant.type})`);\n } catch (error) {\n this.logger.error('Failed to set tenant context from message', error);\n }\n } else {\n this.logger.warn('Message payload missing tenant information');\n }\n }\n\n // Execute handler and clean up after\n return next.handle().pipe(\n tap({\n next: () => {\n this.cleanupContext();\n },\n error: () => {\n this.cleanupContext();\n },\n complete: () => {\n this.cleanupContext();\n },\n }),\n );\n }\n\n /**\n * Clean up tenant context after message is processed\n */\n private cleanupContext(): void {\n if (this.tenantContext.hasTenant()) {\n const tenant = this.tenantContext.getTenantIdSafe();\n this.tenantContext.clearTenant();\n this.logger.debug(`Cleaned up tenant context: ${tenant}`);\n }\n }\n}\n","import { Injectable, Scope, UnauthorizedException } from '@nestjs/common';\nimport type { TenantInfo } from '../interfaces';\n\n/**\n * Request-scoped service that holds tenant context for the current request or RabbitMQ message\n *\n * IMPORTANT: This service is REQUEST-SCOPED, meaning NestJS creates a new instance\n * for each HTTP request or RabbitMQ message. This ensures tenant isolation and\n * prevents cross-tenant data leaks in concurrent scenarios.\n *\n * @example\n * // In a controller or service\n * constructor(private readonly tenantContext: TenantContextService) {}\n *\n * async handleRequest() {\n * const tenant = this.tenantContext.getTenant();\n * console.log(`Processing request for tenant: ${tenant.tenantSlug}`);\n * }\n */\n@Injectable({ scope: Scope.REQUEST })\nexport class TenantContextService {\n private tenantInfo: TenantInfo | null = null;\n\n /**\n * Set tenant information for this request/message\n *\n * This is typically called by:\n * - TenantContextInterceptor (for HTTP requests in gateway)\n * - MessageTenantContextInterceptor (for RabbitMQ messages in microservices)\n * - Manual context setup in message handlers\n *\n * @param tenantInfo Complete tenant information\n * @throws Error if tenant context is already set (prevents accidental overwrites)\n */\n setTenant(tenantInfo: TenantInfo): void {\n if (this.tenantInfo) {\n throw new Error('Tenant context already set for this request');\n }\n this.tenantInfo = tenantInfo;\n }\n\n /**\n * Get tenant information for this request/message\n *\n * @returns Tenant information\n * @throws UnauthorizedException if tenant context hasn't been set\n */\n getTenant(): TenantInfo {\n if (!this.tenantInfo) {\n throw new UnauthorizedException('Tenant context not set');\n }\n return this.tenantInfo;\n }\n\n /**\n * Check if tenant context has been set\n *\n * @returns true if tenant context is available\n */\n hasTenant(): boolean {\n return this.tenantInfo !== null;\n }\n\n /**\n * Clear tenant context\n *\n * This is useful for cleanup in RabbitMQ message handlers\n * after the message has been processed.\n *\n * HTTP requests don't need manual cleanup as the service\n * instance is destroyed when the request ends.\n */\n clearTenant(): void {\n this.tenantInfo = null;\n }\n\n /**\n * Get tenant ID safely (returns null if not set)\n *\n * @returns Tenant ID or null\n */\n getTenantIdSafe(): string | null {\n return this.tenantInfo?.id ?? null;\n }\n\n /**\n * Get tenant subdomain safely (returns null if not set)\n *\n * @returns Tenant subdomain or null\n */\n getTenantSubdomainSafe(): string | null {\n return this.tenantInfo?.subdomain ?? null;\n }\n}\n","import {\n type CallHandler,\n type ExecutionContext,\n Injectable,\n Logger,\n type NestInterceptor,\n Scope,\n UnauthorizedException,\n} from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\nimport type { FastifyRequest } from 'fastify';\nimport type { Observable } from 'rxjs';\nimport { RequestService } from '../../request';\nimport { PrimaryDatabaseService } from '../services/primary-database.service';\nimport { TenantContextService } from '../services/tenant-context.service';\n\n/**\n * Interceptor that extracts tenant context from HTTP requests (Gateway Mode)\n *\n * This interceptor runs BEFORE the controller and:\n * 1. Checks if endpoint is marked with @Public() decorator\n * 2. Extracts tenant identifier from request using RequestService\n * 3. For public endpoints without tenant info → skip tenant context setup\n * 4. For all other requests → queries primary database for tenant configuration\n * 5. Stores tenant info in REQUEST-SCOPED TenantContextService\n *\n * Tenant resolution order:\n * - First: x-tenant-id header\n * - Fallback: x-subdomain header\n *\n * Public Endpoints:\n * - Endpoints marked with @Public() decorator can work with OR without tenant context\n * - If no tenant info in headers → skip tenant context setup (useful for OAuth, registration)\n * - If tenant info present in headers → setup tenant context (multi-tenant public APIs)\n *\n * Only used in API Gateway. Microservices use MessageTenantContextInterceptor instead.\n *\n * @example\n * // Public endpoint without tenant (OAuth callback, registration)\n * @Public()\n * @Get('onboarding/oauth/google')\n * async oauthGoogle() { ... }\n * // No x-tenant-id header → skips tenant context\n *\n * @example\n * // Public endpoint with tenant (multi-tenant public API)\n * @Public()\n * @Get('public/data')\n * async getPublicData() { ... }\n * // x-tenant-id: acme → sets tenant context for 'acme'\n */\n@Injectable({ scope: Scope.REQUEST })\nexport class TenantContextInterceptor implements NestInterceptor {\n private readonly logger = new Logger(TenantContextInterceptor.name);\n\n constructor(\n private readonly reflector: Reflector,\n private readonly tenantContext: TenantContextService,\n private readonly primaryDatabase: PrimaryDatabaseService,\n private readonly requestService: RequestService,\n ) {}\n\n async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<any>> {\n const request = context.switchToHttp().getRequest<FastifyRequest>();\n\n this.logger.debug(`Processing request: ${request.method} ${request.url}`);\n\n // Check if endpoint is marked as @Public()\n const isPublic = this.reflector.getAllAndOverride<boolean>('isPublic', [context.getHandler(), context.getClass()]);\n\n try {\n // Extract tenant identifier using RequestService (no code duplication)\n const tenantIdentifier = this.requestService.getTenantIdentifier();\n\n // Skip tenant context for public endpoints without tenant info\n if (isPublic && !tenantIdentifier) {\n this.logger.debug('Public endpoint without tenant identifier, skipping tenant context setup');\n return next.handle();\n }\n\n if (!tenantIdentifier) {\n throw new UnauthorizedException('Tenant identifier not found in request');\n }\n\n this.logger.debug(`Tenant identifier extracted: ${tenantIdentifier}`);\n\n // Special case: cloud.vritti.com (platform admin)\n if (tenantIdentifier === 'cloud') {\n this.logger.log('Cloud platform access detected, skipping tenant context setup');\n return next.handle();\n }\n\n // Query primary database for tenant configuration\n const tenantInfo = await this.primaryDatabase.getTenantInfo(tenantIdentifier);\n\n if (!tenantInfo) {\n this.logger.warn(`Invalid tenant: ${tenantIdentifier}`);\n throw new UnauthorizedException('Invalid tenant');\n }\n\n if (tenantInfo.status !== 'ACTIVE') {\n this.logger.warn(`Tenant ${tenantIdentifier} has status: ${tenantInfo.status}`);\n throw new UnauthorizedException(`Tenant is ${tenantInfo.status}`);\n }\n\n this.logger.debug(`Tenant config loaded: ${tenantInfo.subdomain} (${tenantInfo.type})`);\n\n // Store in REQUEST-SCOPED context\n this.tenantContext.setTenant(tenantInfo);\n\n // Also attach to request object for easy access\n (request as any).tenant = tenantInfo;\n\n this.logger.log(`Tenant context set: ${tenantInfo.subdomain}`);\n } catch (error) {\n this.logger.error('Failed to set tenant context', error);\n throw error;\n }\n\n return next.handle();\n }\n}\n","import { Inject, Injectable, InternalServerErrorException, Logger, type OnModuleDestroy } from '@nestjs/common';\nimport { drizzle } from 'drizzle-orm/node-postgres';\nimport { Pool } from 'pg';\nimport { DATABASE_MODULE_OPTIONS } from '../constants';\nimport type { DatabaseModuleOptions, TenantInfo } from '../interfaces';\nimport type { TypedDrizzleClient } from '../schema.registry';\nimport { TenantContextService } from './tenant-context.service';\n\n/**\n * Tenant connection wrapper containing both pool and Drizzle instance\n */\ninterface TenantConnection {\n pool: Pool;\n db: TypedDrizzleClient;\n}\n\n/**\n * Service responsible for managing tenant-scoped database connections\n *\n * This service:\n * - Maintains a connection pool (Map<cacheKey, TenantConnection>)\n * - Creates new connections dynamically based on tenant context\n * - Reuses existing connections for the same tenant\n * - Supports both cloud schemas and enterprise databases\n * - Automatically cleans up idle connections\n *\n * @example\n * // In a controller or service\n * const db = this.tenantDatabase.drizzleClient;\n * const users = await db.select().from(usersTable);\n */\n@Injectable()\nexport class TenantDatabaseService implements OnModuleDestroy {\n private readonly logger = new Logger(TenantDatabaseService.name);\n\n /** Connection pool: Map<cacheKey, TenantConnection> */\n private readonly clients = new Map<string, TenantConnection>();\n\n /** Track last usage time for idle connection cleanup */\n private readonly clientLastUsed = new Map<string, number>();\n\n /** Cleanup interval timer */\n private cleanupInterval?: NodeJS.Timeout;\n\n constructor(\n @Inject(DATABASE_MODULE_OPTIONS)\n private readonly options: DatabaseModuleOptions,\n private readonly tenantContext: TenantContextService,\n ) {\n this.startConnectionCleaner();\n }\n\n /**\n * Get the Drizzle client for the current tenant's database.\n * This returns the tenant-scoped database client.\n *\n * @returns Tenant-scoped Drizzle database instance\n * @throws UnauthorizedException if tenant context not set\n * @throws InternalServerErrorException if connection fails\n */\n get drizzleClient(): TypedDrizzleClient {\n return this.getDbClient();\n }\n\n /**\n * Get the Drizzle schema\n */\n get schema(): Record<string, unknown> {\n return this.options.drizzleSchema;\n }\n\n /**\n * Get tenant-scoped database client for the current request/message\n *\n * This method:\n * 1. Gets tenant info from TenantContextService\n * 2. Builds a connection URL based on tenant type\n * 3. Returns cached client if exists, otherwise creates new one\n *\n * @returns Drizzle database instance\n * @throws UnauthorizedException if tenant context not set\n * @throws InternalServerErrorException if connection fails\n */\n private getDbClient(): TypedDrizzleClient {\n const tenant = this.tenantContext.getTenant();\n const cacheKey = this.buildCacheKey(tenant);\n\n // Check if connection already exists\n const existing = this.clients.get(cacheKey);\n if (existing) {\n this.clientLastUsed.set(cacheKey, Date.now());\n this.logger.debug(`Reusing cached connection: ${cacheKey}`);\n return existing.db;\n }\n\n // Create new connection synchronously\n this.logger.log(`Creating new database connection: ${cacheKey}`);\n const connection = this.createDbClientSync(tenant);\n this.clients.set(cacheKey, connection);\n this.clientLastUsed.set(cacheKey, Date.now());\n\n return connection.db;\n }\n\n /**\n * Create a new database client for the given tenant (synchronous)\n */\n private createDbClientSync(tenant: TenantInfo): TenantConnection {\n try {\n // Build tenant-specific database URL\n const databaseUrl = this.buildTenantDbUrl(tenant);\n\n // Create PostgreSQL pool\n const pool = new Pool({\n connectionString: databaseUrl,\n max: tenant.connectionPoolSize || this.options.maxConnections || 10,\n });\n\n // Initialize Drizzle with the schema (v2 API)\n const db = drizzle({\n client: pool,\n schema: this.options.drizzleSchema,\n }) as TypedDrizzleClient;\n\n this.logger.log(`Connected to database for tenant: ${tenant.subdomain}`);\n\n return { pool, db };\n } catch (error) {\n this.logger.error(`Failed to create database connection for tenant: ${tenant.subdomain}`, error);\n throw new InternalServerErrorException('Failed to connect to tenant database');\n }\n }\n\n /**\n * Build connection URL for tenant (dedicated database)\n */\n private buildTenantDbUrl(tenant: TenantInfo): string {\n const { databaseHost, databasePort, databaseName, databaseUsername, databasePassword, databaseSslMode } = tenant;\n\n if (!databaseHost || !databaseName || !databaseUsername) {\n throw new Error(`Tenant ${tenant.subdomain} missing database configuration`);\n }\n\n const port = databasePort || 5432;\n const sslMode = databaseSslMode || 'require';\n const connectionUrl = `postgresql://${databaseUsername}:${encodeURIComponent(databasePassword || '')}@${databaseHost}:${port}/${databaseName}?sslmode=${sslMode}`;\n\n this.logger.debug(`Tenant connection URL: ${this.maskPassword(connectionUrl)}`);\n\n return connectionUrl;\n }\n\n /**\n * Build cache key for connection pooling\n */\n private buildCacheKey(tenant: TenantInfo): string {\n return `${tenant.type}:${tenant.databaseName}@${tenant.databaseHost}`;\n }\n\n /**\n * Start periodic cleanup of idle connections\n */\n private startConnectionCleaner(): void {\n const interval = this.options.connectionCacheTTL || 300000; // 5 minutes\n\n this.cleanupInterval = setInterval(() => {\n this.cleanupIdleConnections();\n }, interval);\n\n this.logger.log(`Connection cleanup scheduled every ${interval / 1000} seconds`);\n }\n\n /**\n * Clean up idle connections that haven't been used recently\n */\n private async cleanupIdleConnections(): Promise<void> {\n const now = Date.now();\n const maxIdle = this.options.connectionCacheTTL || 300000;\n\n let cleaned = 0;\n\n for (const [key, lastUsed] of this.clientLastUsed.entries()) {\n if (now - lastUsed > maxIdle) {\n const connection = this.clients.get(key);\n if (connection) {\n try {\n await connection.pool.end();\n this.logger.debug(`Cleaned up idle connection: ${key}`);\n } catch (error) {\n this.logger.error(`Error disconnecting idle client: ${key}`, error);\n }\n\n this.clients.delete(key);\n this.clientLastUsed.delete(key);\n cleaned++;\n }\n }\n }\n\n if (cleaned > 0) {\n this.logger.log(`Cleaned up ${cleaned} idle connections`);\n }\n }\n\n /**\n * Get current connection pool statistics\n */\n getPoolStats(): {\n activeConnections: number;\n tenants: string[];\n } {\n return {\n activeConnections: this.clients.size,\n tenants: Array.from(this.clients.keys()),\n };\n }\n\n /**\n * Mask password in connection URL for logging\n */\n private maskPassword(url: string): string {\n return url.replace(/:([^@]+)@/, ':****@');\n }\n\n async onModuleDestroy() {\n // Stop cleanup interval\n if (this.cleanupInterval) {\n clearInterval(this.cleanupInterval);\n }\n\n // Disconnect all clients\n this.logger.log(`Disconnecting ${this.clients.size} database connections`);\n\n const disconnectPromises = Array.from(this.clients.entries()).map(async ([key, connection]) => {\n try {\n await connection.pool.end();\n this.logger.debug(`Disconnected: ${key}`);\n } catch (error) {\n this.logger.error(`Error disconnecting client: ${key}`, error);\n }\n });\n\n await Promise.all(disconnectPromises);\n this.logger.log('All database connections closed');\n }\n}\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport type { TenantInfo } from '../interfaces';\nimport { TenantContextService } from '../services/tenant-context.service';\n\n/**\n * Parameter decorator that injects tenant metadata into controller method\n *\n * This decorator retrieves tenant information (ID, slug, type, etc.)\n * from the REQUEST-SCOPED TenantContextService.\n *\n * Useful for:\n * - Logging tenant-specific information\n * - Implementing tenant-specific business logic\n * - Auditing and tracking\n * - Conditional feature flags\n *\n * @returns TenantInfo object with tenant metadata\n *\n * @example\n * // Access tenant metadata\n * @Get('info')\n * async getTenantInfo(@Tenant() tenant: TenantInfo) {\n * return {\n * id: tenant.id,\n * subdomain: tenant.subdomain,\n * type: tenant.type,\n * };\n * }\n *\n * @example\n * // Use for logging\n * @Post()\n * async createUser(\n * @Body() dto: CreateUserDto,\n * @Tenant() tenant: TenantInfo,\n * ) {\n * this.logger.log(`Creating user for tenant: ${tenant.subdomain}`);\n * // ...\n * }\n *\n * @example\n * // Conditional business logic\n * @Get('features')\n * async getFeatures(@Tenant() tenant: TenantInfo) {\n * if (tenant.type === 'ENTERPRISE') {\n * return ['feature-a', 'feature-b', 'feature-c'];\n * }\n * return ['feature-a'];\n * }\n */\nexport const Tenant = createParamDecorator((_data: unknown, ctx: ExecutionContext): TenantInfo => {\n const request = ctx.switchToHttp().getRequest();\n\n // Get from TenantContextService\n const tenantContext = request.app?.get?.(TenantContextService);\n\n if (!tenantContext) {\n throw new Error('TenantContextService not found.');\n }\n\n return tenantContext.getTenant();\n});\n","import { Logger } from '@nestjs/common';\nimport { eq, getTableName, type InferInsertModel, type InferSelectModel, type SQL, sql } from 'drizzle-orm';\nimport type { PgTable } from 'drizzle-orm/pg-core';\nimport type { TypedDrizzleClient } from '../schema.registry';\nimport { PrimaryDatabaseService } from '../services/primary-database.service';\n\n/**\n * Convert snake_case string to camelCase\n * @example 'email_verifications' -> 'emailVerifications'\n */\nfunction snakeToCamel(str: string): string {\n return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());\n}\n\n/**\n * Drizzle ORM v2 object-based where filter type.\n * Supports simple equality, operators, AND/OR/NOT, and RAW SQL.\n *\n * @example\n * ```typescript\n * // Simple equality\n * { email: 'user@example.com' }\n *\n * // With operators\n * { age: { gt: 18, lt: 65 } }\n *\n * // AND/OR combinations\n * { AND: [{ status: 'ACTIVE' }, { age: { gte: 18 } }] }\n *\n * // RAW SQL expression\n * { RAW: (table) => sql`${table.email} ILIKE '%@gmail.com'` }\n * ```\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype RelationsWhereFilter = Record<string, any>;\n\n/**\n * Type-safe wrapper for Drizzle's RelationalQueryBuilder (v2 API).\n * This interface matches the method signatures of RelationalQueryBuilder\n * but properly binds the TSelect generic for type safety.\n *\n * We use this instead of RelationalQueryBuilder directly because\n * TypeScript cannot infer TSelect from the generic base repository context.\n *\n * @remarks\n * Drizzle ORM v2 uses object-based `where` filters instead of SQL expressions.\n * See: https://orm.drizzle.team/docs/relations-v1-v2\n */\ninterface TypedRelationalQueryBuilder<TSelect> {\n findFirst(config?: {\n where?: RelationsWhereFilter;\n with?: Record<string, unknown>;\n columns?: Record<string, boolean>;\n }): Promise<TSelect | undefined>;\n\n findMany(config?: {\n where?: RelationsWhereFilter;\n orderBy?: Record<string, 'asc' | 'desc'>;\n limit?: number;\n offset?: number;\n with?: Record<string, unknown>;\n columns?: Record<string, boolean>;\n }): Promise<TSelect[]>;\n}\n\n/**\n * Abstract base repository for primary database operations using Drizzle ORM.\n * Provides common CRUD operations with automatic logging.\n *\n * @template TTable - The Drizzle table type (must be registered in SchemaRegistry)\n * @template TInsert - Type for insert operations (inferred from table.$inferInsert)\n * @template TSelect - Type for select operations (inferred from table.$inferSelect)\n *\n * @remarks\n * **Type Assertion Pattern:** This repository uses `as any` casts when passing\n * the generic table to Drizzle methods. This is necessary because TypeScript\n * cannot prove that a generic `TTable extends PgTable` satisfies Drizzle's\n * stricter internal type requirements for `insert()`, `update()`, and `delete()`.\n *\n * The public API maintains full type safety:\n * - Input parameters are typed as `TInsert` (inferred from table)\n * - Return values are typed as `TSelect` (inferred from table)\n * - The casts are implementation details that don't leak to consumers\n *\n * @example\n * ```typescript\n * import { users } from '@/db/schema';\n *\n * type User = typeof users.$inferSelect;\n * type NewUser = typeof users.$inferInsert;\n *\n * @Injectable()\n * export class UserRepository extends PrimaryBaseRepository<typeof users> {\n * constructor(database: PrimaryDatabaseService) {\n * super(database, users);\n * }\n *\n * // Use Drizzle v2 object-based where syntax (recommended)\n * async findByEmail(email: string): Promise<User | undefined> {\n * return this.model.findFirst({\n * where: { email },\n * });\n * }\n *\n * // With relations\n * async findWithRelations(id: string): Promise<User | undefined> {\n * return this.model.findFirst({\n * where: { id },\n * with: { posts: true, profile: true }\n * });\n * }\n * }\n * ```\n */\nexport abstract class PrimaryBaseRepository<\n TTable extends PgTable,\n TInsert = InferInsertModel<TTable>,\n TSelect = InferSelectModel<TTable>,\n> {\n protected readonly logger: Logger;\n\n /**\n * The table name extracted from the Drizzle table at runtime.\n * Stored in camelCase to match Drizzle's query object keys.\n * Example: 'email_verifications' -> 'emailVerifications'\n */\n private readonly tableName: string;\n\n /**\n * Lazy getter for Drizzle client.\n * Accesses the client from the database service only when needed,\n * avoiding initialization timing issues with NestJS lifecycle.\n */\n protected get db(): TypedDrizzleClient {\n return this.database.drizzleClient;\n }\n\n /**\n * Model query API for THIS repository's table (Drizzle v2 relational queries)\n * Scoped to only the table this repository manages.\n * Returns a type-safe wrapper around Drizzle's RelationalQueryBuilder.\n *\n * @example\n * ```typescript\n * // Use relational queries with v2 object-based where syntax\n * const user = await this.model.findFirst({\n * where: { id },\n * with: { posts: true, profile: true }\n * });\n * ```\n */\n protected get model(): TypedRelationalQueryBuilder<TSelect> {\n const query = this.database.drizzleClient.query;\n const queryKeys = Object.keys(query || {});\n this.logger.debug(`Looking for '${this.tableName}' in query keys: [${queryKeys.join(', ')}]`);\n\n const model = query[this.tableName as keyof TypedDrizzleClient['query']];\n if (!model) {\n this.logger.error(`Table '${this.tableName}' not found in query object. Available: [${queryKeys.join(', ')}]`);\n }\n\n return model as unknown as TypedRelationalQueryBuilder<TSelect>;\n }\n\n /**\n * Create a new repository instance\n *\n * @param database - The primary database service\n * @param table - The Drizzle table schema object\n *\n * @example\n * ```typescript\n * import { users } from '@/db/schema';\n *\n * constructor(database: PrimaryDatabaseService) {\n * super(database, users);\n * }\n * ```\n */\n constructor(\n protected readonly database: PrimaryDatabaseService,\n protected readonly table: TTable,\n ) {\n // Convert snake_case table name to camelCase to match Drizzle query object keys\n // Example: 'email_verifications' -> 'emailVerifications'\n const dbTableName = getTableName(table);\n this.tableName = snakeToCamel(dbTableName);\n this.logger = new Logger(this.constructor.name);\n this.logger.debug(`Initialized ${this.constructor.name}`);\n this.logger.debug(`Table name: '${dbTableName}' -> query key: '${this.tableName}'`);\n }\n\n /**\n * Create a new record\n *\n * @param data - The data to create the record with\n * @returns Promise resolving to the created record\n *\n * @example\n * ```typescript\n * const user = await userRepository.create({\n * email: 'user@example.com',\n * firstName: 'John'\n * });\n * ```\n */\n async create(data: TInsert): Promise<TSelect> {\n this.logger.log('Creating record');\n const results = (await this.db\n .insert(this.table as any)\n .values(data as any)\n .returning()) as TSelect[];\n return results[0]!;\n }\n\n /**\n * Find a single record by ID\n *\n * @param id - The record ID\n * @returns Promise resolving to the record or undefined if not found\n *\n * @example\n * ```typescript\n * const user = await userRepository.findById('user-id-123');\n * ```\n */\n async findById(id: string): Promise<TSelect | undefined> {\n this.logger.debug(`Finding record by ID: ${id}`);\n return this.model.findFirst({\n where: { id },\n });\n }\n\n /**\n * Find a single record with custom where clause (Drizzle v2 object-based syntax)\n *\n * @param where - Object-based filter condition\n * @returns Promise resolving to the record or undefined if not found\n *\n * @example\n * ```typescript\n * // Simple equality\n * const user = await userRepository.findOne({ email: 'user@example.com' });\n *\n * // With operators\n * const user = await userRepository.findOne({ age: { gte: 18 } });\n *\n * // Multiple conditions (AND)\n * const user = await userRepository.findOne({\n * email: 'user@example.com',\n * status: 'ACTIVE'\n * });\n * ```\n */\n async findOne(where: RelationsWhereFilter): Promise<TSelect | undefined> {\n this.logger.debug('Finding record with custom query');\n return this.model.findFirst({ where });\n }\n\n /**\n * Find multiple records (Drizzle v2 object-based syntax)\n *\n * @param options - Query options (where, orderBy, limit, offset)\n * @returns Promise resolving to an array of records\n *\n * @example\n * ```typescript\n * // Find all users\n * const users = await userRepository.findMany();\n *\n * // Find with filtering and pagination (v2 object syntax)\n * const users = await userRepository.findMany({\n * where: { accountStatus: 'ACTIVE' },\n * orderBy: { createdAt: 'desc' },\n * limit: 10,\n * offset: 0\n * });\n *\n * // Multiple conditions\n * const users = await userRepository.findMany({\n * where: {\n * AND: [\n * { status: 'ACTIVE' },\n * { age: { gte: 18 } }\n * ]\n * }\n * });\n * ```\n */\n async findMany(options?: {\n where?: RelationsWhereFilter;\n orderBy?: Record<string, 'asc' | 'desc'>;\n limit?: number;\n offset?: number;\n }): Promise<TSelect[]> {\n this.logger.debug('Finding multiple records');\n return this.model.findMany(options);\n }\n\n /**\n * Update a record by ID\n *\n * @param id - The record ID\n * @param data - The data to update\n * @returns Promise resolving to the updated record\n *\n * @example\n * ```typescript\n * const user = await userRepository.update('user-id-123', {\n * firstName: 'Jane'\n * });\n * ```\n */\n async update(id: string, data: Partial<TInsert>): Promise<TSelect> {\n this.logger.log(`Updating record with ID: ${id}`);\n const idColumn = (this.table as any).id;\n const results = (await this.db\n .update(this.table as any)\n .set(data as any)\n .where(eq(idColumn, id))\n .returning()) as TSelect[];\n return results[0]!;\n }\n\n /**\n * Update multiple records\n *\n * @param where - SQL condition to match records\n * @param data - The data to update\n * @returns Promise resolving to the count of updated records\n *\n * @example\n * ```typescript\n * import { eq } from 'drizzle-orm';\n *\n * const result = await userRepository.updateMany(\n * eq(users.accountStatus, 'PENDING'),\n * { accountStatus: 'ACTIVE' }\n * );\n * console.log(`Updated ${result.count} users`);\n * ```\n */\n async updateMany(where: SQL, data: Partial<TInsert>): Promise<{ count: number }> {\n this.logger.log('Updating multiple records');\n const result = await this.db\n .update(this.table as any)\n .set(data as any)\n .where(where);\n return { count: result.rowCount ?? 0 };\n }\n\n /**\n * Delete a record by ID\n *\n * @param id - The record ID\n * @returns Promise resolving to the deleted record\n *\n * @example\n * ```typescript\n * const user = await userRepository.delete('user-id-123');\n * ```\n */\n async delete(id: string): Promise<TSelect> {\n this.logger.log(`Deleting record with ID: ${id}`);\n const idColumn = (this.table as any).id;\n const results = (await this.db\n .delete(this.table as any)\n .where(eq(idColumn, id))\n .returning()) as TSelect[];\n return results[0]!;\n }\n\n /**\n * Delete multiple records\n *\n * @param where - SQL condition to match records\n * @returns Promise resolving to the count of deleted records\n *\n * @example\n * ```typescript\n * import { lt } from 'drizzle-orm';\n *\n * const result = await userRepository.deleteMany(\n * lt(users.createdAt, new Date('2020-01-01'))\n * );\n * console.log(`Deleted ${result.count} users`);\n * ```\n */\n async deleteMany(where: SQL): Promise<{ count: number }> {\n this.logger.log('Deleting multiple records');\n const result = await this.db.delete(this.table as any).where(where);\n return { count: result.rowCount ?? 0 };\n }\n\n /**\n * Count records\n *\n * @param where - Optional SQL condition to filter records\n * @returns Promise resolving to the count of records\n *\n * @example\n * ```typescript\n * import { eq } from 'drizzle-orm';\n *\n * // Count all users\n * const total = await userRepository.count();\n *\n * // Count active users\n * const activeCount = await userRepository.count(\n * eq(users.accountStatus, 'ACTIVE')\n * );\n * ```\n */\n async count(where?: SQL): Promise<number> {\n this.logger.debug('Counting records');\n\n let query = this.db\n .select({ count: sql<number>`count(*)::int` })\n .from(this.table as any)\n .$dynamic();\n\n if (where) {\n query = query.where(where);\n }\n\n const results = await query;\n return (results[0] as { count: number }).count;\n }\n\n /**\n * Check if a record exists\n *\n * @param where - SQL condition to match records\n * @returns Promise resolving to true if at least one record exists, false otherwise\n *\n * @example\n * ```typescript\n * import { eq } from 'drizzle-orm';\n *\n * const emailExists = await userRepository.exists(\n * eq(users.email, 'user@example.com')\n * );\n * ```\n */\n async exists(where: SQL): Promise<boolean> {\n const count = await this.count(where);\n return count > 0;\n }\n}\n","import { Logger } from '@nestjs/common';\nimport { eq, getTableName, type InferInsertModel, type InferSelectModel, type SQL, sql } from 'drizzle-orm';\nimport type { PgTable } from 'drizzle-orm/pg-core';\nimport type { TypedDrizzleClient } from '../schema.registry';\nimport { TenantDatabaseService } from '../services/tenant-database.service';\n\n/**\n * Type helper to extract table name from Drizzle table.\n * TTable['_']['name'] gives us the string literal type (e.g., 'products')\n */\ntype ExtractTableName<TTable extends PgTable> = TTable['_']['name'];\n\n/**\n * Abstract base repository for tenant-scoped database operations using Drizzle ORM.\n * All operations are automatically scoped to the current tenant.\n *\n * @template TTable - The Drizzle table type (must be registered in SchemaRegistry)\n * @template TInsert - Type for insert operations (inferred from table.$inferInsert)\n * @template TSelect - Type for select operations (inferred from table.$inferSelect)\n *\n * @remarks\n * **Type Assertion Pattern:** This repository uses `as any` casts when passing\n * the generic table to Drizzle methods. This is necessary because TypeScript\n * cannot prove that a generic `TTable extends PgTable` satisfies Drizzle's\n * stricter internal type requirements for `insert()`, `update()`, and `delete()`.\n *\n * The public API maintains full type safety:\n * - Input parameters are typed as `TInsert` (inferred from table)\n * - Return values are typed as `TSelect` (inferred from table)\n * - The casts are implementation details that don't leak to consumers\n *\n * @example\n * ```typescript\n * import { products } from '@/db/schema';\n *\n * type Product = typeof products.$inferSelect;\n * type NewProduct = typeof products.$inferInsert;\n *\n * @Injectable()\n * export class ProductRepository extends TenantBaseRepository<typeof products> {\n * constructor(database: TenantDatabaseService) {\n * super(database, products);\n * }\n *\n * // Use SQL-builder syntax\n * async findBySku(sku: string): Promise<Product | null> {\n * const [result] = await this.db\n * .select()\n * .from(this.table)\n * .where(eq(products.sku, sku))\n * .limit(1);\n * return result ?? null;\n * }\n *\n * // Use Prisma-like relational query syntax\n * async findWithRelations(id: string): Promise<Product | null> {\n * return await this.model.findFirst({\n * where: eq(products.id, id),\n * with: { category: true, variants: true }\n * });\n * }\n * }\n * ```\n */\nexport abstract class TenantBaseRepository<\n TTable extends PgTable,\n TInsert = InferInsertModel<TTable>,\n TSelect = InferSelectModel<TTable>,\n> {\n protected readonly logger: Logger;\n\n /**\n * The table name extracted from the Drizzle table at runtime.\n * Used to access the query API for this repository's table.\n */\n private readonly tableName: string;\n\n /**\n * Lazy getter for Drizzle client.\n * Accesses the client from the database service only when needed,\n * avoiding initialization timing issues with NestJS lifecycle.\n */\n protected get db(): TypedDrizzleClient {\n return this.database.drizzleClient;\n }\n\n /**\n * Model query API for THIS repository's table (Prisma-like syntax)\n * Scoped to only the table this repository manages\n *\n * @example\n * ```typescript\n * // Use relational queries with type safety\n * const product = await this.model.findFirst({\n * where: eq(products.id, id),\n * with: { category: true, variants: true }\n * });\n * ```\n */\n protected get model(): TypedDrizzleClient['query'][ExtractTableName<TTable> & keyof TypedDrizzleClient['query']] {\n return this.database.drizzleClient.query[\n this.tableName as ExtractTableName<TTable> & keyof TypedDrizzleClient['query']\n ];\n }\n\n /**\n * Create a new repository instance\n *\n * @param database - The tenant database service\n * @param table - The Drizzle table schema object\n *\n * @example\n * ```typescript\n * import { products } from '@/db/schema';\n *\n * constructor(database: TenantDatabaseService) {\n * super(database, products);\n * }\n * ```\n */\n constructor(\n protected readonly database: TenantDatabaseService,\n protected readonly table: TTable,\n ) {\n this.tableName = getTableName(table);\n this.logger = new Logger(this.constructor.name);\n this.logger.debug(`Initialized ${this.constructor.name}`);\n }\n\n /**\n * Create a new record\n *\n * @param data - The data to create the record with\n * @returns Promise resolving to the created record\n *\n * @example\n * ```typescript\n * const product = await productRepository.create({\n * name: 'Widget',\n * sku: 'WDG-001',\n * price: 9.99\n * });\n * ```\n */\n async create(data: TInsert): Promise<TSelect> {\n this.logger.log('Creating record');\n const results = (await this.db\n .insert(this.table as any)\n .values(data as any)\n .returning()) as TSelect[];\n return results[0]!;\n }\n\n /**\n * Find a single record by ID\n *\n * @param id - The record ID\n * @returns Promise resolving to the record or null if not found\n *\n * @example\n * ```typescript\n * const product = await productRepository.findById('product-id-123');\n * ```\n */\n async findById(id: string): Promise<TSelect | null> {\n this.logger.debug(`Finding record by ID: ${id}`);\n const idColumn = (this.table as any).id;\n const results = await this.db\n .select()\n .from(this.table as any)\n .where(eq(idColumn, id))\n .limit(1);\n return (results[0] as TSelect) ?? null;\n }\n\n /**\n * Find a single record with custom where clause\n *\n * @param where - SQL condition\n * @returns Promise resolving to the record or null if not found\n *\n * @example\n * ```typescript\n * import { eq } from 'drizzle-orm';\n * const product = await productRepository.findOne(eq(products.sku, 'WDG-001'));\n * ```\n */\n async findOne(where: SQL): Promise<TSelect | null> {\n this.logger.debug('Finding record with custom query');\n const results = await this.db\n .select()\n .from(this.table as any)\n .where(where)\n .limit(1);\n return (results[0] as TSelect) ?? null;\n }\n\n /**\n * Find multiple records\n *\n * @param options - Query options (where, orderBy, limit, offset)\n * @returns Promise resolving to an array of records\n *\n * @example\n * ```typescript\n * import { eq, desc } from 'drizzle-orm';\n *\n * // Find all products\n * const products = await productRepository.findMany();\n *\n * // Find with filtering and pagination\n * const products = await productRepository.findMany({\n * where: eq(products.status, 'ACTIVE'),\n * orderBy: desc(products.createdAt),\n * limit: 10,\n * offset: 0\n * });\n * ```\n */\n async findMany(options?: { where?: SQL; orderBy?: SQL; limit?: number; offset?: number }): Promise<TSelect[]> {\n this.logger.debug('Finding multiple records');\n\n let query = this.db\n .select()\n .from(this.table as any)\n .$dynamic();\n\n if (options?.where) {\n query = query.where(options.where);\n }\n if (options?.orderBy) {\n query = query.orderBy(options.orderBy);\n }\n if (options?.limit) {\n query = query.limit(options.limit);\n }\n if (options?.offset) {\n query = query.offset(options.offset);\n }\n\n return (await query) as TSelect[];\n }\n\n /**\n * Update a record by ID\n *\n * @param id - The record ID\n * @param data - The data to update\n * @returns Promise resolving to the updated record\n *\n * @example\n * ```typescript\n * const product = await productRepository.update('product-id-123', {\n * price: 12.99\n * });\n * ```\n */\n async update(id: string, data: Partial<TInsert>): Promise<TSelect> {\n this.logger.log(`Updating record with ID: ${id}`);\n const idColumn = (this.table as any).id;\n const results = (await this.db\n .update(this.table as any)\n .set(data as any)\n .where(eq(idColumn, id))\n .returning()) as TSelect[];\n return results[0]!;\n }\n\n /**\n * Update multiple records\n *\n * @param where - SQL condition to match records\n * @param data - The data to update\n * @returns Promise resolving to the count of updated records\n *\n * @example\n * ```typescript\n * import { eq } from 'drizzle-orm';\n *\n * const result = await productRepository.updateMany(\n * eq(products.status, 'PENDING'),\n * { status: 'ACTIVE' }\n * );\n * console.log(`Updated ${result.count} products`);\n * ```\n */\n async updateMany(where: SQL, data: Partial<TInsert>): Promise<{ count: number }> {\n this.logger.log('Updating multiple records');\n const result = await this.db\n .update(this.table as any)\n .set(data as any)\n .where(where);\n return { count: result.rowCount ?? 0 };\n }\n\n /**\n * Delete a record by ID\n *\n * @param id - The record ID\n * @returns Promise resolving to the deleted record\n *\n * @example\n * ```typescript\n * const product = await productRepository.delete('product-id-123');\n * ```\n */\n async delete(id: string): Promise<TSelect> {\n this.logger.log(`Deleting record with ID: ${id}`);\n const idColumn = (this.table as any).id;\n const results = (await this.db\n .delete(this.table as any)\n .where(eq(idColumn, id))\n .returning()) as TSelect[];\n return results[0]!;\n }\n\n /**\n * Delete multiple records\n *\n * @param where - SQL condition to match records\n * @returns Promise resolving to the count of deleted records\n *\n * @example\n * ```typescript\n * import { lt } from 'drizzle-orm';\n *\n * const result = await productRepository.deleteMany(\n * lt(products.createdAt, new Date('2020-01-01'))\n * );\n * console.log(`Deleted ${result.count} products`);\n * ```\n */\n async deleteMany(where: SQL): Promise<{ count: number }> {\n this.logger.log('Deleting multiple records');\n const result = await this.db.delete(this.table as any).where(where);\n return { count: result.rowCount ?? 0 };\n }\n\n /**\n * Count records\n *\n * @param where - Optional SQL condition to filter records\n * @returns Promise resolving to the count of records\n *\n * @example\n * ```typescript\n * import { eq } from 'drizzle-orm';\n *\n * // Count all products\n * const total = await productRepository.count();\n *\n * // Count active products\n * const activeCount = await productRepository.count(\n * eq(products.status, 'ACTIVE')\n * );\n * ```\n */\n async count(where?: SQL): Promise<number> {\n this.logger.debug('Counting records');\n\n let query = this.db\n .select({ count: sql<number>`count(*)::int` })\n .from(this.table as any)\n .$dynamic();\n\n if (where) {\n query = query.where(where);\n }\n\n const results = await query;\n return (results[0] as { count: number }).count;\n }\n\n /**\n * Check if a record exists\n *\n * @param where - SQL condition to match records\n * @returns Promise resolving to true if at least one record exists, false otherwise\n *\n * @example\n * ```typescript\n * import { eq } from 'drizzle-orm';\n *\n * const skuExists = await productRepository.exists(\n * eq(products.sku, 'WDG-001')\n * );\n * ```\n */\n async exists(where: SQL): Promise<boolean> {\n const count = await this.count(where);\n return count > 0;\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\n/**\n * Exception thrown when a gateway or proxy receives an invalid response (HTTP 502).\n * Used when a server acting as a gateway gets an error from an upstream server.\n *\n * @example\n * // Simple message\n * throw new BadGatewayException('Upstream service returned invalid response');\n *\n * // With options\n * throw new BadGatewayException({\n * title: 'Upstream Service Error',\n * detail: 'The payment service is not responding correctly',\n * instance: '/api/payments/process',\n * });\n */\nexport class BadGatewayException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Bad Gateway', HttpStatus.BAD_GATEWAY);\n }\n}\n","import { HttpException, HttpStatus } from '@nestjs/common';\nimport type { FieldError } from '../types/error-response.types';\n\n// Re-export FieldError for backwards compatibility\nexport type { FieldError } from '../types/error-response.types';\n\n/**\n * Options for creating RFC 9457 Problem Details exceptions.\n *\n * @example\n * throw new UnauthorizedException({\n * label: 'Invalid Credentials',\n * detail: 'The email or password is incorrect',\n * });\n *\n * @example\n * throw new BadRequestException({\n * detail: 'Validation failed',\n * errors: [\n * { field: 'email', message: 'Invalid email format' },\n * { field: 'password', message: 'Password too short' },\n * ],\n * });\n */\nexport interface ProblemOptions {\n /** Problem type URI (default: \"about:blank\") */\n type?: string;\n /** Root error heading (maps to AlertTitle in frontend) */\n label?: string;\n /** Root error description (maps to AlertDescription in frontend) */\n detail?: string;\n /** Field-specific errors only (field is required) */\n errors?: FieldError[];\n}\n\n/**\n * Base exception class that follows RFC 9457 Problem Details format.\n *\n * Provides a clean interface for creating HTTP exceptions with:\n * - RFC 9457 standard fields (type, title, status, detail, instance)\n * - Extension members (label for root error heading, errors for field-specific errors)\n *\n * The `title` field is always set to the HTTP status phrase (e.g., \"Unauthorized\")\n * by the HttpExceptionFilter, not by this class.\n */\nexport abstract class HttpProblemException extends HttpException {\n constructor(detailOrOptions: string | ProblemOptions, httpStatus: HttpStatus) {\n const options = typeof detailOrOptions === 'string' ? { detail: detailOrOptions } : detailOrOptions;\n\n super(\n {\n type: options.type ?? 'about:blank',\n label: options.label,\n detail: options.detail,\n errors: options.errors ?? [],\n },\n httpStatus,\n );\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\n/**\n * Exception thrown when a request is malformed or contains invalid data (HTTP 400).\n *\n * @example\n * // Simple message\n * throw new BadRequestException('Invalid request data');\n *\n * // With field errors\n * throw new BadRequestException({\n * detail: 'Validation failed',\n * errors: [\n * { field: 'email', message: 'Invalid email format' },\n * { field: 'password', message: 'Password too short' }\n * ]\n * });\n *\n * // With custom label and type\n * throw new BadRequestException({\n * label: 'Invalid Form Data',\n * detail: 'Please check your input',\n * type: 'validation-error'\n * });\n */\nexport class BadRequestException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Bad Request', HttpStatus.BAD_REQUEST);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\n/**\n * Exception thrown when a request conflicts with the current state (HTTP 409).\n * Commonly used for duplicate resources or concurrent modification issues.\n *\n * @example\n * // Simple detail message\n * throw new ConflictException('Resource already exists');\n *\n * // With custom label and detail\n * throw new ConflictException({\n * label: 'Duplicate Entry',\n * detail: 'Email already exists',\n * });\n *\n * // With field-specific errors\n * throw new ConflictException({\n * detail: 'Duplicate data detected',\n * errors: [\n * { field: 'email', message: 'Email already registered' }\n * ],\n * });\n *\n * // With custom label and field errors\n * throw new ConflictException({\n * label: 'Resource Conflict',\n * detail: 'Try logging in instead or use a different email',\n * errors: [{ field: 'email', message: 'Email already in use' }],\n * });\n */\nexport class ConflictException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Conflict', HttpStatus.CONFLICT);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\n/**\n * Exception thrown when the user does not have permission to access a resource (HTTP 403).\n *\n * @example\n * // Simple detail message\n * throw new ForbiddenException('Access denied');\n *\n * // With custom label\n * throw new ForbiddenException({\n * label: 'Access Denied',\n * detail: 'You do not have permission to perform this action',\n * });\n *\n * // With field-specific errors\n * throw new ForbiddenException({\n * label: 'Permission Denied',\n * detail: 'Contact your administrator for access',\n * errors: [{ field: 'role', message: 'Admin role required' }],\n * });\n */\nexport class ForbiddenException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Forbidden', HttpStatus.FORBIDDEN);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\n/**\n * Exception thrown when a resource has been permanently removed (HTTP 410).\n * Unlike 404, this indicates the resource existed but is intentionally gone.\n *\n * @example\n * // Simple message\n * throw new GoneException('Resource permanently deleted');\n *\n * // With label and detail\n * throw new GoneException({\n * label: 'Account Deleted',\n * detail: 'This account has been permanently removed',\n * });\n *\n * // With field errors\n * throw new GoneException({\n * label: 'Resource Removed',\n * detail: 'The resource was removed due to policy violation',\n * errors: [{ field: 'resource', message: 'This content has been permanently deleted' }],\n * });\n */\nexport class GoneException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Gone', HttpStatus.GONE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\n/**\n * Exception thrown when an unexpected server error occurs (HTTP 500).\n *\n * @example\n * // Simple message\n * throw new InternalServerErrorException('An unexpected error occurred');\n *\n * // With options object\n * throw new InternalServerErrorException({\n * title: 'Server Error',\n * detail: 'Something went wrong',\n * instance: '/api/users',\n * });\n */\nexport class InternalServerErrorException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Internal Server Error', HttpStatus.INTERNAL_SERVER_ERROR);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\n/**\n * Exception thrown when an HTTP method is not supported for the endpoint (HTTP 405).\n * For example, when a POST is sent to a GET-only endpoint.\n *\n * @example\n * // Simple message\n * throw new MethodNotAllowedException('Method not allowed');\n *\n * // With detail\n * throw new MethodNotAllowedException({\n * detail: 'Method not allowed',\n * instance: '/api/resource/123'\n * });\n *\n * // With custom title\n * throw new MethodNotAllowedException({\n * title: 'Invalid HTTP Method',\n * detail: 'This endpoint only supports GET requests',\n * });\n *\n * // With additional context\n * throw new MethodNotAllowedException({\n * title: 'Unsupported Operation',\n * detail: 'PATCH is not supported for this resource',\n * instance: '/api/users/456',\n * extensions: { allowedMethods: ['GET', 'PUT', 'DELETE'] }\n * });\n */\nexport class MethodNotAllowedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Method Not Allowed', HttpStatus.METHOD_NOT_ALLOWED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\n/**\n * Exception thrown when content negotiation fails (HTTP 406).\n * Used when the server cannot produce a response matching the Accept headers.\n *\n * @example\n * // Simple detail message\n * throw new NotAcceptableException('Requested format not available');\n *\n * // With custom label\n * throw new NotAcceptableException({\n * label: 'Content Negotiation Failed',\n * detail: 'Cannot produce response in the requested format',\n * });\n *\n * // With field-specific errors\n * throw new NotAcceptableException({\n * detail: 'Requested format is not supported',\n * errors: [\n * { field: 'accept', message: 'XML format is not available' },\n * { field: 'contentType', message: 'Only JSON is supported' },\n * ],\n * });\n *\n * // With all options\n * throw new NotAcceptableException({\n * type: 'https://api.example.com/errors/format-not-supported',\n * label: 'Unsupported Media Type',\n * detail: 'This API only supports JSON responses',\n * errors: [{ field: 'accept', message: 'XML format is not available' }],\n * });\n */\nexport class NotAcceptableException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Acceptable', HttpStatus.NOT_ACCEPTABLE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\n/**\n * Exception thrown when a requested resource cannot be found (HTTP 404).\n *\n * @example\n * // Simple message\n * throw new NotFoundException('Resource not found');\n *\n * // With custom label and detail\n * throw new NotFoundException({\n * label: 'User Not Found',\n * detail: 'The requested user does not exist',\n * });\n *\n * // With field errors\n * throw new NotFoundException({\n * detail: 'The requested resource could not be located',\n * errors: [{ field: 'userId', message: 'User does not exist' }],\n * });\n */\nexport class NotFoundException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Found', HttpStatus.NOT_FOUND);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\n/**\n * Exception thrown when a feature or endpoint is not yet implemented (HTTP 501).\n * Used for planned but unavailable functionality.\n *\n * @example\n * // Simple message\n * throw new NotImplementedException('Feature not yet implemented');\n *\n * // With options\n * throw new NotImplementedException({\n * detail: 'This feature is coming soon',\n * instance: '/api/v1/export',\n * });\n */\nexport class NotImplementedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Implemented', HttpStatus.NOT_IMPLEMENTED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\n/**\n * Exception thrown when request payload exceeds size limits (HTTP 413).\n * Commonly used for file upload size restrictions or large request bodies.\n *\n * @example\n * // Simple message\n * throw new PayloadTooLargeException('Request payload too large');\n *\n * // With detail\n * throw new PayloadTooLargeException({\n * detail: 'Request payload too large',\n * instance: '/api/upload',\n * });\n *\n * // With custom title\n * throw new PayloadTooLargeException({\n * title: 'File Size Limit Exceeded',\n * detail: 'The uploaded file is too large. Maximum size is 10MB',\n * instance: '/api/files/upload',\n * });\n */\nexport class PayloadTooLargeException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Payload Too Large', HttpStatus.PAYLOAD_TOO_LARGE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\n/**\n * Exception thrown when a request takes too long to process (HTTP 408).\n * Used when the client or server times out while waiting for completion.\n *\n * @example\n * // Simple message\n * throw new RequestTimeoutException('Request timeout');\n *\n * // With custom title and detail\n * throw new RequestTimeoutException({\n * title: 'Operation Timeout',\n * detail: 'The request took too long to complete',\n * });\n *\n * // With instance for tracking\n * throw new RequestTimeoutException({\n * title: 'Database Timeout',\n * detail: 'Query execution exceeded time limit',\n * instance: '/api/queries/123',\n * });\n */\nexport class RequestTimeoutException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Request Timeout', HttpStatus.REQUEST_TIMEOUT);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\n/**\n * Exception thrown when the service is temporarily unavailable (HTTP 503).\n * Used during maintenance, overload, or temporary outages.\n *\n * @example\n * // Simple message\n * throw new ServiceUnavailableException('Service temporarily unavailable');\n *\n * // With custom title and detail\n * throw new ServiceUnavailableException({\n * title: 'Scheduled Maintenance',\n * detail: 'Expected completion: 2 PM EST',\n * });\n *\n * // With field errors\n * throw new ServiceUnavailableException({\n * title: 'External Service Unavailable',\n * detail: 'Payment service is down',\n * errors: [{ field: 'paymentGateway', message: 'Payment gateway unavailable' }],\n * });\n */\nexport class ServiceUnavailableException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Service Unavailable', HttpStatus.SERVICE_UNAVAILABLE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\n/**\n * Exception thrown when rate limiting is triggered (HTTP 429).\n * Used to prevent abuse and ensure fair resource usage.\n *\n * @example\n * // Simple message\n * throw new TooManyRequestsException('Too many requests');\n *\n * // With custom title and detail\n * throw new TooManyRequestsException({\n * title: 'Rate Limit Exceeded',\n * detail: 'You have exceeded the allowed number of requests',\n * });\n *\n * // With field errors\n * throw new TooManyRequestsException({\n * title: 'API Throttled',\n * detail: 'Too many requests to this endpoint',\n * errors: [{ field: 'requests', message: 'Rate limit exceeded' }],\n * });\n *\n * // With instance and additional metadata\n * throw new TooManyRequestsException({\n * detail: 'Rate limit exceeded',\n * instance: '/api/v1/users',\n * retryAfter: 60,\n * limit: 100,\n * remaining: 0,\n * });\n */\nexport class TooManyRequestsException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Too Many Requests', HttpStatus.TOO_MANY_REQUESTS);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\n/**\n * Exception thrown when authentication is required or has failed (HTTP 401).\n *\n * @example\n * // Simple message\n * throw new UnauthorizedException('Authentication required');\n *\n * // With problem details\n * throw new UnauthorizedException({\n * detail: 'Invalid or expired token',\n * instance: '/api/auth/verify'\n * });\n */\nexport class UnauthorizedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unauthorized', HttpStatus.UNAUTHORIZED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\n/**\n * Exception thrown when the request is well-formed but contains semantic errors (HTTP 422).\n * Used for business logic validation failures that prevent processing.\n *\n * @example\n * // Simple message\n * throw new UnprocessableEntityException('Cannot process the request');\n *\n * // With detail\n * throw new UnprocessableEntityException({\n * detail: 'Cannot process the order due to stock limitations',\n * });\n *\n * // With custom title\n * throw new UnprocessableEntityException({\n * title: 'Business Rule Violation',\n * detail: 'Cannot process the order due to stock limitations',\n * });\n *\n * // With field errors\n * throw new UnprocessableEntityException({\n * detail: 'One or more items exceed available inventory',\n * errors: [{ field: 'quantity', message: 'Insufficient stock available' }],\n * });\n *\n * // With custom title and field errors\n * throw new UnprocessableEntityException({\n * title: 'Validation Failed',\n * detail: 'One or more items exceed available inventory',\n * errors: [{ field: 'quantity', message: 'Insufficient stock available' }],\n * });\n */\nexport class UnprocessableEntityException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unprocessable Entity', HttpStatus.UNPROCESSABLE_ENTITY);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\n/**\n * Exception thrown when the media type of the request is not supported (HTTP 415).\n * Used when the Content-Type header specifies an unsupported format.\n *\n * @example\n * // Simple message\n * throw new UnsupportedMediaTypeException('Unsupported media type');\n *\n * // With label and detail\n * throw new UnsupportedMediaTypeException({\n * label: 'Invalid Content Type',\n * detail: 'The content type is not supported',\n * });\n *\n * // With field errors\n * throw new UnsupportedMediaTypeException({\n * label: 'Unsupported File Format',\n * detail: 'Accepted formats: JPEG, PNG, GIF',\n * errors: [{ field: 'file', message: 'PDF format is not accepted for this upload' }],\n * });\n */\nexport class UnsupportedMediaTypeException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unsupported Media Type', HttpStatus.UNSUPPORTED_MEDIA_TYPE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\n/**\n * Exception thrown when request validation fails (HTTP 400).\n * Typically used for form validation or DTO validation errors.\n *\n * @example\n * // Simple message\n * throw new ValidationException('Validation failed');\n *\n * // With custom label and detail\n * throw new ValidationException({\n * label: 'Invalid Input',\n * detail: 'The provided data is invalid',\n * });\n *\n * // With field-specific errors\n * throw new ValidationException({\n * detail: 'Please correct the highlighted fields',\n * errors: [\n * { field: 'email', message: 'Invalid email format' },\n * { field: 'password', message: 'Password must be at least 8 characters' }\n * ],\n * });\n *\n * // With custom label and field errors\n * throw new ValidationException({\n * label: 'Form Validation Failed',\n * detail: 'Please correct the highlighted fields',\n * errors: [\n * { field: 'email', message: 'Invalid email format' },\n * { field: 'password', message: 'Password too weak' }\n * ],\n * });\n */\nexport class ValidationException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Validation Failed', HttpStatus.BAD_REQUEST);\n }\n}\n","import { type ArgumentsHost, Catch, type ExceptionFilter, HttpException, HttpStatus, Logger } from '@nestjs/common';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport type { ApiErrorResponse, FieldError } from '../types/error-response.types';\n\n/**\n * Shape of exception response from custom HttpProblemException.\n */\ninterface ProblemExceptionResponse {\n type?: string;\n label?: string;\n detail?: string;\n errors?: FieldError[];\n}\n\n/**\n * Shape of exception response from class-validator validation errors.\n */\ninterface ValidationExceptionResponse {\n message: Array<string | { property: string; constraints: Record<string, string> }>;\n error?: string;\n}\n\n/**\n * Shape of standard NestJS exception response.\n */\ninterface StandardExceptionResponse {\n message: string | string[];\n error?: string;\n}\n\n/**\n * Union type for all possible exception response shapes.\n */\ntype ExceptionResponseObject = ProblemExceptionResponse | ValidationExceptionResponse | StandardExceptionResponse;\n\n/**\n * Converts an HTTP status code to its corresponding title string.\n * Uses the HttpStatus enum to map status codes to human-readable titles.\n *\n * @param status - The HTTP status code\n * @returns The human-readable title for the status code\n *\n * @example\n * getHttpStatusTitle(400) // Returns: \"Bad Request\"\n * getHttpStatusTitle(404) // Returns: \"Not Found\"\n * getHttpStatusTitle(500) // Returns: \"Internal Server Error\"\n */\nexport function getHttpStatusTitle(status: number): string {\n // Find the enum key for the given status code\n const enumKey = Object.entries(HttpStatus).find(([key, value]) => value === status && Number.isNaN(Number(key)))?.[0];\n\n if (!enumKey) {\n return 'Error';\n }\n\n // Convert enum key to title case (e.g., BAD_REQUEST -> Bad Request)\n return enumKey\n .split('_')\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(' ');\n}\n\n/**\n * Global HTTP Exception Filter implementing RFC 9457 Problem Details\n *\n * Transforms all exceptions into a standardized RFC 9457 format:\n * {\n * type: string, // Problem type URI (default: \"about:blank\")\n * title: string, // HTTP status phrase (e.g., \"Unauthorized\")\n * status: number, // HTTP status code\n * label?: string, // Root error heading (maps to AlertTitle)\n * detail: string, // Root error description (maps to AlertDescription)\n * instance: string, // Request path\n * errors: FieldError[] // Field-specific errors (field is required)\n * }\n *\n * Handles:\n * - Custom HttpProblemException from @vritti/api-sdk\n * - Class-validator DTO validation errors\n * - Standard NestJS HTTP exceptions\n * - Unknown errors\n */\n@Catch()\nexport class HttpExceptionFilter implements ExceptionFilter {\n private readonly logger = new Logger(HttpExceptionFilter.name);\n\n catch(exception: unknown, host: ArgumentsHost) {\n const ctx = host.switchToHttp();\n const response = ctx.getResponse<FastifyReply>();\n const request = ctx.getRequest<FastifyRequest>();\n\n let status = HttpStatus.INTERNAL_SERVER_ERROR;\n let type = 'about:blank';\n let label: string | undefined;\n let detail = 'Internal server error';\n let errors: FieldError[] = [];\n\n if (exception instanceof HttpException) {\n status = exception.getStatus();\n const exceptionResponse = exception.getResponse();\n\n if (typeof exceptionResponse === 'object' && exceptionResponse !== null) {\n const responseObj = exceptionResponse as ExceptionResponseObject;\n\n // Handle custom HttpProblemException from @vritti/api-sdk\n if ('type' in responseObj || 'label' in responseObj || 'errors' in responseObj) {\n const problemResponse = responseObj as ProblemExceptionResponse;\n type = problemResponse.type ?? 'about:blank';\n label = problemResponse.label;\n detail = problemResponse.detail ?? exception.message ?? getHttpStatusTitle(status);\n errors = problemResponse.errors ?? [];\n }\n // Handle class-validator DTO validation errors\n else if ('message' in responseObj && Array.isArray(responseObj.message)) {\n errors = responseObj.message.map((msg) => {\n if (typeof msg === 'object' && 'property' in msg && 'constraints' in msg) {\n const constraintValues = Object.values(msg.constraints);\n return {\n field: msg.property,\n message: constraintValues[0] ?? 'Validation failed',\n };\n }\n // Non-field-specific validation messages are ignored\n // They should be handled as detail at the response level\n return null;\n }).filter((error): error is FieldError => error !== null);\n detail = 'Validation failed';\n }\n // Handle standard NestJS exceptions\n else if ('message' in responseObj) {\n const message = responseObj.message;\n detail = Array.isArray(message) ? message.join(', ') : message;\n }\n } else if (typeof exceptionResponse === 'string') {\n detail = exceptionResponse;\n }\n } else {\n // Unknown errors\n const errorMessage = exception instanceof Error ? exception.message : 'Unknown error';\n const stack = exception instanceof Error ? exception.stack : undefined;\n this.logger.error(`Unexpected error: ${errorMessage}`, stack);\n detail = 'An unexpected error occurred';\n }\n\n const problemDetails: ApiErrorResponse = {\n type,\n title: getHttpStatusTitle(status),\n status,\n ...(label && { label }),\n detail,\n instance: request.url,\n errors,\n };\n\n response\n .header('Content-Type', 'application/problem+json')\n .status(status)\n .send(problemDetails);\n }\n}\n","/**\n * Mapping of country calling codes to ISO 3166-1 alpha-2 country codes\n * Covers ~200 countries with calling codes ranging from 1-3 digits\n */\nconst CALLING_CODE_TO_COUNTRY: Record<string, string> = {\n // 3-digit codes\n '355': 'AL', '213': 'DZ', '376': 'AD', '244': 'AO', '672': 'AQ',\n '374': 'AM', '297': 'AW', '994': 'AZ', '973': 'BH', '880': 'BD',\n '375': 'BY', '501': 'BZ', '229': 'BJ', '975': 'BT', '591': 'BO',\n '387': 'BA', '267': 'BW', '673': 'BN', '359': 'BG', '226': 'BF',\n '257': 'BI', '855': 'KH', '237': 'CM', '238': 'CV', '236': 'CF',\n '235': 'TD', '269': 'KM', '242': 'CG', '243': 'CD', '506': 'CR',\n '385': 'HR', '357': 'CY', '420': 'CZ', '253': 'DJ', '593': 'EC',\n '503': 'SV', '240': 'GQ', '291': 'ER', '372': 'EE', '251': 'ET',\n '679': 'FJ', '358': 'FI', '241': 'GA', '220': 'GM', '995': 'GE',\n '233': 'GH', '350': 'GI', '299': 'GL', '502': 'GT', '224': 'GN',\n '245': 'GW', '592': 'GY', '509': 'HT', '504': 'HN', '354': 'IS',\n '964': 'IQ', '353': 'IE', '972': 'IL', '225': 'CI', '962': 'JO',\n '254': 'KE', '686': 'KI', '965': 'KW', '996': 'KG', '856': 'LA',\n '371': 'LV', '961': 'LB', '266': 'LS', '231': 'LR', '218': 'LY',\n '423': 'LI', '370': 'LT', '352': 'LU', '389': 'MK', '261': 'MG',\n '265': 'MW', '960': 'MV', '223': 'ML', '356': 'MT', '692': 'MH',\n '222': 'MR', '230': 'MU', '262': 'YT', '691': 'FM', '373': 'MD',\n '377': 'MC', '976': 'MN', '382': 'ME', '258': 'MZ', '264': 'NA',\n '674': 'NR', '977': 'NP', '505': 'NI', '227': 'NE', '234': 'NG',\n '683': 'NU', '968': 'OM', '680': 'PW', '970': 'PS', '507': 'PA',\n '675': 'PG', '595': 'PY', '351': 'PT', '974': 'QA', '250': 'RW',\n '685': 'WS', '378': 'SM', '239': 'ST', '966': 'SA', '221': 'SN',\n '381': 'RS', '248': 'SC', '232': 'SL', '421': 'SK', '386': 'SI',\n '677': 'SB', '252': 'SO', '211': 'SS', '249': 'SD', '597': 'SR',\n '268': 'SZ', '963': 'SY', '992': 'TJ', '255': 'TZ', '228': 'TG',\n '676': 'TO', '216': 'TN', '993': 'TM', '688': 'TV', '256': 'UG',\n '380': 'UA', '971': 'AE', '598': 'UY', '998': 'UZ', '678': 'VU',\n '379': 'VA', '967': 'YE', '260': 'ZM', '263': 'ZW',\n\n // 2-digit codes\n '93': 'AF', '54': 'AR', '61': 'AU', '43': 'AT', '32': 'BE',\n '55': 'BR', '56': 'CL', '86': 'CN', '57': 'CO', '53': 'CU',\n '45': 'DK', '20': 'EG', '33': 'FR', '49': 'DE', '30': 'GR',\n '36': 'HU', '91': 'IN', '62': 'ID', '98': 'IR', '39': 'IT',\n '81': 'JP', '82': 'KR', '60': 'MY', '52': 'MX', '31': 'NL',\n '64': 'NZ', '47': 'NO', '92': 'PK', '51': 'PE', '63': 'PH',\n '48': 'PL', '40': 'RO', '65': 'SG', '27': 'ZA', '34': 'ES',\n '94': 'LK', '46': 'SE', '41': 'CH', '66': 'TH', '90': 'TR',\n '44': 'GB', '58': 'VE', '84': 'VN',\n\n // 1-digit codes (shared codes default to most common country)\n '1': 'US', // Also CA, but default to US\n '7': 'RU', // Also KZ, but default to RU\n};\n\n/**\n * Extract ISO country code from E.164 phone number\n * @param phone Phone number in E.164 format (e.g., +919876543210)\n * @returns ISO 3166-1 alpha-2 country code (e.g., \"IN\") or undefined\n */\nexport function extractCountryFromPhone(phone: string): string | undefined {\n // Remove + prefix if present\n const digits = phone.startsWith('+') ? phone.slice(1) : phone;\n\n // Try matching from longest to shortest prefix (3, 2, 1 digits)\n for (const length of [3, 2, 1]) {\n const prefix = digits.slice(0, length);\n if (CALLING_CODE_TO_COUNTRY[prefix]) {\n return CALLING_CODE_TO_COUNTRY[prefix];\n }\n }\n\n return undefined;\n}\n\n/**\n * Normalize phone number to E.164 format with + prefix\n * @param phone Phone number (with or without + prefix)\n * @returns Phone number in E.164 format\n */\nexport function normalizePhoneNumber(phone: string): string {\n return phone.startsWith('+') ? phone : `+${phone}`;\n}\n","/**\n * HTTP Logger Interceptor\n *\n * Automatically logs HTTP requests and responses with correlation tracking.\n * @module logger/http-logger.interceptor\n */\n\nimport { type CallHandler, type ExecutionContext, Injectable, type NestInterceptor, Optional } from '@nestjs/common';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport type { Observable } from 'rxjs';\nimport { catchError, tap } from 'rxjs/operators';\nimport { LoggerService } from '../services/logger.service';\nimport type { HttpLoggerOptions, LogMetadata } from '../types';\nimport { getCorrelationContext } from '../utils';\n\n/**\n * HTTP Logger Interceptor for NestJS applications.\n *\n * Logs all HTTP requests and responses with metadata including\n * correlation IDs, performance metrics, and error details.\n */\n@Injectable()\nexport class HttpLoggerInterceptor implements NestInterceptor {\n private readonly enableRequestLog: boolean;\n private readonly enableResponseLog: boolean;\n private readonly slowRequestThreshold: number;\n\n constructor(\n private readonly logger: LoggerService,\n @Optional() options?: HttpLoggerOptions,\n ) {\n this.enableRequestLog = options?.enableRequestLog ?? true;\n this.enableResponseLog = options?.enableResponseLog ?? true;\n this.slowRequestThreshold = options?.slowRequestThreshold ?? 3000; // 3 seconds\n }\n\n intercept(context: ExecutionContext, next: CallHandler): Observable<any> {\n if (context.getType() !== 'http') {\n return next.handle();\n }\n\n const httpContext = context.switchToHttp();\n const request = httpContext.getRequest<FastifyRequest>();\n const response = httpContext.getResponse<FastifyReply>();\n\n const startTime = Date.now();\n\n // Log incoming request\n if (this.enableRequestLog) {\n this.logRequest(request);\n }\n\n // Process request and log response/errors\n return next.handle().pipe(\n tap(() => {\n if (this.enableResponseLog) {\n const duration = Date.now() - startTime;\n this.logResponse(request, response, duration);\n }\n }),\n catchError((error) => {\n const duration = Date.now() - startTime;\n this.logError(request, response, duration, error);\n throw error;\n }),\n );\n }\n\n private logRequest(request: FastifyRequest): void {\n try {\n const correlationContext = getCorrelationContext();\n const metadata: LogMetadata = {\n type: 'http_request',\n method: request.method,\n url: request.url,\n correlationId: correlationContext?.correlationId,\n ip: request.ip,\n userAgent: request.headers['user-agent'],\n };\n\n this.logger.logWithMetadata('log', `Incoming ${request.method} ${request.url}`, metadata);\n } catch (error) {\n this.logger.error('Failed to log HTTP request', (error as Error).stack);\n }\n }\n\n private logResponse(request: FastifyRequest, response: FastifyReply, duration: number): void {\n try {\n const correlationContext = getCorrelationContext();\n const statusCode = response.statusCode;\n\n // Determine log level based on status code\n const logLevel = statusCode >= 500 ? 'error' : statusCode >= 400 ? 'warn' : 'log';\n\n const metadata: LogMetadata = {\n type: 'http_response',\n method: request.method,\n url: request.url,\n statusCode,\n duration,\n correlationId: correlationContext?.correlationId,\n };\n\n // Flag slow requests\n if (duration > this.slowRequestThreshold) {\n metadata.slowRequest = true;\n }\n\n const message = metadata.slowRequest\n ? `SLOW ${request.method} ${request.url} ${statusCode} - ${duration}ms`\n : `${request.method} ${request.url} ${statusCode} - ${duration}ms`;\n\n this.logger.logWithMetadata(logLevel, message, metadata);\n } catch (error) {\n this.logger.error('Failed to log HTTP response', (error as Error).stack);\n }\n }\n\n private logError(request: FastifyRequest, response: FastifyReply, duration: number, error: any): void {\n try {\n const correlationContext = getCorrelationContext();\n const statusCode = response.statusCode || 500;\n\n const metadata: LogMetadata = {\n type: 'http_error',\n method: request.method,\n url: request.url,\n statusCode,\n duration,\n correlationId: correlationContext?.correlationId,\n errorName: error?.name || 'Error',\n errorMessage: error?.message || 'Unknown error',\n };\n\n if (error?.stack) {\n metadata.trace = error.stack;\n }\n\n if (error?.response) {\n metadata.errorDetails = error.response;\n }\n\n const message = `ERROR ${request.method} ${request.url} ${statusCode} - ${error?.message || 'Unknown error'}`;\n this.logger.logWithMetadata('error', message, metadata);\n } catch (loggingError) {\n this.logger.error('Failed to log HTTP error', (loggingError as Error).stack);\n }\n }\n}\n","/**\n * Unified Logger Service\n *\n * Single service that provides both default NestJS Logger and Winston logger implementations.\n * Automatically delegates to the configured provider (default or winston).\n * @module logger/logger.service\n */\n\nimport { Injectable, type Logger, type LoggerService as NestLoggerService, Optional } from '@nestjs/common';\nimport { createLogger, format, transports, type Logger as WinstonLogger } from 'winston';\nimport DailyRotateFile from 'winston-daily-rotate-file';\nimport type { LoggerModuleOptions, LogLevel, LogMetadata } from '../types';\nimport { getCorrelationContext } from '../utils';\n\n/**\n * Type for log message parameter.\n * NestJS LoggerService interface uses `any` for compatibility,\n * but this type documents the expected message shapes.\n *\n * @remarks\n * The public interface methods use `any` to maintain compatibility with\n * NestJS's LoggerService interface. This type is for documentation purposes.\n */\nexport type LogMessage = string | Error | object;\n\n/**\n * Unified logger service implementing NestJS LoggerService interface.\n * Supports both default NestJS Logger and Winston implementations via facade pattern.\n */\n@Injectable()\nexport class LoggerService implements NestLoggerService {\n private readonly activeLogger: NestLoggerService | WinstonLogger;\n private readonly options: LoggerModuleOptions;\n private context?: string;\n\n constructor(\n @Optional() options: LoggerModuleOptions = {},\n @Optional() private readonly defaultLogger?: Logger,\n ) {\n this.options = options;\n const provider = options.provider ?? 'winston';\n\n if (provider === 'default') {\n if (!this.defaultLogger) {\n throw new Error('LoggerService: Default Logger not provided');\n }\n this.activeLogger = this.defaultLogger;\n } else {\n this.activeLogger = this.createWinstonLogger(options);\n }\n }\n\n /**\n * Creates a Winston logger instance with inline configuration.\n * Consolidates winston-config.factory.ts logic.\n */\n private createWinstonLogger(opts: LoggerModuleOptions): WinstonLogger {\n const level = opts.level ?? 'debug';\n const logFormat = opts.format ?? 'text';\n\n // Base formatters\n // Winston automatically merges metadata into the info object, so all properties\n // (context, correlationId, etc.) are already at the top level\n const baseFormatters = [format.timestamp({ format: 'YYYY-MM-DDTHH:mm:ss.SSSZ' }), format.errors({ stack: true })];\n\n // Console transport\n const consoleTransport =\n logFormat === 'json'\n ? new transports.Console({\n level,\n format: format.combine(...baseFormatters, format.json()),\n })\n : new transports.Console({\n level,\n format: format.combine(\n ...baseFormatters,\n format.printf((info) => {\n const { timestamp, level, message, context, correlationId, trace } = info;\n const parts = [\n timestamp,\n level.toUpperCase().padEnd(7),\n correlationId ? `[${correlationId.toString().slice(-6)}]` : '',\n context ? `[${context}]` : '',\n message,\n ].filter(Boolean);\n let output = parts.join(' ');\n\n // Append stack trace on new line if present\n if (trace) {\n output += `\\n${trace}`;\n }\n\n return output;\n }),\n format.colorize({ all: true }),\n ),\n });\n\n const winstonTransports: any[] = [consoleTransport];\n\n // File transports\n if (opts.enableFileLogger) {\n const filePath = opts.filePath ?? './logs';\n const maxFiles = opts.maxFiles ?? '14d';\n\n winstonTransports.push(\n new DailyRotateFile({\n level,\n filename: `${filePath}/%DATE%-combined.log`,\n datePattern: 'YYYY-MM-DD',\n maxSize: '20m',\n maxFiles,\n format: format.combine(format.timestamp(), format.json()),\n }),\n new DailyRotateFile({\n level: 'error',\n filename: `${filePath}/%DATE%-error.log`,\n datePattern: 'YYYY-MM-DD',\n maxSize: '20m',\n maxFiles,\n format: format.combine(format.timestamp(), format.json()),\n }),\n );\n }\n\n const config: any = {\n level,\n transports: winstonTransports,\n exitOnError: false,\n };\n\n if (opts.defaultMeta || opts.appName) {\n config.defaultMeta = {\n ...opts.defaultMeta,\n appName: opts.appName,\n environment: opts.environment,\n };\n }\n\n return createLogger(config);\n }\n\n // NestJS LoggerService interface methods\n log(message: any, context?: string): void {\n this._log('log', message, context);\n }\n\n error(message: any, trace?: string, context?: string): void {\n this._log('error', message, context, trace);\n }\n\n warn(message: any, context?: string): void {\n this._log('warn', message, context);\n }\n\n debug(message: any, context?: string): void {\n this._log('debug', message, context);\n }\n\n verbose(message: any, context?: string): void {\n this._log('verbose', message, context);\n }\n\n setContext(context: string): void {\n this.context = context;\n }\n\n /**\n * Unified internal logging method that handles both Winston and NestJS Logger.\n */\n private _log(level: LogLevel, message: any, context?: string, trace?: string): void {\n const ctx = context ?? this.context;\n\n // Check if Winston logger by duck typing\n if ('format' in this.activeLogger && 'transports' in this.activeLogger) {\n // Winston logger path\n const winstonLogger = this.activeLogger as WinstonLogger;\n const winstonLevel = level === 'log' ? 'info' : level;\n const formattedMessage = this.formatMessage(message);\n const metadata = this.enrichMetadata({}, ctx, trace);\n // Winston merges all properties into the info object when using object syntax\n winstonLogger.log({ level: winstonLevel, message: formattedMessage, ...metadata });\n } else {\n // NestJS logger path\n const nestLogger = this.activeLogger as Logger;\n if (level === 'error' && trace) {\n ctx ? nestLogger.error(message, trace, ctx) : nestLogger.error(message, trace);\n } else if (level === 'log') {\n ctx ? nestLogger.log(message, ctx) : nestLogger.log(message);\n } else if (level === 'warn') {\n ctx ? nestLogger.warn(message, ctx) : nestLogger.warn(message);\n } else if (level === 'debug' && nestLogger.debug) {\n ctx ? nestLogger.debug(message, ctx) : nestLogger.debug(message);\n } else if (level === 'verbose' && nestLogger.verbose) {\n ctx ? nestLogger.verbose(message, ctx) : nestLogger.verbose(message);\n }\n }\n }\n\n /**\n * Logs with custom metadata (Winston only).\n */\n logWithMetadata(level: LogLevel, message: any, metadata?: LogMetadata, context?: string): void {\n const ctx = context ?? this.context;\n\n // Check if Winston logger by duck typing\n if ('format' in this.activeLogger && 'transports' in this.activeLogger) {\n const winstonLogger = this.activeLogger as WinstonLogger;\n const winstonLevel = level === 'log' ? 'info' : level;\n // const enriched = this.enrichMetadata(metadata, ctx);\n // Winston merges all properties into the info object when using object syntax\n winstonLogger.log({ level: winstonLevel, message: this.formatMessage(message), ...metadata });\n } else {\n // Fallback for default logger\n const messageWithMeta = metadata ? `${message} ${JSON.stringify(metadata)}` : message;\n this[level](messageWithMeta, ctx);\n }\n }\n\n private formatMessage(message: any): string {\n if (message instanceof Error) return message.message;\n if (typeof message === 'object' && message !== null) {\n try {\n return JSON.stringify(message);\n } catch {\n return String(message);\n }\n }\n return String(message);\n }\n\n /**\n * Enriches metadata with correlation context from AsyncLocalStorage.\n * Inline from winston-logger.service.ts\n */\n private enrichMetadata(metadata: LogMetadata = {}, context?: string, trace?: string): LogMetadata {\n const enriched: LogMetadata = { ...metadata };\n\n if (context) enriched.context = context;\n\n const correlationContext = getCorrelationContext();\n if (correlationContext) {\n if (correlationContext.correlationId) enriched.correlationId = correlationContext.correlationId;\n for (const [key, value] of Object.entries(correlationContext)) {\n if (key !== 'correlationId') {\n enriched[key] = value;\n }\n }\n }\n\n if (trace) enriched.trace = trace;\n\n return enriched;\n }\n\n child(context: string): LoggerService {\n const childLogger = new LoggerService(this.options, this.defaultLogger);\n childLogger.setContext(context);\n return childLogger;\n }\n}\n","/**\n * Logging Utilities\n *\n * Consolidated utilities for correlation tracking, PII masking, and async context management.\n * @module logging/utils\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport { randomUUID } from 'node:crypto';\nimport type { FastifyReply } from 'fastify';\nimport type { CorrelationContext } from '../types';\n\n// ============================================================================\n// Async Context Management (AsyncLocalStorage)\n// ============================================================================\n\n/**\n * Async local storage for correlation context tracking across async operations.\n */\nexport const correlationStorage = new AsyncLocalStorage<CorrelationContext>();\n\n/**\n * Gets the current correlation context from async local storage.\n */\nexport function getCorrelationContext(): CorrelationContext | undefined {\n return correlationStorage.getStore();\n}\n\n/**\n * Runs a callback within a correlation context.\n */\nexport function runWithCorrelationContext<T>(context: CorrelationContext, callback: () => T): T {\n return correlationStorage.run(context, callback);\n}\n\n/**\n * Updates the current correlation context with new values.\n */\nexport function updateCorrelationContext(updates: Partial<CorrelationContext>): void {\n const context = correlationStorage.getStore();\n if (context) {\n Object.assign(context, updates);\n }\n}\n\n// ============================================================================\n// Correlation ID Management\n// ============================================================================\n\n/**\n * Default header name for setting correlation ID in responses.\n */\nexport const DEFAULT_CORRELATION_HEADER = 'x-correlation-id';\n\n/**\n * Generates a new correlation ID using UUID v4.\n * Always creates a fresh ID for each request.\n */\nexport function generateCorrelationId(): string {\n return randomUUID();\n}\n\n/**\n * Adds correlation ID to Fastify response headers.\n */\nexport function addCorrelationIdToResponse(\n reply: FastifyReply,\n correlationId: string,\n headerName: string = DEFAULT_CORRELATION_HEADER,\n): void {\n if (typeof reply.header === 'function') {\n reply.header(headerName, correlationId);\n } else if (reply.raw && typeof reply.raw.setHeader === 'function') {\n reply.raw.setHeader(headerName, correlationId);\n }\n}\n","/**\n * Logger Module\n *\n * Dynamic NestJS module providing unified logging infrastructure with:\n * - Environment presets (development, staging, production, test)\n * - Transparent switching between default NestJS Logger and Winston\n * - Correlation ID tracking via middleware\n * - HTTP request/response logging via interceptor\n * - PII masking and file logging support\n *\n * @module logger/logger.module\n */\n\nimport {\n type DynamicModule,\n Global,\n Logger,\n type MiddlewareConsumer,\n Module,\n type NestModule,\n type Provider,\n} from '@nestjs/common';\nimport { HttpLoggerInterceptor } from './interceptors/http-logger.interceptor';\nimport { CorrelationIdMiddleware } from './middleware/correlation-id.middleware';\nimport { LoggerService } from './services/logger.service';\nimport type { LoggerModuleAsyncOptions, LoggerModuleOptions, LoggerOptionsFactory } from './types';\n\n// ============================================================================\n// Constants (inline from constants.ts)\n// ============================================================================\n\n/**\n * Dependency injection token for logger module options\n */\nexport const LOGGER_MODULE_OPTIONS = Symbol('LOGGER_MODULE_OPTIONS');\n\n/**\n * Default options for the logger module\n */\nconst DEFAULT_LOGGER_OPTIONS = {\n provider: 'winston' as const,\n enableCorrelationId: true,\n enableHttpLogger: true,\n filePath: './logs',\n maxFiles: '14d',\n} as const;\n\n// ============================================================================\n// Environment Presets (NEW - replaces process.env auto-detection)\n// ============================================================================\n\n/**\n * Predefined environment configurations.\n * Users must explicitly pass `environment` option to select a preset.\n * All preset values can be overridden by passing explicit options.\n *\n * @example\n * ```typescript\n * // Use production preset\n * LoggerModule.forRoot({\n * environment: 'production',\n * appName: 'my-service'\n * })\n *\n * // Use development preset with custom level override\n * LoggerModule.forRoot({\n * environment: 'development',\n * level: 'verbose' // Overrides preset's 'debug'\n * })\n * ```\n */\nconst ENVIRONMENT_PRESETS: Record<string, Partial<LoggerModuleOptions>> = {\n /**\n * Development preset - maximum verbosity for local development\n */\n development: {\n provider: 'winston',\n level: 'debug',\n format: 'text',\n enableFileLogger: false,\n enableCorrelationId: true,\n enableHttpLogger: true,\n httpLogger: {\n enableRequestLog: true,\n enableResponseLog: true,\n slowRequestThreshold: 1000, // 1 second - lower threshold for dev\n },\n },\n\n /**\n * Staging preset - moderate verbosity with file logging\n */\n staging: {\n provider: 'winston',\n level: 'log',\n format: 'json',\n enableFileLogger: true,\n enableCorrelationId: true,\n enableHttpLogger: true,\n httpLogger: {\n enableRequestLog: true,\n enableResponseLog: true,\n slowRequestThreshold: 3000, // 3 seconds\n },\n },\n\n /**\n * Production preset - minimal verbosity with all safety features enabled\n */\n production: {\n provider: 'winston',\n level: 'warn',\n format: 'json',\n enableFileLogger: true,\n enableCorrelationId: true,\n enableHttpLogger: true,\n httpLogger: {\n enableRequestLog: false, // Reduce noise in production\n enableResponseLog: true,\n slowRequestThreshold: 5000, // 5 seconds - higher threshold for prod\n },\n },\n\n /**\n * Test preset - errors only, minimal features for faster test execution\n */\n test: {\n provider: 'winston',\n level: 'error',\n format: 'json',\n enableFileLogger: false,\n enableCorrelationId: false,\n enableHttpLogger: false,\n },\n} as const;\n\n// ============================================================================\n// Configuration Merging (refactored to use presets instead of process.env)\n// ============================================================================\n\n/**\n * Merges user-provided options with environment preset defaults.\n *\n * Merge order (later overrides earlier):\n * 1. DEFAULT_LOGGER_OPTIONS (base defaults)\n * 2. Environment preset (if `environment` option is provided)\n * 3. User-provided options (highest priority)\n *\n * **IMPORTANT**: No process.env access. Users must explicitly pass `environment`.\n *\n * @param options - User-provided logger options\n * @returns Merged options with all defaults applied\n *\n * @example\n * ```typescript\n * // With environment preset\n * const merged = mergeWithDefaults({ environment: 'production', appName: 'my-app' });\n * // Returns: production preset + { appName: 'my-app' }\n *\n * // Without environment (uses development as fallback)\n * const merged = mergeWithDefaults({ level: 'verbose' });\n * // Returns: development preset + { level: 'verbose' }\n * ```\n */\nfunction mergeWithDefaults(options: LoggerModuleOptions = {}): LoggerModuleOptions {\n // Select preset based on explicit environment option (defaults to development)\n const preset = options.environment\n ? (ENVIRONMENT_PRESETS[options.environment] ?? ENVIRONMENT_PRESETS.development)\n : ENVIRONMENT_PRESETS.development;\n\n // Filter out undefined values from user options to avoid overriding preset defaults\n const filteredOptions = Object.fromEntries(Object.entries(options).filter(([_, value]) => value !== undefined));\n\n // Handle nested httpLogger object - merge with preset httpLogger if both exist\n if (filteredOptions.httpLogger && preset?.httpLogger) {\n filteredOptions.httpLogger = {\n ...preset.httpLogger,\n ...Object.fromEntries(Object.entries(filteredOptions.httpLogger).filter(([_, value]) => value !== undefined)),\n };\n }\n\n // Merge: base defaults < preset < user options (with undefined values removed)\n const merged = {\n ...DEFAULT_LOGGER_OPTIONS,\n ...preset,\n ...filteredOptions,\n };\n\n return merged;\n}\n\n// ============================================================================\n// Provider Factories (inline from logging.providers.ts)\n// ============================================================================\n\n/**\n * Creates the default NestJS Logger provider.\n * Creates a fresh Logger instance to avoid circular reference issues.\n *\n * @param options - Merged logger module options\n * @returns Logger provider\n */\nfunction createDefaultLoggerProvider(options: LoggerModuleOptions): Provider {\n return {\n provide: Logger,\n useFactory: () => {\n const logger = new Logger();\n\n // Set log levels if specified and method exists\n if (options.level && typeof (logger as any).setLogLevels === 'function') {\n const levels = getLevelsUpTo(options.level);\n (logger as any).setLogLevels(levels);\n }\n\n return logger;\n },\n };\n}\n\n/**\n * Creates all logger providers based on the configuration.\n *\n * @param options - User-provided logger options (will be merged with defaults)\n * @returns Complete array of providers for the logger module\n */\nfunction createLoggerProviders(options: LoggerModuleOptions = {}): Provider[] {\n // Merge user options with preset defaults\n const mergedOptions = mergeWithDefaults(options);\n\n // Base providers (always included)\n const providers: Provider[] = [\n // Options provider\n {\n provide: LOGGER_MODULE_OPTIONS,\n useValue: mergedOptions,\n },\n ];\n\n // Default logger provider (only if using default provider)\n if (mergedOptions.provider === 'default') {\n providers.push(createDefaultLoggerProvider(mergedOptions));\n }\n\n // Unified LoggerService facade (always included)\n providers.push({\n provide: LoggerService,\n useFactory: (opts: LoggerModuleOptions, defaultLogger?: Logger) => {\n return new LoggerService(opts, defaultLogger);\n },\n inject: [LOGGER_MODULE_OPTIONS, { token: Logger, optional: true }],\n });\n\n // Correlation ID middleware\n providers.push({\n provide: CorrelationIdMiddleware,\n useFactory: () => {\n return new CorrelationIdMiddleware({\n includeInResponse: true,\n responseHeader: 'x-correlation-id',\n });\n },\n });\n\n // HTTP logger interceptor\n providers.push({\n provide: HttpLoggerInterceptor,\n useFactory: (logger: LoggerService, opts: LoggerModuleOptions) => {\n // Use detailed httpLogger config if provided, otherwise fall back to simple enableHttpLogger\n const httpLoggerOptions = opts.httpLogger ?? {\n enableRequestLog: opts.enableHttpLogger,\n enableResponseLog: opts.enableHttpLogger,\n };\n return new HttpLoggerInterceptor(logger, httpLoggerOptions);\n },\n inject: [LoggerService, LOGGER_MODULE_OPTIONS],\n });\n\n return providers;\n}\n\n/** Valid NestJS log levels */\ntype NestLogLevel = 'error' | 'warn' | 'log' | 'debug' | 'verbose';\n\n/**\n * Helper function to get all log levels up to and including the specified level.\n */\nfunction getLevelsUpTo(level: string): NestLogLevel[] {\n const allLevels: NestLogLevel[] = ['error', 'warn', 'log', 'debug', 'verbose'];\n\n // Check if level is a valid NestLogLevel\n const isValidLevel = (l: string): l is NestLogLevel => allLevels.includes(l as NestLogLevel);\n\n if (!isValidLevel(level)) {\n return ['error', 'warn', 'log'];\n }\n\n const levelIndex = allLevels.indexOf(level);\n return allLevels.slice(0, levelIndex + 1);\n}\n\n// ============================================================================\n// Logger Module\n// ============================================================================\n\n/**\n * Global logger module providing unified logging infrastructure.\n *\n * Features:\n * - Environment presets (development, staging, production, test)\n * - Single `LoggerService` interface for all logging needs\n * - Transparent provider switching (default ↔ Winston)\n * - Correlation ID tracking across async operations\n * - HTTP request/response logging\n * - PII masking for GDPR compliance\n * - File-based logging with rotation\n *\n * @example\n * ```typescript\n * // Production environment with explicit config\n * @Module({\n * imports: [\n * LoggerModule.forRoot({\n * environment: 'production',\n * appName: 'my-service'\n * })\n * ],\n * })\n * export class AppModule {}\n *\n * // Development environment with custom override\n * @Module({\n * imports: [\n * LoggerModule.forRoot({\n * environment: 'development',\n * level: 'verbose' // Override preset's debug\n * })\n * ],\n * })\n * export class AppModule {}\n *\n * // Use default NestJS logger\n * @Module({\n * imports: [\n * LoggerModule.forRoot({\n * provider: 'default',\n * environment: 'development'\n * })\n * ],\n * })\n * export class AppModule {}\n *\n * // Dynamic configuration with ConfigService\n * @Module({\n * imports: [\n * LoggerModule.forRootAsync({\n * imports: [ConfigModule],\n * useFactory: (config: ConfigService) => ({\n * environment: config.get('NODE_ENV', 'development'),\n * provider: config.get('LOG_PROVIDER', 'winston'),\n * appName: config.get('APP_NAME')\n * }),\n * inject: [ConfigService]\n * })\n * ],\n * })\n * export class AppModule {}\n * ```\n */\n@Global()\n@Module({})\nexport class LoggerModule implements NestModule {\n /**\n * Configures the logger module with static options.\n *\n * Users must explicitly pass `environment` to select a preset.\n * All preset values can be overridden by passing explicit options.\n *\n * @param options - Logger configuration options\n * @returns Dynamic module configuration\n *\n * @example\n * ```typescript\n * // Production preset with app name\n * LoggerModule.forRoot({\n * environment: 'production',\n * appName: 'my-service'\n * })\n *\n * // Development preset with custom level\n * LoggerModule.forRoot({\n * environment: 'development',\n * level: 'verbose',\n * enableFileLogger: true\n * })\n *\n * // Use default NestJS logger\n * LoggerModule.forRoot({\n * provider: 'default',\n * environment: 'development'\n * })\n * ```\n */\n static forRoot(options: LoggerModuleOptions = {}): DynamicModule {\n const providers = createLoggerProviders(options);\n\n return {\n module: LoggerModule,\n providers,\n exports: [LoggerService, CorrelationIdMiddleware, HttpLoggerInterceptor, LOGGER_MODULE_OPTIONS],\n };\n }\n\n /**\n * Configures the logger module with async options.\n *\n * Supports dynamic configuration using:\n * - `useFactory`: Factory function with dependency injection\n * - `useClass`: Class implementing `LoggerOptionsFactory`\n * - `useExisting`: Existing provider implementing `LoggerOptionsFactory`\n *\n * Options from the factory/class are merged with environment preset defaults.\n *\n * @param options - Async configuration options\n * @returns Dynamic module configuration\n *\n * @example\n * ```typescript\n * // Factory with ConfigService\n * LoggerModule.forRootAsync({\n * imports: [ConfigModule],\n * useFactory: (config: ConfigService) => ({\n * environment: config.get('NODE_ENV', 'development'),\n * provider: config.get('LOG_PROVIDER', 'winston'),\n * level: config.get('LOG_LEVEL'),\n * appName: config.get('APP_NAME'),\n * }),\n * inject: [ConfigService]\n * })\n *\n * // Factory class\n * @Injectable()\n * class LoggerConfigService implements LoggerOptionsFactory {\n * createLoggerOptions(): LoggerModuleOptions {\n * return {\n * environment: 'production',\n * appName: 'my-service'\n * };\n * }\n * }\n *\n * LoggerModule.forRootAsync({\n * useClass: LoggerConfigService\n * })\n * ```\n */\n static forRootAsync(options: LoggerModuleAsyncOptions): DynamicModule {\n const asyncProviders = LoggerModule.createAsyncProviders(options);\n\n return {\n module: LoggerModule,\n imports: options.imports || [],\n providers: [\n ...asyncProviders,\n // Default logger provider\n {\n provide: Logger,\n useFactory: (opts: LoggerModuleOptions) => {\n if (opts.provider === 'default') {\n const logger = new Logger();\n if (opts.level && typeof (logger as any).setLogLevels === 'function') {\n const levels = getLevelsUpTo(opts.level);\n (logger as any).setLogLevels(levels);\n }\n return logger;\n }\n return null;\n },\n inject: [LOGGER_MODULE_OPTIONS],\n },\n // Unified logger service\n {\n provide: LoggerService,\n useFactory: (opts: LoggerModuleOptions, defaultLogger?: Logger) => {\n return new LoggerService(opts, defaultLogger);\n },\n inject: [LOGGER_MODULE_OPTIONS, { token: Logger, optional: true }],\n },\n // Correlation ID middleware\n {\n provide: CorrelationIdMiddleware,\n useFactory: () => {\n return new CorrelationIdMiddleware({\n includeInResponse: true,\n responseHeader: 'x-correlation-id',\n });\n },\n },\n // HTTP logger interceptor\n {\n provide: HttpLoggerInterceptor,\n useFactory: (logger: LoggerService, opts: LoggerModuleOptions) => {\n // Use detailed httpLogger config if provided, otherwise fall back to simple enableHttpLogger\n const httpLoggerOptions = opts.httpLogger ?? {\n enableRequestLog: opts.enableHttpLogger,\n enableResponseLog: opts.enableHttpLogger,\n };\n return new HttpLoggerInterceptor(logger, httpLoggerOptions);\n },\n inject: [LoggerService, LOGGER_MODULE_OPTIONS],\n },\n ],\n exports: [LoggerService, CorrelationIdMiddleware, HttpLoggerInterceptor, LOGGER_MODULE_OPTIONS],\n };\n }\n\n /**\n * Configures middleware for the module.\n * Middleware is registered globally in main.ts using Fastify hooks.\n */\n configure(_consumer: MiddlewareConsumer): void {\n // Middleware is registered globally in main.ts using Fastify's addHook('onRequest')\n // This avoids DI issues with the middleware constructor\n }\n\n /**\n * Creates async providers for dynamic module configuration.\n */\n private static createAsyncProviders(options: LoggerModuleAsyncOptions): Provider[] {\n if (options.useFactory) {\n return [LoggerModule.createAsyncOptionsProvider(options)];\n }\n\n const providers: Provider[] = [LoggerModule.createAsyncOptionsProvider(options)];\n\n if (options.useClass) {\n providers.push({\n provide: options.useClass,\n useClass: options.useClass,\n });\n }\n\n return providers;\n }\n\n /**\n * Creates the async options provider.\n */\n private static createAsyncOptionsProvider(options: LoggerModuleAsyncOptions): Provider {\n if (options.useFactory) {\n return {\n provide: LOGGER_MODULE_OPTIONS,\n useFactory: async (...args: any[]) => {\n const userOptions = await options.useFactory?.(...args);\n return mergeWithDefaults(userOptions);\n },\n inject: (options.inject || []) as any[],\n };\n }\n\n if (options.useClass) {\n return {\n provide: LOGGER_MODULE_OPTIONS,\n useFactory: async (optionsFactory: LoggerOptionsFactory) => {\n const userOptions = await optionsFactory.createLoggerOptions();\n return mergeWithDefaults(userOptions);\n },\n inject: [options.useClass],\n };\n }\n\n if (options.useExisting) {\n return {\n provide: LOGGER_MODULE_OPTIONS,\n useFactory: async (optionsFactory: LoggerOptionsFactory) => {\n const userOptions = await optionsFactory.createLoggerOptions();\n return mergeWithDefaults(userOptions);\n },\n inject: [options.useExisting],\n };\n }\n\n throw new Error('LoggerModule.forRootAsync() requires one of: useFactory, useClass, or useExisting');\n }\n}\n","/**\n * Correlation ID Middleware\n *\n * Generates unique correlation IDs for request tracking across async operations.\n * Stores correlation ID in AsyncLocalStorage for access throughout the request lifecycle.\n * @module logger/correlation-id.middleware\n */\n\nimport { Injectable, type NestMiddleware } from '@nestjs/common';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport {\n addCorrelationIdToResponse,\n correlationStorage,\n DEFAULT_CORRELATION_HEADER,\n generateCorrelationId,\n runWithCorrelationContext,\n} from '../utils';\n\n/**\n * Configuration options for the Correlation ID middleware.\n */\nexport interface CorrelationIdMiddlewareOptions {\n /**\n * If true, adds the correlation ID to response headers.\n * @default true\n */\n includeInResponse?: boolean;\n\n /**\n * The header name to use when adding correlation ID to response.\n * @default 'x-correlation-id'\n */\n responseHeader?: string;\n}\n\n/**\n * Correlation ID Middleware for Fastify/NestJS applications.\n *\n * Generates a unique correlation ID for each request,\n * stores it in AsyncLocalStorage for access throughout the request lifecycle,\n * and optionally adds it to response headers.\n */\n@Injectable()\nexport class CorrelationIdMiddleware implements NestMiddleware {\n private readonly includeInResponse: boolean;\n private readonly responseHeader: string;\n\n constructor(options: CorrelationIdMiddlewareOptions = {}) {\n this.includeInResponse = options.includeInResponse ?? true;\n this.responseHeader = options.responseHeader ?? DEFAULT_CORRELATION_HEADER;\n }\n\n /**\n * Middleware handler for processing requests.\n */\n use(_req: FastifyRequest, reply: FastifyReply, next: () => void): void {\n // Generate new correlation ID for this request\n const correlationId = generateCorrelationId();\n\n // Add to response headers if enabled\n if (this.includeInResponse) {\n addCorrelationIdToResponse(reply, correlationId, this.responseHeader);\n }\n\n // Run the rest of the request in AsyncLocalStorage context\n runWithCorrelationContext({ correlationId }, () => {\n next();\n });\n }\n\n /**\n * Fastify hook handler for onRequest.\n * This is an async function that returns a Promise, ensuring the AsyncLocalStorage\n * context persists throughout the entire request lifecycle.\n */\n async onRequest(_req: FastifyRequest, reply: FastifyReply): Promise<void> {\n // Generate new correlation ID for this request\n const correlationId = generateCorrelationId();\n\n // Add to response headers if enabled\n if (this.includeInResponse) {\n addCorrelationIdToResponse(reply, correlationId, this.responseHeader);\n }\n\n // Store in AsyncLocalStorage for the request lifecycle\n // Note: We don't wrap in runWithCorrelationContext here because\n // Fastify's async context tracking handles it automatically\n const store = correlationStorage.getStore();\n if (!store) {\n // Initialize new store\n correlationStorage.enterWith({ correlationId });\n }\n }\n}\n"],"mappings":"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EAAM,OAAO;EAC3D,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAC3D,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAC3D,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAC3D,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAC3D,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAC3D,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAC3D,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAC3D,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAC3D,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAC3D,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAC3D,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAC3D,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAC3D,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAC3D,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAC3D,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAC3D,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAC3D,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAC3D,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAC3D,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAC3D,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;EAC3D,OAAO;EAAM,OAAO;EAAM,OAAO;EAAM,OAAO;;EAG9C,MAAM;EAAM,MAAM;EAAM,MAAM;EAAM,MAAM;EAAM,MAAM;EACtD,MAAM;EAAM,MAAM;EAAM,MAAM;EAAM,MAAM;EAAM,MAAM;EACtD,MAAM;EAAM,MAAM;EAAM,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","names":["ForbiddenException","InternalServerErrorException","UnauthorizedException","import_common","import_config","import_core","import_jwt","import_common","defaultConfig","cookie","refreshCookieName","refreshCookieMaxAge","refreshCookiePath","refreshCookieSecure","process","env","NODE_ENV","refreshCookieSameSite","refreshCookieDomain","jwt","accessTokenExpiry","refreshTokenExpiry","onboardingTokenExpiry","validateTokenBinding","guard","tenantHeaderName","authHeaderName","tokenPrefix","currentConfig","defineConfig","config","configureApiSdk","userConfig","getConfig","resetConfig","getRefreshCookieOptions","options","httpOnly","secure","sameSite","path","maxAge","domain","getJwtExpiry","access","refresh","onboarding","RequestService","request","getTenantIdentifier","getHeader","key","value","headers","Array","isArray","getAccessToken","authHeader","authorization","type","token","split","getRefreshToken","cookies","config","getConfig","refreshToken","cookie","refreshCookieName","_error","getAllHeaders","scope","Scope","REQUEST","RequestModule","providers","RequestService","exports","import_common","import_config","import_core","import_common","SKIP_CSRF_KEY","SkipCsrf","SetMetadata","import_common","DATABASE_MODULE_OPTIONS","Symbol","PrimaryDatabaseService","logger","Logger","name","pool","db","tenantConfigCache","Map","cacheTTL","options","connectionCacheTTL","onModuleInit","primaryDb","initializeDrizzleClient","databaseUrl","buildPrimaryDbUrl","Pool","connectionString","max","maxConnections","debug","Object","keys","drizzleSchema","join","drizzleRelations","drizzle","client","schema","relations","query","log","error","InternalServerErrorException","Error","host","port","username","password","database","sslMode","url","encodeURIComponent","params","URLSearchParams","set","queryString","toString","maskPassword","replace","getTenantInfo","tenantIdentifier","cached","get","tenants","tenantDatabaseConfigs","result","select","from","leftJoin","eq","id","tenantId","where","or","subdomain","limit","length","warn","row","tenant","config","tenant_database_configs","status","info","type","dbType","schemaName","dbSchema","undefined","databaseName","dbName","databaseHost","dbHost","databasePort","dbPort","databaseUsername","dbUsername","decrypt","databasePassword","dbPassword","databaseSslMode","dbSslMode","connectionPoolSize","cacheInfo","setTimeout","delete","clearTenantCache","clearAllCaches","size","clear","drizzleClient","encrypted","onModuleDestroy","end","hashToken","token","createHash","update","digest","verifyTokenHash","expectedHash","computedHash","length","timingSafeEqual","Buffer","from","VrittiAuthGuard","logger","Logger","name","reflector","_configService","jwtService","primaryDatabase","requestService","canActivate","context","request","switchToHttp","getRequest","reply","getResponse","skipCsrf","getAllAndOverride","SKIP_CSRF_KEY","getHandler","getClass","validateCsrf","isPublic","debug","isOnboarding","accessToken","getAccessToken","warn","UnauthorizedException","decodedToken","decode","type","validatedToken","validateAccessToken","validateRefreshTokenBinding","userId","user","id","tenantIdentifier","getTenantIdentifier","tenantInfo","getTenantInfo","status","subdomain","error","token","decoded","verify","exp","expiryTime","currentTime","Date","now","timeRemaining","Math","floor","jwtError","expiredAt","message","config","getConfig","jwt","validateTokenBinding","refreshTokenHash","cookies","refreshToken","cookie","refreshCookieName","verifyTokenHash","safeMethods","includes","method","fastifyInstance","server","csrfProtection","ForbiddenException","Promise","resolve","reject","err","url","Error","errors","field","scope","Scope","REQUEST","AuthConfigModule","forRootAsync","module","imports","ConfigModule","RequestModule","JwtModule","registerAsync","inject","ConfigService","useFactory","config","secret","get","signOptions","algorithm","providers","provide","Reflector","useClass","APP_GUARD","VrittiAuthGuard","exports","import_common","Onboarding","SetMetadata","import_common","Public","SetMetadata","import_common","UserId","createParamDecorator","_data","ctx","request","switchToHttp","getRequest","user","id","Error","import_common","import_jwt","SSE_ALLOWED_ORIGINS","SseAuthGuard","logger","Logger","name","jwtService","canActivate","context","request","switchToHttp","getRequest","response","getResponse","setCorsHeaders","token","query","warn","UnauthorizedException","decodedToken","decode","type","validatedToken","verify","debug","userId","user","id","error","jwtError","message","origin","headers","includes","header","scope","Scope","REQUEST","import_common","import_core","import_common","import_common","TenantContextService","tenantInfo","setTenant","Error","getTenant","UnauthorizedException","hasTenant","clearTenant","getTenantIdSafe","id","getTenantSubdomainSafe","subdomain","scope","Scope","REQUEST","MessageTenantContextInterceptor","logger","Logger","name","tenantContext","intercept","context","next","contextType","getType","rpcContext","switchToRpc","payload","getData","tenant","debug","subdomain","setTenant","log","type","error","warn","handle","pipe","tap","cleanupContext","complete","hasTenant","getTenantIdSafe","clearTenant","scope","Scope","REQUEST","import_common","import_core","TenantContextInterceptor","logger","Logger","name","reflector","tenantContext","primaryDatabase","requestService","intercept","context","next","request","switchToHttp","getRequest","debug","method","url","isPublic","getAllAndOverride","getHandler","getClass","tenantIdentifier","getTenantIdentifier","handle","UnauthorizedException","log","tenantInfo","getTenantInfo","warn","status","subdomain","type","setTenant","tenant","error","scope","Scope","REQUEST","import_common","import_node_postgres","import_pg","TenantDatabaseService","logger","Logger","name","clients","Map","clientLastUsed","cleanupInterval","options","tenantContext","startConnectionCleaner","drizzleClient","getDbClient","schema","drizzleSchema","tenant","getTenant","cacheKey","buildCacheKey","existing","get","set","Date","now","debug","db","log","connection","createDbClientSync","databaseUrl","buildTenantDbUrl","pool","Pool","connectionString","max","connectionPoolSize","maxConnections","drizzle","client","subdomain","error","InternalServerErrorException","databaseHost","databasePort","databaseName","databaseUsername","databasePassword","databaseSslMode","Error","port","sslMode","connectionUrl","encodeURIComponent","maskPassword","type","interval","connectionCacheTTL","setInterval","cleanupIdleConnections","maxIdle","cleaned","key","lastUsed","entries","end","delete","getPoolStats","activeConnections","size","tenants","Array","from","keys","url","replace","onModuleDestroy","clearInterval","disconnectPromises","map","Promise","all","DatabaseModule","forServer","options","createDynamicModule","forMicroservice","mode","asyncProvider","provide","DATABASE_MODULE_OPTIONS","useFactory","inject","providers","Reflector","useClass","TenantContextService","PrimaryDatabaseService","TenantDatabaseService","push","APP_INTERCEPTOR","TenantContextInterceptor","MessageTenantContextInterceptor","module","imports","RequestModule","exports","import_common","Tenant","createParamDecorator","_data","ctx","request","switchToHttp","getRequest","tenantContext","app","get","TenantContextService","Error","getTenant","import_common","import_drizzle_orm","snakeToCamel","str","replace","_","letter","toUpperCase","PrimaryBaseRepository","logger","tableName","db","database","drizzleClient","model","query","queryKeys","Object","keys","debug","join","error","table","dbTableName","getTableName","Logger","name","create","data","log","results","insert","values","returning","findById","id","findFirst","where","findOne","findMany","options","update","idColumn","set","eq","updateMany","result","count","rowCount","delete","deleteMany","select","sql","from","$dynamic","exists","import_common","import_drizzle_orm","TenantBaseRepository","logger","tableName","db","database","drizzleClient","model","query","table","getTableName","Logger","name","debug","create","data","log","results","insert","values","returning","findById","id","idColumn","select","from","where","eq","limit","findOne","findMany","options","$dynamic","orderBy","offset","update","set","updateMany","result","count","rowCount","delete","deleteMany","sql","exists","import_common","import_common","HttpProblemException","HttpException","detailOrOptions","httpStatus","options","detail","type","label","errors","BadGatewayException","HttpProblemException","detailOrOptions","HttpStatus","BAD_GATEWAY","import_common","BadRequestException","HttpProblemException","detailOrOptions","HttpStatus","BAD_REQUEST","import_common","ConflictException","HttpProblemException","detailOrOptions","HttpStatus","CONFLICT","import_common","ForbiddenException","HttpProblemException","detailOrOptions","HttpStatus","FORBIDDEN","import_common","GoneException","HttpProblemException","detailOrOptions","HttpStatus","GONE","import_common","InternalServerErrorException","HttpProblemException","detailOrOptions","HttpStatus","INTERNAL_SERVER_ERROR","import_common","MethodNotAllowedException","HttpProblemException","detailOrOptions","HttpStatus","METHOD_NOT_ALLOWED","import_common","NotAcceptableException","HttpProblemException","detailOrOptions","HttpStatus","NOT_ACCEPTABLE","import_common","NotFoundException","HttpProblemException","detailOrOptions","HttpStatus","NOT_FOUND","import_common","NotImplementedException","HttpProblemException","detailOrOptions","HttpStatus","NOT_IMPLEMENTED","import_common","PayloadTooLargeException","HttpProblemException","detailOrOptions","HttpStatus","PAYLOAD_TOO_LARGE","import_common","RequestTimeoutException","HttpProblemException","detailOrOptions","HttpStatus","REQUEST_TIMEOUT","import_common","ServiceUnavailableException","HttpProblemException","detailOrOptions","HttpStatus","SERVICE_UNAVAILABLE","import_common","TooManyRequestsException","HttpProblemException","detailOrOptions","HttpStatus","TOO_MANY_REQUESTS","import_common","UnauthorizedException","HttpProblemException","detailOrOptions","HttpStatus","UNAUTHORIZED","import_common","UnprocessableEntityException","HttpProblemException","detailOrOptions","HttpStatus","UNPROCESSABLE_ENTITY","import_common","UnsupportedMediaTypeException","HttpProblemException","detailOrOptions","HttpStatus","UNSUPPORTED_MEDIA_TYPE","import_common","ValidationException","HttpProblemException","detailOrOptions","HttpStatus","BAD_REQUEST","import_common","getHttpStatusTitle","status","enumKey","Object","entries","HttpStatus","find","key","value","Number","isNaN","split","map","word","charAt","toUpperCase","slice","toLowerCase","join","HttpExceptionFilter","logger","Logger","name","catch","exception","host","ctx","switchToHttp","response","getResponse","request","getRequest","INTERNAL_SERVER_ERROR","type","label","detail","errors","HttpException","getStatus","exceptionResponse","responseObj","problemResponse","message","Array","isArray","msg","constraintValues","values","constraints","field","property","filter","error","errorMessage","Error","stack","undefined","problemDetails","title","instance","url","header","send","CALLING_CODE_TO_COUNTRY","extractCountryFromPhone","phone","digits","startsWith","slice","length","prefix","undefined","normalizePhoneNumber","import_common","import_operators","import_common","correlationStorage","AsyncLocalStorage","getCorrelationContext","getStore","runWithCorrelationContext","context","callback","run","updateCorrelationContext","updates","Object","assign","DEFAULT_CORRELATION_HEADER","generateCorrelationId","randomUUID","addCorrelationIdToResponse","reply","correlationId","headerName","header","raw","setHeader","_ts_decorate","LoggerService","activeLogger","options","context","defaultLogger","provider","Error","createWinstonLogger","opts","level","logFormat","format","baseFormatters","timestamp","errors","stack","consoleTransport","transports","Console","combine","json","printf","info","message","correlationId","trace","parts","toUpperCase","padEnd","toString","slice","filter","Boolean","output","join","colorize","all","winstonTransports","enableFileLogger","filePath","maxFiles","push","DailyRotateFile","filename","datePattern","maxSize","config","exitOnError","defaultMeta","appName","environment","createLogger","log","_log","error","warn","debug","verbose","setContext","ctx","winstonLogger","winstonLevel","formattedMessage","formatMessage","metadata","enrichMetadata","nestLogger","logWithMetadata","messageWithMeta","JSON","stringify","String","enriched","correlationContext","getCorrelationContext","key","value","Object","entries","child","childLogger","_ts_decorate","HttpLoggerInterceptor","enableRequestLog","enableResponseLog","slowRequestThreshold","logger","options","intercept","context","next","getType","handle","httpContext","switchToHttp","request","getRequest","response","getResponse","startTime","Date","now","logRequest","pipe","tap","duration","logResponse","catchError","error","logError","correlationContext","getCorrelationContext","metadata","type","method","url","correlationId","ip","userAgent","headers","logWithMetadata","stack","statusCode","logLevel","slowRequest","message","errorName","name","errorMessage","trace","errorDetails","loggingError","import_common","import_common","_ts_decorate","CorrelationIdMiddleware","includeInResponse","responseHeader","options","DEFAULT_CORRELATION_HEADER","use","_req","reply","next","correlationId","generateCorrelationId","addCorrelationIdToResponse","runWithCorrelationContext","onRequest","store","correlationStorage","getStore","enterWith","_ts_decorate","LOGGER_MODULE_OPTIONS","Symbol","DEFAULT_LOGGER_OPTIONS","provider","enableCorrelationId","enableHttpLogger","filePath","maxFiles","ENVIRONMENT_PRESETS","development","level","format","enableFileLogger","httpLogger","enableRequestLog","enableResponseLog","slowRequestThreshold","staging","production","test","mergeWithDefaults","options","preset","environment","filteredOptions","Object","fromEntries","entries","filter","_","value","undefined","merged","createDefaultLoggerProvider","provide","Logger","useFactory","logger","setLogLevels","levels","getLevelsUpTo","createLoggerProviders","mergedOptions","providers","useValue","push","LoggerService","opts","defaultLogger","inject","token","optional","CorrelationIdMiddleware","includeInResponse","responseHeader","HttpLoggerInterceptor","httpLoggerOptions","allLevels","isValidLevel","l","includes","levelIndex","indexOf","slice","LoggerModule","forRoot","module","exports","forRootAsync","asyncProviders","createAsyncProviders","imports","configure","_consumer","createAsyncOptionsProvider","useClass","args","userOptions","optionsFactory","createLoggerOptions","useExisting","Error"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/auth/auth-config.module.ts","../src/request/request.module.ts","../src/request/services/request.service.ts","../src/config/index.ts","../src/auth/guards/vritti-auth.guard.ts","../src/auth/decorators/reset.decorator.ts","../src/auth/decorators/skip-csrf.decorator.ts","../src/auth/utils/token-hash.util.ts","../src/auth/services/jwt-auth.service.ts","../src/utils/time.utils.ts","../src/auth/jwt.config.ts","../src/auth/decorators/access-token.decorator.ts","../src/auth/decorators/onboarding.decorator.ts","../src/auth/decorators/public.decorator.ts","../src/auth/decorators/refresh-token-cookie.decorator.ts","../src/auth/decorators/session-data.decorator.ts","../src/auth/decorators/user-id.decorator.ts","../src/database/database.module.ts","../src/database/constants.ts","../src/database/services/primary-database.service.ts","../src/database/services/tenant-context.service.ts","../src/database/services/tenant-database.service.ts","../src/database/decorators/tenant.decorator.ts","../src/database/dto/select-options-query.dto.ts","../src/database/repositories/primary-base.repository.ts","../src/database/repositories/tenant-base.repository.ts","../src/email/email.module.ts","../src/email/email.service.ts","../src/exceptions/bad-gateway.exception.ts","../src/exceptions/base-field.exception.ts","../src/exceptions/bad-request.exception.ts","../src/exceptions/conflict.exception.ts","../src/exceptions/forbidden.exception.ts","../src/exceptions/gone.exception.ts","../src/exceptions/internal-server-error.exception.ts","../src/exceptions/method-not-allowed.exception.ts","../src/exceptions/not-acceptable.exception.ts","../src/exceptions/not-found.exception.ts","../src/exceptions/not-implemented.exception.ts","../src/exceptions/payload-too-large.exception.ts","../src/exceptions/request-timeout.exception.ts","../src/exceptions/service-unavailable.exception.ts","../src/exceptions/too-many-requests.exception.ts","../src/exceptions/unauthorized.exception.ts","../src/exceptions/unprocessable-entity.exception.ts","../src/exceptions/unsupported-media-type.exception.ts","../src/exceptions/validation.exception.ts","../src/filters/http-exception.filter.ts","../src/logger/interceptors/http-logger.interceptor.ts","../src/logger/services/logger.service.ts","../src/logger/utils/index.ts","../src/logger/logger.module.ts","../src/logger/middleware/correlation-id.middleware.ts","../src/root/root.module.ts","../src/root/controllers/app.controller.ts","../src/root/docs/app.docs.ts","../src/root/services/app.service.ts","../src/root/controllers/csrf.controller.ts","../src/root/docs/csrf.docs.ts","../src/utils/phone.utils.ts"],"sourcesContent":["import './types/fastify-augmentation';\n\n// Config system\n\n// Auth (decorators, guards, token utilities)\nexport * from './auth';\n// Auth decorators (SkipCsrf for webhook endpoints)\nexport { SKIP_CSRF_KEY, SkipCsrf } from './auth/decorators/skip-csrf.decorator';\n// JWT config utilities\nexport {\n getTokenExpiry,\n jwtConfigFactory,\n type AccessTokenPayload,\n type RefreshTokenPayload,\n type TokenExpiry,\n TokenType,\n} from './auth/jwt.config';\n// JWT auth service\nexport { JwtAuthService } from './auth/services/jwt-auth.service';\nexport {\n type ApiSdkConfig,\n type CookieConfig,\n configureApiSdk,\n defineConfig,\n getConfig,\n getJwtExpiry,\n getRefreshCookieOptions,\n type GuardConfig,\n type JwtConfig,\n resetConfig,\n} from './config';\nexport { DatabaseModule } from './database/database.module';\n// Decorators\nexport { Tenant } from './database/decorators/tenant.decorator';\n// Database DTOs\nexport { SelectOptionsQueryDto } from './database/dto/select-options-query.dto';\n// Interfaces\nexport * from './database/interfaces';\n// Repositories\nexport { PrimaryBaseRepository } from './database/repositories/primary-base.repository';\nexport { TenantBaseRepository } from './database/repositories/tenant-base.repository';\n// Schema Registry (for module augmentation)\nexport type {\n RegisteredSchema,\n TypedDrizzleClient,\n} from './database/schema.registry';\nexport { PrimaryDatabaseService } from './database/services/primary-database.service';\n// Services\nexport { TenantContextService } from './database/services/tenant-context.service';\nexport { TenantDatabaseService } from './database/services/tenant-database.service';\n// Database types\nexport type {\n FindForSelectConfig,\n SelectQueryGroup,\n SelectQueryOption,\n SelectQueryResult,\n} from './database/types';\n// Email module\nexport { EmailModule } from './email/email.module';\nexport { EmailService } from './email/email.service';\n// Exceptions\nexport * from './exceptions';\n// RFC 7807 Filters (includes HttpExceptionFilter)\nexport * from './filters';\n// Logger utilities\nexport * from './logger';\n// Root module (health check + CSRF)\nexport { RootModule } from './root/root.module';\n// RFC 7807 Types (using named exports to avoid conflicts)\nexport type { ApiErrorResponse, ProblemDetails } from './types';\n// Phone utilities\nexport { extractCountryFromPhone, normalizePhoneNumber } from './utils/phone.utils';\n// Time utilities\nexport { parseExpiryToMs } from './utils/time.utils';\n","import { type DynamicModule, Global, Module } from '@nestjs/common';\nimport { ConfigModule, ConfigService } from '@nestjs/config';\nimport { APP_GUARD, Reflector } from '@nestjs/core';\nimport { JwtModule } from '@nestjs/jwt';\nimport { RequestModule } from '../request/request.module';\nimport { VrittiAuthGuard } from './guards/vritti-auth.guard';\nimport { JwtAuthService } from './services/jwt-auth.service';\n\n@Global()\n@Module({})\nexport class AuthConfigModule {\n // Registers JWT and global VrittiAuthGuard with async config\n static forRootAsync(): DynamicModule {\n return {\n module: AuthConfigModule,\n imports: [\n ConfigModule,\n RequestModule,\n JwtModule.registerAsync({\n imports: [ConfigModule],\n inject: [ConfigService],\n useFactory: (config: ConfigService) => ({\n secret: config.get<string>('JWT_SECRET'),\n signOptions: {\n algorithm: 'HS256',\n },\n }),\n }),\n ],\n providers: [\n // Required for external packages - NestJS global Reflector not available\n {\n provide: Reflector,\n useClass: Reflector,\n },\n {\n provide: APP_GUARD,\n useClass: VrittiAuthGuard,\n },\n JwtAuthService,\n ],\n exports: [\n JwtModule, // Export for use in other modules (e.g., generating tokens)\n JwtAuthService,\n ],\n };\n }\n}\n","import { Global, Module } from '@nestjs/common';\nimport { RequestService } from './services/request.service';\n\n@Global()\n@Module({\n providers: [RequestService],\n exports: [RequestService],\n})\nexport class RequestModule {}\n","import { Inject, Injectable, Scope } from '@nestjs/common';\nimport { REQUEST } from '@nestjs/core';\nimport type { FastifyRequest } from 'fastify';\nimport { getConfig } from '../../config';\n\n@Injectable({ scope: Scope.REQUEST })\nexport class RequestService {\n constructor(@Inject(REQUEST) private readonly request: FastifyRequest) {}\n\n // Extracts tenant identifier from x-tenant-id or x-subdomain request header\n getTenantIdentifier(): string | null {\n const getHeader = (key: string) => {\n const value = this.request.headers?.[key];\n return Array.isArray(value) ? value[0] : value;\n };\n\n return getHeader('x-tenant-id') || getHeader('x-subdomain') || null;\n }\n\n // Extracts the bearer access token from the Authorization header\n getAccessToken(): string | null {\n const authHeader = this.request.headers?.authorization;\n if (!authHeader) {\n return null;\n }\n const [type, token] = authHeader.split(' ') ?? [];\n return type === 'Bearer' && token ? token : null;\n }\n\n // Extracts the refresh token from the configured httpOnly cookie\n getRefreshToken(): string | null {\n try {\n const cookies = (this.request as unknown as { cookies?: Record<string, string> }).cookies;\n if (cookies && typeof cookies === 'object') {\n const config = getConfig();\n const refreshToken = cookies[config.cookie.refreshCookieName];\n if (refreshToken) {\n return refreshToken;\n }\n }\n return null;\n } catch (_error: unknown) {\n return null;\n }\n }\n\n // Returns the value of a specific request header by key\n getHeader(key: string): string | string[] | undefined {\n return this.request.headers?.[key];\n }\n\n // Returns all request headers\n getAllHeaders(): FastifyRequest['headers'] {\n return this.request.headers || {};\n }\n}\n","export interface CookieConfig {\n refreshCookieName: string;\n refreshCookieMaxAge: number;\n refreshCookiePath: string;\n refreshCookieSecure: boolean;\n refreshCookieSameSite: 'strict' | 'lax' | 'none';\n refreshCookieDomain?: string;\n}\n\nexport interface JwtConfig {\n accessTokenExpiry: string;\n refreshTokenExpiry: string;\n onboardingTokenExpiry: string;\n\n}\n\nexport interface GuardConfig {\n tenantHeaderName: string;\n authHeaderName: string;\n tokenPrefix: string;\n}\n\nexport interface ApiSdkConfig {\n cookie?: Partial<CookieConfig>;\n jwt?: Partial<JwtConfig>;\n guard?: Partial<GuardConfig>;\n}\n\nexport interface FullConfig {\n cookie: CookieConfig;\n jwt: JwtConfig;\n guard: GuardConfig;\n}\n\nconst defaultConfig: FullConfig = {\n cookie: {\n refreshCookieName: 'vritti_refresh',\n refreshCookieMaxAge: 30 * 24 * 60 * 60 * 1000, // 30 days\n refreshCookiePath: '/',\n refreshCookieSecure: process.env.NODE_ENV === 'production',\n refreshCookieSameSite: 'strict',\n refreshCookieDomain: 'localhost',\n },\n jwt: {\n accessTokenExpiry: '15m',\n refreshTokenExpiry: '30d',\n onboardingTokenExpiry: '24h',\n },\n guard: {\n tenantHeaderName: 'x-tenant-id',\n authHeaderName: 'authorization',\n tokenPrefix: 'Bearer',\n },\n};\n\nlet currentConfig: FullConfig = { ...defaultConfig };\n\n// Helper to define configuration with type safety (similar to Tailwind's defineConfig)\nexport function defineConfig(config: ApiSdkConfig): ApiSdkConfig {\n return config;\n}\n\n// Configures api-sdk with user settings — call once in application bootstrap\nexport function configureApiSdk(userConfig: ApiSdkConfig): void {\n currentConfig = {\n cookie: {\n ...defaultConfig.cookie,\n ...(userConfig.cookie || {}),\n },\n jwt: {\n ...defaultConfig.jwt,\n ...(userConfig.jwt || {}),\n },\n guard: {\n ...defaultConfig.guard,\n ...(userConfig.guard || {}),\n },\n };\n}\n\n// Returns the current active configuration\nexport function getConfig(): FullConfig {\n return currentConfig;\n}\n\n// Resets configuration to defaults (for testing)\nexport function resetConfig(): void {\n currentConfig = { ...defaultConfig };\n}\n\n// Returns refresh cookie options built from the current configuration\nexport function getRefreshCookieOptions() {\n const options: Record<string, unknown> = {\n httpOnly: true,\n secure: currentConfig.cookie.refreshCookieSecure,\n sameSite: currentConfig.cookie.refreshCookieSameSite,\n path: currentConfig.cookie.refreshCookiePath,\n maxAge: currentConfig.cookie.refreshCookieMaxAge,\n };\n\n // Only add domain if specified (needed for cross-subdomain auth like cloud.localhost)\n if (currentConfig.cookie.refreshCookieDomain) {\n options.domain = currentConfig.cookie.refreshCookieDomain;\n }\n\n return options;\n}\n\n// Returns JWT expiry settings for access, refresh, and onboarding tokens\nexport function getJwtExpiry() {\n return {\n access: currentConfig.jwt.accessTokenExpiry,\n refresh: currentConfig.jwt.refreshTokenExpiry,\n onboarding: currentConfig.jwt.onboardingTokenExpiry,\n };\n}\n","import {\n type CanActivate,\n type ExecutionContext,\n ForbiddenException,\n Injectable,\n Logger,\n Scope,\n UnauthorizedException,\n} from '@nestjs/common';\nimport { SSE_METADATA } from '@nestjs/common/constants';\nimport { ConfigService } from '@nestjs/config';\nimport { Reflector } from '@nestjs/core';\nimport { JwtService } from '@nestjs/jwt';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport '../../types/fastify-augmentation';\nimport { RequestService } from '../../request/services/request.service';\nimport { RESET_KEY } from '../decorators/reset.decorator';\nimport { SKIP_CSRF_KEY } from '../decorators/skip-csrf.decorator';\nimport { verifyTokenHash } from '../utils/token-hash.util';\n\ninterface DecodedToken {\n userId: string;\n sessionId: string;\n sessionType: string;\n tokenType: string;\n refreshTokenHash?: string;\n exp?: number;\n iat?: number;\n}\n\n@Injectable({ scope: Scope.REQUEST })\nexport class VrittiAuthGuard implements CanActivate {\n private readonly logger = new Logger(VrittiAuthGuard.name);\n\n constructor(\n private readonly reflector: Reflector,\n readonly _configService: ConfigService,\n private readonly jwtService: JwtService,\n private readonly requestService: RequestService,\n ) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = context.switchToHttp().getRequest<FastifyRequest>();\n const reply = context.switchToHttp().getResponse<FastifyReply>();\n\n // Validate CSRF for state-changing methods (unless @SkipCsrf)\n const skipCsrf = this.reflector.getAllAndOverride<boolean>(SKIP_CSRF_KEY, [\n context.getHandler(),\n context.getClass(),\n ]);\n if (!skipCsrf) {\n await this.validateCsrf(request, reply);\n }\n\n // @Public() endpoints skip all auth\n const isPublic = this.reflector.getAllAndOverride<boolean>('isPublic', [context.getHandler(), context.getClass()]);\n if (isPublic) {\n return true;\n }\n\n // @Onboarding() endpoints require ONBOARDING session type\n const isOnboarding = this.reflector.getAllAndOverride<boolean>('isOnboarding', [\n context.getHandler(),\n context.getClass(),\n ]);\n\n // @Reset() endpoints require RESET session type\n const isReset = this.reflector.getAllAndOverride<boolean>(RESET_KEY, [context.getHandler(), context.getClass()]);\n\n // SSE endpoints authenticate via refresh token cookie (EventSource cannot send Authorization headers)\n const isSseEndpoint = this.reflector.get<boolean>(SSE_METADATA, context.getHandler());\n if (isSseEndpoint) {\n return this.handleSseAuth(request, isOnboarding);\n }\n\n try {\n const accessToken = this.requestService.getAccessToken();\n if (!accessToken) {\n throw new UnauthorizedException('Access token not found');\n }\n\n // Validate JWT signature and expiry\n const decodedAccessToken = this.validateAccessToken(accessToken);\n\n // Must be an ACCESS token, not REFRESH\n if (decodedAccessToken.tokenType !== 'access') {\n throw new UnauthorizedException('Invalid token type');\n }\n\n // Validate refresh token binding (hash check on every request)\n this.validateRefreshTokenBinding(decodedAccessToken);\n\n // @Onboarding endpoints require ONBOARDING session type\n if (isOnboarding && decodedAccessToken.sessionType !== 'ONBOARDING') {\n throw new UnauthorizedException('This endpoint requires an onboarding session');\n }\n\n // @Reset endpoints require RESET session type\n if (isReset && decodedAccessToken.sessionType !== 'RESET') {\n throw new UnauthorizedException('This endpoint requires a reset session');\n }\n\n // Regular endpoints reject ONBOARDING and RESET sessions\n if (\n !isOnboarding &&\n !isReset &&\n (decodedAccessToken.sessionType === 'ONBOARDING' || decodedAccessToken.sessionType === 'RESET')\n ) {\n throw new UnauthorizedException(`${decodedAccessToken.sessionType} sessions cannot access this endpoint`);\n }\n\n // Attach session info to request\n request.sessionInfo = {\n userId: decodedAccessToken.userId,\n sessionId: decodedAccessToken.sessionId,\n sessionType: decodedAccessToken.sessionType,\n };\n\n return true;\n } catch (error) {\n if (error instanceof UnauthorizedException) {\n throw error;\n }\n this.logger.error('Unexpected error in auth guard', error);\n throw new UnauthorizedException('Authentication failed');\n }\n }\n\n // Validates JWT signature, expiry, and not-before claims\n private validateAccessToken(token: string): DecodedToken {\n try {\n return this.jwtService.verify<DecodedToken>(token);\n } catch (error: unknown) {\n if (error instanceof UnauthorizedException) throw error;\n\n const jwtError = error as { name?: string; message?: string };\n if (jwtError?.name === 'TokenExpiredError') {\n throw new UnauthorizedException('Access token has expired');\n }\n if (jwtError?.name === 'JsonWebTokenError') {\n throw new UnauthorizedException('Invalid access token');\n }\n if (jwtError?.name === 'NotBeforeError') {\n throw new UnauthorizedException('Access token not yet valid');\n }\n\n throw new UnauthorizedException('Access token validation failed');\n }\n }\n\n // Validates that the access token is bound to the refresh token in the cookie\n private validateRefreshTokenBinding(decodedAccessToken: DecodedToken): void {\n if (!decodedAccessToken.refreshTokenHash) {\n throw new UnauthorizedException('Token missing refresh token binding');\n }\n\n const refreshToken = this.requestService.getRefreshToken();\n\n if (!refreshToken) {\n throw new UnauthorizedException('Session validation failed');\n }\n\n if (!verifyTokenHash(refreshToken, decodedAccessToken.refreshTokenHash)) {\n throw new UnauthorizedException('Session validation failed');\n }\n }\n\n // Authenticates SSE connections using the refresh token httpOnly cookie\n private handleSseAuth(request: FastifyRequest, isOnboarding: boolean): boolean {\n const refreshToken = this.requestService.getRefreshToken();\n if (!refreshToken) {\n throw new UnauthorizedException('Authentication required');\n }\n\n let decoded: { userId: string; sessionId: string; sessionType: string; tokenType: string };\n try {\n decoded = this.jwtService.verify<{ userId: string; sessionId: string; sessionType: string; tokenType: string }>(refreshToken);\n } catch {\n throw new UnauthorizedException('Invalid or expired session');\n }\n\n if (decoded.tokenType !== 'refresh') {\n throw new UnauthorizedException('Invalid token type');\n }\n\n if (isOnboarding && decoded.sessionType !== 'ONBOARDING') {\n throw new UnauthorizedException('This endpoint requires an onboarding session');\n }\n\n request.sessionInfo = {\n userId: decoded.userId,\n sessionId: decoded.sessionId,\n sessionType: decoded.sessionType,\n };\n\n return true;\n }\n\n // Validates CSRF token for state-changing requests\n private async validateCsrf(request: FastifyRequest, reply: FastifyReply): Promise<void> {\n const safeMethods = ['GET', 'HEAD', 'OPTIONS'];\n if (safeMethods.includes(request.method)) return;\n\n interface FastifyInstanceWithCsrf {\n csrfProtection?: (req: FastifyRequest, reply: FastifyReply, next: (err?: Error) => void) => void;\n }\n type PatchableReply = { send: (...args: unknown[]) => unknown };\n\n try {\n const fastifyInstance = request.server as unknown as FastifyInstanceWithCsrf;\n const csrfProtection = fastifyInstance.csrfProtection;\n if (!csrfProtection) {\n throw new ForbiddenException('CSRF protection not configured');\n }\n\n await new Promise<void>((resolve, reject) => {\n // Intercept reply.send to prevent the plugin from bypassing NestJS error handling\n const originalSend = reply.send.bind(reply);\n (reply as PatchableReply).send = () => {\n (reply as PatchableReply).send = originalSend as PatchableReply['send'];\n reject(new Error('CSRF validation failed'));\n return reply;\n };\n\n csrfProtection(request, reply, (err?: Error) => {\n (reply as PatchableReply).send = originalSend as PatchableReply['send'];\n if (err) reject(err);\n else resolve();\n });\n });\n } catch (error) {\n throw new ForbiddenException({\n errors: [{ field: 'csrf', message: 'Invalid or missing CSRF token' }],\n message: 'CSRF validation failed',\n });\n }\n }\n}\n","import { SetMetadata } from '@nestjs/common';\n\n// Marks endpoints that require a RESET session token (password reset flow)\nexport const RESET_KEY = 'isReset';\nexport const Reset = () => SetMetadata(RESET_KEY, true);\n","import { SetMetadata } from '@nestjs/common';\n\nexport const SKIP_CSRF_KEY = 'skipCsrf';\n\nexport const SkipCsrf = () => SetMetadata(SKIP_CSRF_KEY, true);\n","import * as crypto from 'node:crypto';\n\n// Hashes a token using SHA-256\nexport function hashToken(token: string): string {\n return crypto.createHash('sha256').update(token).digest('hex');\n}\n\n// Verifies a token against its stored hash\nexport function verifyTokenHash(token: string, expectedHash: string): boolean {\n const computedHash = hashToken(token);\n if (computedHash.length !== expectedHash.length) return false;\n return crypto.timingSafeEqual(Buffer.from(computedHash, 'hex'), Buffer.from(expectedHash, 'hex'));\n}\n","import { Injectable, Logger } from '@nestjs/common';\nimport { ConfigService } from '@nestjs/config';\nimport { type JwtSignOptions, JwtService as NestJwtService } from '@nestjs/jwt';\nimport { parseExpiryToMs } from '../../utils/time.utils';\nimport { getTokenExpiry, type TokenExpiry, TokenType } from '../jwt.config';\nimport { hashToken } from '../utils/token-hash.util';\n\n@Injectable()\nexport class JwtAuthService {\n private readonly logger = new Logger(JwtAuthService.name);\n private readonly tokenExpiry: TokenExpiry;\n\n constructor(\n private readonly jwtService: NestJwtService,\n readonly configService: ConfigService,\n ) {\n this.tokenExpiry = getTokenExpiry(configService);\n }\n\n // Generates an access token bound to the given refresh token\n generateAccessToken(userId: string, sessionId: string, sessionType: string, refreshToken: string): string {\n return this.jwtService.sign(\n { sessionType, tokenType: TokenType.ACCESS, userId, sessionId, refreshTokenHash: hashToken(refreshToken) },\n { expiresIn: this.tokenExpiry.access },\n );\n }\n\n // Generates a refresh token for session persistence\n generateRefreshToken(userId: string, sessionId: string, sessionType: string): string {\n return this.jwtService.sign(\n { sessionType, tokenType: TokenType.REFRESH, userId, sessionId },\n { expiresIn: this.tokenExpiry.refresh },\n );\n }\n\n // Signs an arbitrary payload with optional JWT options\n sign(payload: object, options?: JwtSignOptions): string {\n return this.jwtService.sign(payload, options);\n }\n\n // Verifies a token and ensures it matches the expected token type\n verify(\n token: string,\n expectedType: TokenType,\n ): { userId: string; sessionId: string; sessionType: string; tokenType: TokenType } {\n try {\n const payload = this.jwtService.verify(token);\n\n if (payload.tokenType !== expectedType) {\n throw new Error(`Expected ${expectedType} token, got ${payload.tokenType}`);\n }\n\n return payload;\n } catch (error) {\n this.logger.error(`Failed to verify ${expectedType} token`, error);\n throw error;\n }\n }\n\n // Returns the expiry as a Date for the given token type\n getExpiryTime(type: TokenType): Date {\n return new Date(Date.now() + parseExpiryToMs(this.tokenExpiry[type]));\n }\n\n // Returns the token lifetime in seconds for the given type\n getExpiryInSeconds(type: TokenType): number {\n return Math.floor(parseExpiryToMs(this.tokenExpiry[type]) / 1000);\n }\n}\n","// Parses a duration string (e.g. '10m', '1h', '30s', '7d') to milliseconds\nexport function parseExpiryToMs(expiry: string): number {\n const match = expiry.match(/^(\\d+)([smhdwy])$/);\n if (!match) throw new Error(`Invalid expiry format: ${expiry}`);\n\n const value = Number.parseInt(match[1]!, 10);\n const multipliers: Record<string, number> = {\n s: 1000,\n m: 60_000,\n h: 3_600_000,\n d: 86_400_000,\n w: 604_800_000,\n y: 31_536_000_000,\n };\n\n return value * multipliers[match[2]!]!;\n}\n","import type { ConfigService } from '@nestjs/config';\nimport type { JwtModuleOptions } from '@nestjs/jwt';\n\nexport const jwtConfigFactory = (configService: ConfigService): JwtModuleOptions => ({\n secret: configService.getOrThrow<string>('JWT_SECRET'),\n signOptions: {\n issuer: 'vritti-api',\n },\n});\n\ntype TokenExpiryString = `${number}${'s' | 'm' | 'h' | 'd' | 'w' | 'y'}`;\n\nexport interface TokenExpiry {\n access: TokenExpiryString;\n refresh: TokenExpiryString;\n}\n\nexport const getTokenExpiry = (configService: ConfigService): TokenExpiry => ({\n access: configService.getOrThrow<string>('ACCESS_TOKEN_EXPIRY') as TokenExpiryString,\n refresh: configService.getOrThrow<string>('REFRESH_TOKEN_EXPIRY') as TokenExpiryString,\n});\n\nexport enum TokenType {\n ACCESS = 'access',\n REFRESH = 'refresh',\n}\n\n// sessionType typed as string — each server's enum is a valid subtype\nexport interface AccessTokenPayload {\n sessionType: string;\n tokenType: TokenType.ACCESS;\n userId: string;\n sessionId: string;\n refreshTokenHash: string;\n}\n\nexport interface RefreshTokenPayload {\n sessionType: string;\n tokenType: TokenType.REFRESH;\n userId: string;\n sessionId: string;\n}\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport type { FastifyRequest } from 'fastify';\n\n// Extracts the bearer token from the Authorization header\nexport const AccessToken = createParamDecorator(\n (_data: unknown, ctx: ExecutionContext): string => {\n const request = ctx.switchToHttp().getRequest<FastifyRequest>();\n const authHeader = request.headers.authorization;\n return authHeader?.replace('Bearer ', '') || '';\n },\n);\n","import { SetMetadata } from '@nestjs/common';\n\nexport const Onboarding = () => SetMetadata('isOnboarding', true);\n","import { SetMetadata } from '@nestjs/common';\n\nexport const Public = () => SetMetadata('isPublic', true);\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport type { FastifyRequest } from 'fastify';\nimport { getConfig } from '../../config';\n\nexport const RefreshTokenCookie = createParamDecorator(\n (_data: unknown, ctx: ExecutionContext): string | undefined => {\n const request = ctx.switchToHttp().getRequest<FastifyRequest>();\n const cookies = request.cookies ?? {};\n const config = getConfig();\n return cookies[config.cookie.refreshCookieName] as string | undefined;\n },\n);\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport type { FastifyRequest } from 'fastify';\nimport '../../types/fastify-augmentation';\n\nexport interface SessionInfo {\n userId: string;\n sessionId: string;\n sessionType: string;\n}\n\n// Returns full decoded session info from request.sessionInfo (set by VrittiAuthGuard)\nexport const SessionData = createParamDecorator(\n (_data: unknown, ctx: ExecutionContext): SessionInfo => {\n const request = ctx.switchToHttp().getRequest<FastifyRequest>();\n const sessionInfo = request.sessionInfo;\n\n if (!sessionInfo?.sessionId) {\n throw new Error('Session info not found on request. Ensure route is protected by auth guard.');\n }\n\n return {\n userId: sessionInfo.userId,\n sessionId: sessionInfo.sessionId,\n sessionType: sessionInfo.sessionType,\n };\n },\n);\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport type { FastifyRequest } from 'fastify';\nimport '../../types/fastify-augmentation';\n\n// Extracts userId from request.sessionInfo (set by VrittiAuthGuard)\nexport const UserId = createParamDecorator(\n (_data: unknown, ctx: ExecutionContext): string => {\n const request = ctx.switchToHttp().getRequest<FastifyRequest>();\n const sessionInfo = request.sessionInfo;\n\n if (!sessionInfo?.userId) {\n throw new Error('User ID not found on request. Ensure route is protected by auth guard.');\n }\n\n return sessionInfo.userId;\n },\n);\n","import { type DynamicModule, Global, type InjectionToken, Module, type Provider } from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\nimport { RequestModule } from '../request/request.module';\nimport { DATABASE_MODULE_OPTIONS } from './constants';\n\nimport type { DatabaseModuleOptions } from './interfaces';\nimport { PrimaryDatabaseService } from './services/primary-database.service';\nimport { TenantContextService } from './services/tenant-context.service';\nimport { TenantDatabaseService } from './services/tenant-database.service';\n\n@Global()\n@Module({})\nexport class DatabaseModule {\n // Configures the module for gateway/HTTP mode with TenantContextInterceptor\n static forServer(options: {\n useFactory: (...args: unknown[]) => Promise<DatabaseModuleOptions> | DatabaseModuleOptions;\n inject?: InjectionToken[];\n }): DynamicModule {\n return DatabaseModule.createDynamicModule(options, 'server');\n }\n\n // Configures the module for microservice mode with MessageTenantContextInterceptor\n static forMicroservice(options: {\n useFactory: (...args: unknown[]) => Promise<DatabaseModuleOptions> | DatabaseModuleOptions;\n inject?: InjectionToken[];\n }): DynamicModule {\n return DatabaseModule.createDynamicModule(options, 'microservice');\n }\n\n // Creates the dynamic module configuration with the appropriate interceptor for the given mode\n private static createDynamicModule(\n options: {\n useFactory: (...args: unknown[]) => Promise<DatabaseModuleOptions> | DatabaseModuleOptions;\n inject?: InjectionToken[];\n },\n mode: 'server' | 'microservice',\n ): DynamicModule {\n const asyncProvider: Provider = {\n provide: DATABASE_MODULE_OPTIONS,\n useFactory: options.useFactory,\n inject: options.inject || [],\n };\n\n const providers: Provider[] = [\n // Required for external packages - NestJS global Reflector not available\n {\n provide: Reflector,\n useClass: Reflector,\n },\n asyncProvider,\n TenantContextService,\n PrimaryDatabaseService,\n TenantDatabaseService,\n ];\n\n // Temporarily disabled — tenant database routing not yet in use\n // if (mode === 'server') {\n // providers.push({\n // provide: APP_INTERCEPTOR,\n // useClass: TenantContextInterceptor,\n // });\n // } else {\n // providers.push({\n // provide: APP_INTERCEPTOR,\n // useClass: MessageTenantContextInterceptor,\n // });\n // }\n\n return {\n module: DatabaseModule,\n imports: [RequestModule],\n providers,\n exports: [TenantDatabaseService, TenantContextService, PrimaryDatabaseService, asyncProvider],\n };\n }\n}\n","export const DATABASE_MODULE_OPTIONS = Symbol('DATABASE_MODULE_OPTIONS');\n","import {\n Inject,\n Injectable,\n InternalServerErrorException,\n Logger,\n type OnModuleDestroy,\n type OnModuleInit,\n} from '@nestjs/common';\nimport { eq, or } from 'drizzle-orm';\nimport { drizzle } from 'drizzle-orm/node-postgres';\nimport type { PgColumn, PgTable } from 'drizzle-orm/pg-core';\nimport { Pool } from 'pg';\nimport { DATABASE_MODULE_OPTIONS } from '../constants';\nimport type { DatabaseModuleOptions, TenantInfo } from '../interfaces';\nimport type { TypedDrizzleClient } from '../schema.registry';\n\ninterface TenantSchemaRequirement {\n tenants: PgTable & { id: PgColumn; subdomain: PgColumn; dbType: PgColumn; status: PgColumn };\n tenantDatabaseConfigs: PgTable & {\n tenantId: PgColumn;\n dbSchema: PgColumn;\n dbName: PgColumn;\n dbHost: PgColumn;\n dbPort: PgColumn;\n dbUsername: PgColumn;\n dbPassword: PgColumn;\n dbSslMode: PgColumn;\n connectionPoolSize: PgColumn;\n };\n}\n\ninterface TenantJoinResultRow {\n tenants: TenantRow;\n tenant_database_configs: TenantDatabaseConfigRow | null;\n}\n\ninterface TenantRow {\n id: string;\n subdomain: string;\n dbType: 'SHARED' | 'DEDICATED';\n status: string;\n}\n\ninterface TenantDatabaseConfigRow {\n tenantId: string;\n dbSchema: string | null;\n dbName: string | null;\n dbHost: string | null;\n dbPort: number | null;\n dbUsername: string | null;\n dbPassword: string | null;\n dbSslMode: string | null;\n connectionPoolSize: number | null;\n}\n\n@Injectable()\nexport class PrimaryDatabaseService implements OnModuleInit, OnModuleDestroy {\n private readonly logger = new Logger(PrimaryDatabaseService.name);\n\n private pool: Pool | null = null;\n private db: TypedDrizzleClient | null = null;\n private readonly tenantConfigCache = new Map<string, TenantInfo>();\n private readonly cacheTTL: number;\n\n constructor(\n @Inject(DATABASE_MODULE_OPTIONS)\n private readonly options: DatabaseModuleOptions,\n ) {\n this.cacheTTL = options.connectionCacheTTL || 300000; // 5 minutes default\n }\n\n async onModuleInit() {\n // Only initialize if we have primary database config (gateway mode)\n if (this.options.primaryDb) {\n await this.initializeDrizzleClient();\n }\n }\n\n // Initializes connection to primary database using Drizzle\n private async initializeDrizzleClient(): Promise<void> {\n try {\n const databaseUrl = this.buildPrimaryDbUrl();\n\n this.pool = new Pool({\n connectionString: databaseUrl,\n max: this.options.maxConnections || 10,\n });\n\n // Initialize Drizzle with the schema provided (v2 API)\n // Relations must be passed separately for db.query to work\n this.logger.debug(`Schema keys passed to drizzle: [${Object.keys(this.options.drizzleSchema || {}).join(', ')}]`);\n this.logger.debug(\n `Relations keys passed to drizzle: [${Object.keys(this.options.drizzleRelations || {}).join(', ')}]`,\n );\n this.db = drizzle({\n client: this.pool,\n schema: this.options.drizzleSchema,\n relations: this.options.drizzleRelations,\n }) as TypedDrizzleClient;\n this.logger.debug(`Drizzle query keys after init: [${Object.keys(this.db.query || {}).join(', ')}]`);\n\n // Test connection\n await this.pool.query('SELECT 1');\n this.logger.log('Connected to primary database (tenant registry)');\n } catch (error) {\n this.logger.error('Failed to connect to primary database', error);\n throw new InternalServerErrorException('Failed to initialize tenant registry');\n }\n }\n\n // Builds the PostgreSQL connection URL from primary database config properties\n private buildPrimaryDbUrl(): string {\n if (!this.options.primaryDb) {\n throw new Error('Primary database configuration not provided');\n }\n\n const {\n host,\n port = 5432,\n username,\n password,\n database,\n schema = 'public',\n sslMode = 'require',\n } = this.options.primaryDb;\n\n // Build base URL\n let url = `postgresql://${username}:${encodeURIComponent(password)}@${host}:${port}/${database}`;\n\n // Add query parameters\n const params = new URLSearchParams();\n if (schema) {\n params.set('schema', schema);\n }\n params.set('sslmode', sslMode);\n\n const queryString = params.toString();\n if (queryString) {\n url += `?${queryString}`;\n }\n\n this.logger.debug(`Primary DB connection URL: ${this.maskPassword(url)}`);\n\n return url;\n }\n\n // Masks password in connection URL for safe logging\n private maskPassword(url: string): string {\n return url.replace(/:([^@]+)@/, ':****@');\n }\n\n // Retrieves tenant configuration by ID or subdomain, with in-memory caching\n async getTenantInfo(tenantIdentifier: string): Promise<TenantInfo | null> {\n // Check cache first\n const cached = this.tenantConfigCache.get(tenantIdentifier);\n if (cached) {\n this.logger.debug(`Cache hit for tenant: ${tenantIdentifier}`);\n return cached;\n }\n\n // Query primary database\n try {\n if (!this.db) {\n throw new Error('Primary database client not initialized');\n }\n\n this.logger.debug(`Querying primary database for tenant: ${tenantIdentifier}`);\n\n // Get table references from schema\n // Cast to TenantSchemaRequirement - consumer must provide these tables\n const schema = this.options.drizzleSchema as unknown as TenantSchemaRequirement;\n const { tenants, tenantDatabaseConfigs } = schema;\n\n // Query with left join to get tenant and its database config\n const result = await this.db\n .select()\n .from(tenants)\n .leftJoin(tenantDatabaseConfigs, eq(tenants.id, tenantDatabaseConfigs.tenantId))\n .where(or(eq(tenants.id, tenantIdentifier), eq(tenants.subdomain, tenantIdentifier)))\n .limit(1);\n\n if (!result.length) {\n this.logger.warn(`Tenant not found: ${tenantIdentifier}`);\n return null;\n }\n\n // Cast row to access joined table results using typed interfaces\n const row = result[0] as unknown as TenantJoinResultRow;\n const tenant = row.tenants;\n const config = row.tenant_database_configs;\n\n // Check if tenant is active\n if (tenant.status !== 'ACTIVE') {\n this.logger.warn(`Tenant not active: ${tenantIdentifier}`);\n return null;\n }\n\n // Build info object - map from separated tables\n const info: TenantInfo = {\n id: tenant.id,\n subdomain: tenant.subdomain,\n type: tenant.dbType,\n status: tenant.status,\n // For SHARED tenants: schema name\n schemaName: config?.dbSchema || undefined,\n // For DEDICATED tenants: database configuration from TenantDatabaseConfig table\n databaseName: config?.dbName || undefined,\n databaseHost: config?.dbHost || undefined,\n databasePort: config?.dbPort || undefined,\n databaseUsername: config?.dbUsername ? this.decrypt(config.dbUsername) : undefined,\n databasePassword: config?.dbPassword ? this.decrypt(config.dbPassword) : undefined,\n databaseSslMode: config?.dbSslMode || undefined,\n connectionPoolSize: config?.connectionPoolSize || undefined,\n };\n\n // Cache by both ID and subdomain\n this.cacheInfo(info);\n\n return info;\n } catch (error) {\n this.logger.error(`Failed to fetch tenant info: ${tenantIdentifier}`, error);\n throw new InternalServerErrorException('Failed to resolve tenant');\n }\n }\n\n // Caches tenant info by both ID and subdomain with TTL expiration\n private cacheInfo(info: TenantInfo): void {\n this.tenantConfigCache.set(info.id, info);\n this.tenantConfigCache.set(info.subdomain, info);\n\n // Set expiration\n setTimeout(() => {\n this.tenantConfigCache.delete(info.id);\n this.tenantConfigCache.delete(info.subdomain);\n this.logger.debug(`Cache expired for tenant: ${info.subdomain}`);\n }, this.cacheTTL);\n }\n\n // Clears cached tenant info for the given ID or subdomain\n clearTenantCache(tenantIdentifier: string): void {\n const config = this.tenantConfigCache.get(tenantIdentifier);\n if (config) {\n this.tenantConfigCache.delete(config.id);\n this.tenantConfigCache.delete(config.subdomain);\n this.logger.log(`Cleared cache for tenant: ${tenantIdentifier}`);\n }\n }\n\n // Clears all cached tenant configurations\n clearAllCaches(): void {\n const size = this.tenantConfigCache.size;\n this.tenantConfigCache.clear();\n this.logger.log(`Cleared ${size} cached tenant configs`);\n }\n\n // Returns the initialized Drizzle client, throwing if not yet initialized\n get drizzleClient(): TypedDrizzleClient {\n if (!this.db) {\n throw new Error('Primary database client not initialized');\n }\n return this.db;\n }\n\n // Returns the Drizzle schema passed in module options\n get schema(): typeof this.options.drizzleSchema {\n return this.options.drizzleSchema;\n }\n\n // Decrypts a database credential value (placeholder for actual decryption)\n private decrypt(encrypted: string): string {\n // TODO: Implement actual decryption using this.options.encryptionKey\n // For now, return as-is (assumes unencrypted or encryption happens elsewhere)\n return encrypted;\n }\n\n async onModuleDestroy() {\n if (this.pool) {\n await this.pool.end();\n this.logger.log('Disconnected from primary database');\n }\n }\n}\n","import { Injectable, Scope, UnauthorizedException } from '@nestjs/common';\nimport type { TenantInfo } from '../interfaces';\n\n@Injectable({ scope: Scope.REQUEST })\nexport class TenantContextService {\n private tenantInfo: TenantInfo | null = null;\n\n // Sets tenant info for this request, throwing if already set to prevent overwrites\n setTenant(tenantInfo: TenantInfo): void {\n if (this.tenantInfo) {\n throw new Error('Tenant context already set for this request');\n }\n this.tenantInfo = tenantInfo;\n }\n\n // Returns the tenant info for this request, throwing if context is not set\n getTenant(): TenantInfo {\n if (!this.tenantInfo) {\n throw new UnauthorizedException('Tenant context not set');\n }\n return this.tenantInfo;\n }\n\n // Returns true if tenant context has been set for this request\n hasTenant(): boolean {\n return this.tenantInfo !== null;\n }\n\n // Clears the tenant context (useful for RabbitMQ message handler cleanup)\n clearTenant(): void {\n this.tenantInfo = null;\n }\n\n // Returns the tenant ID or null if context is not set\n getTenantIdSafe(): string | null {\n return this.tenantInfo?.id ?? null;\n }\n\n // Returns the tenant subdomain or null if context is not set\n getTenantSubdomainSafe(): string | null {\n return this.tenantInfo?.subdomain ?? null;\n }\n}\n","import { Inject, Injectable, InternalServerErrorException, Logger, type OnModuleDestroy } from '@nestjs/common';\nimport { drizzle } from 'drizzle-orm/node-postgres';\nimport { Pool } from 'pg';\nimport { DATABASE_MODULE_OPTIONS } from '../constants';\nimport type { DatabaseModuleOptions, TenantInfo } from '../interfaces';\nimport type { TypedDrizzleClient } from '../schema.registry';\nimport { TenantContextService } from './tenant-context.service';\n\ninterface TenantConnection {\n pool: Pool;\n db: TypedDrizzleClient;\n}\n\n@Injectable()\nexport class TenantDatabaseService implements OnModuleDestroy {\n private readonly logger = new Logger(TenantDatabaseService.name);\n\n private readonly clients = new Map<string, TenantConnection>();\n private readonly clientLastUsed = new Map<string, number>();\n private cleanupInterval?: NodeJS.Timeout;\n\n constructor(\n @Inject(DATABASE_MODULE_OPTIONS)\n private readonly options: DatabaseModuleOptions,\n private readonly tenantContext: TenantContextService,\n ) {\n this.startConnectionCleaner();\n }\n\n // Returns the Drizzle client scoped to the current tenant's database\n get drizzleClient(): TypedDrizzleClient {\n return this.getDbClient();\n }\n\n // Returns the Drizzle schema passed in module options\n get schema(): Record<string, unknown> {\n return this.options.drizzleSchema;\n }\n\n // Returns a cached or new Drizzle client for the current tenant context\n private getDbClient(): TypedDrizzleClient {\n const tenant = this.tenantContext.getTenant();\n const cacheKey = this.buildCacheKey(tenant);\n\n // Check if connection already exists\n const existing = this.clients.get(cacheKey);\n if (existing) {\n this.clientLastUsed.set(cacheKey, Date.now());\n this.logger.debug(`Reusing cached connection: ${cacheKey}`);\n return existing.db;\n }\n\n // Create new connection synchronously\n this.logger.log(`Creating new database connection: ${cacheKey}`);\n const connection = this.createDbClientSync(tenant);\n this.clients.set(cacheKey, connection);\n this.clientLastUsed.set(cacheKey, Date.now());\n\n return connection.db;\n }\n\n // Creates a new pool and Drizzle client for the given tenant\n private createDbClientSync(tenant: TenantInfo): TenantConnection {\n try {\n // Build tenant-specific database URL\n const databaseUrl = this.buildTenantDbUrl(tenant);\n\n // Create PostgreSQL pool\n const pool = new Pool({\n connectionString: databaseUrl,\n max: tenant.connectionPoolSize || this.options.maxConnections || 10,\n });\n\n // Initialize Drizzle with the schema (v2 API)\n const db = drizzle({\n client: pool,\n schema: this.options.drizzleSchema,\n }) as TypedDrizzleClient;\n\n this.logger.log(`Connected to database for tenant: ${tenant.subdomain}`);\n\n return { pool, db };\n } catch (error) {\n this.logger.error(`Failed to create database connection for tenant: ${tenant.subdomain}`, error);\n throw new InternalServerErrorException('Failed to connect to tenant database');\n }\n }\n\n // Builds the PostgreSQL connection URL for a dedicated tenant database\n private buildTenantDbUrl(tenant: TenantInfo): string {\n const { databaseHost, databasePort, databaseName, databaseUsername, databasePassword, databaseSslMode } = tenant;\n\n if (!databaseHost || !databaseName || !databaseUsername) {\n throw new Error(`Tenant ${tenant.subdomain} missing database configuration`);\n }\n\n const port = databasePort || 5432;\n const sslMode = databaseSslMode || 'require';\n const connectionUrl = `postgresql://${databaseUsername}:${encodeURIComponent(databasePassword || '')}@${databaseHost}:${port}/${databaseName}?sslmode=${sslMode}`;\n\n this.logger.debug(`Tenant connection URL: ${this.maskPassword(connectionUrl)}`);\n\n return connectionUrl;\n }\n\n // Builds a cache key for connection pooling from tenant database coordinates\n private buildCacheKey(tenant: TenantInfo): string {\n return `${tenant.type}:${tenant.databaseName}@${tenant.databaseHost}`;\n }\n\n // Starts a periodic interval to close idle database connections\n private startConnectionCleaner(): void {\n const interval = this.options.connectionCacheTTL || 300000; // 5 minutes\n\n this.cleanupInterval = setInterval(() => {\n this.cleanupIdleConnections();\n }, interval);\n\n this.logger.log(`Connection cleanup scheduled every ${interval / 1000} seconds`);\n }\n\n // Closes and removes connections that have been idle beyond the TTL\n private async cleanupIdleConnections(): Promise<void> {\n const now = Date.now();\n const maxIdle = this.options.connectionCacheTTL || 300000;\n\n let cleaned = 0;\n\n for (const [key, lastUsed] of this.clientLastUsed.entries()) {\n if (now - lastUsed > maxIdle) {\n const connection = this.clients.get(key);\n if (connection) {\n try {\n await connection.pool.end();\n this.logger.debug(`Cleaned up idle connection: ${key}`);\n } catch (error) {\n this.logger.error(`Error disconnecting idle client: ${key}`, error);\n }\n\n this.clients.delete(key);\n this.clientLastUsed.delete(key);\n cleaned++;\n }\n }\n }\n\n if (cleaned > 0) {\n this.logger.log(`Cleaned up ${cleaned} idle connections`);\n }\n }\n\n // Returns the current number of active pooled connections and their tenant keys\n getPoolStats(): {\n activeConnections: number;\n tenants: string[];\n } {\n return {\n activeConnections: this.clients.size,\n tenants: Array.from(this.clients.keys()),\n };\n }\n\n // Masks password in connection URL for safe logging\n private maskPassword(url: string): string {\n return url.replace(/:([^@]+)@/, ':****@');\n }\n\n async onModuleDestroy() {\n // Stop cleanup interval\n if (this.cleanupInterval) {\n clearInterval(this.cleanupInterval);\n }\n\n // Disconnect all clients\n this.logger.log(`Disconnecting ${this.clients.size} database connections`);\n\n const disconnectPromises = Array.from(this.clients.entries()).map(async ([key, connection]) => {\n try {\n await connection.pool.end();\n this.logger.debug(`Disconnected: ${key}`);\n } catch (error) {\n this.logger.error(`Error disconnecting client: ${key}`, error);\n }\n });\n\n await Promise.all(disconnectPromises);\n this.logger.log('All database connections closed');\n }\n}\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport type { TenantInfo } from '../interfaces';\nimport { TenantContextService } from '../services/tenant-context.service';\n\nexport const Tenant = createParamDecorator((_data: unknown, ctx: ExecutionContext): TenantInfo => {\n const request = ctx.switchToHttp().getRequest();\n\n // Get from TenantContextService\n const tenantContext = request.app?.get?.(TenantContextService);\n\n if (!tenantContext) {\n throw new Error('TenantContextService not found.');\n }\n\n return tenantContext.getTenant();\n});\n","import { ApiPropertyOptional } from '@nestjs/swagger';\nimport { Type } from 'class-transformer';\nimport { IsInt, IsOptional, IsString, Min } from 'class-validator';\n\n// Standardized query params for select dropdown option endpoints\nexport class SelectOptionsQueryDto {\n @ApiPropertyOptional({ description: 'Search term to filter by label', example: 'united' })\n @IsOptional()\n @IsString()\n search?: string;\n\n @ApiPropertyOptional({ description: 'Maximum number of results', example: 20, default: 20 })\n @IsOptional()\n @Type(() => Number)\n @IsInt()\n @Min(1)\n limit?: number;\n\n @ApiPropertyOptional({ description: 'Number of results to skip', example: 0, default: 0 })\n @IsOptional()\n @Type(() => Number)\n @IsInt()\n @Min(0)\n offset?: number;\n\n @ApiPropertyOptional({ description: 'Comma-separated values to fetch specific options', example: '1,2,3' })\n @IsOptional()\n @IsString()\n values?: string;\n\n @ApiPropertyOptional({ description: 'Comma-separated IDs to exclude from results (already selected)', example: '5,10' })\n @IsOptional()\n @IsString()\n excludeIds?: string;\n\n @ApiPropertyOptional({ description: 'Column name for option value', example: 'id', default: 'id' })\n @IsOptional()\n @IsString()\n valueKey?: string;\n\n @ApiPropertyOptional({ description: 'Column name for option label', example: 'name', default: 'name' })\n @IsOptional()\n @IsString()\n labelKey?: string;\n\n @ApiPropertyOptional({ description: 'Column name for group ID', example: 'regionId' })\n @IsOptional()\n @IsString()\n groupIdKey?: string;\n}\n","import { Logger } from '@nestjs/common';\nimport {\n and,\n asc,\n type Column,\n eq,\n getTableName,\n type InferInsertModel,\n type InferSelectModel,\n ilike,\n inArray,\n notInArray,\n type SQL,\n sql,\n} from 'drizzle-orm';\nimport type { PgTable } from 'drizzle-orm/pg-core';\nimport type { TypedDrizzleClient } from '../schema.registry';\nimport { PrimaryDatabaseService } from '../services/primary-database.service';\nimport type { FindForSelectConfig, SelectQueryResult } from '../types';\n\n// Converts snake_case string to camelCase\nfunction snakeToCamel(str: string): string {\n return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());\n}\n\ntype RelationsWhereFilter = Record<string, unknown>;\n\ninterface TypedRelationalQueryBuilder<TSelect> {\n findFirst(config?: {\n where?: RelationsWhereFilter;\n with?: Record<string, unknown>;\n columns?: Record<string, boolean>;\n }): Promise<TSelect | undefined>;\n\n findMany(config?: {\n where?: RelationsWhereFilter;\n orderBy?: Record<string, 'asc' | 'desc'>;\n limit?: number;\n offset?: number;\n with?: Record<string, unknown>;\n columns?: Record<string, boolean>;\n }): Promise<TSelect[]>;\n}\n\nexport abstract class PrimaryBaseRepository<\n TTable extends PgTable,\n TInsert = InferInsertModel<TTable>,\n TSelect = InferSelectModel<TTable>,\n> {\n protected readonly logger: Logger;\n\n private readonly tableName: string;\n\n protected get db(): TypedDrizzleClient {\n return this.database.drizzleClient;\n }\n\n protected get model(): TypedRelationalQueryBuilder<TSelect> {\n const query = this.database.drizzleClient.query;\n const queryKeys = Object.keys(query || {});\n this.logger.debug(`Looking for '${this.tableName}' in query keys: [${queryKeys.join(', ')}]`);\n\n const model = query[this.tableName as keyof TypedDrizzleClient['query']];\n if (!model) {\n this.logger.error(`Table '${this.tableName}' not found in query object. Available: [${queryKeys.join(', ')}]`);\n }\n\n return model as unknown as TypedRelationalQueryBuilder<TSelect>;\n }\n\n constructor(\n protected readonly database: PrimaryDatabaseService,\n protected readonly table: TTable,\n ) {\n // Convert snake_case table name to camelCase to match Drizzle query object keys\n // Example: 'email_verifications' -> 'emailVerifications'\n const dbTableName = getTableName(table);\n this.tableName = snakeToCamel(dbTableName);\n this.logger = new Logger(this.constructor.name);\n this.logger.debug(`Initialized ${this.constructor.name}`);\n this.logger.debug(`Table name: '${dbTableName}' -> query key: '${this.tableName}'`);\n }\n\n // Creates a new record and returns it\n async create(data: TInsert): Promise<TSelect> {\n this.logger.log('Creating record');\n const results = (await this.db\n .insert(this.table as PgTable)\n .values(data as Record<string, unknown>)\n .returning()) as TSelect[];\n const record = results[0];\n if (!record) throw new Error(`${this.tableName}: database operation returned no record`);\n return record;\n }\n\n // Finds a single record by primary key ID\n async findById(id: string): Promise<TSelect | undefined> {\n this.logger.debug(`Finding record by ID: ${id}`);\n return this.model.findFirst({\n where: { id },\n });\n }\n\n // Finds a single record matching the given where filter\n async findOne(where: RelationsWhereFilter): Promise<TSelect | undefined> {\n this.logger.debug('Finding record with custom query');\n return this.model.findFirst({ where });\n }\n\n // Finds multiple records with optional filtering, ordering, and pagination\n async findMany(options?: {\n where?: RelationsWhereFilter;\n orderBy?: Record<string, 'asc' | 'desc'>;\n limit?: number;\n offset?: number;\n }): Promise<TSelect[]> {\n this.logger.debug('Finding multiple records');\n return this.model.findMany(options);\n }\n\n // Updates a record by ID and returns the updated record\n async update(id: string, data: Partial<TInsert>): Promise<TSelect> {\n this.logger.log(`Updating record with ID: ${id}`);\n const idColumn = (this.table as unknown as Record<string, Column>).id;\n if (!idColumn) throw new Error(`Table '${this.tableName}' has no 'id' column`);\n const results = (await this.db\n .update(this.table as PgTable)\n .set(data as Record<string, unknown>)\n .where(eq(idColumn, id))\n .returning()) as TSelect[];\n const record = results[0];\n if (!record) throw new Error(`${this.tableName}: database operation returned no record`);\n return record;\n }\n\n // Updates all records matching the SQL condition and returns the affected count\n async updateMany(where: SQL, data: Partial<TInsert>): Promise<{ count: number }> {\n this.logger.log('Updating multiple records');\n const result = await this.db\n .update(this.table as PgTable)\n .set(data as Record<string, unknown>)\n .where(where);\n return { count: result.rowCount ?? 0 };\n }\n\n // Deletes a record by ID and returns the deleted record\n async delete(id: string): Promise<TSelect> {\n this.logger.log(`Deleting record with ID: ${id}`);\n const idColumn = (this.table as unknown as Record<string, Column>).id;\n if (!idColumn) throw new Error(`Table '${this.tableName}' has no 'id' column`);\n const results = (await this.db\n .delete(this.table as PgTable)\n .where(eq(idColumn, id))\n .returning()) as TSelect[];\n const record = results[0];\n if (!record) throw new Error(`${this.tableName}: database operation returned no record`);\n return record;\n }\n\n // Deletes all records matching the SQL condition and returns the affected count\n async deleteMany(where: SQL): Promise<{ count: number }> {\n this.logger.log('Deleting multiple records');\n const result = await this.db.delete(this.table as PgTable).where(where);\n return { count: result.rowCount ?? 0 };\n }\n\n // Counts records matching the optional SQL condition\n async count(where?: SQL): Promise<number> {\n this.logger.debug('Counting records');\n\n let query = this.db\n .select({ count: sql<number>`count(*)::int` })\n .from(this.table as PgTable)\n .$dynamic();\n\n if (where) {\n query = query.where(where);\n }\n\n const results = await query;\n return (results[0] as { count: number }).count;\n }\n\n // Returns true if at least one record matches the SQL condition\n async exists(where: SQL): Promise<boolean> {\n const count = await this.count(where);\n return count > 0;\n }\n\n // Finds records formatted as select dropdown options with optional search, pagination, and grouping\n async findForSelect(config: FindForSelectConfig): Promise<SelectQueryResult> {\n this.logger.debug('Finding records for select dropdown');\n\n interface SelectRow {\n value: string | number | boolean;\n label: string;\n groupId?: string | number;\n }\n interface SelectRowWithCount extends SelectRow {\n totalCount: number;\n }\n\n // Parse values from CSV string or use array as-is\n const parsedValues =\n typeof config.values === 'string'\n ? config.values\n .split(',')\n .map((v) => v.trim())\n .filter(Boolean)\n : config.values;\n\n // Parse excludeIds from CSV string or use array as-is\n const parsedExcludeIds =\n typeof config.excludeIds === 'string'\n ? config.excludeIds\n .split(',')\n .map((v) => v.trim())\n .filter(Boolean)\n : (config.excludeIds ?? []);\n\n const tableColumns = this.table as unknown as Record<string, Column>;\n const valueCol = tableColumns[config.value];\n if (!valueCol) throw new Error(`Column '${config.value}' not found in table '${this.tableName}'`);\n const labelCol = tableColumns[config.label];\n if (!labelCol) throw new Error(`Column '${config.label}' not found in table '${this.tableName}'`);\n\n // When values are provided, fetch those specific options by value (skip search/pagination)\n if (parsedValues && parsedValues.length > 0) {\n const selectCols: Record<string, Column | SQL> = { value: valueCol, label: labelCol };\n if (config.groupId) {\n const groupIdCol = tableColumns[config.groupId];\n if (groupIdCol) selectCols.groupId = groupIdCol;\n }\n\n const rows = await this.db\n .select(selectCols)\n .from(this.table as PgTable)\n .where(inArray(valueCol, parsedValues));\n\n return {\n options: (rows as unknown as SelectRow[]).map((row) => ({\n value: row.value,\n label: String(row.label),\n ...(config.groupId && row.groupId != null ? { groupId: row.groupId } : {}),\n })),\n hasMore: false,\n ...(config.groups ? { groups: config.groups } : {}),\n };\n }\n\n // Use SQL builder for count(*) over() window function support\n const selectFields: Record<string, Column | SQL> = {\n value: valueCol,\n label: labelCol,\n totalCount: sql<number>`count(*) over()`.mapWith(Number),\n };\n if (config.groupId) {\n const groupIdCol = tableColumns[config.groupId];\n if (groupIdCol) selectFields.groupId = groupIdCol;\n }\n\n const conditions: SQL[] = [];\n if (config.search) {\n conditions.push(ilike(labelCol, `%${config.search}%`));\n }\n if (parsedExcludeIds.length > 0) {\n conditions.push(notInArray(valueCol, parsedExcludeIds));\n }\n if (config.where) {\n for (const [field, val] of Object.entries(config.where)) {\n const column = tableColumns[field];\n if (column) {\n conditions.push(eq(column, val));\n }\n }\n }\n\n const orderByKey = config.orderBy ? Object.keys(config.orderBy)[0] : undefined;\n const orderByCol = orderByKey ? (tableColumns[orderByKey] ?? labelCol) : labelCol;\n const limit = Number(config.limit) || 20;\n const offset = Number(config.offset) || 0;\n\n let query = this.db\n .select(selectFields)\n .from(this.table as PgTable)\n .$dynamic();\n\n if (conditions.length > 0) {\n query = query.where(conditions.length === 1 ? conditions[0] : and(...conditions) as SQL);\n }\n\n const orderClauses: SQL[] = [];\n if (config.groupId) {\n const groupIdCol = tableColumns[config.groupId];\n if (groupIdCol) orderClauses.push(asc(groupIdCol));\n }\n orderClauses.push(asc(orderByCol));\n\n query = query\n .orderBy(...orderClauses)\n .limit(limit)\n .offset(offset);\n\n const rows = await query;\n\n const totalCount = rows.length > 0 ? (rows[0] as unknown as SelectRowWithCount).totalCount : 0;\n\n const options = (rows as unknown as SelectRow[]).map((row) => ({\n value: row.value,\n label: String(row.label),\n ...(config.groupId && row.groupId != null ? { groupId: row.groupId } : {}),\n }));\n\n // Auto-resolve groups from groupTable when provided\n let resolvedGroups = config.groups;\n\n if (config.groupTable && config.groupId) {\n const groupTableColumns = config.groupTable as unknown as Record<string, Column>;\n const groupIdKey = config.groupIdKey ?? 'id';\n const groupNameKey = config.groupLabelKey ?? 'name';\n const groupIdCol = groupTableColumns[groupIdKey];\n if (!groupIdCol) throw new Error(`Column '${groupIdKey}' not found in group table`);\n const groupNameCol = groupTableColumns[groupNameKey];\n if (!groupNameCol) throw new Error(`Column '${groupNameKey}' not found in group table`);\n\n const groupRows = await this.db\n .select({ id: groupIdCol, name: groupNameCol })\n .from(config.groupTable)\n .orderBy(asc(groupNameCol));\n\n resolvedGroups = (groupRows as unknown as Array<{ id: string | number; name: string }>).map((r) => ({\n id: r.id,\n name: String(r.name),\n }));\n }\n\n return {\n options,\n hasMore: offset + limit < totalCount,\n totalCount,\n ...(resolvedGroups ? { groups: resolvedGroups } : {}),\n };\n }\n}\n","import { Logger } from '@nestjs/common';\nimport {\n and,\n asc,\n type Column,\n eq,\n getTableName,\n type InferInsertModel,\n type InferSelectModel,\n ilike,\n inArray,\n notInArray,\n type SQL,\n sql,\n} from 'drizzle-orm';\nimport type { PgTable } from 'drizzle-orm/pg-core';\nimport type { TypedDrizzleClient } from '../schema.registry';\nimport { TenantDatabaseService } from '../services/tenant-database.service';\nimport type { FindForSelectConfig, SelectQueryResult } from '../types';\n\ntype ExtractTableName<TTable extends PgTable> = TTable['_']['name'];\n\nexport abstract class TenantBaseRepository<\n TTable extends PgTable,\n TInsert = InferInsertModel<TTable>,\n TSelect = InferSelectModel<TTable>,\n> {\n protected readonly logger: Logger;\n\n private readonly tableName: string;\n\n protected get db(): TypedDrizzleClient {\n return this.database.drizzleClient;\n }\n\n protected get model(): TypedDrizzleClient['query'][ExtractTableName<TTable> & keyof TypedDrizzleClient['query']] {\n return this.database.drizzleClient.query[\n this.tableName as ExtractTableName<TTable> & keyof TypedDrizzleClient['query']\n ];\n }\n\n constructor(\n protected readonly database: TenantDatabaseService,\n protected readonly table: TTable,\n ) {\n this.tableName = getTableName(table);\n this.logger = new Logger(this.constructor.name);\n this.logger.debug(`Initialized ${this.constructor.name}`);\n }\n\n // Creates a new record and returns it\n async create(data: TInsert): Promise<TSelect> {\n this.logger.log('Creating record');\n const results = (await this.db\n .insert(this.table as PgTable)\n .values(data as Record<string, unknown>)\n .returning()) as TSelect[];\n const record = results[0];\n if (!record) throw new Error(`${this.tableName}: database operation returned no record`);\n return record;\n }\n\n // Finds a single record by primary key ID\n async findById(id: string): Promise<TSelect | null> {\n this.logger.debug(`Finding record by ID: ${id}`);\n const idColumn = (this.table as unknown as Record<string, Column>).id;\n if (!idColumn) throw new Error(`Table '${this.tableName}' has no 'id' column`);\n const results = await this.db\n .select()\n .from(this.table as PgTable)\n .where(eq(idColumn, id))\n .limit(1);\n return (results[0] as TSelect) ?? null;\n }\n\n // Finds a single record matching the given SQL condition\n async findOne(where: SQL): Promise<TSelect | null> {\n this.logger.debug('Finding record with custom query');\n const results = await this.db\n .select()\n .from(this.table as PgTable)\n .where(where)\n .limit(1);\n return (results[0] as TSelect) ?? null;\n }\n\n // Finds multiple records with optional SQL filtering, ordering, and pagination\n async findMany(options?: { where?: SQL; orderBy?: SQL; limit?: number; offset?: number }): Promise<TSelect[]> {\n this.logger.debug('Finding multiple records');\n\n let query = this.db\n .select()\n .from(this.table as PgTable)\n .$dynamic();\n\n if (options?.where) {\n query = query.where(options.where);\n }\n if (options?.orderBy) {\n query = query.orderBy(options.orderBy);\n }\n if (options?.limit) {\n query = query.limit(options.limit);\n }\n if (options?.offset) {\n query = query.offset(options.offset);\n }\n\n return (await query) as TSelect[];\n }\n\n // Updates a record by ID and returns the updated record\n async update(id: string, data: Partial<TInsert>): Promise<TSelect> {\n this.logger.log(`Updating record with ID: ${id}`);\n const idColumn = (this.table as unknown as Record<string, Column>).id;\n if (!idColumn) throw new Error(`Table '${this.tableName}' has no 'id' column`);\n const results = (await this.db\n .update(this.table as PgTable)\n .set(data as Record<string, unknown>)\n .where(eq(idColumn, id))\n .returning()) as TSelect[];\n const record = results[0];\n if (!record) throw new Error(`${this.tableName}: database operation returned no record`);\n return record;\n }\n\n // Updates all records matching the SQL condition and returns the affected count\n async updateMany(where: SQL, data: Partial<TInsert>): Promise<{ count: number }> {\n this.logger.log('Updating multiple records');\n const result = await this.db\n .update(this.table as PgTable)\n .set(data as Record<string, unknown>)\n .where(where);\n return { count: result.rowCount ?? 0 };\n }\n\n // Deletes a record by ID and returns the deleted record\n async delete(id: string): Promise<TSelect> {\n this.logger.log(`Deleting record with ID: ${id}`);\n const idColumn = (this.table as unknown as Record<string, Column>).id;\n if (!idColumn) throw new Error(`Table '${this.tableName}' has no 'id' column`);\n const results = (await this.db\n .delete(this.table as PgTable)\n .where(eq(idColumn, id))\n .returning()) as TSelect[];\n const record = results[0];\n if (!record) throw new Error(`${this.tableName}: database operation returned no record`);\n return record;\n }\n\n // Deletes all records matching the SQL condition and returns the affected count\n async deleteMany(where: SQL): Promise<{ count: number }> {\n this.logger.log('Deleting multiple records');\n const result = await this.db.delete(this.table as PgTable).where(where);\n return { count: result.rowCount ?? 0 };\n }\n\n // Counts records matching the optional SQL condition\n async count(where?: SQL): Promise<number> {\n this.logger.debug('Counting records');\n\n let query = this.db\n .select({ count: sql<number>`count(*)::int` })\n .from(this.table as PgTable)\n .$dynamic();\n\n if (where) {\n query = query.where(where);\n }\n\n const results = await query;\n return (results[0] as { count: number }).count;\n }\n\n // Returns true if at least one record matches the SQL condition\n async exists(where: SQL): Promise<boolean> {\n const count = await this.count(where);\n return count > 0;\n }\n\n // Finds records formatted as select dropdown options with optional search, pagination, and grouping\n async findForSelect(config: FindForSelectConfig): Promise<SelectQueryResult> {\n this.logger.debug('Finding records for select dropdown');\n\n interface SelectRow {\n value: string | number | boolean;\n label: string;\n groupId?: string | number;\n }\n interface SelectRowWithCount extends SelectRow {\n totalCount: number;\n }\n\n // Parse values from CSV string or use array as-is\n const parsedValues =\n typeof config.values === 'string'\n ? config.values\n .split(',')\n .map((v) => v.trim())\n .filter(Boolean)\n : config.values;\n\n // Parse excludeIds from CSV string or use array as-is\n const parsedExcludeIds =\n typeof config.excludeIds === 'string'\n ? config.excludeIds.split(',').map((v) => v.trim()).filter(Boolean)\n : config.excludeIds ?? [];\n\n const tableColumns = this.table as unknown as Record<string, Column>;\n const valueCol = tableColumns[config.value];\n if (!valueCol) throw new Error(`Column '${config.value}' not found in table '${this.tableName}'`);\n const labelCol = tableColumns[config.label];\n if (!labelCol) throw new Error(`Column '${config.label}' not found in table '${this.tableName}'`);\n\n // When values are provided, fetch those specific options by value (skip search/pagination)\n if (parsedValues && parsedValues.length > 0) {\n const selectCols: Record<string, Column | SQL> = { value: valueCol, label: labelCol };\n if (config.groupId) {\n const groupIdCol = tableColumns[config.groupId];\n if (groupIdCol) selectCols.groupId = groupIdCol;\n }\n\n const rows = await this.db\n .select(selectCols)\n .from(this.table as PgTable)\n .where(inArray(valueCol, parsedValues));\n\n return {\n options: (rows as unknown as SelectRow[]).map((row) => ({\n value: row.value,\n label: String(row.label),\n ...(config.groupId && row.groupId != null ? { groupId: row.groupId } : {}),\n })),\n hasMore: false,\n ...(config.groups ? { groups: config.groups } : {}),\n };\n }\n\n const selectFields: Record<string, Column | SQL> = {\n value: valueCol,\n label: labelCol,\n totalCount: sql<number>`count(*) over()`.mapWith(Number),\n };\n if (config.groupId) {\n const groupIdCol = tableColumns[config.groupId];\n if (groupIdCol) selectFields.groupId = groupIdCol;\n }\n\n const conditions: SQL[] = [];\n if (config.search) {\n conditions.push(ilike(labelCol, `%${config.search}%`));\n }\n if (parsedExcludeIds.length > 0) {\n conditions.push(notInArray(valueCol, parsedExcludeIds));\n }\n if (config.where) {\n for (const [field, val] of Object.entries(config.where)) {\n const column = tableColumns[field];\n if (column) {\n conditions.push(eq(column, val));\n }\n }\n }\n\n const orderByKey = config.orderBy ? Object.keys(config.orderBy)[0] : undefined;\n const orderByCol = orderByKey ? (tableColumns[orderByKey] ?? labelCol) : labelCol;\n const limit = Number(config.limit) || 20;\n const offset = Number(config.offset) || 0;\n\n let query = this.db\n .select(selectFields)\n .from(this.table as PgTable)\n .$dynamic();\n\n if (conditions.length > 0) {\n query = query.where(conditions.length === 1 ? conditions[0] : and(...conditions) as SQL);\n }\n\n const orderClauses: SQL[] = [];\n if (config.groupId) {\n const groupIdCol = tableColumns[config.groupId];\n if (groupIdCol) orderClauses.push(asc(groupIdCol));\n }\n orderClauses.push(asc(orderByCol));\n\n query = query\n .orderBy(...orderClauses)\n .limit(limit)\n .offset(offset);\n\n const rows = await query;\n\n const totalCount = rows.length > 0 ? (rows[0] as unknown as SelectRowWithCount).totalCount : 0;\n\n const options = (rows as unknown as SelectRow[]).map((row) => ({\n value: row.value,\n label: String(row.label),\n ...(config.groupId && row.groupId != null ? { groupId: row.groupId } : {}),\n }));\n\n // Auto-resolve groups from groupTable when provided\n let resolvedGroups = config.groups;\n\n if (config.groupTable && config.groupId) {\n const groupTableColumns = config.groupTable as unknown as Record<string, Column>;\n const groupIdKey = config.groupIdKey ?? 'id';\n const groupNameKey = config.groupLabelKey ?? 'name';\n const groupIdCol = groupTableColumns[groupIdKey];\n if (!groupIdCol) throw new Error(`Column '${groupIdKey}' not found in group table`);\n const groupNameCol = groupTableColumns[groupNameKey];\n if (!groupNameCol) throw new Error(`Column '${groupNameKey}' not found in group table`);\n\n const groupRows = await this.db\n .select({ id: groupIdCol, name: groupNameCol })\n .from(config.groupTable)\n .orderBy(asc(groupNameCol));\n\n resolvedGroups = (groupRows as unknown as Array<{ id: string | number; name: string }>).map((r) => ({\n id: r.id,\n name: String(r.name),\n }));\n }\n\n return {\n options,\n hasMore: offset + limit < totalCount,\n totalCount,\n ...(resolvedGroups ? { groups: resolvedGroups } : {}),\n };\n }\n}\n","import { Global, Module } from '@nestjs/common';\nimport { ConfigModule } from '@nestjs/config';\nimport { EmailService } from './email.service';\n\n@Global()\n@Module({\n imports: [ConfigModule],\n providers: [EmailService],\n exports: [EmailService],\n})\nexport class EmailModule {}\n","import { BrevoClient, BrevoError, BrevoTimeoutError } from '@getbrevo/brevo';\nimport { Injectable, Logger } from '@nestjs/common';\nimport { ConfigService } from '@nestjs/config';\n\n@Injectable()\nexport class EmailService {\n private readonly logger = new Logger(EmailService.name);\n private readonly brevoClient: BrevoClient;\n private readonly senderEmail: string;\n private readonly senderName: string;\n\n constructor(private readonly configService: ConfigService) {\n const apiKey = this.configService.get<string>('BREVO_API_KEY');\n\n if (!apiKey) {\n this.logger.error('BREVO_API_KEY is not configured. Email sending will fail.');\n throw new Error('Email service configuration error: Missing BREVO_API_KEY');\n }\n\n // Initialize Brevo client with built-in retry support\n this.brevoClient = new BrevoClient({ apiKey, maxRetries: 3 });\n\n // Get sender configuration\n const senderEmail = this.configService.get<string>('SENDER_EMAIL');\n const senderName = this.configService.get<string>('SENDER_NAME');\n\n if (!senderEmail || !senderName) {\n this.logger.error('Sender email or name is not configured.');\n throw new Error('Email service configuration error: Missing SENDER_EMAIL or SENDER_NAME');\n }\n\n this.senderEmail = senderEmail;\n this.senderName = senderName;\n\n this.logger.log('Brevo email service initialized successfully');\n }\n\n // Sends an email verification OTP to the given recipient\n async sendVerificationEmail(email: string, otp: string, expiresAt: Date, displayName?: string): Promise<void> {\n const name = displayName || 'there';\n const expiryMinutes = Math.ceil((expiresAt.getTime() - Date.now()) / 60_000);\n const subject = 'Verify Your Email - Vritti AI Cloud';\n\n const htmlContent = `\n <!DOCTYPE html>\n <html>\n <head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n </head>\n <body style=\"margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background-color: #f5f5f5;\">\n <table role=\"presentation\" style=\"width: 100%; border-collapse: collapse;\">\n <tr>\n <td style=\"padding: 40px 20px;\">\n <table role=\"presentation\" style=\"max-width: 600px; margin: 0 auto; background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);\">\n <!-- Header -->\n <tr>\n <td style=\"padding: 40px 40px 20px; text-align: center; border-bottom: 1px solid #e0e0e0;\">\n <h1 style=\"margin: 0; color: #1a1a1a; font-size: 24px; font-weight: 600;\">Email Verification</h1>\n </td>\n </tr>\n\n <!-- Content -->\n <tr>\n <td style=\"padding: 40px;\">\n <p style=\"margin: 0 0 20px; color: #333333; font-size: 16px; line-height: 1.6;\">\n Hello <strong>${name}</strong>,\n </p>\n <p style=\"margin: 0 0 30px; color: #333333; font-size: 16px; line-height: 1.6;\">\n Thank you for signing up with Vritti AI Cloud. Please use the following verification code to complete your registration:\n </p>\n\n <!-- OTP Box -->\n <div style=\"background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 30px; border-radius: 8px; text-align: center; margin: 30px 0;\">\n <div style=\"color: #ffffff; font-size: 36px; font-weight: bold; letter-spacing: 10px; font-family: 'Courier New', monospace;\">\n ${otp}\n </div>\n </div>\n\n <p style=\"margin: 30px 0 20px; color: #666666; font-size: 14px; line-height: 1.6;\">\n <strong>Important:</strong> This code will expire in <strong>${expiryMinutes} minute${expiryMinutes === 1 ? '' : 's'}</strong>.\n </p>\n <p style=\"margin: 0; color: #666666; font-size: 14px; line-height: 1.6;\">\n If you didn't request this verification, please ignore this email.\n </p>\n </td>\n </tr>\n\n <!-- Footer -->\n <tr>\n <td style=\"padding: 30px 40px; border-top: 1px solid #e0e0e0; text-align: center;\">\n <p style=\"margin: 0; color: #999999; font-size: 12px; line-height: 1.5;\">\n Vritti AI Cloud - Cloud Management Platform\n </p>\n <p style=\"margin: 8px 0 0; color: #999999; font-size: 12px; line-height: 1.5;\">\n This is an automated message, please do not reply.\n </p>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n </table>\n </body>\n </html>\n `;\n\n const textContent = `\nHello ${name},\n\nThank you for signing up with Vritti AI Cloud. Please use the following verification code to complete your registration:\n\nVerification Code: ${otp}\n\nThis code will expire in ${expiryMinutes} minute${expiryMinutes === 1 ? '' : 's'}.\n\nIf you didn't request this verification, please ignore this email.\n\n---\nVritti AI Cloud - Cloud Management Platform\nThis is an automated message, please do not reply.\n `.trim();\n\n await this.sendEmail({\n to: [{ email, name }],\n subject,\n htmlContent,\n textContent,\n });\n\n this.logger.log(`Verification email sent to ${email}`);\n }\n\n // Sends a password reset OTP to the given recipient\n async sendPasswordResetEmail(email: string, otp: string, expiresAt: Date, displayName?: string): Promise<void> {\n const name = displayName || 'there';\n const expiryMinutes = Math.ceil((expiresAt.getTime() - Date.now()) / 60_000);\n const subject = 'Reset Your Password - Vritti AI Cloud';\n\n const htmlContent = `\n <!DOCTYPE html>\n <html>\n <head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n </head>\n <body style=\"margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background-color: #f5f5f5;\">\n <table role=\"presentation\" style=\"width: 100%; border-collapse: collapse;\">\n <tr>\n <td style=\"padding: 40px 20px;\">\n <table role=\"presentation\" style=\"max-width: 600px; margin: 0 auto; background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);\">\n <!-- Header -->\n <tr>\n <td style=\"padding: 40px 40px 20px; text-align: center; border-bottom: 1px solid #e0e0e0;\">\n <h1 style=\"margin: 0; color: #1a1a1a; font-size: 24px; font-weight: 600;\">Password Reset</h1>\n </td>\n </tr>\n\n <!-- Content -->\n <tr>\n <td style=\"padding: 40px;\">\n <p style=\"margin: 0 0 20px; color: #333333; font-size: 16px; line-height: 1.6;\">\n Hello <strong>${name}</strong>,\n </p>\n <p style=\"margin: 0 0 30px; color: #333333; font-size: 16px; line-height: 1.6;\">\n We received a request to reset your password. Use the following code to complete the process:\n </p>\n\n <!-- OTP Box -->\n <div style=\"background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); padding: 30px; border-radius: 8px; text-align: center; margin: 30px 0;\">\n <div style=\"color: #ffffff; font-size: 36px; font-weight: bold; letter-spacing: 10px; font-family: 'Courier New', monospace;\">\n ${otp}\n </div>\n </div>\n\n <p style=\"margin: 30px 0 20px; color: #666666; font-size: 14px; line-height: 1.6;\">\n <strong>Important:</strong> This code will expire in <strong>${expiryMinutes} minute${expiryMinutes === 1 ? '' : 's'}</strong>.\n </p>\n <p style=\"margin: 0 0 20px; color: #666666; font-size: 14px; line-height: 1.6;\">\n If you didn't request a password reset, please ignore this email and your password will remain unchanged.\n </p>\n <div style=\"background-color: #fff3cd; border-left: 4px solid #ffc107; padding: 15px; margin-top: 20px; border-radius: 4px;\">\n <p style=\"margin: 0; color: #856404; font-size: 13px; line-height: 1.5;\">\n <strong>Security Tip:</strong> Never share this code with anyone. Vritti will never ask for your verification code.\n </p>\n </div>\n </td>\n </tr>\n\n <!-- Footer -->\n <tr>\n <td style=\"padding: 30px 40px; border-top: 1px solid #e0e0e0; text-align: center;\">\n <p style=\"margin: 0; color: #999999; font-size: 12px; line-height: 1.5;\">\n Vritti AI Cloud - Cloud Management Platform\n </p>\n <p style=\"margin: 8px 0 0; color: #999999; font-size: 12px; line-height: 1.5;\">\n This is an automated message, please do not reply.\n </p>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n </table>\n </body>\n </html>\n `;\n\n const textContent = `\nHello ${name},\n\nWe received a request to reset your password. Use the following code to complete the process:\n\nReset Code: ${otp}\n\nThis code will expire in ${expiryMinutes} minute${expiryMinutes === 1 ? '' : 's'}.\n\nIf you didn't request a password reset, please ignore this email and your password will remain unchanged.\n\nSECURITY TIP: Never share this code with anyone. Vritti will never ask for your verification code.\n\n---\nVritti AI Cloud - Cloud Management Platform\nThis is an automated message, please do not reply.\n `.trim();\n\n await this.sendEmail({\n to: [{ email, name }],\n subject,\n htmlContent,\n textContent,\n });\n\n this.logger.log(`Password reset email sent to ${email}`);\n }\n\n // Sends an email change notification to the old address with a revert link\n async sendEmailChangeNotification(\n oldEmail: string,\n newEmail: string,\n revertToken: string,\n revertExpiresAt: Date,\n displayName?: string,\n ): Promise<void> {\n const name = displayName || 'there';\n const subject = 'Your Email Address Has Been Changed - Vritti AI Cloud';\n\n // Calculate hours until expiry\n const hoursUntilExpiry = Math.floor((revertExpiresAt.getTime() - Date.now()) / (1000 * 60 * 60));\n\n // TODO: Replace with actual frontend URL from config\n const revertLink = `https://local.vrittiai.com:3012/settings/profile/revert-email?token=${revertToken}`;\n\n const htmlContent = `\n <!DOCTYPE html>\n <html>\n <head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n </head>\n <body style=\"margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background-color: #f5f5f5;\">\n <table role=\"presentation\" style=\"width: 100%; border-collapse: collapse;\">\n <tr>\n <td style=\"padding: 40px 20px;\">\n <table role=\"presentation\" style=\"max-width: 600px; margin: 0 auto; background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);\">\n <!-- Header -->\n <tr>\n <td style=\"padding: 40px 40px 20px; text-align: center; border-bottom: 1px solid #e0e0e0;\">\n <h1 style=\"margin: 0; color: #1a1a1a; font-size: 24px; font-weight: 600;\">Email Address Changed</h1>\n </td>\n </tr>\n\n <!-- Content -->\n <tr>\n <td style=\"padding: 40px;\">\n <p style=\"margin: 0 0 20px; color: #333333; font-size: 16px; line-height: 1.6;\">\n Hello <strong>${name}</strong>,\n </p>\n <p style=\"margin: 0 0 30px; color: #333333; font-size: 16px; line-height: 1.6;\">\n We're writing to inform you that your Vritti AI Cloud email address has been successfully changed.\n </p>\n\n <div style=\"background-color: #f8f9fa; padding: 20px; border-radius: 8px; margin: 30px 0;\">\n <p style=\"margin: 0 0 10px; color: #666666; font-size: 14px;\">\n <strong>Previous Email:</strong>\n </p>\n <p style=\"margin: 0 0 20px; color: #333333; font-size: 16px; font-family: monospace;\">\n ${oldEmail}\n </p>\n <p style=\"margin: 0 0 10px; color: #666666; font-size: 14px;\">\n <strong>New Email:</strong>\n </p>\n <p style=\"margin: 0; color: #333333; font-size: 16px; font-family: monospace;\">\n ${newEmail}\n </p>\n </div>\n\n <div style=\"background-color: #fff3cd; border-left: 4px solid #ffc107; padding: 20px; margin: 30px 0; border-radius: 4px;\">\n <p style=\"margin: 0 0 15px; color: #856404; font-size: 14px; line-height: 1.6;\">\n <strong>Didn't make this change?</strong>\n </p>\n <p style=\"margin: 0 0 20px; color: #856404; font-size: 14px; line-height: 1.6;\">\n If you did not authorize this change, you can revert it within the next <strong>${hoursUntilExpiry} hours</strong> by clicking the button below:\n </p>\n <div style=\"text-align: center;\">\n <a href=\"${revertLink}\" style=\"display: inline-block; padding: 12px 30px; background-color: #dc3545; color: #ffffff; text-decoration: none; border-radius: 6px; font-weight: 600; font-size: 14px;\">\n Revert Email Change\n </a>\n </div>\n </div>\n\n <p style=\"margin: 30px 0 0; color: #666666; font-size: 14px; line-height: 1.6;\">\n If you made this change, you can safely ignore this email.\n </p>\n </td>\n </tr>\n\n <!-- Footer -->\n <tr>\n <td style=\"padding: 30px 40px; border-top: 1px solid #e0e0e0; text-align: center;\">\n <p style=\"margin: 0; color: #999999; font-size: 12px; line-height: 1.5;\">\n Vritti AI Cloud - Cloud Management Platform\n </p>\n <p style=\"margin: 8px 0 0; color: #999999; font-size: 12px; line-height: 1.5;\">\n This is an automated message, please do not reply.\n </p>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n </table>\n </body>\n </html>\n `;\n\n const textContent = `\nHello ${name},\n\nWe're writing to inform you that your Vritti AI Cloud email address has been successfully changed.\n\nPrevious Email: ${oldEmail}\nNew Email: ${newEmail}\n\nDIDN'T MAKE THIS CHANGE?\n\nIf you did not authorize this change, you can revert it within the next ${hoursUntilExpiry} hours by visiting:\n${revertLink}\n\nIf you made this change, you can safely ignore this email.\n\n---\nVritti AI Cloud - Cloud Management Platform\nThis is an automated message, please do not reply.\n `.trim();\n\n await this.sendEmail({\n to: [{ email: oldEmail, name }],\n subject,\n htmlContent,\n textContent,\n });\n\n this.logger.log(`Email change notification sent to ${oldEmail}`);\n }\n\n // Sends a confirmation to the restored email address after a revert\n async sendEmailRevertConfirmation(email: string, displayName?: string): Promise<void> {\n const name = displayName || 'there';\n const subject = 'Email Address Change Reverted - Vritti AI Cloud';\n\n const htmlContent = `\n <!DOCTYPE html>\n <html>\n <head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n </head>\n <body style=\"margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background-color: #f5f5f5;\">\n <table role=\"presentation\" style=\"width: 100%; border-collapse: collapse;\">\n <tr>\n <td style=\"padding: 40px 20px;\">\n <table role=\"presentation\" style=\"max-width: 600px; margin: 0 auto; background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);\">\n <!-- Header -->\n <tr>\n <td style=\"padding: 40px 40px 20px; text-align: center; border-bottom: 1px solid #e0e0e0;\">\n <h1 style=\"margin: 0; color: #1a1a1a; font-size: 24px; font-weight: 600;\">Email Change Reverted</h1>\n </td>\n </tr>\n\n <!-- Content -->\n <tr>\n <td style=\"padding: 40px;\">\n <p style=\"margin: 0 0 20px; color: #333333; font-size: 16px; line-height: 1.6;\">\n Hello <strong>${name}</strong>,\n </p>\n <p style=\"margin: 0 0 30px; color: #333333; font-size: 16px; line-height: 1.6;\">\n Your recent email address change has been successfully reverted. Your email is now:\n </p>\n\n <div style=\"background-color: #d4edda; padding: 20px; border-radius: 8px; margin: 30px 0; text-align: center;\">\n <p style=\"margin: 0; color: #155724; font-size: 18px; font-weight: 600; font-family: monospace;\">\n ${email}\n </p>\n </div>\n\n <p style=\"margin: 30px 0 20px; color: #666666; font-size: 14px; line-height: 1.6;\">\n If you did not request this revert, please contact our support team immediately.\n </p>\n </td>\n </tr>\n\n <!-- Footer -->\n <tr>\n <td style=\"padding: 30px 40px; border-top: 1px solid #e0e0e0; text-align: center;\">\n <p style=\"margin: 0; color: #999999; font-size: 12px; line-height: 1.5;\">\n Vritti AI Cloud - Cloud Management Platform\n </p>\n <p style=\"margin: 8px 0 0; color: #999999; font-size: 12px; line-height: 1.5;\">\n This is an automated message, please do not reply.\n </p>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n </table>\n </body>\n </html>\n `;\n\n const textContent = `\nHello ${name},\n\nYour recent email address change has been successfully reverted. Your email is now:\n\n${email}\n\nIf you did not request this revert, please contact our support team immediately.\n\n---\nVritti AI Cloud - Cloud Management Platform\nThis is an automated message, please do not reply.\n `.trim();\n\n await this.sendEmail({\n to: [{ email, name }],\n subject,\n htmlContent,\n textContent,\n });\n\n this.logger.log(`Email revert confirmation sent to ${email}`);\n }\n\n // Verifies Brevo API connectivity — a 400 response means the API is reachable\n async verifyConnection(): Promise<boolean> {\n try {\n await this.brevoClient.transactionalEmails.sendTransacEmail({\n sender: { email: this.senderEmail, name: this.senderName },\n to: [{ email: this.senderEmail }],\n subject: 'Connection Test',\n htmlContent: '<p>Test</p>',\n });\n return true;\n } catch (err) {\n // A 400 error means the API is reachable but params are incomplete — still a successful connection test\n if (err instanceof BrevoError && err.statusCode === 400) {\n return true;\n }\n this.logger.error('Brevo connection verification failed:', err);\n return false;\n }\n }\n\n // Sends a transactional email via Brevo — retries handled internally by BrevoClient\n private async sendEmail(emailData: {\n to: Array<{ email: string; name?: string }>;\n subject: string;\n htmlContent: string;\n textContent: string;\n }): Promise<void> {\n try {\n const result = await this.brevoClient.transactionalEmails.sendTransacEmail({\n sender: { email: this.senderEmail, name: this.senderName },\n to: emailData.to,\n subject: emailData.subject,\n htmlContent: emailData.htmlContent,\n textContent: emailData.textContent,\n });\n this.logger.debug(`Email sent successfully. Message ID: ${result.messageId}`);\n } catch (err) {\n if (err instanceof BrevoTimeoutError) {\n this.logger.error('Brevo request timed out after retries.');\n throw new Error('Email sending failed: timeout');\n }\n if (err instanceof BrevoError) {\n if (err.statusCode === 429) {\n this.logger.error('Brevo rate limit exceeded after retries.');\n throw new Error('Email sending failed: rate limit exceeded');\n }\n if (err.statusCode === 401) {\n this.logger.error('Brevo authentication failed. Check your API key.');\n throw new Error('Email service authentication failed');\n }\n if (err.statusCode === 400) {\n this.logger.error('Bad request to Brevo API:', err.message);\n throw new Error(`Invalid email parameters: ${err.message}`);\n }\n this.logger.error(`Brevo API error ${err.statusCode}:`, err.message);\n throw new Error(`Email sending failed: ${err.message}`);\n }\n throw err;\n }\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class BadGatewayException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Bad Gateway', HttpStatus.BAD_GATEWAY);\n }\n}\n","import { HttpException, HttpStatus } from '@nestjs/common';\nimport type { FieldError } from '../types/error-response.types';\n\n// Re-export FieldError for backwards compatibility\nexport type { FieldError } from '../types/error-response.types';\n\nexport interface ProblemOptions {\n type?: string;\n label?: string;\n detail?: string;\n errors?: FieldError[];\n}\n\nexport abstract class HttpProblemException extends HttpException {\n constructor(detailOrOptions: string | ProblemOptions, httpStatus: HttpStatus) {\n const options = typeof detailOrOptions === 'string' ? { detail: detailOrOptions } : detailOrOptions;\n\n super(\n {\n type: options.type ?? 'about:blank',\n label: options.label,\n detail: options.detail,\n errors: options.errors ?? [],\n },\n httpStatus,\n );\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class BadRequestException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Bad Request', HttpStatus.BAD_REQUEST);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ConflictException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Conflict', HttpStatus.CONFLICT);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ForbiddenException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Forbidden', HttpStatus.FORBIDDEN);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class GoneException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Gone', HttpStatus.GONE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class InternalServerErrorException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Internal Server Error', HttpStatus.INTERNAL_SERVER_ERROR);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class MethodNotAllowedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Method Not Allowed', HttpStatus.METHOD_NOT_ALLOWED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class NotAcceptableException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Acceptable', HttpStatus.NOT_ACCEPTABLE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class NotFoundException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Found', HttpStatus.NOT_FOUND);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class NotImplementedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Implemented', HttpStatus.NOT_IMPLEMENTED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class PayloadTooLargeException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Payload Too Large', HttpStatus.PAYLOAD_TOO_LARGE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class RequestTimeoutException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Request Timeout', HttpStatus.REQUEST_TIMEOUT);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ServiceUnavailableException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Service Unavailable', HttpStatus.SERVICE_UNAVAILABLE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class TooManyRequestsException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Too Many Requests', HttpStatus.TOO_MANY_REQUESTS);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class UnauthorizedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unauthorized', HttpStatus.UNAUTHORIZED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class UnprocessableEntityException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unprocessable Entity', HttpStatus.UNPROCESSABLE_ENTITY);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class UnsupportedMediaTypeException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unsupported Media Type', HttpStatus.UNSUPPORTED_MEDIA_TYPE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ValidationException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Validation Failed', HttpStatus.BAD_REQUEST);\n }\n}\n","import { type ArgumentsHost, Catch, type ExceptionFilter, HttpException, HttpStatus, Logger } from '@nestjs/common';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport type { ApiErrorResponse, FieldError } from '../types/error-response.types';\n\ninterface ProblemExceptionResponse {\n type?: string;\n label?: string;\n detail?: string;\n errors?: FieldError[];\n}\n\ninterface ValidationExceptionResponse {\n message: Array<string | { property: string; constraints: Record<string, string> }>;\n error?: string;\n}\n\ninterface StandardExceptionResponse {\n message: string | string[];\n error?: string;\n}\n\ntype ExceptionResponseObject = ProblemExceptionResponse | ValidationExceptionResponse | StandardExceptionResponse;\n\n// Converts an HTTP status code to its title string (e.g., 400 → \"Bad Request\")\nexport function getHttpStatusTitle(status: number): string {\n // Find the enum key for the given status code\n const enumKey = Object.entries(HttpStatus).find(([key, value]) => value === status && Number.isNaN(Number(key)))?.[0];\n\n if (!enumKey) {\n return 'Error';\n }\n\n // Convert enum key to title case (e.g., BAD_REQUEST -> Bad Request)\n return enumKey\n .split('_')\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(' ');\n}\n\n@Catch()\nexport class HttpExceptionFilter implements ExceptionFilter {\n private readonly logger = new Logger(HttpExceptionFilter.name);\n\n catch(exception: unknown, host: ArgumentsHost) {\n const ctx = host.switchToHttp();\n const response = ctx.getResponse<FastifyReply>();\n const request = ctx.getRequest<FastifyRequest>();\n\n let status = HttpStatus.INTERNAL_SERVER_ERROR;\n let type = 'about:blank';\n let label: string | undefined;\n let detail = 'Internal server error';\n let errors: FieldError[] = [];\n\n if (exception instanceof HttpException) {\n status = exception.getStatus();\n const exceptionResponse = exception.getResponse();\n\n if (typeof exceptionResponse === 'object' && exceptionResponse !== null) {\n const responseObj = exceptionResponse as ExceptionResponseObject;\n\n // Handle custom HttpProblemException from @vritti/api-sdk\n if ('type' in responseObj || 'label' in responseObj || 'errors' in responseObj) {\n const problemResponse = responseObj as ProblemExceptionResponse;\n type = problemResponse.type ?? 'about:blank';\n label = problemResponse.label;\n detail = problemResponse.detail ?? exception.message ?? getHttpStatusTitle(status);\n errors = problemResponse.errors ?? [];\n }\n // Handle class-validator DTO validation errors\n else if ('message' in responseObj && Array.isArray(responseObj.message)) {\n errors = responseObj.message.map((msg) => {\n if (typeof msg === 'object' && 'property' in msg && 'constraints' in msg) {\n const constraintValues = Object.values(msg.constraints);\n return {\n field: msg.property,\n message: constraintValues[0] ?? 'Validation failed',\n };\n }\n // Non-field-specific validation messages are ignored\n // They should be handled as detail at the response level\n return null;\n }).filter((error): error is FieldError => error !== null);\n detail = 'Validation failed';\n }\n // Handle standard NestJS exceptions\n else if ('message' in responseObj) {\n const message = responseObj.message;\n detail = Array.isArray(message) ? message.join(', ') : message;\n }\n } else if (typeof exceptionResponse === 'string') {\n detail = exceptionResponse;\n }\n } else {\n // Unknown errors\n const errorMessage = exception instanceof Error ? exception.message : 'Unknown error';\n const stack = exception instanceof Error ? exception.stack : undefined;\n this.logger.error(`Unexpected error: ${errorMessage}`, stack);\n detail = 'An unexpected error occurred';\n }\n\n const problemDetails: ApiErrorResponse = {\n type,\n title: getHttpStatusTitle(status),\n status,\n ...(label && { label }),\n detail,\n instance: request.url,\n errors,\n };\n\n response\n .header('Content-Type', 'application/problem+json')\n .status(status)\n .send(problemDetails);\n }\n}\n","import { type CallHandler, type ExecutionContext, Injectable, type NestInterceptor, Optional } from '@nestjs/common';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport type { Observable } from 'rxjs';\nimport { catchError, tap } from 'rxjs/operators';\nimport { LoggerService } from '../services/logger.service';\nimport type { HttpLoggerOptions, LogMetadata } from '../types';\nimport { getCorrelationContext } from '../utils';\n\n@Injectable()\nexport class HttpLoggerInterceptor implements NestInterceptor {\n private readonly enableRequestLog: boolean;\n private readonly enableResponseLog: boolean;\n private readonly slowRequestThreshold: number;\n\n constructor(\n private readonly logger: LoggerService,\n @Optional() options?: HttpLoggerOptions,\n ) {\n this.enableRequestLog = options?.enableRequestLog ?? true;\n this.enableResponseLog = options?.enableResponseLog ?? true;\n this.slowRequestThreshold = options?.slowRequestThreshold ?? 3000; // 3 seconds\n }\n\n intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {\n if (context.getType() !== 'http') {\n return next.handle();\n }\n\n const httpContext = context.switchToHttp();\n const request = httpContext.getRequest<FastifyRequest>();\n const response = httpContext.getResponse<FastifyReply>();\n\n const startTime = Date.now();\n\n // Log incoming request\n if (this.enableRequestLog) {\n this.logRequest(request);\n }\n\n // Process request and log response/errors\n return next.handle().pipe(\n tap(() => {\n if (this.enableResponseLog) {\n const duration = Date.now() - startTime;\n this.logResponse(request, response, duration);\n }\n }),\n catchError((error) => {\n const duration = Date.now() - startTime;\n this.logError(request, response, duration, error);\n throw error;\n }),\n );\n }\n\n private logRequest(request: FastifyRequest): void {\n try {\n const correlationContext = getCorrelationContext();\n const metadata: LogMetadata = {\n type: 'http_request',\n method: request.method,\n url: request.url,\n correlationId: correlationContext?.correlationId,\n ip: request.ip,\n userAgent: request.headers['user-agent'],\n };\n\n this.logger.logWithMetadata('log', `Incoming ${request.method} ${request.url}`, metadata);\n } catch (error) {\n this.logger.error('Failed to log HTTP request', (error as Error).stack);\n }\n }\n\n private logResponse(request: FastifyRequest, response: FastifyReply, duration: number): void {\n try {\n const correlationContext = getCorrelationContext();\n const statusCode = response.statusCode;\n\n // Determine log level based on status code\n const logLevel = statusCode >= 500 ? 'error' : statusCode >= 400 ? 'warn' : 'log';\n\n const metadata: LogMetadata = {\n type: 'http_response',\n method: request.method,\n url: request.url,\n statusCode,\n duration,\n correlationId: correlationContext?.correlationId,\n };\n\n // Flag slow requests\n if (duration > this.slowRequestThreshold) {\n metadata.slowRequest = true;\n }\n\n const message = metadata.slowRequest\n ? `SLOW ${request.method} ${request.url} ${statusCode} - ${duration}ms`\n : `${request.method} ${request.url} ${statusCode} - ${duration}ms`;\n\n this.logger.logWithMetadata(logLevel, message, metadata);\n } catch (error) {\n this.logger.error('Failed to log HTTP response', (error as Error).stack);\n }\n }\n\n private logError(request: FastifyRequest, response: FastifyReply, duration: number, error: unknown): void {\n try {\n const correlationContext = getCorrelationContext();\n const statusCode = response.statusCode || 500;\n const err = error as { name?: string; message?: string; stack?: string; response?: unknown };\n\n const metadata: LogMetadata = {\n type: 'http_error',\n method: request.method,\n url: request.url,\n statusCode,\n duration,\n correlationId: correlationContext?.correlationId,\n errorName: err.name || 'Error',\n errorMessage: err.message || 'Unknown error',\n };\n\n if (err.stack) {\n metadata.trace = err.stack;\n }\n\n if (err.response) {\n metadata.errorDetails = err.response;\n }\n\n const message = `ERROR ${request.method} ${request.url} ${statusCode} - ${err.message || 'Unknown error'}`;\n this.logger.logWithMetadata('error', message, metadata);\n } catch (loggingError) {\n this.logger.error('Failed to log HTTP error', (loggingError as Error).stack);\n }\n }\n}\n","import { Injectable, type Logger, type LoggerService as NestLoggerService, Optional } from '@nestjs/common';\nimport { createLogger, format, type LoggerOptions, transports, type Logger as WinstonLogger } from 'winston';\nimport DailyRotateFile from 'winston-daily-rotate-file';\nimport type { LoggerModuleOptions, LogLevel, LogMetadata } from '../types';\nimport { getCorrelationContext } from '../utils';\n\nexport type LogMessage = string | Error | object;\n\n@Injectable()\nexport class LoggerService implements NestLoggerService {\n private readonly activeLogger: NestLoggerService | WinstonLogger;\n private readonly options: LoggerModuleOptions;\n private context?: string;\n\n constructor(\n @Optional() options: LoggerModuleOptions = {},\n @Optional() private readonly defaultLogger?: Logger,\n ) {\n this.options = options;\n const provider = options.provider ?? 'winston';\n\n if (provider === 'default') {\n if (!this.defaultLogger) {\n throw new Error('LoggerService: Default Logger not provided');\n }\n this.activeLogger = this.defaultLogger;\n } else {\n this.activeLogger = this.createWinstonLogger(options);\n }\n }\n\n // Creates a Winston logger instance with inline transports and format configuration\n private createWinstonLogger(opts: LoggerModuleOptions): WinstonLogger {\n const level = opts.level ?? 'debug';\n const logFormat = opts.format ?? 'text';\n\n // Base formatters\n // Winston automatically merges metadata into the info object, so all properties\n // (context, correlationId, etc.) are already at the top level\n const baseFormatters = [format.timestamp({ format: 'YYYY-MM-DDTHH:mm:ss.SSSZ' }), format.errors({ stack: true })];\n\n // Console transport\n const consoleTransport =\n logFormat === 'json'\n ? new transports.Console({\n level,\n format: format.combine(...baseFormatters, format.json()),\n })\n : new transports.Console({\n level,\n format: format.combine(\n ...baseFormatters,\n format.printf((info) => {\n const { timestamp, level, message, context, correlationId, trace } = info;\n const parts = [\n timestamp,\n level.toUpperCase().padEnd(7),\n correlationId ? `[${correlationId.toString().slice(-6)}]` : '',\n context ? `[${context}]` : '',\n message,\n ].filter(Boolean);\n let output = parts.join(' ');\n\n // Append stack trace on new line if present\n if (trace) {\n output += `\\n${trace}`;\n }\n\n return output;\n }),\n format.colorize({ all: true }),\n ),\n });\n\n const winstonTransports: (InstanceType<typeof transports.Console> | DailyRotateFile)[] = [consoleTransport];\n\n // File transports\n if (opts.enableFileLogger) {\n const filePath = opts.filePath ?? './logs';\n const maxFiles = opts.maxFiles ?? '14d';\n\n winstonTransports.push(\n new DailyRotateFile({\n level,\n filename: `${filePath}/%DATE%-combined.log`,\n datePattern: 'YYYY-MM-DD',\n maxSize: '20m',\n maxFiles,\n format: format.combine(format.timestamp(), format.json()),\n }),\n new DailyRotateFile({\n level: 'error',\n filename: `${filePath}/%DATE%-error.log`,\n datePattern: 'YYYY-MM-DD',\n maxSize: '20m',\n maxFiles,\n format: format.combine(format.timestamp(), format.json()),\n }),\n );\n }\n\n const config: LoggerOptions = {\n level,\n transports: winstonTransports,\n exitOnError: false,\n };\n\n if (opts.defaultMeta || opts.appName) {\n config.defaultMeta = {\n ...opts.defaultMeta,\n appName: opts.appName,\n environment: opts.environment,\n };\n }\n\n return createLogger(config);\n }\n\n // NestJS LoggerService interface methods\n log(message: LogMessage, context?: string): void {\n this._log('log', message, context);\n }\n\n error(message: LogMessage, trace?: string, context?: string): void {\n this._log('error', message, context, trace);\n }\n\n warn(message: LogMessage, context?: string): void {\n this._log('warn', message, context);\n }\n\n debug(message: LogMessage, context?: string): void {\n this._log('debug', message, context);\n }\n\n verbose(message: LogMessage, context?: string): void {\n this._log('verbose', message, context);\n }\n\n setContext(context: string): void {\n this.context = context;\n }\n\n // Dispatches a log entry to either the Winston or NestJS logger implementation\n private _log(level: LogLevel, message: LogMessage, context?: string, trace?: string): void {\n const ctx = context ?? this.context;\n\n // Check if Winston logger by duck typing\n if ('format' in this.activeLogger && 'transports' in this.activeLogger) {\n // Winston logger path\n const winstonLogger = this.activeLogger as WinstonLogger;\n const winstonLevel = level === 'log' ? 'info' : level;\n const formattedMessage = this.formatMessage(message);\n const metadata = this.enrichMetadata({}, ctx, trace);\n // Winston merges all properties into the info object when using object syntax\n winstonLogger.log({ level: winstonLevel, message: formattedMessage, ...metadata });\n } else {\n // NestJS logger path\n const nestLogger = this.activeLogger as Logger;\n if (level === 'error' && trace) {\n ctx ? nestLogger.error(message, trace, ctx) : nestLogger.error(message, trace);\n } else if (level === 'log') {\n ctx ? nestLogger.log(message, ctx) : nestLogger.log(message);\n } else if (level === 'warn') {\n ctx ? nestLogger.warn(message, ctx) : nestLogger.warn(message);\n } else if (level === 'debug' && nestLogger.debug) {\n ctx ? nestLogger.debug(message, ctx) : nestLogger.debug(message);\n } else if (level === 'verbose' && nestLogger.verbose) {\n ctx ? nestLogger.verbose(message, ctx) : nestLogger.verbose(message);\n }\n }\n }\n\n // Logs a message with custom metadata fields (Winston only)\n logWithMetadata(level: LogLevel, message: LogMessage, metadata?: LogMetadata, context?: string): void {\n const ctx = context ?? this.context;\n\n // Check if Winston logger by duck typing\n if ('format' in this.activeLogger && 'transports' in this.activeLogger) {\n const winstonLogger = this.activeLogger as WinstonLogger;\n const winstonLevel = level === 'log' ? 'info' : level;\n // const enriched = this.enrichMetadata(metadata, ctx);\n // Winston merges all properties into the info object when using object syntax\n winstonLogger.log({ level: winstonLevel, message: this.formatMessage(message), ...metadata });\n } else {\n // Fallback for default logger\n const messageWithMeta = metadata ? `${message} ${JSON.stringify(metadata)}` : message;\n this[level](messageWithMeta, ctx);\n }\n }\n\n private formatMessage(message: LogMessage): string {\n if (message instanceof Error) return message.message;\n if (typeof message === 'object' && message !== null) {\n try {\n return JSON.stringify(message);\n } catch {\n return String(message);\n }\n }\n return String(message);\n }\n\n // Enriches metadata with correlation context from AsyncLocalStorage\n private enrichMetadata(metadata: LogMetadata = {}, context?: string, trace?: string): LogMetadata {\n const enriched: LogMetadata = { ...metadata };\n\n if (context) enriched.context = context;\n\n const correlationContext = getCorrelationContext();\n if (correlationContext) {\n if (correlationContext.correlationId) enriched.correlationId = correlationContext.correlationId;\n for (const [key, value] of Object.entries(correlationContext)) {\n if (key !== 'correlationId') {\n enriched[key] = value;\n }\n }\n }\n\n if (trace) enriched.trace = trace;\n\n return enriched;\n }\n\n child(context: string): LoggerService {\n const childLogger = new LoggerService(this.options, this.defaultLogger);\n childLogger.setContext(context);\n return childLogger;\n }\n}\n","import { AsyncLocalStorage } from 'node:async_hooks';\nimport { randomUUID } from 'node:crypto';\nimport type { FastifyReply } from 'fastify';\nimport type { CorrelationContext } from '../types';\n\n// ============================================================================\n// Async Context Management (AsyncLocalStorage)\n// ============================================================================\n\nexport const correlationStorage = new AsyncLocalStorage<CorrelationContext>();\n\n// Returns the current correlation context from AsyncLocalStorage\nexport function getCorrelationContext(): CorrelationContext | undefined {\n return correlationStorage.getStore();\n}\n\n// Runs a callback within the given correlation context\nexport function runWithCorrelationContext<T>(context: CorrelationContext, callback: () => T): T {\n return correlationStorage.run(context, callback);\n}\n\n// Updates the current correlation context with new values\nexport function updateCorrelationContext(updates: Partial<CorrelationContext>): void {\n const context = correlationStorage.getStore();\n if (context) {\n Object.assign(context, updates);\n }\n}\n\n// ============================================================================\n// Correlation ID Management\n// ============================================================================\n\nexport const DEFAULT_CORRELATION_HEADER = 'x-correlation-id';\n\n// Generates a new UUID v4 correlation ID for the current request\nexport function generateCorrelationId(): string {\n return randomUUID();\n}\n\n// Adds the correlation ID to Fastify response headers\nexport function addCorrelationIdToResponse(\n reply: FastifyReply,\n correlationId: string,\n headerName: string = DEFAULT_CORRELATION_HEADER,\n): void {\n if (typeof reply.header === 'function') {\n reply.header(headerName, correlationId);\n } else if (reply.raw && typeof reply.raw.setHeader === 'function') {\n reply.raw.setHeader(headerName, correlationId);\n }\n}\n","import {\n type DynamicModule,\n Global,\n type InjectionToken,\n Logger,\n type MiddlewareConsumer,\n Module,\n type NestModule,\n type Provider,\n} from '@nestjs/common';\nimport { HttpLoggerInterceptor } from './interceptors/http-logger.interceptor';\nimport { CorrelationIdMiddleware } from './middleware/correlation-id.middleware';\nimport { LoggerService } from './services/logger.service';\nimport type { LoggerModuleAsyncOptions, LoggerModuleOptions, LoggerOptionsFactory } from './types';\n\n// ============================================================================\n// Constants (inline from constants.ts)\n// ============================================================================\n\nexport const LOGGER_MODULE_OPTIONS = Symbol('LOGGER_MODULE_OPTIONS');\n\nconst DEFAULT_LOGGER_OPTIONS = {\n provider: 'winston' as const,\n enableCorrelationId: true,\n enableHttpLogger: true,\n filePath: './logs',\n maxFiles: '14d',\n} as const;\n\n// ============================================================================\n// Environment Presets (NEW - replaces process.env auto-detection)\n// ============================================================================\n\nconst ENVIRONMENT_PRESETS: Record<string, Partial<LoggerModuleOptions>> = {\n development: {\n provider: 'winston',\n level: 'debug',\n format: 'text',\n enableFileLogger: false,\n enableCorrelationId: true,\n enableHttpLogger: true,\n httpLogger: {\n enableRequestLog: true,\n enableResponseLog: true,\n slowRequestThreshold: 1000, // 1 second - lower threshold for dev\n },\n },\n\n staging: {\n provider: 'winston',\n level: 'log',\n format: 'json',\n enableFileLogger: true,\n enableCorrelationId: true,\n enableHttpLogger: true,\n httpLogger: {\n enableRequestLog: true,\n enableResponseLog: true,\n slowRequestThreshold: 3000, // 3 seconds\n },\n },\n\n production: {\n provider: 'winston',\n level: 'warn',\n format: 'json',\n enableFileLogger: true,\n enableCorrelationId: true,\n enableHttpLogger: true,\n httpLogger: {\n enableRequestLog: false, // Reduce noise in production\n enableResponseLog: true,\n slowRequestThreshold: 5000, // 5 seconds - higher threshold for prod\n },\n },\n\n test: {\n provider: 'winston',\n level: 'error',\n format: 'json',\n enableFileLogger: false,\n enableCorrelationId: false,\n enableHttpLogger: false,\n },\n} as const;\n\n// ============================================================================\n// Configuration Merging (refactored to use presets instead of process.env)\n// ============================================================================\n\n// Merges user-provided options with default and environment preset values\nfunction mergeWithDefaults(options: LoggerModuleOptions = {}): LoggerModuleOptions {\n // Select preset based on explicit environment option (defaults to development)\n const preset = options.environment\n ? (ENVIRONMENT_PRESETS[options.environment] ?? ENVIRONMENT_PRESETS.development)\n : ENVIRONMENT_PRESETS.development;\n\n // Filter out undefined values from user options to avoid overriding preset defaults\n const filteredOptions = Object.fromEntries(Object.entries(options).filter(([_, value]) => value !== undefined));\n\n // Handle nested httpLogger object - merge with preset httpLogger if both exist\n if (filteredOptions.httpLogger && preset?.httpLogger) {\n filteredOptions.httpLogger = {\n ...preset.httpLogger,\n ...Object.fromEntries(Object.entries(filteredOptions.httpLogger).filter(([_, value]) => value !== undefined)),\n };\n }\n\n // Merge: base defaults < preset < user options (with undefined values removed)\n const merged = {\n ...DEFAULT_LOGGER_OPTIONS,\n ...preset,\n ...filteredOptions,\n };\n\n return merged;\n}\n\n// ============================================================================\n// Provider Factories (inline from logging.providers.ts)\n// ============================================================================\n\n// Creates the default NestJS Logger provider with optional log level configuration\nfunction createDefaultLoggerProvider(options: LoggerModuleOptions): Provider {\n return {\n provide: Logger,\n useFactory: () => {\n const logger = new Logger();\n\n // Set log levels if specified and method exists\n if (options.level) {\n const levels = getLevelsUpTo(options.level);\n (logger as { setLogLevels?: (levels: NestLogLevel[]) => void }).setLogLevels?.(levels);\n }\n\n return logger;\n },\n };\n}\n\n// Builds all logger providers for the module based on merged configuration\nfunction createLoggerProviders(options: LoggerModuleOptions = {}): Provider[] {\n // Merge user options with preset defaults\n const mergedOptions = mergeWithDefaults(options);\n\n // Base providers (always included)\n const providers: Provider[] = [\n // Options provider\n {\n provide: LOGGER_MODULE_OPTIONS,\n useValue: mergedOptions,\n },\n ];\n\n // Default logger provider (only if using default provider)\n if (mergedOptions.provider === 'default') {\n providers.push(createDefaultLoggerProvider(mergedOptions));\n }\n\n // Unified LoggerService facade (always included)\n providers.push({\n provide: LoggerService,\n useFactory: (opts: LoggerModuleOptions, defaultLogger?: Logger) => {\n return new LoggerService(opts, defaultLogger);\n },\n inject: [LOGGER_MODULE_OPTIONS, { token: Logger, optional: true }],\n });\n\n // Correlation ID middleware\n providers.push({\n provide: CorrelationIdMiddleware,\n useFactory: () => {\n return new CorrelationIdMiddleware({\n includeInResponse: true,\n responseHeader: 'x-correlation-id',\n });\n },\n });\n\n // HTTP logger interceptor\n providers.push({\n provide: HttpLoggerInterceptor,\n useFactory: (logger: LoggerService, opts: LoggerModuleOptions) => {\n // Use detailed httpLogger config if provided, otherwise fall back to simple enableHttpLogger\n const httpLoggerOptions = opts.httpLogger ?? {\n enableRequestLog: opts.enableHttpLogger,\n enableResponseLog: opts.enableHttpLogger,\n };\n return new HttpLoggerInterceptor(logger, httpLoggerOptions);\n },\n inject: [LoggerService, LOGGER_MODULE_OPTIONS],\n });\n\n return providers;\n}\n\ntype NestLogLevel = 'error' | 'warn' | 'log' | 'debug' | 'verbose';\n\n// Returns all NestJS log levels up to and including the specified level\nfunction getLevelsUpTo(level: string): NestLogLevel[] {\n const allLevels: NestLogLevel[] = ['error', 'warn', 'log', 'debug', 'verbose'];\n\n // Check if level is a valid NestLogLevel\n const isValidLevel = (l: string): l is NestLogLevel => allLevels.includes(l as NestLogLevel);\n\n if (!isValidLevel(level)) {\n return ['error', 'warn', 'log'];\n }\n\n const levelIndex = allLevels.indexOf(level);\n return allLevels.slice(0, levelIndex + 1);\n}\n\n// ============================================================================\n// Logger Module\n// ============================================================================\n\n@Global()\n@Module({})\nexport class LoggerModule implements NestModule {\n // Configures the logger module with static options and environment preset\n static forRoot(options: LoggerModuleOptions = {}): DynamicModule {\n const providers = createLoggerProviders(options);\n\n return {\n module: LoggerModule,\n providers,\n exports: [LoggerService, CorrelationIdMiddleware, HttpLoggerInterceptor, LOGGER_MODULE_OPTIONS],\n };\n }\n\n // Configures the logger module with async options (useFactory, useClass, useExisting)\n static forRootAsync(options: LoggerModuleAsyncOptions): DynamicModule {\n const asyncProviders = LoggerModule.createAsyncProviders(options);\n\n return {\n module: LoggerModule,\n imports: options.imports || [],\n providers: [\n ...asyncProviders,\n // Default logger provider\n {\n provide: Logger,\n useFactory: (opts: LoggerModuleOptions) => {\n if (opts.provider === 'default') {\n const logger = new Logger();\n if (opts.level) {\n const levels = getLevelsUpTo(opts.level);\n (logger as { setLogLevels?: (levels: NestLogLevel[]) => void }).setLogLevels?.(levels);\n }\n return logger;\n }\n return null;\n },\n inject: [LOGGER_MODULE_OPTIONS],\n },\n // Unified logger service\n {\n provide: LoggerService,\n useFactory: (opts: LoggerModuleOptions, defaultLogger?: Logger) => {\n return new LoggerService(opts, defaultLogger);\n },\n inject: [LOGGER_MODULE_OPTIONS, { token: Logger, optional: true }],\n },\n // Correlation ID middleware\n {\n provide: CorrelationIdMiddleware,\n useFactory: () => {\n return new CorrelationIdMiddleware({\n includeInResponse: true,\n responseHeader: 'x-correlation-id',\n });\n },\n },\n // HTTP logger interceptor\n {\n provide: HttpLoggerInterceptor,\n useFactory: (logger: LoggerService, opts: LoggerModuleOptions) => {\n // Use detailed httpLogger config if provided, otherwise fall back to simple enableHttpLogger\n const httpLoggerOptions = opts.httpLogger ?? {\n enableRequestLog: opts.enableHttpLogger,\n enableResponseLog: opts.enableHttpLogger,\n };\n return new HttpLoggerInterceptor(logger, httpLoggerOptions);\n },\n inject: [LoggerService, LOGGER_MODULE_OPTIONS],\n },\n ],\n exports: [LoggerService, CorrelationIdMiddleware, HttpLoggerInterceptor, LOGGER_MODULE_OPTIONS],\n };\n }\n\n // Middleware registration is handled globally in main.ts via Fastify hooks\n configure(_consumer: MiddlewareConsumer): void {\n // Middleware is registered globally in main.ts using Fastify's addHook('onRequest')\n // This avoids DI issues with the middleware constructor\n }\n\n // Creates async providers for dynamic module configuration\n private static createAsyncProviders(options: LoggerModuleAsyncOptions): Provider[] {\n if (options.useFactory) {\n return [LoggerModule.createAsyncOptionsProvider(options)];\n }\n\n const providers: Provider[] = [LoggerModule.createAsyncOptionsProvider(options)];\n\n if (options.useClass) {\n providers.push({\n provide: options.useClass,\n useClass: options.useClass,\n });\n }\n\n return providers;\n }\n\n // Creates the DI provider that resolves and merges async logger options\n private static createAsyncOptionsProvider(options: LoggerModuleAsyncOptions): Provider {\n if (options.useFactory) {\n return {\n provide: LOGGER_MODULE_OPTIONS,\n useFactory: async (...args: unknown[]) => {\n const userOptions = await options.useFactory?.(...args);\n return mergeWithDefaults(userOptions);\n },\n inject: (options.inject || []) as InjectionToken[],\n };\n }\n\n if (options.useClass) {\n return {\n provide: LOGGER_MODULE_OPTIONS,\n useFactory: async (optionsFactory: LoggerOptionsFactory) => {\n const userOptions = await optionsFactory.createLoggerOptions();\n return mergeWithDefaults(userOptions);\n },\n inject: [options.useClass],\n };\n }\n\n if (options.useExisting) {\n return {\n provide: LOGGER_MODULE_OPTIONS,\n useFactory: async (optionsFactory: LoggerOptionsFactory) => {\n const userOptions = await optionsFactory.createLoggerOptions();\n return mergeWithDefaults(userOptions);\n },\n inject: [options.useExisting],\n };\n }\n\n throw new Error('LoggerModule.forRootAsync() requires one of: useFactory, useClass, or useExisting');\n }\n}\n","import { Injectable, type NestMiddleware } from '@nestjs/common';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport {\n addCorrelationIdToResponse,\n correlationStorage,\n DEFAULT_CORRELATION_HEADER,\n generateCorrelationId,\n runWithCorrelationContext,\n} from '../utils';\n\nexport interface CorrelationIdMiddlewareOptions {\n includeInResponse?: boolean;\n responseHeader?: string;\n}\n\n@Injectable()\nexport class CorrelationIdMiddleware implements NestMiddleware {\n private readonly includeInResponse: boolean;\n private readonly responseHeader: string;\n\n constructor(options: CorrelationIdMiddlewareOptions = {}) {\n this.includeInResponse = options.includeInResponse ?? true;\n this.responseHeader = options.responseHeader ?? DEFAULT_CORRELATION_HEADER;\n }\n\n // Generates and stores a correlation ID for the incoming request\n use(_req: FastifyRequest, reply: FastifyReply, next: () => void): void {\n // Generate new correlation ID for this request\n const correlationId = generateCorrelationId();\n\n // Add to response headers if enabled\n if (this.includeInResponse) {\n addCorrelationIdToResponse(reply, correlationId, this.responseHeader);\n }\n\n // Run the rest of the request in AsyncLocalStorage context\n runWithCorrelationContext({ correlationId }, () => {\n next();\n });\n }\n\n // Fastify onRequest hook that initializes correlation context in AsyncLocalStorage\n async onRequest(_req: FastifyRequest, reply: FastifyReply): Promise<void> {\n // Generate new correlation ID for this request\n const correlationId = generateCorrelationId();\n\n // Add to response headers if enabled\n if (this.includeInResponse) {\n addCorrelationIdToResponse(reply, correlationId, this.responseHeader);\n }\n\n // Store in AsyncLocalStorage for the request lifecycle\n // Note: We don't wrap in runWithCorrelationContext here because\n // Fastify's async context tracking handles it automatically\n const store = correlationStorage.getStore();\n if (!store) {\n // Initialize new store\n correlationStorage.enterWith({ correlationId });\n }\n }\n}\n","import { Module } from '@nestjs/common';\nimport { AppController } from './controllers/app.controller';\nimport { CsrfController } from './controllers/csrf.controller';\nimport { AppService } from './services/app.service';\n\n@Module({\n controllers: [AppController, CsrfController],\n providers: [AppService],\n})\nexport class RootModule {}\n","import { Controller, Get } from '@nestjs/common';\nimport { ApiTags } from '@nestjs/swagger';\nimport { Public } from '../../auth/decorators/public.decorator';\nimport { ApiHealthCheck } from '../docs/app.docs';\nimport { AppService } from '../services/app.service';\n\n@ApiTags('Health')\n@Controller()\nexport class AppController {\n constructor(private readonly appService: AppService) {}\n\n // Returns a welcome message indicating the API is running\n @Get()\n @Public()\n @ApiHealthCheck()\n getHello(): string {\n return this.appService.getHello();\n }\n}\n","import { applyDecorators } from '@nestjs/common';\nimport { ApiOperation, ApiResponse } from '@nestjs/swagger';\n\nexport function ApiHealthCheck() {\n return applyDecorators(\n ApiOperation({ summary: 'Health check endpoint' }),\n ApiResponse({\n status: 200,\n description: 'Returns a welcome message indicating the API is running',\n type: String,\n }),\n );\n}\n","import { Injectable } from '@nestjs/common';\n\n@Injectable()\nexport class AppService {\n // Returns the API welcome message\n getHello(): string {\n return `Hello World!`;\n }\n}\n","import { Controller, Get, HttpCode, HttpStatus, Res } from '@nestjs/common';\nimport { ApiTags } from '@nestjs/swagger';\nimport type { FastifyReply } from 'fastify';\nimport { Public } from '../../auth/decorators/public.decorator';\nimport { ApiGetCsrfToken } from '../docs/csrf.docs';\n\n// Type augmentation for @fastify/csrf-protection — added by the consuming server at runtime\ntype FastifyReplyWithCsrf = FastifyReply & { generateCsrf(): string };\n\n@ApiTags('CSRF')\n@Controller('csrf')\nexport class CsrfController {\n // Generates a CSRF token via Fastify's csrf-protection plugin\n @Get('token')\n @Public()\n @HttpCode(HttpStatus.OK)\n @ApiGetCsrfToken()\n getToken(@Res({ passthrough: true }) reply: FastifyReply): { csrfToken: string } {\n const csrfToken = (reply as FastifyReplyWithCsrf).generateCsrf();\n return { csrfToken };\n }\n}\n","import { applyDecorators } from '@nestjs/common';\nimport { ApiOperation, ApiResponse } from '@nestjs/swagger';\n\nexport function ApiGetCsrfToken() {\n return applyDecorators(\n ApiOperation({\n summary: 'Get CSRF token',\n description:\n 'Generates and returns a CSRF token that must be included in all state-changing requests (POST, PUT, PATCH, DELETE). The token should be sent in the X-CSRF-Token header.',\n }),\n ApiResponse({\n status: 200,\n description: 'CSRF token generated successfully',\n schema: {\n type: 'object',\n properties: {\n csrfToken: {\n type: 'string',\n description: 'The CSRF token to use in subsequent requests',\n example: 'abc123xyz789',\n },\n },\n required: ['csrfToken'],\n },\n }),\n );\n}\n","const CALLING_CODE_TO_COUNTRY: Record<string, string> = {\n // 3-digit codes\n '355': 'AL', '213': 'DZ', '376': 'AD', '244': 'AO', '672': 'AQ',\n '374': 'AM', '297': 'AW', '994': 'AZ', '973': 'BH', '880': 'BD',\n '375': 'BY', '501': 'BZ', '229': 'BJ', '975': 'BT', '591': 'BO',\n '387': 'BA', '267': 'BW', '673': 'BN', '359': 'BG', '226': 'BF',\n '257': 'BI', '855': 'KH', '237': 'CM', '238': 'CV', '236': 'CF',\n '235': 'TD', '269': 'KM', '242': 'CG', '243': 'CD', '506': 'CR',\n '385': 'HR', '357': 'CY', '420': 'CZ', '253': 'DJ', '593': 'EC',\n '503': 'SV', '240': 'GQ', '291': 'ER', '372': 'EE', '251': 'ET',\n '679': 'FJ', '358': 'FI', '241': 'GA', '220': 'GM', '995': 'GE',\n '233': 'GH', '350': 'GI', '299': 'GL', '502': 'GT', '224': 'GN',\n '245': 'GW', '592': 'GY', '509': 'HT', '504': 'HN', '354': 'IS',\n '964': 'IQ', '353': 'IE', '972': 'IL', '225': 'CI', '962': 'JO',\n '254': 'KE', '686': 'KI', '965': 'KW', '996': 'KG', '856': 'LA',\n '371': 'LV', '961': 'LB', '266': 'LS', '231': 'LR', '218': 'LY',\n '423': 'LI', '370': 'LT', '352': 'LU', '389': 'MK', '261': 'MG',\n '265': 'MW', '960': 'MV', '223': 'ML', '356': 'MT', '692': 'MH',\n '222': 'MR', '230': 'MU', '262': 'YT', '691': 'FM', '373': 'MD',\n '377': 'MC', '976': 'MN', '382': 'ME', '258': 'MZ', '264': 'NA',\n '674': 'NR', '977': 'NP', '505': 'NI', '227': 'NE', '234': 'NG',\n '683': 'NU', '968': 'OM', '680': 'PW', '970': 'PS', '507': 'PA',\n '675': 'PG', '595': 'PY', '351': 'PT', '974': 'QA', '250': 'RW',\n '685': 'WS', '378': 'SM', '239': 'ST', '966': 'SA', '221': 'SN',\n '381': 'RS', '248': 'SC', '232': 'SL', '421': 'SK', '386': 'SI',\n '677': 'SB', '252': 'SO', '211': 'SS', '249': 'SD', '597': 'SR',\n '268': 'SZ', '963': 'SY', '992': 'TJ', '255': 'TZ', '228': 'TG',\n '676': 'TO', '216': 'TN', '993': 'TM', '688': 'TV', '256': 'UG',\n '380': 'UA', '971': 'AE', '598': 'UY', '998': 'UZ', '678': 'VU',\n '379': 'VA', '967': 'YE', '260': 'ZM', '263': 'ZW',\n\n // 2-digit codes\n '93': 'AF', '54': 'AR', '61': 'AU', '43': 'AT', '32': 'BE',\n '55': 'BR', '56': 'CL', '86': 'CN', '57': 'CO', '53': 'CU',\n '45': 'DK', '20': 'EG', '33': 'FR', '49': 'DE', '30': 'GR',\n '36': 'HU', '91': 'IN', '62': 'ID', '98': 'IR', '39': 'IT',\n '81': 'JP', '82': 'KR', '60': 'MY', '52': 'MX', '31': 'NL',\n '64': 'NZ', '47': 'NO', '92': 'PK', '51': 'PE', '63': 'PH',\n '48': 'PL', '40': 'RO', '65': 'SG', '27': 'ZA', '34': 'ES',\n '94': 'LK', '46': 'SE', '41': 'CH', '66': 'TH', '90': 'TR',\n '44': 'GB', '58': 'VE', '84': 'VN',\n\n // 1-digit codes (shared codes default to most common country)\n '1': 'US', // Also CA, but default to US\n '7': 'RU', // Also KZ, but default to RU\n};\n\n// Extracts ISO 3166-1 alpha-2 country code from an E.164 phone number\nexport function extractCountryFromPhone(phone: string): string | undefined {\n // Remove + prefix if present\n const digits = phone.startsWith('+') ? phone.slice(1) : phone;\n\n // Try matching from longest to shortest prefix (3, 2, 1 digits)\n for (const length of [3, 2, 1]) {\n const prefix = digits.slice(0, length);\n if (CALLING_CODE_TO_COUNTRY[prefix]) {\n return CALLING_CODE_TO_COUNTRY[prefix];\n }\n }\n\n return undefined;\n}\n\n// Normalizes a phone number to E.164 format by ensuring a + prefix\nexport function normalizePhoneNumber(phone: string): string {\n return phone.startsWith('+') ? phone : `+${phone}`;\n}\n"],"mappings":"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","names":["ForbiddenException","InternalServerErrorException","UnauthorizedException","import_common","import_config","import_core","import_jwt","import_common","defaultConfig","cookie","refreshCookieName","refreshCookieMaxAge","refreshCookiePath","refreshCookieSecure","process","env","NODE_ENV","refreshCookieSameSite","refreshCookieDomain","jwt","accessTokenExpiry","refreshTokenExpiry","onboardingTokenExpiry","guard","tenantHeaderName","authHeaderName","tokenPrefix","currentConfig","defineConfig","config","configureApiSdk","userConfig","getConfig","resetConfig","getRefreshCookieOptions","options","httpOnly","secure","sameSite","path","maxAge","domain","getJwtExpiry","access","refresh","onboarding","RequestService","request","getTenantIdentifier","getHeader","key","value","headers","Array","isArray","getAccessToken","authHeader","authorization","type","token","split","getRefreshToken","cookies","config","getConfig","refreshToken","cookie","refreshCookieName","_error","getAllHeaders","scope","Scope","REQUEST","RequestModule","providers","RequestService","exports","import_common","import_config","import_core","import_common","RESET_KEY","Reset","SetMetadata","import_common","SKIP_CSRF_KEY","SkipCsrf","SetMetadata","hashToken","token","createHash","update","digest","verifyTokenHash","expectedHash","computedHash","length","timingSafeEqual","Buffer","from","VrittiAuthGuard","logger","Logger","name","reflector","_configService","jwtService","requestService","canActivate","context","request","switchToHttp","getRequest","reply","getResponse","skipCsrf","getAllAndOverride","SKIP_CSRF_KEY","getHandler","getClass","validateCsrf","isPublic","isOnboarding","isReset","RESET_KEY","isSseEndpoint","get","SSE_METADATA","handleSseAuth","accessToken","getAccessToken","UnauthorizedException","decodedAccessToken","validateAccessToken","tokenType","validateRefreshTokenBinding","sessionType","sessionInfo","userId","sessionId","error","token","verify","jwtError","refreshTokenHash","refreshToken","getRefreshToken","verifyTokenHash","decoded","safeMethods","includes","method","fastifyInstance","server","csrfProtection","ForbiddenException","Promise","resolve","reject","originalSend","send","bind","Error","err","errors","field","message","scope","Scope","REQUEST","import_common","import_config","import_jwt","parseExpiryToMs","expiry","match","Error","value","Number","parseInt","multipliers","s","m","h","d","w","y","jwtConfigFactory","configService","secret","getOrThrow","signOptions","issuer","getTokenExpiry","access","refresh","TokenType","JwtAuthService","logger","Logger","name","tokenExpiry","jwtService","configService","getTokenExpiry","generateAccessToken","userId","sessionId","sessionType","refreshToken","sign","tokenType","TokenType","ACCESS","refreshTokenHash","hashToken","expiresIn","access","generateRefreshToken","REFRESH","refresh","payload","options","verify","token","expectedType","Error","error","getExpiryTime","type","Date","now","parseExpiryToMs","getExpiryInSeconds","Math","floor","AuthConfigModule","forRootAsync","module","imports","ConfigModule","RequestModule","JwtModule","registerAsync","inject","ConfigService","useFactory","config","secret","get","signOptions","algorithm","providers","provide","Reflector","useClass","APP_GUARD","VrittiAuthGuard","JwtAuthService","exports","import_common","AccessToken","createParamDecorator","_data","ctx","request","switchToHttp","getRequest","authHeader","headers","authorization","replace","import_common","Onboarding","SetMetadata","import_common","Public","SetMetadata","import_common","RefreshTokenCookie","createParamDecorator","_data","ctx","request","switchToHttp","getRequest","cookies","config","getConfig","cookie","refreshCookieName","import_common","SessionData","createParamDecorator","_data","ctx","request","switchToHttp","getRequest","sessionInfo","sessionId","Error","userId","sessionType","import_common","UserId","createParamDecorator","_data","ctx","request","switchToHttp","getRequest","sessionInfo","userId","Error","import_common","import_core","DATABASE_MODULE_OPTIONS","Symbol","import_common","PrimaryDatabaseService","logger","Logger","name","pool","db","tenantConfigCache","Map","cacheTTL","options","connectionCacheTTL","onModuleInit","primaryDb","initializeDrizzleClient","databaseUrl","buildPrimaryDbUrl","Pool","connectionString","max","maxConnections","debug","Object","keys","drizzleSchema","join","drizzleRelations","drizzle","client","schema","relations","query","log","error","InternalServerErrorException","Error","host","port","username","password","database","sslMode","url","encodeURIComponent","params","URLSearchParams","set","queryString","toString","maskPassword","replace","getTenantInfo","tenantIdentifier","cached","get","tenants","tenantDatabaseConfigs","result","select","from","leftJoin","eq","id","tenantId","where","or","subdomain","limit","length","warn","row","tenant","config","tenant_database_configs","status","info","type","dbType","schemaName","dbSchema","undefined","databaseName","dbName","databaseHost","dbHost","databasePort","dbPort","databaseUsername","dbUsername","decrypt","databasePassword","dbPassword","databaseSslMode","dbSslMode","connectionPoolSize","cacheInfo","setTimeout","delete","clearTenantCache","clearAllCaches","size","clear","drizzleClient","encrypted","onModuleDestroy","end","import_common","TenantContextService","tenantInfo","setTenant","Error","getTenant","UnauthorizedException","hasTenant","clearTenant","getTenantIdSafe","id","getTenantSubdomainSafe","subdomain","scope","Scope","REQUEST","import_common","import_node_postgres","import_pg","TenantDatabaseService","logger","Logger","name","clients","Map","clientLastUsed","cleanupInterval","options","tenantContext","startConnectionCleaner","drizzleClient","getDbClient","schema","drizzleSchema","tenant","getTenant","cacheKey","buildCacheKey","existing","get","set","Date","now","debug","db","log","connection","createDbClientSync","databaseUrl","buildTenantDbUrl","pool","Pool","connectionString","max","connectionPoolSize","maxConnections","drizzle","client","subdomain","error","InternalServerErrorException","databaseHost","databasePort","databaseName","databaseUsername","databasePassword","databaseSslMode","Error","port","sslMode","connectionUrl","encodeURIComponent","maskPassword","type","interval","connectionCacheTTL","setInterval","cleanupIdleConnections","maxIdle","cleaned","key","lastUsed","entries","end","delete","getPoolStats","activeConnections","size","tenants","Array","from","keys","url","replace","onModuleDestroy","clearInterval","disconnectPromises","map","Promise","all","DatabaseModule","forServer","options","createDynamicModule","forMicroservice","mode","asyncProvider","provide","DATABASE_MODULE_OPTIONS","useFactory","inject","providers","Reflector","useClass","TenantContextService","PrimaryDatabaseService","TenantDatabaseService","module","imports","RequestModule","exports","import_common","Tenant","createParamDecorator","_data","ctx","request","switchToHttp","getRequest","tenantContext","app","get","TenantContextService","Error","getTenant","SelectOptionsQueryDto","search","limit","offset","values","excludeIds","valueKey","labelKey","groupIdKey","description","example","default","Number","import_common","import_drizzle_orm","snakeToCamel","str","replace","_","letter","toUpperCase","PrimaryBaseRepository","logger","tableName","db","database","drizzleClient","model","query","queryKeys","Object","keys","debug","join","error","table","dbTableName","getTableName","Logger","name","create","data","log","results","insert","values","returning","record","Error","findById","id","findFirst","where","findOne","findMany","options","update","idColumn","set","eq","updateMany","result","count","rowCount","delete","deleteMany","select","sql","from","$dynamic","exists","findForSelect","config","parsedValues","split","map","v","trim","filter","Boolean","parsedExcludeIds","excludeIds","tableColumns","valueCol","value","labelCol","label","length","selectCols","groupId","groupIdCol","rows","inArray","row","String","hasMore","groups","selectFields","totalCount","mapWith","Number","conditions","search","push","ilike","notInArray","field","val","entries","column","orderByKey","orderBy","undefined","orderByCol","limit","offset","and","orderClauses","asc","resolvedGroups","groupTable","groupTableColumns","groupIdKey","groupNameKey","groupLabelKey","groupNameCol","groupRows","r","import_common","import_drizzle_orm","TenantBaseRepository","logger","tableName","db","database","drizzleClient","model","query","table","getTableName","Logger","name","debug","create","data","log","results","insert","values","returning","record","Error","findById","id","idColumn","select","from","where","eq","limit","findOne","findMany","options","$dynamic","orderBy","offset","update","set","updateMany","result","count","rowCount","delete","deleteMany","sql","exists","findForSelect","config","parsedValues","split","map","v","trim","filter","Boolean","parsedExcludeIds","excludeIds","tableColumns","valueCol","value","labelCol","label","length","selectCols","groupId","groupIdCol","rows","inArray","row","String","hasMore","groups","selectFields","totalCount","mapWith","Number","conditions","search","push","ilike","notInArray","field","val","Object","entries","column","orderByKey","keys","undefined","orderByCol","and","orderClauses","asc","resolvedGroups","groupTable","groupTableColumns","groupIdKey","groupNameKey","groupLabelKey","groupNameCol","groupRows","r","import_common","import_config","import_common","import_config","EmailService","logger","Logger","name","brevoClient","senderEmail","senderName","configService","apiKey","get","error","Error","BrevoClient","maxRetries","log","sendVerificationEmail","email","otp","expiresAt","displayName","expiryMinutes","Math","ceil","getTime","Date","now","subject","htmlContent","textContent","trim","sendEmail","to","sendPasswordResetEmail","sendEmailChangeNotification","oldEmail","newEmail","revertToken","revertExpiresAt","hoursUntilExpiry","floor","revertLink","sendEmailRevertConfirmation","verifyConnection","transactionalEmails","sendTransacEmail","sender","err","BrevoError","statusCode","emailData","result","debug","messageId","BrevoTimeoutError","message","EmailModule","imports","ConfigModule","providers","EmailService","exports","import_common","import_common","HttpProblemException","HttpException","detailOrOptions","httpStatus","options","detail","type","label","errors","BadGatewayException","HttpProblemException","detailOrOptions","HttpStatus","BAD_GATEWAY","import_common","BadRequestException","HttpProblemException","detailOrOptions","HttpStatus","BAD_REQUEST","import_common","ConflictException","HttpProblemException","detailOrOptions","HttpStatus","CONFLICT","import_common","ForbiddenException","HttpProblemException","detailOrOptions","HttpStatus","FORBIDDEN","import_common","GoneException","HttpProblemException","detailOrOptions","HttpStatus","GONE","import_common","InternalServerErrorException","HttpProblemException","detailOrOptions","HttpStatus","INTERNAL_SERVER_ERROR","import_common","MethodNotAllowedException","HttpProblemException","detailOrOptions","HttpStatus","METHOD_NOT_ALLOWED","import_common","NotAcceptableException","HttpProblemException","detailOrOptions","HttpStatus","NOT_ACCEPTABLE","import_common","NotFoundException","HttpProblemException","detailOrOptions","HttpStatus","NOT_FOUND","import_common","NotImplementedException","HttpProblemException","detailOrOptions","HttpStatus","NOT_IMPLEMENTED","import_common","PayloadTooLargeException","HttpProblemException","detailOrOptions","HttpStatus","PAYLOAD_TOO_LARGE","import_common","RequestTimeoutException","HttpProblemException","detailOrOptions","HttpStatus","REQUEST_TIMEOUT","import_common","ServiceUnavailableException","HttpProblemException","detailOrOptions","HttpStatus","SERVICE_UNAVAILABLE","import_common","TooManyRequestsException","HttpProblemException","detailOrOptions","HttpStatus","TOO_MANY_REQUESTS","import_common","UnauthorizedException","HttpProblemException","detailOrOptions","HttpStatus","UNAUTHORIZED","import_common","UnprocessableEntityException","HttpProblemException","detailOrOptions","HttpStatus","UNPROCESSABLE_ENTITY","import_common","UnsupportedMediaTypeException","HttpProblemException","detailOrOptions","HttpStatus","UNSUPPORTED_MEDIA_TYPE","import_common","ValidationException","HttpProblemException","detailOrOptions","HttpStatus","BAD_REQUEST","import_common","getHttpStatusTitle","status","enumKey","Object","entries","HttpStatus","find","key","value","Number","isNaN","split","map","word","charAt","toUpperCase","slice","toLowerCase","join","HttpExceptionFilter","logger","Logger","name","catch","exception","host","ctx","switchToHttp","response","getResponse","request","getRequest","INTERNAL_SERVER_ERROR","type","label","detail","errors","HttpException","getStatus","exceptionResponse","responseObj","problemResponse","message","Array","isArray","msg","constraintValues","values","constraints","field","property","filter","error","errorMessage","Error","stack","undefined","problemDetails","title","instance","url","header","send","import_common","import_common","correlationStorage","AsyncLocalStorage","getCorrelationContext","getStore","runWithCorrelationContext","context","callback","run","updateCorrelationContext","updates","Object","assign","DEFAULT_CORRELATION_HEADER","generateCorrelationId","randomUUID","addCorrelationIdToResponse","reply","correlationId","headerName","header","raw","setHeader","LoggerService","activeLogger","options","context","defaultLogger","provider","Error","createWinstonLogger","opts","level","logFormat","format","baseFormatters","timestamp","errors","stack","consoleTransport","transports","Console","combine","json","printf","info","message","correlationId","trace","parts","toUpperCase","padEnd","toString","slice","filter","Boolean","output","join","colorize","all","winstonTransports","enableFileLogger","filePath","maxFiles","push","DailyRotateFile","filename","datePattern","maxSize","config","exitOnError","defaultMeta","appName","environment","createLogger","log","_log","error","warn","debug","verbose","setContext","ctx","winstonLogger","winstonLevel","formattedMessage","formatMessage","metadata","enrichMetadata","nestLogger","logWithMetadata","messageWithMeta","JSON","stringify","String","enriched","correlationContext","getCorrelationContext","key","value","Object","entries","child","childLogger","HttpLoggerInterceptor","enableRequestLog","enableResponseLog","slowRequestThreshold","logger","options","intercept","context","next","getType","handle","httpContext","switchToHttp","request","getRequest","response","getResponse","startTime","Date","now","logRequest","pipe","tap","duration","logResponse","catchError","error","logError","correlationContext","getCorrelationContext","metadata","type","method","url","correlationId","ip","userAgent","headers","logWithMetadata","stack","statusCode","logLevel","slowRequest","message","err","errorName","name","errorMessage","trace","errorDetails","loggingError","import_common","import_common","CorrelationIdMiddleware","includeInResponse","responseHeader","options","DEFAULT_CORRELATION_HEADER","use","_req","reply","next","correlationId","generateCorrelationId","addCorrelationIdToResponse","runWithCorrelationContext","onRequest","store","correlationStorage","getStore","enterWith","LOGGER_MODULE_OPTIONS","Symbol","DEFAULT_LOGGER_OPTIONS","provider","enableCorrelationId","enableHttpLogger","filePath","maxFiles","ENVIRONMENT_PRESETS","development","level","format","enableFileLogger","httpLogger","enableRequestLog","enableResponseLog","slowRequestThreshold","staging","production","test","mergeWithDefaults","options","preset","environment","filteredOptions","Object","fromEntries","entries","filter","_","value","undefined","merged","createDefaultLoggerProvider","provide","Logger","useFactory","logger","levels","getLevelsUpTo","setLogLevels","createLoggerProviders","mergedOptions","providers","useValue","push","LoggerService","opts","defaultLogger","inject","token","optional","CorrelationIdMiddleware","includeInResponse","responseHeader","HttpLoggerInterceptor","httpLoggerOptions","allLevels","isValidLevel","l","includes","levelIndex","indexOf","slice","LoggerModule","forRoot","module","exports","forRootAsync","asyncProviders","createAsyncProviders","imports","configure","_consumer","createAsyncOptionsProvider","useClass","args","userOptions","optionsFactory","createLoggerOptions","useExisting","Error","import_common","import_common","import_swagger","import_common","import_swagger","ApiHealthCheck","applyDecorators","ApiOperation","summary","ApiResponse","status","description","type","String","import_common","AppService","getHello","AppController","appService","getHello","import_common","import_swagger","import_common","import_swagger","ApiGetCsrfToken","applyDecorators","ApiOperation","summary","description","ApiResponse","status","schema","type","properties","csrfToken","example","required","CsrfController","getToken","reply","csrfToken","generateCsrf","OK","passthrough","RootModule","controllers","AppController","CsrfController","providers","AppService","CALLING_CODE_TO_COUNTRY","extractCountryFromPhone","phone","digits","startsWith","slice","length","prefix","undefined","normalizePhoneNumber"]}