@vritti/api-sdk 0.1.1 → 0.1.3

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,12 +1,12 @@
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
 
@@ -84,6 +84,78 @@ declare class AuthConfigModule {
84
84
  static forRootAsync(): DynamicModule;
85
85
  }
86
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
+
87
159
  /**
88
160
  * Schema Registry Interface
89
161
  *
@@ -98,8 +170,7 @@ declare class AuthConfigModule {
98
170
  * }
99
171
  * }
100
172
  */
101
- interface SchemaRegistry {
102
- }
173
+ type SchemaRegistry = {};
103
174
  /**
104
175
  * Extracts the registered schema type.
105
176
  * Falls back to Record<string, unknown> if no schema is registered.
@@ -209,358 +280,554 @@ interface TenantInfo {
209
280
  }
210
281
 
211
282
  /**
212
- * Dynamic module for multi-tenant database management
213
- *
214
- * This module provides:
215
- * - Tenant context management (request-scoped)
216
- * - Database connection pooling
217
- * - Dynamic schema/cluster routing
218
- * - Support for both gateway and microservice modes
219
- *
220
- * ## Gateway Mode (API Gateway)
221
- * - Use DatabaseModule.forServer() method
222
- * - Automatically extracts tenant from subdomain, falls back to x-tenant-id header
223
- * - Provide primaryDb configuration and prismaClientConstructor
224
- * - Automatically queries primary DB for tenant config
225
- * - Automatically registers TenantContextInterceptor globally
226
- * - No manual interceptor registration needed
227
- *
228
- * ## Microservice Mode (RabbitMQ Workers)
229
- * - Use DatabaseModule.forMicroservice() method
230
- * - Only provide prismaClientConstructor
231
- * - Tenant context comes from RabbitMQ messages
232
- * - Automatically registers MessageTenantContextInterceptor globally
233
- * - No manual interceptor registration needed
283
+ * Service responsible for querying the primary database to resolve tenant configurations
234
284
  *
235
- * @example
236
- * // Gateway configuration
237
- * DatabaseModule.forServer({
238
- * inject: [ConfigService],
239
- * useFactory: (config: ConfigService) => ({
240
- * primaryDb: {
241
- * host: config.get('PRIMARY_DB_HOST'),
242
- * port: config.get('PRIMARY_DB_PORT'),
243
- * username: config.get('PRIMARY_DB_USERNAME'),
244
- * password: config.get('PRIMARY_DB_PASSWORD'),
245
- * database: config.get('PRIMARY_DB_DATABASE'),
246
- * },
247
- * prismaClientConstructor: PrismaClient,
248
- * }),
249
- * })
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)
250
290
  *
251
291
  * @example
252
- * // Microservice configuration
253
- * DatabaseModule.forMicroservice({
254
- * inject: [ConfigService],
255
- * useFactory: (config: ConfigService) => ({
256
- * prismaClientConstructor: PrismaClient,
257
- * }),
258
- * })
292
+ * // In API Gateway
293
+ * const config = await primaryDatabase.getTenantConfig('acme');
294
+ * // Returns: { id, slug, type, databaseHost, databaseName, ... }
259
295
  */
260
- declare class DatabaseModule {
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>;
261
309
  /**
262
- * Configure DatabaseModule for Gateway/HTTP mode (multi-tenant web servers)
263
- *
264
- * This mode is for API Gateways that handle HTTP requests:
265
- * - Automatically registers TenantContextInterceptor
266
- * - Extracts tenant from subdomain or x-tenant-id header
267
- * - Queries primary database for tenant configuration
268
- * - Provides PrimaryDatabaseService for tenant lookup
269
- *
270
- * @param options Async configuration options
271
- * @returns Dynamic module configuration with HTTP interceptor
272
- *
273
- * @example
274
- * DatabaseModule.forServer({
275
- * inject: [ConfigService],
276
- * useFactory: (config: ConfigService) => ({
277
- * primaryDb: {
278
- * host: config.get('PRIMARY_DB_HOST'),
279
- * port: config.get('PRIMARY_DB_PORT'),
280
- * username: config.get('PRIMARY_DB_USERNAME'),
281
- * password: config.get('PRIMARY_DB_PASSWORD'),
282
- * database: config.get('PRIMARY_DB_DATABASE'),
283
- * },
284
- * prismaClientConstructor: PrismaClient,
285
- * }),
286
- * })
310
+ * Initialize connection to primary database using Drizzle
287
311
  */
288
- static forServer(options: {
289
- useFactory: (...args: any[]) => Promise<DatabaseModuleOptions> | DatabaseModuleOptions;
290
- inject?: any[];
291
- }): DynamicModule;
312
+ private initializeDrizzleClient;
292
313
  /**
293
- * Configure DatabaseModule for Microservice/Messaging mode (RabbitMQ workers)
294
- *
295
- * This mode is for microservices that process messages from queues:
296
- * - Automatically registers MessageTenantContextInterceptor
297
- * - Extracts tenant from RabbitMQ message patterns
298
- * - No primary database needed (tenant comes from message context)
299
- *
300
- * @param options Async configuration options
301
- * @returns Dynamic module configuration with message interceptor
302
- *
303
- * @example
304
- * DatabaseModule.forMicroservice({
305
- * inject: [ConfigService],
306
- * useFactory: (config: ConfigService) => ({
307
- * prismaClientConstructor: PrismaClient,
308
- * }),
309
- * })
314
+ * Build connection URL from primary database properties
310
315
  */
311
- static forMicroservice(options: {
312
- useFactory: (...args: any[]) => Promise<DatabaseModuleOptions> | DatabaseModuleOptions;
313
- inject?: any[];
314
- }): DynamicModule;
316
+ private buildPrimaryDbUrl;
315
317
  /**
316
- * Internal helper to create dynamic module with conditional interceptor registration
317
- *
318
- * @param options Configuration options
319
- * @param mode Mode of operation (gateway or microservice)
320
- * @returns Dynamic module configuration
318
+ * Mask password in connection URL for logging
321
319
  */
322
- private static createDynamicModule;
323
- }
324
-
325
- /**
326
- * Request-scoped service that holds tenant context for the current request or RabbitMQ message
327
- *
328
- * IMPORTANT: This service is REQUEST-SCOPED, meaning NestJS creates a new instance
329
- * for each HTTP request or RabbitMQ message. This ensures tenant isolation and
330
- * prevents cross-tenant data leaks in concurrent scenarios.
331
- *
332
- * @example
333
- * // In a controller or service
334
- * constructor(private readonly tenantContext: TenantContextService) {}
335
- *
336
- * async handleRequest() {
337
- * const tenant = this.tenantContext.getTenant();
338
- * console.log(`Processing request for tenant: ${tenant.tenantSlug}`);
339
- * }
340
- */
341
- declare class TenantContextService {
342
- private tenantInfo;
320
+ private maskPassword;
343
321
  /**
344
- * Set tenant information for this request/message
345
- *
346
- * This is typically called by:
347
- * - TenantContextInterceptor (for HTTP requests in gateway)
348
- * - MessageTenantContextInterceptor (for RabbitMQ messages in microservices)
349
- * - Manual context setup in message handlers
322
+ * Get tenant configuration by identifier (ID or subdomain)
350
323
  *
351
- * @param tenantInfo Complete tenant information
352
- * @throws Error if tenant context is already set (prevents accidental overwrites)
324
+ * @param tenantIdentifier Tenant ID or subdomain
325
+ * @returns Tenant configuration or null if not found
353
326
  */
354
- setTenant(tenantInfo: TenantInfo): void;
327
+ getTenantInfo(tenantIdentifier: string): Promise<TenantInfo | null>;
355
328
  /**
356
- * Get tenant information for this request/message
357
- *
358
- * @returns Tenant information
359
- * @throws UnauthorizedException if tenant context hasn't been set
329
+ * Cache tenant information with TTL
360
330
  */
361
- getTenant(): TenantInfo;
331
+ private cacheInfo;
362
332
  /**
363
- * Check if tenant context has been set
333
+ * Clear cached tenant information
364
334
  *
365
- * @returns true if tenant context is available
335
+ * Useful when tenant settings are updated and cache needs to be invalidated
336
+ *
337
+ * @param tenantIdentifier Tenant ID or subdomain
366
338
  */
367
- hasTenant(): boolean;
339
+ clearTenantCache(tenantIdentifier: string): void;
368
340
  /**
369
- * Clear tenant context
370
- *
371
- * This is useful for cleanup in RabbitMQ message handlers
372
- * after the message has been processed.
373
- *
374
- * HTTP requests don't need manual cleanup as the service
375
- * instance is destroyed when the request ends.
376
- */
377
- clearTenant(): void;
378
- /**
379
- * Get tenant ID safely (returns null if not set)
380
- *
381
- * @returns Tenant ID or null
382
- */
383
- getTenantIdSafe(): string | null;
384
- /**
385
- * Get tenant subdomain safely (returns null if not set)
386
- *
387
- * @returns Tenant subdomain or null
341
+ * Clear all cached tenant configurations
388
342
  */
389
- getTenantSubdomainSafe(): string | null;
390
- }
391
-
392
- /**
393
- * Service responsible for managing tenant-scoped database connections
394
- *
395
- * This service:
396
- * - Maintains a connection pool (Map<cacheKey, TenantConnection>)
397
- * - Creates new connections dynamically based on tenant context
398
- * - Reuses existing connections for the same tenant
399
- * - Supports both cloud schemas and enterprise databases
400
- * - Automatically cleans up idle connections
401
- *
402
- * @example
403
- * // In a controller or service
404
- * const db = this.tenantDatabase.drizzleClient;
405
- * const users = await db.select().from(usersTable);
406
- */
407
- declare class TenantDatabaseService implements OnModuleDestroy {
408
- private readonly options;
409
- private readonly tenantContext;
410
- private readonly logger;
411
- /** Connection pool: Map<cacheKey, TenantConnection> */
412
- private readonly clients;
413
- /** Track last usage time for idle connection cleanup */
414
- private readonly clientLastUsed;
415
- /** Cleanup interval timer */
416
- private cleanupInterval?;
417
- constructor(options: DatabaseModuleOptions, tenantContext: TenantContextService);
343
+ clearAllCaches(): void;
418
344
  /**
419
- * Get the Drizzle client for the current tenant's database.
420
- * 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.
421
347
  *
422
- * @returns Tenant-scoped Drizzle database instance
423
- * @throws UnauthorizedException if tenant context not set
424
- * @throws InternalServerErrorException if connection fails
348
+ * @returns Primary database Drizzle instance
349
+ * @throws Error if primary database client is not initialized
425
350
  */
426
351
  get drizzleClient(): TypedDrizzleClient;
427
352
  /**
428
353
  * Get the Drizzle schema
429
354
  */
430
- get schema(): Record<string, unknown>;
355
+ get schema(): typeof this$1.options.drizzleSchema;
431
356
  /**
432
- * Get tenant-scoped database client for the current request/message
357
+ * Decrypt database credentials
433
358
  *
434
- * This method:
435
- * 1. Gets tenant info from TenantContextService
436
- * 2. Builds a connection URL based on tenant type
437
- * 3. Returns cached client if exists, otherwise creates new one
359
+ * Override this method to implement your encryption strategy
438
360
  *
439
- * @returns Drizzle database instance
440
- * @throws UnauthorizedException if tenant context not set
441
- * @throws InternalServerErrorException if connection fails
361
+ * @param encrypted Encrypted value
362
+ * @returns Decrypted value
442
363
  */
443
- private getDbClient;
364
+ private decrypt;
365
+ onModuleDestroy(): Promise<void>;
366
+ }
367
+
368
+ declare class RequestService {
369
+ private readonly request;
370
+ constructor(request: FastifyRequest);
444
371
  /**
445
- * 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
446
375
  */
447
- private createDbClientSync;
376
+ getTenantIdentifier(): string | null;
448
377
  /**
449
- * 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
450
381
  */
451
- private buildTenantDbUrl;
382
+ getAccessToken(): string | null;
452
383
  /**
453
- * 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
454
387
  */
455
- private buildCacheKey;
388
+ getRefreshToken(): string | null;
456
389
  /**
457
- * 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)
458
393
  */
459
- private startConnectionCleaner;
394
+ getHeader(key: string): string | string[] | undefined;
460
395
  /**
461
- * Clean up idle connections that haven't been used recently
396
+ * Get all headers
397
+ * @returns Record of all headers
462
398
  */
463
- 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>;
464
475
  /**
465
- * Get current connection pool statistics
476
+ * Validate access token with proper expiry checks
477
+ * Throws UnauthorizedException if token is invalid or expired
466
478
  */
467
- getPoolStats(): {
468
- activeConnections: number;
469
- tenants: string[];
470
- };
479
+ private validateAccessToken;
471
480
  /**
472
- * 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
473
488
  */
474
- private maskPassword;
475
- onModuleDestroy(): Promise<void>;
489
+ private validateRefreshTokenBinding;
476
490
  }
477
491
 
478
492
  /**
479
- * 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
480
508
  *
481
- * This service:
482
- * - Connects to the primary database (tenant registry)
483
- * - Queries tenant metadata (database location, credentials, etc.)
484
- * - Caches tenant configs in memory to reduce database load
485
- * - 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
486
510
  *
487
511
  * @example
488
- * // In API Gateway
489
- * const config = await primaryDatabase.getTenantConfig('acme');
490
- * // 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
+ * ```
491
531
  */
492
- declare class PrimaryDatabaseService implements OnModuleInit, OnModuleDestroy {
493
- private readonly options;
494
- private readonly logger;
495
- /** PostgreSQL connection pool */
496
- private pool;
497
- /** Drizzle database instance */
498
- private db;
499
- /** In-memory cache: Map<tenantIdentifier, TenantConfig> */
500
- private readonly tenantConfigCache;
501
- /** Cache TTL in milliseconds */
502
- private readonly cacheTTL;
503
- constructor(options: DatabaseModuleOptions);
504
- onModuleInit(): Promise<void>;
532
+ /**
533
+ * Cookie configuration options
534
+ */
535
+ interface CookieConfig {
505
536
  /**
506
- * Initialize connection to primary database using Drizzle
537
+ * The name of the httpOnly cookie containing the refresh token
538
+ * @default 'vritti_refresh'
507
539
  */
508
- private initializeDrizzleClient;
540
+ refreshCookieName: string;
509
541
  /**
510
- * Build connection URL from primary database properties
542
+ * Max age of the refresh cookie in milliseconds
543
+ * @default 2592000000 (30 days)
511
544
  */
512
- private buildPrimaryDbUrl;
545
+ refreshCookieMaxAge: number;
513
546
  /**
514
- * Mask password in connection URL for logging
547
+ * Cookie path
548
+ * @default '/'
515
549
  */
516
- private maskPassword;
550
+ refreshCookiePath: string;
517
551
  /**
518
- * Get tenant configuration by identifier (ID or subdomain)
519
- *
520
- * @param tenantIdentifier Tenant ID or subdomain
521
- * @returns Tenant configuration or null if not found
552
+ * Whether the cookie is secure (HTTPS only)
553
+ * @default true in production
522
554
  */
523
- getTenantInfo(tenantIdentifier: string): Promise<TenantInfo | null>;
555
+ refreshCookieSecure: boolean;
524
556
  /**
525
- * Cache tenant information with TTL
557
+ * SameSite attribute for the cookie
558
+ * @default 'strict'
526
559
  */
527
- private cacheInfo;
560
+ refreshCookieSameSite: 'strict' | 'lax' | 'none';
561
+ }
562
+ /**
563
+ * JWT token configuration options
564
+ */
565
+ interface JwtConfig {
528
566
  /**
529
- * Clear cached tenant information
530
- *
531
- * Useful when tenant settings are updated and cache needs to be invalidated
532
- *
533
- * @param tenantIdentifier Tenant ID or subdomain
567
+ * Access token expiry time
568
+ * @default '15m'
534
569
  */
535
- clearTenantCache(tenantIdentifier: string): void;
570
+ accessTokenExpiry: string;
536
571
  /**
537
- * Clear all cached tenant configurations
572
+ * Refresh token expiry time
573
+ * @default '30d'
538
574
  */
539
- clearAllCaches(): void;
575
+ refreshTokenExpiry: string;
540
576
  /**
541
- * Get the Drizzle database instance for the primary database.
542
- * This is a synchronous property that returns the initialized Drizzle client.
543
- *
544
- * @returns Primary database Drizzle instance
545
- * @throws Error if primary database client is not initialized
577
+ * Onboarding token expiry time
578
+ * @default '24h'
546
579
  */
547
- get drizzleClient(): TypedDrizzleClient;
580
+ onboardingTokenExpiry: string;
548
581
  /**
549
- * Get the Drizzle schema
582
+ * Whether to validate refresh token binding (hash in access token)
583
+ * @default true
550
584
  */
551
- get schema(): typeof this$1.options.drizzleSchema;
585
+ validateTokenBinding: boolean;
586
+ }
587
+ /**
588
+ * Auth guard configuration options
589
+ */
590
+ interface GuardConfig {
552
591
  /**
553
- * Decrypt database credentials
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)
554
721
  *
555
- * Override this method to implement your encryption strategy
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
556
727
  *
557
- * @param encrypted Encrypted value
558
- * @returns Decrypted value
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
+ * })
559
745
  */
560
- private decrypt;
561
- onModuleDestroy(): Promise<void>;
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;
562
781
  }
563
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
+
564
831
  /**
565
832
  * Drizzle ORM v2 object-based where filter type.
566
833
  * Supports simple equality, operators, AND/OR/NOT, and RAW SQL.
@@ -663,7 +930,8 @@ declare abstract class PrimaryBaseRepository<TTable extends PgTable, TInsert = I
663
930
  protected readonly logger: Logger;
664
931
  /**
665
932
  * The table name extracted from the Drizzle table at runtime.
666
- * Used to access the query API for this repository's table.
933
+ * Stored in camelCase to match Drizzle's query object keys.
934
+ * Example: 'email_verifications' -> 'emailVerifications'
667
935
  */
668
936
  private readonly tableName;
669
937
  /**
@@ -894,65 +1162,218 @@ declare abstract class PrimaryBaseRepository<TTable extends PgTable, TInsert = I
894
1162
  }
895
1163
 
896
1164
  /**
897
- * Type helper to extract table name from Drizzle table.
898
- * TTable['_']['name'] gives us the string literal type (e.g., 'products')
899
- */
900
- type ExtractTableName<TTable extends PgTable> = TTable['_']['name'];
901
- /**
902
- * Abstract base repository for tenant-scoped database operations using Drizzle ORM.
903
- * All operations are automatically scoped to the current tenant.
904
- *
905
- * @template TTable - The Drizzle table type (must be registered in SchemaRegistry)
906
- * @template TInsert - Type for insert operations (inferred from table.$inferInsert)
907
- * @template TSelect - Type for select operations (inferred from table.$inferSelect)
908
- *
909
- * @remarks
910
- * **Type Assertion Pattern:** This repository uses `as any` casts when passing
911
- * the generic table to Drizzle methods. This is necessary because TypeScript
912
- * cannot prove that a generic `TTable extends PgTable` satisfies Drizzle's
913
- * stricter internal type requirements for `insert()`, `update()`, and `delete()`.
1165
+ * Request-scoped service that holds tenant context for the current request or RabbitMQ message
914
1166
  *
915
- * The public API maintains full type safety:
916
- * - Input parameters are typed as `TInsert` (inferred from table)
917
- * - Return values are typed as `TSelect` (inferred from table)
918
- * - The casts are implementation details that don't leak to consumers
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.
919
1170
  *
920
1171
  * @example
921
- * ```typescript
922
- * import { products } from '@/db/schema';
923
- *
924
- * type Product = typeof products.$inferSelect;
925
- * type NewProduct = typeof products.$inferInsert;
926
- *
927
- * @Injectable()
928
- * export class ProductRepository extends TenantBaseRepository<typeof products> {
929
- * constructor(database: TenantDatabaseService) {
930
- * super(database, products);
931
- * }
932
- *
933
- * // Use SQL-builder syntax
934
- * async findBySku(sku: string): Promise<Product | null> {
935
- * const [result] = await this.db
936
- * .select()
937
- * .from(this.table)
938
- * .where(eq(products.sku, sku))
939
- * .limit(1);
940
- * return result ?? null;
941
- * }
1172
+ * // In a controller or service
1173
+ * constructor(private readonly tenantContext: TenantContextService) {}
942
1174
  *
943
- * // Use Prisma-like relational query syntax
944
- * async findWithRelations(id: string): Promise<Product | null> {
945
- * return await this.model.findFirst({
946
- * where: eq(products.id, id),
947
- * with: { category: true, variants: true }
948
- * });
949
- * }
1175
+ * async handleRequest() {
1176
+ * const tenant = this.tenantContext.getTenant();
1177
+ * console.log(`Processing request for tenant: ${tenant.tenantSlug}`);
950
1178
  * }
951
- * ```
952
1179
  */
953
- declare abstract class TenantBaseRepository<TTable extends PgTable, TInsert = InferInsertModel<TTable>, TSelect = InferSelectModel<TTable>> {
954
- protected readonly database: TenantDatabaseService;
955
- protected readonly table: TTable;
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)
1328
+ * @template TSelect - Type for select operations (inferred from table.$inferSelect)
1329
+ *
1330
+ * @remarks
1331
+ * **Type Assertion Pattern:** This repository uses `as any` casts when passing
1332
+ * the generic table to Drizzle methods. This is necessary because TypeScript
1333
+ * cannot prove that a generic `TTable extends PgTable` satisfies Drizzle's
1334
+ * stricter internal type requirements for `insert()`, `update()`, and `delete()`.
1335
+ *
1336
+ * The public API maintains full type safety:
1337
+ * - Input parameters are typed as `TInsert` (inferred from table)
1338
+ * - Return values are typed as `TSelect` (inferred from table)
1339
+ * - The casts are implementation details that don't leak to consumers
1340
+ *
1341
+ * @example
1342
+ * ```typescript
1343
+ * import { products } from '@/db/schema';
1344
+ *
1345
+ * type Product = typeof products.$inferSelect;
1346
+ * type NewProduct = typeof products.$inferInsert;
1347
+ *
1348
+ * @Injectable()
1349
+ * export class ProductRepository extends TenantBaseRepository<typeof products> {
1350
+ * constructor(database: TenantDatabaseService) {
1351
+ * super(database, products);
1352
+ * }
1353
+ *
1354
+ * // Use SQL-builder syntax
1355
+ * async findBySku(sku: string): Promise<Product | null> {
1356
+ * const [result] = await this.db
1357
+ * .select()
1358
+ * .from(this.table)
1359
+ * .where(eq(products.sku, sku))
1360
+ * .limit(1);
1361
+ * return result ?? null;
1362
+ * }
1363
+ *
1364
+ * // Use Prisma-like relational query syntax
1365
+ * async findWithRelations(id: string): Promise<Product | null> {
1366
+ * return await this.model.findFirst({
1367
+ * where: eq(products.id, id),
1368
+ * with: { category: true, variants: true }
1369
+ * });
1370
+ * }
1371
+ * }
1372
+ * ```
1373
+ */
1374
+ declare abstract class TenantBaseRepository<TTable extends PgTable, TInsert = InferInsertModel<TTable>, TSelect = InferSelectModel<TTable>> {
1375
+ protected readonly database: TenantDatabaseService;
1376
+ protected readonly table: TTable;
956
1377
  protected readonly logger: Logger;
957
1378
  /**
958
1379
  * The table name extracted from the Drizzle table at runtime.
@@ -1143,351 +1564,30 @@ declare abstract class TenantBaseRepository<TTable extends PgTable, TInsert = In
1143
1564
  *
1144
1565
  * // Count all products
1145
1566
  * const total = await productRepository.count();
1146
- *
1147
- * // Count active products
1148
- * const activeCount = await productRepository.count(
1149
- * eq(products.status, 'ACTIVE')
1150
- * );
1151
- * ```
1152
- */
1153
- count(where?: SQL): Promise<number>;
1154
- /**
1155
- * Check if a record exists
1156
- *
1157
- * @param where - SQL condition to match records
1158
- * @returns Promise resolving to true if at least one record exists, false otherwise
1159
- *
1160
- * @example
1161
- * ```typescript
1162
- * import { eq } from 'drizzle-orm';
1163
- *
1164
- * const skuExists = await productRepository.exists(
1165
- * eq(products.sku, 'WDG-001')
1166
- * );
1167
- * ```
1168
- */
1169
- exists(where: SQL): Promise<boolean>;
1170
- }
1171
-
1172
- declare class RequestService {
1173
- private readonly request;
1174
- constructor(request: FastifyRequest);
1175
- /**
1176
- * Extract tenant identifier from request headers
1177
- * Priority: x-tenant-id > x-subdomain
1178
- * @returns Tenant identifier or null if not found
1179
- */
1180
- getTenantIdentifier(): string | null;
1181
- /**
1182
- * Extract access token from Authorization header
1183
- * Format: "Bearer <token>"
1184
- * @returns Access token or null if not found
1185
- */
1186
- getAccessToken(): string | null;
1187
- /**
1188
- * Extract refresh token from session-id cookie
1189
- * Cookie name: session-id
1190
- * @returns Refresh token or null if not found
1191
- */
1192
- getRefreshToken(): string | null;
1193
- /**
1194
- * Get a specific header value
1195
- * @param key Header key
1196
- * @returns Header value (string, array, or undefined)
1197
- */
1198
- getHeader(key: string): string | string[] | undefined;
1199
- /**
1200
- * Get all headers
1201
- * @returns Record of all headers
1202
- */
1203
- getAllHeaders(): FastifyRequest['headers'];
1204
- }
1205
-
1206
- /**
1207
- * Vritti Authentication Guard - Validates JWT tokens and tenant context
1208
- *
1209
- * This guard performs comprehensive validation and attaches user data to request.
1210
- *
1211
- * Validation Flow:
1212
- * 1. Checks if endpoint is marked with @Public() decorator → skip all validation
1213
- * 2. Checks if endpoint is marked with @Onboarding() decorator:
1214
- * - Requires token type='onboarding'
1215
- * - Validates JWT signature and expiry only
1216
- * - Skips tenant and refresh token validation
1217
- * - Attaches user data to request.user
1218
- * 3. For regular endpoints (no decorator):
1219
- * - Rejects tokens with type='onboarding'
1220
- * - Validates access token (JWT signature, expiry, nbf)
1221
- * - Validates refresh token from session-id cookie
1222
- * - Validates tenant exists and is ACTIVE
1223
- * - Attaches user data to request.user
1224
- *
1225
- * Token Format:
1226
- * - Access Token: "Authorization: Bearer <jwt_token>"
1227
- * - Refresh Token: "session-id" cookie
1228
- *
1229
- * Token Types:
1230
- * - type='onboarding': Limited access during registration flow (@Onboarding endpoints only)
1231
- * - type='access': Full access to authenticated endpoints
1232
- *
1233
- * Environment Variables Required:
1234
- * - JWT_SECRET: Secret key to verify access tokens (required)
1235
- * - JWT_REFRESH_SECRET: Secret key for refresh tokens (optional, falls back to JWT_SECRET)
1236
- *
1237
- * Error Responses:
1238
- * - 401: Invalid/expired access token
1239
- * - 401: Invalid/expired refresh token
1240
- * - 401: Tenant not found or inactive
1241
- * - 401: Tenant identifier not found
1242
- * - 401: Token type mismatch (onboarding token on regular endpoint or vice versa)
1243
- *
1244
- * @example
1245
- * // Automatically registered by AuthConfigModule.forRootAsync()
1246
- * // No manual registration needed
1247
- * //
1248
- * // Internal registration uses useExisting pattern:
1249
- * // providers: [
1250
- * // VrittiAuthGuard,
1251
- * // {
1252
- * // provide: APP_GUARD,
1253
- * // useExisting: VrittiAuthGuard,
1254
- * // },
1255
- * // ]
1256
- *
1257
- * @example
1258
- * // Bypass guard with @Public() decorator
1259
- * @Public()
1260
- * @Post('auth/login')
1261
- * async login(@Body() dto: LoginDto) { ... }
1262
- *
1263
- * @example
1264
- * // Restrict to onboarding tokens with @Onboarding() decorator
1265
- * @Onboarding()
1266
- * @Post('onboarding/verify-email')
1267
- * async verifyEmail(@Request() req) {
1268
- * const userId = req.user.id; // Available from guard
1269
- * ...
1270
- * }
1271
- */
1272
- declare class VrittiAuthGuard implements CanActivate {
1273
- private readonly reflector;
1274
- private readonly configService;
1275
- private readonly jwtService;
1276
- private readonly primaryDatabase;
1277
- private readonly requestService;
1278
- private readonly logger;
1279
- constructor(reflector: Reflector, configService: ConfigService, jwtService: JwtService, primaryDatabase: PrimaryDatabaseService, requestService: RequestService);
1280
- canActivate(context: ExecutionContext): Promise<boolean>;
1281
- /**
1282
- * Validate access token with proper expiry checks
1283
- * Throws UnauthorizedException if token is invalid or expired
1284
- */
1285
- private validateAccessToken;
1286
- /**
1287
- * Validate refresh token with proper expiry checks
1288
- * Throws UnauthorizedException if token is invalid or expired
1289
- */
1290
- private validateRefreshToken;
1291
- /**
1292
- * Helper to validate refresh token with specific secret
1293
- */
1294
- private validateRefreshTokenWithSecret;
1295
- }
1296
-
1297
- /**
1298
- * Parameter decorator that injects tenant metadata into controller method
1299
- *
1300
- * This decorator retrieves tenant information (ID, slug, type, etc.)
1301
- * from the REQUEST-SCOPED TenantContextService.
1302
- *
1303
- * Useful for:
1304
- * - Logging tenant-specific information
1305
- * - Implementing tenant-specific business logic
1306
- * - Auditing and tracking
1307
- * - Conditional feature flags
1308
- *
1309
- * @returns TenantInfo object with tenant metadata
1310
- *
1311
- * @example
1312
- * // Access tenant metadata
1313
- * @Get('info')
1314
- * async getTenantInfo(@Tenant() tenant: TenantInfo) {
1315
- * return {
1316
- * id: tenant.id,
1317
- * subdomain: tenant.subdomain,
1318
- * type: tenant.type,
1319
- * };
1320
- * }
1321
- *
1322
- * @example
1323
- * // Use for logging
1324
- * @Post()
1325
- * async createUser(
1326
- * @Body() dto: CreateUserDto,
1327
- * @Tenant() tenant: TenantInfo,
1328
- * ) {
1329
- * this.logger.log(`Creating user for tenant: ${tenant.subdomain}`);
1330
- * // ...
1331
- * }
1332
- *
1333
- * @example
1334
- * // Conditional business logic
1335
- * @Get('features')
1336
- * async getFeatures(@Tenant() tenant: TenantInfo) {
1337
- * if (tenant.type === 'ENTERPRISE') {
1338
- * return ['feature-a', 'feature-b', 'feature-c'];
1339
- * }
1340
- * return ['feature-a'];
1341
- * }
1342
- */
1343
- declare const Tenant: (...dataOrPipes: unknown[]) => ParameterDecorator;
1344
-
1345
- /**
1346
- * Onboarding Decorator - Marks endpoints that require onboarding token
1347
- *
1348
- * Use this decorator on controllers or route handlers that should only be
1349
- * accessible during the onboarding flow with JWT tokens containing type='onboarding'.
1350
- *
1351
- * These endpoints:
1352
- * - Accept ONLY tokens with type='onboarding'
1353
- * - Reject regular access tokens (type='access')
1354
- * - Skip tenant validation and refresh token checks
1355
- * - Only validate JWT signature and expiry
1356
- *
1357
- * Useful for:
1358
- * - Email/phone verification during onboarding
1359
- * - Onboarding status checks
1360
- * - Resending OTPs during registration
1361
- *
1362
- * @example
1363
- * // On a controller method
1364
- * @Post('verify-email')
1365
- * @Onboarding()
1366
- * async verifyEmail(@Request() req, @Body() dto: VerifyEmailDto) {
1367
- * const userId = req.user.id; // Available from VrittiAuthGuard
1368
- * return this.service.verifyEmail(userId, dto.otp);
1369
- * }
1370
- *
1371
- * @example
1372
- * // Multiple onboarding endpoints
1373
- * @Controller('onboarding')
1374
- * export class OnboardingController {
1375
- * @Post('verify-email')
1376
- * @Onboarding()
1377
- * async verifyEmail() { ... }
1378
- *
1379
- * @Post('resend-otp')
1380
- * @Onboarding()
1381
- * async resendOtp() { ... }
1382
- * }
1383
- */
1384
- declare const Onboarding: () => _nestjs_common.CustomDecorator<string>;
1385
-
1386
- /**
1387
- * Public Decorator - Marks endpoints that don't require authentication
1388
- *
1389
- * Use this decorator on controllers or route handlers to bypass VrittiAuthGuard
1390
- * tenant validation. Useful for:
1391
- * - Login/signup endpoints
1392
- * - Health checks
1393
- * - Public documentation endpoints
1394
- * - Webhook endpoints that don't require tenant context
1395
- *
1396
- * @example
1397
- * // On a controller method
1398
- * @Public()
1399
- * @Post('auth/login')
1400
- * async login(@Body() dto: LoginDto) {
1401
- * return this.authService.login(dto);
1402
- * }
1403
- *
1404
- * @example
1405
- * // On an entire controller
1406
- * @Public()
1407
- * @Controller('health')
1408
- * export class HealthController {
1409
- * @Get()
1410
- * check() {
1411
- * return { status: 'ok' };
1412
- * }
1413
- * }
1414
- */
1415
- declare const Public: () => _nestjs_common.CustomDecorator<string>;
1416
-
1417
- /**
1418
- * HTTP Module
1419
- *
1420
- * Provides HTTP utilities including:
1421
- * - CSRF Guard for request protection
1422
- * - HTTP Exception Filter for standardized error responses
1423
- *
1424
- * Usage:
1425
- * Import this module to access HTTP guards and filters.
1426
- * Guards and filters are registered globally in the main application.
1427
- */
1428
- declare class HttpModule {
1429
- }
1430
-
1431
- /**
1432
- * CSRF Guard
1433
- *
1434
- * Global guard that automatically protects all state-changing requests (POST, PUT, PATCH, DELETE)
1435
- * from CSRF attacks using Fastify's csrf-protection plugin.
1436
- *
1437
- * Flow:
1438
- * 1. Skip safe methods (GET, HEAD, OPTIONS)
1439
- * 2. Skip endpoints marked with @Public()
1440
- * 3. Validate CSRF token for all other requests
1441
- *
1442
- * Token Sources (in priority order by @fastify/csrf-protection):
1443
- * 1. req.headers['csrf-token']
1444
- * 2. req.headers['xsrf-token']
1445
- * 3. req.headers['x-csrf-token']
1446
- * 4. req.headers['x-xsrf-token']
1447
- * 5. req.body._csrf
1448
- *
1449
- * This guard should be registered globally in main.ts after CSRF plugin registration.
1450
- */
1451
- declare class CsrfGuard implements CanActivate {
1452
- private reflector;
1453
- private readonly logger;
1454
- constructor(reflector: Reflector);
1455
- canActivate(context: ExecutionContext): Promise<boolean>;
1456
- }
1457
-
1458
- /**
1459
- * Converts an HTTP status code to its corresponding title string.
1460
- * Uses the HttpStatus enum to map status codes to human-readable titles.
1461
- *
1462
- * @param status - The HTTP status code
1463
- * @returns The human-readable title for the status code
1464
- *
1465
- * @example
1466
- * getHttpStatusTitle(400) // Returns: "Bad Request"
1467
- * getHttpStatusTitle(404) // Returns: "Not Found"
1468
- * getHttpStatusTitle(500) // Returns: "Internal Server Error"
1469
- */
1470
- declare function getHttpStatusTitle(status: number): string;
1471
- /**
1472
- * Global HTTP Exception Filter implementing RFC 7807 Problem Details
1473
- *
1474
- * Transforms all exceptions into a standardized RFC 7807 format:
1475
- * {
1476
- * title: string, // Human-readable status title
1477
- * status: number, // HTTP status code
1478
- * detail: string, // Detailed error description
1479
- * errors: FieldError[] // Field-specific error messages
1480
- * }
1481
- *
1482
- * Handles:
1483
- * - Custom field exceptions from @vritti/api-sdk (BaseFieldException)
1484
- * - Class-validator DTO validation errors
1485
- * - Standard NestJS HTTP exceptions
1486
- * - Unknown errors
1487
- */
1488
- declare class HttpExceptionFilter implements ExceptionFilter {
1489
- private readonly logger;
1490
- 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>;
1491
1591
  }
1492
1592
 
1493
1593
  interface FieldError {
@@ -1507,6 +1607,29 @@ declare abstract class BaseFieldException extends HttpException {
1507
1607
  constructor(statusOrMessageOrErrors: HttpStatus | string | FieldError[], messageOrStatus?: string | HttpStatus, statusOrDetail?: HttpStatus | string, detail?: string);
1508
1608
  }
1509
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
+
1510
1633
  /**
1511
1634
  * Exception thrown when a request is malformed or contains invalid data (HTTP 400).
1512
1635
  *
@@ -1531,24 +1654,25 @@ declare class BadRequestException extends BaseFieldException {
1531
1654
  }
1532
1655
 
1533
1656
  /**
1534
- * 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.
1535
1659
  *
1536
1660
  * @example
1537
1661
  * // Simple message
1538
- * throw new UnauthorizedException('Authentication required');
1662
+ * throw new ConflictException('Resource already exists');
1539
1663
  *
1540
1664
  * // Field-specific error
1541
- * throw new UnauthorizedException('token', 'Invalid or expired token');
1665
+ * throw new ConflictException('email', 'Email already registered');
1542
1666
  *
1543
1667
  * // With detail
1544
- * throw new UnauthorizedException('token', 'Invalid token', 'Please login again');
1668
+ * throw new ConflictException('email', 'Email already exists', 'Try logging in instead');
1545
1669
  *
1546
1670
  * // Multiple field errors
1547
- * throw new UnauthorizedException([
1548
- * { field: 'token', message: 'Token expired' }
1671
+ * throw new ConflictException([
1672
+ * { field: 'email', message: 'Email already in use' }
1549
1673
  * ]);
1550
1674
  */
1551
- declare class UnauthorizedException extends BaseFieldException {
1675
+ declare class ConflictException extends BaseFieldException {
1552
1676
  constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1553
1677
  }
1554
1678
 
@@ -1575,275 +1699,276 @@ declare class ForbiddenException extends BaseFieldException {
1575
1699
  }
1576
1700
 
1577
1701
  /**
1578
- * 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.
1579
1704
  *
1580
1705
  * @example
1581
1706
  * // Simple message
1582
- * throw new NotFoundException('Resource not found');
1707
+ * throw new GoneException('Resource permanently deleted');
1583
1708
  *
1584
1709
  * // Field-specific error
1585
- * throw new NotFoundException('userId', 'User not found');
1710
+ * throw new GoneException('account', 'Account has been permanently deleted');
1586
1711
  *
1587
1712
  * // With detail
1588
- * 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');
1589
1714
  *
1590
1715
  * // Multiple field errors
1591
- * throw new NotFoundException([
1592
- * { field: 'userId', message: 'User does not exist' }
1716
+ * throw new GoneException([
1717
+ * { field: 'resource', message: 'This content has been permanently removed' }
1593
1718
  * ]);
1594
1719
  */
1595
- declare class NotFoundException extends BaseFieldException {
1720
+ declare class GoneException extends BaseFieldException {
1596
1721
  constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1597
1722
  }
1598
1723
 
1599
1724
  /**
1600
- * Exception thrown when a request conflicts with the current state (HTTP 409).
1601
- * Commonly used for duplicate resources or concurrent modification issues.
1725
+ * Exception thrown when an unexpected server error occurs (HTTP 500).
1602
1726
  *
1603
1727
  * @example
1604
1728
  * // Simple message
1605
- * throw new ConflictException('Resource already exists');
1729
+ * throw new InternalServerErrorException('An unexpected error occurred');
1606
1730
  *
1607
1731
  * // Field-specific error
1608
- * throw new ConflictException('email', 'Email already registered');
1732
+ * throw new InternalServerErrorException('database', 'Database connection failed');
1609
1733
  *
1610
1734
  * // With detail
1611
- * throw new ConflictException('email', 'Email already exists', 'Try logging in instead');
1735
+ * throw new InternalServerErrorException('database', 'Connection failed', 'Please try again later');
1612
1736
  *
1613
1737
  * // Multiple field errors
1614
- * throw new ConflictException([
1615
- * { field: 'email', message: 'Email already in use' }
1738
+ * throw new InternalServerErrorException([
1739
+ * { field: 'system', message: 'Internal error' }
1616
1740
  * ]);
1617
1741
  */
1618
- declare class ConflictException extends BaseFieldException {
1742
+ declare class InternalServerErrorException extends BaseFieldException {
1619
1743
  constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1620
1744
  }
1621
1745
 
1622
1746
  /**
1623
- * 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.
1624
1749
  *
1625
1750
  * @example
1626
1751
  * // Simple message
1627
- * throw new InternalServerErrorException('An unexpected error occurred');
1752
+ * throw new MethodNotAllowedException('Method not allowed');
1628
1753
  *
1629
1754
  * // Field-specific error
1630
- * throw new InternalServerErrorException('database', 'Database connection failed');
1755
+ * throw new MethodNotAllowedException('method', 'POST method not allowed on this endpoint');
1631
1756
  *
1632
1757
  * // With detail
1633
- * throw new InternalServerErrorException('database', 'Connection failed', 'Please try again later');
1758
+ * throw new MethodNotAllowedException('method', 'Not allowed', 'Only GET and PUT are supported');
1634
1759
  *
1635
1760
  * // Multiple field errors
1636
- * throw new InternalServerErrorException([
1637
- * { field: 'system', message: 'Internal error' }
1761
+ * throw new MethodNotAllowedException([
1762
+ * { field: 'method', message: 'DELETE is not allowed on this resource' }
1638
1763
  * ]);
1639
1764
  */
1640
- declare class InternalServerErrorException extends BaseFieldException {
1765
+ declare class MethodNotAllowedException extends BaseFieldException {
1641
1766
  constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1642
1767
  }
1643
1768
 
1644
1769
  /**
1645
- * Exception thrown when request validation fails (HTTP 400).
1646
- * 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.
1647
1772
  *
1648
1773
  * @example
1649
- * // Multiple validation errors
1650
- * throw new ValidationException([
1651
- * { field: 'email', message: 'Invalid email format' },
1652
- * { field: 'password', message: 'Password must be at least 8 characters' }
1653
- * ]);
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');
1654
1779
  *
1655
1780
  * // With detail
1656
- * throw new ValidationException(
1657
- * [{ field: 'email', message: 'Invalid format' }],
1658
- * 'Please correct the errors and try again'
1659
- * );
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
+ * ]);
1660
1787
  */
1661
- declare class ValidationException extends BaseFieldException {
1662
- constructor(errors: FieldError[], detail?: string);
1788
+ declare class NotAcceptableException extends BaseFieldException {
1789
+ constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1663
1790
  }
1664
1791
 
1665
1792
  /**
1666
- * Exception thrown when the request is well-formed but contains semantic errors (HTTP 422).
1667
- * Used for business logic validation failures that prevent processing.
1793
+ * Exception thrown when a requested resource cannot be found (HTTP 404).
1668
1794
  *
1669
1795
  * @example
1670
1796
  * // Simple message
1671
- * throw new UnprocessableEntityException('Cannot process the request');
1797
+ * throw new NotFoundException('Resource not found');
1672
1798
  *
1673
1799
  * // Field-specific error
1674
- * throw new UnprocessableEntityException('age', 'Age must be 18 or older');
1800
+ * throw new NotFoundException('userId', 'User not found');
1675
1801
  *
1676
1802
  * // With detail
1677
- * 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');
1678
1804
  *
1679
1805
  * // Multiple field errors
1680
- * throw new UnprocessableEntityException([
1681
- * { field: 'startDate', message: 'Start date must be before end date' },
1682
- * { field: 'endDate', message: 'End date cannot be in the past' }
1806
+ * throw new NotFoundException([
1807
+ * { field: 'userId', message: 'User does not exist' }
1683
1808
  * ]);
1684
1809
  */
1685
- declare class UnprocessableEntityException extends BaseFieldException {
1810
+ declare class NotFoundException extends BaseFieldException {
1686
1811
  constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1687
1812
  }
1688
1813
 
1689
1814
  /**
1690
- * Exception thrown when rate limiting is triggered (HTTP 429).
1691
- * 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.
1692
1817
  *
1693
1818
  * @example
1694
1819
  * // Simple message
1695
- * throw new TooManyRequestsException('Too many requests');
1820
+ * throw new NotImplementedException('Feature not yet implemented');
1696
1821
  *
1697
1822
  * // Field-specific error
1698
- * throw new TooManyRequestsException('api', 'Rate limit exceeded');
1823
+ * throw new NotImplementedException('feature', 'This feature is coming soon');
1699
1824
  *
1700
1825
  * // With detail
1701
- * 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');
1702
1827
  *
1703
1828
  * // Multiple field errors
1704
- * throw new TooManyRequestsException([
1705
- * { field: 'requests', message: 'Rate limit exceeded for this endpoint' }
1829
+ * throw new NotImplementedException([
1830
+ * { field: 'functionality', message: 'This functionality is not available yet' }
1706
1831
  * ]);
1707
1832
  */
1708
- declare class TooManyRequestsException extends BaseFieldException {
1833
+ declare class NotImplementedException extends BaseFieldException {
1709
1834
  constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1710
1835
  }
1711
1836
 
1712
1837
  /**
1713
- * Exception thrown when the service is temporarily unavailable (HTTP 503).
1714
- * 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.
1715
1840
  *
1716
1841
  * @example
1717
1842
  * // Simple message
1718
- * throw new ServiceUnavailableException('Service temporarily unavailable');
1843
+ * throw new PayloadTooLargeException('Request payload too large');
1719
1844
  *
1720
1845
  * // Field-specific error
1721
- * throw new ServiceUnavailableException('service', 'Scheduled maintenance in progress');
1846
+ * throw new PayloadTooLargeException('file', 'File size exceeds maximum allowed');
1722
1847
  *
1723
1848
  * // With detail
1724
- * 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');
1725
1850
  *
1726
1851
  * // Multiple field errors
1727
- * throw new ServiceUnavailableException([
1728
- * { field: 'database', message: 'Database is temporarily unavailable' }
1852
+ * throw new PayloadTooLargeException([
1853
+ * { field: 'upload', message: 'File exceeds 10MB limit' }
1729
1854
  * ]);
1730
1855
  */
1731
- declare class ServiceUnavailableException extends BaseFieldException {
1856
+ declare class PayloadTooLargeException extends BaseFieldException {
1732
1857
  constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1733
1858
  }
1734
1859
 
1735
1860
  /**
1736
- * Exception thrown when an HTTP method is not supported for the endpoint (HTTP 405).
1737
- * 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.
1738
1863
  *
1739
1864
  * @example
1740
1865
  * // Simple message
1741
- * throw new MethodNotAllowedException('Method not allowed');
1866
+ * throw new RequestTimeoutException('Request timeout');
1742
1867
  *
1743
1868
  * // Field-specific error
1744
- * throw new MethodNotAllowedException('method', 'POST method not allowed on this endpoint');
1869
+ * throw new RequestTimeoutException('operation', 'Operation timed out');
1745
1870
  *
1746
1871
  * // With detail
1747
- * throw new MethodNotAllowedException('method', 'Not allowed', 'Only GET and PUT are supported');
1872
+ * throw new RequestTimeoutException('query', 'Database query timeout', 'Try with fewer filters');
1748
1873
  *
1749
1874
  * // Multiple field errors
1750
- * throw new MethodNotAllowedException([
1751
- * { field: 'method', message: 'DELETE is not allowed on this resource' }
1875
+ * throw new RequestTimeoutException([
1876
+ * { field: 'processing', message: 'Request took too long to complete' }
1752
1877
  * ]);
1753
1878
  */
1754
- declare class MethodNotAllowedException extends BaseFieldException {
1879
+ declare class RequestTimeoutException extends BaseFieldException {
1755
1880
  constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1756
1881
  }
1757
1882
 
1758
1883
  /**
1759
- * Exception thrown when a resource has been permanently removed (HTTP 410).
1760
- * 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.
1761
1886
  *
1762
1887
  * @example
1763
1888
  * // Simple message
1764
- * throw new GoneException('Resource permanently deleted');
1889
+ * throw new ServiceUnavailableException('Service temporarily unavailable');
1765
1890
  *
1766
1891
  * // Field-specific error
1767
- * throw new GoneException('account', 'Account has been permanently deleted');
1892
+ * throw new ServiceUnavailableException('service', 'Scheduled maintenance in progress');
1768
1893
  *
1769
1894
  * // With detail
1770
- * 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');
1771
1896
  *
1772
1897
  * // Multiple field errors
1773
- * throw new GoneException([
1774
- * { field: 'resource', message: 'This content has been permanently removed' }
1898
+ * throw new ServiceUnavailableException([
1899
+ * { field: 'database', message: 'Database is temporarily unavailable' }
1775
1900
  * ]);
1776
1901
  */
1777
- declare class GoneException extends BaseFieldException {
1902
+ declare class ServiceUnavailableException extends BaseFieldException {
1778
1903
  constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1779
1904
  }
1780
1905
 
1781
1906
  /**
1782
- * Exception thrown when content negotiation fails (HTTP 406).
1783
- * 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.
1784
1909
  *
1785
1910
  * @example
1786
1911
  * // Simple message
1787
- * throw new NotAcceptableException('Requested format not available');
1912
+ * throw new TooManyRequestsException('Too many requests');
1788
1913
  *
1789
1914
  * // Field-specific error
1790
- * throw new NotAcceptableException('accept', 'Cannot produce response in requested format');
1915
+ * throw new TooManyRequestsException('api', 'Rate limit exceeded');
1791
1916
  *
1792
1917
  * // With detail
1793
- * throw new NotAcceptableException('accept', 'Format not supported', 'Only JSON is available');
1918
+ * throw new TooManyRequestsException('api', 'Rate limit exceeded', 'Try again in 60 seconds');
1794
1919
  *
1795
1920
  * // Multiple field errors
1796
- * throw new NotAcceptableException([
1797
- * { field: 'contentType', message: 'XML format is not supported' }
1921
+ * throw new TooManyRequestsException([
1922
+ * { field: 'requests', message: 'Rate limit exceeded for this endpoint' }
1798
1923
  * ]);
1799
1924
  */
1800
- declare class NotAcceptableException extends BaseFieldException {
1925
+ declare class TooManyRequestsException extends BaseFieldException {
1801
1926
  constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1802
1927
  }
1803
1928
 
1804
1929
  /**
1805
- * Exception thrown when a request takes too long to process (HTTP 408).
1806
- * Used when the client or server times out while waiting for completion.
1930
+ * Exception thrown when authentication is required or has failed (HTTP 401).
1807
1931
  *
1808
1932
  * @example
1809
1933
  * // Simple message
1810
- * throw new RequestTimeoutException('Request timeout');
1934
+ * throw new UnauthorizedException('Authentication required');
1811
1935
  *
1812
1936
  * // Field-specific error
1813
- * throw new RequestTimeoutException('operation', 'Operation timed out');
1937
+ * throw new UnauthorizedException('token', 'Invalid or expired token');
1814
1938
  *
1815
1939
  * // With detail
1816
- * throw new RequestTimeoutException('query', 'Database query timeout', 'Try with fewer filters');
1940
+ * throw new UnauthorizedException('token', 'Invalid token', 'Please login again');
1817
1941
  *
1818
1942
  * // Multiple field errors
1819
- * throw new RequestTimeoutException([
1820
- * { field: 'processing', message: 'Request took too long to complete' }
1943
+ * throw new UnauthorizedException([
1944
+ * { field: 'token', message: 'Token expired' }
1821
1945
  * ]);
1822
1946
  */
1823
- declare class RequestTimeoutException extends BaseFieldException {
1947
+ declare class UnauthorizedException extends BaseFieldException {
1824
1948
  constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1825
1949
  }
1826
1950
 
1827
1951
  /**
1828
- * Exception thrown when request payload exceeds size limits (HTTP 413).
1829
- * 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.
1830
1954
  *
1831
1955
  * @example
1832
1956
  * // Simple message
1833
- * throw new PayloadTooLargeException('Request payload too large');
1957
+ * throw new UnprocessableEntityException('Cannot process the request');
1834
1958
  *
1835
1959
  * // Field-specific error
1836
- * throw new PayloadTooLargeException('file', 'File size exceeds maximum allowed');
1960
+ * throw new UnprocessableEntityException('age', 'Age must be 18 or older');
1837
1961
  *
1838
1962
  * // With detail
1839
- * throw new PayloadTooLargeException('file', 'File too large', 'Maximum size is 10MB');
1963
+ * throw new UnprocessableEntityException('quantity', 'Insufficient stock', 'Only 5 items available');
1840
1964
  *
1841
1965
  * // Multiple field errors
1842
- * throw new PayloadTooLargeException([
1843
- * { 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' }
1844
1969
  * ]);
1845
1970
  */
1846
- declare class PayloadTooLargeException extends BaseFieldException {
1971
+ declare class UnprocessableEntityException extends BaseFieldException {
1847
1972
  constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1848
1973
  }
1849
1974
 
@@ -1871,49 +1996,98 @@ declare class UnsupportedMediaTypeException extends BaseFieldException {
1871
1996
  }
1872
1997
 
1873
1998
  /**
1874
- * Exception thrown when a feature or endpoint is not yet implemented (HTTP 501).
1875
- * 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.
1876
2001
  *
1877
2002
  * @example
1878
- * // Simple message
1879
- * throw new NotImplementedException('Feature not yet implemented');
1880
- *
1881
- * // Field-specific error
1882
- * 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
+ * ]);
1883
2008
  *
1884
2009
  * // With detail
1885
- * throw new NotImplementedException('export', 'Not implemented', 'PDF export will be available in v2.0');
1886
- *
1887
- * // Multiple field errors
1888
- * throw new NotImplementedException([
1889
- * { field: 'functionality', message: 'This functionality is not available yet' }
1890
- * ]);
2010
+ * throw new ValidationException(
2011
+ * [{ field: 'email', message: 'Invalid format' }],
2012
+ * 'Please correct the errors and try again'
2013
+ * );
1891
2014
  */
1892
- declare class NotImplementedException extends BaseFieldException {
1893
- constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
2015
+ declare class ValidationException extends BaseFieldException {
2016
+ constructor(errors: FieldError[], detail?: string);
1894
2017
  }
1895
2018
 
1896
2019
  /**
1897
- * Exception thrown when a gateway or proxy receives an invalid response (HTTP 502).
1898
- * 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
1899
2025
  *
1900
2026
  * @example
1901
- * // Simple message
1902
- * 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
1903
2034
  *
1904
- * // Field-specific error
1905
- * 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
+ * }
1906
2042
  *
1907
- * // With detail
1908
- * 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
1909
2056
  *
1910
- * // Multiple field errors
1911
- * throw new BadGatewayException([
1912
- * { field: 'gateway', message: 'Invalid response from upstream server' }
1913
- * ]);
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.
1914
2073
  */
1915
- declare class BadGatewayException extends BaseFieldException {
1916
- 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 {
1917
2091
  }
1918
2092
 
1919
2093
  /**
@@ -1990,6 +2164,77 @@ interface HttpLoggerOptions {
1990
2164
  maxBodySize?: number;
1991
2165
  }
1992
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
+
1993
2238
  /**
1994
2239
  * Logger Module
1995
2240
  *
@@ -2152,7 +2397,7 @@ declare class LoggerModule implements NestModule {
2152
2397
  * Configures middleware for the module.
2153
2398
  * Middleware is registered globally in main.ts using Fastify hooks.
2154
2399
  */
2155
- configure(consumer: MiddlewareConsumer): void;
2400
+ configure(_consumer: MiddlewareConsumer): void;
2156
2401
  /**
2157
2402
  * Creates async providers for dynamic module configuration.
2158
2403
  */
@@ -2163,52 +2408,6 @@ declare class LoggerModule implements NestModule {
2163
2408
  private static createAsyncOptionsProvider;
2164
2409
  }
2165
2410
 
2166
- /**
2167
- * Unified Logger Service
2168
- *
2169
- * Single service that provides both default NestJS Logger and Winston logger implementations.
2170
- * Automatically delegates to the configured provider (default or winston).
2171
- * @module logger/logger.service
2172
- */
2173
-
2174
- /**
2175
- * Unified logger service implementing NestJS LoggerService interface.
2176
- * Supports both default NestJS Logger and Winston implementations via facade pattern.
2177
- */
2178
- declare class LoggerService implements LoggerService$1 {
2179
- private readonly defaultLogger?;
2180
- private readonly activeLogger;
2181
- private readonly options;
2182
- private context?;
2183
- constructor(options?: LoggerModuleOptions, defaultLogger?: Logger | undefined);
2184
- /**
2185
- * Creates a Winston logger instance with inline configuration.
2186
- * Consolidates winston-config.factory.ts logic.
2187
- */
2188
- private createWinstonLogger;
2189
- log(message: any, context?: string): void;
2190
- error(message: any, trace?: string, context?: string): void;
2191
- warn(message: any, context?: string): void;
2192
- debug(message: any, context?: string): void;
2193
- verbose(message: any, context?: string): void;
2194
- setContext(context: string): void;
2195
- /**
2196
- * Unified internal logging method that handles both Winston and NestJS Logger.
2197
- */
2198
- private _log;
2199
- /**
2200
- * Logs with custom metadata (Winston only).
2201
- */
2202
- logWithMetadata(level: LogLevel, message: any, metadata?: LogMetadata, context?: string): void;
2203
- private formatMessage;
2204
- /**
2205
- * Enriches metadata with correlation context from AsyncLocalStorage.
2206
- * Inline from winston-logger.service.ts
2207
- */
2208
- private enrichMetadata;
2209
- child(context: string): LoggerService;
2210
- }
2211
-
2212
2411
  /**
2213
2412
  * Correlation ID Middleware
2214
2413
  *
@@ -2246,38 +2445,13 @@ declare class CorrelationIdMiddleware implements NestMiddleware {
2246
2445
  /**
2247
2446
  * Middleware handler for processing requests.
2248
2447
  */
2249
- use(req: FastifyRequest, reply: FastifyReply, next: () => void): void;
2448
+ use(_req: FastifyRequest, reply: FastifyReply, next: () => void): void;
2250
2449
  /**
2251
2450
  * Fastify hook handler for onRequest.
2252
2451
  * This is an async function that returns a Promise, ensuring the AsyncLocalStorage
2253
2452
  * context persists throughout the entire request lifecycle.
2254
2453
  */
2255
- onRequest(req: FastifyRequest, reply: FastifyReply): Promise<void>;
2256
- }
2257
-
2258
- /**
2259
- * HTTP Logger Interceptor
2260
- *
2261
- * Automatically logs HTTP requests and responses with correlation tracking.
2262
- * @module logger/http-logger.interceptor
2263
- */
2264
-
2265
- /**
2266
- * HTTP Logger Interceptor for NestJS applications.
2267
- *
2268
- * Logs all HTTP requests and responses with metadata including
2269
- * correlation IDs, performance metrics, and error details.
2270
- */
2271
- declare class HttpLoggerInterceptor implements NestInterceptor {
2272
- private readonly logger;
2273
- private readonly enableRequestLog;
2274
- private readonly enableResponseLog;
2275
- private readonly slowRequestThreshold;
2276
- constructor(logger: LoggerService, options?: HttpLoggerOptions);
2277
- intercept(context: ExecutionContext, next: CallHandler): Observable<any>;
2278
- private logRequest;
2279
- private logResponse;
2280
- private logError;
2454
+ onRequest(_req: FastifyRequest, reply: FastifyReply): Promise<void>;
2281
2455
  }
2282
2456
 
2283
2457
  /**
@@ -2317,4 +2491,4 @@ declare function generateCorrelationId(): string;
2317
2491
  */
2318
2492
  declare function addCorrelationIdToResponse(reply: FastifyReply, correlationId: string, headerName?: string): void;
2319
2493
 
2320
- export { type ApiErrorResponse, AuthConfigModule, BadGatewayException, BadRequestException, BaseFieldException, ConflictException, type CorrelationContext, CorrelationIdMiddleware, CsrfGuard, DEFAULT_CORRELATION_HEADER, DatabaseModule, type DatabaseModuleOptions, type FieldError, ForbiddenException, GoneException, HttpExceptionFilter, HttpLoggerInterceptor, type HttpLoggerOptions, HttpModule, InternalServerErrorException, 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, correlationStorage, generateCorrelationId, getCorrelationContext, getHttpStatusTitle, runWithCorrelationContext, updateCorrelationContext };
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, 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 };