@nestjs-redisx/rate-limit 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -13,6 +13,9 @@ var __decorateClass = (decorators, target, key, kind) => {
13
13
  return result;
14
14
  };
15
15
  var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
16
+
17
+ // package.json
18
+ var version = "1.0.3";
16
19
  var RateLimitError = class extends core$1.RedisXError {
17
20
  constructor(message, code, result, cause) {
18
21
  super(message, code, cause, { result });
@@ -685,7 +688,7 @@ var RateLimitPlugin = class _RateLimitPlugin {
685
688
  this.options = options;
686
689
  }
687
690
  name = "rate-limit";
688
- version = "0.1.0";
691
+ version = version;
689
692
  description = "Rate limiting with fixed-window, sliding-window, and token-bucket algorithms";
690
693
  asyncOptions;
691
694
  static registerAsync(asyncOptions) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../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/rate-limit/application/services/rate-limit.service.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":["RedisXError","ErrorCode","RateLimitExceptionFilter","HttpStatus","Catch","applyDecorators","SetMetadata","UseGuards","RateLimitGuard","Injectable","Reflector","Optional","RateLimitService","Inject","points","REDIS_DRIVER","APP_FILTER"],"mappings":"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","file":"index.js","sourcesContent":["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","import { Catch, ExceptionFilter, ArgumentsHost, HttpStatus } from '@nestjs/common';\n\nimport { RateLimitExceededError } from '../../../shared/errors';\n\n/**\n * Exception filter for rate limit errors.\n * Catches RateLimitExceededError and returns 429 Too Many Requests.\n */\n@Catch(RateLimitExceededError)\nexport class RateLimitExceptionFilter implements ExceptionFilter {\n /**\n * Catch rate limit exceeded error and format response.\n */\n catch(exception: RateLimitExceededError, host: ArgumentsHost): void {\n const ctx = host.switchToHttp();\n const response = ctx.getResponse();\n\n const result = exception.result;\n\n response.status(HttpStatus.TOO_MANY_REQUESTS).header('Retry-After', exception.retryAfter.toString()).json({\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}\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 rate limit store.\n */\nexport const RATE_LIMIT_STORE = Symbol.for('RATE_LIMIT_STORE');\n","import { SetMetadata, UseGuards, applyDecorators, ExecutionContext } from '@nestjs/common';\n\nimport { IRateLimitResult } 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 * 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 { 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 * 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 @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 const options = this.getOptions(context);\n\n // Check skip condition\n if (await this.shouldSkip(context, options)) {\n return true;\n }\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 // Check X-Forwarded-For header\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 return ips[0] || 'unknown';\n }\n\n // Check X-Real-IP header\n const realIp = (request.headers as unknown as Record<string, string>)['x-real-ip'];\n if (realIp) {\n return realIp;\n }\n\n // Fall back to request.ip\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 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 response.header(limitHeader, result.limit.toString());\n response.header(remainingHeader, result.remaining.toString());\n response.header(resetHeader, result.reset.toString());\n\n if (!result.allowed && result.retryAfter) {\n response.header(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 { Injectable, Inject } from '@nestjs/common';\n\nimport { RATE_LIMIT_PLUGIN_OPTIONS, RATE_LIMIT_STORE } from '../../../shared/constants';\nimport { RateLimitScriptError } from '../../../shared/errors';\nimport { IRateLimitPluginOptions, IRateLimitConfig, IRateLimitResult, IRateLimitState } from '../../../shared/types';\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.\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 ) {}\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\n try {\n switch (algorithm) {\n case 'fixed-window':\n return await this.checkFixedWindow(fullKey, config);\n case 'sliding-window':\n return await this.checkSlidingWindow(fullKey, config);\n case 'token-bucket':\n return await this.checkTokenBucket(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\n try {\n const storeConfig = this.buildStoreConfig(algorithm, config);\n return await this.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 */\n async reset(key: string): Promise<void> {\n const algorithms = ['fixed-window', 'sliding-window', 'token-bucket'] as const;\n await Promise.all(algorithms.map((algo) => this.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 * Check fixed window rate limit.\n */\n private async checkFixedWindow(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 this.store.fixedWindow(key, points, duration);\n }\n\n /**\n * Check sliding window rate limit.\n */\n private async checkSlidingWindow(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 this.store.slidingWindow(key, points, duration);\n }\n\n /**\n * Check token bucket rate limit.\n */\n private async checkTokenBucket(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 this.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","/**\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 * 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\nlocal window_key = '{' .. key .. '}:' .. window\n\nlocal current = redis.call('INCR', window_key)\n\nif current == 1 then\n redis.call('EXPIRE', window_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, REDIS_DRIVER } from '@nestjs-redisx/core';\n\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(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 const windowKey = `${key}:${window}`;\n const currentStr = await this.driver.get(windowKey);\n const current = currentStr ? parseInt(currentStr, 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 peek would require HMGET\n const points = config.capacity || 100;\n return {\n allowed: true,\n limit: points,\n remaining: points,\n reset: Math.floor(Date.now() / 1000) + 60,\n current: 0,\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 } from '@nestjs-redisx/core';\n\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 { RedisRateLimitStoreAdapter } from './rate-limit/infrastructure/adapters/redis-rate-limit-store.adapter';\nimport { RATE_LIMIT_PLUGIN_OPTIONS, RATE_LIMIT_SERVICE, RATE_LIMIT_STORE } from './shared/constants';\nimport { IRateLimitPluginOptions } from './shared/types';\n\nconst DEFAULT_RATE_LIMIT_CONFIG: Required<Omit<IRateLimitPluginOptions, 'isGlobal' | '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};\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 = '0.1.0';\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 return {\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 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 return [\n optionsProvider,\n { provide: RATE_LIMIT_STORE, useClass: RedisRateLimitStoreAdapter },\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 // Global exception filter to return 429 instead of 500\n { provide: APP_FILTER, useClass: RateLimitExceptionFilter },\n ];\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"]}
1
+ {"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/rate-limit/application/services/rate-limit.service.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":["RedisXError","ErrorCode","RateLimitExceptionFilter","HttpStatus","Catch","applyDecorators","SetMetadata","UseGuards","RateLimitGuard","Injectable","Reflector","Optional","RateLimitService","Inject","points","REDIS_DRIVER","APP_FILTER"],"mappings":";;;;;;;;;;;;;;;;;AAEE,IAAA,OAAA,GAAW,OAAA;ACKN,IAAM,cAAA,GAAN,cAA6BA,kBAAA,CAAY;AAAA,EAC9C,WAAA,CACE,OAAA,EACA,IAAA,EACgB,MAAA,EAChB,KAAA,EACA;AACA,IAAA,KAAA,CAAM,OAAA,EAAS,IAAA,EAAM,KAAA,EAAO,EAAE,QAAQ,CAAA;AAHtB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EAIlB;AACF;AAKO,IAAM,sBAAA,GAAN,cAAqC,cAAA,CAAe;AAAA,EACzD,WAAA,CAAY,SAAiB,MAAA,EAA0B;AACrD,IAAA,KAAA,CAAM,OAAA,EAASC,gBAAA,CAAU,mBAAA,EAAqB,MAAM,CAAA;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,UAAA,GAAqB;AACvB,IAAA,OAAO,IAAA,CAAK,QAAQ,UAAA,IAAc,CAAA;AAAA,EACpC;AACF;AAKO,IAAM,oBAAA,GAAN,cAAmC,cAAA,CAAe;AAAA,EACvD,WAAA,CAAY,SAAiB,KAAA,EAAe;AAC1C,IAAA,KAAA,CAAM,OAAA,EAASA,gBAAA,CAAU,uBAAA,EAAyB,MAAA,EAAW,KAAK,CAAA;AAAA,EACpE;AACF;;;AChCaC,mCAAN,8BAAA,CAA0D;AAAA;AAAA;AAAA;AAAA,EAI/D,KAAA,CAAM,WAAmC,IAAA,EAA2B;AAClE,IAAA,MAAM,GAAA,GAAM,KAAK,YAAA,EAAa;AAC9B,IAAA,MAAM,QAAA,GAAW,IAAI,WAAA,EAAY;AAEjC,IAAA,MAAM,SAAS,SAAA,CAAU,MAAA;AAEzB,IAAA,QAAA,CAAS,MAAA,CAAOC,iBAAA,CAAW,iBAAiB,CAAA,CAAE,MAAA,CAAO,aAAA,EAAe,SAAA,CAAU,UAAA,CAAW,QAAA,EAAU,CAAA,CAAE,IAAA,CAAK;AAAA,MACxG,YAAYA,iBAAA,CAAW,iBAAA;AAAA,MACvB,SAAS,SAAA,CAAU,OAAA;AAAA,MACnB,KAAA,EAAO,mBAAA;AAAA,MACP,YAAY,SAAA,CAAU,UAAA;AAAA,MACtB,OAAO,MAAA,EAAQ,KAAA;AAAA,MACf,WAAW,MAAA,EAAQ,SAAA;AAAA,MACnB,OAAO,MAAA,EAAQ;AAAA,KAChB,CAAA;AAAA,EACH;AACF;AApBaD,gCAAA,GAAN,eAAA,CAAA;AAAA,EADNE,aAAM,sBAAsB;AAAA,CAAA,EAChBF,gCAAA,CAAA;;;ACFN,IAAM,yBAAA,mBAA4B,MAAA,CAAO,GAAA,CAAI,2BAA2B;AAKxE,IAAM,kBAAA,mBAAqB,MAAA,CAAO,GAAA,CAAI,oBAAoB;AAK1D,IAAM,gBAAA,mBAAmB,MAAA,CAAO,GAAA,CAAI,kBAAkB;ACTtD,IAAM,kBAAA,mBAAqB,MAAA,CAAO,GAAA,CAAI,oBAAoB;AA8H1D,SAAS,SAAA,CAAU,OAAA,GAA6B,EAAC,EAAqC;AAC3F,EAAA,OAAOG,uBAAgBC,kBAAA,CAAY,kBAAA,EAAoB,OAAO,CAAA,EAAGC,gBAAA,CAAUC,sBAAc,CAAC,CAAA;AAC5F;;;AC9HA,IAAM,eAAA,mBAAkB,MAAA,CAAO,GAAA,CAAI,iBAAiB,CAAA;AAOpD,IAAM,eAAA,mBAAkB,MAAA,CAAO,GAAA,CAAI,iBAAiB,CAAA;AAmBvCA,yBAAN,oBAAA,CAA4C;AAAA,EACjD,WAAA,CAEmB,gBAAA,EAEA,MAAA,EACmB,SAAA,EACkB,SACA,OAAA,EACtD;AANiB,IAAA,IAAA,CAAA,gBAAA,GAAA,gBAAA;AAEA,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AACmB,IAAA,IAAA,CAAA,SAAA,GAAA,SAAA;AACkB,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AACA,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAAA,EACrD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMH,MAAM,YAAY,OAAA,EAA6C;AAC7D,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,UAAA,CAAW,OAAO,CAAA;AAGvC,IAAA,IAAI,MAAM,IAAA,CAAK,UAAA,CAAW,OAAA,EAAS,OAAO,CAAA,EAAG;AAC3C,MAAA,OAAO,IAAA;AAAA,IACT;AAEA,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,UAAA,CAAW,SAAS,OAAO,CAAA;AAClD,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,OAAA,EAAS,SAAA,CAAU,iBAAA,EAAmB;AAAA,MACtD,IAAA,EAAM,UAAA;AAAA,MACN,UAAA,EAAY,EAAE,eAAA,EAAiB,GAAA;AAAI,KACpC,CAAA;AAED,IAAA,IAAI;AACF,MAAA,MAAM,SAAS,MAAM,IAAA,CAAK,gBAAA,CAAiB,KAAA,CAAM,KAAK,OAAO,CAAA;AAG7D,MAAA,IAAA,CAAK,UAAA,CAAW,SAAS,MAAM,CAAA;AAE/B,MAAA,IAAA,EAAM,YAAA,CAAa,mBAAA,EAAqB,MAAA,CAAO,OAAO,CAAA;AACtD,MAAA,IAAA,EAAM,YAAA,CAAa,qBAAA,EAAuB,MAAA,CAAO,SAAS,CAAA;AAC1D,MAAA,IAAA,EAAM,YAAA,CAAa,iBAAA,EAAmB,MAAA,CAAO,KAAK,CAAA;AAElD,MAAA,IAAI,CAAC,OAAO,OAAA,EAAS;AACnB,QAAA,IAAA,CAAK,SAAS,gBAAA,CAAiB,iCAAA,EAAmC,EAAE,MAAA,EAAQ,YAAY,CAAA;AACxF,QAAA,IAAA,EAAM,UAAU,IAAI,CAAA;AACpB,QAAA,MAAM,IAAA,CAAK,WAAA,CAAY,MAAA,EAAQ,OAAO,CAAA;AAAA,MACxC;AAEA,MAAA,IAAA,CAAK,SAAS,gBAAA,CAAiB,iCAAA,EAAmC,EAAE,MAAA,EAAQ,WAAW,CAAA;AACvF,MAAA,IAAA,EAAM,UAAU,IAAI,CAAA;AACpB,MAAA,OAAO,IAAA;AAAA,IACT,SAAS,KAAA,EAAO;AACd,MAAA,IAAI,EAAE,iBAAiB,sBAAA,CAAA,EAAyB;AAC9C,QAAA,IAAA,EAAM,gBAAgB,KAAc,CAAA;AACpC,QAAA,IAAA,EAAM,UAAU,OAAO,CAAA;AAAA,MACzB;AACA,MAAA,MAAM,KAAA;AAAA,IACR,CAAA,SAAE;AACA,MAAA,IAAA,EAAM,GAAA,EAAI;AAAA,IACZ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,WAAW,OAAA,EAA8C;AAC/D,IAAA,MAAM,iBAAiB,IAAA,CAAK,SAAA,CAAU,IAAuB,kBAAA,EAAoB,OAAA,CAAQ,YAAY,CAAA;AACrG,IAAA,MAAM,eAAe,IAAA,CAAK,SAAA,CAAU,IAAuB,kBAAA,EAAoB,OAAA,CAAQ,UAAU,CAAA;AAEjG,IAAA,OAAO,EAAE,GAAG,YAAA,EAAc,GAAG,cAAA,EAAe;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,UAAA,CAAW,OAAA,EAA2B,OAAA,EAA6C;AAC/F,IAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,GAAA,IAAO,IAAA,CAAK,OAAO,mBAAA,IAAuB,IAAA;AAGpE,IAAA,IAAI,OAAO,cAAc,UAAA,EAAY;AACnC,MAAA,OAAO,MAAM,UAAU,OAAO,CAAA;AAAA,IAChC;AAGA,IAAA,IAAI,OAAO,SAAA,KAAc,QAAA,IAAY,CAAC,CAAC,IAAA,EAAM,MAAA,EAAQ,QAAQ,CAAA,CAAE,QAAA,CAAS,SAAS,CAAA,EAAG;AAClF,MAAA,OAAO,SAAA;AAAA,IACT;AAGA,IAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,YAAA,EAAa,CAAE,UAAA,EAAW;AAElD,IAAA,QAAQ,SAAA;AAAW,MACjB,KAAK,IAAA;AACH,QAAA,OAAO,IAAA,CAAK,YAAY,OAAO,CAAA;AAAA,MACjC,KAAK,MAAA;AACH,QAAA,OAAO,IAAA,CAAK,UAAU,OAAO,CAAA;AAAA,MAC/B,KAAK,QAAA;AACH,QAAA,OAAO,IAAA,CAAK,UAAU,OAAO,CAAA;AAAA,MAC/B;AACE,QAAA,OAAO,IAAA,CAAK,YAAY,OAAO,CAAA;AAAA;AACnC,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,YAAY,OAAA,EAA4D;AAE9E,IAAA,MAAM,YAAA,GAAgB,OAAA,CAAQ,OAAA,CAA8C,iBAAiB,CAAA;AAC7F,IAAA,IAAI,YAAA,EAAc;AAChB,MAAA,MAAM,GAAA,GAAM,YAAA,CAAa,KAAA,CAAM,GAAG,CAAA,CAAE,IAAI,CAAC,EAAA,KAAO,EAAA,CAAG,IAAA,EAAM,CAAA;AACzD,MAAA,OAAO,GAAA,CAAI,CAAC,CAAA,IAAK,SAAA;AAAA,IACnB;AAGA,IAAA,MAAM,MAAA,GAAU,OAAA,CAAQ,OAAA,CAA8C,WAAW,CAAA;AACjF,IAAA,IAAI,MAAA,EAAQ;AACV,MAAA,OAAO,MAAA;AAAA,IACT;AAGA,IAAA,OAAO,QAAQ,EAAA,IAAM,SAAA;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKQ,UAAU,OAAA,EAAuD;AACvE,IAAA,MAAM,MAAA,GAAS,QAAQ,IAAA,EAAM,EAAA;AAC7B,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,MAAM,IAAI,MAAM,8EAA8E,CAAA;AAAA,IAChG;AACA,IAAA,OAAO,QAAQ,MAAM,CAAA,CAAA;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKQ,UAAU,OAAA,EAA0B;AAC1C,IAAA,MAAM,SAAU,OAAA,CAAQ,OAAA,CAA8C,WAAW,CAAA,IAAM,OAAA,CAAQ,QAA8C,eAAe,CAAA;AAE5J,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,MAAM,IAAI,MAAM,+EAA+E,CAAA;AAAA,IACjG;AAEA,IAAA,OAAO,UAAU,MAAM,CAAA,CAAA;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKQ,UAAA,CAAW,SAA2B,MAAA,EAAgC;AAC5E,IAAA,IAAI,IAAA,CAAK,MAAA,CAAO,cAAA,KAAmB,KAAA,EAAO;AACxC,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,YAAA,EAAa,CAAE,WAAA,EAAY;AACpD,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,MAAA,CAAO,OAAA,IAAW,EAAC;AAExC,IAAA,MAAM,WAAA,GAAc,QAAQ,KAAA,IAAS,mBAAA;AACrC,IAAA,MAAM,eAAA,GAAkB,QAAQ,SAAA,IAAa,uBAAA;AAC7C,IAAA,MAAM,WAAA,GAAc,QAAQ,KAAA,IAAS,mBAAA;AACrC,IAAA,MAAM,gBAAA,GAAmB,QAAQ,UAAA,IAAc,aAAA;AAE/C,IAAA,QAAA,CAAS,MAAA,CAAO,WAAA,EAAa,MAAA,CAAO,KAAA,CAAM,UAAU,CAAA;AACpD,IAAA,QAAA,CAAS,MAAA,CAAO,eAAA,EAAiB,MAAA,CAAO,SAAA,CAAU,UAAU,CAAA;AAC5D,IAAA,QAAA,CAAS,MAAA,CAAO,WAAA,EAAa,MAAA,CAAO,KAAA,CAAM,UAAU,CAAA;AAEpD,IAAA,IAAI,CAAC,MAAA,CAAO,OAAA,IAAW,MAAA,CAAO,UAAA,EAAY;AACxC,MAAA,QAAA,CAAS,MAAA,CAAO,gBAAA,EAAkB,MAAA,CAAO,UAAA,CAAW,UAAU,CAAA;AAAA,IAChE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,WAAA,CAAY,QAA0B,OAAA,EAAmC;AAE/E,IAAA,IAAI,QAAQ,YAAA,EAAc;AACxB,MAAA,OAAO,OAAA,CAAQ,aAAa,MAAM,CAAA;AAAA,IACpC;AAGA,IAAA,IAAI,IAAA,CAAK,OAAO,YAAA,EAAc;AAC5B,MAAA,OAAO,IAAA,CAAK,MAAA,CAAO,YAAA,CAAa,MAAM,CAAA;AAAA,IACxC;AAGA,IAAA,MAAM,UAAU,OAAA,CAAQ,OAAA,IAAW,CAAA,kCAAA,EAAqC,MAAA,CAAO,cAAc,CAAC,CAAA,SAAA,CAAA;AAE9F,IAAA,OAAO,IAAI,sBAAA,CAAuB,OAAA,EAAS,MAAM,CAAA;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,UAAA,CAAW,OAAA,EAA2B,OAAA,EAA8C;AAEhG,IAAA,IAAI,QAAQ,IAAA,EAAM;AAChB,MAAA,OAAO,MAAM,OAAA,CAAQ,IAAA,CAAK,OAAO,CAAA;AAAA,IACnC;AAGA,IAAA,IAAI,IAAA,CAAK,OAAO,IAAA,EAAM;AACpB,MAAA,OAAO,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,OAAO,CAAA;AAAA,IACvC;AAEA,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AA/MaA,sBAAA,GAAN,eAAA,CAAA;AAAA,EADNC,iBAAA,EAAW;AAAA,EAGP,iCAAO,kBAAkB,CAAA,CAAA;AAAA,EAEzB,iCAAO,yBAAyB,CAAA,CAAA;AAAA,EAEhC,iCAAOC,cAAS,CAAA,CAAA;AAAA,EAChB,eAAA,CAAA,CAAA,EAAAC,eAAA,EAAS,CAAA;AAAA,EAAG,iCAAO,eAAe,CAAA,CAAA;AAAA,EAClC,eAAA,CAAA,CAAA,EAAAA,eAAA,EAAS,CAAA;AAAA,EAAG,iCAAO,eAAe,CAAA;AAAA,CAAA,EAR1BH,sBAAA,CAAA;ACvBAI,2BAAN,sBAAA,CAAoD;AAAA,EACzD,WAAA,CAEmB,QAEA,KAAA,EACjB;AAHiB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAEA,IAAA,IAAA,CAAA,KAAA,GAAA,KAAA;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA,EAKH,MAAM,KAAA,CAAM,GAAA,EAAa,MAAA,GAA2B,EAAC,EAA8B;AACjF,IAAA,MAAM,SAAA,GAAY,MAAA,CAAO,SAAA,IAAa,IAAA,CAAK,OAAO,gBAAA,IAAoB,gBAAA;AAGtE,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,QAAA,CAAS,GAAA,EAAK,SAAS,CAAA;AAE5C,IAAA,IAAI;AACF,MAAA,QAAQ,SAAA;AAAW,QACjB,KAAK,cAAA;AACH,UAAA,OAAO,MAAM,IAAA,CAAK,gBAAA,CAAiB,OAAA,EAAS,MAAM,CAAA;AAAA,QACpD,KAAK,gBAAA;AACH,UAAA,OAAO,MAAM,IAAA,CAAK,kBAAA,CAAmB,OAAA,EAAS,MAAM,CAAA;AAAA,QACtD,KAAK,cAAA;AACH,UAAA,OAAO,MAAM,IAAA,CAAK,gBAAA,CAAiB,OAAA,EAAS,MAAM,CAAA;AAAA,QACpD;AACE,UAAA,MAAM,IAAI,KAAA,CAAM,CAAA,mBAAA,EAAsB,SAAS,CAAA,CAAE,CAAA;AAAA;AACrD,IACF,SAAS,KAAA,EAAO;AACd,MAAA,OAAO,IAAA,CAAK,WAAA,CAAY,KAAA,EAAgB,MAAM,CAAA;AAAA,IAChD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,IAAA,CAAK,GAAA,EAAa,MAAA,GAA2B,EAAC,EAA8B;AAChF,IAAA,MAAM,SAAA,GAAY,MAAA,CAAO,SAAA,IAAa,IAAA,CAAK,OAAO,gBAAA,IAAoB,gBAAA;AACtE,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,QAAA,CAAS,GAAA,EAAK,SAAS,CAAA;AAE5C,IAAA,IAAI;AACF,MAAA,MAAM,WAAA,GAAc,IAAA,CAAK,gBAAA,CAAiB,SAAA,EAAW,MAAM,CAAA;AAC3D,MAAA,OAAO,MAAM,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,OAAA,EAAS,WAAW,WAAW,CAAA;AAAA,IAC9D,SAAS,KAAA,EAAO;AACd,MAAA,OAAO,IAAA,CAAK,WAAA,CAAY,KAAA,EAAgB,MAAM,CAAA;AAAA,IAChD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,MAAM,GAAA,EAA4B;AACtC,IAAA,MAAM,UAAA,GAAa,CAAC,cAAA,EAAgB,gBAAA,EAAkB,cAAc,CAAA;AACpE,IAAA,MAAM,OAAA,CAAQ,GAAA,CAAI,UAAA,CAAW,GAAA,CAAI,CAAC,IAAA,KAAS,IAAA,CAAK,KAAA,CAAM,KAAA,CAAM,KAAK,QAAA,CAAS,GAAA,EAAK,IAAI,CAAC,CAAC,CAAC,CAAA;AAAA,EACxF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAA,CAAS,GAAA,EAAa,MAAA,GAA2B,EAAC,EAA6B;AACnF,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,KAAK,MAAM,CAAA;AAE1C,IAAA,OAAO;AAAA,MACL,SAAS,MAAA,CAAO,OAAA;AAAA,MAChB,OAAO,MAAA,CAAO,KAAA;AAAA,MACd,WAAW,MAAA,CAAO,SAAA;AAAA,MAClB,OAAA,EAAS,IAAI,IAAA,CAAK,MAAA,CAAO,QAAQ,GAAI;AAAA,KACvC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,gBAAA,CAAiB,GAAA,EAAa,MAAA,EAAqD;AAC/F,IAAA,MAAM,MAAA,GAAS,MAAA,CAAO,MAAA,IAAU,IAAA,CAAK,OAAO,aAAA,IAAiB,GAAA;AAC7D,IAAA,MAAM,QAAA,GAAW,MAAA,CAAO,QAAA,IAAY,IAAA,CAAK,OAAO,eAAA,IAAmB,EAAA;AAEnE,IAAA,OAAO,MAAM,IAAA,CAAK,KAAA,CAAM,WAAA,CAAY,GAAA,EAAK,QAAQ,QAAQ,CAAA;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,kBAAA,CAAmB,GAAA,EAAa,MAAA,EAAqD;AACjG,IAAA,MAAM,MAAA,GAAS,MAAA,CAAO,MAAA,IAAU,IAAA,CAAK,OAAO,aAAA,IAAiB,GAAA;AAC7D,IAAA,MAAM,QAAA,GAAW,MAAA,CAAO,QAAA,IAAY,IAAA,CAAK,OAAO,eAAA,IAAmB,EAAA;AAEnE,IAAA,OAAO,MAAM,IAAA,CAAK,KAAA,CAAM,aAAA,CAAc,GAAA,EAAK,QAAQ,QAAQ,CAAA;AAAA,EAC7D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,gBAAA,CAAiB,GAAA,EAAa,MAAA,EAAqD;AAC/F,IAAA,MAAM,WAAW,MAAA,CAAO,QAAA,IAAY,OAAO,MAAA,IAAU,IAAA,CAAK,OAAO,aAAA,IAAiB,GAAA;AAClF,IAAA,MAAM,aAAa,MAAA,CAAO,UAAA,KAAe,OAAO,QAAA,GAAW,QAAA,GAAW,OAAO,QAAA,GAAW,EAAA,CAAA;AAExF,IAAA,OAAO,MAAM,IAAA,CAAK,KAAA,CAAM,YAAY,GAAA,EAAK,QAAA,EAAU,YAAY,CAAC,CAAA;AAAA,EAClE;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAA,CAAiB,WAAmB,MAAA,EAAkD;AAC5F,IAAA,MAAM,UAAA,GAAa;AAAA,MACjB,MAAA,EAAQ,MAAA,CAAO,MAAA,IAAU,IAAA,CAAK,OAAO,aAAA,IAAiB,GAAA;AAAA,MACtD,QAAA,EAAU,MAAA,CAAO,QAAA,IAAY,IAAA,CAAK,OAAO,eAAA,IAAmB;AAAA,KAC9D;AAEA,IAAA,IAAI,cAAc,cAAA,EAAgB;AAChC,MAAA,MAAM,QAAA,GAAW,MAAA,CAAO,QAAA,IAAY,UAAA,CAAW,MAAA;AAC/C,MAAA,MAAM,UAAA,GAAa,MAAA,CAAO,UAAA,IAAc,QAAA,GAAW,UAAA,CAAW,QAAA;AAC9D,MAAA,OAAO,EAAE,UAAU,UAAA,EAAW;AAAA,IAChC;AAEA,IAAA,OAAO,UAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,WAAA,CAAY,OAAc,MAAA,EAA4C;AAC5E,IAAA,MAAM,WAAA,GAAc,IAAA,CAAK,MAAA,CAAO,WAAA,IAAe,aAAA;AAE/C,IAAA,IAAI,gBAAgB,WAAA,EAAa;AAE/B,MAAA,MAAM,MAAA,GAAS,MAAA,CAAO,MAAA,IAAU,IAAA,CAAK,OAAO,aAAA,IAAiB,GAAA;AAC7D,MAAA,MAAM,QAAA,GAAW,MAAA,CAAO,QAAA,IAAY,IAAA,CAAK,OAAO,eAAA,IAAmB,EAAA;AAEnE,MAAA,OAAO;AAAA,QACL,OAAA,EAAS,IAAA;AAAA,QACT,KAAA,EAAO,MAAA;AAAA,QACP,SAAA,EAAW,MAAA;AAAA,QACX,OAAO,IAAA,CAAK,KAAA,CAAM,KAAK,GAAA,EAAI,GAAI,GAAI,CAAA,GAAI,QAAA;AAAA,QACvC,OAAA,EAAS;AAAA,OACX;AAAA,IACF;AAGA,IAAA,MAAM,IAAI,oBAAA,CAAqB,CAAA,yBAAA,EAA4B,KAAA,CAAM,OAAO,IAAI,KAAK,CAAA;AAAA,EACnF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,QAAA,CAAS,KAAa,SAAA,EAA4B;AACxD,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,MAAA,CAAO,SAAA,IAAa,KAAA;AACxC,IAAA,MAAM,UAAA,GAAa,SAAA,GAAY,CAAA,EAAG,SAAS,CAAA,CAAA,CAAA,GAAM,EAAA;AACjD,IAAA,OAAO,CAAA,EAAG,MAAM,CAAA,EAAG,UAAU,GAAG,GAAG,CAAA,CAAA;AAAA,EACrC;AACF;AAzJaA,wBAAA,GAAN,eAAA,CAAA;AAAA,EADNH,iBAAAA,EAAW;AAAA,EAGP,eAAA,CAAA,CAAA,EAAAI,cAAO,yBAAyB,CAAA,CAAA;AAAA,EAEhC,eAAA,CAAA,CAAA,EAAAA,cAAO,gBAAgB,CAAA;AAAA,CAAA,EAJfD,wBAAA,CAAA;;;ACIN,IAAM,mBAAA,GAAsB;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA,CAAA,CAoBjC,IAAA,EAAK;AAaA,IAAM,qBAAA,GAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA,CAAA,CA+BnC,IAAA,EAAK;AAaA,IAAM,mBAAA,GAAsB;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA,CAAA,CAuCjC,IAAA,EAAK;;;ACxHA,IAAM,6BAAN,MAA0E;AAAA,EAK/E,YAAmD,MAAA,EAAsB;AAAtB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EAAuB;AAAA,EAJlE,cAAA,GAAgC,IAAA;AAAA,EAChC,gBAAA,GAAkC,IAAA;AAAA,EAClC,cAAA,GAAgC,IAAA;AAAA;AAAA;AAAA;AAAA,EAOxC,MAAM,YAAA,GAA8B;AAClC,IAAA,IAAI;AACF,MAAA,IAAA,CAAK,cAAA,GAAiB,MAAM,IAAA,CAAK,MAAA,CAAO,WAAW,mBAAmB,CAAA;AACtE,MAAA,IAAA,CAAK,gBAAA,GAAmB,MAAM,IAAA,CAAK,MAAA,CAAO,WAAW,qBAAqB,CAAA;AAC1E,MAAA,IAAA,CAAK,cAAA,GAAiB,MAAM,IAAA,CAAK,MAAA,CAAO,WAAW,mBAAmB,CAAA;AAAA,IACxE,SAAS,KAAA,EAAO;AACd,MAAA,MAAM,IAAI,oBAAA,CAAqB,CAAA,4BAAA,EAAgC,KAAA,CAAgB,OAAO,IAAI,KAAc,CAAA;AAAA,IAC1G;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WAAA,CAAY,GAAA,EAAa,MAAA,EAAgB,QAAA,EAA6C;AAC1F,IAAA,MAAM,MAAM,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,GAAA,KAAQ,GAAI,CAAA;AAExC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,MAAA,CAAO,QAAQ,IAAA,CAAK,cAAA,EAAiB,CAAC,GAAG,CAAA,EAAG,CAAC,MAAA,EAAQ,QAAA,EAAU,GAAG,CAAC,CAAA;AAE7F,MAAA,OAAO,IAAA,CAAK,sBAAA,CAAuB,MAAA,EAAoB,MAAM,CAAA;AAAA,IAC/D,SAAS,KAAA,EAAO;AAEd,MAAA,IAAI,IAAA,CAAK,eAAA,CAAgB,KAAK,CAAA,EAAG;AAC/B,QAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,mBAAA,EAAqB,CAAC,GAAG,CAAA,EAAG,CAAC,MAAA,EAAQ,QAAA,EAAU,GAAG,CAAC,CAAA;AACzF,QAAA,OAAO,IAAA,CAAK,sBAAA,CAAuB,MAAA,EAAoB,MAAM,CAAA;AAAA,MAC/D;AAEA,MAAA,MAAM,IAAI,oBAAA,CAAqB,CAAA,2BAAA,EAA+B,KAAA,CAAgB,OAAO,IAAI,KAAc,CAAA;AAAA,IACzG;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAA,CAAc,GAAA,EAAa,MAAA,EAAgB,QAAA,EAA6C;AAC5F,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,IAAA,MAAM,SAAA,GAAY,CAAA,EAAG,GAAG,CAAA,CAAA,EAAI,IAAA,CAAK,MAAA,EAAO,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,SAAA,CAAU,CAAC,CAAC,CAAA,CAAA;AAEnE,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,MAAA,CAAO,QAAQ,IAAA,CAAK,gBAAA,EAAmB,CAAC,GAAG,GAAG,CAAC,MAAA,EAAQ,QAAA,EAAU,GAAA,EAAK,SAAS,CAAC,CAAA;AAE1G,MAAA,OAAO,IAAA,CAAK,wBAAA,CAAyB,MAAA,EAAoB,MAAM,CAAA;AAAA,IACjE,SAAS,KAAA,EAAO;AAEd,MAAA,IAAI,IAAA,CAAK,eAAA,CAAgB,KAAK,CAAA,EAAG;AAC/B,QAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,MAAA,CAAO,KAAK,qBAAA,EAAuB,CAAC,GAAG,CAAA,EAAG,CAAC,MAAA,EAAQ,QAAA,EAAU,GAAA,EAAK,SAAS,CAAC,CAAA;AACtG,QAAA,OAAO,IAAA,CAAK,wBAAA,CAAyB,MAAA,EAAoB,MAAM,CAAA;AAAA,MACjE;AAEA,MAAA,MAAM,IAAI,oBAAA,CAAqB,CAAA,6BAAA,EAAiC,KAAA,CAAgB,OAAO,IAAI,KAAc,CAAA;AAAA,IAC3G;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WAAA,CAAY,GAAA,EAAa,QAAA,EAAkB,UAAA,EAAoB,UAAU,CAAA,EAA8B;AAC3G,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AAErB,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,MAAA,CAAO,QAAQ,IAAA,CAAK,cAAA,EAAiB,CAAC,GAAG,GAAG,CAAC,QAAA,EAAU,UAAA,EAAY,GAAA,EAAK,OAAO,CAAC,CAAA;AAE1G,MAAA,OAAO,IAAA,CAAK,sBAAA,CAAuB,MAAA,EAAoB,QAAQ,CAAA;AAAA,IACjE,SAAS,KAAA,EAAO;AAEd,MAAA,IAAI,IAAA,CAAK,eAAA,CAAgB,KAAK,CAAA,EAAG;AAC/B,QAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,MAAA,CAAO,KAAK,mBAAA,EAAqB,CAAC,GAAG,CAAA,EAAG,CAAC,QAAA,EAAU,UAAA,EAAY,GAAA,EAAK,OAAO,CAAC,CAAA;AACtG,QAAA,OAAO,IAAA,CAAK,sBAAA,CAAuB,MAAA,EAAoB,QAAQ,CAAA;AAAA,MACjE;AAEA,MAAA,MAAM,IAAI,oBAAA,CAAqB,CAAA,2BAAA,EAA+B,KAAA,CAAgB,OAAO,IAAI,KAAc,CAAA;AAAA,IACzG;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,IAAA,CAAK,GAAA,EAAa,SAAA,EAAmB,MAAA,EAA2D;AAGpG,IAAA,IAAI;AACF,MAAA,IAAI,cAAc,cAAA,EAAgB;AAChC,QAAA,MAAM,MAAM,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,GAAA,KAAQ,GAAI,CAAA;AACxC,QAAA,MAAM,QAAA,GAAW,OAAO,QAAA,IAAY,EAAA;AACpC,QAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,GAAA,GAAM,QAAQ,CAAA,GAAI,QAAA;AAC5C,QAAA,MAAM,SAAA,GAAY,CAAA,EAAG,GAAG,CAAA,CAAA,EAAI,MAAM,CAAA,CAAA;AAClC,QAAA,MAAM,UAAA,GAAa,MAAM,IAAA,CAAK,MAAA,CAAO,IAAI,SAAS,CAAA;AAClD,QAAA,MAAM,OAAA,GAAU,UAAA,GAAa,QAAA,CAAS,UAAA,EAAY,EAAE,CAAA,GAAI,CAAA;AACxD,QAAA,MAAME,OAAAA,GAAS,OAAO,MAAA,IAAU,GAAA;AAEhC,QAAA,OAAO;AAAA,UACL,SAAS,OAAA,GAAUA,OAAAA;AAAA,UACnB,KAAA,EAAOA,OAAAA;AAAA,UACP,SAAA,EAAW,IAAA,CAAK,GAAA,CAAI,CAAA,EAAGA,UAAS,OAAO,CAAA;AAAA,UACvC,OAAO,MAAA,GAAS,QAAA;AAAA,UAChB;AAAA,SACF;AAAA,MACF,CAAA,MAAA,IAAW,cAAc,gBAAA,EAAkB;AACzC,QAAA,MAAM,KAAA,GAAQ,MAAM,IAAA,CAAK,MAAA,CAAO,MAAM,GAAG,CAAA;AACzC,QAAA,MAAMA,OAAAA,GAAS,OAAO,MAAA,IAAU,GAAA;AAChC,QAAA,MAAM,QAAA,GAAW,OAAO,QAAA,IAAY,EAAA;AAEpC,QAAA,OAAO;AAAA,UACL,SAAS,KAAA,GAAQA,OAAAA;AAAA,UACjB,KAAA,EAAOA,OAAAA;AAAA,UACP,SAAA,EAAW,IAAA,CAAK,GAAA,CAAI,CAAA,EAAGA,UAAS,KAAK,CAAA;AAAA,UACrC,OAAO,IAAA,CAAK,KAAA,CAAM,KAAK,GAAA,EAAI,GAAI,GAAI,CAAA,GAAI,QAAA;AAAA,UACvC,OAAA,EAAS;AAAA,SACX;AAAA,MACF;AAGA,MAAA,MAAM,MAAA,GAAS,OAAO,QAAA,IAAY,GAAA;AAClC,MAAA,OAAO;AAAA,QACL,OAAA,EAAS,IAAA;AAAA,QACT,KAAA,EAAO,MAAA;AAAA,QACP,SAAA,EAAW,MAAA;AAAA,QACX,OAAO,IAAA,CAAK,KAAA,CAAM,KAAK,GAAA,EAAI,GAAI,GAAI,CAAA,GAAI,EAAA;AAAA,QACvC,OAAA,EAAS;AAAA,OACX;AAAA,IACF,SAAS,KAAA,EAAO;AACd,MAAA,MAAM,IAAI,oBAAA,CAAqB,CAAA,aAAA,EAAiB,KAAA,CAAgB,OAAO,IAAI,KAAc,CAAA;AAAA,IAC3F;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,MAAM,GAAA,EAA4B;AACtC,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,GAAG,CAAA;AAAA,IAC3B,SAAS,KAAA,EAAO;AACd,MAAA,MAAM,IAAI,oBAAA,CAAqB,CAAA,cAAA,EAAkB,KAAA,CAAgB,OAAO,IAAI,KAAc,CAAA;AAAA,IAC5F;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,sBAAA,CAAuB,QAAkB,KAAA,EAAiC;AAChF,IAAA,MAAM,OAAA,GAAU,MAAA,CAAO,CAAC,CAAA,IAAK,CAAA;AAC7B,IAAA,MAAM,SAAA,GAAY,MAAA,CAAO,CAAC,CAAA,IAAK,CAAA;AAC/B,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,CAAC,CAAA,IAAK,CAAA;AAC3B,IAAA,MAAM,OAAA,GAAU,MAAA,CAAO,CAAC,CAAA,IAAK,CAAA;AAE7B,IAAA,OAAO;AAAA,MACL,SAAS,OAAA,KAAY,CAAA;AAAA,MACrB,KAAA;AAAA,MACA,SAAA;AAAA,MACA,KAAA;AAAA,MACA,OAAA;AAAA,MACA,UAAA,EAAY,OAAA,KAAY,CAAA,GAAI,IAAA,CAAK,IAAA,CAAK,QAAQ,IAAA,CAAK,GAAA,EAAI,GAAI,GAAI,CAAA,GAAI;AAAA,KACrE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,wBAAA,CAAyB,QAAkB,KAAA,EAAiC;AAClF,IAAA,MAAM,OAAA,GAAU,MAAA,CAAO,CAAC,CAAA,IAAK,CAAA;AAC7B,IAAA,MAAM,SAAA,GAAY,MAAA,CAAO,CAAC,CAAA,IAAK,CAAA;AAC/B,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,CAAC,CAAA,IAAK,CAAA;AAC3B,IAAA,MAAM,OAAA,GAAU,MAAA,CAAO,CAAC,CAAA,IAAK,CAAA;AAC7B,IAAA,MAAM,UAAA,GAAa,OAAO,CAAC,CAAA;AAE3B,IAAA,OAAO;AAAA,MACL,SAAS,OAAA,KAAY,CAAA;AAAA,MACrB,KAAA;AAAA,MACA,SAAA;AAAA,MACA,KAAA;AAAA,MACA,OAAA;AAAA,MACA,YAAY,UAAA,GAAa,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,UAAU,CAAA,GAAI;AAAA,KACrD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,sBAAA,CAAuB,QAAkB,QAAA,EAAoC;AACnF,IAAA,MAAM,OAAA,GAAU,MAAA,CAAO,CAAC,CAAA,IAAK,CAAA;AAC7B,IAAA,MAAM,SAAA,GAAY,MAAA,CAAO,CAAC,CAAA,IAAK,CAAA;AAC/B,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,CAAC,CAAA,IAAK,CAAA;AAC3B,IAAA,MAAM,OAAA,GAAU,MAAA,CAAO,CAAC,CAAA,IAAK,CAAA;AAC7B,IAAA,MAAM,UAAA,GAAa,OAAO,CAAC,CAAA;AAE3B,IAAA,OAAO;AAAA,MACL,SAAS,OAAA,KAAY,CAAA;AAAA,MACrB,KAAA,EAAO,QAAA;AAAA,MACP,SAAA;AAAA,MACA,KAAA;AAAA;AAAA,MACA,OAAA;AAAA,MACA,YAAY,UAAA,GAAa,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,UAAU,CAAA,GAAI;AAAA,KACrD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAgB,KAAA,EAAyB;AAC/C,IAAA,MAAM,UAAW,KAAA,CAAgB,OAAA;AACjC,IAAA,OAAO,QAAQ,QAAA,CAAS,UAAU,CAAA,IAAK,OAAA,CAAQ,SAAS,oBAAoB,CAAA;AAAA,EAC9E;AACF,CAAA;AAzNa,0BAAA,GAAN,eAAA,CAAA;AAAA,EADNL,iBAAAA,EAAW;AAAA,EAMG,eAAA,CAAA,CAAA,EAAAI,cAAOE,mBAAY,CAAA;AAAA,CAAA,EALrB,0BAAA,CAAA;;;ACIb,IAAM,yBAAA,GAA2G;AAAA,EAC/G,gBAAA,EAAkB,gBAAA;AAAA,EAClB,aAAA,EAAe,GAAA;AAAA,EACf,eAAA,EAAiB,EAAA;AAAA,EACjB,SAAA,EAAW,KAAA;AAAA,EACX,mBAAA,EAAqB,IAAA;AAAA,EACrB,cAAA,EAAgB,IAAA;AAAA,EAChB,OAAA,EAAS;AAAA,IACP,KAAA,EAAO,mBAAA;AAAA,IACP,SAAA,EAAW,uBAAA;AAAA,IACX,KAAA,EAAO,mBAAA;AAAA,IACP,UAAA,EAAY;AAAA,GACd;AAAA,EACA,WAAA,EAAa;AACf,CAAA;AA8BO,IAAM,eAAA,GAAN,MAAM,gBAAA,CAAyC;AAAA,EAOpD,WAAA,CAA6B,OAAA,GAAmC,EAAC,EAAG;AAAvC,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAAA,EAAwC;AAAA,EAN5D,IAAA,GAAO,YAAA;AAAA,EACP,OAAA,GAAkB,OAAA;AAAA,EAClB,WAAA,GAAc,8EAAA;AAAA,EAEf,YAAA;AAAA,EAIR,OAAO,cAAc,YAAA,EAA6E;AAChG,IAAA,MAAM,MAAA,GAAS,IAAI,gBAAA,EAAgB;AACnC,IAAA,MAAA,CAAO,YAAA,GAAe,YAAA;AACtB,IAAA,OAAO,MAAA;AAAA,EACT;AAAA,EAEA,OAAe,cAAc,OAAA,EAA2D;AACtF,IAAA,OAAO;AAAA,MACL,gBAAA,EAAkB,OAAA,CAAQ,gBAAA,IAAoB,yBAAA,CAA0B,gBAAA;AAAA,MACxE,aAAA,EAAe,OAAA,CAAQ,aAAA,IAAiB,yBAAA,CAA0B,aAAA;AAAA,MAClE,eAAA,EAAiB,OAAA,CAAQ,eAAA,IAAmB,yBAAA,CAA0B,eAAA;AAAA,MACtE,SAAA,EAAW,OAAA,CAAQ,SAAA,IAAa,yBAAA,CAA0B,SAAA;AAAA,MAC1D,mBAAA,EAAqB,OAAA,CAAQ,mBAAA,IAAuB,yBAAA,CAA0B,mBAAA;AAAA,MAC9E,cAAA,EAAgB,OAAA,CAAQ,cAAA,IAAkB,yBAAA,CAA0B,cAAA;AAAA,MACpE,SAAS,EAAE,GAAG,0BAA0B,OAAA,EAAS,GAAG,QAAQ,OAAA,EAAQ;AAAA,MACpE,WAAA,EAAa,OAAA,CAAQ,WAAA,IAAe,yBAAA,CAA0B,WAAA;AAAA,MAC9D,MAAM,OAAA,CAAQ,IAAA;AAAA,MACd,cAAc,OAAA,CAAQ;AAAA,KACxB;AAAA,EACF;AAAA,EAEA,UAAA,GAAsE;AACpE,IAAA,OAAO,IAAA,CAAK,YAAA,EAAc,OAAA,IAAW,EAAC;AAAA,EACxC;AAAA,EAEA,YAAA,GAA2B;AACzB,IAAA,MAAM,eAAA,GAA4B,KAAK,YAAA,GACnC;AAAA,MACE,OAAA,EAAS,yBAAA;AAAA,MACT,UAAA,EAAY,UAAU,IAAA,KAAoB;AACxC,QAAA,MAAM,cAAc,MAAM,IAAA,CAAK,YAAA,CAAc,UAAA,CAAW,GAAG,IAAI,CAAA;AAC/D,QAAA,OAAO,gBAAA,CAAgB,cAAc,WAAW,CAAA;AAAA,MAClD,CAAA;AAAA,MACA,MAAA,EAAQ,IAAA,CAAK,YAAA,CAAa,MAAA,IAAU;AAAC,KACvC,GACA;AAAA,MACE,OAAA,EAAS,yBAAA;AAAA,MACT,QAAA,EAAU,gBAAA,CAAgB,aAAA,CAAc,IAAA,CAAK,OAAO;AAAA,KACtD;AAEJ,IAAA,OAAO;AAAA,MACL,eAAA;AAAA,MACA,EAAE,OAAA,EAAS,gBAAA,EAAkB,QAAA,EAAU,0BAAA,EAA2B;AAAA,MAClE,EAAE,OAAA,EAAS,kBAAA,EAAoB,QAAA,EAAUH,wBAAA,EAAiB;AAAA;AAAA,MAE1DF,cAAAA;AAAA;AAAA,MAEAF,sBAAA;AAAA;AAAA,MAEA,EAAE,OAAA,EAASQ,eAAA,EAAY,QAAA,EAAUd,gCAAA;AAAyB,KAC5D;AAAA,EACF;AAAA,EAEA,UAAA,GAAgD;AAC9C,IAAA,OAAO,CAAC,yBAAA,EAA2B,kBAAA,EAAoBM,sBAAc,CAAA;AAAA,EACvE;AACF;;;AClHO,IAAM,sBAAN,MAAwD;AAAA,EAG7D,YAA6B,KAAA,EAAyB;AAAzB,IAAA,IAAA,CAAA,KAAA,GAAA,KAAA;AAAA,EAA0B;AAAA,EAF9C,IAAA,GAAO,cAAA;AAAA,EAIhB,SAAA,GAAoB;AAClB,IAAA,OAAO,mBAAA;AAAA,EACT;AAAA,EAEA,MAAM,KAAA,CAAM,GAAA,EAAa,MAAA,EAAoD;AAC3E,IAAA,IAAI,CAAC,KAAK,KAAA,EAAO;AACf,MAAA,MAAM,IAAI,MAAM,2GAA2G,CAAA;AAAA,IAC7H;AAEA,IAAA,MAAM,MAAA,GAAS,OAAO,MAAA,IAAU,GAAA;AAChC,IAAA,MAAM,QAAA,GAAW,OAAO,QAAA,IAAY,EAAA;AAEpC,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,WAAA,CAAY,GAAA,EAAK,QAAQ,QAAQ,CAAA;AAAA,EACrD;AACF;;;ACnBO,IAAM,wBAAN,MAA0D;AAAA,EAG/D,YAA6B,KAAA,EAAyB;AAAzB,IAAA,IAAA,CAAA,KAAA,GAAA,KAAA;AAAA,EAA0B;AAAA,EAF9C,IAAA,GAAO,gBAAA;AAAA,EAIhB,SAAA,GAAoB;AAClB,IAAA,OAAO,qBAAA;AAAA,EACT;AAAA,EAEA,MAAM,KAAA,CAAM,GAAA,EAAa,MAAA,EAAoD;AAC3E,IAAA,IAAI,CAAC,KAAK,KAAA,EAAO;AACf,MAAA,MAAM,IAAI,MAAM,6GAA6G,CAAA;AAAA,IAC/H;AAEA,IAAA,MAAM,MAAA,GAAS,OAAO,MAAA,IAAU,GAAA;AAChC,IAAA,MAAM,QAAA,GAAW,OAAO,QAAA,IAAY,EAAA;AAEpC,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,aAAA,CAAc,GAAA,EAAK,QAAQ,QAAQ,CAAA;AAAA,EACvD;AACF;;;ACnBO,IAAM,sBAAN,MAAwD;AAAA,EAG7D,YAA6B,KAAA,EAAyB;AAAzB,IAAA,IAAA,CAAA,KAAA,GAAA,KAAA;AAAA,EAA0B;AAAA,EAF9C,IAAA,GAAO,cAAA;AAAA,EAIhB,SAAA,GAAoB;AAClB,IAAA,OAAO,mBAAA;AAAA,EACT;AAAA,EAEA,MAAM,KAAA,CAAM,GAAA,EAAa,MAAA,EAAoD;AAC3E,IAAA,IAAI,CAAC,KAAK,KAAA,EAAO;AACf,MAAA,MAAM,IAAI,MAAM,2GAA2G,CAAA;AAAA,IAC7H;AAEA,IAAA,MAAM,QAAA,GAAW,MAAA,CAAO,QAAA,IAAY,MAAA,CAAO,MAAA,IAAU,GAAA;AACrD,IAAA,MAAM,aAAa,MAAA,CAAO,UAAA,KAAe,OAAO,QAAA,GAAW,QAAA,GAAW,OAAO,QAAA,GAAW,EAAA,CAAA;AAExF,IAAA,OAAO,KAAK,KAAA,CAAM,WAAA,CAAY,GAAA,EAAK,QAAA,EAAU,YAAY,CAAC,CAAA;AAAA,EAC5D;AACF","file":"index.js","sourcesContent":["{\n \"name\": \"@nestjs-redisx/rate-limit\",\n \"version\": \"1.0.3\",\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 \"types\": \"dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"require\": \"./dist/index.js\",\n \"import\": \"./dist/index.js\",\n \"default\": \"./dist/index.js\"\n }\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/rate-limit.integration.spec.ts\",\n \"test:integration:watch\": \"SKIP_INTEGRATION=false vitest watch test/integration/rate-limit.integration.spec.ts\",\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 },\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\",\n \"@nestjs/core\": \"^10.0.0 || ^11.0.0\",\n \"reflect-metadata\": \"^0.2.0\",\n \"rxjs\": \"^7.8.0\"\n },\n \"devDependencies\": {\n \"@nestjs/testing\": \"^10.0.0\",\n \"@types/node\": \"^20.0.0\",\n \"@typescript-eslint/eslint-plugin\": \"^6.0.0\",\n \"@typescript-eslint/parser\": \"^6.0.0\",\n \"eslint\": \"^8.0.0\",\n \"prettier\": \"^3.0.0\",\n \"tsup\": \"^8.0.0\",\n \"typescript\": \"^5.3.0\",\n \"vitest\": \"^1.6.0\",\n \"@vitest/coverage-v8\": \"^1.6.0\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\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","import { Catch, ExceptionFilter, ArgumentsHost, HttpStatus } from '@nestjs/common';\n\nimport { RateLimitExceededError } from '../../../shared/errors';\n\n/**\n * Exception filter for rate limit errors.\n * Catches RateLimitExceededError and returns 429 Too Many Requests.\n */\n@Catch(RateLimitExceededError)\nexport class RateLimitExceptionFilter implements ExceptionFilter {\n /**\n * Catch rate limit exceeded error and format response.\n */\n catch(exception: RateLimitExceededError, host: ArgumentsHost): void {\n const ctx = host.switchToHttp();\n const response = ctx.getResponse();\n\n const result = exception.result;\n\n response.status(HttpStatus.TOO_MANY_REQUESTS).header('Retry-After', exception.retryAfter.toString()).json({\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}\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 rate limit store.\n */\nexport const RATE_LIMIT_STORE = Symbol.for('RATE_LIMIT_STORE');\n","import { SetMetadata, UseGuards, applyDecorators, ExecutionContext } from '@nestjs/common';\n\nimport { IRateLimitResult } 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 * 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 { 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 * 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 @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 const options = this.getOptions(context);\n\n // Check skip condition\n if (await this.shouldSkip(context, options)) {\n return true;\n }\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 // Check X-Forwarded-For header\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 return ips[0] || 'unknown';\n }\n\n // Check X-Real-IP header\n const realIp = (request.headers as unknown as Record<string, string>)['x-real-ip'];\n if (realIp) {\n return realIp;\n }\n\n // Fall back to request.ip\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 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 response.header(limitHeader, result.limit.toString());\n response.header(remainingHeader, result.remaining.toString());\n response.header(resetHeader, result.reset.toString());\n\n if (!result.allowed && result.retryAfter) {\n response.header(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 { Injectable, Inject } from '@nestjs/common';\n\nimport { RATE_LIMIT_PLUGIN_OPTIONS, RATE_LIMIT_STORE } from '../../../shared/constants';\nimport { RateLimitScriptError } from '../../../shared/errors';\nimport { IRateLimitPluginOptions, IRateLimitConfig, IRateLimitResult, IRateLimitState } from '../../../shared/types';\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.\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 ) {}\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\n try {\n switch (algorithm) {\n case 'fixed-window':\n return await this.checkFixedWindow(fullKey, config);\n case 'sliding-window':\n return await this.checkSlidingWindow(fullKey, config);\n case 'token-bucket':\n return await this.checkTokenBucket(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\n try {\n const storeConfig = this.buildStoreConfig(algorithm, config);\n return await this.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 */\n async reset(key: string): Promise<void> {\n const algorithms = ['fixed-window', 'sliding-window', 'token-bucket'] as const;\n await Promise.all(algorithms.map((algo) => this.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 * Check fixed window rate limit.\n */\n private async checkFixedWindow(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 this.store.fixedWindow(key, points, duration);\n }\n\n /**\n * Check sliding window rate limit.\n */\n private async checkSlidingWindow(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 this.store.slidingWindow(key, points, duration);\n }\n\n /**\n * Check token bucket rate limit.\n */\n private async checkTokenBucket(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 this.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","/**\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 * 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\nlocal window_key = '{' .. key .. '}:' .. window\n\nlocal current = redis.call('INCR', window_key)\n\nif current == 1 then\n redis.call('EXPIRE', window_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, REDIS_DRIVER } from '@nestjs-redisx/core';\n\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(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 const windowKey = `${key}:${window}`;\n const currentStr = await this.driver.get(windowKey);\n const current = currentStr ? parseInt(currentStr, 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 peek would require HMGET\n const points = config.capacity || 100;\n return {\n allowed: true,\n limit: points,\n remaining: points,\n reset: Math.floor(Date.now() / 1000) + 60,\n current: 0,\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 } 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 { RedisRateLimitStoreAdapter } from './rate-limit/infrastructure/adapters/redis-rate-limit-store.adapter';\nimport { RATE_LIMIT_PLUGIN_OPTIONS, RATE_LIMIT_SERVICE, RATE_LIMIT_STORE } from './shared/constants';\nimport { IRateLimitPluginOptions } from './shared/types';\n\nconst DEFAULT_RATE_LIMIT_CONFIG: Required<Omit<IRateLimitPluginOptions, 'isGlobal' | '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};\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 return {\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 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 return [\n optionsProvider,\n { provide: RATE_LIMIT_STORE, useClass: RedisRateLimitStoreAdapter },\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 // Global exception filter to return 429 instead of 500\n { provide: APP_FILTER, useClass: RateLimitExceptionFilter },\n ];\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"]}
package/dist/index.mjs CHANGED
@@ -11,6 +11,9 @@ var __decorateClass = (decorators, target, key, kind) => {
11
11
  return result;
12
12
  };
13
13
  var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
14
+
15
+ // package.json
16
+ var version = "1.0.3";
14
17
  var RateLimitError = class extends RedisXError {
15
18
  constructor(message, code, result, cause) {
16
19
  super(message, code, cause, { result });
@@ -683,7 +686,7 @@ var RateLimitPlugin = class _RateLimitPlugin {
683
686
  this.options = options;
684
687
  }
685
688
  name = "rate-limit";
686
- version = "0.1.0";
689
+ version = version;
687
690
  description = "Rate limiting with fixed-window, sliding-window, and token-bucket algorithms";
688
691
  asyncOptions;
689
692
  static registerAsync(asyncOptions) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../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/rate-limit/application/services/rate-limit.service.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":["Injectable","Inject","points","Reflector"],"mappings":"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","file":"index.mjs","sourcesContent":["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","import { Catch, ExceptionFilter, ArgumentsHost, HttpStatus } from '@nestjs/common';\n\nimport { RateLimitExceededError } from '../../../shared/errors';\n\n/**\n * Exception filter for rate limit errors.\n * Catches RateLimitExceededError and returns 429 Too Many Requests.\n */\n@Catch(RateLimitExceededError)\nexport class RateLimitExceptionFilter implements ExceptionFilter {\n /**\n * Catch rate limit exceeded error and format response.\n */\n catch(exception: RateLimitExceededError, host: ArgumentsHost): void {\n const ctx = host.switchToHttp();\n const response = ctx.getResponse();\n\n const result = exception.result;\n\n response.status(HttpStatus.TOO_MANY_REQUESTS).header('Retry-After', exception.retryAfter.toString()).json({\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}\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 rate limit store.\n */\nexport const RATE_LIMIT_STORE = Symbol.for('RATE_LIMIT_STORE');\n","import { SetMetadata, UseGuards, applyDecorators, ExecutionContext } from '@nestjs/common';\n\nimport { IRateLimitResult } 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 * 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 { 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 * 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 @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 const options = this.getOptions(context);\n\n // Check skip condition\n if (await this.shouldSkip(context, options)) {\n return true;\n }\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 // Check X-Forwarded-For header\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 return ips[0] || 'unknown';\n }\n\n // Check X-Real-IP header\n const realIp = (request.headers as unknown as Record<string, string>)['x-real-ip'];\n if (realIp) {\n return realIp;\n }\n\n // Fall back to request.ip\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 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 response.header(limitHeader, result.limit.toString());\n response.header(remainingHeader, result.remaining.toString());\n response.header(resetHeader, result.reset.toString());\n\n if (!result.allowed && result.retryAfter) {\n response.header(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 { Injectable, Inject } from '@nestjs/common';\n\nimport { RATE_LIMIT_PLUGIN_OPTIONS, RATE_LIMIT_STORE } from '../../../shared/constants';\nimport { RateLimitScriptError } from '../../../shared/errors';\nimport { IRateLimitPluginOptions, IRateLimitConfig, IRateLimitResult, IRateLimitState } from '../../../shared/types';\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.\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 ) {}\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\n try {\n switch (algorithm) {\n case 'fixed-window':\n return await this.checkFixedWindow(fullKey, config);\n case 'sliding-window':\n return await this.checkSlidingWindow(fullKey, config);\n case 'token-bucket':\n return await this.checkTokenBucket(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\n try {\n const storeConfig = this.buildStoreConfig(algorithm, config);\n return await this.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 */\n async reset(key: string): Promise<void> {\n const algorithms = ['fixed-window', 'sliding-window', 'token-bucket'] as const;\n await Promise.all(algorithms.map((algo) => this.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 * Check fixed window rate limit.\n */\n private async checkFixedWindow(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 this.store.fixedWindow(key, points, duration);\n }\n\n /**\n * Check sliding window rate limit.\n */\n private async checkSlidingWindow(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 this.store.slidingWindow(key, points, duration);\n }\n\n /**\n * Check token bucket rate limit.\n */\n private async checkTokenBucket(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 this.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","/**\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 * 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\nlocal window_key = '{' .. key .. '}:' .. window\n\nlocal current = redis.call('INCR', window_key)\n\nif current == 1 then\n redis.call('EXPIRE', window_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, REDIS_DRIVER } from '@nestjs-redisx/core';\n\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(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 const windowKey = `${key}:${window}`;\n const currentStr = await this.driver.get(windowKey);\n const current = currentStr ? parseInt(currentStr, 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 peek would require HMGET\n const points = config.capacity || 100;\n return {\n allowed: true,\n limit: points,\n remaining: points,\n reset: Math.floor(Date.now() / 1000) + 60,\n current: 0,\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 } from '@nestjs-redisx/core';\n\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 { RedisRateLimitStoreAdapter } from './rate-limit/infrastructure/adapters/redis-rate-limit-store.adapter';\nimport { RATE_LIMIT_PLUGIN_OPTIONS, RATE_LIMIT_SERVICE, RATE_LIMIT_STORE } from './shared/constants';\nimport { IRateLimitPluginOptions } from './shared/types';\n\nconst DEFAULT_RATE_LIMIT_CONFIG: Required<Omit<IRateLimitPluginOptions, 'isGlobal' | '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};\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 = '0.1.0';\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 return {\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 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 return [\n optionsProvider,\n { provide: RATE_LIMIT_STORE, useClass: RedisRateLimitStoreAdapter },\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 // Global exception filter to return 429 instead of 500\n { provide: APP_FILTER, useClass: RateLimitExceptionFilter },\n ];\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"]}
1
+ {"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/rate-limit/application/services/rate-limit.service.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":["Injectable","Inject","points","Reflector"],"mappings":"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","file":"index.mjs","sourcesContent":["{\n \"name\": \"@nestjs-redisx/rate-limit\",\n \"version\": \"1.0.3\",\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 \"types\": \"dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"require\": \"./dist/index.js\",\n \"import\": \"./dist/index.js\",\n \"default\": \"./dist/index.js\"\n }\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/rate-limit.integration.spec.ts\",\n \"test:integration:watch\": \"SKIP_INTEGRATION=false vitest watch test/integration/rate-limit.integration.spec.ts\",\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 },\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\",\n \"@nestjs/core\": \"^10.0.0 || ^11.0.0\",\n \"reflect-metadata\": \"^0.2.0\",\n \"rxjs\": \"^7.8.0\"\n },\n \"devDependencies\": {\n \"@nestjs/testing\": \"^10.0.0\",\n \"@types/node\": \"^20.0.0\",\n \"@typescript-eslint/eslint-plugin\": \"^6.0.0\",\n \"@typescript-eslint/parser\": \"^6.0.0\",\n \"eslint\": \"^8.0.0\",\n \"prettier\": \"^3.0.0\",\n \"tsup\": \"^8.0.0\",\n \"typescript\": \"^5.3.0\",\n \"vitest\": \"^1.6.0\",\n \"@vitest/coverage-v8\": \"^1.6.0\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\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","import { Catch, ExceptionFilter, ArgumentsHost, HttpStatus } from '@nestjs/common';\n\nimport { RateLimitExceededError } from '../../../shared/errors';\n\n/**\n * Exception filter for rate limit errors.\n * Catches RateLimitExceededError and returns 429 Too Many Requests.\n */\n@Catch(RateLimitExceededError)\nexport class RateLimitExceptionFilter implements ExceptionFilter {\n /**\n * Catch rate limit exceeded error and format response.\n */\n catch(exception: RateLimitExceededError, host: ArgumentsHost): void {\n const ctx = host.switchToHttp();\n const response = ctx.getResponse();\n\n const result = exception.result;\n\n response.status(HttpStatus.TOO_MANY_REQUESTS).header('Retry-After', exception.retryAfter.toString()).json({\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}\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 rate limit store.\n */\nexport const RATE_LIMIT_STORE = Symbol.for('RATE_LIMIT_STORE');\n","import { SetMetadata, UseGuards, applyDecorators, ExecutionContext } from '@nestjs/common';\n\nimport { IRateLimitResult } 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 * 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 { 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 * 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 @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 const options = this.getOptions(context);\n\n // Check skip condition\n if (await this.shouldSkip(context, options)) {\n return true;\n }\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 // Check X-Forwarded-For header\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 return ips[0] || 'unknown';\n }\n\n // Check X-Real-IP header\n const realIp = (request.headers as unknown as Record<string, string>)['x-real-ip'];\n if (realIp) {\n return realIp;\n }\n\n // Fall back to request.ip\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 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 response.header(limitHeader, result.limit.toString());\n response.header(remainingHeader, result.remaining.toString());\n response.header(resetHeader, result.reset.toString());\n\n if (!result.allowed && result.retryAfter) {\n response.header(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 { Injectable, Inject } from '@nestjs/common';\n\nimport { RATE_LIMIT_PLUGIN_OPTIONS, RATE_LIMIT_STORE } from '../../../shared/constants';\nimport { RateLimitScriptError } from '../../../shared/errors';\nimport { IRateLimitPluginOptions, IRateLimitConfig, IRateLimitResult, IRateLimitState } from '../../../shared/types';\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.\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 ) {}\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\n try {\n switch (algorithm) {\n case 'fixed-window':\n return await this.checkFixedWindow(fullKey, config);\n case 'sliding-window':\n return await this.checkSlidingWindow(fullKey, config);\n case 'token-bucket':\n return await this.checkTokenBucket(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\n try {\n const storeConfig = this.buildStoreConfig(algorithm, config);\n return await this.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 */\n async reset(key: string): Promise<void> {\n const algorithms = ['fixed-window', 'sliding-window', 'token-bucket'] as const;\n await Promise.all(algorithms.map((algo) => this.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 * Check fixed window rate limit.\n */\n private async checkFixedWindow(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 this.store.fixedWindow(key, points, duration);\n }\n\n /**\n * Check sliding window rate limit.\n */\n private async checkSlidingWindow(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 this.store.slidingWindow(key, points, duration);\n }\n\n /**\n * Check token bucket rate limit.\n */\n private async checkTokenBucket(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 this.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","/**\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 * 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\nlocal window_key = '{' .. key .. '}:' .. window\n\nlocal current = redis.call('INCR', window_key)\n\nif current == 1 then\n redis.call('EXPIRE', window_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, REDIS_DRIVER } from '@nestjs-redisx/core';\n\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(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 const windowKey = `${key}:${window}`;\n const currentStr = await this.driver.get(windowKey);\n const current = currentStr ? parseInt(currentStr, 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 peek would require HMGET\n const points = config.capacity || 100;\n return {\n allowed: true,\n limit: points,\n remaining: points,\n reset: Math.floor(Date.now() / 1000) + 60,\n current: 0,\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 } 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 { RedisRateLimitStoreAdapter } from './rate-limit/infrastructure/adapters/redis-rate-limit-store.adapter';\nimport { RATE_LIMIT_PLUGIN_OPTIONS, RATE_LIMIT_SERVICE, RATE_LIMIT_STORE } from './shared/constants';\nimport { IRateLimitPluginOptions } from './shared/types';\n\nconst DEFAULT_RATE_LIMIT_CONFIG: Required<Omit<IRateLimitPluginOptions, 'isGlobal' | '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};\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 return {\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 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 return [\n optionsProvider,\n { provide: RATE_LIMIT_STORE, useClass: RedisRateLimitStoreAdapter },\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 // Global exception filter to return 429 instead of 500\n { provide: APP_FILTER, useClass: RateLimitExceptionFilter },\n ];\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"]}
@@ -36,7 +36,7 @@ import { IRateLimitPluginOptions } from './shared/types';
36
36
  export declare class RateLimitPlugin implements IRedisXPlugin {
37
37
  private readonly options;
38
38
  readonly name = "rate-limit";
39
- readonly version = "0.1.0";
39
+ readonly version: string;
40
40
  readonly description = "Rate limiting with fixed-window, sliding-window, and token-bucket algorithms";
41
41
  private asyncOptions?;
42
42
  constructor(options?: IRateLimitPluginOptions);
@@ -1 +1 @@
1
- {"version":3,"file":"rate-limit.plugin.d.ts","sourceRoot":"","sources":["../src/rate-limit.plugin.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAEjF,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAOzE,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAkBzD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,eAAgB,YAAW,aAAa;IAOvC,OAAO,CAAC,QAAQ,CAAC,OAAO;IANpC,QAAQ,CAAC,IAAI,gBAAgB;IAC7B,QAAQ,CAAC,OAAO,WAAW;IAC3B,QAAQ,CAAC,WAAW,kFAAkF;IAEtG,OAAO,CAAC,YAAY,CAAC,CAA+C;gBAEvC,OAAO,GAAE,uBAA4B;IAElE,MAAM,CAAC,aAAa,CAAC,YAAY,EAAE,mBAAmB,CAAC,uBAAuB,CAAC,GAAG,eAAe;IAMjG,OAAO,CAAC,MAAM,CAAC,aAAa;IAe5B,UAAU,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,aAAa,GAAG,gBAAgB,CAAC;IAIrE,YAAY,IAAI,QAAQ,EAAE;IA4B1B,UAAU,IAAI,KAAK,CAAC,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;CAGhD"}
1
+ {"version":3,"file":"rate-limit.plugin.d.ts","sourceRoot":"","sources":["../src/rate-limit.plugin.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAEjF,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAQzE,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAkBzD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,eAAgB,YAAW,aAAa;IAOvC,OAAO,CAAC,QAAQ,CAAC,OAAO;IANpC,QAAQ,CAAC,IAAI,gBAAgB;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAW;IACnC,QAAQ,CAAC,WAAW,kFAAkF;IAEtG,OAAO,CAAC,YAAY,CAAC,CAA+C;gBAEvC,OAAO,GAAE,uBAA4B;IAElE,MAAM,CAAC,aAAa,CAAC,YAAY,EAAE,mBAAmB,CAAC,uBAAuB,CAAC,GAAG,eAAe;IAMjG,OAAO,CAAC,MAAM,CAAC,aAAa;IAe5B,UAAU,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,aAAa,GAAG,gBAAgB,CAAC;IAIrE,YAAY,IAAI,QAAQ,EAAE;IA4B1B,UAAU,IAAI,KAAK,CAAC,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;CAGhD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nestjs-redisx/rate-limit",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "Rate limiting plugin for NestJS RedisX with multiple algorithms (fixed-window, sliding-window, token-bucket)",
5
5
  "author": "NestJS RedisX Team",
6
6
  "license": "MIT",