@vritti/api-sdk 0.3.9 → 0.3.11

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/auth.js.map CHANGED
@@ -1 +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"]}
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 host, resolved the same way as @Hostname() — x-forwarded-host\n// (set by dev and reverse proxies) with a fallback to request.hostname. Origin is deliberately not used:\n// it is client-controlled and can disagree with the Host header, which would mint a session for one\n// subdomain that the auth guard's host check then rejects against another.\nexport const Subdomain = createParamDecorator((_data: unknown, ctx: ExecutionContext): string | undefined => {\n const request = getRequestFromContext(ctx);\n\n const forwarded = request.headers['x-forwarded-host'];\n const raw = Array.isArray(forwarded) ? forwarded[0] : forwarded;\n const hostStr = raw ?? request.hostname;\n if (!hostStr) return undefined;\n\n const host = hostStr.split(':')[0] ?? hostStr;\n return host.split('.')[0] || 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","forwarded","headers","raw","Array","isArray","hostStr","hostname","undefined","host","split","createParamDecorator","UserAgent","createParamDecorator","_data","ctx","userAgent","getRequestFromContext","headers","Array","isArray","createParamDecorator","UserId","createParamDecorator","_data","ctx","request","getRequestFromContext","sessionInfo","userId","Error"]}
package/dist/storage.cjs CHANGED
@@ -25,6 +25,7 @@ __export(storage_exports, {
25
25
  R2BucketProvisioner: () => R2BucketProvisioner,
26
26
  R2StorageProvider: () => R2StorageProvider,
27
27
  StorageFactory: () => StorageFactory,
28
+ StorageProvisionerFactory: () => StorageProvisionerFactory,
28
29
  readConfigSource: () => readConfigSource
29
30
  });
30
31
  module.exports = __toCommonJS(storage_exports);
@@ -473,12 +474,41 @@ var StorageFactory = class {
473
474
  }
474
475
  }
475
476
  };
477
+
478
+ // src/storage/storage-provisioner.factory.ts
479
+ var StorageProvisionerFactory = class {
480
+ static {
481
+ __name(this, "StorageProvisionerFactory");
482
+ }
483
+ options;
484
+ cache = /* @__PURE__ */ new Map();
485
+ constructor(options) {
486
+ this.options = options;
487
+ }
488
+ // Resolves a provisioner by provider name, constructing it once on first use
489
+ resolve(provider) {
490
+ const cached = this.cache.get(provider);
491
+ if (cached) return cached;
492
+ const created = this.create(provider);
493
+ this.cache.set(provider, created);
494
+ return created;
495
+ }
496
+ create(provider) {
497
+ switch (provider) {
498
+ case "r2":
499
+ return new R2BucketProvisioner(readConfigSource(this.options.r2, "r2"));
500
+ default:
501
+ throw new Error(`Unsupported storage provisioner: ${provider}`);
502
+ }
503
+ }
504
+ };
476
505
  // Annotate the CommonJS export names for ESM import in node:
477
506
  0 && (module.exports = {
478
507
  BucketUsageReader,
479
508
  R2BucketProvisioner,
480
509
  R2StorageProvider,
481
510
  StorageFactory,
511
+ StorageProvisionerFactory,
482
512
  readConfigSource
483
513
  });
484
514
  //# sourceMappingURL=storage.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/storage/index.ts","../src/storage/bucket-usage.reader.ts","../src/storage/config-source.ts","../src/storage/providers/r2-storage.provider.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/storage/provisioners/r2-bucket.provisioner.ts","../src/storage/storage.factory.ts"],"sourcesContent":["export { type BucketUsage, BucketUsageReader, type BucketUsageReaderConfig } from './bucket-usage.reader';\nexport { readConfigSource, type StorageConfigSource } from './config-source';\nexport { R2StorageProvider } from './providers/r2-storage.provider';\nexport { R2BucketProvisioner } from './provisioners/r2-bucket.provisioner';\nexport { StorageFactory, type StorageFactoryOptions } from './storage.factory';\nexport type {\n ListObjectsPage,\n R2ProvisionerConfig,\n R2StorageConfig,\n StorageProvider,\n StoredObject,\n UploadParams,\n} from './types';\n","import { Logger } from '@nestjs/common';\n\nconst CF_GRAPHQL = 'https://api.cloudflare.com/client/v4/graphql';\n\nexport interface BucketUsage {\n bytes: number;\n objectCount: number;\n}\n\nexport interface BucketUsageReaderConfig {\n accountId: string;\n // Account-level token with Analytics Read. NOT the org's scoped S3 credential and NOT the R2 admin token: the\n // analytics dataset is filtered by accountTag and is not reachable with object-level credentials.\n analyticsToken: string;\n}\n\n// r2StorageAdaptiveGroups is a time series, so the newest bucketed sample is the closest thing to \"current\". It lags\n// real writes by minutes — fine for a periodic quota check, useless for gating an individual upload.\n//\n// `dimensions { datetime }` is required, not decorative: ordering by a field that is neither aggregated nor selected\n// as a dimension is rejected. Keep the query free of `#` comments too — Cloudflare's parser rejects them.\nconst QUERY = `query BucketUsage($accountTag: string!, $bucketName: string, $start: Time, $end: Time) {\n viewer {\n accounts(filter: { accountTag: $accountTag }) {\n r2StorageAdaptiveGroups(\n limit: 1\n filter: { datetime_geq: $start, datetime_leq: $end, bucketName: $bucketName }\n orderBy: [datetime_DESC]\n ) {\n max { objectCount payloadSize metadataSize }\n dimensions { datetime }\n }\n }\n }\n}`;\n\ninterface UsageResponse {\n errors?: { message: string }[];\n data?: {\n viewer?: {\n accounts?: {\n r2StorageAdaptiveGroups?: {\n max?: { objectCount?: number; payloadSize?: number; metadataSize?: number };\n }[];\n }[];\n };\n };\n}\n\n// Reads how much an org's bucket actually holds, straight from the provider — the authoritative figure a locally\n// maintained counter would only ever approximate.\nexport class BucketUsageReader {\n private readonly logger = new Logger(BucketUsageReader.name);\n\n constructor(private readonly config: BucketUsageReaderConfig) {}\n\n // Returns the newest reported sample, or zeroes for a bucket the dataset has not reported on yet (a new or\n // empty bucket produces no rows at all rather than a row of zeroes)\n async getBucketUsage(bucketName: string, windowHours = 24): Promise<BucketUsage> {\n const end = new Date();\n const start = new Date(end.getTime() - windowHours * 60 * 60 * 1000);\n\n const response = await fetch(CF_GRAPHQL, {\n method: 'POST',\n headers: { Authorization: `Bearer ${this.config.analyticsToken}`, 'Content-Type': 'application/json' },\n body: JSON.stringify({\n query: QUERY,\n variables: {\n accountTag: this.config.accountId,\n bucketName,\n start: start.toISOString(),\n end: end.toISOString(),\n },\n }),\n });\n\n const body = (await response.json()) as UsageResponse;\n if (body.errors?.length) {\n throw new Error(\n `Cloudflare usage query failed for ${bucketName}: ${body.errors.map((e) => e.message).join('; ')}`,\n );\n }\n\n const sample = body.data?.viewer?.accounts?.[0]?.r2StorageAdaptiveGroups?.[0]?.max;\n if (!sample) {\n this.logger.debug(`No usage samples yet for bucket ${bucketName}`);\n return { bytes: 0, objectCount: 0 };\n }\n\n // Billed storage is payload plus per-object metadata, so both count against a quota\n return {\n bytes: (sample.payloadSize ?? 0) + (sample.metadataSize ?? 0),\n objectCount: sample.objectCount ?? 0,\n };\n }\n}\n","// Config may be a value or a thunk. Servers pass a thunk when the credentials come from required-only-if-selected\n// env keys, so reading them is deferred to the first resolve() instead of running at module construction.\nexport type StorageConfigSource<T> = T | (() => T);\n\n// A backend configured nowhere is a deployment asking for something it was never given credentials for\nexport function readConfigSource<T>(source: StorageConfigSource<T> | undefined, provider: string): T {\n if (source === undefined) {\n throw new Error(`Storage provider '${provider}' is not configured.`);\n }\n return typeof source === 'function' ? (source as () => T)() : source;\n}\n","import type { Readable } from 'node:stream';\nimport {\n DeleteObjectCommand,\n GetObjectCommand,\n ListObjectsV2Command,\n PutObjectCommand,\n S3Client,\n} from '@aws-sdk/client-s3';\nimport { getSignedUrl } from '@aws-sdk/s3-request-presigner';\nimport { Logger } from '@nestjs/common';\nimport { NotFoundException } from '../../exceptions';\nimport type { ListObjectsPage, R2StorageConfig, StorageProvider, UploadParams } from '../types';\n\nexport class R2StorageProvider implements StorageProvider {\n private readonly logger = new Logger(R2StorageProvider.name);\n private readonly client: S3Client;\n\n constructor(private readonly config: R2StorageConfig) {\n this.client = new S3Client({\n region: 'auto',\n endpoint: `https://${config.accountId}.r2.cloudflarestorage.com`,\n credentials: { accessKeyId: config.accessKeyId, secretAccessKey: config.secretAccessKey },\n });\n }\n\n // Uploads a file buffer or stream\n async upload(params: UploadParams): Promise<string> {\n await this.client.send(\n new PutObjectCommand({\n Bucket: this.requireBucket(params.bucket, this.config.defaultBucket),\n Key: params.key,\n Body: params.body,\n ContentType: params.contentType,\n }),\n );\n\n this.logger.log(`Uploaded file to R2: ${params.key}`);\n return params.key;\n }\n\n // Uploads a file to a public bucket and returns its permanent URL\n async uploadPublic(key: string, body: Buffer, contentType: string, bucket?: string): Promise<string> {\n await this.client.send(\n new PutObjectCommand({\n Bucket: this.requireBucket(bucket, this.config.publicBucket),\n Key: key,\n Body: body,\n ContentType: contentType,\n }),\n );\n\n const url = this.getPublicUrl(key);\n this.logger.log(`Uploaded public file to R2: ${key} → ${url}`);\n return url;\n }\n\n // Public URLs come from the configured custom domain, which maps to one bucket — R2 has no per-bucket URL to derive,\n // so a multi-tenant caller with a bucket per org cannot build one at all\n getPublicUrl(key: string): string {\n if (!this.config.publicUrl) {\n throw new Error('R2 storage has no publicUrl configured; public URLs cannot be built.');\n }\n return `${this.config.publicUrl}/${key}`;\n }\n\n // Deletes a file\n async delete(key: string, bucket?: string): Promise<void> {\n await this.client.send(new DeleteObjectCommand({ Bucket: bucket ?? this.config.defaultBucket, Key: key }));\n\n this.logger.log(`Deleted file from R2: ${key}`);\n }\n\n // Generates a presigned download URL (default 1 hour)\n async getSignedUrl(key: string, expiresInSeconds = 3600, bucket?: string): Promise<string> {\n const command = new GetObjectCommand({ Bucket: this.requireBucket(bucket, this.config.defaultBucket), Key: key });\n return getSignedUrl(this.client, command, { expiresIn: expiresInSeconds });\n }\n\n // Returns a readable stream\n async getStream(key: string, bucket?: string): Promise<Readable> {\n const response = await this.client.send(\n new GetObjectCommand({ Bucket: this.requireBucket(bucket, this.config.defaultBucket), Key: key }),\n );\n\n if (!response.Body) {\n throw new NotFoundException('File not found in storage.');\n }\n\n return response.Body as Readable;\n }\n\n // One page of a bucket's contents. S3 has no \"list everything\" call and no bucket-size call — a full inventory is\n // this looped until nextToken is absent, and each page is a Class A request.\n async listObjects(bucket: string, continuationToken?: string): Promise<ListObjectsPage> {\n const response = await this.client.send(\n new ListObjectsV2Command({ Bucket: bucket, ContinuationToken: continuationToken }),\n );\n\n return {\n objects: (response.Contents ?? []).map((o) => ({\n key: o.Key ?? '',\n size: o.Size ?? 0,\n lastModified: o.LastModified ?? new Date(0),\n })),\n nextToken: response.NextContinuationToken,\n };\n }\n\n // Failing loudly here beats letting a missing org bucket fall through to some other tenant's default\n private requireBucket(bucket: string | undefined, fallback: string | undefined): string {\n const resolved = bucket ?? fallback;\n if (!resolved) {\n throw new Error('No bucket supplied and R2 storage has no default bucket configured.');\n }\n return resolved;\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 { Logger } from '@nestjs/common';\nimport type { R2ProvisionerConfig } from '../types';\n\nconst CF_API = 'https://api.cloudflare.com/client/v4';\n\n// Bucket-level group: read/write/list objects in named buckets. Deliberately NOT 'Workers R2 Storage Write', which is\n// account-level and can create and delete buckets — exactly what a tenant credential must never do.\nconst BUCKET_ITEM_WRITE = 'Workers R2 Storage Bucket Item Write';\n\ninterface CloudflareEnvelope<T> {\n success: boolean;\n errors: { code: number; message: string }[];\n result: T;\n}\n\n// Generic R2 admin client: create/delete buckets, toggle a bucket's Cloudflare-managed public domain, and mint/revoke\n// bucket-scoped credentials. It has NO notion of organizations — bucket naming, which buckets a tenant gets, and how\n// the stored descriptor is assembled all live in the caller. Every operation is a Cloudflare REST call.\nexport class R2BucketProvisioner {\n private readonly logger = new Logger(R2BucketProvisioner.name);\n private readonly jurisdiction: string;\n private bucketItemWriteGroupId: string | null = null;\n\n constructor(private readonly config: R2ProvisionerConfig) {\n this.jurisdiction = config.jurisdiction ?? 'default';\n }\n\n // Creates one bucket, treating an existing bucket as success so provisioning can be re-run to reconcile\n async createBucket(name: string): Promise<void> {\n const response = await fetch(`${CF_API}/accounts/${this.config.accountId}/r2/buckets`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.config.adminToken}`,\n 'Content-Type': 'application/json',\n 'cf-r2-jurisdiction': this.jurisdiction,\n },\n body: JSON.stringify({\n name,\n ...(this.config.locationHint && { locationHint: this.config.locationHint }),\n }),\n });\n\n const body = (await response.json()) as CloudflareEnvelope<unknown>;\n if (body.success) {\n this.logger.log(`Created bucket ${name}`);\n return;\n }\n\n // 10004 is \"bucket already exists\" — two provisioning attempts racing, or a reconcile pass\n if (body.errors?.some((e) => e.code === 10004)) return;\n throw new Error(`Cloudflare bucket create failed for ${name}: ${this.describe(body)}`);\n }\n\n // Deletes one bucket. R2 refuses to delete a bucket that still holds objects, so the caller must have emptied it\n // first — a 'not empty' failure here means that step did not finish. A missing bucket counts as success.\n async deleteBucket(name: string): Promise<void> {\n const response = await fetch(`${CF_API}/accounts/${this.config.accountId}/r2/buckets/${name}`, {\n method: 'DELETE',\n headers: { Authorization: `Bearer ${this.config.adminToken}` },\n });\n\n const body = (await response.json()) as CloudflareEnvelope<unknown>;\n // 10006 is \"bucket not found\" — already gone, which is the state we wanted\n if (body.success || body.errors?.some((e) => e.code === 10006)) {\n this.logger.log(`Deleted bucket ${name}`);\n return;\n }\n throw new Error(`Cloudflare bucket delete failed for ${name}: ${this.describe(body)}`);\n }\n\n // Turns on the bucket's Cloudflare-managed domain and returns its https URL. NOTE: r2.dev is rate limited and\n // documented as non-production — sustained traffic gets 429s. A custom domain per bucket is the production answer,\n // and drops into this same field. Throws on failure; the caller decides whether that is fatal.\n async enablePublicAccess(bucket: string): Promise<string> {\n const response = await fetch(`${CF_API}/accounts/${this.config.accountId}/r2/buckets/${bucket}/domains/managed`, {\n method: 'PUT',\n headers: { Authorization: `Bearer ${this.config.adminToken}`, 'Content-Type': 'application/json' },\n body: JSON.stringify({ enabled: true }),\n });\n\n const body = (await response.json()) as CloudflareEnvelope<{ domain?: string; enabled?: boolean }>;\n if (!body.success || !body.result?.domain) {\n throw new Error(`Cloudflare enable public access failed for ${bucket}: ${this.describe(body)}`);\n }\n\n this.logger.log(`Enabled public access for ${bucket} → ${body.result.domain}`);\n return `https://${body.result.domain}`;\n }\n\n // Mints an ACCOUNT-owned token that can only touch the named buckets. Account-owned rather than user-owned so a\n // tenant's credential does not die with whichever Cloudflare user happened to create the parent token. Requires the\n // parent to hold `Account API Tokens Write`; a user-scoped `API Tokens: Edit` token is rejected here with 9109.\n // `name` is the token's display label; the value is returned by R2 exactly once.\n async createScopedToken(name: string, buckets: string[]): Promise<{ id: string; value: string }> {\n const permissionGroupId = await this.resolveBucketItemWriteGroupId();\n\n // The jurisdiction is embedded in the resource key and must match the bucket's, or the token authenticates fine\n // and then 403s on every object because it is scoped to a bucket that does not exist\n const resources = Object.fromEntries(\n buckets.map((bucket) => [\n `com.cloudflare.edge.r2.bucket.${this.config.accountId}_${this.jurisdiction}_${bucket}`,\n '*',\n ]),\n );\n\n const response = await fetch(`${CF_API}/accounts/${this.config.accountId}/tokens`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${this.config.tokensToken}`, 'Content-Type': 'application/json' },\n body: JSON.stringify({\n name,\n policies: [{ effect: 'allow', permission_groups: [{ id: permissionGroupId }], resources }],\n }),\n });\n\n const body = (await response.json()) as CloudflareEnvelope<{ id: string; value: string }>;\n if (!body.success || !body.result?.value) {\n throw new Error(`Cloudflare token create failed for ${name}: ${this.describe(body)}`);\n }\n\n this.logger.log(`Minted storage credential ${name} scoped to ${buckets.length} bucket(s)`);\n return body.result;\n }\n\n // Revokes a credential by its access key id, which on R2 is the Cloudflare token id\n async deleteCredential(accessKeyId: string): Promise<void> {\n const response = await fetch(`${CF_API}/accounts/${this.config.accountId}/tokens/${accessKeyId}`, {\n method: 'DELETE',\n headers: { Authorization: `Bearer ${this.config.tokensToken}` },\n });\n\n const body = (await response.json()) as CloudflareEnvelope<unknown>;\n if (!body.success) {\n throw new Error(`Cloudflare token delete failed for ${accessKeyId}: ${this.describe(body)}`);\n }\n\n this.logger.log(`Revoked storage credential ${accessKeyId}`);\n }\n\n // The create-token API takes permission group UUIDs, not names. Looked up once rather than hardcoded: a UUID copied\n // from docs fails much later with an error that says nothing useful. Same account-scoped endpoint as creation.\n private async resolveBucketItemWriteGroupId(): Promise<string> {\n if (this.bucketItemWriteGroupId) return this.bucketItemWriteGroupId;\n\n const response = await fetch(`${CF_API}/accounts/${this.config.accountId}/tokens/permission_groups`, {\n headers: { Authorization: `Bearer ${this.config.tokensToken}` },\n });\n\n const body = (await response.json()) as CloudflareEnvelope<{ id: string; name: string }[]>;\n const group = body.result?.find((g) => g.name === BUCKET_ITEM_WRITE);\n if (!group) {\n throw new Error(`Cloudflare permission group '${BUCKET_ITEM_WRITE}' not found: ${this.describe(body)}`);\n }\n\n this.bucketItemWriteGroupId = group.id;\n return group.id;\n }\n\n private describe(body: CloudflareEnvelope<unknown>): string {\n return body.errors?.map((e) => `${e.code} ${e.message}`).join('; ') || 'unknown error';\n }\n}\n","import { readConfigSource, type StorageConfigSource } from './config-source';\nimport { R2StorageProvider } from './providers/r2-storage.provider';\nimport type { R2StorageConfig, StorageProvider } from './types';\n\nexport interface StorageFactoryOptions {\n r2?: StorageConfigSource<R2StorageConfig>;\n}\n\nexport class StorageFactory {\n private readonly cache = new Map<string, StorageProvider>();\n\n constructor(private readonly options: StorageFactoryOptions) {}\n\n // Resolves a provider by name, constructing it once on first use\n resolve(provider: string): StorageProvider {\n const cached = this.cache.get(provider);\n if (cached) return cached;\n\n const created = this.create(provider);\n this.cache.set(provider, created);\n return created;\n }\n\n private create(provider: string): StorageProvider {\n switch (provider) {\n case 'r2':\n return new R2StorageProvider(readConfigSource(this.options.r2, 'r2'));\n default:\n throw new Error(`Unsupported storage provider: ${provider}`);\n }\n }\n}\n"],"mappings":"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","names":["CF_GRAPHQL","QUERY","BucketUsageReader","logger","Logger","name","config","getBucketUsage","bucketName","windowHours","end","Date","start","getTime","response","fetch","method","headers","Authorization","analyticsToken","body","JSON","stringify","query","variables","accountTag","accountId","toISOString","json","errors","length","Error","map","e","message","join","sample","data","viewer","accounts","r2StorageAdaptiveGroups","max","debug","bytes","objectCount","payloadSize","metadataSize","readConfigSource","source","provider","undefined","Error","import_common","import_common","import_common","HttpProblemException","HttpException","detailOrOptions","httpStatus","options","detail","type","label","errors","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","NotFoundException","HttpProblemException","detailOrOptions","HttpStatus","NOT_FOUND","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","R2StorageProvider","logger","Logger","name","client","config","S3Client","region","endpoint","accountId","credentials","accessKeyId","secretAccessKey","upload","params","send","PutObjectCommand","Bucket","requireBucket","bucket","defaultBucket","Key","key","Body","body","ContentType","contentType","log","uploadPublic","publicBucket","url","getPublicUrl","publicUrl","Error","delete","DeleteObjectCommand","getSignedUrl","expiresInSeconds","command","GetObjectCommand","expiresIn","getStream","response","NotFoundException","listObjects","continuationToken","ListObjectsV2Command","ContinuationToken","objects","Contents","map","o","size","Size","lastModified","LastModified","Date","nextToken","NextContinuationToken","fallback","resolved","import_common","CF_API","BUCKET_ITEM_WRITE","R2BucketProvisioner","logger","Logger","name","jurisdiction","bucketItemWriteGroupId","config","createBucket","response","fetch","accountId","method","headers","Authorization","adminToken","body","JSON","stringify","locationHint","json","success","log","errors","some","e","code","Error","describe","deleteBucket","enablePublicAccess","bucket","enabled","result","domain","createScopedToken","buckets","permissionGroupId","resolveBucketItemWriteGroupId","resources","Object","fromEntries","map","tokensToken","policies","effect","permission_groups","id","value","length","deleteCredential","accessKeyId","group","find","g","message","join","StorageFactory","cache","Map","options","resolve","provider","cached","get","created","create","set","R2StorageProvider","readConfigSource","r2","Error"]}
1
+ {"version":3,"sources":["../src/storage/index.ts","../src/storage/bucket-usage.reader.ts","../src/storage/config-source.ts","../src/storage/providers/r2-storage.provider.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/storage/provisioners/r2-bucket.provisioner.ts","../src/storage/storage.factory.ts","../src/storage/storage-provisioner.factory.ts"],"sourcesContent":["export { type BucketUsage, BucketUsageReader, type BucketUsageReaderConfig } from './bucket-usage.reader';\nexport { readConfigSource, type StorageConfigSource } from './config-source';\nexport { R2StorageProvider } from './providers/r2-storage.provider';\nexport { R2BucketProvisioner } from './provisioners/r2-bucket.provisioner';\nexport { StorageFactory, type StorageFactoryOptions } from './storage.factory';\nexport { StorageProvisionerFactory, type StorageProvisionerFactoryOptions } from './storage-provisioner.factory';\nexport type {\n ListObjectsPage,\n R2ProvisionerConfig,\n R2StorageConfig,\n ScopedTokenResult,\n StorageProvider,\n StorageProvisioner,\n StoredObject,\n UploadParams,\n} from './types';\n","import { Logger } from '@nestjs/common';\n\nconst CF_GRAPHQL = 'https://api.cloudflare.com/client/v4/graphql';\n\nexport interface BucketUsage {\n bytes: number;\n objectCount: number;\n}\n\nexport interface BucketUsageReaderConfig {\n accountId: string;\n // Account-level token with Analytics Read. NOT the org's scoped S3 credential and NOT the R2 admin token: the\n // analytics dataset is filtered by accountTag and is not reachable with object-level credentials.\n analyticsToken: string;\n}\n\n// r2StorageAdaptiveGroups is a time series, so the newest bucketed sample is the closest thing to \"current\". It lags\n// real writes by minutes — fine for a periodic quota check, useless for gating an individual upload.\n//\n// `dimensions { datetime }` is required, not decorative: ordering by a field that is neither aggregated nor selected\n// as a dimension is rejected. Keep the query free of `#` comments too — Cloudflare's parser rejects them.\nconst QUERY = `query BucketUsage($accountTag: string!, $bucketName: string, $start: Time, $end: Time) {\n viewer {\n accounts(filter: { accountTag: $accountTag }) {\n r2StorageAdaptiveGroups(\n limit: 1\n filter: { datetime_geq: $start, datetime_leq: $end, bucketName: $bucketName }\n orderBy: [datetime_DESC]\n ) {\n max { objectCount payloadSize metadataSize }\n dimensions { datetime }\n }\n }\n }\n}`;\n\ninterface UsageResponse {\n errors?: { message: string }[];\n data?: {\n viewer?: {\n accounts?: {\n r2StorageAdaptiveGroups?: {\n max?: { objectCount?: number; payloadSize?: number; metadataSize?: number };\n }[];\n }[];\n };\n };\n}\n\n// Reads how much an org's bucket actually holds, straight from the provider — the authoritative figure a locally\n// maintained counter would only ever approximate.\nexport class BucketUsageReader {\n private readonly logger = new Logger(BucketUsageReader.name);\n\n constructor(private readonly config: BucketUsageReaderConfig) {}\n\n // Returns the newest reported sample, or zeroes for a bucket the dataset has not reported on yet (a new or\n // empty bucket produces no rows at all rather than a row of zeroes)\n async getBucketUsage(bucketName: string, windowHours = 24): Promise<BucketUsage> {\n const end = new Date();\n const start = new Date(end.getTime() - windowHours * 60 * 60 * 1000);\n\n const response = await fetch(CF_GRAPHQL, {\n method: 'POST',\n headers: { Authorization: `Bearer ${this.config.analyticsToken}`, 'Content-Type': 'application/json' },\n body: JSON.stringify({\n query: QUERY,\n variables: {\n accountTag: this.config.accountId,\n bucketName,\n start: start.toISOString(),\n end: end.toISOString(),\n },\n }),\n });\n\n const body = (await response.json()) as UsageResponse;\n if (body.errors?.length) {\n throw new Error(\n `Cloudflare usage query failed for ${bucketName}: ${body.errors.map((e) => e.message).join('; ')}`,\n );\n }\n\n const sample = body.data?.viewer?.accounts?.[0]?.r2StorageAdaptiveGroups?.[0]?.max;\n if (!sample) {\n this.logger.debug(`No usage samples yet for bucket ${bucketName}`);\n return { bytes: 0, objectCount: 0 };\n }\n\n // Billed storage is payload plus per-object metadata, so both count against a quota\n return {\n bytes: (sample.payloadSize ?? 0) + (sample.metadataSize ?? 0),\n objectCount: sample.objectCount ?? 0,\n };\n }\n}\n","// Config may be a value or a thunk. Servers pass a thunk when the credentials come from required-only-if-selected\n// env keys, so reading them is deferred to the first resolve() instead of running at module construction.\nexport type StorageConfigSource<T> = T | (() => T);\n\n// A backend configured nowhere is a deployment asking for something it was never given credentials for\nexport function readConfigSource<T>(source: StorageConfigSource<T> | undefined, provider: string): T {\n if (source === undefined) {\n throw new Error(`Storage provider '${provider}' is not configured.`);\n }\n return typeof source === 'function' ? (source as () => T)() : source;\n}\n","import type { Readable } from 'node:stream';\nimport {\n DeleteObjectCommand,\n GetObjectCommand,\n ListObjectsV2Command,\n PutObjectCommand,\n S3Client,\n} from '@aws-sdk/client-s3';\nimport { getSignedUrl } from '@aws-sdk/s3-request-presigner';\nimport { Logger } from '@nestjs/common';\nimport { NotFoundException } from '../../exceptions';\nimport type { ListObjectsPage, R2StorageConfig, StorageProvider, UploadParams } from '../types';\n\nexport class R2StorageProvider implements StorageProvider {\n private readonly logger = new Logger(R2StorageProvider.name);\n private readonly client: S3Client;\n\n constructor(private readonly config: R2StorageConfig) {\n this.client = new S3Client({\n region: 'auto',\n endpoint: `https://${config.accountId}.r2.cloudflarestorage.com`,\n credentials: { accessKeyId: config.accessKeyId, secretAccessKey: config.secretAccessKey },\n });\n }\n\n // Uploads a file buffer or stream\n async upload(params: UploadParams): Promise<string> {\n await this.client.send(\n new PutObjectCommand({\n Bucket: this.requireBucket(params.bucket, this.config.defaultBucket),\n Key: params.key,\n Body: params.body,\n ContentType: params.contentType,\n }),\n );\n\n this.logger.log(`Uploaded file to R2: ${params.key}`);\n return params.key;\n }\n\n // Uploads a file to a public bucket and returns its permanent URL\n async uploadPublic(key: string, body: Buffer, contentType: string, bucket?: string): Promise<string> {\n await this.client.send(\n new PutObjectCommand({\n Bucket: this.requireBucket(bucket, this.config.publicBucket),\n Key: key,\n Body: body,\n ContentType: contentType,\n }),\n );\n\n const url = this.getPublicUrl(key);\n this.logger.log(`Uploaded public file to R2: ${key} → ${url}`);\n return url;\n }\n\n // Public URLs come from the configured custom domain, which maps to one bucket — R2 has no per-bucket URL to derive,\n // so a multi-tenant caller with a bucket per org cannot build one at all\n getPublicUrl(key: string): string {\n if (!this.config.publicUrl) {\n throw new Error('R2 storage has no publicUrl configured; public URLs cannot be built.');\n }\n return `${this.config.publicUrl}/${key}`;\n }\n\n // Deletes a file\n async delete(key: string, bucket?: string): Promise<void> {\n await this.client.send(new DeleteObjectCommand({ Bucket: bucket ?? this.config.defaultBucket, Key: key }));\n\n this.logger.log(`Deleted file from R2: ${key}`);\n }\n\n // Generates a presigned download URL (default 1 hour)\n async getSignedUrl(key: string, expiresInSeconds = 3600, bucket?: string): Promise<string> {\n const command = new GetObjectCommand({ Bucket: this.requireBucket(bucket, this.config.defaultBucket), Key: key });\n return getSignedUrl(this.client, command, { expiresIn: expiresInSeconds });\n }\n\n // Returns a readable stream\n async getStream(key: string, bucket?: string): Promise<Readable> {\n const response = await this.client.send(\n new GetObjectCommand({ Bucket: this.requireBucket(bucket, this.config.defaultBucket), Key: key }),\n );\n\n if (!response.Body) {\n throw new NotFoundException('File not found in storage.');\n }\n\n return response.Body as Readable;\n }\n\n // One page of a bucket's contents. S3 has no \"list everything\" call and no bucket-size call — a full inventory is\n // this looped until nextToken is absent, and each page is a Class A request.\n async listObjects(bucket: string, continuationToken?: string): Promise<ListObjectsPage> {\n const response = await this.client.send(\n new ListObjectsV2Command({ Bucket: bucket, ContinuationToken: continuationToken }),\n );\n\n return {\n objects: (response.Contents ?? []).map((o) => ({\n key: o.Key ?? '',\n size: o.Size ?? 0,\n lastModified: o.LastModified ?? new Date(0),\n })),\n nextToken: response.NextContinuationToken,\n };\n }\n\n // Failing loudly here beats letting a missing org bucket fall through to some other tenant's default\n private requireBucket(bucket: string | undefined, fallback: string | undefined): string {\n const resolved = bucket ?? fallback;\n if (!resolved) {\n throw new Error('No bucket supplied and R2 storage has no default bucket configured.');\n }\n return resolved;\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 { Logger } from '@nestjs/common';\nimport type { R2ProvisionerConfig, ScopedTokenResult, StorageProvisioner } from '../types';\n\nconst CF_API = 'https://api.cloudflare.com/client/v4';\n\n// Bucket-level group: read/write/list objects in named buckets. Deliberately NOT 'Workers R2 Storage Write', which is\n// account-level and can create and delete buckets — exactly what a tenant credential must never do.\nconst BUCKET_ITEM_WRITE = 'Workers R2 Storage Bucket Item Write';\n\ninterface CloudflareEnvelope<T> {\n success: boolean;\n errors: { code: number; message: string }[];\n result: T;\n}\n\n// Generic R2 admin client: create/delete buckets, toggle a bucket's Cloudflare-managed public domain, and mint/revoke\n// bucket-scoped credentials. It has NO notion of organizations — bucket naming, which buckets a tenant gets, and how\n// the stored descriptor is assembled all live in the caller. Every operation is a Cloudflare REST call.\nexport class R2BucketProvisioner implements StorageProvisioner {\n private readonly logger = new Logger(R2BucketProvisioner.name);\n private readonly jurisdiction: string;\n private bucketItemWriteGroupId: string | null = null;\n\n constructor(private readonly config: R2ProvisionerConfig) {\n this.jurisdiction = config.jurisdiction ?? 'default';\n }\n\n // Creates one bucket, treating an existing bucket as success so provisioning can be re-run to reconcile\n async createBucket(name: string): Promise<void> {\n const response = await fetch(`${CF_API}/accounts/${this.config.accountId}/r2/buckets`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.config.adminToken}`,\n 'Content-Type': 'application/json',\n 'cf-r2-jurisdiction': this.jurisdiction,\n },\n body: JSON.stringify({\n name,\n ...(this.config.locationHint && { locationHint: this.config.locationHint }),\n }),\n });\n\n const body = (await response.json()) as CloudflareEnvelope<unknown>;\n if (body.success) {\n this.logger.log(`Created bucket ${name}`);\n return;\n }\n\n // 10004 is \"bucket already exists\" — two provisioning attempts racing, or a reconcile pass\n if (body.errors?.some((e) => e.code === 10004)) return;\n throw new Error(`Cloudflare bucket create failed for ${name}: ${this.describe(body)}`);\n }\n\n // Deletes one bucket. R2 refuses to delete a bucket that still holds objects, so the caller must have emptied it\n // first — a 'not empty' failure here means that step did not finish. A missing bucket counts as success.\n async deleteBucket(name: string): Promise<void> {\n const response = await fetch(`${CF_API}/accounts/${this.config.accountId}/r2/buckets/${name}`, {\n method: 'DELETE',\n headers: { Authorization: `Bearer ${this.config.adminToken}` },\n });\n\n const body = (await response.json()) as CloudflareEnvelope<unknown>;\n // 10006 is \"bucket not found\" — already gone, which is the state we wanted\n if (body.success || body.errors?.some((e) => e.code === 10006)) {\n this.logger.log(`Deleted bucket ${name}`);\n return;\n }\n throw new Error(`Cloudflare bucket delete failed for ${name}: ${this.describe(body)}`);\n }\n\n // Turns on the bucket's Cloudflare-managed domain and returns its https URL. NOTE: r2.dev is rate limited and\n // documented as non-production — sustained traffic gets 429s. A custom domain per bucket is the production answer,\n // and drops into this same field. Throws on failure; the caller decides whether that is fatal.\n async enablePublicAccess(bucket: string): Promise<string> {\n const response = await fetch(`${CF_API}/accounts/${this.config.accountId}/r2/buckets/${bucket}/domains/managed`, {\n method: 'PUT',\n headers: { Authorization: `Bearer ${this.config.adminToken}`, 'Content-Type': 'application/json' },\n body: JSON.stringify({ enabled: true }),\n });\n\n const body = (await response.json()) as CloudflareEnvelope<{ domain?: string; enabled?: boolean }>;\n if (!body.success || !body.result?.domain) {\n throw new Error(`Cloudflare enable public access failed for ${bucket}: ${this.describe(body)}`);\n }\n\n this.logger.log(`Enabled public access for ${bucket} → ${body.result.domain}`);\n return `https://${body.result.domain}`;\n }\n\n // Mints an ACCOUNT-owned token that can only touch the named buckets. Account-owned rather than user-owned so a\n // tenant's credential does not die with whichever Cloudflare user happened to create the parent token. Requires the\n // parent to hold `Account API Tokens Write`; a user-scoped `API Tokens: Edit` token is rejected here with 9109.\n // `name` is the token's display label; the value is returned by R2 exactly once.\n async createScopedToken(name: string, buckets: string[]): Promise<ScopedTokenResult> {\n const permissionGroupId = await this.resolveBucketItemWriteGroupId();\n\n // The jurisdiction is embedded in the resource key and must match the bucket's, or the token authenticates fine\n // and then 403s on every object because it is scoped to a bucket that does not exist\n const resources = Object.fromEntries(\n buckets.map((bucket) => [\n `com.cloudflare.edge.r2.bucket.${this.config.accountId}_${this.jurisdiction}_${bucket}`,\n '*',\n ]),\n );\n\n const response = await fetch(`${CF_API}/accounts/${this.config.accountId}/tokens`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${this.config.tokensToken}`, 'Content-Type': 'application/json' },\n body: JSON.stringify({\n name,\n policies: [{ effect: 'allow', permission_groups: [{ id: permissionGroupId }], resources }],\n }),\n });\n\n const body = (await response.json()) as CloudflareEnvelope<{ id: string; value: string }>;\n if (!body.success || !body.result?.value) {\n throw new Error(`Cloudflare token create failed for ${name}: ${this.describe(body)}`);\n }\n\n this.logger.log(`Minted storage credential ${name} scoped to ${buckets.length} bucket(s)`);\n return body.result;\n }\n\n // Revokes a credential by its access key id, which on R2 is the Cloudflare token id\n async deleteCredential(accessKeyId: string): Promise<void> {\n const response = await fetch(`${CF_API}/accounts/${this.config.accountId}/tokens/${accessKeyId}`, {\n method: 'DELETE',\n headers: { Authorization: `Bearer ${this.config.tokensToken}` },\n });\n\n const body = (await response.json()) as CloudflareEnvelope<unknown>;\n if (!body.success) {\n throw new Error(`Cloudflare token delete failed for ${accessKeyId}: ${this.describe(body)}`);\n }\n\n this.logger.log(`Revoked storage credential ${accessKeyId}`);\n }\n\n // The create-token API takes permission group UUIDs, not names. Looked up once rather than hardcoded: a UUID copied\n // from docs fails much later with an error that says nothing useful. Same account-scoped endpoint as creation.\n private async resolveBucketItemWriteGroupId(): Promise<string> {\n if (this.bucketItemWriteGroupId) return this.bucketItemWriteGroupId;\n\n const response = await fetch(`${CF_API}/accounts/${this.config.accountId}/tokens/permission_groups`, {\n headers: { Authorization: `Bearer ${this.config.tokensToken}` },\n });\n\n const body = (await response.json()) as CloudflareEnvelope<{ id: string; name: string }[]>;\n const group = body.result?.find((g) => g.name === BUCKET_ITEM_WRITE);\n if (!group) {\n throw new Error(`Cloudflare permission group '${BUCKET_ITEM_WRITE}' not found: ${this.describe(body)}`);\n }\n\n this.bucketItemWriteGroupId = group.id;\n return group.id;\n }\n\n private describe(body: CloudflareEnvelope<unknown>): string {\n return body.errors?.map((e) => `${e.code} ${e.message}`).join('; ') || 'unknown error';\n }\n}\n","import { readConfigSource, type StorageConfigSource } from './config-source';\nimport { R2StorageProvider } from './providers/r2-storage.provider';\nimport type { R2StorageConfig, StorageProvider } from './types';\n\nexport interface StorageFactoryOptions {\n r2?: StorageConfigSource<R2StorageConfig>;\n}\n\nexport class StorageFactory {\n private readonly cache = new Map<string, StorageProvider>();\n\n constructor(private readonly options: StorageFactoryOptions) {}\n\n // Resolves a provider by name, constructing it once on first use\n resolve(provider: string): StorageProvider {\n const cached = this.cache.get(provider);\n if (cached) return cached;\n\n const created = this.create(provider);\n this.cache.set(provider, created);\n return created;\n }\n\n private create(provider: string): StorageProvider {\n switch (provider) {\n case 'r2':\n return new R2StorageProvider(readConfigSource(this.options.r2, 'r2'));\n default:\n throw new Error(`Unsupported storage provider: ${provider}`);\n }\n }\n}\n","import { readConfigSource, type StorageConfigSource } from './config-source';\nimport { R2BucketProvisioner } from './provisioners/r2-bucket.provisioner';\nimport type { R2ProvisionerConfig, StorageProvisioner } from './types';\n\nexport interface StorageProvisionerFactoryOptions {\n r2?: StorageConfigSource<R2ProvisionerConfig>;\n}\n\n// Mirrors StorageFactory for the admin side: same provider names, same lazy config, different job. Resolves a generic\n// StorageProvisioner (bucket + credential admin) by provider name so the caller is not pinned to a single backend.\nexport class StorageProvisionerFactory {\n private readonly cache = new Map<string, StorageProvisioner>();\n\n constructor(private readonly options: StorageProvisionerFactoryOptions) {}\n\n // Resolves a provisioner by provider name, constructing it once on first use\n resolve(provider: string): StorageProvisioner {\n const cached = this.cache.get(provider);\n if (cached) return cached;\n\n const created = this.create(provider);\n this.cache.set(provider, created);\n return created;\n }\n\n private create(provider: string): StorageProvisioner {\n switch (provider) {\n case 'r2':\n return new R2BucketProvisioner(readConfigSource(this.options.r2, 'r2'));\n default:\n throw new Error(`Unsupported storage provisioner: ${provider}`);\n }\n }\n}\n"],"mappings":"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","names":["CF_GRAPHQL","QUERY","BucketUsageReader","logger","Logger","name","config","getBucketUsage","bucketName","windowHours","end","Date","start","getTime","response","fetch","method","headers","Authorization","analyticsToken","body","JSON","stringify","query","variables","accountTag","accountId","toISOString","json","errors","length","Error","map","e","message","join","sample","data","viewer","accounts","r2StorageAdaptiveGroups","max","debug","bytes","objectCount","payloadSize","metadataSize","readConfigSource","source","provider","undefined","Error","import_common","import_common","import_common","HttpProblemException","HttpException","detailOrOptions","httpStatus","options","detail","type","label","errors","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","NotFoundException","HttpProblemException","detailOrOptions","HttpStatus","NOT_FOUND","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","R2StorageProvider","logger","Logger","name","client","config","S3Client","region","endpoint","accountId","credentials","accessKeyId","secretAccessKey","upload","params","send","PutObjectCommand","Bucket","requireBucket","bucket","defaultBucket","Key","key","Body","body","ContentType","contentType","log","uploadPublic","publicBucket","url","getPublicUrl","publicUrl","Error","delete","DeleteObjectCommand","getSignedUrl","expiresInSeconds","command","GetObjectCommand","expiresIn","getStream","response","NotFoundException","listObjects","continuationToken","ListObjectsV2Command","ContinuationToken","objects","Contents","map","o","size","Size","lastModified","LastModified","Date","nextToken","NextContinuationToken","fallback","resolved","import_common","CF_API","BUCKET_ITEM_WRITE","R2BucketProvisioner","logger","Logger","name","jurisdiction","bucketItemWriteGroupId","config","createBucket","response","fetch","accountId","method","headers","Authorization","adminToken","body","JSON","stringify","locationHint","json","success","log","errors","some","e","code","Error","describe","deleteBucket","enablePublicAccess","bucket","enabled","result","domain","createScopedToken","buckets","permissionGroupId","resolveBucketItemWriteGroupId","resources","Object","fromEntries","map","tokensToken","policies","effect","permission_groups","id","value","length","deleteCredential","accessKeyId","group","find","g","message","join","StorageFactory","cache","Map","options","resolve","provider","cached","get","created","create","set","R2StorageProvider","readConfigSource","r2","Error","StorageProvisionerFactory","cache","Map","options","resolve","provider","cached","get","created","create","set","R2BucketProvisioner","readConfigSource","r2","Error"]}