@vritti/api-sdk 0.1.2 → 0.1.4

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.
package/dist/index.d.ts CHANGED
@@ -1,192 +1,15 @@
1
1
  import * as _nestjs_common from '@nestjs/common';
2
- import { DynamicModule, OnModuleDestroy, OnModuleInit, Logger, CanActivate, ExecutionContext, ExceptionFilter, ArgumentsHost, HttpException, HttpStatus, ModuleMetadata, Type, NestModule, MiddlewareConsumer, LoggerService as LoggerService$1, NestMiddleware, NestInterceptor, CallHandler } from '@nestjs/common';
3
- import { NodePgDatabase } from 'drizzle-orm/node-postgres';
4
- import { InferInsertModel, InferSelectModel, SQL } from 'drizzle-orm';
5
- import { PgTable } from 'drizzle-orm/pg-core';
2
+ import { DynamicModule, OnModuleInit, OnModuleDestroy, CanActivate, ExecutionContext, Logger, HttpException, HttpStatus, ExceptionFilter, ArgumentsHost, ModuleMetadata, Type, LoggerService as LoggerService$1, NestInterceptor, CallHandler, NestModule, MiddlewareConsumer, NestMiddleware } from '@nestjs/common';
6
3
  import { ConfigService } from '@nestjs/config';
7
4
  import { Reflector } from '@nestjs/core';
8
5
  import { JwtService } from '@nestjs/jwt';
6
+ import { NodePgDatabase } from 'drizzle-orm/node-postgres';
9
7
  import { FastifyRequest, FastifyReply } from 'fastify';
8
+ import { InferInsertModel, InferSelectModel, SQL } from 'drizzle-orm';
9
+ import { PgTable } from 'drizzle-orm/pg-core';
10
10
  import { Observable } from 'rxjs';
11
11
  import { AsyncLocalStorage } from 'node:async_hooks';
12
12
 
13
- /**
14
- * api-sdk Configuration System
15
- *
16
- * Similar to quantum-ui's config pattern - provides a type-safe configuration system
17
- *
18
- * @example
19
- * ```typescript
20
- * // In vritti-api-nexus/src/main.ts
21
- * import { configureApiSdk } from '@vritti/api-sdk';
22
- *
23
- * configureApiSdk({
24
- * cookie: {
25
- * refreshCookieName: 'vritti_refresh',
26
- * refreshCookieMaxAge: 30 * 24 * 60 * 60 * 1000, // 30 days
27
- * },
28
- * jwt: {
29
- * accessTokenExpiry: '15m',
30
- * refreshTokenExpiry: '30d',
31
- * validateTokenBinding: true,
32
- * },
33
- * guard: {
34
- * tenantHeaderName: 'x-tenant-id',
35
- * },
36
- * });
37
- * ```
38
- */
39
- /**
40
- * Cookie configuration options
41
- */
42
- interface CookieConfig {
43
- /**
44
- * The name of the httpOnly cookie containing the refresh token
45
- * @default 'vritti_refresh'
46
- */
47
- refreshCookieName: string;
48
- /**
49
- * Max age of the refresh cookie in milliseconds
50
- * @default 2592000000 (30 days)
51
- */
52
- refreshCookieMaxAge: number;
53
- /**
54
- * Cookie path
55
- * @default '/'
56
- */
57
- refreshCookiePath: string;
58
- /**
59
- * Whether the cookie is secure (HTTPS only)
60
- * @default true in production
61
- */
62
- refreshCookieSecure: boolean;
63
- /**
64
- * SameSite attribute for the cookie
65
- * @default 'strict'
66
- */
67
- refreshCookieSameSite: 'strict' | 'lax' | 'none';
68
- }
69
- /**
70
- * JWT token configuration options
71
- */
72
- interface JwtConfig {
73
- /**
74
- * Access token expiry time
75
- * @default '15m'
76
- */
77
- accessTokenExpiry: string;
78
- /**
79
- * Refresh token expiry time
80
- * @default '30d'
81
- */
82
- refreshTokenExpiry: string;
83
- /**
84
- * Onboarding token expiry time
85
- * @default '24h'
86
- */
87
- onboardingTokenExpiry: string;
88
- /**
89
- * Whether to validate refresh token binding (hash in access token)
90
- * @default true
91
- */
92
- validateTokenBinding: boolean;
93
- }
94
- /**
95
- * Auth guard configuration options
96
- */
97
- interface GuardConfig {
98
- /**
99
- * Header name for tenant ID
100
- * @default 'x-tenant-id'
101
- */
102
- tenantHeaderName: string;
103
- /**
104
- * Header name for authorization
105
- * @default 'authorization'
106
- */
107
- authHeaderName: string;
108
- /**
109
- * Token prefix (e.g., 'Bearer')
110
- * @default 'Bearer'
111
- */
112
- tokenPrefix: string;
113
- }
114
- /**
115
- * Complete api-sdk configuration interface
116
- */
117
- interface ApiSdkConfig {
118
- /**
119
- * Cookie configuration
120
- */
121
- cookie?: Partial<CookieConfig>;
122
- /**
123
- * JWT token configuration
124
- */
125
- jwt?: Partial<JwtConfig>;
126
- /**
127
- * Auth guard configuration
128
- */
129
- guard?: Partial<GuardConfig>;
130
- }
131
- /**
132
- * Full configuration type with all properties required
133
- */
134
- interface FullConfig {
135
- cookie: CookieConfig;
136
- jwt: JwtConfig;
137
- guard: GuardConfig;
138
- }
139
- /**
140
- * Helper function to define configuration with type safety
141
- * Similar to Tailwind's defineConfig()
142
- */
143
- declare function defineConfig(config: ApiSdkConfig): ApiSdkConfig;
144
- /**
145
- * Configure api-sdk with user settings
146
- * This should be called once in the application's bootstrap (main.ts)
147
- */
148
- declare function configureApiSdk(userConfig: ApiSdkConfig): void;
149
- /**
150
- * Get the current configuration
151
- */
152
- declare function getConfig(): FullConfig;
153
- /**
154
- * Reset configuration to defaults (for testing)
155
- */
156
- declare function resetConfig(): void;
157
- /**
158
- * Get refresh cookie options (convenience method)
159
- */
160
- declare function getRefreshCookieOptions(): {
161
- httpOnly: boolean;
162
- secure: boolean;
163
- sameSite: "strict" | "lax" | "none";
164
- path: string;
165
- maxAge: number;
166
- };
167
- /**
168
- * Get JWT expiry settings (convenience method)
169
- */
170
- declare function getJwtExpiry(): {
171
- access: string;
172
- refresh: string;
173
- onboarding: string;
174
- };
175
-
176
- /**
177
- * Hash a token using SHA-256
178
- * @param token The token to hash
179
- * @returns The hex-encoded SHA-256 hash
180
- */
181
- declare function hashToken(token: string): string;
182
- /**
183
- * Verify a token against its expected hash using constant-time comparison
184
- * @param token The token to verify
185
- * @param expectedHash The expected SHA-256 hash
186
- * @returns true if the token matches the hash
187
- */
188
- declare function verifyTokenHash(token: string, expectedHash: string): boolean;
189
-
190
13
  /**
191
14
  * Global authentication configuration module
192
15
  *
@@ -261,6 +84,78 @@ declare class AuthConfigModule {
261
84
  static forRootAsync(): DynamicModule;
262
85
  }
263
86
 
87
+ /**
88
+ * Onboarding Decorator - Marks endpoints that require onboarding token
89
+ *
90
+ * Use this decorator on controllers or route handlers that should only be
91
+ * accessible during the onboarding flow with JWT tokens containing type='onboarding'.
92
+ *
93
+ * These endpoints:
94
+ * - Accept ONLY tokens with type='onboarding'
95
+ * - Reject regular access tokens (type='access')
96
+ * - Skip tenant validation and refresh token checks
97
+ * - Only validate JWT signature and expiry
98
+ *
99
+ * Useful for:
100
+ * - Email/phone verification during onboarding
101
+ * - Onboarding status checks
102
+ * - Resending OTPs during registration
103
+ *
104
+ * @example
105
+ * // On a controller method
106
+ * @Post('verify-email')
107
+ * @Onboarding()
108
+ * async verifyEmail(@Request() req, @Body() dto: VerifyEmailDto) {
109
+ * const userId = req.user.id; // Available from VrittiAuthGuard
110
+ * return this.service.verifyEmail(userId, dto.otp);
111
+ * }
112
+ *
113
+ * @example
114
+ * // Multiple onboarding endpoints
115
+ * @Controller('onboarding')
116
+ * export class OnboardingController {
117
+ * @Post('verify-email')
118
+ * @Onboarding()
119
+ * async verifyEmail() { ... }
120
+ *
121
+ * @Post('resend-otp')
122
+ * @Onboarding()
123
+ * async resendOtp() { ... }
124
+ * }
125
+ */
126
+ declare const Onboarding: () => _nestjs_common.CustomDecorator<string>;
127
+
128
+ /**
129
+ * Public Decorator - Marks endpoints that don't require authentication
130
+ *
131
+ * Use this decorator on controllers or route handlers to bypass VrittiAuthGuard
132
+ * tenant validation. Useful for:
133
+ * - Login/signup endpoints
134
+ * - Health checks
135
+ * - Public documentation endpoints
136
+ * - Webhook endpoints that don't require tenant context
137
+ *
138
+ * @example
139
+ * // On a controller method
140
+ * @Public()
141
+ * @Post('auth/login')
142
+ * async login(@Body() dto: LoginDto) {
143
+ * return this.authService.login(dto);
144
+ * }
145
+ *
146
+ * @example
147
+ * // On an entire controller
148
+ * @Public()
149
+ * @Controller('health')
150
+ * export class HealthController {
151
+ * @Get()
152
+ * check() {
153
+ * return { status: 'ok' };
154
+ * }
155
+ * }
156
+ */
157
+ declare const Public: () => _nestjs_common.CustomDecorator<string>;
158
+
264
159
  /**
265
160
  * Schema Registry Interface
266
161
  *
@@ -275,8 +170,7 @@ declare class AuthConfigModule {
275
170
  * }
276
171
  * }
277
172
  */
278
- interface SchemaRegistry {
279
- }
173
+ type SchemaRegistry = {};
280
174
  /**
281
175
  * Extracts the registered schema type.
282
176
  * Falls back to Record<string, unknown> if no schema is registered.
@@ -386,361 +280,557 @@ interface TenantInfo {
386
280
  }
387
281
 
388
282
  /**
389
- * Dynamic module for multi-tenant database management
390
- *
391
- * This module provides:
392
- * - Tenant context management (request-scoped)
393
- * - Database connection pooling
394
- * - Dynamic schema/cluster routing
395
- * - Support for both gateway and microservice modes
396
- *
397
- * ## Gateway Mode (API Gateway)
398
- * - Use DatabaseModule.forServer() method
399
- * - Automatically extracts tenant from subdomain, falls back to x-tenant-id header
400
- * - Provide primaryDb configuration and prismaClientConstructor
401
- * - Automatically queries primary DB for tenant config
402
- * - Automatically registers TenantContextInterceptor globally
403
- * - No manual interceptor registration needed
404
- *
405
- * ## Microservice Mode (RabbitMQ Workers)
406
- * - Use DatabaseModule.forMicroservice() method
407
- * - Only provide prismaClientConstructor
408
- * - Tenant context comes from RabbitMQ messages
409
- * - Automatically registers MessageTenantContextInterceptor globally
410
- * - No manual interceptor registration needed
283
+ * Service responsible for querying the primary database to resolve tenant configurations
411
284
  *
412
- * @example
413
- * // Gateway configuration
414
- * DatabaseModule.forServer({
415
- * inject: [ConfigService],
416
- * useFactory: (config: ConfigService) => ({
417
- * primaryDb: {
418
- * host: config.get('PRIMARY_DB_HOST'),
419
- * port: config.get('PRIMARY_DB_PORT'),
420
- * username: config.get('PRIMARY_DB_USERNAME'),
421
- * password: config.get('PRIMARY_DB_PASSWORD'),
422
- * database: config.get('PRIMARY_DB_DATABASE'),
423
- * },
424
- * prismaClientConstructor: PrismaClient,
425
- * }),
426
- * })
285
+ * This service:
286
+ * - Connects to the primary database (tenant registry)
287
+ * - Queries tenant metadata (database location, credentials, etc.)
288
+ * - Caches tenant configs in memory to reduce database load
289
+ * - Only used in GATEWAY MODE (microservices receive tenant config from messages)
427
290
  *
428
291
  * @example
429
- * // Microservice configuration
430
- * DatabaseModule.forMicroservice({
431
- * inject: [ConfigService],
432
- * useFactory: (config: ConfigService) => ({
433
- * prismaClientConstructor: PrismaClient,
434
- * }),
435
- * })
292
+ * // In API Gateway
293
+ * const config = await primaryDatabase.getTenantConfig('acme');
294
+ * // Returns: { id, slug, type, databaseHost, databaseName, ... }
436
295
  */
437
- declare class DatabaseModule {
438
- /**
439
- * Configure DatabaseModule for Gateway/HTTP mode (multi-tenant web servers)
440
- *
441
- * This mode is for API Gateways that handle HTTP requests:
442
- * - Automatically registers TenantContextInterceptor
443
- * - Extracts tenant from subdomain or x-tenant-id header
444
- * - Queries primary database for tenant configuration
445
- * - Provides PrimaryDatabaseService for tenant lookup
446
- *
447
- * @param options Async configuration options
448
- * @returns Dynamic module configuration with HTTP interceptor
449
- *
450
- * @example
451
- * DatabaseModule.forServer({
452
- * inject: [ConfigService],
453
- * useFactory: (config: ConfigService) => ({
454
- * primaryDb: {
455
- * host: config.get('PRIMARY_DB_HOST'),
456
- * port: config.get('PRIMARY_DB_PORT'),
457
- * username: config.get('PRIMARY_DB_USERNAME'),
458
- * password: config.get('PRIMARY_DB_PASSWORD'),
459
- * database: config.get('PRIMARY_DB_DATABASE'),
460
- * },
461
- * prismaClientConstructor: PrismaClient,
462
- * }),
463
- * })
464
- */
465
- static forServer(options: {
466
- useFactory: (...args: any[]) => Promise<DatabaseModuleOptions> | DatabaseModuleOptions;
467
- inject?: any[];
468
- }): DynamicModule;
296
+ declare class PrimaryDatabaseService implements OnModuleInit, OnModuleDestroy {
297
+ private readonly options;
298
+ private readonly logger;
299
+ /** PostgreSQL connection pool */
300
+ private pool;
301
+ /** Drizzle database instance */
302
+ private db;
303
+ /** In-memory cache: Map<tenantIdentifier, TenantConfig> */
304
+ private readonly tenantConfigCache;
305
+ /** Cache TTL in milliseconds */
306
+ private readonly cacheTTL;
307
+ constructor(options: DatabaseModuleOptions);
308
+ onModuleInit(): Promise<void>;
469
309
  /**
470
- * Configure DatabaseModule for Microservice/Messaging mode (RabbitMQ workers)
471
- *
472
- * This mode is for microservices that process messages from queues:
473
- * - Automatically registers MessageTenantContextInterceptor
474
- * - Extracts tenant from RabbitMQ message patterns
475
- * - No primary database needed (tenant comes from message context)
476
- *
477
- * @param options Async configuration options
478
- * @returns Dynamic module configuration with message interceptor
479
- *
480
- * @example
481
- * DatabaseModule.forMicroservice({
482
- * inject: [ConfigService],
483
- * useFactory: (config: ConfigService) => ({
484
- * prismaClientConstructor: PrismaClient,
485
- * }),
486
- * })
310
+ * Initialize connection to primary database using Drizzle
487
311
  */
488
- static forMicroservice(options: {
489
- useFactory: (...args: any[]) => Promise<DatabaseModuleOptions> | DatabaseModuleOptions;
490
- inject?: any[];
491
- }): DynamicModule;
312
+ private initializeDrizzleClient;
492
313
  /**
493
- * Internal helper to create dynamic module with conditional interceptor registration
494
- *
495
- * @param options Configuration options
496
- * @param mode Mode of operation (gateway or microservice)
497
- * @returns Dynamic module configuration
314
+ * Build connection URL from primary database properties
498
315
  */
499
- private static createDynamicModule;
500
- }
501
-
502
- /**
503
- * Request-scoped service that holds tenant context for the current request or RabbitMQ message
504
- *
505
- * IMPORTANT: This service is REQUEST-SCOPED, meaning NestJS creates a new instance
506
- * for each HTTP request or RabbitMQ message. This ensures tenant isolation and
507
- * prevents cross-tenant data leaks in concurrent scenarios.
508
- *
509
- * @example
510
- * // In a controller or service
511
- * constructor(private readonly tenantContext: TenantContextService) {}
512
- *
513
- * async handleRequest() {
514
- * const tenant = this.tenantContext.getTenant();
515
- * console.log(`Processing request for tenant: ${tenant.tenantSlug}`);
516
- * }
517
- */
518
- declare class TenantContextService {
519
- private tenantInfo;
316
+ private buildPrimaryDbUrl;
520
317
  /**
521
- * Set tenant information for this request/message
522
- *
523
- * This is typically called by:
524
- * - TenantContextInterceptor (for HTTP requests in gateway)
525
- * - MessageTenantContextInterceptor (for RabbitMQ messages in microservices)
526
- * - Manual context setup in message handlers
527
- *
528
- * @param tenantInfo Complete tenant information
529
- * @throws Error if tenant context is already set (prevents accidental overwrites)
318
+ * Mask password in connection URL for logging
530
319
  */
531
- setTenant(tenantInfo: TenantInfo): void;
320
+ private maskPassword;
532
321
  /**
533
- * Get tenant information for this request/message
322
+ * Get tenant configuration by identifier (ID or subdomain)
534
323
  *
535
- * @returns Tenant information
536
- * @throws UnauthorizedException if tenant context hasn't been set
324
+ * @param tenantIdentifier Tenant ID or subdomain
325
+ * @returns Tenant configuration or null if not found
537
326
  */
538
- getTenant(): TenantInfo;
327
+ getTenantInfo(tenantIdentifier: string): Promise<TenantInfo | null>;
539
328
  /**
540
- * Check if tenant context has been set
541
- *
542
- * @returns true if tenant context is available
329
+ * Cache tenant information with TTL
543
330
  */
544
- hasTenant(): boolean;
331
+ private cacheInfo;
545
332
  /**
546
- * Clear tenant context
547
- *
548
- * This is useful for cleanup in RabbitMQ message handlers
549
- * after the message has been processed.
333
+ * Clear cached tenant information
550
334
  *
551
- * HTTP requests don't need manual cleanup as the service
552
- * instance is destroyed when the request ends.
553
- */
554
- clearTenant(): void;
555
- /**
556
- * Get tenant ID safely (returns null if not set)
335
+ * Useful when tenant settings are updated and cache needs to be invalidated
557
336
  *
558
- * @returns Tenant ID or null
337
+ * @param tenantIdentifier Tenant ID or subdomain
559
338
  */
560
- getTenantIdSafe(): string | null;
339
+ clearTenantCache(tenantIdentifier: string): void;
561
340
  /**
562
- * Get tenant subdomain safely (returns null if not set)
563
- *
564
- * @returns Tenant subdomain or null
341
+ * Clear all cached tenant configurations
565
342
  */
566
- getTenantSubdomainSafe(): string | null;
567
- }
568
-
569
- /**
570
- * Service responsible for managing tenant-scoped database connections
571
- *
572
- * This service:
573
- * - Maintains a connection pool (Map<cacheKey, TenantConnection>)
574
- * - Creates new connections dynamically based on tenant context
575
- * - Reuses existing connections for the same tenant
576
- * - Supports both cloud schemas and enterprise databases
577
- * - Automatically cleans up idle connections
578
- *
579
- * @example
580
- * // In a controller or service
581
- * const db = this.tenantDatabase.drizzleClient;
582
- * const users = await db.select().from(usersTable);
583
- */
584
- declare class TenantDatabaseService implements OnModuleDestroy {
585
- private readonly options;
586
- private readonly tenantContext;
587
- private readonly logger;
588
- /** Connection pool: Map<cacheKey, TenantConnection> */
589
- private readonly clients;
590
- /** Track last usage time for idle connection cleanup */
591
- private readonly clientLastUsed;
592
- /** Cleanup interval timer */
593
- private cleanupInterval?;
594
- constructor(options: DatabaseModuleOptions, tenantContext: TenantContextService);
343
+ clearAllCaches(): void;
595
344
  /**
596
- * Get the Drizzle client for the current tenant's database.
597
- * This returns the tenant-scoped database client.
345
+ * Get the Drizzle database instance for the primary database.
346
+ * This is a synchronous property that returns the initialized Drizzle client.
598
347
  *
599
- * @returns Tenant-scoped Drizzle database instance
600
- * @throws UnauthorizedException if tenant context not set
601
- * @throws InternalServerErrorException if connection fails
348
+ * @returns Primary database Drizzle instance
349
+ * @throws Error if primary database client is not initialized
602
350
  */
603
351
  get drizzleClient(): TypedDrizzleClient;
604
352
  /**
605
353
  * Get the Drizzle schema
606
354
  */
607
- get schema(): Record<string, unknown>;
355
+ get schema(): typeof this$1.options.drizzleSchema;
608
356
  /**
609
- * Get tenant-scoped database client for the current request/message
357
+ * Decrypt database credentials
610
358
  *
611
- * This method:
612
- * 1. Gets tenant info from TenantContextService
613
- * 2. Builds a connection URL based on tenant type
614
- * 3. Returns cached client if exists, otherwise creates new one
359
+ * Override this method to implement your encryption strategy
615
360
  *
616
- * @returns Drizzle database instance
617
- * @throws UnauthorizedException if tenant context not set
618
- * @throws InternalServerErrorException if connection fails
361
+ * @param encrypted Encrypted value
362
+ * @returns Decrypted value
619
363
  */
620
- private getDbClient;
364
+ private decrypt;
365
+ onModuleDestroy(): Promise<void>;
366
+ }
367
+
368
+ declare class RequestService {
369
+ private readonly request;
370
+ constructor(request: FastifyRequest);
621
371
  /**
622
- * Create a new database client for the given tenant (synchronous)
372
+ * Extract tenant identifier from request headers
373
+ * Priority: x-tenant-id > x-subdomain
374
+ * @returns Tenant identifier or null if not found
623
375
  */
624
- private createDbClientSync;
376
+ getTenantIdentifier(): string | null;
625
377
  /**
626
- * Build connection URL for tenant (dedicated database)
378
+ * Extract access token from Authorization header
379
+ * Format: "Bearer <token>"
380
+ * @returns Access token or null if not found
627
381
  */
628
- private buildTenantDbUrl;
382
+ getAccessToken(): string | null;
629
383
  /**
630
- * Build cache key for connection pooling
384
+ * Extract refresh token from httpOnly cookie
385
+ * Cookie name is configurable via api-sdk config
386
+ * @returns Refresh token or null if not found
631
387
  */
632
- private buildCacheKey;
388
+ getRefreshToken(): string | null;
633
389
  /**
634
- * Start periodic cleanup of idle connections
390
+ * Get a specific header value
391
+ * @param key Header key
392
+ * @returns Header value (string, array, or undefined)
635
393
  */
636
- private startConnectionCleaner;
394
+ getHeader(key: string): string | string[] | undefined;
637
395
  /**
638
- * Clean up idle connections that haven't been used recently
396
+ * Get all headers
397
+ * @returns Record of all headers
639
398
  */
640
- private cleanupIdleConnections;
399
+ getAllHeaders(): FastifyRequest['headers'];
400
+ }
401
+
402
+ /**
403
+ * Vritti Authentication Guard - Validates JWT access tokens and tenant context
404
+ *
405
+ * This guard performs access token validation and attaches user data to request.
406
+ * NOTE: Refresh tokens are NOT validated here - they are only validated in
407
+ * /auth/token and /auth/refresh endpoints (session.service.ts).
408
+ *
409
+ * Validation Flow:
410
+ * 1. Checks if endpoint is marked with @Public() decorator → skip all validation
411
+ * 2. Checks if endpoint is marked with @Onboarding() decorator:
412
+ * - Requires token type='onboarding'
413
+ * - Validates JWT signature and expiry only
414
+ * - Skips tenant validation
415
+ * - Attaches user data to request.user
416
+ * 3. For regular endpoints (no decorator):
417
+ * - Rejects tokens with type='onboarding'
418
+ * - Validates access token (JWT signature, expiry, nbf)
419
+ * - Validates tenant exists and is ACTIVE
420
+ * - Attaches user data to request.user
421
+ *
422
+ * Token Format:
423
+ * - Access Token: "Authorization: Bearer <jwt_token>"
424
+ *
425
+ * Token Types:
426
+ * - type='onboarding': Limited access during registration flow (@Onboarding endpoints only)
427
+ * - type='access': Full access to authenticated endpoints
428
+ *
429
+ * Environment Variables Required:
430
+ * - JWT_SECRET: Secret key to verify access tokens (required)
431
+ *
432
+ * Error Responses:
433
+ * - 401: Invalid/expired access token
434
+ * - 401: Tenant not found or inactive
435
+ * - 401: Tenant identifier not found
436
+ * - 401: Token type mismatch (onboarding token on regular endpoint or vice versa)
437
+ *
438
+ * @example
439
+ * // Automatically registered by AuthConfigModule.forRootAsync()
440
+ * // No manual registration needed
441
+ * //
442
+ * // Internal registration uses useExisting pattern:
443
+ * // providers: [
444
+ * // VrittiAuthGuard,
445
+ * // {
446
+ * // provide: APP_GUARD,
447
+ * // useExisting: VrittiAuthGuard,
448
+ * // },
449
+ * // ]
450
+ *
451
+ * @example
452
+ * // Bypass guard with @Public() decorator
453
+ * @Public()
454
+ * @Post('auth/login')
455
+ * async login(@Body() dto: LoginDto) { ... }
456
+ *
457
+ * @example
458
+ * // Restrict to onboarding tokens with @Onboarding() decorator
459
+ * @Onboarding()
460
+ * @Post('onboarding/verify-email')
461
+ * async verifyEmail(@Request() req) {
462
+ * const userId = req.user.id; // Available from guard
463
+ * ...
464
+ * }
465
+ */
466
+ declare class VrittiAuthGuard implements CanActivate {
467
+ private readonly reflector;
468
+ readonly _configService: ConfigService;
469
+ private readonly jwtService;
470
+ private readonly primaryDatabase;
471
+ private readonly requestService;
472
+ private readonly logger;
473
+ constructor(reflector: Reflector, _configService: ConfigService, jwtService: JwtService, primaryDatabase: PrimaryDatabaseService, requestService: RequestService);
474
+ canActivate(context: ExecutionContext): Promise<boolean>;
641
475
  /**
642
- * Get current connection pool statistics
476
+ * Validate access token with proper expiry checks
477
+ * Throws UnauthorizedException if token is invalid or expired
643
478
  */
644
- getPoolStats(): {
645
- activeConnections: number;
646
- tenants: string[];
647
- };
479
+ private validateAccessToken;
648
480
  /**
649
- * Mask password in connection URL for logging
481
+ * Validate that the access token is bound to the refresh token in the cookie.
482
+ * This prevents token theft - a stolen access token is useless without the
483
+ * corresponding refresh token cookie.
484
+ *
485
+ * @param context - The execution context containing the request
486
+ * @param validatedToken - The decoded and validated JWT token
487
+ * @throws UnauthorizedException if token binding validation fails
650
488
  */
651
- private maskPassword;
652
- onModuleDestroy(): Promise<void>;
489
+ private validateRefreshTokenBinding;
653
490
  }
654
491
 
655
492
  /**
656
- * Service responsible for querying the primary database to resolve tenant configurations
493
+ * Hash a token using SHA-256
494
+ * @param token The token to hash
495
+ * @returns The hex-encoded SHA-256 hash
496
+ */
497
+ declare function hashToken(token: string): string;
498
+ /**
499
+ * Verify a token against its expected hash using constant-time comparison
500
+ * @param token The token to verify
501
+ * @param expectedHash The expected SHA-256 hash
502
+ * @returns true if the token matches the hash
503
+ */
504
+ declare function verifyTokenHash(token: string, expectedHash: string): boolean;
505
+
506
+ /**
507
+ * api-sdk Configuration System
657
508
  *
658
- * This service:
659
- * - Connects to the primary database (tenant registry)
660
- * - Queries tenant metadata (database location, credentials, etc.)
661
- * - Caches tenant configs in memory to reduce database load
662
- * - Only used in GATEWAY MODE (microservices receive tenant config from messages)
509
+ * Similar to quantum-ui's config pattern - provides a type-safe configuration system
663
510
  *
664
511
  * @example
665
- * // In API Gateway
666
- * const config = await primaryDatabase.getTenantConfig('acme');
667
- * // Returns: { id, slug, type, databaseHost, databaseName, ... }
512
+ * ```typescript
513
+ * // In vritti-api-nexus/src/main.ts
514
+ * import { configureApiSdk } from '@vritti/api-sdk';
515
+ *
516
+ * configureApiSdk({
517
+ * cookie: {
518
+ * refreshCookieName: 'vritti_refresh',
519
+ * refreshCookieMaxAge: 30 * 24 * 60 * 60 * 1000, // 30 days
520
+ * },
521
+ * jwt: {
522
+ * accessTokenExpiry: '15m',
523
+ * refreshTokenExpiry: '30d',
524
+ * validateTokenBinding: true,
525
+ * },
526
+ * guard: {
527
+ * tenantHeaderName: 'x-tenant-id',
528
+ * },
529
+ * });
530
+ * ```
668
531
  */
669
- declare class PrimaryDatabaseService implements OnModuleInit, OnModuleDestroy {
670
- private readonly options;
671
- private readonly logger;
672
- /** PostgreSQL connection pool */
673
- private pool;
674
- /** Drizzle database instance */
675
- private db;
676
- /** In-memory cache: Map<tenantIdentifier, TenantConfig> */
677
- private readonly tenantConfigCache;
678
- /** Cache TTL in milliseconds */
679
- private readonly cacheTTL;
680
- constructor(options: DatabaseModuleOptions);
681
- onModuleInit(): Promise<void>;
682
- /**
683
- * Initialize connection to primary database using Drizzle
684
- */
685
- private initializeDrizzleClient;
532
+ /**
533
+ * Cookie configuration options
534
+ */
535
+ interface CookieConfig {
686
536
  /**
687
- * Build connection URL from primary database properties
537
+ * The name of the httpOnly cookie containing the refresh token
538
+ * @default 'vritti_refresh'
688
539
  */
689
- private buildPrimaryDbUrl;
540
+ refreshCookieName: string;
690
541
  /**
691
- * Mask password in connection URL for logging
542
+ * Max age of the refresh cookie in milliseconds
543
+ * @default 2592000000 (30 days)
692
544
  */
693
- private maskPassword;
545
+ refreshCookieMaxAge: number;
694
546
  /**
695
- * Get tenant configuration by identifier (ID or subdomain)
696
- *
697
- * @param tenantIdentifier Tenant ID or subdomain
698
- * @returns Tenant configuration or null if not found
547
+ * Cookie path
548
+ * @default '/'
699
549
  */
700
- getTenantInfo(tenantIdentifier: string): Promise<TenantInfo | null>;
550
+ refreshCookiePath: string;
701
551
  /**
702
- * Cache tenant information with TTL
552
+ * Whether the cookie is secure (HTTPS only)
553
+ * @default true in production
703
554
  */
704
- private cacheInfo;
555
+ refreshCookieSecure: boolean;
705
556
  /**
706
- * Clear cached tenant information
707
- *
708
- * Useful when tenant settings are updated and cache needs to be invalidated
709
- *
710
- * @param tenantIdentifier Tenant ID or subdomain
557
+ * SameSite attribute for the cookie
558
+ * @default 'strict'
711
559
  */
712
- clearTenantCache(tenantIdentifier: string): void;
560
+ refreshCookieSameSite: 'strict' | 'lax' | 'none';
561
+ }
562
+ /**
563
+ * JWT token configuration options
564
+ */
565
+ interface JwtConfig {
713
566
  /**
714
- * Clear all cached tenant configurations
567
+ * Access token expiry time
568
+ * @default '15m'
715
569
  */
716
- clearAllCaches(): void;
570
+ accessTokenExpiry: string;
717
571
  /**
718
- * Get the Drizzle database instance for the primary database.
719
- * This is a synchronous property that returns the initialized Drizzle client.
720
- *
721
- * @returns Primary database Drizzle instance
722
- * @throws Error if primary database client is not initialized
572
+ * Refresh token expiry time
573
+ * @default '30d'
723
574
  */
724
- get drizzleClient(): TypedDrizzleClient;
575
+ refreshTokenExpiry: string;
725
576
  /**
726
- * Get the Drizzle schema
577
+ * Onboarding token expiry time
578
+ * @default '24h'
727
579
  */
728
- get schema(): typeof this$1.options.drizzleSchema;
580
+ onboardingTokenExpiry: string;
729
581
  /**
730
- * Decrypt database credentials
731
- *
732
- * Override this method to implement your encryption strategy
733
- *
734
- * @param encrypted Encrypted value
735
- * @returns Decrypted value
582
+ * Whether to validate refresh token binding (hash in access token)
583
+ * @default true
736
584
  */
737
- private decrypt;
738
- onModuleDestroy(): Promise<void>;
585
+ validateTokenBinding: boolean;
739
586
  }
740
-
741
587
  /**
742
- * Drizzle ORM v2 object-based where filter type.
743
- * Supports simple equality, operators, AND/OR/NOT, and RAW SQL.
588
+ * Auth guard configuration options
589
+ */
590
+ interface GuardConfig {
591
+ /**
592
+ * Header name for tenant ID
593
+ * @default 'x-tenant-id'
594
+ */
595
+ tenantHeaderName: string;
596
+ /**
597
+ * Header name for authorization
598
+ * @default 'authorization'
599
+ */
600
+ authHeaderName: string;
601
+ /**
602
+ * Token prefix (e.g., 'Bearer')
603
+ * @default 'Bearer'
604
+ */
605
+ tokenPrefix: string;
606
+ }
607
+ /**
608
+ * Complete api-sdk configuration interface
609
+ */
610
+ interface ApiSdkConfig {
611
+ /**
612
+ * Cookie configuration
613
+ */
614
+ cookie?: Partial<CookieConfig>;
615
+ /**
616
+ * JWT token configuration
617
+ */
618
+ jwt?: Partial<JwtConfig>;
619
+ /**
620
+ * Auth guard configuration
621
+ */
622
+ guard?: Partial<GuardConfig>;
623
+ }
624
+ /**
625
+ * Full configuration type with all properties required
626
+ */
627
+ interface FullConfig {
628
+ cookie: CookieConfig;
629
+ jwt: JwtConfig;
630
+ guard: GuardConfig;
631
+ }
632
+ /**
633
+ * Helper function to define configuration with type safety
634
+ * Similar to Tailwind's defineConfig()
635
+ */
636
+ declare function defineConfig(config: ApiSdkConfig): ApiSdkConfig;
637
+ /**
638
+ * Configure api-sdk with user settings
639
+ * This should be called once in the application's bootstrap (main.ts)
640
+ */
641
+ declare function configureApiSdk(userConfig: ApiSdkConfig): void;
642
+ /**
643
+ * Get the current configuration
644
+ */
645
+ declare function getConfig(): FullConfig;
646
+ /**
647
+ * Reset configuration to defaults (for testing)
648
+ */
649
+ declare function resetConfig(): void;
650
+ /**
651
+ * Get refresh cookie options (convenience method)
652
+ */
653
+ declare function getRefreshCookieOptions(): {
654
+ httpOnly: boolean;
655
+ secure: boolean;
656
+ sameSite: "strict" | "lax" | "none";
657
+ path: string;
658
+ maxAge: number;
659
+ };
660
+ /**
661
+ * Get JWT expiry settings (convenience method)
662
+ */
663
+ declare function getJwtExpiry(): {
664
+ access: string;
665
+ refresh: string;
666
+ onboarding: string;
667
+ };
668
+
669
+ /**
670
+ * Dynamic module for multi-tenant database management
671
+ *
672
+ * This module provides:
673
+ * - Tenant context management (request-scoped)
674
+ * - Database connection pooling
675
+ * - Dynamic schema/cluster routing
676
+ * - Support for both gateway and microservice modes
677
+ *
678
+ * ## Gateway Mode (API Gateway)
679
+ * - Use DatabaseModule.forServer() method
680
+ * - Automatically extracts tenant from subdomain, falls back to x-tenant-id header
681
+ * - Provide primaryDb configuration and prismaClientConstructor
682
+ * - Automatically queries primary DB for tenant config
683
+ * - Automatically registers TenantContextInterceptor globally
684
+ * - No manual interceptor registration needed
685
+ *
686
+ * ## Microservice Mode (RabbitMQ Workers)
687
+ * - Use DatabaseModule.forMicroservice() method
688
+ * - Only provide prismaClientConstructor
689
+ * - Tenant context comes from RabbitMQ messages
690
+ * - Automatically registers MessageTenantContextInterceptor globally
691
+ * - No manual interceptor registration needed
692
+ *
693
+ * @example
694
+ * // Gateway configuration
695
+ * DatabaseModule.forServer({
696
+ * inject: [ConfigService],
697
+ * useFactory: (config: ConfigService) => ({
698
+ * primaryDb: {
699
+ * host: config.get('PRIMARY_DB_HOST'),
700
+ * port: config.get('PRIMARY_DB_PORT'),
701
+ * username: config.get('PRIMARY_DB_USERNAME'),
702
+ * password: config.get('PRIMARY_DB_PASSWORD'),
703
+ * database: config.get('PRIMARY_DB_DATABASE'),
704
+ * },
705
+ * prismaClientConstructor: PrismaClient,
706
+ * }),
707
+ * })
708
+ *
709
+ * @example
710
+ * // Microservice configuration
711
+ * DatabaseModule.forMicroservice({
712
+ * inject: [ConfigService],
713
+ * useFactory: (config: ConfigService) => ({
714
+ * prismaClientConstructor: PrismaClient,
715
+ * }),
716
+ * })
717
+ */
718
+ declare class DatabaseModule {
719
+ /**
720
+ * Configure DatabaseModule for Gateway/HTTP mode (multi-tenant web servers)
721
+ *
722
+ * This mode is for API Gateways that handle HTTP requests:
723
+ * - Automatically registers TenantContextInterceptor
724
+ * - Extracts tenant from subdomain or x-tenant-id header
725
+ * - Queries primary database for tenant configuration
726
+ * - Provides PrimaryDatabaseService for tenant lookup
727
+ *
728
+ * @param options Async configuration options
729
+ * @returns Dynamic module configuration with HTTP interceptor
730
+ *
731
+ * @example
732
+ * DatabaseModule.forServer({
733
+ * inject: [ConfigService],
734
+ * useFactory: (config: ConfigService) => ({
735
+ * primaryDb: {
736
+ * host: config.get('PRIMARY_DB_HOST'),
737
+ * port: config.get('PRIMARY_DB_PORT'),
738
+ * username: config.get('PRIMARY_DB_USERNAME'),
739
+ * password: config.get('PRIMARY_DB_PASSWORD'),
740
+ * database: config.get('PRIMARY_DB_DATABASE'),
741
+ * },
742
+ * prismaClientConstructor: PrismaClient,
743
+ * }),
744
+ * })
745
+ */
746
+ static forServer(options: {
747
+ useFactory: (...args: any[]) => Promise<DatabaseModuleOptions> | DatabaseModuleOptions;
748
+ inject?: any[];
749
+ }): DynamicModule;
750
+ /**
751
+ * Configure DatabaseModule for Microservice/Messaging mode (RabbitMQ workers)
752
+ *
753
+ * This mode is for microservices that process messages from queues:
754
+ * - Automatically registers MessageTenantContextInterceptor
755
+ * - Extracts tenant from RabbitMQ message patterns
756
+ * - No primary database needed (tenant comes from message context)
757
+ *
758
+ * @param options Async configuration options
759
+ * @returns Dynamic module configuration with message interceptor
760
+ *
761
+ * @example
762
+ * DatabaseModule.forMicroservice({
763
+ * inject: [ConfigService],
764
+ * useFactory: (config: ConfigService) => ({
765
+ * prismaClientConstructor: PrismaClient,
766
+ * }),
767
+ * })
768
+ */
769
+ static forMicroservice(options: {
770
+ useFactory: (...args: any[]) => Promise<DatabaseModuleOptions> | DatabaseModuleOptions;
771
+ inject?: any[];
772
+ }): DynamicModule;
773
+ /**
774
+ * Internal helper to create dynamic module with conditional interceptor registration
775
+ *
776
+ * @param options Configuration options
777
+ * @param mode Mode of operation (gateway or microservice)
778
+ * @returns Dynamic module configuration
779
+ */
780
+ private static createDynamicModule;
781
+ }
782
+
783
+ /**
784
+ * Parameter decorator that injects tenant metadata into controller method
785
+ *
786
+ * This decorator retrieves tenant information (ID, slug, type, etc.)
787
+ * from the REQUEST-SCOPED TenantContextService.
788
+ *
789
+ * Useful for:
790
+ * - Logging tenant-specific information
791
+ * - Implementing tenant-specific business logic
792
+ * - Auditing and tracking
793
+ * - Conditional feature flags
794
+ *
795
+ * @returns TenantInfo object with tenant metadata
796
+ *
797
+ * @example
798
+ * // Access tenant metadata
799
+ * @Get('info')
800
+ * async getTenantInfo(@Tenant() tenant: TenantInfo) {
801
+ * return {
802
+ * id: tenant.id,
803
+ * subdomain: tenant.subdomain,
804
+ * type: tenant.type,
805
+ * };
806
+ * }
807
+ *
808
+ * @example
809
+ * // Use for logging
810
+ * @Post()
811
+ * async createUser(
812
+ * @Body() dto: CreateUserDto,
813
+ * @Tenant() tenant: TenantInfo,
814
+ * ) {
815
+ * this.logger.log(`Creating user for tenant: ${tenant.subdomain}`);
816
+ * // ...
817
+ * }
818
+ *
819
+ * @example
820
+ * // Conditional business logic
821
+ * @Get('features')
822
+ * async getFeatures(@Tenant() tenant: TenantInfo) {
823
+ * if (tenant.type === 'ENTERPRISE') {
824
+ * return ['feature-a', 'feature-b', 'feature-c'];
825
+ * }
826
+ * return ['feature-a'];
827
+ * }
828
+ */
829
+ declare const Tenant: (...dataOrPipes: unknown[]) => ParameterDecorator;
830
+
831
+ /**
832
+ * Drizzle ORM v2 object-based where filter type.
833
+ * Supports simple equality, operators, AND/OR/NOT, and RAW SQL.
744
834
  *
745
835
  * @example
746
836
  * ```typescript
@@ -1072,16 +1162,169 @@ declare abstract class PrimaryBaseRepository<TTable extends PgTable, TInsert = I
1072
1162
  }
1073
1163
 
1074
1164
  /**
1075
- * Type helper to extract table name from Drizzle table.
1076
- * TTable['_']['name'] gives us the string literal type (e.g., 'products')
1077
- */
1078
- type ExtractTableName<TTable extends PgTable> = TTable['_']['name'];
1079
- /**
1080
- * Abstract base repository for tenant-scoped database operations using Drizzle ORM.
1081
- * All operations are automatically scoped to the current tenant.
1165
+ * Request-scoped service that holds tenant context for the current request or RabbitMQ message
1082
1166
  *
1083
- * @template TTable - The Drizzle table type (must be registered in SchemaRegistry)
1084
- * @template TInsert - Type for insert operations (inferred from table.$inferInsert)
1167
+ * IMPORTANT: This service is REQUEST-SCOPED, meaning NestJS creates a new instance
1168
+ * for each HTTP request or RabbitMQ message. This ensures tenant isolation and
1169
+ * prevents cross-tenant data leaks in concurrent scenarios.
1170
+ *
1171
+ * @example
1172
+ * // In a controller or service
1173
+ * constructor(private readonly tenantContext: TenantContextService) {}
1174
+ *
1175
+ * async handleRequest() {
1176
+ * const tenant = this.tenantContext.getTenant();
1177
+ * console.log(`Processing request for tenant: ${tenant.tenantSlug}`);
1178
+ * }
1179
+ */
1180
+ declare class TenantContextService {
1181
+ private tenantInfo;
1182
+ /**
1183
+ * Set tenant information for this request/message
1184
+ *
1185
+ * This is typically called by:
1186
+ * - TenantContextInterceptor (for HTTP requests in gateway)
1187
+ * - MessageTenantContextInterceptor (for RabbitMQ messages in microservices)
1188
+ * - Manual context setup in message handlers
1189
+ *
1190
+ * @param tenantInfo Complete tenant information
1191
+ * @throws Error if tenant context is already set (prevents accidental overwrites)
1192
+ */
1193
+ setTenant(tenantInfo: TenantInfo): void;
1194
+ /**
1195
+ * Get tenant information for this request/message
1196
+ *
1197
+ * @returns Tenant information
1198
+ * @throws UnauthorizedException if tenant context hasn't been set
1199
+ */
1200
+ getTenant(): TenantInfo;
1201
+ /**
1202
+ * Check if tenant context has been set
1203
+ *
1204
+ * @returns true if tenant context is available
1205
+ */
1206
+ hasTenant(): boolean;
1207
+ /**
1208
+ * Clear tenant context
1209
+ *
1210
+ * This is useful for cleanup in RabbitMQ message handlers
1211
+ * after the message has been processed.
1212
+ *
1213
+ * HTTP requests don't need manual cleanup as the service
1214
+ * instance is destroyed when the request ends.
1215
+ */
1216
+ clearTenant(): void;
1217
+ /**
1218
+ * Get tenant ID safely (returns null if not set)
1219
+ *
1220
+ * @returns Tenant ID or null
1221
+ */
1222
+ getTenantIdSafe(): string | null;
1223
+ /**
1224
+ * Get tenant subdomain safely (returns null if not set)
1225
+ *
1226
+ * @returns Tenant subdomain or null
1227
+ */
1228
+ getTenantSubdomainSafe(): string | null;
1229
+ }
1230
+
1231
+ /**
1232
+ * Service responsible for managing tenant-scoped database connections
1233
+ *
1234
+ * This service:
1235
+ * - Maintains a connection pool (Map<cacheKey, TenantConnection>)
1236
+ * - Creates new connections dynamically based on tenant context
1237
+ * - Reuses existing connections for the same tenant
1238
+ * - Supports both cloud schemas and enterprise databases
1239
+ * - Automatically cleans up idle connections
1240
+ *
1241
+ * @example
1242
+ * // In a controller or service
1243
+ * const db = this.tenantDatabase.drizzleClient;
1244
+ * const users = await db.select().from(usersTable);
1245
+ */
1246
+ declare class TenantDatabaseService implements OnModuleDestroy {
1247
+ private readonly options;
1248
+ private readonly tenantContext;
1249
+ private readonly logger;
1250
+ /** Connection pool: Map<cacheKey, TenantConnection> */
1251
+ private readonly clients;
1252
+ /** Track last usage time for idle connection cleanup */
1253
+ private readonly clientLastUsed;
1254
+ /** Cleanup interval timer */
1255
+ private cleanupInterval?;
1256
+ constructor(options: DatabaseModuleOptions, tenantContext: TenantContextService);
1257
+ /**
1258
+ * Get the Drizzle client for the current tenant's database.
1259
+ * This returns the tenant-scoped database client.
1260
+ *
1261
+ * @returns Tenant-scoped Drizzle database instance
1262
+ * @throws UnauthorizedException if tenant context not set
1263
+ * @throws InternalServerErrorException if connection fails
1264
+ */
1265
+ get drizzleClient(): TypedDrizzleClient;
1266
+ /**
1267
+ * Get the Drizzle schema
1268
+ */
1269
+ get schema(): Record<string, unknown>;
1270
+ /**
1271
+ * Get tenant-scoped database client for the current request/message
1272
+ *
1273
+ * This method:
1274
+ * 1. Gets tenant info from TenantContextService
1275
+ * 2. Builds a connection URL based on tenant type
1276
+ * 3. Returns cached client if exists, otherwise creates new one
1277
+ *
1278
+ * @returns Drizzle database instance
1279
+ * @throws UnauthorizedException if tenant context not set
1280
+ * @throws InternalServerErrorException if connection fails
1281
+ */
1282
+ private getDbClient;
1283
+ /**
1284
+ * Create a new database client for the given tenant (synchronous)
1285
+ */
1286
+ private createDbClientSync;
1287
+ /**
1288
+ * Build connection URL for tenant (dedicated database)
1289
+ */
1290
+ private buildTenantDbUrl;
1291
+ /**
1292
+ * Build cache key for connection pooling
1293
+ */
1294
+ private buildCacheKey;
1295
+ /**
1296
+ * Start periodic cleanup of idle connections
1297
+ */
1298
+ private startConnectionCleaner;
1299
+ /**
1300
+ * Clean up idle connections that haven't been used recently
1301
+ */
1302
+ private cleanupIdleConnections;
1303
+ /**
1304
+ * Get current connection pool statistics
1305
+ */
1306
+ getPoolStats(): {
1307
+ activeConnections: number;
1308
+ tenants: string[];
1309
+ };
1310
+ /**
1311
+ * Mask password in connection URL for logging
1312
+ */
1313
+ private maskPassword;
1314
+ onModuleDestroy(): Promise<void>;
1315
+ }
1316
+
1317
+ /**
1318
+ * Type helper to extract table name from Drizzle table.
1319
+ * TTable['_']['name'] gives us the string literal type (e.g., 'products')
1320
+ */
1321
+ type ExtractTableName<TTable extends PgTable> = TTable['_']['name'];
1322
+ /**
1323
+ * Abstract base repository for tenant-scoped database operations using Drizzle ORM.
1324
+ * All operations are automatically scoped to the current tenant.
1325
+ *
1326
+ * @template TTable - The Drizzle table type (must be registered in SchemaRegistry)
1327
+ * @template TInsert - Type for insert operations (inferred from table.$inferInsert)
1085
1328
  * @template TSelect - Type for select operations (inferred from table.$inferSelect)
1086
1329
  *
1087
1330
  * @remarks
@@ -1321,350 +1564,30 @@ declare abstract class TenantBaseRepository<TTable extends PgTable, TInsert = In
1321
1564
  *
1322
1565
  * // Count all products
1323
1566
  * const total = await productRepository.count();
1324
- *
1325
- * // Count active products
1326
- * const activeCount = await productRepository.count(
1327
- * eq(products.status, 'ACTIVE')
1328
- * );
1329
- * ```
1330
- */
1331
- count(where?: SQL): Promise<number>;
1332
- /**
1333
- * Check if a record exists
1334
- *
1335
- * @param where - SQL condition to match records
1336
- * @returns Promise resolving to true if at least one record exists, false otherwise
1337
- *
1338
- * @example
1339
- * ```typescript
1340
- * import { eq } from 'drizzle-orm';
1341
- *
1342
- * const skuExists = await productRepository.exists(
1343
- * eq(products.sku, 'WDG-001')
1344
- * );
1345
- * ```
1346
- */
1347
- exists(where: SQL): Promise<boolean>;
1348
- }
1349
-
1350
- declare class RequestService {
1351
- private readonly request;
1352
- constructor(request: FastifyRequest);
1353
- /**
1354
- * Extract tenant identifier from request headers
1355
- * Priority: x-tenant-id > x-subdomain
1356
- * @returns Tenant identifier or null if not found
1357
- */
1358
- getTenantIdentifier(): string | null;
1359
- /**
1360
- * Extract access token from Authorization header
1361
- * Format: "Bearer <token>"
1362
- * @returns Access token or null if not found
1363
- */
1364
- getAccessToken(): string | null;
1365
- /**
1366
- * Extract refresh token from httpOnly cookie
1367
- * Cookie name is configurable via api-sdk config
1368
- * @returns Refresh token or null if not found
1369
- */
1370
- getRefreshToken(): string | null;
1371
- /**
1372
- * Get a specific header value
1373
- * @param key Header key
1374
- * @returns Header value (string, array, or undefined)
1375
- */
1376
- getHeader(key: string): string | string[] | undefined;
1377
- /**
1378
- * Get all headers
1379
- * @returns Record of all headers
1380
- */
1381
- getAllHeaders(): FastifyRequest['headers'];
1382
- }
1383
-
1384
- /**
1385
- * Vritti Authentication Guard - Validates JWT access tokens and tenant context
1386
- *
1387
- * This guard performs access token validation and attaches user data to request.
1388
- * NOTE: Refresh tokens are NOT validated here - they are only validated in
1389
- * /auth/token and /auth/refresh endpoints (session.service.ts).
1390
- *
1391
- * Validation Flow:
1392
- * 1. Checks if endpoint is marked with @Public() decorator → skip all validation
1393
- * 2. Checks if endpoint is marked with @Onboarding() decorator:
1394
- * - Requires token type='onboarding'
1395
- * - Validates JWT signature and expiry only
1396
- * - Skips tenant validation
1397
- * - Attaches user data to request.user
1398
- * 3. For regular endpoints (no decorator):
1399
- * - Rejects tokens with type='onboarding'
1400
- * - Validates access token (JWT signature, expiry, nbf)
1401
- * - Validates tenant exists and is ACTIVE
1402
- * - Attaches user data to request.user
1403
- *
1404
- * Token Format:
1405
- * - Access Token: "Authorization: Bearer <jwt_token>"
1406
- *
1407
- * Token Types:
1408
- * - type='onboarding': Limited access during registration flow (@Onboarding endpoints only)
1409
- * - type='access': Full access to authenticated endpoints
1410
- *
1411
- * Environment Variables Required:
1412
- * - JWT_SECRET: Secret key to verify access tokens (required)
1413
- *
1414
- * Error Responses:
1415
- * - 401: Invalid/expired access token
1416
- * - 401: Tenant not found or inactive
1417
- * - 401: Tenant identifier not found
1418
- * - 401: Token type mismatch (onboarding token on regular endpoint or vice versa)
1419
- *
1420
- * @example
1421
- * // Automatically registered by AuthConfigModule.forRootAsync()
1422
- * // No manual registration needed
1423
- * //
1424
- * // Internal registration uses useExisting pattern:
1425
- * // providers: [
1426
- * // VrittiAuthGuard,
1427
- * // {
1428
- * // provide: APP_GUARD,
1429
- * // useExisting: VrittiAuthGuard,
1430
- * // },
1431
- * // ]
1432
- *
1433
- * @example
1434
- * // Bypass guard with @Public() decorator
1435
- * @Public()
1436
- * @Post('auth/login')
1437
- * async login(@Body() dto: LoginDto) { ... }
1438
- *
1439
- * @example
1440
- * // Restrict to onboarding tokens with @Onboarding() decorator
1441
- * @Onboarding()
1442
- * @Post('onboarding/verify-email')
1443
- * async verifyEmail(@Request() req) {
1444
- * const userId = req.user.id; // Available from guard
1445
- * ...
1446
- * }
1447
- */
1448
- declare class VrittiAuthGuard implements CanActivate {
1449
- private readonly reflector;
1450
- private readonly configService;
1451
- private readonly jwtService;
1452
- private readonly primaryDatabase;
1453
- private readonly requestService;
1454
- private readonly logger;
1455
- constructor(reflector: Reflector, configService: ConfigService, jwtService: JwtService, primaryDatabase: PrimaryDatabaseService, requestService: RequestService);
1456
- canActivate(context: ExecutionContext): Promise<boolean>;
1457
- /**
1458
- * Validate access token with proper expiry checks
1459
- * Throws UnauthorizedException if token is invalid or expired
1460
- */
1461
- private validateAccessToken;
1462
- /**
1463
- * Validate that the access token is bound to the refresh token in the cookie.
1464
- * This prevents token theft - a stolen access token is useless without the
1465
- * corresponding refresh token cookie.
1466
- *
1467
- * @param context - The execution context containing the request
1468
- * @param validatedToken - The decoded and validated JWT token
1469
- * @throws UnauthorizedException if token binding validation fails
1470
- */
1471
- private validateRefreshTokenBinding;
1472
- }
1473
-
1474
- /**
1475
- * Parameter decorator that injects tenant metadata into controller method
1476
- *
1477
- * This decorator retrieves tenant information (ID, slug, type, etc.)
1478
- * from the REQUEST-SCOPED TenantContextService.
1479
- *
1480
- * Useful for:
1481
- * - Logging tenant-specific information
1482
- * - Implementing tenant-specific business logic
1483
- * - Auditing and tracking
1484
- * - Conditional feature flags
1485
- *
1486
- * @returns TenantInfo object with tenant metadata
1487
- *
1488
- * @example
1489
- * // Access tenant metadata
1490
- * @Get('info')
1491
- * async getTenantInfo(@Tenant() tenant: TenantInfo) {
1492
- * return {
1493
- * id: tenant.id,
1494
- * subdomain: tenant.subdomain,
1495
- * type: tenant.type,
1496
- * };
1497
- * }
1498
- *
1499
- * @example
1500
- * // Use for logging
1501
- * @Post()
1502
- * async createUser(
1503
- * @Body() dto: CreateUserDto,
1504
- * @Tenant() tenant: TenantInfo,
1505
- * ) {
1506
- * this.logger.log(`Creating user for tenant: ${tenant.subdomain}`);
1507
- * // ...
1508
- * }
1509
- *
1510
- * @example
1511
- * // Conditional business logic
1512
- * @Get('features')
1513
- * async getFeatures(@Tenant() tenant: TenantInfo) {
1514
- * if (tenant.type === 'ENTERPRISE') {
1515
- * return ['feature-a', 'feature-b', 'feature-c'];
1516
- * }
1517
- * return ['feature-a'];
1518
- * }
1519
- */
1520
- declare const Tenant: (...dataOrPipes: unknown[]) => ParameterDecorator;
1521
-
1522
- /**
1523
- * Onboarding Decorator - Marks endpoints that require onboarding token
1524
- *
1525
- * Use this decorator on controllers or route handlers that should only be
1526
- * accessible during the onboarding flow with JWT tokens containing type='onboarding'.
1527
- *
1528
- * These endpoints:
1529
- * - Accept ONLY tokens with type='onboarding'
1530
- * - Reject regular access tokens (type='access')
1531
- * - Skip tenant validation and refresh token checks
1532
- * - Only validate JWT signature and expiry
1533
- *
1534
- * Useful for:
1535
- * - Email/phone verification during onboarding
1536
- * - Onboarding status checks
1537
- * - Resending OTPs during registration
1538
- *
1539
- * @example
1540
- * // On a controller method
1541
- * @Post('verify-email')
1542
- * @Onboarding()
1543
- * async verifyEmail(@Request() req, @Body() dto: VerifyEmailDto) {
1544
- * const userId = req.user.id; // Available from VrittiAuthGuard
1545
- * return this.service.verifyEmail(userId, dto.otp);
1546
- * }
1547
- *
1548
- * @example
1549
- * // Multiple onboarding endpoints
1550
- * @Controller('onboarding')
1551
- * export class OnboardingController {
1552
- * @Post('verify-email')
1553
- * @Onboarding()
1554
- * async verifyEmail() { ... }
1555
- *
1556
- * @Post('resend-otp')
1557
- * @Onboarding()
1558
- * async resendOtp() { ... }
1559
- * }
1560
- */
1561
- declare const Onboarding: () => _nestjs_common.CustomDecorator<string>;
1562
-
1563
- /**
1564
- * Public Decorator - Marks endpoints that don't require authentication
1565
- *
1566
- * Use this decorator on controllers or route handlers to bypass VrittiAuthGuard
1567
- * tenant validation. Useful for:
1568
- * - Login/signup endpoints
1569
- * - Health checks
1570
- * - Public documentation endpoints
1571
- * - Webhook endpoints that don't require tenant context
1572
- *
1573
- * @example
1574
- * // On a controller method
1575
- * @Public()
1576
- * @Post('auth/login')
1577
- * async login(@Body() dto: LoginDto) {
1578
- * return this.authService.login(dto);
1579
- * }
1580
- *
1581
- * @example
1582
- * // On an entire controller
1583
- * @Public()
1584
- * @Controller('health')
1585
- * export class HealthController {
1586
- * @Get()
1587
- * check() {
1588
- * return { status: 'ok' };
1589
- * }
1590
- * }
1591
- */
1592
- declare const Public: () => _nestjs_common.CustomDecorator<string>;
1593
-
1594
- /**
1595
- * HTTP Module
1596
- *
1597
- * Provides HTTP utilities including:
1598
- * - CSRF Guard for request protection
1599
- * - HTTP Exception Filter for standardized error responses
1600
- *
1601
- * Usage:
1602
- * Import this module to access HTTP guards and filters.
1603
- * Guards and filters are registered globally in the main application.
1604
- */
1605
- declare class HttpModule {
1606
- }
1607
-
1608
- /**
1609
- * CSRF Guard
1610
- *
1611
- * Global guard that automatically protects all state-changing requests (POST, PUT, PATCH, DELETE)
1612
- * from CSRF attacks using Fastify's csrf-protection plugin.
1613
- *
1614
- * Flow:
1615
- * 1. Skip safe methods (GET, HEAD, OPTIONS)
1616
- * 2. Skip endpoints marked with @Public()
1617
- * 3. Validate CSRF token for all other requests
1618
- *
1619
- * Token Sources (in priority order by @fastify/csrf-protection):
1620
- * 1. req.headers['csrf-token']
1621
- * 2. req.headers['xsrf-token']
1622
- * 3. req.headers['x-csrf-token']
1623
- * 4. req.headers['x-xsrf-token']
1624
- * 5. req.body._csrf
1625
- *
1626
- * This guard should be registered globally in main.ts after CSRF plugin registration.
1627
- */
1628
- declare class CsrfGuard implements CanActivate {
1629
- private reflector;
1630
- private readonly logger;
1631
- constructor(reflector: Reflector);
1632
- canActivate(context: ExecutionContext): Promise<boolean>;
1633
- }
1634
-
1635
- /**
1636
- * Converts an HTTP status code to its corresponding title string.
1637
- * Uses the HttpStatus enum to map status codes to human-readable titles.
1638
- *
1639
- * @param status - The HTTP status code
1640
- * @returns The human-readable title for the status code
1641
- *
1642
- * @example
1643
- * getHttpStatusTitle(400) // Returns: "Bad Request"
1644
- * getHttpStatusTitle(404) // Returns: "Not Found"
1645
- * getHttpStatusTitle(500) // Returns: "Internal Server Error"
1646
- */
1647
- declare function getHttpStatusTitle(status: number): string;
1648
- /**
1649
- * Global HTTP Exception Filter implementing RFC 7807 Problem Details
1650
- *
1651
- * Transforms all exceptions into a standardized RFC 7807 format:
1652
- * {
1653
- * title: string, // Human-readable status title
1654
- * status: number, // HTTP status code
1655
- * detail: string, // Detailed error description
1656
- * errors: FieldError[] // Field-specific error messages
1657
- * }
1658
- *
1659
- * Handles:
1660
- * - Custom field exceptions from @vritti/api-sdk (BaseFieldException)
1661
- * - Class-validator DTO validation errors
1662
- * - Standard NestJS HTTP exceptions
1663
- * - Unknown errors
1664
- */
1665
- declare class HttpExceptionFilter implements ExceptionFilter {
1666
- private readonly logger;
1667
- catch(exception: unknown, host: ArgumentsHost): void;
1567
+ *
1568
+ * // Count active products
1569
+ * const activeCount = await productRepository.count(
1570
+ * eq(products.status, 'ACTIVE')
1571
+ * );
1572
+ * ```
1573
+ */
1574
+ count(where?: SQL): Promise<number>;
1575
+ /**
1576
+ * Check if a record exists
1577
+ *
1578
+ * @param where - SQL condition to match records
1579
+ * @returns Promise resolving to true if at least one record exists, false otherwise
1580
+ *
1581
+ * @example
1582
+ * ```typescript
1583
+ * import { eq } from 'drizzle-orm';
1584
+ *
1585
+ * const skuExists = await productRepository.exists(
1586
+ * eq(products.sku, 'WDG-001')
1587
+ * );
1588
+ * ```
1589
+ */
1590
+ exists(where: SQL): Promise<boolean>;
1668
1591
  }
1669
1592
 
1670
1593
  interface FieldError {
@@ -1684,6 +1607,29 @@ declare abstract class BaseFieldException extends HttpException {
1684
1607
  constructor(statusOrMessageOrErrors: HttpStatus | string | FieldError[], messageOrStatus?: string | HttpStatus, statusOrDetail?: HttpStatus | string, detail?: string);
1685
1608
  }
1686
1609
 
1610
+ /**
1611
+ * Exception thrown when a gateway or proxy receives an invalid response (HTTP 502).
1612
+ * Used when a server acting as a gateway gets an error from an upstream server.
1613
+ *
1614
+ * @example
1615
+ * // Simple message
1616
+ * throw new BadGatewayException('Bad gateway');
1617
+ *
1618
+ * // Field-specific error
1619
+ * throw new BadGatewayException('upstream', 'Upstream service returned invalid response');
1620
+ *
1621
+ * // With detail
1622
+ * throw new BadGatewayException('proxy', 'Gateway error', 'Payment service is not responding correctly');
1623
+ *
1624
+ * // Multiple field errors
1625
+ * throw new BadGatewayException([
1626
+ * { field: 'gateway', message: 'Invalid response from upstream server' }
1627
+ * ]);
1628
+ */
1629
+ declare class BadGatewayException extends BaseFieldException {
1630
+ constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1631
+ }
1632
+
1687
1633
  /**
1688
1634
  * Exception thrown when a request is malformed or contains invalid data (HTTP 400).
1689
1635
  *
@@ -1708,24 +1654,25 @@ declare class BadRequestException extends BaseFieldException {
1708
1654
  }
1709
1655
 
1710
1656
  /**
1711
- * Exception thrown when authentication is required or has failed (HTTP 401).
1657
+ * Exception thrown when a request conflicts with the current state (HTTP 409).
1658
+ * Commonly used for duplicate resources or concurrent modification issues.
1712
1659
  *
1713
1660
  * @example
1714
1661
  * // Simple message
1715
- * throw new UnauthorizedException('Authentication required');
1662
+ * throw new ConflictException('Resource already exists');
1716
1663
  *
1717
1664
  * // Field-specific error
1718
- * throw new UnauthorizedException('token', 'Invalid or expired token');
1665
+ * throw new ConflictException('email', 'Email already registered');
1719
1666
  *
1720
1667
  * // With detail
1721
- * throw new UnauthorizedException('token', 'Invalid token', 'Please login again');
1668
+ * throw new ConflictException('email', 'Email already exists', 'Try logging in instead');
1722
1669
  *
1723
1670
  * // Multiple field errors
1724
- * throw new UnauthorizedException([
1725
- * { field: 'token', message: 'Token expired' }
1671
+ * throw new ConflictException([
1672
+ * { field: 'email', message: 'Email already in use' }
1726
1673
  * ]);
1727
1674
  */
1728
- declare class UnauthorizedException extends BaseFieldException {
1675
+ declare class ConflictException extends BaseFieldException {
1729
1676
  constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1730
1677
  }
1731
1678
 
@@ -1752,275 +1699,276 @@ declare class ForbiddenException extends BaseFieldException {
1752
1699
  }
1753
1700
 
1754
1701
  /**
1755
- * Exception thrown when a requested resource cannot be found (HTTP 404).
1702
+ * Exception thrown when a resource has been permanently removed (HTTP 410).
1703
+ * Unlike 404, this indicates the resource existed but is intentionally gone.
1756
1704
  *
1757
1705
  * @example
1758
1706
  * // Simple message
1759
- * throw new NotFoundException('Resource not found');
1707
+ * throw new GoneException('Resource permanently deleted');
1760
1708
  *
1761
1709
  * // Field-specific error
1762
- * throw new NotFoundException('userId', 'User not found');
1710
+ * throw new GoneException('account', 'Account has been permanently deleted');
1763
1711
  *
1764
1712
  * // With detail
1765
- * throw new NotFoundException('userId', 'User not found', 'No user exists with the provided ID');
1713
+ * throw new GoneException('account', 'Deleted', 'This account was removed on user request');
1766
1714
  *
1767
1715
  * // Multiple field errors
1768
- * throw new NotFoundException([
1769
- * { field: 'userId', message: 'User does not exist' }
1716
+ * throw new GoneException([
1717
+ * { field: 'resource', message: 'This content has been permanently removed' }
1770
1718
  * ]);
1771
1719
  */
1772
- declare class NotFoundException extends BaseFieldException {
1720
+ declare class GoneException extends BaseFieldException {
1773
1721
  constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1774
1722
  }
1775
1723
 
1776
1724
  /**
1777
- * Exception thrown when a request conflicts with the current state (HTTP 409).
1778
- * Commonly used for duplicate resources or concurrent modification issues.
1725
+ * Exception thrown when an unexpected server error occurs (HTTP 500).
1779
1726
  *
1780
1727
  * @example
1781
1728
  * // Simple message
1782
- * throw new ConflictException('Resource already exists');
1729
+ * throw new InternalServerErrorException('An unexpected error occurred');
1783
1730
  *
1784
1731
  * // Field-specific error
1785
- * throw new ConflictException('email', 'Email already registered');
1732
+ * throw new InternalServerErrorException('database', 'Database connection failed');
1786
1733
  *
1787
1734
  * // With detail
1788
- * throw new ConflictException('email', 'Email already exists', 'Try logging in instead');
1735
+ * throw new InternalServerErrorException('database', 'Connection failed', 'Please try again later');
1789
1736
  *
1790
1737
  * // Multiple field errors
1791
- * throw new ConflictException([
1792
- * { field: 'email', message: 'Email already in use' }
1738
+ * throw new InternalServerErrorException([
1739
+ * { field: 'system', message: 'Internal error' }
1793
1740
  * ]);
1794
1741
  */
1795
- declare class ConflictException extends BaseFieldException {
1742
+ declare class InternalServerErrorException extends BaseFieldException {
1796
1743
  constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1797
1744
  }
1798
1745
 
1799
1746
  /**
1800
- * Exception thrown when an unexpected server error occurs (HTTP 500).
1747
+ * Exception thrown when an HTTP method is not supported for the endpoint (HTTP 405).
1748
+ * For example, when a POST is sent to a GET-only endpoint.
1801
1749
  *
1802
1750
  * @example
1803
1751
  * // Simple message
1804
- * throw new InternalServerErrorException('An unexpected error occurred');
1752
+ * throw new MethodNotAllowedException('Method not allowed');
1805
1753
  *
1806
1754
  * // Field-specific error
1807
- * throw new InternalServerErrorException('database', 'Database connection failed');
1755
+ * throw new MethodNotAllowedException('method', 'POST method not allowed on this endpoint');
1808
1756
  *
1809
1757
  * // With detail
1810
- * throw new InternalServerErrorException('database', 'Connection failed', 'Please try again later');
1758
+ * throw new MethodNotAllowedException('method', 'Not allowed', 'Only GET and PUT are supported');
1811
1759
  *
1812
1760
  * // Multiple field errors
1813
- * throw new InternalServerErrorException([
1814
- * { field: 'system', message: 'Internal error' }
1761
+ * throw new MethodNotAllowedException([
1762
+ * { field: 'method', message: 'DELETE is not allowed on this resource' }
1815
1763
  * ]);
1816
1764
  */
1817
- declare class InternalServerErrorException extends BaseFieldException {
1765
+ declare class MethodNotAllowedException extends BaseFieldException {
1818
1766
  constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1819
1767
  }
1820
1768
 
1821
1769
  /**
1822
- * Exception thrown when request validation fails (HTTP 400).
1823
- * Typically used for form validation or DTO validation errors.
1770
+ * Exception thrown when content negotiation fails (HTTP 406).
1771
+ * Used when the server cannot produce a response matching the Accept headers.
1824
1772
  *
1825
1773
  * @example
1826
- * // Multiple validation errors
1827
- * throw new ValidationException([
1828
- * { field: 'email', message: 'Invalid email format' },
1829
- * { field: 'password', message: 'Password must be at least 8 characters' }
1830
- * ]);
1774
+ * // Simple message
1775
+ * throw new NotAcceptableException('Requested format not available');
1776
+ *
1777
+ * // Field-specific error
1778
+ * throw new NotAcceptableException('accept', 'Cannot produce response in requested format');
1831
1779
  *
1832
1780
  * // With detail
1833
- * throw new ValidationException(
1834
- * [{ field: 'email', message: 'Invalid format' }],
1835
- * 'Please correct the errors and try again'
1836
- * );
1781
+ * throw new NotAcceptableException('accept', 'Format not supported', 'Only JSON is available');
1782
+ *
1783
+ * // Multiple field errors
1784
+ * throw new NotAcceptableException([
1785
+ * { field: 'contentType', message: 'XML format is not supported' }
1786
+ * ]);
1837
1787
  */
1838
- declare class ValidationException extends BaseFieldException {
1839
- constructor(errors: FieldError[], detail?: string);
1788
+ declare class NotAcceptableException extends BaseFieldException {
1789
+ constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1840
1790
  }
1841
1791
 
1842
1792
  /**
1843
- * Exception thrown when the request is well-formed but contains semantic errors (HTTP 422).
1844
- * Used for business logic validation failures that prevent processing.
1793
+ * Exception thrown when a requested resource cannot be found (HTTP 404).
1845
1794
  *
1846
1795
  * @example
1847
1796
  * // Simple message
1848
- * throw new UnprocessableEntityException('Cannot process the request');
1797
+ * throw new NotFoundException('Resource not found');
1849
1798
  *
1850
1799
  * // Field-specific error
1851
- * throw new UnprocessableEntityException('age', 'Age must be 18 or older');
1800
+ * throw new NotFoundException('userId', 'User not found');
1852
1801
  *
1853
1802
  * // With detail
1854
- * throw new UnprocessableEntityException('quantity', 'Insufficient stock', 'Only 5 items available');
1803
+ * throw new NotFoundException('userId', 'User not found', 'No user exists with the provided ID');
1855
1804
  *
1856
1805
  * // Multiple field errors
1857
- * throw new UnprocessableEntityException([
1858
- * { field: 'startDate', message: 'Start date must be before end date' },
1859
- * { field: 'endDate', message: 'End date cannot be in the past' }
1806
+ * throw new NotFoundException([
1807
+ * { field: 'userId', message: 'User does not exist' }
1860
1808
  * ]);
1861
1809
  */
1862
- declare class UnprocessableEntityException extends BaseFieldException {
1810
+ declare class NotFoundException extends BaseFieldException {
1863
1811
  constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1864
1812
  }
1865
1813
 
1866
1814
  /**
1867
- * Exception thrown when rate limiting is triggered (HTTP 429).
1868
- * Used to prevent abuse and ensure fair resource usage.
1815
+ * Exception thrown when a feature or endpoint is not yet implemented (HTTP 501).
1816
+ * Used for planned but unavailable functionality.
1869
1817
  *
1870
1818
  * @example
1871
1819
  * // Simple message
1872
- * throw new TooManyRequestsException('Too many requests');
1820
+ * throw new NotImplementedException('Feature not yet implemented');
1873
1821
  *
1874
1822
  * // Field-specific error
1875
- * throw new TooManyRequestsException('api', 'Rate limit exceeded');
1823
+ * throw new NotImplementedException('feature', 'This feature is coming soon');
1876
1824
  *
1877
1825
  * // With detail
1878
- * throw new TooManyRequestsException('api', 'Rate limit exceeded', 'Try again in 60 seconds');
1826
+ * throw new NotImplementedException('export', 'Not implemented', 'PDF export will be available in v2.0');
1879
1827
  *
1880
1828
  * // Multiple field errors
1881
- * throw new TooManyRequestsException([
1882
- * { field: 'requests', message: 'Rate limit exceeded for this endpoint' }
1829
+ * throw new NotImplementedException([
1830
+ * { field: 'functionality', message: 'This functionality is not available yet' }
1883
1831
  * ]);
1884
1832
  */
1885
- declare class TooManyRequestsException extends BaseFieldException {
1833
+ declare class NotImplementedException extends BaseFieldException {
1886
1834
  constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1887
1835
  }
1888
1836
 
1889
1837
  /**
1890
- * Exception thrown when the service is temporarily unavailable (HTTP 503).
1891
- * Used during maintenance, overload, or temporary outages.
1838
+ * Exception thrown when request payload exceeds size limits (HTTP 413).
1839
+ * Commonly used for file upload size restrictions or large request bodies.
1892
1840
  *
1893
1841
  * @example
1894
1842
  * // Simple message
1895
- * throw new ServiceUnavailableException('Service temporarily unavailable');
1843
+ * throw new PayloadTooLargeException('Request payload too large');
1896
1844
  *
1897
1845
  * // Field-specific error
1898
- * throw new ServiceUnavailableException('service', 'Scheduled maintenance in progress');
1846
+ * throw new PayloadTooLargeException('file', 'File size exceeds maximum allowed');
1899
1847
  *
1900
1848
  * // With detail
1901
- * throw new ServiceUnavailableException('service', 'Maintenance', 'Service will be back at 2 PM EST');
1849
+ * throw new PayloadTooLargeException('file', 'File too large', 'Maximum size is 10MB');
1902
1850
  *
1903
1851
  * // Multiple field errors
1904
- * throw new ServiceUnavailableException([
1905
- * { field: 'database', message: 'Database is temporarily unavailable' }
1852
+ * throw new PayloadTooLargeException([
1853
+ * { field: 'upload', message: 'File exceeds 10MB limit' }
1906
1854
  * ]);
1907
1855
  */
1908
- declare class ServiceUnavailableException extends BaseFieldException {
1856
+ declare class PayloadTooLargeException extends BaseFieldException {
1909
1857
  constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1910
1858
  }
1911
1859
 
1912
1860
  /**
1913
- * Exception thrown when an HTTP method is not supported for the endpoint (HTTP 405).
1914
- * For example, when a POST is sent to a GET-only endpoint.
1861
+ * Exception thrown when a request takes too long to process (HTTP 408).
1862
+ * Used when the client or server times out while waiting for completion.
1915
1863
  *
1916
1864
  * @example
1917
1865
  * // Simple message
1918
- * throw new MethodNotAllowedException('Method not allowed');
1866
+ * throw new RequestTimeoutException('Request timeout');
1919
1867
  *
1920
1868
  * // Field-specific error
1921
- * throw new MethodNotAllowedException('method', 'POST method not allowed on this endpoint');
1869
+ * throw new RequestTimeoutException('operation', 'Operation timed out');
1922
1870
  *
1923
1871
  * // With detail
1924
- * throw new MethodNotAllowedException('method', 'Not allowed', 'Only GET and PUT are supported');
1872
+ * throw new RequestTimeoutException('query', 'Database query timeout', 'Try with fewer filters');
1925
1873
  *
1926
1874
  * // Multiple field errors
1927
- * throw new MethodNotAllowedException([
1928
- * { field: 'method', message: 'DELETE is not allowed on this resource' }
1875
+ * throw new RequestTimeoutException([
1876
+ * { field: 'processing', message: 'Request took too long to complete' }
1929
1877
  * ]);
1930
1878
  */
1931
- declare class MethodNotAllowedException extends BaseFieldException {
1879
+ declare class RequestTimeoutException extends BaseFieldException {
1932
1880
  constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1933
1881
  }
1934
1882
 
1935
1883
  /**
1936
- * Exception thrown when a resource has been permanently removed (HTTP 410).
1937
- * Unlike 404, this indicates the resource existed but is intentionally gone.
1884
+ * Exception thrown when the service is temporarily unavailable (HTTP 503).
1885
+ * Used during maintenance, overload, or temporary outages.
1938
1886
  *
1939
1887
  * @example
1940
1888
  * // Simple message
1941
- * throw new GoneException('Resource permanently deleted');
1889
+ * throw new ServiceUnavailableException('Service temporarily unavailable');
1942
1890
  *
1943
1891
  * // Field-specific error
1944
- * throw new GoneException('account', 'Account has been permanently deleted');
1892
+ * throw new ServiceUnavailableException('service', 'Scheduled maintenance in progress');
1945
1893
  *
1946
1894
  * // With detail
1947
- * throw new GoneException('account', 'Deleted', 'This account was removed on user request');
1895
+ * throw new ServiceUnavailableException('service', 'Maintenance', 'Service will be back at 2 PM EST');
1948
1896
  *
1949
1897
  * // Multiple field errors
1950
- * throw new GoneException([
1951
- * { field: 'resource', message: 'This content has been permanently removed' }
1898
+ * throw new ServiceUnavailableException([
1899
+ * { field: 'database', message: 'Database is temporarily unavailable' }
1952
1900
  * ]);
1953
1901
  */
1954
- declare class GoneException extends BaseFieldException {
1902
+ declare class ServiceUnavailableException extends BaseFieldException {
1955
1903
  constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1956
1904
  }
1957
1905
 
1958
1906
  /**
1959
- * Exception thrown when content negotiation fails (HTTP 406).
1960
- * Used when the server cannot produce a response matching the Accept headers.
1907
+ * Exception thrown when rate limiting is triggered (HTTP 429).
1908
+ * Used to prevent abuse and ensure fair resource usage.
1961
1909
  *
1962
1910
  * @example
1963
1911
  * // Simple message
1964
- * throw new NotAcceptableException('Requested format not available');
1912
+ * throw new TooManyRequestsException('Too many requests');
1965
1913
  *
1966
1914
  * // Field-specific error
1967
- * throw new NotAcceptableException('accept', 'Cannot produce response in requested format');
1915
+ * throw new TooManyRequestsException('api', 'Rate limit exceeded');
1968
1916
  *
1969
1917
  * // With detail
1970
- * throw new NotAcceptableException('accept', 'Format not supported', 'Only JSON is available');
1918
+ * throw new TooManyRequestsException('api', 'Rate limit exceeded', 'Try again in 60 seconds');
1971
1919
  *
1972
1920
  * // Multiple field errors
1973
- * throw new NotAcceptableException([
1974
- * { field: 'contentType', message: 'XML format is not supported' }
1921
+ * throw new TooManyRequestsException([
1922
+ * { field: 'requests', message: 'Rate limit exceeded for this endpoint' }
1975
1923
  * ]);
1976
1924
  */
1977
- declare class NotAcceptableException extends BaseFieldException {
1925
+ declare class TooManyRequestsException extends BaseFieldException {
1978
1926
  constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1979
1927
  }
1980
1928
 
1981
1929
  /**
1982
- * Exception thrown when a request takes too long to process (HTTP 408).
1983
- * Used when the client or server times out while waiting for completion.
1930
+ * Exception thrown when authentication is required or has failed (HTTP 401).
1984
1931
  *
1985
1932
  * @example
1986
1933
  * // Simple message
1987
- * throw new RequestTimeoutException('Request timeout');
1934
+ * throw new UnauthorizedException('Authentication required');
1988
1935
  *
1989
1936
  * // Field-specific error
1990
- * throw new RequestTimeoutException('operation', 'Operation timed out');
1937
+ * throw new UnauthorizedException('token', 'Invalid or expired token');
1991
1938
  *
1992
1939
  * // With detail
1993
- * throw new RequestTimeoutException('query', 'Database query timeout', 'Try with fewer filters');
1940
+ * throw new UnauthorizedException('token', 'Invalid token', 'Please login again');
1994
1941
  *
1995
1942
  * // Multiple field errors
1996
- * throw new RequestTimeoutException([
1997
- * { field: 'processing', message: 'Request took too long to complete' }
1943
+ * throw new UnauthorizedException([
1944
+ * { field: 'token', message: 'Token expired' }
1998
1945
  * ]);
1999
1946
  */
2000
- declare class RequestTimeoutException extends BaseFieldException {
1947
+ declare class UnauthorizedException extends BaseFieldException {
2001
1948
  constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
2002
1949
  }
2003
1950
 
2004
1951
  /**
2005
- * Exception thrown when request payload exceeds size limits (HTTP 413).
2006
- * Commonly used for file upload size restrictions or large request bodies.
1952
+ * Exception thrown when the request is well-formed but contains semantic errors (HTTP 422).
1953
+ * Used for business logic validation failures that prevent processing.
2007
1954
  *
2008
1955
  * @example
2009
1956
  * // Simple message
2010
- * throw new PayloadTooLargeException('Request payload too large');
1957
+ * throw new UnprocessableEntityException('Cannot process the request');
2011
1958
  *
2012
1959
  * // Field-specific error
2013
- * throw new PayloadTooLargeException('file', 'File size exceeds maximum allowed');
1960
+ * throw new UnprocessableEntityException('age', 'Age must be 18 or older');
2014
1961
  *
2015
1962
  * // With detail
2016
- * throw new PayloadTooLargeException('file', 'File too large', 'Maximum size is 10MB');
1963
+ * throw new UnprocessableEntityException('quantity', 'Insufficient stock', 'Only 5 items available');
2017
1964
  *
2018
1965
  * // Multiple field errors
2019
- * throw new PayloadTooLargeException([
2020
- * { field: 'upload', message: 'File exceeds 10MB limit' }
1966
+ * throw new UnprocessableEntityException([
1967
+ * { field: 'startDate', message: 'Start date must be before end date' },
1968
+ * { field: 'endDate', message: 'End date cannot be in the past' }
2021
1969
  * ]);
2022
1970
  */
2023
- declare class PayloadTooLargeException extends BaseFieldException {
1971
+ declare class UnprocessableEntityException extends BaseFieldException {
2024
1972
  constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
2025
1973
  }
2026
1974
 
@@ -2048,49 +1996,98 @@ declare class UnsupportedMediaTypeException extends BaseFieldException {
2048
1996
  }
2049
1997
 
2050
1998
  /**
2051
- * Exception thrown when a feature or endpoint is not yet implemented (HTTP 501).
2052
- * Used for planned but unavailable functionality.
1999
+ * Exception thrown when request validation fails (HTTP 400).
2000
+ * Typically used for form validation or DTO validation errors.
2053
2001
  *
2054
2002
  * @example
2055
- * // Simple message
2056
- * throw new NotImplementedException('Feature not yet implemented');
2057
- *
2058
- * // Field-specific error
2059
- * throw new NotImplementedException('feature', 'This feature is coming soon');
2003
+ * // Multiple validation errors
2004
+ * throw new ValidationException([
2005
+ * { field: 'email', message: 'Invalid email format' },
2006
+ * { field: 'password', message: 'Password must be at least 8 characters' }
2007
+ * ]);
2060
2008
  *
2061
2009
  * // With detail
2062
- * throw new NotImplementedException('export', 'Not implemented', 'PDF export will be available in v2.0');
2063
- *
2064
- * // Multiple field errors
2065
- * throw new NotImplementedException([
2066
- * { field: 'functionality', message: 'This functionality is not available yet' }
2067
- * ]);
2010
+ * throw new ValidationException(
2011
+ * [{ field: 'email', message: 'Invalid format' }],
2012
+ * 'Please correct the errors and try again'
2013
+ * );
2068
2014
  */
2069
- declare class NotImplementedException extends BaseFieldException {
2070
- constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
2015
+ declare class ValidationException extends BaseFieldException {
2016
+ constructor(errors: FieldError[], detail?: string);
2071
2017
  }
2072
2018
 
2073
2019
  /**
2074
- * Exception thrown when a gateway or proxy receives an invalid response (HTTP 502).
2075
- * Used when a server acting as a gateway gets an error from an upstream server.
2020
+ * Converts an HTTP status code to its corresponding title string.
2021
+ * Uses the HttpStatus enum to map status codes to human-readable titles.
2022
+ *
2023
+ * @param status - The HTTP status code
2024
+ * @returns The human-readable title for the status code
2076
2025
  *
2077
2026
  * @example
2078
- * // Simple message
2079
- * throw new BadGatewayException('Bad gateway');
2027
+ * getHttpStatusTitle(400) // Returns: "Bad Request"
2028
+ * getHttpStatusTitle(404) // Returns: "Not Found"
2029
+ * getHttpStatusTitle(500) // Returns: "Internal Server Error"
2030
+ */
2031
+ declare function getHttpStatusTitle(status: number): string;
2032
+ /**
2033
+ * Global HTTP Exception Filter implementing RFC 7807 Problem Details
2080
2034
  *
2081
- * // Field-specific error
2082
- * throw new BadGatewayException('upstream', 'Upstream service returned invalid response');
2035
+ * Transforms all exceptions into a standardized RFC 7807 format:
2036
+ * {
2037
+ * title: string, // Human-readable status title
2038
+ * status: number, // HTTP status code
2039
+ * detail: string, // Detailed error description
2040
+ * errors: FieldError[] // Field-specific error messages
2041
+ * }
2083
2042
  *
2084
- * // With detail
2085
- * throw new BadGatewayException('proxy', 'Gateway error', 'Payment service is not responding correctly');
2043
+ * Handles:
2044
+ * - Custom field exceptions from @vritti/api-sdk (BaseFieldException)
2045
+ * - Class-validator DTO validation errors
2046
+ * - Standard NestJS HTTP exceptions
2047
+ * - Unknown errors
2048
+ */
2049
+ declare class HttpExceptionFilter implements ExceptionFilter {
2050
+ private readonly logger;
2051
+ catch(exception: unknown, host: ArgumentsHost): void;
2052
+ }
2053
+
2054
+ /**
2055
+ * CSRF Guard
2086
2056
  *
2087
- * // Multiple field errors
2088
- * throw new BadGatewayException([
2089
- * { field: 'gateway', message: 'Invalid response from upstream server' }
2090
- * ]);
2057
+ * Global guard that automatically protects all state-changing requests (POST, PUT, PATCH, DELETE)
2058
+ * from CSRF attacks using Fastify's csrf-protection plugin.
2059
+ *
2060
+ * Flow:
2061
+ * 1. Skip safe methods (GET, HEAD, OPTIONS)
2062
+ * 2. Skip endpoints marked with @Public()
2063
+ * 3. Validate CSRF token for all other requests
2064
+ *
2065
+ * Token Sources (in priority order by @fastify/csrf-protection):
2066
+ * 1. req.headers['csrf-token']
2067
+ * 2. req.headers['xsrf-token']
2068
+ * 3. req.headers['x-csrf-token']
2069
+ * 4. req.headers['x-xsrf-token']
2070
+ * 5. req.body._csrf
2071
+ *
2072
+ * This guard should be registered globally in main.ts after CSRF plugin registration.
2091
2073
  */
2092
- declare class BadGatewayException extends BaseFieldException {
2093
- constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
2074
+ declare class CsrfGuard implements CanActivate {
2075
+ private readonly logger;
2076
+ canActivate(context: ExecutionContext): Promise<boolean>;
2077
+ }
2078
+
2079
+ /**
2080
+ * HTTP Module
2081
+ *
2082
+ * Provides HTTP utilities including:
2083
+ * - CSRF Guard for request protection
2084
+ * - HTTP Exception Filter for standardized error responses
2085
+ *
2086
+ * Usage:
2087
+ * Import this module to access HTTP guards and filters.
2088
+ * Guards and filters are registered globally in the main application.
2089
+ */
2090
+ declare class HttpModule {
2094
2091
  }
2095
2092
 
2096
2093
  /**
@@ -2167,6 +2164,77 @@ interface HttpLoggerOptions {
2167
2164
  maxBodySize?: number;
2168
2165
  }
2169
2166
 
2167
+ /**
2168
+ * Unified Logger Service
2169
+ *
2170
+ * Single service that provides both default NestJS Logger and Winston logger implementations.
2171
+ * Automatically delegates to the configured provider (default or winston).
2172
+ * @module logger/logger.service
2173
+ */
2174
+
2175
+ /**
2176
+ * Unified logger service implementing NestJS LoggerService interface.
2177
+ * Supports both default NestJS Logger and Winston implementations via facade pattern.
2178
+ */
2179
+ declare class LoggerService implements LoggerService$1 {
2180
+ private readonly defaultLogger?;
2181
+ private readonly activeLogger;
2182
+ private readonly options;
2183
+ private context?;
2184
+ constructor(options?: LoggerModuleOptions, defaultLogger?: Logger | undefined);
2185
+ /**
2186
+ * Creates a Winston logger instance with inline configuration.
2187
+ * Consolidates winston-config.factory.ts logic.
2188
+ */
2189
+ private createWinstonLogger;
2190
+ log(message: any, context?: string): void;
2191
+ error(message: any, trace?: string, context?: string): void;
2192
+ warn(message: any, context?: string): void;
2193
+ debug(message: any, context?: string): void;
2194
+ verbose(message: any, context?: string): void;
2195
+ setContext(context: string): void;
2196
+ /**
2197
+ * Unified internal logging method that handles both Winston and NestJS Logger.
2198
+ */
2199
+ private _log;
2200
+ /**
2201
+ * Logs with custom metadata (Winston only).
2202
+ */
2203
+ logWithMetadata(level: LogLevel, message: any, metadata?: LogMetadata, context?: string): void;
2204
+ private formatMessage;
2205
+ /**
2206
+ * Enriches metadata with correlation context from AsyncLocalStorage.
2207
+ * Inline from winston-logger.service.ts
2208
+ */
2209
+ private enrichMetadata;
2210
+ child(context: string): LoggerService;
2211
+ }
2212
+
2213
+ /**
2214
+ * HTTP Logger Interceptor
2215
+ *
2216
+ * Automatically logs HTTP requests and responses with correlation tracking.
2217
+ * @module logger/http-logger.interceptor
2218
+ */
2219
+
2220
+ /**
2221
+ * HTTP Logger Interceptor for NestJS applications.
2222
+ *
2223
+ * Logs all HTTP requests and responses with metadata including
2224
+ * correlation IDs, performance metrics, and error details.
2225
+ */
2226
+ declare class HttpLoggerInterceptor implements NestInterceptor {
2227
+ private readonly logger;
2228
+ private readonly enableRequestLog;
2229
+ private readonly enableResponseLog;
2230
+ private readonly slowRequestThreshold;
2231
+ constructor(logger: LoggerService, options?: HttpLoggerOptions);
2232
+ intercept(context: ExecutionContext, next: CallHandler): Observable<any>;
2233
+ private logRequest;
2234
+ private logResponse;
2235
+ private logError;
2236
+ }
2237
+
2170
2238
  /**
2171
2239
  * Logger Module
2172
2240
  *
@@ -2329,7 +2397,7 @@ declare class LoggerModule implements NestModule {
2329
2397
  * Configures middleware for the module.
2330
2398
  * Middleware is registered globally in main.ts using Fastify hooks.
2331
2399
  */
2332
- configure(consumer: MiddlewareConsumer): void;
2400
+ configure(_consumer: MiddlewareConsumer): void;
2333
2401
  /**
2334
2402
  * Creates async providers for dynamic module configuration.
2335
2403
  */
@@ -2340,52 +2408,6 @@ declare class LoggerModule implements NestModule {
2340
2408
  private static createAsyncOptionsProvider;
2341
2409
  }
2342
2410
 
2343
- /**
2344
- * Unified Logger Service
2345
- *
2346
- * Single service that provides both default NestJS Logger and Winston logger implementations.
2347
- * Automatically delegates to the configured provider (default or winston).
2348
- * @module logger/logger.service
2349
- */
2350
-
2351
- /**
2352
- * Unified logger service implementing NestJS LoggerService interface.
2353
- * Supports both default NestJS Logger and Winston implementations via facade pattern.
2354
- */
2355
- declare class LoggerService implements LoggerService$1 {
2356
- private readonly defaultLogger?;
2357
- private readonly activeLogger;
2358
- private readonly options;
2359
- private context?;
2360
- constructor(options?: LoggerModuleOptions, defaultLogger?: Logger | undefined);
2361
- /**
2362
- * Creates a Winston logger instance with inline configuration.
2363
- * Consolidates winston-config.factory.ts logic.
2364
- */
2365
- private createWinstonLogger;
2366
- log(message: any, context?: string): void;
2367
- error(message: any, trace?: string, context?: string): void;
2368
- warn(message: any, context?: string): void;
2369
- debug(message: any, context?: string): void;
2370
- verbose(message: any, context?: string): void;
2371
- setContext(context: string): void;
2372
- /**
2373
- * Unified internal logging method that handles both Winston and NestJS Logger.
2374
- */
2375
- private _log;
2376
- /**
2377
- * Logs with custom metadata (Winston only).
2378
- */
2379
- logWithMetadata(level: LogLevel, message: any, metadata?: LogMetadata, context?: string): void;
2380
- private formatMessage;
2381
- /**
2382
- * Enriches metadata with correlation context from AsyncLocalStorage.
2383
- * Inline from winston-logger.service.ts
2384
- */
2385
- private enrichMetadata;
2386
- child(context: string): LoggerService;
2387
- }
2388
-
2389
2411
  /**
2390
2412
  * Correlation ID Middleware
2391
2413
  *
@@ -2423,38 +2445,13 @@ declare class CorrelationIdMiddleware implements NestMiddleware {
2423
2445
  /**
2424
2446
  * Middleware handler for processing requests.
2425
2447
  */
2426
- use(req: FastifyRequest, reply: FastifyReply, next: () => void): void;
2448
+ use(_req: FastifyRequest, reply: FastifyReply, next: () => void): void;
2427
2449
  /**
2428
2450
  * Fastify hook handler for onRequest.
2429
2451
  * This is an async function that returns a Promise, ensuring the AsyncLocalStorage
2430
2452
  * context persists throughout the entire request lifecycle.
2431
2453
  */
2432
- onRequest(req: FastifyRequest, reply: FastifyReply): Promise<void>;
2433
- }
2434
-
2435
- /**
2436
- * HTTP Logger Interceptor
2437
- *
2438
- * Automatically logs HTTP requests and responses with correlation tracking.
2439
- * @module logger/http-logger.interceptor
2440
- */
2441
-
2442
- /**
2443
- * HTTP Logger Interceptor for NestJS applications.
2444
- *
2445
- * Logs all HTTP requests and responses with metadata including
2446
- * correlation IDs, performance metrics, and error details.
2447
- */
2448
- declare class HttpLoggerInterceptor implements NestInterceptor {
2449
- private readonly logger;
2450
- private readonly enableRequestLog;
2451
- private readonly enableResponseLog;
2452
- private readonly slowRequestThreshold;
2453
- constructor(logger: LoggerService, options?: HttpLoggerOptions);
2454
- intercept(context: ExecutionContext, next: CallHandler): Observable<any>;
2455
- private logRequest;
2456
- private logResponse;
2457
- private logError;
2454
+ onRequest(_req: FastifyRequest, reply: FastifyReply): Promise<void>;
2458
2455
  }
2459
2456
 
2460
2457
  /**
@@ -2494,4 +2491,4 @@ declare function generateCorrelationId(): string;
2494
2491
  */
2495
2492
  declare function addCorrelationIdToResponse(reply: FastifyReply, correlationId: string, headerName?: string): void;
2496
2493
 
2497
- export { type ApiErrorResponse, type ApiSdkConfig, AuthConfigModule, BadGatewayException, BadRequestException, BaseFieldException, ConflictException, type CookieConfig, type CorrelationContext, CorrelationIdMiddleware, CsrfGuard, DEFAULT_CORRELATION_HEADER, DatabaseModule, type DatabaseModuleOptions, type FieldError, ForbiddenException, GoneException, type GuardConfig, HttpExceptionFilter, HttpLoggerInterceptor, type HttpLoggerOptions, HttpModule, InternalServerErrorException, type JwtConfig, LOGGER_MODULE_OPTIONS, type LogFormat, type LogLevel, type LogMetadata, LoggerModule, type LoggerModuleAsyncOptions, type LoggerModuleOptions, type LoggerOptionsFactory, LoggerService, MethodNotAllowedException, NotAcceptableException, NotFoundException, NotImplementedException, Onboarding, PayloadTooLargeException, PrimaryBaseRepository, PrimaryDatabaseService, type PrimaryDbConfig, type ProblemDetails, Public, type RegisteredSchema, RequestTimeoutException, type SchemaRegistry, ServiceUnavailableException, Tenant, TenantBaseRepository, TenantContextService, TenantDatabaseService, type TenantInfo, TooManyRequestsException, type TypedDrizzleClient, UnauthorizedException, UnprocessableEntityException, UnsupportedMediaTypeException, ValidationException, VrittiAuthGuard, addCorrelationIdToResponse, configureApiSdk, correlationStorage, defineConfig, generateCorrelationId, getConfig, getCorrelationContext, getHttpStatusTitle, getJwtExpiry, getRefreshCookieOptions, hashToken, resetConfig, runWithCorrelationContext, updateCorrelationContext, verifyTokenHash };
2494
+ export { type ApiErrorResponse, type ApiSdkConfig, AuthConfigModule, BadGatewayException, BadRequestException, BaseFieldException, ConflictException, type CookieConfig, type CorrelationContext, CorrelationIdMiddleware, CsrfGuard, DEFAULT_CORRELATION_HEADER, DatabaseModule, type DatabaseModuleOptions, type FieldError, ForbiddenException, GoneException, type GuardConfig, HttpExceptionFilter, HttpLoggerInterceptor, type HttpLoggerOptions, HttpModule, InternalServerErrorException, type JwtConfig, LOGGER_MODULE_OPTIONS, type LogFormat, type LogLevel, type LogMetadata, LoggerModule, type LoggerModuleAsyncOptions, type LoggerModuleOptions, type LoggerOptionsFactory, LoggerService, MethodNotAllowedException, NotAcceptableException, NotFoundException, NotImplementedException, Onboarding, PayloadTooLargeException, PrimaryBaseRepository, PrimaryDatabaseService, type PrimaryDbConfig, type ProblemDetails, Public, type RegisteredSchema, RequestTimeoutException, ServiceUnavailableException, Tenant, TenantBaseRepository, TenantContextService, TenantDatabaseService, type TenantInfo, TooManyRequestsException, type TypedDrizzleClient, UnauthorizedException, UnprocessableEntityException, UnsupportedMediaTypeException, ValidationException, VrittiAuthGuard, addCorrelationIdToResponse, configureApiSdk, correlationStorage, defineConfig, generateCorrelationId, getConfig, getCorrelationContext, getHttpStatusTitle, getJwtExpiry, getRefreshCookieOptions, hashToken, resetConfig, runWithCorrelationContext, updateCorrelationContext, verifyTokenHash };