@vritti/api-sdk 0.3.3 → 0.3.5

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.
Files changed (133) hide show
  1. package/dist/auth.cjs +942 -0
  2. package/dist/auth.cjs.map +1 -0
  3. package/dist/auth.d.cts +95 -0
  4. package/dist/auth.d.ts +95 -0
  5. package/dist/auth.js +884 -0
  6. package/dist/auth.js.map +1 -0
  7. package/dist/cache.cjs +182 -14
  8. package/dist/cache.cjs.map +1 -1
  9. package/dist/cache.d.cts +48 -4
  10. package/dist/cache.d.ts +48 -4
  11. package/dist/cache.js +177 -12
  12. package/dist/cache.js.map +1 -1
  13. package/dist/catalog-resolver.cjs +226 -51
  14. package/dist/catalog-resolver.cjs.map +1 -1
  15. package/dist/catalog-resolver.d.cts +41 -19
  16. package/dist/catalog-resolver.d.ts +41 -19
  17. package/dist/catalog-resolver.js +212 -46
  18. package/dist/catalog-resolver.js.map +1 -1
  19. package/dist/code-pattern-BP4zkNtT.d.cts +12 -0
  20. package/dist/code-pattern-BP4zkNtT.d.ts +12 -0
  21. package/dist/context.cjs +93 -0
  22. package/dist/context.cjs.map +1 -0
  23. package/dist/context.d.cts +19 -0
  24. package/dist/context.d.ts +19 -0
  25. package/dist/context.js +64 -0
  26. package/dist/context.js.map +1 -0
  27. package/dist/countries.cjs +1559 -0
  28. package/dist/countries.cjs.map +1 -0
  29. package/dist/countries.d.cts +13 -0
  30. package/dist/countries.d.ts +13 -0
  31. package/dist/countries.js +1532 -0
  32. package/dist/countries.js.map +1 -0
  33. package/dist/data-table.cjs +1280 -0
  34. package/dist/data-table.cjs.map +1 -0
  35. package/dist/data-table.d.cts +123 -0
  36. package/dist/data-table.d.ts +123 -0
  37. package/dist/data-table.js +1260 -0
  38. package/dist/data-table.js.map +1 -0
  39. package/dist/database.cjs +1510 -0
  40. package/dist/database.cjs.map +1 -0
  41. package/dist/database.d.cts +287 -0
  42. package/dist/database.d.ts +287 -0
  43. package/dist/database.js +1469 -0
  44. package/dist/database.js.map +1 -0
  45. package/dist/decimal.d.cts +0 -1
  46. package/dist/decimal.d.ts +0 -1
  47. package/dist/decorators.cjs +1817 -0
  48. package/dist/decorators.cjs.map +1 -0
  49. package/dist/decorators.d.cts +24 -0
  50. package/dist/decorators.d.ts +24 -0
  51. package/dist/decorators.js +1780 -0
  52. package/dist/decorators.js.map +1 -0
  53. package/dist/drizzle-pg-core.cjs +25 -0
  54. package/dist/drizzle-pg-core.cjs.map +1 -1
  55. package/dist/drizzle-pg-core.d.cts +7 -0
  56. package/dist/drizzle-pg-core.d.ts +7 -0
  57. package/dist/drizzle-pg-core.js +22 -0
  58. package/dist/drizzle-pg-core.js.map +1 -1
  59. package/dist/exceptions.cjs +301 -0
  60. package/dist/exceptions.cjs.map +1 -0
  61. package/dist/exceptions.d.cts +90 -0
  62. package/dist/exceptions.d.ts +90 -0
  63. package/dist/exceptions.js +257 -0
  64. package/dist/exceptions.js.map +1 -0
  65. package/dist/filter.types-PuARU9XD.d.cts +50 -0
  66. package/dist/filter.types-PuARU9XD.d.ts +50 -0
  67. package/dist/filters.cjs +407 -0
  68. package/dist/filters.cjs.map +1 -0
  69. package/dist/filters.d.cts +55 -0
  70. package/dist/filters.d.ts +55 -0
  71. package/dist/filters.js +377 -0
  72. package/dist/filters.js.map +1 -0
  73. package/dist/index.cjs +0 -5423
  74. package/dist/index.cjs.map +1 -1
  75. package/dist/index.d.cts +93 -854
  76. package/dist/index.d.ts +93 -854
  77. package/dist/index.js +0 -5301
  78. package/dist/index.js.map +1 -1
  79. package/dist/is-country.decorator-CcNwSSr5.d.cts +5 -0
  80. package/dist/is-country.decorator-CcNwSSr5.d.ts +5 -0
  81. package/dist/license.d.cts +1 -1
  82. package/dist/license.d.ts +1 -1
  83. package/dist/lodash.cjs.map +1 -1
  84. package/dist/lodash.js.map +1 -1
  85. package/dist/logger.cjs +862 -0
  86. package/dist/logger.cjs.map +1 -0
  87. package/dist/logger.d.cts +118 -0
  88. package/dist/logger.d.ts +118 -0
  89. package/dist/logger.js +816 -0
  90. package/dist/logger.js.map +1 -0
  91. package/dist/migrate.cjs +2 -2
  92. package/dist/migrate.cjs.map +1 -1
  93. package/dist/migrate.d.cts +2 -0
  94. package/dist/migrate.d.ts +2 -0
  95. package/dist/migrate.js +2 -2
  96. package/dist/migrate.js.map +1 -1
  97. package/dist/money.cjs +18 -3
  98. package/dist/money.cjs.map +1 -1
  99. package/dist/money.d.cts +13 -13
  100. package/dist/money.d.ts +13 -13
  101. package/dist/money.js +18 -3
  102. package/dist/money.js.map +1 -1
  103. package/dist/nats.cjs +34 -35
  104. package/dist/nats.cjs.map +1 -1
  105. package/dist/nats.d.cts +14 -14
  106. package/dist/nats.d.ts +14 -14
  107. package/dist/nats.js +32 -33
  108. package/dist/nats.js.map +1 -1
  109. package/dist/root.cjs +225 -0
  110. package/dist/root.cjs.map +1 -0
  111. package/dist/root.d.cts +4 -0
  112. package/dist/root.d.ts +4 -0
  113. package/dist/root.js +200 -0
  114. package/dist/root.js.map +1 -0
  115. package/dist/{types-DQRwj5rr.d.cts → types-C9jPdQqH.d.cts} +32 -6
  116. package/dist/{types-DQRwj5rr.d.ts → types-C9jPdQqH.d.ts} +32 -6
  117. package/dist/types.cjs +19 -0
  118. package/dist/types.cjs.map +1 -0
  119. package/dist/types.d.cts +17 -0
  120. package/dist/types.d.ts +17 -0
  121. package/dist/types.js +1 -0
  122. package/dist/types.js.map +1 -0
  123. package/dist/utils.cjs +273 -0
  124. package/dist/utils.cjs.map +1 -0
  125. package/dist/utils.d.cts +8 -0
  126. package/dist/utils.d.ts +8 -0
  127. package/dist/utils.js +245 -0
  128. package/dist/utils.js.map +1 -0
  129. package/package.json +122 -1
  130. package/dist/cache.service-_SuJIcNA.d.cts +0 -20
  131. package/dist/cache.service-_SuJIcNA.d.ts +0 -20
  132. package/dist/fastify-augmentation-CQPBNS5a.d.cts +0 -105
  133. package/dist/fastify-augmentation-CQPBNS5a.d.ts +0 -105
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/auth/auth.config.ts","../src/auth/auth-config.module.ts","../src/request/request.module.ts","../src/request/services/request.service.ts","../src/context/resolve-request.ts","../src/auth/guards/vritti-auth.guard.ts","../src/context/extractors/graphql.extractor.ts","../src/context/extractors/http.extractor.ts","../src/context/context.registry.ts","../src/context/get-request.ts","../src/context/get-response.ts","../src/auth/decorators/require-session.decorator.ts","../src/auth/decorators/skip-csrf.decorator.ts","../src/auth/services/token.service.ts","../src/utils/time.utils.ts","../src/auth/utils/token-hash.util.ts","../src/auth/decorators/access-token.decorator.ts","../src/auth/decorators/client-ip.decorator.ts","../src/auth/decorators/cookie-domain.decorator.ts","../src/auth/decorators/cookie-name.decorator.ts","../src/auth/decorators/hostname.decorator.ts","../src/auth/decorators/public.decorator.ts","../src/auth/decorators/refresh-cookie-options.decorator.ts","../src/exceptions/bad-gateway.exception.ts","../src/exceptions/base-field.exception.ts","../src/exceptions/bad-request.exception.ts","../src/exceptions/conflict.exception.ts","../src/exceptions/forbidden.exception.ts","../src/exceptions/gone.exception.ts","../src/exceptions/internal-server-error.exception.ts","../src/exceptions/method-not-allowed.exception.ts","../src/exceptions/not-acceptable.exception.ts","../src/exceptions/not-found.exception.ts","../src/exceptions/not-implemented.exception.ts","../src/exceptions/payload-too-large.exception.ts","../src/exceptions/request-timeout.exception.ts","../src/exceptions/service-unavailable.exception.ts","../src/exceptions/too-many-requests.exception.ts","../src/exceptions/unauthorized.exception.ts","../src/exceptions/unprocessable-entity.exception.ts","../src/exceptions/unsupported-media-type.exception.ts","../src/exceptions/validation.exception.ts","../src/auth/decorators/refresh-token-cookie.decorator.ts","../src/auth/decorators/session-data.decorator.ts","../src/auth/decorators/subdomain.decorator.ts","../src/auth/decorators/user-agent.decorator.ts","../src/auth/decorators/user-id.decorator.ts"],"sourcesContent":["import type { FastifyRequest } from 'fastify';\nimport type { RequestService } from '../request/services/request.service';\n\nexport const AUTH_CONFIG = Symbol('AUTH_CONFIG');\n\nexport type OnAuthenticatedCallback = (\n requestService: RequestService,\n sessionInfo: NonNullable<FastifyRequest['sessionInfo']>,\n) => void | Promise<void>;\n\nexport type TokenExpiryString = `${number}${'s' | 'm' | 'h' | 'd' | 'w' | 'y'}`;\n\nexport interface TokenExpiry {\n access: TokenExpiryString;\n refresh: TokenExpiryString;\n}\n\nexport interface CookieConfig {\n refreshCookieName: string;\n refreshCookieMaxAge: number;\n refreshCookiePath: string;\n refreshCookieSecure: boolean;\n refreshCookieSameSite: 'strict' | 'lax' | 'none';\n refreshCookieDomain?: string;\n}\n\nexport interface GuardConfig {\n authHeaderName: string;\n tokenPrefix: string;\n csrfExemptSessionTypes?: string[];\n csrfExemptTransports?: string[];\n refreshTokenBindingExemptSessionTypes?: string[];\n onAuthenticated?: OnAuthenticatedCallback;\n}\n\nexport interface AuthConfig {\n tokenExpiry: TokenExpiry;\n cookie: CookieConfig;\n guard: GuardConfig;\n}\n\nexport const AUTH_CONFIG_DEFAULTS = {\n cookie: {\n refreshCookieName: 'vritti_refresh',\n refreshCookieMaxAge: 30 * 24 * 60 * 60 * 1000, // 30 days\n refreshCookiePath: '/',\n refreshCookieSecure: process.env.NODE_ENV === 'production',\n refreshCookieSameSite: 'strict' as const,\n refreshCookieDomain: 'localhost',\n },\n guard: {\n authHeaderName: 'authorization',\n tokenPrefix: 'Bearer',\n csrfExemptSessionTypes: [],\n csrfExemptTransports: [],\n refreshTokenBindingExemptSessionTypes: [],\n },\n} satisfies Omit<AuthConfig, 'tokenExpiry'>;\n\nexport interface CookieSerializeOptions {\n httpOnly: boolean;\n secure: boolean;\n sameSite: 'strict' | 'lax' | 'none';\n path: string;\n maxAge: number;\n domain: string;\n}\n\nexport enum TokenType {\n ACCESS = 'access',\n REFRESH = 'refresh',\n}\n\ninterface JwtClaims {\n exp: number;\n iat: number;\n}\n\nexport interface AccessTokenPayload {\n sessionType: string;\n tokenType: TokenType.ACCESS;\n userId: string;\n sessionId: string;\n refreshTokenHash: string;\n}\n\nexport type DecodedAccessToken = AccessTokenPayload & JwtClaims;\n\nexport interface RefreshTokenPayload {\n sessionType: string;\n tokenType: TokenType.REFRESH;\n userId: string;\n sessionId: string;\n}\n\nexport type DecodedRefreshToken = RefreshTokenPayload & JwtClaims;\n","import { type DynamicModule, Global, type InjectionToken, Module } from '@nestjs/common';\nimport { ConfigModule, ConfigService } from '@nestjs/config';\nimport { APP_GUARD, Reflector } from '@nestjs/core';\nimport { JwtModule } from '@nestjs/jwt';\nimport { RequestModule } from '../request/request.module';\nimport {\n AUTH_CONFIG,\n AUTH_CONFIG_DEFAULTS,\n type AuthConfig,\n type CookieConfig,\n type GuardConfig,\n type TokenExpiry,\n} from './auth.config';\nimport { VrittiAuthGuard } from './guards/vritti-auth.guard';\nimport { TokenService } from './services/token.service';\n\n// Factory return type — tokenExpiry is required, cookie and guard are partial with defaults merged\ninterface AuthConfigInput {\n tokenExpiry: TokenExpiry;\n cookie?: Partial<CookieConfig>;\n guard?: Partial<GuardConfig>;\n}\n\ninterface AuthConfigModuleOptions<T extends unknown[] = unknown[]> {\n useFactory: (...args: [...T]) => AuthConfigInput | Promise<AuthConfigInput>;\n inject?: InjectionToken[];\n}\n\n// Merges user-provided partial config with defaults to produce a complete AuthConfig\nfunction mergeWithDefaults(input: AuthConfigInput): AuthConfig {\n return {\n tokenExpiry: input.tokenExpiry,\n cookie: {\n ...AUTH_CONFIG_DEFAULTS.cookie,\n ...(input.cookie ?? {}),\n },\n guard: {\n ...AUTH_CONFIG_DEFAULTS.guard,\n ...(input.guard ?? {}),\n },\n };\n}\n\n@Global()\n@Module({})\nexport class AuthConfigModule {\n // Registers JWT, TokenService, and global VrittiAuthGuard\n static forRootAsync<T extends unknown[] = unknown[]>(options: AuthConfigModuleOptions<T>): DynamicModule {\n return {\n module: AuthConfigModule,\n imports: [\n ConfigModule,\n RequestModule,\n JwtModule.registerAsync({\n imports: [ConfigModule],\n inject: [ConfigService],\n useFactory: (config: ConfigService) => ({\n secret: config.getOrThrow<string>('JWT_SECRET'),\n signOptions: { algorithm: 'HS256' as const },\n }),\n }),\n ],\n providers: [\n {\n provide: Reflector,\n useClass: Reflector,\n },\n {\n provide: APP_GUARD,\n useClass: VrittiAuthGuard,\n },\n {\n provide: AUTH_CONFIG,\n useFactory: async (...args: unknown[]) => {\n const input = await options.useFactory(...(args as [...T]));\n return mergeWithDefaults(input);\n },\n inject: options.inject || [],\n },\n TokenService,\n ],\n exports: [JwtModule, TokenService, AUTH_CONFIG],\n };\n }\n}\n","import { Global, Module } from '@nestjs/common';\nimport { RequestService } from './services/request.service';\n\n@Global()\n@Module({\n providers: [RequestService],\n exports: [RequestService],\n})\nexport class RequestModule {}\n","import { Inject, Injectable, Scope } from '@nestjs/common';\nimport { REQUEST } from '@nestjs/core';\nimport type { FastifyRequest } from 'fastify';\nimport { AUTH_CONFIG, type AuthConfig } from '../../auth/auth.config';\nimport { resolveInjectedRequest } from '../../context/resolve-request';\n\n@Injectable({ scope: Scope.REQUEST })\nexport class RequestService {\n constructor(\n @Inject(REQUEST) private readonly injectedRequest: FastifyRequest,\n @Inject(AUTH_CONFIG) private readonly config: AuthConfig,\n ) {}\n\n // Unwraps the GraphQL { req, reply } context wrapper so every accessor below works across both transports\n private get request(): FastifyRequest {\n return resolveInjectedRequest(this.injectedRequest);\n }\n\n // Extracts the bearer access token from the Authorization header\n getAccessToken(): string | null {\n const authHeader = this.request.headers?.[this.config.guard.authHeaderName];\n if (!authHeader || typeof authHeader !== 'string') {\n return null;\n }\n const [type, token] = authHeader.split(' ') ?? [];\n return type === this.config.guard.tokenPrefix && token ? token : null;\n }\n\n // Extracts the refresh token from the configured httpOnly cookie\n getRefreshToken(): string | null {\n try {\n const cookies = (this.request as unknown as { cookies?: Record<string, string> }).cookies;\n if (cookies && typeof cookies === 'object') {\n const refreshToken = cookies[this.config.cookie.refreshCookieName];\n if (refreshToken) {\n return refreshToken;\n }\n }\n return null;\n } catch (_error: unknown) {\n return null;\n }\n }\n\n // Returns the value of a specific request header by key\n getHeader(key: string): string | string[] | undefined {\n return this.request.headers?.[key];\n }\n\n // Returns the request hostname (without port)\n getHostname(): string {\n return this.request.hostname ?? '';\n }\n\n // Returns all request headers\n getAllHeaders(): FastifyRequest['headers'] {\n return this.request.headers || {};\n }\n}\n","import type { FastifyRequest } from 'fastify';\n\n// Unwraps the @Inject(REQUEST) value to the real Fastify request (GraphQL injects a { req, reply } context).\nexport function resolveInjectedRequest(injected: FastifyRequest): FastifyRequest {\n const candidate = injected as unknown as { headers?: unknown; req?: FastifyRequest };\n if (candidate && candidate.headers === undefined && candidate.req) {\n return candidate.req;\n }\n return injected;\n}\n","import {\n type CanActivate,\n type ExecutionContext,\n ForbiddenException,\n Inject,\n Injectable,\n Logger,\n Scope,\n UnauthorizedException,\n} from '@nestjs/common';\nimport { SSE_METADATA } from '@nestjs/common/constants';\nimport { Reflector } from '@nestjs/core';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport '../../types/fastify-augmentation';\nimport { getRequestFromContext, getResponseFromContext } from '../../context';\nimport { RequestService } from '../../request/services/request.service';\nimport { AUTH_CONFIG, type AuthConfig } from '../auth.config';\nimport { REQUIRE_SESSION_KEY } from '../decorators/require-session.decorator';\nimport { SKIP_CSRF_KEY } from '../decorators/skip-csrf.decorator';\nimport { TokenService } from '../services/token.service';\n\ninterface FastifyInstanceWithCsrf {\n csrfProtection?: (req: FastifyRequest, reply: FastifyReply, next: (err?: Error) => void) => void;\n}\n\ntype PatchableReply = { send: (...args: unknown[]) => unknown };\n\n@Injectable({ scope: Scope.REQUEST })\nexport class VrittiAuthGuard implements CanActivate {\n private readonly logger = new Logger(VrittiAuthGuard.name);\n\n constructor(\n private readonly reflector: Reflector,\n private readonly requestService: RequestService,\n private readonly tokenService: TokenService,\n @Inject(AUTH_CONFIG) private readonly config: AuthConfig,\n ) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = getRequestFromContext(context);\n const reply = getResponseFromContext(context);\n const route = `${request.method} ${request.url}`;\n\n // Attach auth config to request so decorators can access it without injection\n request.authConfig = this.config;\n\n // CSRF is skipped via @SkipCsrf() or when the request transport is CSRF-exempt (e.g. 'graphql')\n const csrfExemptTransports = this.config.guard.csrfExemptTransports ?? [];\n const skipCsrf =\n this.reflector.getAllAndOverride<boolean>(SKIP_CSRF_KEY, [context.getHandler(), context.getClass()]) ||\n csrfExemptTransports.includes(context.getType<string>());\n\n // @Public() endpoints skip auth, while preserving their current CSRF behavior\n const isPublic = this.reflector.getAllAndOverride<boolean>('isPublic', [context.getHandler(), context.getClass()]);\n if (isPublic) {\n if (!skipCsrf) {\n await this.validateCsrf(request, reply);\n }\n this.logger.debug(`${route} — public endpoint, skipping auth`);\n return true;\n }\n\n // @RequireSession() restricts access to specific session types\n const requiredSessionTypes = this.reflector.getAllAndOverride<string[]>(REQUIRE_SESSION_KEY, [\n context.getHandler(),\n context.getClass(),\n ]);\n\n // SSE endpoints authenticate via refresh token cookie (EventSource cannot send Authorization headers)\n const isSseEndpoint = this.reflector.get<boolean>(SSE_METADATA, context.getHandler());\n if (isSseEndpoint) {\n this.logger.debug(`${route} — SSE endpoint, authenticating via refresh cookie`);\n return this.handleSseAuth(request, requiredSessionTypes);\n }\n\n const sessionType = await this.handleHttpAuth(request, requiredSessionTypes);\n\n const csrfExemptSessionTypes = this.config.guard.csrfExemptSessionTypes ?? [];\n if (!skipCsrf && !csrfExemptSessionTypes.includes(sessionType)) {\n await this.validateCsrf(request, reply);\n }\n\n return true;\n }\n\n // Authenticates standard HTTP requests using the access token from Authorization header\n private async handleHttpAuth(request: FastifyRequest, requiredSessionTypes?: string[]): Promise<string> {\n const route = `${request.method} ${request.url}`;\n\n const accessToken = this.requestService.getAccessToken();\n if (!accessToken) {\n this.logger.warn(`${route} — no access token found`);\n throw new UnauthorizedException('Access token not found');\n }\n\n const decoded = this.tokenService.validateAccessToken(accessToken);\n\n const refreshTokenBindingExemptSessionTypes = this.config.guard.refreshTokenBindingExemptSessionTypes ?? [];\n\n if (!refreshTokenBindingExemptSessionTypes.includes(decoded.sessionType)) {\n const refreshToken = this.requestService.getRefreshToken();\n if (!refreshToken) {\n throw new UnauthorizedException('Session validation failed');\n }\n this.tokenService.validateTokenBinding(decoded, refreshToken);\n }\n\n // Validate session type access (only if @RequireSession specifies types)\n if (requiredSessionTypes?.length && !requiredSessionTypes.includes(decoded.sessionType)) {\n this.logger.warn(\n `${route} — session type ${decoded.sessionType} not in allowed: [${requiredSessionTypes.join(', ')}]`,\n );\n throw new UnauthorizedException(`${decoded.sessionType} sessions cannot access this endpoint`);\n }\n\n // Attach session info to request — spread full decoded token (includes metadata fields)\n const { tokenType: _tokenType, refreshTokenHash: _hash, exp: _exp, iat: _iat, ...sessionInfo } = decoded;\n request.sessionInfo = sessionInfo;\n\n // Call onAuthenticated callback if configured\n const onAuthenticated = this.config.guard.onAuthenticated;\n if (onAuthenticated) {\n await onAuthenticated(this.requestService, request.sessionInfo);\n }\n\n this.logger.debug(`${route} — authenticated user: ${decoded.userId} (${decoded.sessionType})`);\n return decoded.sessionType;\n }\n\n // Authenticates SSE connections using the refresh token httpOnly cookie\n private handleSseAuth(request: FastifyRequest, requiredSessionTypes?: string[]): boolean {\n const refreshToken = this.requestService.getRefreshToken();\n if (!refreshToken) {\n this.logger.warn(`SSE ${request.url} — no refresh token cookie`);\n throw new UnauthorizedException('Authentication required');\n }\n\n const decoded = this.tokenService.validateRefreshToken(refreshToken);\n\n if (requiredSessionTypes?.length && !requiredSessionTypes.includes(decoded.sessionType)) {\n this.logger.warn(`SSE ${request.url} — session type ${decoded.sessionType} not allowed`);\n throw new UnauthorizedException(`${decoded.sessionType} sessions cannot access this endpoint`);\n }\n\n const { tokenType: _tokenType, exp: _exp, iat: _iat, ...sessionInfo } = decoded;\n request.sessionInfo = sessionInfo;\n\n this.logger.debug(`SSE ${request.url} — authenticated user: ${decoded.userId} (${decoded.sessionType})`);\n return true;\n }\n\n // Validates CSRF token for state-changing requests\n private async validateCsrf(request: FastifyRequest, reply: FastifyReply): Promise<void> {\n const safeMethods = ['GET', 'HEAD', 'OPTIONS'];\n if (safeMethods.includes(request.method)) return;\n\n try {\n const fastifyInstance = request.server as unknown as FastifyInstanceWithCsrf;\n const csrfProtection = fastifyInstance.csrfProtection;\n if (!csrfProtection) {\n throw new ForbiddenException('CSRF protection not configured');\n }\n\n await new Promise<void>((resolve, reject) => {\n const originalSend = reply.send.bind(reply);\n (reply as PatchableReply).send = () => {\n (reply as PatchableReply).send = originalSend as PatchableReply['send'];\n reject(new Error('CSRF validation failed'));\n return reply;\n };\n\n csrfProtection(request, reply, (err?: Error) => {\n (reply as PatchableReply).send = originalSend as PatchableReply['send'];\n if (err) reject(err);\n else resolve();\n });\n });\n } catch (_error: unknown) {\n this.logger.warn(`${request.method} ${request.url} — CSRF validation failed`);\n throw new ForbiddenException({\n errors: [{ field: 'csrf', message: 'Invalid or missing CSRF token' }],\n message: 'CSRF validation failed',\n });\n }\n }\n}\n","import type { ArgumentsHost } from '@nestjs/common';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport type { RequestExtractor } from '../context.types';\n\ninterface GqlContext {\n req: FastifyRequest;\n reply?: FastifyReply;\n}\n\nexport const graphqlExtractor: RequestExtractor = {\n getRequest: (host: ArgumentsHost) => host.getArgByIndex<GqlContext>(2).req,\n getResponse: (host: ArgumentsHost) => host.getArgByIndex<GqlContext>(2).reply as FastifyReply,\n};\n","import type { FastifyReply, FastifyRequest } from 'fastify';\nimport type { RequestExtractor } from '../context.types';\n\n// Default transport: standard HTTP via the Fastify adapter.\nexport const httpExtractor: RequestExtractor = {\n getRequest: (host) => host.switchToHttp().getRequest<FastifyRequest>(),\n getResponse: (host) => host.switchToHttp().getResponse<FastifyReply>(),\n};\n","import type { RequestExtractor, TransportType } from './context.types';\nimport { graphqlExtractor } from './extractors/graphql.extractor';\nimport { httpExtractor } from './extractors/http.extractor';\n\nconst registry = new Map<TransportType, RequestExtractor>([\n ['http', httpExtractor],\n ['graphql', graphqlExtractor],\n]);\n\n// Registers (or overrides) the extractor for a transport type without modifying the SDK\nexport function registerTransport(type: TransportType, extractor: RequestExtractor): void {\n registry.set(type, extractor);\n}\n\n// Resolves the extractor for the host's reported transport, falling back to HTTP.\nexport function resolveExtractor(type: string): RequestExtractor {\n return registry.get(type as TransportType) ?? httpExtractor;\n}\n","import type { ArgumentsHost } from '@nestjs/common';\nimport type { FastifyRequest } from 'fastify';\nimport { resolveExtractor } from './context.registry';\n\n// Returns the underlying Fastify request for any registered transport, via the transport registry.\nexport function getRequestFromContext(host: ArgumentsHost): FastifyRequest {\n return resolveExtractor(host.getType()).getRequest(host);\n}\n","import type { ArgumentsHost } from '@nestjs/common';\nimport type { FastifyReply } from 'fastify';\nimport { resolveExtractor } from './context.registry';\n\n// Returns the underlying Fastify reply for any registered transport (HTTP, GraphQL, ...).\nexport function getResponseFromContext(host: ArgumentsHost): FastifyReply {\n return resolveExtractor(host.getType()).getResponse(host);\n}\n","import { SetMetadata } from '@nestjs/common';\n\n// Restricts endpoint access to specific session types\nexport const REQUIRE_SESSION_KEY = 'requiredSessionTypes';\nexport const RequireSession = (...types: string[]) => SetMetadata(REQUIRE_SESSION_KEY, types);\n","import { SetMetadata } from '@nestjs/common';\n\nexport const SKIP_CSRF_KEY = 'skipCsrf';\n\nexport const SkipCsrf = () => SetMetadata(SKIP_CSRF_KEY, true);\n","import { Inject, Injectable, Logger, UnauthorizedException } from '@nestjs/common';\nimport { JwtService, type JwtSignOptions } from '@nestjs/jwt';\nimport type { FastifyRequest } from 'fastify';\nimport '../../types/fastify-augmentation';\nimport { parseExpiryToMs } from '../../utils/time.utils';\nimport {\n AUTH_CONFIG,\n type AuthConfig,\n type DecodedAccessToken,\n type DecodedRefreshToken,\n TokenType,\n} from '../auth.config';\nimport { hashToken, verifyTokenHash } from '../utils/token-hash.util';\n\nexport type { DecodedAccessToken, DecodedRefreshToken };\n\n// Session info type from Fastify augmentation\ntype SessionInfo = NonNullable<FastifyRequest['sessionInfo']>;\n\ninterface TokenError extends Error {\n name: 'TokenExpiredError' | 'JsonWebTokenError' | 'NotBeforeError';\n}\n\n// Handles all token operations — generation, validation, and binding verification\n@Injectable()\nexport class TokenService {\n private readonly logger = new Logger(TokenService.name);\n\n constructor(\n private readonly jwtService: JwtService,\n @Inject(AUTH_CONFIG) private readonly config: AuthConfig,\n ) {}\n\n // ---- Generation ----\n\n // Generates an access token bound to the given refresh token\n generateAccessToken(sessionInfo: SessionInfo, refreshToken: string): string {\n const { userId, sessionId, sessionType, ...metadata } = sessionInfo;\n return this.jwtService.sign(\n {\n sessionType,\n tokenType: TokenType.ACCESS,\n userId,\n sessionId,\n refreshTokenHash: hashToken(refreshToken),\n ...metadata,\n },\n { expiresIn: this.config.tokenExpiry.access },\n );\n }\n\n // Generates a refresh token for session persistence\n generateRefreshToken(sessionInfo: SessionInfo): string {\n const { userId, sessionId, sessionType, ...metadata } = sessionInfo;\n return this.jwtService.sign(\n { sessionType, tokenType: TokenType.REFRESH, userId, sessionId, ...metadata },\n { expiresIn: this.config.tokenExpiry.refresh },\n );\n }\n\n // Signs an arbitrary payload with optional JWT options\n sign(payload: object, options?: JwtSignOptions): string {\n return this.jwtService.sign(payload, options);\n }\n\n // Verifies a token and ensures it matches the expected token type\n verify(\n token: string,\n expectedType: TokenType,\n ): { userId: string; sessionId: string; sessionType: string; tokenType: TokenType } {\n try {\n const payload = this.jwtService.verify(token);\n\n if (payload.tokenType !== expectedType) {\n throw new Error(`Expected ${expectedType} token, got ${payload.tokenType}`);\n }\n\n return payload;\n } catch (error) {\n this.logger.error(`Failed to verify ${expectedType} token`, error);\n throw error;\n }\n }\n\n // Returns the expiry as a Date for the given token type\n getExpiryTime(type: TokenType): Date {\n return new Date(Date.now() + parseExpiryToMs(this.config.tokenExpiry[type]));\n }\n\n // Returns the token lifetime in seconds for the given type\n getExpiryInSeconds(type: TokenType): number {\n return Math.floor(parseExpiryToMs(this.config.tokenExpiry[type]) / 1000);\n }\n\n // ---- Validation ----\n\n // Decodes and validates an access token JWT\n validateAccessToken(token: string): DecodedAccessToken {\n try {\n const decoded = this.jwtService.verify<DecodedAccessToken>(token);\n\n if (decoded.tokenType !== TokenType.ACCESS) {\n throw new UnauthorizedException('Invalid token type');\n }\n\n return decoded;\n } catch (error) {\n if (error instanceof UnauthorizedException) throw error;\n\n const jwtError = error as TokenError;\n switch (jwtError.name) {\n case 'TokenExpiredError':\n throw new UnauthorizedException('Access token has expired');\n case 'JsonWebTokenError':\n throw new UnauthorizedException('Invalid access token');\n case 'NotBeforeError':\n throw new UnauthorizedException('Access token not yet valid');\n default:\n throw new UnauthorizedException('Access token validation failed');\n }\n }\n }\n\n // Decodes and validates a refresh token JWT\n validateRefreshToken(token: string): DecodedRefreshToken {\n try {\n const decoded = this.jwtService.verify<DecodedRefreshToken>(token);\n\n if (decoded.tokenType !== TokenType.REFRESH) {\n throw new UnauthorizedException('Invalid token type');\n }\n\n return decoded;\n } catch (error) {\n if (error instanceof UnauthorizedException) throw error;\n throw new UnauthorizedException('Invalid or expired session');\n }\n }\n\n // Validates that the access token is bound to the refresh token\n validateTokenBinding(accessToken: DecodedAccessToken, refreshToken: string): void {\n if (!verifyTokenHash(refreshToken, accessToken.refreshTokenHash)) {\n throw new UnauthorizedException('Session validation failed');\n }\n }\n}\n","// Parses a duration string (e.g. '10m', '1h', '30s', '7d') to milliseconds\nexport function parseExpiryToMs(expiry: string): number {\n const match = expiry.match(/^(\\d+)([smhdwy])$/);\n if (!match) throw new Error(`Invalid expiry format: ${expiry}`);\n\n const [, digits = '', unit = ''] = match;\n const value = Number.parseInt(digits, 10);\n const multipliers: Record<string, number> = {\n s: 1000,\n m: 60_000,\n h: 3_600_000,\n d: 86_400_000,\n w: 604_800_000,\n y: 31_536_000_000,\n };\n\n const multiplier = multipliers[unit];\n if (multiplier === undefined) throw new Error(`Invalid expiry format: ${expiry}`);\n\n return value * multiplier;\n}\n","import * as crypto from 'node:crypto';\n\n// Hashes a token using SHA-256\nexport function hashToken(token: string): string {\n return crypto.createHash('sha256').update(token).digest('hex');\n}\n\n// Verifies a token against its stored hash\nexport function verifyTokenHash(token: string, expectedHash: string): boolean {\n const computedHash = hashToken(token);\n if (computedHash.length !== expectedHash.length) return false;\n return crypto.timingSafeEqual(Buffer.from(computedHash, 'hex'), Buffer.from(expectedHash, 'hex'));\n}\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\n\n// Extracts the bearer token from the Authorization header\nexport const AccessToken = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {\n const request = getRequestFromContext(ctx);\n const authHeader = request.headers.authorization;\n return authHeader?.replace('Bearer ', '') || '';\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\n\n// Extracts the client IP from the request, working across HTTP and GraphQL transports\nexport const ClientIp = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {\n return getRequestFromContext(ctx).ip;\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\nimport { AUTH_CONFIG_DEFAULTS } from '../auth.config';\n\n// Extracts the cookie domain from x-forwarded-host (injected by proxy), validated against baseDomain, falls back to baseDomain if invalid\nexport const CookieDomain = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {\n const request = getRequestFromContext(ctx);\n const forwarded = request.headers['x-forwarded-host'];\n const raw = Array.isArray(forwarded) ? forwarded[0] : forwarded;\n const hostStr = raw ?? request.hostname;\n const domain = hostStr.split(':')[0] ?? hostStr;\n const baseDomain =\n request.authConfig?.cookie.refreshCookieDomain ?? AUTH_CONFIG_DEFAULTS.cookie.refreshCookieDomain ?? '';\n return domain.endsWith(`.${baseDomain}`) ? domain : baseDomain;\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\nimport { AUTH_CONFIG_DEFAULTS } from '../auth.config';\n\n// Returns the configured refresh cookie name from request.authConfig\nexport const CookieName = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {\n const request = getRequestFromContext(ctx);\n return request.authConfig?.cookie.refreshCookieName ?? AUTH_CONFIG_DEFAULTS.cookie.refreshCookieName;\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\n\n// Extracts the request hostname from x-forwarded-host (set by reverse proxies and dev proxy) with fallback to request.hostname\nexport const Hostname = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {\n const request = getRequestFromContext(ctx);\n const forwarded = request.headers['x-forwarded-host'];\n const raw = Array.isArray(forwarded) ? forwarded[0] : forwarded;\n const hostStr = raw ?? request.hostname;\n return hostStr.split(':')[0] ?? hostStr;\n});\n","import { SetMetadata } from '@nestjs/common';\n\nexport const Public = () => SetMetadata('isPublic', true);\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\nimport { UnauthorizedException } from '../../exceptions';\nimport { AUTH_CONFIG_DEFAULTS, type CookieConfig, type CookieSerializeOptions } from '../auth.config';\n\n// Builds cookie serialize options from the given cookie config\nfunction buildCookieOptionsForHost(cookieConfig: CookieConfig, hostname: string): CookieSerializeOptions {\n const baseDomain = cookieConfig.refreshCookieDomain;\n\n if (!baseDomain) {\n throw new Error('refreshCookieDomain must be configured before using @RefreshCookieOptions()');\n }\n\n if (!hostname.endsWith(`.${baseDomain}`)) {\n throw new UnauthorizedException('Invalid request host.');\n }\n\n return {\n httpOnly: true,\n secure: cookieConfig.refreshCookieSecure,\n sameSite: cookieConfig.refreshCookieSameSite,\n path: cookieConfig.refreshCookiePath,\n maxAge: cookieConfig.refreshCookieMaxAge,\n domain: hostname,\n };\n}\n\n// Returns refresh cookie options with domain scoped to the request subdomain (reads x-forwarded-host injected by proxy)\nexport const RefreshCookieOptions = createParamDecorator(\n (_data: unknown, ctx: ExecutionContext): CookieSerializeOptions => {\n const request = getRequestFromContext(ctx);\n const forwarded = request.headers['x-forwarded-host'];\n const raw = Array.isArray(forwarded) ? forwarded[0] : forwarded;\n const hostStr = raw ?? request.hostname;\n const domain = hostStr.split(':')[0] ?? hostStr;\n const cookieConfig: CookieConfig = request.authConfig?.cookie ?? AUTH_CONFIG_DEFAULTS.cookie;\n return buildCookieOptionsForHost(cookieConfig, domain);\n },\n);\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class BadGatewayException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Bad Gateway', HttpStatus.BAD_GATEWAY);\n }\n}\n","import { HttpException, HttpStatus } from '@nestjs/common';\nimport type { FieldError } from '../types/error-response.types';\n\n// Re-export FieldError for backwards compatibility\nexport type { FieldError } from '../types/error-response.types';\n\nexport interface ProblemOptions {\n type?: string;\n label?: string;\n detail?: string;\n errors?: FieldError[];\n}\n\nexport abstract class HttpProblemException extends HttpException {\n constructor(detailOrOptions: string | ProblemOptions, httpStatus: HttpStatus) {\n const options = typeof detailOrOptions === 'string' ? { detail: detailOrOptions } : detailOrOptions;\n\n super(\n {\n type: options.type ?? 'about:blank',\n label: options.label,\n detail: options.detail,\n errors: options.errors ?? [],\n },\n httpStatus,\n );\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class BadRequestException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Bad Request', HttpStatus.BAD_REQUEST);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ConflictException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Conflict', HttpStatus.CONFLICT);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ForbiddenException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Forbidden', HttpStatus.FORBIDDEN);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class GoneException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Gone', HttpStatus.GONE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class InternalServerErrorException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Internal Server Error', HttpStatus.INTERNAL_SERVER_ERROR);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class MethodNotAllowedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Method Not Allowed', HttpStatus.METHOD_NOT_ALLOWED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class NotAcceptableException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Acceptable', HttpStatus.NOT_ACCEPTABLE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class NotFoundException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Found', HttpStatus.NOT_FOUND);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class NotImplementedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Implemented', HttpStatus.NOT_IMPLEMENTED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class PayloadTooLargeException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Payload Too Large', HttpStatus.PAYLOAD_TOO_LARGE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class RequestTimeoutException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Request Timeout', HttpStatus.REQUEST_TIMEOUT);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ServiceUnavailableException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Service Unavailable', HttpStatus.SERVICE_UNAVAILABLE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class TooManyRequestsException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Too Many Requests', HttpStatus.TOO_MANY_REQUESTS);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class UnauthorizedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unauthorized', HttpStatus.UNAUTHORIZED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class UnprocessableEntityException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unprocessable Entity', HttpStatus.UNPROCESSABLE_ENTITY);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class UnsupportedMediaTypeException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unsupported Media Type', HttpStatus.UNSUPPORTED_MEDIA_TYPE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ValidationException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Validation Failed', HttpStatus.UNPROCESSABLE_ENTITY);\n }\n}\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\nimport { AUTH_CONFIG_DEFAULTS } from '../auth.config';\n\nexport const RefreshTokenCookie = createParamDecorator((_data: unknown, ctx: ExecutionContext): string | undefined => {\n const request = getRequestFromContext(ctx);\n const cookies = request.cookies ?? {};\n const cookieName = request.authConfig?.cookie.refreshCookieName ?? AUTH_CONFIG_DEFAULTS.cookie.refreshCookieName;\n return cookies[cookieName] as string | undefined;\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\nimport '../../types/fastify-augmentation';\n\nexport interface SessionInfo {\n userId: string;\n sessionId: string;\n sessionType: string;\n}\n\n// Returns full decoded session info from request.sessionInfo (set by VrittiAuthGuard)\nexport const SessionData = createParamDecorator((_data: unknown, ctx: ExecutionContext): SessionInfo => {\n const request = getRequestFromContext(ctx);\n const sessionInfo = request.sessionInfo;\n\n if (!sessionInfo?.sessionId) {\n throw new Error('Session info not found on request. Ensure route is protected by auth guard.');\n }\n\n return {\n userId: sessionInfo.userId,\n sessionId: sessionInfo.sessionId,\n sessionType: sessionInfo.sessionType,\n };\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\n\n// Extracts the subdomain from the request's origin or x-forwarded-host header\nexport const Subdomain = createParamDecorator((_data: unknown, ctx: ExecutionContext): string | undefined => {\n const request = getRequestFromContext(ctx);\n\n // Try origin header first (browser always sends this on cross-origin requests)\n const origin = request.headers.origin;\n if (origin) {\n try {\n const url = new URL(origin);\n return url.hostname.split('.')[0];\n } catch {\n // Invalid origin, fall through\n }\n }\n\n // Fallback to x-forwarded-host (set by rsbuild proxy and production reverse proxies)\n const forwarded = request.headers['x-forwarded-host'];\n const host = Array.isArray(forwarded) ? forwarded[0] : forwarded;\n if (host) return host.split('.')[0];\n\n return undefined;\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\n\n// Extracts the User-Agent header from the request, working across HTTP and GraphQL transports\nexport const UserAgent = createParamDecorator((_data: unknown, ctx: ExecutionContext): string | undefined => {\n const userAgent = getRequestFromContext(ctx).headers['user-agent'];\n return Array.isArray(userAgent) ? userAgent[0] : userAgent;\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\nimport '../../types/fastify-augmentation';\n\n// Extracts userId from request.sessionInfo (set by VrittiAuthGuard)\nexport const UserId = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {\n const request = getRequestFromContext(ctx);\n const sessionInfo = request.sessionInfo;\n\n if (!sessionInfo?.userId) {\n throw new Error('User ID not found on request. Ensure route is protected by auth guard.');\n }\n\n return sessionInfo.userId;\n});\n"],"mappings":"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","names":["AUTH_CONFIG","Symbol","AUTH_CONFIG_DEFAULTS","cookie","refreshCookieName","refreshCookieMaxAge","refreshCookiePath","refreshCookieSecure","process","env","NODE_ENV","refreshCookieSameSite","refreshCookieDomain","guard","authHeaderName","tokenPrefix","csrfExemptSessionTypes","csrfExemptTransports","refreshTokenBindingExemptSessionTypes","TokenType","Global","Module","ConfigModule","ConfigService","APP_GUARD","Reflector","JwtModule","Global","Module","Inject","Injectable","Scope","REQUEST","resolveInjectedRequest","injected","candidate","headers","undefined","req","RequestService","injectedRequest","config","request","resolveInjectedRequest","getAccessToken","authHeader","headers","guard","authHeaderName","type","token","split","tokenPrefix","getRefreshToken","cookies","refreshToken","cookie","refreshCookieName","_error","getHeader","key","getHostname","hostname","getAllHeaders","scope","Scope","REQUEST","RequestModule","providers","RequestService","exports","ForbiddenException","Inject","Injectable","Logger","Scope","UnauthorizedException","SSE_METADATA","Reflector","graphqlExtractor","getRequest","host","getArgByIndex","req","getResponse","reply","httpExtractor","getRequest","host","switchToHttp","getResponse","registry","Map","httpExtractor","graphqlExtractor","resolveExtractor","type","registry","get","httpExtractor","getRequestFromContext","host","resolveExtractor","getType","getRequest","getResponseFromContext","host","resolveExtractor","getType","getResponse","SetMetadata","REQUIRE_SESSION_KEY","RequireSession","types","SetMetadata","SetMetadata","SKIP_CSRF_KEY","SkipCsrf","SetMetadata","Inject","Injectable","Logger","UnauthorizedException","JwtService","parseExpiryToMs","expiry","match","Error","digits","unit","value","Number","parseInt","multipliers","s","m","h","d","w","y","multiplier","undefined","crypto","hashToken","token","createHash","update","digest","verifyTokenHash","expectedHash","computedHash","length","timingSafeEqual","Buffer","from","TokenService","logger","Logger","name","jwtService","config","generateAccessToken","sessionInfo","refreshToken","userId","sessionId","sessionType","metadata","sign","tokenType","TokenType","ACCESS","refreshTokenHash","hashToken","expiresIn","tokenExpiry","access","generateRefreshToken","REFRESH","refresh","payload","options","verify","token","expectedType","Error","error","getExpiryTime","type","Date","now","parseExpiryToMs","getExpiryInSeconds","Math","floor","validateAccessToken","decoded","UnauthorizedException","jwtError","validateRefreshToken","validateTokenBinding","accessToken","verifyTokenHash","VrittiAuthGuard","logger","Logger","name","reflector","requestService","tokenService","config","canActivate","context","request","getRequestFromContext","reply","getResponseFromContext","route","method","url","authConfig","csrfExemptTransports","guard","skipCsrf","getAllAndOverride","SKIP_CSRF_KEY","getHandler","getClass","includes","getType","isPublic","validateCsrf","debug","requiredSessionTypes","REQUIRE_SESSION_KEY","isSseEndpoint","get","SSE_METADATA","handleSseAuth","sessionType","handleHttpAuth","csrfExemptSessionTypes","accessToken","getAccessToken","warn","UnauthorizedException","decoded","validateAccessToken","refreshTokenBindingExemptSessionTypes","refreshToken","getRefreshToken","validateTokenBinding","length","join","tokenType","_tokenType","refreshTokenHash","_hash","exp","_exp","iat","_iat","sessionInfo","onAuthenticated","userId","validateRefreshToken","safeMethods","fastifyInstance","server","csrfProtection","ForbiddenException","Promise","resolve","reject","originalSend","send","bind","Error","err","_error","errors","field","message","scope","Scope","REQUEST","mergeWithDefaults","input","tokenExpiry","cookie","AUTH_CONFIG_DEFAULTS","guard","AuthConfigModule","forRootAsync","options","module","imports","ConfigModule","RequestModule","JwtModule","registerAsync","inject","ConfigService","useFactory","config","secret","getOrThrow","signOptions","algorithm","providers","provide","Reflector","useClass","APP_GUARD","VrittiAuthGuard","AUTH_CONFIG","args","TokenService","exports","createParamDecorator","AccessToken","createParamDecorator","_data","ctx","request","getRequestFromContext","authHeader","headers","authorization","replace","createParamDecorator","ClientIp","createParamDecorator","_data","ctx","getRequestFromContext","ip","createParamDecorator","CookieDomain","createParamDecorator","_data","ctx","request","getRequestFromContext","forwarded","headers","raw","Array","isArray","hostStr","hostname","domain","split","baseDomain","authConfig","cookie","refreshCookieDomain","AUTH_CONFIG_DEFAULTS","endsWith","createParamDecorator","CookieName","createParamDecorator","_data","ctx","request","getRequestFromContext","authConfig","cookie","refreshCookieName","AUTH_CONFIG_DEFAULTS","createParamDecorator","Hostname","createParamDecorator","_data","ctx","request","getRequestFromContext","forwarded","headers","raw","Array","isArray","hostStr","hostname","split","SetMetadata","Public","SetMetadata","createParamDecorator","HttpStatus","HttpException","HttpProblemException","HttpException","detailOrOptions","httpStatus","options","detail","type","label","errors","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","UnauthorizedException","HttpProblemException","detailOrOptions","HttpStatus","UNAUTHORIZED","HttpStatus","HttpStatus","HttpStatus","buildCookieOptionsForHost","cookieConfig","hostname","baseDomain","refreshCookieDomain","Error","endsWith","UnauthorizedException","httpOnly","secure","refreshCookieSecure","sameSite","refreshCookieSameSite","path","refreshCookiePath","maxAge","refreshCookieMaxAge","domain","RefreshCookieOptions","createParamDecorator","_data","ctx","request","getRequestFromContext","forwarded","headers","raw","Array","isArray","hostStr","split","authConfig","cookie","AUTH_CONFIG_DEFAULTS","createParamDecorator","RefreshTokenCookie","createParamDecorator","_data","ctx","request","getRequestFromContext","cookies","cookieName","authConfig","cookie","refreshCookieName","AUTH_CONFIG_DEFAULTS","createParamDecorator","SessionData","createParamDecorator","_data","ctx","request","getRequestFromContext","sessionInfo","sessionId","Error","userId","sessionType","createParamDecorator","Subdomain","createParamDecorator","_data","ctx","request","getRequestFromContext","origin","headers","url","URL","hostname","split","forwarded","host","Array","isArray","undefined","createParamDecorator","UserAgent","createParamDecorator","_data","ctx","userAgent","getRequestFromContext","headers","Array","isArray","createParamDecorator","UserId","createParamDecorator","_data","ctx","request","getRequestFromContext","sessionInfo","userId","Error"]}
package/dist/cache.cjs CHANGED
@@ -34,12 +34,15 @@ __export(cache_exports, {
34
34
  CACHE_PROVIDER: () => CACHE_PROVIDER,
35
35
  CacheModule: () => CacheModule,
36
36
  CacheService: () => CacheService,
37
+ DEFAULT_LRU_MAX: () => DEFAULT_LRU_MAX,
38
+ LRU_CACHE_OPTIONS: () => LRU_CACHE_OPTIONS,
39
+ LruCacheProvider: () => LruCacheProvider,
37
40
  RedisCacheProvider: () => RedisCacheProvider
38
41
  });
39
42
  module.exports = __toCommonJS(cache_exports);
40
43
 
41
44
  // src/cache/cache.module.ts
42
- var import_common3 = require("@nestjs/common");
45
+ var import_common4 = require("@nestjs/common");
43
46
  var import_config2 = require("@nestjs/config");
44
47
 
45
48
  // src/cache/cache.service.ts
@@ -47,6 +50,9 @@ var import_common = require("@nestjs/common");
47
50
 
48
51
  // src/cache/constants.ts
49
52
  var CACHE_PROVIDER = Symbol("CACHE_PROVIDER");
53
+ var LRU_CACHE_OPTIONS = Symbol("LRU_CACHE_OPTIONS");
54
+ var CACHE_MODULE_OPTIONS = Symbol("CACHE_MODULE_OPTIONS");
55
+ var DEFAULT_LRU_MAX = 1e4;
50
56
 
51
57
  // src/cache/cache.service.ts
52
58
  function _ts_decorate(decorators, target, key, desc) {
@@ -128,10 +134,9 @@ CacheService = _ts_decorate([
128
134
  ])
129
135
  ], CacheService);
130
136
 
131
- // src/cache/providers/redis.provider.ts
137
+ // src/cache/providers/lru.provider.ts
132
138
  var import_common2 = require("@nestjs/common");
133
- var import_config = require("@nestjs/config");
134
- var import_ioredis = __toESM(require("ioredis"), 1);
139
+ var import_lru_cache = require("lru-cache");
135
140
  function _ts_decorate2(decorators, target, key, desc) {
136
141
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
137
142
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -143,12 +148,87 @@ function _ts_metadata2(k, v) {
143
148
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
144
149
  }
145
150
  __name(_ts_metadata2, "_ts_metadata");
151
+ function _ts_param2(paramIndex, decorator) {
152
+ return function(target, key) {
153
+ decorator(target, key, paramIndex);
154
+ };
155
+ }
156
+ __name(_ts_param2, "_ts_param");
157
+ function globToRegExp(pattern) {
158
+ const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
159
+ return new RegExp(`^${escaped}$`);
160
+ }
161
+ __name(globToRegExp, "globToRegExp");
162
+ var LruCacheProvider = class {
163
+ static {
164
+ __name(this, "LruCacheProvider");
165
+ }
166
+ cache;
167
+ constructor(options) {
168
+ this.cache = new import_lru_cache.LRUCache({
169
+ max: options?.max ?? DEFAULT_LRU_MAX
170
+ });
171
+ }
172
+ // Stores a value with a per-key TTL (wrapped so primitives survive lru-cache's object value type)
173
+ async set(key, value, ttlSeconds) {
174
+ this.cache.set(key, {
175
+ value
176
+ }, {
177
+ ttl: ttlSeconds * 1e3
178
+ });
179
+ }
180
+ // Returns the stored value, or null on miss or expiry
181
+ async get(key) {
182
+ const entry = this.cache.get(key);
183
+ return entry ? entry.value : null;
184
+ }
185
+ // Deletes one or more keys
186
+ async del(...keys) {
187
+ for (const key of keys) this.cache.delete(key);
188
+ }
189
+ // Returns all live keys matching a Redis-style glob pattern
190
+ async scanKeys(pattern) {
191
+ const regex = globToRegExp(pattern);
192
+ return [
193
+ ...this.cache.keys()
194
+ ].filter((key) => regex.test(key));
195
+ }
196
+ // Reports entry count for monitoring utilities
197
+ async getMemoryInfo() {
198
+ return `entries:${this.cache.size} max:${this.cache.max}`;
199
+ }
200
+ };
201
+ LruCacheProvider = _ts_decorate2([
202
+ (0, import_common2.Injectable)(),
203
+ _ts_param2(0, (0, import_common2.Optional)()),
204
+ _ts_param2(0, (0, import_common2.Inject)(LRU_CACHE_OPTIONS)),
205
+ _ts_metadata2("design:type", Function),
206
+ _ts_metadata2("design:paramtypes", [
207
+ typeof LruCacheOptions === "undefined" ? Object : LruCacheOptions
208
+ ])
209
+ ], LruCacheProvider);
210
+
211
+ // src/cache/providers/redis.provider.ts
212
+ var import_common3 = require("@nestjs/common");
213
+ var import_config = require("@nestjs/config");
214
+ var import_ioredis = __toESM(require("ioredis"), 1);
215
+ function _ts_decorate3(decorators, target, key, desc) {
216
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
217
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
218
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
219
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
220
+ }
221
+ __name(_ts_decorate3, "_ts_decorate");
222
+ function _ts_metadata3(k, v) {
223
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
224
+ }
225
+ __name(_ts_metadata3, "_ts_metadata");
146
226
  var RedisCacheProvider = class _RedisCacheProvider {
147
227
  static {
148
228
  __name(this, "RedisCacheProvider");
149
229
  }
150
230
  configService;
151
- logger = new import_common2.Logger(_RedisCacheProvider.name);
231
+ logger = new import_common3.Logger(_RedisCacheProvider.name);
152
232
  client;
153
233
  constructor(configService) {
154
234
  this.configService = configService;
@@ -201,29 +281,114 @@ var RedisCacheProvider = class _RedisCacheProvider {
201
281
  return this.client.info("memory");
202
282
  }
203
283
  };
204
- RedisCacheProvider = _ts_decorate2([
205
- (0, import_common2.Injectable)(),
206
- _ts_metadata2("design:type", Function),
207
- _ts_metadata2("design:paramtypes", [
284
+ RedisCacheProvider = _ts_decorate3([
285
+ (0, import_common3.Injectable)(),
286
+ _ts_metadata3("design:type", Function),
287
+ _ts_metadata3("design:paramtypes", [
208
288
  typeof import_config.ConfigService === "undefined" ? Object : import_config.ConfigService
209
289
  ])
210
290
  ], RedisCacheProvider);
211
291
 
212
292
  // src/cache/cache.module.ts
213
- function _ts_decorate3(decorators, target, key, desc) {
293
+ function _ts_decorate4(decorators, target, key, desc) {
214
294
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
215
295
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
216
296
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
217
297
  return c > 3 && r && Object.defineProperty(target, key, r), r;
218
298
  }
219
- __name(_ts_decorate3, "_ts_decorate");
220
- var CacheModule = class {
299
+ __name(_ts_decorate4, "_ts_decorate");
300
+ var CacheModule = class _CacheModule {
221
301
  static {
222
302
  __name(this, "CacheModule");
223
303
  }
304
+ // Selects the backing provider; each import gets its own CacheService bound to the chosen driver
305
+ static forRoot(options = {}) {
306
+ if (options.driver === "lru") {
307
+ return {
308
+ module: _CacheModule,
309
+ providers: [
310
+ {
311
+ provide: LRU_CACHE_OPTIONS,
312
+ useValue: options.lru ?? {}
313
+ },
314
+ LruCacheProvider,
315
+ {
316
+ provide: CACHE_PROVIDER,
317
+ useExisting: LruCacheProvider
318
+ },
319
+ CacheService
320
+ ],
321
+ exports: [
322
+ LruCacheProvider,
323
+ CACHE_PROVIDER,
324
+ CacheService
325
+ ]
326
+ };
327
+ }
328
+ return {
329
+ module: _CacheModule,
330
+ imports: [
331
+ import_config2.ConfigModule
332
+ ],
333
+ providers: [
334
+ RedisCacheProvider,
335
+ {
336
+ provide: CACHE_PROVIDER,
337
+ useExisting: RedisCacheProvider
338
+ },
339
+ CacheService
340
+ ],
341
+ exports: [
342
+ RedisCacheProvider,
343
+ CACHE_PROVIDER,
344
+ CacheService
345
+ ]
346
+ };
347
+ }
348
+ // Same driver selection as forRoot, but options (and the driver) resolve via a factory
349
+ static forRootAsync(options) {
350
+ const optionsProvider = {
351
+ provide: CACHE_MODULE_OPTIONS,
352
+ useFactory: options.useFactory,
353
+ inject: options.inject ?? []
354
+ };
355
+ return {
356
+ module: _CacheModule,
357
+ imports: [
358
+ import_config2.ConfigModule,
359
+ ...options.imports ?? []
360
+ ],
361
+ providers: [
362
+ optionsProvider,
363
+ {
364
+ provide: LRU_CACHE_OPTIONS,
365
+ useFactory: /* @__PURE__ */ __name((opts) => opts.lru ?? {}, "useFactory"),
366
+ inject: [
367
+ CACHE_MODULE_OPTIONS
368
+ ]
369
+ },
370
+ LruCacheProvider,
371
+ RedisCacheProvider,
372
+ {
373
+ provide: CACHE_PROVIDER,
374
+ useFactory: /* @__PURE__ */ __name((opts, lru, redis) => opts.driver === "redis" ? redis : lru, "useFactory"),
375
+ inject: [
376
+ CACHE_MODULE_OPTIONS,
377
+ LruCacheProvider,
378
+ RedisCacheProvider
379
+ ]
380
+ },
381
+ CacheService
382
+ ],
383
+ exports: [
384
+ CACHE_PROVIDER,
385
+ CacheService
386
+ ]
387
+ };
388
+ }
224
389
  };
225
- CacheModule = _ts_decorate3([
226
- (0, import_common3.Module)({
390
+ CacheModule = _ts_decorate4([
391
+ (0, import_common4.Module)({
227
392
  imports: [
228
393
  import_config2.ConfigModule
229
394
  ],
@@ -247,6 +412,9 @@ CacheModule = _ts_decorate3([
247
412
  CACHE_PROVIDER,
248
413
  CacheModule,
249
414
  CacheService,
415
+ DEFAULT_LRU_MAX,
416
+ LRU_CACHE_OPTIONS,
417
+ LruCacheProvider,
250
418
  RedisCacheProvider
251
419
  });
252
420
  //# sourceMappingURL=cache.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cache/index.ts","../src/cache/cache.module.ts","../src/cache/cache.service.ts","../src/cache/constants.ts","../src/cache/providers/redis.provider.ts"],"sourcesContent":["export { CacheModule } from './cache.module';\nexport { CacheService } from './cache.service';\nexport { CACHE_PROVIDER } from './constants';\nexport type { ICacheProvider } from './interfaces/cache-provider.interface';\nexport { RedisCacheProvider } from './providers/redis.provider';\n","import { Module } from '@nestjs/common';\nimport { ConfigModule } from '@nestjs/config';\nimport { CacheService } from './cache.service';\nimport { CACHE_PROVIDER } from './constants';\nimport { RedisCacheProvider } from './providers/redis.provider';\n\n// To add a new provider in the future:\n// 1. Create providers/memcached.provider.ts implementing ICacheProvider\n// 2. Replace RedisCacheProvider with the new class in useExisting below\n\n@Module({\n imports: [ConfigModule],\n providers: [\n RedisCacheProvider,\n {\n provide: CACHE_PROVIDER,\n useExisting: RedisCacheProvider,\n },\n CacheService,\n ],\n exports: [RedisCacheProvider, CACHE_PROVIDER, CacheService],\n})\nexport class CacheModule {}\n","import { Inject, Injectable, Logger } from '@nestjs/common';\nimport { CACHE_PROVIDER } from './constants';\nimport type { ICacheProvider } from './interfaces/cache-provider.interface';\n\n@Injectable()\nexport class CacheService {\n private readonly logger = new Logger(CacheService.name);\n\n constructor(@Inject(CACHE_PROVIDER) private readonly provider: ICacheProvider) {}\n\n // Stores a value with mandatory TTL — errors are logged and swallowed so DB writes still succeed\n async set<T>(key: string, value: T, ttlSeconds: number): Promise<void> {\n try {\n await this.provider.set(key, value, ttlSeconds);\n } catch (err) {\n this.logger.error(`Cache set failed for key \"${key}\"`, err);\n }\n }\n\n // Returns cached value or null on miss or any Redis error — callers fall back to DB\n async get<T>(key: string): Promise<T | null> {\n try {\n return await this.provider.get<T>(key);\n } catch (err) {\n this.logger.error(`Cache get failed for key \"${key}\"`, err);\n return null;\n }\n }\n\n // Deletes one or more keys — errors are logged and swallowed\n async del(...keys: string[]): Promise<void> {\n try {\n await this.provider.del(...keys);\n } catch (err) {\n this.logger.error(`Cache del failed for keys \"${keys.join(', ')}\"`, err);\n }\n }\n\n // Returns all keys matching a glob pattern — returns empty array on error\n async scanKeys(pattern: string): Promise<string[]> {\n try {\n return await this.provider.scanKeys(pattern);\n } catch (err) {\n this.logger.error(`Cache scanKeys failed for pattern \"${pattern}\"`, err);\n return [];\n }\n }\n\n // Returns raw memory info from the provider — returns empty string on error\n async getMemoryInfo(): Promise<string> {\n try {\n return await this.provider.getMemoryInfo();\n } catch (err) {\n this.logger.error('Cache getMemoryInfo failed', err);\n return '';\n }\n }\n}\n","// Injection token for the active cache provider — use this to inject ICacheProvider\nexport const CACHE_PROVIDER = Symbol('CACHE_PROVIDER');\n","import { Injectable, Logger, OnModuleDestroy, OnModuleInit } from '@nestjs/common';\nimport { ConfigService } from '@nestjs/config';\nimport Redis from 'ioredis';\nimport type { ICacheProvider } from '../interfaces/cache-provider.interface';\n\n@Injectable()\nexport class RedisCacheProvider implements ICacheProvider, OnModuleInit, OnModuleDestroy {\n private readonly logger = new Logger(RedisCacheProvider.name);\n private client: Redis;\n\n constructor(private readonly configService: ConfigService) {}\n\n // Creates the ioredis client and attaches connection/error listeners\n onModuleInit(): void {\n const url = this.configService.getOrThrow<string>('REDIS_URL');\n this.client = new Redis(url, {\n lazyConnect: true,\n maxRetriesPerRequest: 3,\n });\n this.client.on('connect', () => this.logger.log('Redis connected'));\n this.client.on('error', (err) => this.logger.error('Redis error', err));\n }\n\n // Gracefully closes the ioredis connection on app shutdown\n async onModuleDestroy(): Promise<void> {\n await this.client.quit();\n this.logger.log('Redis disconnected');\n }\n\n // Serializes value to JSON and stores with a mandatory TTL\n async set<T>(key: string, value: T, ttlSeconds: number): Promise<void> {\n const json = JSON.stringify(value);\n await this.client.setex(key, ttlSeconds, json);\n }\n\n // Reads the stored JSON string and parses back to the original type\n async get<T>(key: string): Promise<T | null> {\n const json = await this.client.get(key);\n if (!json) return null;\n return JSON.parse(json) as T;\n }\n\n // Deletes one or more keys in a single command\n async del(...keys: string[]): Promise<void> {\n if (keys.length > 0) {\n await this.client.del(...keys);\n }\n }\n\n // Cursor-iterates all keys matching a glob pattern — never uses KEYS command\n async scanKeys(pattern: string): Promise<string[]> {\n const keys: string[] = [];\n let cursor = '0';\n do {\n const [nextCursor, batch] = await this.client.scan(cursor, 'MATCH', pattern, 'COUNT', 100);\n cursor = nextCursor;\n keys.push(...batch);\n } while (cursor !== '0');\n return keys;\n }\n\n // Returns raw INFO memory output for memory monitoring\n async getMemoryInfo(): Promise<string> {\n return this.client.info('memory');\n }\n}\n"],"mappings":"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","names":["import_common","import_config","CACHE_PROVIDER","Symbol","CacheService","logger","Logger","name","provider","set","key","value","ttlSeconds","err","error","get","del","keys","join","scanKeys","pattern","getMemoryInfo","import_common","RedisCacheProvider","logger","Logger","name","client","configService","onModuleInit","url","getOrThrow","Redis","lazyConnect","maxRetriesPerRequest","on","log","err","error","onModuleDestroy","quit","set","key","value","ttlSeconds","json","JSON","stringify","setex","get","parse","del","keys","length","scanKeys","pattern","cursor","nextCursor","batch","scan","push","getMemoryInfo","info","CacheModule","imports","ConfigModule","providers","RedisCacheProvider","provide","CACHE_PROVIDER","useExisting","CacheService","exports"]}
1
+ {"version":3,"sources":["../src/cache/index.ts","../src/cache/cache.module.ts","../src/cache/cache.service.ts","../src/cache/constants.ts","../src/cache/providers/lru.provider.ts","../src/cache/providers/redis.provider.ts"],"sourcesContent":["export { CacheModule, type CacheModuleAsyncOptions, type CacheModuleOptions } from './cache.module';\nexport { CacheService } from './cache.service';\nexport { CACHE_PROVIDER, DEFAULT_LRU_MAX, LRU_CACHE_OPTIONS } from './constants';\nexport type { ICacheProvider } from './interfaces/cache-provider.interface';\nexport { type LruCacheOptions, LruCacheProvider } from './providers/lru.provider';\nexport { RedisCacheProvider } from './providers/redis.provider';\n","import { type DynamicModule, Module, type Provider } from '@nestjs/common';\nimport { ConfigModule } from '@nestjs/config';\nimport { CacheService } from './cache.service';\nimport { CACHE_MODULE_OPTIONS, CACHE_PROVIDER, LRU_CACHE_OPTIONS } from './constants';\nimport { type LruCacheOptions, LruCacheProvider } from './providers/lru.provider';\nimport { RedisCacheProvider } from './providers/redis.provider';\n\nexport interface CacheModuleOptions {\n driver?: 'redis' | 'lru';\n lru?: LruCacheOptions;\n}\n\n// biome-ignore lint/suspicious/noExplicitAny: NestJS async factory injects/imports are heterogeneous\ntype AnyList = any[];\n\nexport interface CacheModuleAsyncOptions {\n imports?: AnyList;\n inject?: AnyList;\n useFactory: (...args: AnyList) => CacheModuleOptions | Promise<CacheModuleOptions>;\n}\n\n@Module({\n imports: [ConfigModule],\n providers: [\n RedisCacheProvider,\n {\n provide: CACHE_PROVIDER,\n useExisting: RedisCacheProvider,\n },\n CacheService,\n ],\n exports: [RedisCacheProvider, CACHE_PROVIDER, CacheService],\n})\nexport class CacheModule {\n // Selects the backing provider; each import gets its own CacheService bound to the chosen driver\n static forRoot(options: CacheModuleOptions = {}): DynamicModule {\n if (options.driver === 'lru') {\n return {\n module: CacheModule,\n providers: [\n { provide: LRU_CACHE_OPTIONS, useValue: options.lru ?? {} },\n LruCacheProvider,\n { provide: CACHE_PROVIDER, useExisting: LruCacheProvider },\n CacheService,\n ],\n exports: [LruCacheProvider, CACHE_PROVIDER, CacheService],\n };\n }\n return {\n module: CacheModule,\n imports: [ConfigModule],\n providers: [RedisCacheProvider, { provide: CACHE_PROVIDER, useExisting: RedisCacheProvider }, CacheService],\n exports: [RedisCacheProvider, CACHE_PROVIDER, CacheService],\n };\n }\n\n // Same driver selection as forRoot, but options (and the driver) resolve via a factory\n static forRootAsync(options: CacheModuleAsyncOptions): DynamicModule {\n const optionsProvider: Provider = {\n provide: CACHE_MODULE_OPTIONS,\n useFactory: options.useFactory,\n inject: options.inject ?? [],\n };\n return {\n module: CacheModule,\n imports: [ConfigModule, ...(options.imports ?? [])],\n providers: [\n optionsProvider,\n {\n provide: LRU_CACHE_OPTIONS,\n useFactory: (opts: CacheModuleOptions) => opts.lru ?? {},\n inject: [CACHE_MODULE_OPTIONS],\n },\n LruCacheProvider,\n RedisCacheProvider,\n {\n provide: CACHE_PROVIDER,\n useFactory: (opts: CacheModuleOptions, lru: LruCacheProvider, redis: RedisCacheProvider) =>\n opts.driver === 'redis' ? redis : lru,\n inject: [CACHE_MODULE_OPTIONS, LruCacheProvider, RedisCacheProvider],\n },\n CacheService,\n ],\n exports: [CACHE_PROVIDER, CacheService],\n };\n }\n}\n","import { Inject, Injectable, Logger } from '@nestjs/common';\nimport { CACHE_PROVIDER } from './constants';\nimport type { ICacheProvider } from './interfaces/cache-provider.interface';\n\n@Injectable()\nexport class CacheService {\n private readonly logger = new Logger(CacheService.name);\n\n constructor(@Inject(CACHE_PROVIDER) private readonly provider: ICacheProvider) {}\n\n // Stores a value with mandatory TTL — errors are logged and swallowed so DB writes still succeed\n async set<T>(key: string, value: T, ttlSeconds: number): Promise<void> {\n try {\n await this.provider.set(key, value, ttlSeconds);\n } catch (err) {\n this.logger.error(`Cache set failed for key \"${key}\"`, err);\n }\n }\n\n // Returns cached value or null on miss or any Redis error — callers fall back to DB\n async get<T>(key: string): Promise<T | null> {\n try {\n return await this.provider.get<T>(key);\n } catch (err) {\n this.logger.error(`Cache get failed for key \"${key}\"`, err);\n return null;\n }\n }\n\n // Deletes one or more keys — errors are logged and swallowed\n async del(...keys: string[]): Promise<void> {\n try {\n await this.provider.del(...keys);\n } catch (err) {\n this.logger.error(`Cache del failed for keys \"${keys.join(', ')}\"`, err);\n }\n }\n\n // Returns all keys matching a glob pattern — returns empty array on error\n async scanKeys(pattern: string): Promise<string[]> {\n try {\n return await this.provider.scanKeys(pattern);\n } catch (err) {\n this.logger.error(`Cache scanKeys failed for pattern \"${pattern}\"`, err);\n return [];\n }\n }\n\n // Returns raw memory info from the provider — returns empty string on error\n async getMemoryInfo(): Promise<string> {\n try {\n return await this.provider.getMemoryInfo();\n } catch (err) {\n this.logger.error('Cache getMemoryInfo failed', err);\n return '';\n }\n }\n}\n","// Injection token for the active cache provider — use this to inject ICacheProvider\nexport const CACHE_PROVIDER = Symbol('CACHE_PROVIDER');\n\n// Injection token for LruCacheProvider construction options\nexport const LRU_CACHE_OPTIONS = Symbol('LRU_CACHE_OPTIONS');\n\n// Injection token for the resolved CacheModuleOptions (used by forRootAsync)\nexport const CACHE_MODULE_OPTIONS = Symbol('CACHE_MODULE_OPTIONS');\n\n// Default entry cap for the in-memory LRU provider when no max is configured\nexport const DEFAULT_LRU_MAX = 10000;\n","import { Inject, Injectable, Optional } from '@nestjs/common';\nimport { LRUCache } from 'lru-cache';\nimport { DEFAULT_LRU_MAX, LRU_CACHE_OPTIONS } from '../constants';\nimport type { ICacheProvider } from '../interfaces/cache-provider.interface';\n\nexport interface LruCacheOptions {\n max?: number;\n}\n\n// Escapes a Redis-style glob (only * and ? are special) into an anchored RegExp for key scanning\nfunction globToRegExp(pattern: string): RegExp {\n const escaped = pattern\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/\\*/g, '.*')\n .replace(/\\?/g, '.');\n return new RegExp(`^${escaped}$`);\n}\n\n@Injectable()\nexport class LruCacheProvider implements ICacheProvider {\n private readonly cache: LRUCache<string, object>;\n\n constructor(@Optional() @Inject(LRU_CACHE_OPTIONS) options?: LruCacheOptions) {\n this.cache = new LRUCache<string, object>({ max: options?.max ?? DEFAULT_LRU_MAX });\n }\n\n // Stores a value with a per-key TTL (wrapped so primitives survive lru-cache's object value type)\n async set<T>(key: string, value: T, ttlSeconds: number): Promise<void> {\n this.cache.set(key, { value } as object, { ttl: ttlSeconds * 1000 });\n }\n\n // Returns the stored value, or null on miss or expiry\n async get<T>(key: string): Promise<T | null> {\n const entry = this.cache.get(key) as { value: T } | undefined;\n return entry ? entry.value : null;\n }\n\n // Deletes one or more keys\n async del(...keys: string[]): Promise<void> {\n for (const key of keys) this.cache.delete(key);\n }\n\n // Returns all live keys matching a Redis-style glob pattern\n async scanKeys(pattern: string): Promise<string[]> {\n const regex = globToRegExp(pattern);\n return [...this.cache.keys()].filter((key) => regex.test(key));\n }\n\n // Reports entry count for monitoring utilities\n async getMemoryInfo(): Promise<string> {\n return `entries:${this.cache.size} max:${this.cache.max}`;\n }\n}\n","import { Injectable, Logger, OnModuleDestroy, OnModuleInit } from '@nestjs/common';\nimport { ConfigService } from '@nestjs/config';\nimport Redis from 'ioredis';\nimport type { ICacheProvider } from '../interfaces/cache-provider.interface';\n\n@Injectable()\nexport class RedisCacheProvider implements ICacheProvider, OnModuleInit, OnModuleDestroy {\n private readonly logger = new Logger(RedisCacheProvider.name);\n private client: Redis;\n\n constructor(private readonly configService: ConfigService) {}\n\n // Creates the ioredis client and attaches connection/error listeners\n onModuleInit(): void {\n const url = this.configService.getOrThrow<string>('REDIS_URL');\n this.client = new Redis(url, {\n lazyConnect: true,\n maxRetriesPerRequest: 3,\n });\n this.client.on('connect', () => this.logger.log('Redis connected'));\n this.client.on('error', (err) => this.logger.error('Redis error', err));\n }\n\n // Gracefully closes the ioredis connection on app shutdown\n async onModuleDestroy(): Promise<void> {\n await this.client.quit();\n this.logger.log('Redis disconnected');\n }\n\n // Serializes value to JSON and stores with a mandatory TTL\n async set<T>(key: string, value: T, ttlSeconds: number): Promise<void> {\n const json = JSON.stringify(value);\n await this.client.setex(key, ttlSeconds, json);\n }\n\n // Reads the stored JSON string and parses back to the original type\n async get<T>(key: string): Promise<T | null> {\n const json = await this.client.get(key);\n if (!json) return null;\n return JSON.parse(json) as T;\n }\n\n // Deletes one or more keys in a single command\n async del(...keys: string[]): Promise<void> {\n if (keys.length > 0) {\n await this.client.del(...keys);\n }\n }\n\n // Cursor-iterates all keys matching a glob pattern — never uses KEYS command\n async scanKeys(pattern: string): Promise<string[]> {\n const keys: string[] = [];\n let cursor = '0';\n do {\n const [nextCursor, batch] = await this.client.scan(cursor, 'MATCH', pattern, 'COUNT', 100);\n cursor = nextCursor;\n keys.push(...batch);\n } while (cursor !== '0');\n return keys;\n }\n\n // Returns raw INFO memory output for memory monitoring\n async getMemoryInfo(): Promise<string> {\n return this.client.info('memory');\n }\n}\n"],"mappings":"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","names":["import_common","import_config","CACHE_PROVIDER","Symbol","LRU_CACHE_OPTIONS","CACHE_MODULE_OPTIONS","DEFAULT_LRU_MAX","CacheService","logger","Logger","name","provider","set","key","value","ttlSeconds","err","error","get","del","keys","join","scanKeys","pattern","getMemoryInfo","import_common","globToRegExp","pattern","escaped","replace","RegExp","LruCacheProvider","cache","options","LRUCache","max","DEFAULT_LRU_MAX","set","key","value","ttlSeconds","ttl","get","entry","del","keys","delete","scanKeys","regex","filter","test","getMemoryInfo","size","import_common","RedisCacheProvider","logger","Logger","name","client","configService","onModuleInit","url","getOrThrow","Redis","lazyConnect","maxRetriesPerRequest","on","log","err","error","onModuleDestroy","quit","set","key","value","ttlSeconds","json","JSON","stringify","setex","get","parse","del","keys","length","scanKeys","pattern","cursor","nextCursor","batch","scan","push","getMemoryInfo","info","CacheModule","forRoot","options","driver","module","providers","provide","LRU_CACHE_OPTIONS","useValue","lru","LruCacheProvider","CACHE_PROVIDER","useExisting","CacheService","exports","imports","ConfigModule","RedisCacheProvider","forRootAsync","optionsProvider","CACHE_MODULE_OPTIONS","useFactory","inject","opts","redis"]}
package/dist/cache.d.cts CHANGED
@@ -1,12 +1,56 @@
1
- import { I as ICacheProvider } from './cache.service-_SuJIcNA.cjs';
2
- export { C as CacheService } from './cache.service-_SuJIcNA.cjs';
3
- import { OnModuleInit, OnModuleDestroy } from '@nestjs/common';
1
+ import { DynamicModule, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
4
2
  import { ConfigService } from '@nestjs/config';
5
3
 
4
+ interface ICacheProvider {
5
+ set<T>(key: string, value: T, ttlSeconds: number): Promise<void>;
6
+ get<T>(key: string): Promise<T | null>;
7
+ del(...keys: string[]): Promise<void>;
8
+ scanKeys(pattern: string): Promise<string[]>;
9
+ getMemoryInfo(): Promise<string>;
10
+ }
11
+
12
+ interface LruCacheOptions {
13
+ max?: number;
14
+ }
15
+ declare class LruCacheProvider implements ICacheProvider {
16
+ private readonly cache;
17
+ constructor(options?: LruCacheOptions);
18
+ set<T>(key: string, value: T, ttlSeconds: number): Promise<void>;
19
+ get<T>(key: string): Promise<T | null>;
20
+ del(...keys: string[]): Promise<void>;
21
+ scanKeys(pattern: string): Promise<string[]>;
22
+ getMemoryInfo(): Promise<string>;
23
+ }
24
+
25
+ interface CacheModuleOptions {
26
+ driver?: 'redis' | 'lru';
27
+ lru?: LruCacheOptions;
28
+ }
29
+ type AnyList = any[];
30
+ interface CacheModuleAsyncOptions {
31
+ imports?: AnyList;
32
+ inject?: AnyList;
33
+ useFactory: (...args: AnyList) => CacheModuleOptions | Promise<CacheModuleOptions>;
34
+ }
6
35
  declare class CacheModule {
36
+ static forRoot(options?: CacheModuleOptions): DynamicModule;
37
+ static forRootAsync(options: CacheModuleAsyncOptions): DynamicModule;
38
+ }
39
+
40
+ declare class CacheService {
41
+ private readonly provider;
42
+ private readonly logger;
43
+ constructor(provider: ICacheProvider);
44
+ set<T>(key: string, value: T, ttlSeconds: number): Promise<void>;
45
+ get<T>(key: string): Promise<T | null>;
46
+ del(...keys: string[]): Promise<void>;
47
+ scanKeys(pattern: string): Promise<string[]>;
48
+ getMemoryInfo(): Promise<string>;
7
49
  }
8
50
 
9
51
  declare const CACHE_PROVIDER: unique symbol;
52
+ declare const LRU_CACHE_OPTIONS: unique symbol;
53
+ declare const DEFAULT_LRU_MAX = 10000;
10
54
 
11
55
  declare class RedisCacheProvider implements ICacheProvider, OnModuleInit, OnModuleDestroy {
12
56
  private readonly configService;
@@ -22,4 +66,4 @@ declare class RedisCacheProvider implements ICacheProvider, OnModuleInit, OnModu
22
66
  getMemoryInfo(): Promise<string>;
23
67
  }
24
68
 
25
- export { CACHE_PROVIDER, CacheModule, ICacheProvider, RedisCacheProvider };
69
+ export { CACHE_PROVIDER, CacheModule, type CacheModuleAsyncOptions, type CacheModuleOptions, CacheService, DEFAULT_LRU_MAX, type ICacheProvider, LRU_CACHE_OPTIONS, type LruCacheOptions, LruCacheProvider, RedisCacheProvider };
package/dist/cache.d.ts CHANGED
@@ -1,12 +1,56 @@
1
- import { I as ICacheProvider } from './cache.service-_SuJIcNA.js';
2
- export { C as CacheService } from './cache.service-_SuJIcNA.js';
3
- import { OnModuleInit, OnModuleDestroy } from '@nestjs/common';
1
+ import { DynamicModule, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
4
2
  import { ConfigService } from '@nestjs/config';
5
3
 
4
+ interface ICacheProvider {
5
+ set<T>(key: string, value: T, ttlSeconds: number): Promise<void>;
6
+ get<T>(key: string): Promise<T | null>;
7
+ del(...keys: string[]): Promise<void>;
8
+ scanKeys(pattern: string): Promise<string[]>;
9
+ getMemoryInfo(): Promise<string>;
10
+ }
11
+
12
+ interface LruCacheOptions {
13
+ max?: number;
14
+ }
15
+ declare class LruCacheProvider implements ICacheProvider {
16
+ private readonly cache;
17
+ constructor(options?: LruCacheOptions);
18
+ set<T>(key: string, value: T, ttlSeconds: number): Promise<void>;
19
+ get<T>(key: string): Promise<T | null>;
20
+ del(...keys: string[]): Promise<void>;
21
+ scanKeys(pattern: string): Promise<string[]>;
22
+ getMemoryInfo(): Promise<string>;
23
+ }
24
+
25
+ interface CacheModuleOptions {
26
+ driver?: 'redis' | 'lru';
27
+ lru?: LruCacheOptions;
28
+ }
29
+ type AnyList = any[];
30
+ interface CacheModuleAsyncOptions {
31
+ imports?: AnyList;
32
+ inject?: AnyList;
33
+ useFactory: (...args: AnyList) => CacheModuleOptions | Promise<CacheModuleOptions>;
34
+ }
6
35
  declare class CacheModule {
36
+ static forRoot(options?: CacheModuleOptions): DynamicModule;
37
+ static forRootAsync(options: CacheModuleAsyncOptions): DynamicModule;
38
+ }
39
+
40
+ declare class CacheService {
41
+ private readonly provider;
42
+ private readonly logger;
43
+ constructor(provider: ICacheProvider);
44
+ set<T>(key: string, value: T, ttlSeconds: number): Promise<void>;
45
+ get<T>(key: string): Promise<T | null>;
46
+ del(...keys: string[]): Promise<void>;
47
+ scanKeys(pattern: string): Promise<string[]>;
48
+ getMemoryInfo(): Promise<string>;
7
49
  }
8
50
 
9
51
  declare const CACHE_PROVIDER: unique symbol;
52
+ declare const LRU_CACHE_OPTIONS: unique symbol;
53
+ declare const DEFAULT_LRU_MAX = 10000;
10
54
 
11
55
  declare class RedisCacheProvider implements ICacheProvider, OnModuleInit, OnModuleDestroy {
12
56
  private readonly configService;
@@ -22,4 +66,4 @@ declare class RedisCacheProvider implements ICacheProvider, OnModuleInit, OnModu
22
66
  getMemoryInfo(): Promise<string>;
23
67
  }
24
68
 
25
- export { CACHE_PROVIDER, CacheModule, ICacheProvider, RedisCacheProvider };
69
+ export { CACHE_PROVIDER, CacheModule, type CacheModuleAsyncOptions, type CacheModuleOptions, CacheService, DEFAULT_LRU_MAX, type ICacheProvider, LRU_CACHE_OPTIONS, type LruCacheOptions, LruCacheProvider, RedisCacheProvider };