{"version":3,"sources":["../package.json","../src/shared/errors/index.ts","../src/rate-limit/api/filters/rate-limit-exception.filter.ts","../src/shared/constants/index.ts","../src/rate-limit/api/decorators/rate-limit.decorator.ts","../src/rate-limit/api/guards/rate-limit.guard.ts","../src/shared/utils/validate-store-options.ts","../src/rate-limit/application/services/rate-limit.service.ts","../src/rate-limit/domain/memory/in-memory-algorithms.ts","../src/rate-limit/infrastructure/adapters/in-memory-rate-limit-store.adapter.ts","../src/rate-limit/infrastructure/scripts/lua-scripts.ts","../src/rate-limit/infrastructure/adapters/redis-rate-limit-store.adapter.ts","../src/rate-limit.plugin.ts","../src/rate-limit/domain/strategies/fixed-window.strategy.ts","../src/rate-limit/domain/strategies/sliding-window.strategy.ts","../src/rate-limit/domain/strategies/token-bucket.strategy.ts"],"names":["body","Inject","HttpAdapterHost","Injectable","Optional","now","Reflector"],"mappings":"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","file":"index.mjs","sourcesContent":["{\n  \"name\": \"@nestjs-redisx/rate-limit\",\n  \"version\": \"1.13.0\",\n  \"description\": \"Rate limiting plugin for NestJS RedisX with multiple algorithms (fixed-window, sliding-window, token-bucket)\",\n  \"author\": \"NestJS RedisX Team\",\n  \"license\": \"MIT\",\n  \"main\": \"./dist/index.js\",\n  \"module\": \"./dist/index.mjs\",\n  \"types\": \"./dist/index.d.ts\",\n  \"exports\": {\n    \".\": {\n      \"import\": {\n        \"types\": \"./dist/index.d.mts\",\n        \"default\": \"./dist/index.mjs\"\n      },\n      \"require\": {\n        \"types\": \"./dist/index.d.ts\",\n        \"default\": \"./dist/index.js\"\n      },\n      \"default\": \"./dist/index.js\"\n    },\n    \"./package.json\": \"./package.json\"\n  },\n  \"files\": [\n    \"dist\",\n    \"README.md\",\n    \"LICENSE\"\n  ],\n  \"scripts\": {\n    \"build\": \"tsup\",\n    \"test\": \"SKIP_INTEGRATION=true vitest run\",\n    \"test:watch\": \"SKIP_INTEGRATION=true vitest\",\n    \"test:cov\": \"SKIP_INTEGRATION=true vitest run --coverage\",\n    \"test:integration\": \"SKIP_INTEGRATION=false vitest run test/integration --pool=forks --poolOptions.forks.singleFork=true\",\n    \"test:integration:watch\": \"SKIP_INTEGRATION=false vitest watch test/integration\",\n    \"docker:up\": \"cd ../.. && docker-compose up -d redis\",\n    \"docker:down\": \"cd ../.. && docker-compose down\",\n    \"docker:logs\": \"cd ../.. && docker-compose logs -f redis\",\n    \"test:docker\": \"npm run docker:up && sleep 3 && npm run test:integration; TEST_EXIT=$?; npm run docker:down; exit $TEST_EXIT\",\n    \"test:all\": \"npm run test:cov && npm run test:integration\",\n    \"lint\": \"eslint \\\"{src,test}/**/*.ts\\\"\",\n    \"format\": \"prettier --write \\\"{src,test}/**/*.ts\\\"\",\n    \"prepublishOnly\": \"node ../../scripts/check-changelog.mjs\"\n  },\n  \"keywords\": [\n    \"nestjs\",\n    \"redis\",\n    \"rate-limit\",\n    \"rate-limiting\",\n    \"throttle\",\n    \"fixed-window\",\n    \"sliding-window\",\n    \"token-bucket\"\n  ],\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/nestjs-redisx/nestjs-redisx.git\",\n    \"directory\": \"packages/rate-limit\"\n  },\n  \"homepage\": \"https://nestjs-redisx.dev/en/reference/rate-limit/\",\n  \"bugs\": {\n    \"url\": \"https://github.com/nestjs-redisx/nestjs-redisx/issues\"\n  },\n  \"peerDependencies\": {\n    \"@nestjs-redisx/core\": \"^1.0.0\",\n    \"@nestjs/common\": \"^10.0.0 || ^11.0.0 || ^12.0.0\",\n    \"@nestjs/core\": \"^10.0.0 || ^11.0.0 || ^12.0.0\",\n    \"reflect-metadata\": \"^0.2.0\",\n    \"rxjs\": \"^7.8.0\"\n  },\n  \"devDependencies\": {\n    \"@nestjs-redisx/testing\": \"^1.13.0\",\n    \"@nestjs/testing\": \"^9.0.0 || ^10.0.0 || ^11.0.0\",\n    \"@types/node\": \"^20.0.0\",\n    \"@types/supertest\": \"^6.0.0\",\n    \"@typescript-eslint/eslint-plugin\": \"^6.0.0\",\n    \"@typescript-eslint/parser\": \"^6.0.0\",\n    \"@vitest/coverage-v8\": \"^1.6.0\",\n    \"eslint\": \"^8.0.0\",\n    \"prettier\": \"^3.0.0\",\n    \"supertest\": \"^7.0.0\",\n    \"tsup\": \"^8.0.0\",\n    \"typescript\": \"^5.3.0\",\n    \"vitest\": \"^1.6.0\"\n  },\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"engines\": {\n    \"node\": \">=18.0.0\"\n  }\n}\n","import { RedisXError, ErrorCode } from '@nestjs-redisx/core';\n\nimport { IRateLimitResult } from '../types';\n\n/**\n * Base error class for rate limit errors.\n */\nexport class RateLimitError extends RedisXError {\n  constructor(\n    message: string,\n    code: ErrorCode,\n    public readonly result?: IRateLimitResult,\n    cause?: Error,\n  ) {\n    super(message, code, cause, { result });\n  }\n}\n\n/**\n * Error thrown when rate limit is exceeded.\n */\nexport class RateLimitExceededError extends RateLimitError {\n  constructor(message: string, result: IRateLimitResult) {\n    super(message, ErrorCode.RATE_LIMIT_EXCEEDED, result);\n  }\n\n  /**\n   * Seconds until retry is allowed.\n   */\n  get retryAfter(): number {\n    return this.result?.retryAfter ?? 0;\n  }\n}\n\n/**\n * Error thrown when Lua script execution fails.\n */\nexport class RateLimitScriptError extends RateLimitError {\n  constructor(message: string, cause?: Error) {\n    super(message, ErrorCode.RATE_LIMIT_SCRIPT_ERROR, undefined, cause);\n  }\n}\n\n/**\n * Error thrown when plugin or per-call configuration is invalid.\n * Always fail-fast: never subject to the errorPolicy.\n */\nexport class InvalidRateLimitConfigError extends RateLimitError {\n  constructor(message: string) {\n    super(message, ErrorCode.RATE_LIMIT_CONFIG_INVALID);\n  }\n}\n","import { Catch, ExceptionFilter, ArgumentsHost, HttpStatus, Inject, Logger } from '@nestjs/common';\nimport { HttpAdapterHost } from '@nestjs/core';\n\nimport { RateLimitError, RateLimitExceededError, RateLimitScriptError } from '../../../shared/errors';\n\n/**\n * Exception filter for rate-limit errors.\n *\n * - `RateLimitExceededError` -> `429 Too Many Requests` (with rate-limit headers).\n * - `RateLimitScriptError` (raised on store failure under `errorPolicy:\n *   'fail-closed'`) -> `503 Service Unavailable`. Without this, the store error\n *   is NOT a subclass of `RateLimitExceededError` and would surface as an\n *   uncaught `500` on every request while Redis is unavailable.\n *\n * Catches the base `RateLimitError` so both cases are handled; any other\n * subclass falls back to 503 (a rate-limit subsystem failure, not the caller's\n * fault).\n */\n@Catch(RateLimitError)\nexport class RateLimitExceptionFilter implements ExceptionFilter {\n  private readonly logger = new Logger(RateLimitExceptionFilter.name);\n\n  constructor(@Inject(HttpAdapterHost) private readonly adapterHost: HttpAdapterHost) {}\n\n  catch(exception: RateLimitError, host: ArgumentsHost): void {\n    const httpAdapter = this.adapterHost.httpAdapter;\n    if (!httpAdapter) {\n      throw new Error('RateLimitExceptionFilter: HttpAdapterHost is not initialized. Ensure the NestJS application has bootstrapped with an HTTP adapter before handling requests.');\n    }\n\n    const response = host.switchToHttp().getResponse();\n\n    if (exception instanceof RateLimitExceededError) {\n      const result = exception.result;\n      const body = {\n        statusCode: HttpStatus.TOO_MANY_REQUESTS,\n        message: exception.message,\n        error: 'Too Many Requests',\n        retryAfter: exception.retryAfter,\n        limit: result?.limit,\n        remaining: result?.remaining,\n        reset: result?.reset,\n      };\n\n      httpAdapter.setHeader(response, 'Retry-After', exception.retryAfter.toString());\n      httpAdapter.reply(response, body, HttpStatus.TOO_MANY_REQUESTS);\n      return;\n    }\n\n    // Store/script failure under fail-closed (or any other rate-limit error):\n    // the limiter itself is degraded — 503, not a 500 — and log it so the\n    // outage is visible without being mistaken for an application bug.\n    if (exception instanceof RateLimitScriptError) {\n      this.logger.error(`Rate-limit store unavailable, rejecting request (fail-closed): ${exception.message}`);\n    }\n\n    const body = {\n      statusCode: HttpStatus.SERVICE_UNAVAILABLE,\n      message: 'Rate limiting is temporarily unavailable',\n      error: 'Service Unavailable',\n    };\n    httpAdapter.reply(response, body, HttpStatus.SERVICE_UNAVAILABLE);\n  }\n}\n","/**\n * Injection tokens for rate limit plugin.\n */\n\n/**\n * Injection token for rate limit plugin options.\n */\nexport const RATE_LIMIT_PLUGIN_OPTIONS = Symbol.for('RATE_LIMIT_PLUGIN_OPTIONS');\n\n/**\n * Injection token for rate limit service.\n */\nexport const RATE_LIMIT_SERVICE = Symbol.for('RATE_LIMIT_SERVICE');\n\n/**\n * Injection token for the Redis-backed rate limit store.\n * Kept as the primary store token for backward compatibility.\n */\nexport const RATE_LIMIT_STORE = Symbol.for('RATE_LIMIT_STORE');\n\n/**\n * Injection token for the in-memory (per-instance) rate limit store.\n * Registered alongside the Redis store; the service picks per check based on\n * the `store` option.\n */\nexport const RATE_LIMIT_MEMORY_STORE = Symbol.for('RATE_LIMIT_MEMORY_STORE');\n\n/**\n * Plugin-specific Redis driver token.\n * Resolves to the named client specified in plugin options.\n */\nexport const RATE_LIMIT_REDIS_DRIVER = Symbol.for('RATE_LIMIT_REDIS_DRIVER');\n","import { SetMetadata, UseGuards, applyDecorators, ExecutionContext } from '@nestjs/common';\n\nimport { IRateLimitResult, RateLimitStoreType } from '../../../shared/types';\nimport { RateLimitGuard } from '../guards/rate-limit.guard';\n\n/**\n * Metadata key for rate limit options.\n */\nexport const RATE_LIMIT_OPTIONS = Symbol.for('RATE_LIMIT_OPTIONS');\n\n/**\n * Key extractor function type.\n */\nexport type KeyExtractor = (context: ExecutionContext) => string | Promise<string>;\n\n/**\n * Rate limit options for decorator.\n */\nexport interface IRateLimitOptions {\n  /**\n   * Rate limit key or key extractor function.\n   * If string: used as-is\n   * If function: called with execution context\n   * If not provided: uses default key extractor from module config\n   *\n   * @example\n   * ```typescript\n   * @RateLimit({ key: 'global' })\n   * @RateLimit({ key: (ctx) => ctx.switchToHttp().getRequest().user.id })\n   * ```\n   */\n  key?: string | KeyExtractor;\n\n  /**\n   * Algorithm to use.\n   * @default from module config\n   */\n  algorithm?: 'fixed-window' | 'sliding-window' | 'token-bucket';\n\n  /**\n   * Max requests (fixed/sliding) or capacity (token bucket).\n   * @default from module config\n   */\n  points?: number;\n\n  /**\n   * Window duration in seconds.\n   * @default from module config\n   */\n  duration?: number;\n\n  /**\n   * Tokens per second (token bucket only).\n   */\n  refillRate?: number;\n\n  /**\n   * Store backend for this route. Overrides the plugin-level `store` default\n   * in either direction.\n   *\n   * `'memory'` counts per instance with zero Redis round-trip (approximate\n   * global limit); `'redis'` keeps the limit exact and shared across all\n   * instances. Keep auth-sensitive routes (login, OTP, password reset) on\n   * `'redis'`.\n   *\n   * @example\n   * ```typescript\n   * // Plugin default store: 'memory' — cheap per-node limiting everywhere\n   * @Post('login')\n   * @RateLimit({ store: 'redis', points: 5, duration: 300 })\n   * login() {}\n   * ```\n   */\n  store?: RateLimitStoreType;\n\n  /**\n   * Skip condition function.\n   * If returns true, rate limiting is skipped.\n   *\n   * @example\n   * ```typescript\n   * @RateLimit({\n   *   skip: (ctx) => {\n   *     const req = ctx.switchToHttp().getRequest();\n   *     return req.user?.role === 'admin';\n   *   }\n   * })\n   * ```\n   */\n  skip?: (context: ExecutionContext) => boolean | Promise<boolean>;\n\n  /**\n   * Custom error message.\n   */\n  message?: string;\n\n  /**\n   * Custom error factory.\n   * Allows creating custom errors based on rate limit result.\n   *\n   * @example\n   * ```typescript\n   * @RateLimit({\n   *   errorFactory: (result) => new CustomRateLimitError(result)\n   * })\n   * ```\n   */\n  errorFactory?: (result: IRateLimitResult) => Error;\n}\n\n/**\n * Rate limit decorator.\n * Can be applied to methods or classes.\n *\n * @param options - Rate limit options\n * @returns Decorator function\n *\n * @example\n * ```typescript\n * // Method decorator\n * @Controller('api')\n * export class ApiController {\n *   @Get('data')\n *   @RateLimit({ points: 10, duration: 60 })\n *   getData() {\n *     return { data: 'value' };\n *   }\n * }\n *\n * // Class decorator (applies to all methods)\n * @Controller('api')\n * @RateLimit({ points: 100, duration: 60 })\n * export class ApiController {\n *   @Get('data')\n *   getData() {\n *     return { data: 'value' };\n *   }\n * }\n *\n * // Custom key extractor\n * @Get('user-data')\n * @RateLimit({\n *   key: (ctx) => {\n *     const req = ctx.switchToHttp().getRequest();\n *     return `user:${req.user.id}`;\n *   },\n *   points: 50,\n * })\n * getUserData() {\n *   return { data: 'value' };\n * }\n * ```\n */\nexport function RateLimit(options: IRateLimitOptions = {}): MethodDecorator & ClassDecorator {\n  return applyDecorators(SetMetadata(RATE_LIMIT_OPTIONS, options), UseGuards(RateLimitGuard)) as MethodDecorator & ClassDecorator;\n}\n","import { Injectable, CanActivate, ExecutionContext, Inject, Optional } from '@nestjs/common';\nimport { HttpAdapterHost, Reflector } from '@nestjs/core';\n\nimport { RATE_LIMIT_SERVICE, RATE_LIMIT_PLUGIN_OPTIONS } from '../../../shared/constants';\nimport { RateLimitExceededError } from '../../../shared/errors';\nimport { IRateLimitPluginOptions, IRateLimitResult } from '../../../shared/types';\nimport { IRateLimitService } from '../../application/ports/rate-limit-service.port';\nimport { RATE_LIMIT_OPTIONS, IRateLimitOptions } from '../decorators/rate-limit.decorator';\n\n// Optional metrics integration\nconst METRICS_SERVICE = Symbol.for('METRICS_SERVICE');\n\ninterface IMetricsService {\n  incrementCounter(name: string, labels?: Record<string, string>, value?: number): void;\n}\n\n// Optional tracing integration\nconst TRACING_SERVICE = Symbol.for('TRACING_SERVICE');\n\ninterface ISpan {\n  setAttribute(key: string, value: unknown): this;\n  addEvent(name: string, attributes?: Record<string, unknown>): this;\n  setStatus(status: 'OK' | 'ERROR'): this;\n  recordException(error: Error): this;\n  end(): void;\n}\n\ninterface ITracingService {\n  startSpan(name: string, options?: { kind?: string; attributes?: Record<string, unknown> }): ISpan;\n}\n\n/**\n * Per-request marker that records the rate limit has already been consumed for\n * the current request.\n *\n * The {@link RateLimit} decorator binds this guard via `UseGuards`. NestJS does\n * not deduplicate the same guard across binding scopes, so a route decorated on\n * both the class and the method — or a route decorated with `@RateLimit` while\n * the guard is also registered globally as `APP_GUARD` — would otherwise run\n * `canActivate` (and consume a point) more than once for a single request. The\n * marker makes consumption idempotent within one request.\n */\nconst RATE_LIMIT_CONSUMED = Symbol('redisx.rateLimitConsumed');\n\n/**\n * Rate limit guard.\n * Enforces rate limiting based on @RateLimit() decorator configuration.\n */\n@Injectable()\nexport class RateLimitGuard implements CanActivate {\n  constructor(\n    @Inject(RATE_LIMIT_SERVICE)\n    private readonly rateLimitService: IRateLimitService,\n    @Inject(RATE_LIMIT_PLUGIN_OPTIONS)\n    private readonly config: IRateLimitPluginOptions,\n    @Inject(Reflector) private readonly reflector: Reflector,\n    @Inject(HttpAdapterHost) private readonly adapterHost: HttpAdapterHost,\n    @Optional() @Inject(METRICS_SERVICE) private readonly metrics?: IMetricsService,\n    @Optional() @Inject(TRACING_SERVICE) private readonly tracing?: ITracingService,\n  ) {}\n\n  /**\n   * Guard activation logic.\n   * Checks rate limit and sets response headers.\n   */\n  async canActivate(context: ExecutionContext): Promise<boolean> {\n    // The limit must be consumed at most once per request even if the guard is\n    // bound more than once (class + method decorator, or decorator + global\n    // APP_GUARD). If a prior binding already consumed it, allow without\n    // re-consuming.\n    const request = context.switchToHttp().getRequest<Record<symbol, unknown>>();\n    if (request[RATE_LIMIT_CONSUMED]) {\n      return true;\n    }\n\n    const options = this.getOptions(context);\n\n    // Check skip condition\n    if (await this.shouldSkip(context, options)) {\n      return true;\n    }\n\n    request[RATE_LIMIT_CONSUMED] = true;\n\n    const key = await this.extractKey(context, options);\n    const span = this.tracing?.startSpan('ratelimit.check', {\n      kind: 'INTERNAL',\n      attributes: { 'ratelimit.key': key },\n    });\n\n    try {\n      const result = await this.rateLimitService.check(key, options);\n\n      // Set response headers\n      this.setHeaders(context, result);\n\n      span?.setAttribute('ratelimit.allowed', result.allowed);\n      span?.setAttribute('ratelimit.remaining', result.remaining);\n      span?.setAttribute('ratelimit.limit', result.limit);\n\n      if (!result.allowed) {\n        this.metrics?.incrementCounter('redisx_ratelimit_requests_total', { status: 'rejected' });\n        span?.setStatus('OK'); // Not an error - rate limit working as expected\n        throw this.createError(result, options);\n      }\n\n      this.metrics?.incrementCounter('redisx_ratelimit_requests_total', { status: 'allowed' });\n      span?.setStatus('OK');\n      return true;\n    } catch (error) {\n      if (!(error instanceof RateLimitExceededError)) {\n        span?.recordException(error as Error);\n        span?.setStatus('ERROR');\n      }\n      throw error;\n    } finally {\n      span?.end();\n    }\n  }\n\n  /**\n   * Get rate limit options from decorator metadata.\n   * Merges class-level and method-level options.\n   */\n  private getOptions(context: ExecutionContext): IRateLimitOptions {\n    const handlerOptions = this.reflector.get<IRateLimitOptions>(RATE_LIMIT_OPTIONS, context.getHandler());\n    const classOptions = this.reflector.get<IRateLimitOptions>(RATE_LIMIT_OPTIONS, context.getClass());\n\n    return { ...classOptions, ...handlerOptions };\n  }\n\n  /**\n   * Extract rate limit key from context.\n   */\n  private async extractKey(context: ExecutionContext, options: IRateLimitOptions): Promise<string> {\n    const extractor = options.key ?? this.config.defaultKeyExtractor ?? 'ip';\n\n    // Custom function\n    if (typeof extractor === 'function') {\n      return await extractor(context);\n    }\n\n    // String key\n    if (typeof extractor === 'string' && !['ip', 'user', 'apiKey'].includes(extractor)) {\n      return extractor;\n    }\n\n    // Predefined extractors\n    const request = context.switchToHttp().getRequest();\n\n    switch (extractor) {\n      case 'ip':\n        return this.getClientIp(request);\n      case 'user':\n        return this.getUserId(request);\n      case 'apiKey':\n        return this.getApiKey(request);\n      default:\n        return this.getClientIp(request);\n    }\n  }\n\n  /**\n   * Get client IP address.\n   */\n  private getClientIp(request: Request & { ip?: string; ips?: string[] }): string {\n    // Secure default: take the IP from the framework. `request.ip` respects the\n    // app's own trust-proxy configuration (Express `trust proxy` / Fastify\n    // `trustProxy`), so it is the real client IP and cannot be spoofed by a\n    // client-supplied header. Only read forwarding headers ourselves when the\n    // operator explicitly opts in via `trustProxy: true`.\n    if (this.config.trustProxy === true) {\n      const forwardedFor = (request.headers as unknown as Record<string, string>)['x-forwarded-for'];\n      if (forwardedFor) {\n        const ips = forwardedFor.split(',').map((ip) => ip.trim());\n        if (ips[0]) return ips[0];\n      }\n\n      const realIp = (request.headers as unknown as Record<string, string>)['x-real-ip'];\n      if (realIp) {\n        return realIp;\n      }\n    }\n\n    return request.ip || 'unknown';\n  }\n\n  /**\n   * Get user ID from request.\n   */\n  private getUserId(request: Request & { user?: { id?: string } }): string {\n    const userId = request.user?.id;\n    if (!userId) {\n      throw new Error('User ID not found. Ensure authentication guard runs before rate limit guard.');\n    }\n    return `user:${userId}`;\n  }\n\n  /**\n   * Get API key from request.\n   */\n  private getApiKey(request: Request): string {\n    const apiKey = (request.headers as unknown as Record<string, string>)['x-api-key'] || (request.headers as unknown as Record<string, string>)['authorization'];\n\n    if (!apiKey) {\n      throw new Error('API key not found. Ensure request includes X-API-Key or Authorization header.');\n    }\n\n    return `apikey:${apiKey}`;\n  }\n\n  /**\n   * Set response headers.\n   */\n  private setHeaders(context: ExecutionContext, result: IRateLimitResult): void {\n    if (this.config.includeHeaders === false) {\n      return;\n    }\n\n    const httpAdapter = this.adapterHost.httpAdapter;\n    if (!httpAdapter) {\n      throw new Error('RateLimitGuard: HttpAdapterHost is not initialized. Ensure the NestJS application has bootstrapped with an HTTP adapter before handling requests.');\n    }\n\n    const response = context.switchToHttp().getResponse();\n    const headers = this.config.headers ?? {};\n\n    const limitHeader = headers.limit ?? 'X-RateLimit-Limit';\n    const remainingHeader = headers.remaining ?? 'X-RateLimit-Remaining';\n    const resetHeader = headers.reset ?? 'X-RateLimit-Reset';\n    const retryAfterHeader = headers.retryAfter ?? 'Retry-After';\n\n    httpAdapter.setHeader(response, limitHeader, result.limit.toString());\n    httpAdapter.setHeader(response, remainingHeader, result.remaining.toString());\n    httpAdapter.setHeader(response, resetHeader, result.reset.toString());\n\n    if (!result.allowed && result.retryAfter) {\n      httpAdapter.setHeader(response, retryAfterHeader, result.retryAfter.toString());\n    }\n  }\n\n  /**\n   * Create error when rate limit exceeded.\n   */\n  private createError(result: IRateLimitResult, options: IRateLimitOptions): Error {\n    // Use custom error factory if provided\n    if (options.errorFactory) {\n      return options.errorFactory(result);\n    }\n\n    // Use module-level error factory if provided\n    if (this.config.errorFactory) {\n      return this.config.errorFactory(result);\n    }\n\n    // Default error\n    const message = options.message ?? `Rate limit exceeded. Try again in ${result.retryAfter || 0} seconds.`;\n\n    return new RateLimitExceededError(message, result);\n  }\n\n  /**\n   * Check if rate limiting should be skipped.\n   */\n  private async shouldSkip(context: ExecutionContext, options: IRateLimitOptions): Promise<boolean> {\n    // Check decorator-level skip\n    if (options.skip) {\n      return await options.skip(context);\n    }\n\n    // Check module-level skip\n    if (this.config.skip) {\n      return await this.config.skip(context);\n    }\n\n    return false;\n  }\n}\n","import { InvalidRateLimitConfigError } from '../errors';\nimport { IRateLimitMemoryOptions, RateLimitStoreType } from '../types';\n\nconst STORE_TYPES: readonly RateLimitStoreType[] = ['redis', 'memory'];\n\n/**\n * Validates a `store` value from plugin options or a per-call config.\n * Fail-fast: configuration mistakes must never be masked by the errorPolicy.\n */\nexport function validateStoreType(store: unknown, source: string): void {\n  if (store === undefined) {\n    return;\n  }\n  if (!STORE_TYPES.includes(store as RateLimitStoreType)) {\n    throw new InvalidRateLimitConfigError(`${source}: \"store\" must be one of ${STORE_TYPES.map((s) => `'${s}'`).join(' | ')}, got ${JSON.stringify(store)}.`);\n  }\n}\n\n/**\n * Validates memory-store sizing options.\n */\nexport function validateMemoryOptions(memory: IRateLimitMemoryOptions | undefined, source: string): void {\n  if (memory === undefined) {\n    return;\n  }\n  if (memory.maxKeys !== undefined && (!Number.isSafeInteger(memory.maxKeys) || memory.maxKeys <= 0)) {\n    throw new InvalidRateLimitConfigError(`${source}: \"memory.maxKeys\" must be a positive integer, got ${JSON.stringify(memory.maxKeys)}.`);\n  }\n  if (memory.sweepIntervalMs !== undefined && (!Number.isSafeInteger(memory.sweepIntervalMs) || memory.sweepIntervalMs <= 0)) {\n    throw new InvalidRateLimitConfigError(`${source}: \"memory.sweepIntervalMs\" must be a positive integer (milliseconds), got ${JSON.stringify(memory.sweepIntervalMs)}.`);\n  }\n}\n","import { Injectable, Inject, Optional } from '@nestjs/common';\n\nimport { RATE_LIMIT_MEMORY_STORE, RATE_LIMIT_PLUGIN_OPTIONS, RATE_LIMIT_STORE } from '../../../shared/constants';\nimport { InvalidRateLimitConfigError, RateLimitScriptError } from '../../../shared/errors';\nimport { IRateLimitPluginOptions, IRateLimitConfig, IRateLimitResetOptions, IRateLimitResult, IRateLimitState, RateLimitStoreType } from '../../../shared/types';\nimport { validateStoreType } from '../../../shared/utils/validate-store-options';\nimport { IRateLimitService } from '../ports/rate-limit-service.port';\nimport { IRateLimitStore } from '../ports/rate-limit-store.port';\n\n/**\n * Rate limit service implementation.\n * Provides rate limiting operations with multiple algorithms over two\n * interchangeable stores: distributed (redis) and per-instance (memory).\n */\n@Injectable()\nexport class RateLimitService implements IRateLimitService {\n  constructor(\n    @Inject(RATE_LIMIT_PLUGIN_OPTIONS)\n    private readonly config: IRateLimitPluginOptions,\n    @Inject(RATE_LIMIT_STORE)\n    private readonly store: IRateLimitStore,\n    @Optional()\n    @Inject(RATE_LIMIT_MEMORY_STORE)\n    private readonly memoryStore?: IRateLimitStore,\n  ) {}\n\n  /**\n   * Check and consume rate limit.\n   */\n  async check(key: string, config: IRateLimitConfig = {}): Promise<IRateLimitResult> {\n    const algorithm = config.algorithm ?? this.config.defaultAlgorithm ?? 'sliding-window';\n    // Include algorithm in key to avoid WRONGTYPE errors when different algorithms\n    // use different Redis data types (string, sorted set, hash) for the same key\n    const fullKey = this.buildKey(key, algorithm);\n    // Resolved OUTSIDE the try block: configuration errors are fail-fast and\n    // must never be masked by errorPolicy 'fail-open'.\n    const store = this.resolveStore(config.store);\n\n    try {\n      switch (algorithm) {\n        case 'fixed-window':\n          return await this.checkFixedWindow(store, fullKey, config);\n        case 'sliding-window':\n          return await this.checkSlidingWindow(store, fullKey, config);\n        case 'token-bucket':\n          return await this.checkTokenBucket(store, fullKey, config);\n        default:\n          throw new Error(`Unknown algorithm: ${algorithm}`);\n      }\n    } catch (error) {\n      return this.handleError(error as Error, config);\n    }\n  }\n\n  /**\n   * Check without consuming.\n   */\n  async peek(key: string, config: IRateLimitConfig = {}): Promise<IRateLimitResult> {\n    const algorithm = config.algorithm ?? this.config.defaultAlgorithm ?? 'sliding-window';\n    const fullKey = this.buildKey(key, algorithm);\n    const store = this.resolveStore(config.store);\n\n    try {\n      const storeConfig = this.buildStoreConfig(algorithm, config);\n      return await store.peek(fullKey, algorithm, storeConfig);\n    } catch (error) {\n      return this.handleError(error as Error, config);\n    }\n  }\n\n  /**\n   * Reset rate limit for key.\n   * Resets all algorithm variants (fixed-window, sliding-window, token-bucket).\n   * With no store targeting, BOTH stores are swept: the service cannot know\n   * which store a key was counted in. Memory-store resets are per-instance.\n   */\n  async reset(key: string, options: IRateLimitResetOptions = {}): Promise<void> {\n    validateStoreType(options.store, 'reset options');\n\n    const stores: IRateLimitStore[] = [];\n    if (options.store === undefined) {\n      stores.push(this.store);\n      if (this.memoryStore) {\n        stores.push(this.memoryStore);\n      }\n    } else {\n      stores.push(this.resolveStore(options.store));\n    }\n\n    const algorithms = ['fixed-window', 'sliding-window', 'token-bucket'] as const;\n    await Promise.all(stores.flatMap((store) => algorithms.map((algo) => store.reset(this.buildKey(key, algo)))));\n  }\n\n  /**\n   * Get current state.\n   */\n  async getState(key: string, config: IRateLimitConfig = {}): Promise<IRateLimitState> {\n    const result = await this.peek(key, config);\n\n    return {\n      current: result.current,\n      limit: result.limit,\n      remaining: result.remaining,\n      resetAt: new Date(result.reset * 1000),\n    };\n  }\n\n  /**\n   * Resolve the store for a call: per-call override -> plugin default -> redis.\n   * Configuration errors here are fail-fast (never subject to errorPolicy).\n   */\n  private resolveStore(override: RateLimitStoreType | undefined): IRateLimitStore {\n    validateStoreType(override, 'rate limit config');\n    const storeType = override ?? this.config.store ?? 'redis';\n\n    if (storeType === 'memory') {\n      if (!this.memoryStore) {\n        throw new InvalidRateLimitConfigError('The in-memory rate limit store is not available. It is registered automatically by RateLimitPlugin; when constructing RateLimitService manually, pass an InMemoryRateLimitStoreAdapter as the third argument.');\n      }\n      return this.memoryStore;\n    }\n\n    return this.store;\n  }\n\n  /**\n   * Check fixed window rate limit.\n   */\n  private async checkFixedWindow(store: IRateLimitStore, key: string, config: IRateLimitConfig): Promise<IRateLimitResult> {\n    const points = config.points ?? this.config.defaultPoints ?? 100;\n    const duration = config.duration ?? this.config.defaultDuration ?? 60;\n\n    return await store.fixedWindow(key, points, duration);\n  }\n\n  /**\n   * Check sliding window rate limit.\n   */\n  private async checkSlidingWindow(store: IRateLimitStore, key: string, config: IRateLimitConfig): Promise<IRateLimitResult> {\n    const points = config.points ?? this.config.defaultPoints ?? 100;\n    const duration = config.duration ?? this.config.defaultDuration ?? 60;\n\n    return await store.slidingWindow(key, points, duration);\n  }\n\n  /**\n   * Check token bucket rate limit.\n   */\n  private async checkTokenBucket(store: IRateLimitStore, key: string, config: IRateLimitConfig): Promise<IRateLimitResult> {\n    const capacity = config.capacity ?? config.points ?? this.config.defaultPoints ?? 100;\n    const refillRate = config.refillRate ?? (config.duration ? capacity / config.duration : 10);\n\n    return await store.tokenBucket(key, capacity, refillRate, 1);\n  }\n\n  /**\n   * Build store configuration.\n   */\n  private buildStoreConfig(algorithm: string, config: IRateLimitConfig): Record<string, number> {\n    const baseConfig = {\n      points: config.points ?? this.config.defaultPoints ?? 100,\n      duration: config.duration ?? this.config.defaultDuration ?? 60,\n    };\n\n    if (algorithm === 'token-bucket') {\n      const capacity = config.capacity ?? baseConfig.points;\n      const refillRate = config.refillRate ?? capacity / baseConfig.duration;\n      return { capacity, refillRate };\n    }\n\n    return baseConfig;\n  }\n\n  /**\n   * Handle error based on error policy.\n   */\n  private handleError(error: Error, config: IRateLimitConfig): IRateLimitResult {\n    const errorPolicy = this.config.errorPolicy ?? 'fail-closed';\n\n    if (errorPolicy === 'fail-open') {\n      // Allow request on error (high availability)\n      const points = config.points ?? this.config.defaultPoints ?? 100;\n      const duration = config.duration ?? this.config.defaultDuration ?? 60;\n\n      return {\n        allowed: true,\n        limit: points,\n        remaining: points,\n        reset: Math.floor(Date.now() / 1000) + duration,\n        current: 0,\n      };\n    }\n\n    // Fail-closed: propagate error\n    throw new RateLimitScriptError(`Rate limit check failed: ${error.message}`, error);\n  }\n\n  /**\n   * Build full key with prefix and algorithm.\n   * Including algorithm prevents WRONGTYPE errors when different algorithms\n   * use different Redis data types for the same logical key.\n   */\n  private buildKey(key: string, algorithm?: string): string {\n    const prefix = this.config.keyPrefix ?? 'rl:';\n    const algoPrefix = algorithm ? `${algorithm}:` : '';\n    return `${prefix}${algoPrefix}${key}`;\n  }\n}\n","import { IRateLimitResult } from '../../../shared/types';\n\n/**\n * Pure, time-injected implementations of the three rate limit algorithms for\n * the in-memory store. Result parity contract: for the same inputs these\n * functions produce exactly what the Lua scripts + the Redis adapter's\n * parse helpers produce — including rounding and the `retryAfter`\n * 0 -> undefined mapping. `now` is always epoch milliseconds.\n */\n\nexport interface IFixedWindowEntry {\n  kind: 'fixed';\n  /** Window start, epoch seconds. */\n  window: number;\n  count: number;\n  /** Epoch ms after which the entry is dead (sweep/lazy-expiry boundary). */\n  expiresAt: number;\n}\n\nexport interface ISlidingWindowEntry {\n  kind: 'sliding';\n  /** Epoch ms of each allowed request, oldest first. */\n  timestamps: number[];\n  expiresAt: number;\n}\n\nexport interface ITokenBucketEntry {\n  kind: 'bucket';\n  tokens: number;\n  /** Epoch ms of the last refill computation. */\n  lastRefill: number;\n  expiresAt: number;\n}\n\nexport type IMemoryRateLimitEntry = IFixedWindowEntry | ISlidingWindowEntry | ITokenBucketEntry;\n\n/**\n * True when the entry has outlived its retention window and must be treated\n * as absent (the in-memory equivalent of Redis key expiry).\n */\nexport function isEntryExpired(entry: IMemoryRateLimitEntry, now: number): boolean {\n  return now >= entry.expiresAt;\n}\n\nfunction liveEntry<K extends IMemoryRateLimitEntry['kind']>(entry: IMemoryRateLimitEntry | undefined, kind: K, now: number): Extract<IMemoryRateLimitEntry, { kind: K }> | undefined {\n  if (entry?.kind !== kind || isEntryExpired(entry, now)) {\n    return undefined;\n  }\n  return entry as Extract<IMemoryRateLimitEntry, { kind: K }>;\n}\n\n/**\n * Fixed window: counter per aligned window. Lua parity: the counter keeps\n * incrementing on rejects (INCR runs unconditionally).\n */\nexport function applyFixedWindow(entry: IMemoryRateLimitEntry | undefined, now: number, points: number, duration: number): { entry: IFixedWindowEntry; result: IRateLimitResult } {\n  const nowSeconds = Math.floor(now / 1000);\n  const window = Math.floor(nowSeconds / duration) * duration;\n\n  const live = liveEntry(entry, 'fixed', now);\n  const count = (live?.window === window ? live.count : 0) + 1;\n\n  const allowed = count <= points;\n  const reset = window + duration;\n  const next: IFixedWindowEntry = { kind: 'fixed', window, count, expiresAt: reset * 1000 };\n\n  return {\n    entry: next,\n    result: {\n      allowed,\n      limit: points,\n      remaining: Math.max(0, points - count),\n      reset,\n      current: count,\n      retryAfter: allowed ? undefined : Math.ceil(reset - now / 1000),\n    },\n  };\n}\n\n/**\n * Sliding window log. Lua parity: entries with score <= now - duration are\n * pruned (inclusive boundary), rejected requests are NOT recorded, and the\n * expiry is refreshed only on allow (PEXPIRE lives in the allow branch).\n */\nexport function applySlidingWindow(entry: IMemoryRateLimitEntry | undefined, now: number, points: number, duration: number): { entry: ISlidingWindowEntry; result: IRateLimitResult } {\n  const durationMs = duration * 1000;\n  const windowStart = now - durationMs;\n\n  const live = liveEntry(entry, 'sliding', now);\n  const timestamps = live ? live.timestamps.filter((ts) => ts > windowStart) : [];\n  const current = timestamps.length;\n  const reset = Math.ceil((now + durationMs) / 1000);\n\n  if (current < points) {\n    timestamps.push(now);\n    return {\n      entry: { kind: 'sliding', timestamps, expiresAt: now + durationMs },\n      result: {\n        allowed: true,\n        limit: points,\n        remaining: points - current - 1,\n        reset,\n        current: current + 1,\n        retryAfter: undefined,\n      },\n    };\n  }\n\n  const oldest = timestamps[0];\n  const retryAfter = oldest !== undefined ? Math.max(0, Math.ceil((oldest + durationMs - now) / 1000)) : 0;\n  return {\n    entry: { kind: 'sliding', timestamps, expiresAt: live ? live.expiresAt : now + durationMs },\n    result: {\n      allowed: false,\n      limit: points,\n      remaining: 0,\n      reset,\n      current,\n      // Lua parity: parse maps a zero retry_after to undefined\n      retryAfter: retryAfter > 0 ? retryAfter : undefined,\n    },\n  };\n}\n\n/**\n * Token bucket. Lua parity: state is persisted on BOTH branches (reject still\n * stores the refilled tokens and stamps lastRefill), `current` is the floored\n * pre-consume token count, and retention mirrors\n * PEXPIRE ceil(capacity / refillRate * 1000) + 1000.\n */\nexport function applyTokenBucket(entry: IMemoryRateLimitEntry | undefined, now: number, capacity: number, refillRate: number, consume = 1): { entry: ITokenBucketEntry; result: IRateLimitResult } {\n  const live = liveEntry(entry, 'bucket', now);\n  const storedTokens = live ? live.tokens : capacity;\n  const lastRefill = live ? live.lastRefill : now;\n\n  const elapsedSeconds = (now - lastRefill) / 1000;\n  const tokens = Math.min(capacity, storedTokens + elapsedSeconds * refillRate);\n\n  const allowed = tokens >= consume;\n  const newTokens = allowed ? tokens - consume : tokens;\n\n  const retryAfter = allowed ? 0 : Math.ceil((consume - newTokens) / refillRate);\n  const timeToFull = refillRate > 0 ? (capacity - newTokens) / refillRate : 0;\n\n  return {\n    entry: {\n      kind: 'bucket',\n      tokens: newTokens,\n      lastRefill: now,\n      expiresAt: now + Math.ceil((capacity / refillRate) * 1000) + 1000,\n    },\n    result: {\n      allowed,\n      limit: capacity,\n      remaining: Math.floor(newTokens),\n      reset: Math.ceil(now / 1000 + timeToFull),\n      current: Math.floor(tokens),\n      retryAfter: retryAfter > 0 ? retryAfter : undefined,\n    },\n  };\n}\n\n/**\n * Peek parity with RedisRateLimitStoreAdapter.peek: fixed window uses strict\n * `<` for allowed, sliding window resets at floor(now/1000) + duration, and\n * token bucket reports `current` as capacity - remaining.\n */\nexport function peekFixedWindow(entry: IMemoryRateLimitEntry | undefined, now: number, points: number, duration: number): IRateLimitResult {\n  const nowSeconds = Math.floor(now / 1000);\n  const window = Math.floor(nowSeconds / duration) * duration;\n\n  const live = liveEntry(entry, 'fixed', now);\n  const current = live?.window === window ? live.count : 0;\n\n  return {\n    allowed: current < points,\n    limit: points,\n    remaining: Math.max(0, points - current),\n    reset: window + duration,\n    current,\n  };\n}\n\nexport function peekSlidingWindow(entry: IMemoryRateLimitEntry | undefined, now: number, points: number, duration: number): IRateLimitResult {\n  const live = liveEntry(entry, 'sliding', now);\n  const windowStart = now - duration * 1000;\n  const current = live ? live.timestamps.filter((ts) => ts > windowStart).length : 0;\n\n  return {\n    allowed: current < points,\n    limit: points,\n    remaining: Math.max(0, points - current),\n    reset: Math.floor(now / 1000) + duration,\n    current,\n  };\n}\n\nexport function peekTokenBucket(entry: IMemoryRateLimitEntry | undefined, now: number, capacity: number, refillRate: number): IRateLimitResult {\n  const live = liveEntry(entry, 'bucket', now);\n  const storedTokens = live ? live.tokens : capacity;\n  const lastRefill = live ? live.lastRefill : now;\n\n  const elapsedSeconds = Math.max(0, (now - lastRefill) / 1000);\n  const tokens = Math.min(capacity, storedTokens + elapsedSeconds * refillRate);\n  const remaining = Math.floor(tokens);\n  const timeToFull = refillRate > 0 ? (capacity - tokens) / refillRate : 0;\n\n  return {\n    allowed: tokens >= 1,\n    limit: capacity,\n    remaining,\n    reset: Math.ceil(now / 1000 + timeToFull),\n    current: capacity - remaining,\n  };\n}\n","import { Injectable, Inject, Optional, OnModuleDestroy } from '@nestjs/common';\n\nimport { RATE_LIMIT_PLUGIN_OPTIONS } from '../../../shared/constants';\nimport { IRateLimitPluginOptions, IRateLimitResult } from '../../../shared/types';\nimport { IRateLimitStore } from '../../application/ports/rate-limit-store.port';\nimport { applyFixedWindow, applySlidingWindow, applyTokenBucket, peekFixedWindow, peekSlidingWindow, peekTokenBucket, isEntryExpired, IMemoryRateLimitEntry } from '../../domain/memory/in-memory-algorithms';\n\nconst DEFAULT_MAX_KEYS = 100_000;\nconst DEFAULT_SWEEP_INTERVAL_MS = 30_000;\n\n/**\n * In-memory (per-instance) rate limit store.\n *\n * Counters live in process memory: no Redis round-trip on the request path,\n * at the cost of per-node (approximate) limits. Node's single-threaded event\n * loop makes each check atomic without locking.\n *\n * Memory safety: Redis bounds its keyspace via EXPIRE; here a key cap with\n * oldest-first eviction plus a periodic sweep of expired entries prevents\n * unbounded growth under key spray (e.g. random IPs). The sweep timer starts\n * lazily on first use and never keeps the process alive (unref).\n */\n@Injectable()\nexport class InMemoryRateLimitStoreAdapter implements IRateLimitStore, OnModuleDestroy {\n  private readonly entries = new Map<string, IMemoryRateLimitEntry>();\n  private readonly maxKeys: number;\n  private readonly sweepIntervalMs: number;\n  private sweepTimer: NodeJS.Timeout | null = null;\n\n  constructor(@Optional() @Inject(RATE_LIMIT_PLUGIN_OPTIONS) options: IRateLimitPluginOptions = {}) {\n    this.maxKeys = options.memory?.maxKeys ?? DEFAULT_MAX_KEYS;\n    this.sweepIntervalMs = options.memory?.sweepIntervalMs ?? DEFAULT_SWEEP_INTERVAL_MS;\n  }\n\n  /** Number of tracked keys (monitoring/testing aid). */\n  get size(): number {\n    return this.entries.size;\n  }\n\n  async fixedWindow(key: string, points: number, duration: number): Promise<IRateLimitResult> {\n    const { entry, result } = applyFixedWindow(this.entries.get(key), Date.now(), points, duration);\n    this.write(key, entry);\n    return result;\n  }\n\n  async slidingWindow(key: string, points: number, duration: number): Promise<IRateLimitResult> {\n    const { entry, result } = applySlidingWindow(this.entries.get(key), Date.now(), points, duration);\n    this.write(key, entry);\n    return result;\n  }\n\n  async tokenBucket(key: string, capacity: number, refillRate: number, consume = 1): Promise<IRateLimitResult> {\n    const { entry, result } = applyTokenBucket(this.entries.get(key), Date.now(), capacity, refillRate, consume);\n    this.write(key, entry);\n    return result;\n  }\n\n  async peek(key: string, algorithm: string, config: Record<string, number>): Promise<IRateLimitResult> {\n    const now = Date.now();\n    const entry = this.entries.get(key);\n\n    if (algorithm === 'fixed-window') {\n      return peekFixedWindow(entry, now, config.points || 100, config.duration || 60);\n    }\n    if (algorithm === 'sliding-window') {\n      return peekSlidingWindow(entry, now, config.points || 100, config.duration || 60);\n    }\n    return peekTokenBucket(entry, now, config.capacity || 100, config.refillRate || 10);\n  }\n\n  async reset(key: string): Promise<void> {\n    this.entries.delete(key);\n  }\n\n  onModuleDestroy(): void {\n    if (this.sweepTimer) {\n      clearInterval(this.sweepTimer);\n      this.sweepTimer = null;\n    }\n  }\n\n  private write(key: string, entry: IMemoryRateLimitEntry): void {\n    if (!this.entries.has(key) && this.entries.size >= this.maxKeys) {\n      this.evictOldest();\n    }\n    this.entries.set(key, entry);\n    this.ensureSweepTimer();\n  }\n\n  /**\n   * Approximate FIFO eviction: Map preserves insertion order, so the first\n   * key is the oldest-inserted one. Limiter entries are short-lived, which\n   * makes insertion age a good enough proxy for staleness.\n   */\n  private evictOldest(): void {\n    while (this.entries.size >= this.maxKeys) {\n      const oldest = this.entries.keys().next();\n      if (oldest.done) {\n        return;\n      }\n      this.entries.delete(oldest.value);\n    }\n  }\n\n  private ensureSweepTimer(): void {\n    if (this.sweepTimer) {\n      return;\n    }\n    this.sweepTimer = setInterval(() => this.sweep(), this.sweepIntervalMs);\n    this.sweepTimer.unref?.();\n  }\n\n  private sweep(): void {\n    const now = Date.now();\n    for (const [key, entry] of this.entries) {\n      if (isEntryExpired(entry, now)) {\n        this.entries.delete(key);\n      }\n    }\n  }\n}\n","/**\n * Inline Lua scripts for rate limiting operations.\n *\n * Scripts are stored as inline strings to avoid issues with file reading\n * after build (dist directory doesn't contain .lua files).\n */\n\n/**\n * Fixed Window Lua script.\n *\n * State lives in a hash at the BARE key ({window, count}) instead of a\n * per-window subkey, so `reset()`'s DEL on the key actually clears the\n * counter (and stays single-key, i.e. cluster-safe without hash tags).\n * A stale window field means the window rolled over: the hash is dropped\n * and recreated for the current window.\n *\n * KEYS[1] = rate limit key\n * ARGV[1] = max points\n * ARGV[2] = window duration (seconds)\n * ARGV[3] = current timestamp\n *\n * Returns: {allowed, remaining, reset, current}\n */\nexport const FIXED_WINDOW_SCRIPT = `\nlocal key = KEYS[1]\nlocal max_points = tonumber(ARGV[1])\nlocal duration = tonumber(ARGV[2])\nlocal now = tonumber(ARGV[3])\n\nlocal window = math.floor(now / duration) * duration\n\nlocal reset_needed = false\nlocal stored_window = redis.call('HGET', key, 'window')\nif not stored_window then\n  reset_needed = true\nelse\n  if tonumber(stored_window) ~= window then\n    reset_needed = true\n  end\nend\nif reset_needed then\n  redis.call('DEL', key)\nend\n\nlocal current = redis.call('HINCRBY', key, 'count', 1)\n\nif current == 1 then\n  redis.call('HSET', key, 'window', window)\n  redis.call('EXPIRE', key, duration)\nend\n\nlocal allowed = current <= max_points\nlocal remaining = math.max(0, max_points - current)\nlocal reset = window + duration\n\nreturn {allowed and 1 or 0, remaining, reset, current}\n`.trim();\n\n/**\n * Sliding Window Log Lua script.\n *\n * KEYS[1] = rate limit key\n * ARGV[1] = max points\n * ARGV[2] = window duration (seconds)\n * ARGV[3] = current timestamp (ms)\n * ARGV[4] = unique request id\n *\n * Returns: {allowed, remaining, reset, current, retryAfter?}\n */\nexport const SLIDING_WINDOW_SCRIPT = `\nlocal key = KEYS[1]\nlocal max_points = tonumber(ARGV[1])\nlocal duration = tonumber(ARGV[2]) * 1000  -- Convert to ms\nlocal now = tonumber(ARGV[3])\nlocal request_id = ARGV[4]\n\nlocal window_start = now - duration\n\n-- Remove expired entries\nredis.call('ZREMRANGEBYSCORE', key, '-inf', window_start)\n\n-- Count current requests\nlocal current = redis.call('ZCARD', key)\n\nif current < max_points then\n  -- Add new request\n  redis.call('ZADD', key, now, request_id)\n  redis.call('PEXPIRE', key, duration)\n\n  return {1, max_points - current - 1, math.ceil((now + duration) / 1000), current + 1}\nelse\n  -- Get oldest entry to calculate retry time\n  local oldest = redis.call('ZRANGE', key, 0, 0, 'WITHSCORES')\n  local retry_after = 0\n  if #oldest > 0 then\n    retry_after = math.ceil((tonumber(oldest[2]) + duration - now) / 1000)\n  end\n\n  return {0, 0, math.ceil((now + duration) / 1000), current, retry_after}\nend\n`.trim();\n\n/**\n * Token Bucket Lua script.\n *\n * KEYS[1] = rate limit key\n * ARGV[1] = bucket capacity\n * ARGV[2] = refill rate (tokens per second)\n * ARGV[3] = current timestamp (ms)\n * ARGV[4] = tokens to consume (default: 1)\n *\n * Returns: {allowed, remaining, reset, current, retryAfter?}\n */\nexport const TOKEN_BUCKET_SCRIPT = `\nlocal key = KEYS[1]\nlocal capacity = tonumber(ARGV[1])\nlocal refill_rate = tonumber(ARGV[2])\nlocal now = tonumber(ARGV[3])\nlocal consume = tonumber(ARGV[4]) or 1\n\n-- Get current state\nlocal bucket = redis.call('HMGET', key, 'tokens', 'last_refill')\nlocal tokens = tonumber(bucket[1]) or capacity\nlocal last_refill = tonumber(bucket[2]) or now\n\n-- Calculate refill\nlocal elapsed = (now - last_refill) / 1000  -- Convert to seconds\nlocal refill = elapsed * refill_rate\ntokens = math.min(capacity, tokens + refill)\n\n-- Try to consume\nlocal allowed = tokens >= consume\nlocal new_tokens = tokens\n\nif allowed then\n  new_tokens = tokens - consume\nend\n\n-- Save state\nredis.call('HMSET', key, 'tokens', new_tokens, 'last_refill', now)\nredis.call('PEXPIRE', key, math.ceil(capacity / refill_rate * 1000) + 1000)\n\nlocal retry_after = 0\nif not allowed then\n  retry_after = math.ceil((consume - new_tokens) / refill_rate)\nend\n\n-- Calculate reset time (when bucket will be full again)\nlocal time_to_full = (capacity - new_tokens) / refill_rate\nlocal reset = math.ceil(now / 1000 + time_to_full)\n\nreturn {allowed and 1 or 0, math.floor(new_tokens), reset, math.floor(tokens), retry_after}\n`.trim();\n","import { Injectable, Inject, OnModuleInit } from '@nestjs/common';\nimport { IRedisDriver } from '@nestjs-redisx/core';\n\nimport { RATE_LIMIT_REDIS_DRIVER } from '../../../shared/constants';\nimport { RateLimitScriptError } from '../../../shared/errors';\nimport { IRateLimitResult } from '../../../shared/types';\nimport { IRateLimitStore } from '../../application/ports/rate-limit-store.port';\nimport { FIXED_WINDOW_SCRIPT, SLIDING_WINDOW_SCRIPT, TOKEN_BUCKET_SCRIPT } from '../scripts/lua-scripts';\n\n/**\n * Redis-based rate limit store implementation.\n * Uses Lua scripts for atomic operations.\n */\n@Injectable()\nexport class RedisRateLimitStoreAdapter implements IRateLimitStore, OnModuleInit {\n  private fixedWindowSha: string | null = null;\n  private slidingWindowSha: string | null = null;\n  private tokenBucketSha: string | null = null;\n\n  constructor(@Inject(RATE_LIMIT_REDIS_DRIVER) private readonly driver: IRedisDriver) {}\n\n  /**\n   * Pre-load Lua scripts on module initialization.\n   */\n  async onModuleInit(): Promise<void> {\n    try {\n      this.fixedWindowSha = await this.driver.scriptLoad(FIXED_WINDOW_SCRIPT);\n      this.slidingWindowSha = await this.driver.scriptLoad(SLIDING_WINDOW_SCRIPT);\n      this.tokenBucketSha = await this.driver.scriptLoad(TOKEN_BUCKET_SCRIPT);\n    } catch (error) {\n      throw new RateLimitScriptError(`Failed to load Lua scripts: ${(error as Error).message}`, error as Error);\n    }\n  }\n\n  /**\n   * Fixed window rate limiting.\n   */\n  async fixedWindow(key: string, points: number, duration: number): Promise<IRateLimitResult> {\n    const now = Math.floor(Date.now() / 1000);\n\n    try {\n      const result = await this.driver.evalsha(this.fixedWindowSha!, [key], [points, duration, now]);\n\n      return this.parseFixedWindowResult(result as number[], points);\n    } catch (error) {\n      // Fallback to eval if script not loaded\n      if (this.isNoScriptError(error)) {\n        const result = await this.driver.eval(FIXED_WINDOW_SCRIPT, [key], [points, duration, now]);\n        return this.parseFixedWindowResult(result as number[], points);\n      }\n\n      throw new RateLimitScriptError(`Fixed window check failed: ${(error as Error).message}`, error as Error);\n    }\n  }\n\n  /**\n   * Sliding window rate limiting.\n   */\n  async slidingWindow(key: string, points: number, duration: number): Promise<IRateLimitResult> {\n    const now = Date.now();\n    const requestId = `${now}-${Math.random().toString(36).substring(7)}`;\n\n    try {\n      const result = await this.driver.evalsha(this.slidingWindowSha!, [key], [points, duration, now, requestId]);\n\n      return this.parseSlidingWindowResult(result as number[], points);\n    } catch (error) {\n      // Fallback to eval if script not loaded\n      if (this.isNoScriptError(error)) {\n        const result = await this.driver.eval(SLIDING_WINDOW_SCRIPT, [key], [points, duration, now, requestId]);\n        return this.parseSlidingWindowResult(result as number[], points);\n      }\n\n      throw new RateLimitScriptError(`Sliding window check failed: ${(error as Error).message}`, error as Error);\n    }\n  }\n\n  /**\n   * Token bucket rate limiting.\n   */\n  async tokenBucket(key: string, capacity: number, refillRate: number, consume = 1): Promise<IRateLimitResult> {\n    const now = Date.now();\n\n    try {\n      const result = await this.driver.evalsha(this.tokenBucketSha!, [key], [capacity, refillRate, now, consume]);\n\n      return this.parseTokenBucketResult(result as number[], capacity);\n    } catch (error) {\n      // Fallback to eval if script not loaded\n      if (this.isNoScriptError(error)) {\n        const result = await this.driver.eval(TOKEN_BUCKET_SCRIPT, [key], [capacity, refillRate, now, consume]);\n        return this.parseTokenBucketResult(result as number[], capacity);\n      }\n\n      throw new RateLimitScriptError(`Token bucket check failed: ${(error as Error).message}`, error as Error);\n    }\n  }\n\n  /**\n   * Peek current state without consuming.\n   * Note: This is a simplified implementation.\n   * For accurate peek, we would need separate Lua scripts.\n   */\n  async peek(key: string, algorithm: string, config: Record<string, number>): Promise<IRateLimitResult> {\n    // Simplified: Use GET/ZCARD to check current state\n    // This is not perfect but avoids consuming\n    try {\n      if (algorithm === 'fixed-window') {\n        const now = Math.floor(Date.now() / 1000);\n        const duration = config.duration || 60;\n        const window = Math.floor(now / duration) * duration;\n        // State lives in a hash {window, count} at the bare key; a stale\n        // window field means the counter belongs to a previous window.\n        const stored = await this.driver.hmget(key, 'window', 'count');\n        const storedWindow = stored[0] !== null && stored[0] !== undefined ? parseInt(stored[0], 10) : null;\n        const current = storedWindow === window && stored[1] ? parseInt(stored[1], 10) : 0;\n        const points = config.points || 100;\n\n        return {\n          allowed: current < points,\n          limit: points,\n          remaining: Math.max(0, points - current),\n          reset: window + duration,\n          current,\n        };\n      } else if (algorithm === 'sliding-window') {\n        const count = await this.driver.zcard(key);\n        const points = config.points || 100;\n        const duration = config.duration || 60;\n\n        return {\n          allowed: count < points,\n          limit: points,\n          remaining: Math.max(0, points - count),\n          reset: Math.floor(Date.now() / 1000) + duration,\n          current: count,\n        };\n      }\n\n      // Token bucket: read the stored state via HMGET and compute the currently\n      // available tokens (applying refill) WITHOUT consuming any.\n      const capacity = config.capacity || 100;\n      const refillRate = config.refillRate || 10;\n      const now = Date.now();\n      const bucket = await this.driver.hmget(key, 'tokens', 'last_refill');\n      const storedTokens = bucket[0] !== null && bucket[0] !== undefined ? parseFloat(bucket[0]) : capacity;\n      const lastRefill = bucket[1] !== null && bucket[1] !== undefined ? parseFloat(bucket[1]) : now;\n      const elapsedSeconds = Math.max(0, (now - lastRefill) / 1000);\n      const tokens = Math.min(capacity, storedTokens + elapsedSeconds * refillRate);\n      const remaining = Math.floor(tokens);\n      const timeToFull = refillRate > 0 ? (capacity - tokens) / refillRate : 0;\n\n      return {\n        allowed: tokens >= 1,\n        limit: capacity,\n        remaining,\n        reset: Math.ceil(now / 1000 + timeToFull),\n        current: capacity - remaining,\n      };\n    } catch (error) {\n      throw new RateLimitScriptError(`Peek failed: ${(error as Error).message}`, error as Error);\n    }\n  }\n\n  /**\n   * Reset rate limit key.\n   */\n  async reset(key: string): Promise<void> {\n    try {\n      await this.driver.del(key);\n    } catch (error) {\n      throw new RateLimitScriptError(`Reset failed: ${(error as Error).message}`, error as Error);\n    }\n  }\n\n  /**\n   * Parse fixed window script result.\n   * Returns: {allowed, remaining, reset, current}\n   */\n  private parseFixedWindowResult(result: number[], limit: number): IRateLimitResult {\n    const allowed = result[0] ?? 0;\n    const remaining = result[1] ?? 0;\n    const reset = result[2] ?? 0;\n    const current = result[3] ?? 0;\n\n    return {\n      allowed: allowed === 1,\n      limit,\n      remaining,\n      reset,\n      current,\n      retryAfter: allowed === 0 ? Math.ceil(reset - Date.now() / 1000) : undefined,\n    };\n  }\n\n  /**\n   * Parse sliding window script result.\n   * Returns: {allowed, remaining, reset, current, retryAfter?}\n   */\n  private parseSlidingWindowResult(result: number[], limit: number): IRateLimitResult {\n    const allowed = result[0] ?? 0;\n    const remaining = result[1] ?? 0;\n    const reset = result[2] ?? 0;\n    const current = result[3] ?? 0;\n    const retryAfter = result[4];\n\n    return {\n      allowed: allowed === 1,\n      limit,\n      remaining,\n      reset,\n      current,\n      retryAfter: retryAfter ? Math.max(0, retryAfter) : undefined,\n    };\n  }\n\n  /**\n   * Parse token bucket script result.\n   * Returns: {allowed, remaining, reset, current, retryAfter?}\n   */\n  private parseTokenBucketResult(result: number[], capacity: number): IRateLimitResult {\n    const allowed = result[0] ?? 0;\n    const remaining = result[1] ?? 0;\n    const reset = result[2] ?? 0;\n    const current = result[3] ?? 0;\n    const retryAfter = result[4];\n\n    return {\n      allowed: allowed === 1,\n      limit: capacity,\n      remaining,\n      reset, // Unix timestamp when bucket will be full again\n      current,\n      retryAfter: retryAfter ? Math.max(0, retryAfter) : undefined,\n    };\n  }\n\n  /**\n   * Check if error is NOSCRIPT error.\n   */\n  private isNoScriptError(error: unknown): boolean {\n    const message = (error as Error).message;\n    return message.includes('NOSCRIPT') || message.includes('No matching script');\n  }\n}\n","/**\n * Rate limiting plugin for NestJS RedisX.\n * Provides multiple algorithms: fixed-window, sliding-window, token-bucket.\n */\n\nimport { DynamicModule, ForwardReference, Provider, Type } from '@nestjs/common';\nimport { APP_FILTER, Reflector } from '@nestjs/core';\nimport { IRedisXPlugin, IPluginAsyncOptions, CLIENT_MANAGER, REDIS_CLIENTS_INITIALIZATION, RedisClientManager } from '@nestjs-redisx/core';\n\nimport { version } from '../package.json';\nimport { RateLimitExceptionFilter } from './rate-limit/api/filters/rate-limit-exception.filter';\nimport { RateLimitGuard } from './rate-limit/api/guards/rate-limit.guard';\nimport { RateLimitService } from './rate-limit/application/services/rate-limit.service';\nimport { InMemoryRateLimitStoreAdapter } from './rate-limit/infrastructure/adapters/in-memory-rate-limit-store.adapter';\nimport { RedisRateLimitStoreAdapter } from './rate-limit/infrastructure/adapters/redis-rate-limit-store.adapter';\nimport { RATE_LIMIT_MEMORY_STORE, RATE_LIMIT_PLUGIN_OPTIONS, RATE_LIMIT_REDIS_DRIVER, RATE_LIMIT_SERVICE, RATE_LIMIT_STORE } from './shared/constants';\nimport { IRateLimitPluginOptions } from './shared/types';\nimport { validateMemoryOptions, validateStoreType } from './shared/utils/validate-store-options';\n\nconst DEFAULT_RATE_LIMIT_CONFIG: Required<Omit<IRateLimitPluginOptions, 'isGlobal' | 'client' | 'skip' | 'errorFactory'>> = {\n  defaultAlgorithm: 'sliding-window',\n  defaultPoints: 100,\n  defaultDuration: 60,\n  keyPrefix: 'rl:',\n  defaultKeyExtractor: 'ip',\n  includeHeaders: true,\n  headers: {\n    limit: 'X-RateLimit-Limit',\n    remaining: 'X-RateLimit-Remaining',\n    reset: 'X-RateLimit-Reset',\n    retryAfter: 'Retry-After',\n  },\n  errorPolicy: 'fail-closed',\n  trustProxy: false,\n  registerExceptionFilter: true,\n  store: 'redis',\n  memory: {\n    maxKeys: 100_000,\n    sweepIntervalMs: 30_000,\n  },\n};\n\n/**\n * Rate limiting plugin for NestJS RedisX.\n *\n * Provides rate limiting with multiple algorithms:\n * - Fixed Window\n * - Sliding Window\n * - Token Bucket\n *\n * @example\n * ```typescript\n * @Module({\n *   imports: [\n *     RedisModule.forRoot({\n *       clients: { host: 'localhost', port: 6379 },\n *       plugins: [\n *         new RateLimitPlugin({\n *           defaultAlgorithm: 'sliding-window',\n *           defaultPoints: 100,\n *           defaultDuration: 60,\n *           includeHeaders: true,\n *         }),\n *       ],\n *     }),\n *   ],\n * })\n * export class AppModule {}\n * ```\n */\nexport class RateLimitPlugin implements IRedisXPlugin {\n  readonly name = 'rate-limit';\n  readonly version: string = version;\n  readonly description = 'Rate limiting with fixed-window, sliding-window, and token-bucket algorithms';\n\n  private asyncOptions?: IPluginAsyncOptions<IRateLimitPluginOptions>;\n\n  constructor(private readonly options: IRateLimitPluginOptions = {}) {}\n\n  static registerAsync(asyncOptions: IPluginAsyncOptions<IRateLimitPluginOptions>): RateLimitPlugin {\n    const plugin = new RateLimitPlugin();\n    plugin.asyncOptions = asyncOptions;\n    return plugin;\n  }\n\n  private static mergeDefaults(options: IRateLimitPluginOptions): IRateLimitPluginOptions {\n    // Fail-fast validation of the new store surface (sync AND async paths).\n    validateStoreType(options.store, 'RateLimitPlugin options');\n    validateMemoryOptions(options.memory, 'RateLimitPlugin options');\n\n    return {\n      client: options.client,\n      defaultAlgorithm: options.defaultAlgorithm ?? DEFAULT_RATE_LIMIT_CONFIG.defaultAlgorithm,\n      defaultPoints: options.defaultPoints ?? DEFAULT_RATE_LIMIT_CONFIG.defaultPoints,\n      defaultDuration: options.defaultDuration ?? DEFAULT_RATE_LIMIT_CONFIG.defaultDuration,\n      keyPrefix: options.keyPrefix ?? DEFAULT_RATE_LIMIT_CONFIG.keyPrefix,\n      defaultKeyExtractor: options.defaultKeyExtractor ?? DEFAULT_RATE_LIMIT_CONFIG.defaultKeyExtractor,\n      includeHeaders: options.includeHeaders ?? DEFAULT_RATE_LIMIT_CONFIG.includeHeaders,\n      headers: { ...DEFAULT_RATE_LIMIT_CONFIG.headers, ...options.headers },\n      errorPolicy: options.errorPolicy ?? DEFAULT_RATE_LIMIT_CONFIG.errorPolicy,\n      trustProxy: options.trustProxy ?? DEFAULT_RATE_LIMIT_CONFIG.trustProxy,\n      registerExceptionFilter: options.registerExceptionFilter ?? DEFAULT_RATE_LIMIT_CONFIG.registerExceptionFilter,\n      store: options.store ?? DEFAULT_RATE_LIMIT_CONFIG.store,\n      memory: { ...DEFAULT_RATE_LIMIT_CONFIG.memory, ...options.memory },\n      skip: options.skip,\n      errorFactory: options.errorFactory,\n    };\n  }\n\n  getImports(): Array<Type<unknown> | DynamicModule | ForwardReference> {\n    return this.asyncOptions?.imports ?? [];\n  }\n\n  getProviders(): Provider[] {\n    const optionsProvider: Provider = this.asyncOptions\n      ? {\n          provide: RATE_LIMIT_PLUGIN_OPTIONS,\n          useFactory: async (...args: unknown[]) => {\n            const userOptions = await this.asyncOptions!.useFactory(...args);\n            return RateLimitPlugin.mergeDefaults(userOptions);\n          },\n          inject: this.asyncOptions.inject || [],\n        }\n      : {\n          provide: RATE_LIMIT_PLUGIN_OPTIONS,\n          useValue: RateLimitPlugin.mergeDefaults(this.options),\n        };\n\n    const providers: Provider[] = [\n      optionsProvider,\n      // Plugin-specific Redis driver (resolves named client)\n      {\n        provide: RATE_LIMIT_REDIS_DRIVER,\n        useFactory: async (manager: RedisClientManager, _init: void, options: IRateLimitPluginOptions) => {\n          const clientName = options.client ?? 'default';\n          try {\n            return await manager.getClient(clientName);\n          } catch {\n            throw new Error(`RateLimitPlugin: Redis client \"${clientName}\" not found. ` + `Available clients are configured in RedisModule.forRoot({ clients: { ... } }). ` + `Either add a \"${clientName}\" client or remove the \"client\" option to use the default connection.`);\n          }\n        },\n        inject: [CLIENT_MANAGER, REDIS_CLIENTS_INITIALIZATION, RATE_LIMIT_PLUGIN_OPTIONS],\n      },\n      { provide: RATE_LIMIT_STORE, useClass: RedisRateLimitStoreAdapter },\n      // Both stores are always registered; the service picks per check based\n      // on the plugin-level `store` default and per-call overrides.\n      { provide: RATE_LIMIT_MEMORY_STORE, useClass: InMemoryRateLimitStoreAdapter },\n      { provide: RATE_LIMIT_SERVICE, useClass: RateLimitService },\n      // Reflector is needed for @RateLimit decorator metadata\n      Reflector,\n      // Guard must be in providers for proper DI\n      RateLimitGuard,\n    ];\n\n    // Global exception filter (429 for exceeded, 503 for store failure).\n    // Opt-out via registerExceptionFilter: false to use your own filter and\n    // response envelope. Construction-time flag: honored for synchronous\n    // construction; registerAsync keeps the default (registered).\n    if (this.options.registerExceptionFilter !== false) {\n      providers.push({ provide: APP_FILTER, useClass: RateLimitExceptionFilter });\n    }\n\n    return providers;\n  }\n\n  getExports(): Array<string | symbol | Provider> {\n    return [RATE_LIMIT_PLUGIN_OPTIONS, RATE_LIMIT_SERVICE, RateLimitGuard];\n  }\n}\n","import { IRateLimitResult } from '../../../shared/types';\nimport { IRateLimitStore } from '../../application/ports/rate-limit-store.port';\nimport { FIXED_WINDOW_SCRIPT } from '../../infrastructure/scripts/lua-scripts';\nimport { IRateLimitStrategy, IStrategyConfig } from './rate-limit-strategy.interface';\n\n/**\n * Fixed window rate limiting strategy.\n *\n * Simple counter that resets at fixed intervals.\n * Pros: Simple, low memory\n * Cons: Burst at window boundaries\n */\nexport class FixedWindowStrategy implements IRateLimitStrategy {\n  readonly name = 'fixed-window' as const;\n\n  constructor(private readonly store?: IRateLimitStore) {}\n\n  getScript(): string {\n    return FIXED_WINDOW_SCRIPT;\n  }\n\n  async check(key: string, config: IStrategyConfig): Promise<IRateLimitResult> {\n    if (!this.store) {\n      throw new Error('FixedWindowStrategy requires an IRateLimitStore. Pass it via constructor or use RateLimitService instead.');\n    }\n\n    const points = config.points ?? 100;\n    const duration = config.duration ?? 60;\n\n    return this.store.fixedWindow(key, points, duration);\n  }\n}\n","import { IRateLimitResult } from '../../../shared/types';\nimport { IRateLimitStore } from '../../application/ports/rate-limit-store.port';\nimport { SLIDING_WINDOW_SCRIPT } from '../../infrastructure/scripts/lua-scripts';\nimport { IRateLimitStrategy, IStrategyConfig } from './rate-limit-strategy.interface';\n\n/**\n * Sliding window log rate limiting strategy.\n *\n * Tracks individual request timestamps for precise limiting.\n * Pros: Accurate, no boundary issues\n * Cons: Higher memory usage\n */\nexport class SlidingWindowStrategy implements IRateLimitStrategy {\n  readonly name = 'sliding-window' as const;\n\n  constructor(private readonly store?: IRateLimitStore) {}\n\n  getScript(): string {\n    return SLIDING_WINDOW_SCRIPT;\n  }\n\n  async check(key: string, config: IStrategyConfig): Promise<IRateLimitResult> {\n    if (!this.store) {\n      throw new Error('SlidingWindowStrategy requires an IRateLimitStore. Pass it via constructor or use RateLimitService instead.');\n    }\n\n    const points = config.points ?? 100;\n    const duration = config.duration ?? 60;\n\n    return this.store.slidingWindow(key, points, duration);\n  }\n}\n","import { IRateLimitResult } from '../../../shared/types';\nimport { IRateLimitStore } from '../../application/ports/rate-limit-store.port';\nimport { TOKEN_BUCKET_SCRIPT } from '../../infrastructure/scripts/lua-scripts';\nimport { IRateLimitStrategy, IStrategyConfig } from './rate-limit-strategy.interface';\n\n/**\n * Token bucket rate limiting strategy.\n *\n * Smooth rate limiting with burst allowance.\n * Pros: Smooth limiting, configurable burst\n * Cons: More complex\n */\nexport class TokenBucketStrategy implements IRateLimitStrategy {\n  readonly name = 'token-bucket' as const;\n\n  constructor(private readonly store?: IRateLimitStore) {}\n\n  getScript(): string {\n    return TOKEN_BUCKET_SCRIPT;\n  }\n\n  async check(key: string, config: IStrategyConfig): Promise<IRateLimitResult> {\n    if (!this.store) {\n      throw new Error('TokenBucketStrategy requires an IRateLimitStore. Pass it via constructor or use RateLimitService instead.');\n    }\n\n    const capacity = config.capacity ?? config.points ?? 100;\n    const refillRate = config.refillRate ?? (config.duration ? capacity / config.duration : 10);\n\n    return this.store.tokenBucket(key, capacity, refillRate, 1);\n  }\n}\n"]}