{"version":3,"sources":["../src/index.ts","../src/rate-limit.ts","../src/utils.ts"],"sourcesContent":["import { type RedisClientType, createClient } from 'redis';\nimport { Ratelimit } from './rate-limit';\nimport {\n  FixedWindowOptions,\n  LimiterType,\n  RatelimitConfig,\n  SlidingWindowOptions,\n  TimeWindow,\n  TokenBucketOptions,\n} from './types';\nimport { parseTimeWindow } from './utils';\nexport { Ratelimit };\n// Enhanced error handling\nexport class RatelimitError extends Error {\n  constructor(message: string) {\n    super(message);\n    this.name = 'RatelimitError';\n  }\n}\n\n// Limiter functions\nexport const fixedWindow = (limit: number, window: TimeWindow): FixedWindowOptions => {\n  return {\n    limit,\n    interval: parseTimeWindow(window),\n  };\n};\n\nexport const slidingWindow = (limit: number, window: TimeWindow): SlidingWindowOptions => {\n  return {\n    limit,\n    interval: parseTimeWindow(window),\n  };\n};\n\nexport const tokenBucket = (\n  refillRate: number,\n  interval: TimeWindow,\n  limit: number\n): TokenBucketOptions => {\n  return {\n    refillRate,\n    interval: parseTimeWindow(interval),\n    limit,\n  };\n};\n\nlet redisClient: RedisClientType | undefined;\nexport const getRedisSingleClient = (envRedisKey: string) => {\n  if (redisClient) {\n    return redisClient;\n  }\n  redisClient = createClient({\n    url: process.env[envRedisKey] || 'redis://localhost:6379',\n    socket: {\n      reconnectStrategy: (retries) => Math.min(retries * 50, 1000),\n    },\n  }) as RedisClientType;\n  redisClient.on('error', (err) => console.error('Redis Client Error', err));\n  // Connect automatically\n  (async () => {\n    try {\n      if (!redisClient.isOpen || !(await redisClient.ping())) {\n        await redisClient.connect();\n      }\n    } catch (error) {\n      console.error('Failed to connect to Redis:', error);\n    }\n  })();\n  return redisClient;\n};\n// Factory function\nexport interface CreateRateLimiterProps extends Omit<RatelimitConfig, 'redis'> {}\n\nexport const createSingletonRateLimiter = (\n  props?: CreateRateLimiterProps & {\n    limiter?: LimiterType;\n    envRedisKey?: string;\n  }\n) => {\n  const redis = getRedisSingleClient(props?.envRedisKey ?? 'REDIS_URL');\n  return createRateLimiter(redis, props);\n};\n\nexport const createRateLimiter = (\n  redis: RedisClientType,\n  props?: CreateRateLimiterProps & {\n    limiter?: LimiterType;\n    envRedisKey?: string;\n  }\n) => {\n  return new Ratelimit({\n    redis,\n    limiter: props?.limiter ?? slidingWindow(10, '10 s'),\n    prefix: props?.prefix ?? 'open-ratelimit',\n    analytics: props?.analytics ?? false,\n    timeout: props?.timeout ?? 1000,\n    ephemeralCache: props?.ephemeralCache ?? true,\n    ephemeralCacheTTL: props?.ephemeralCacheTTL ?? 60000,\n  });\n};\n","import { RedisClientType } from 'redis';\nimport { RatelimitError } from '.';\nimport {\n  FixedWindowOptions,\n  LimiterType,\n  RatelimitConfig,\n  RatelimitResponse,\n  SlidingWindowOptions,\n  TokenBucketOptions,\n} from './types';\n// Function to detect limiter type\nconst getLimiterType = (limiter: LimiterType): string => {\n  if ('refillRate' in limiter) return 'tokenBucket';\n  // Both fixed and sliding window have same properties, so we'll use an explicit property\n  return 'interval' in limiter ? 'slidingWindow' : 'fixedWindow';\n};\n\n// Memory cache for handling Redis outages\nclass EphemeralCache {\n  private cache: Map<string, { count: number; expires: number }>;\n  private ttl: number;\n\n  constructor(ttlMs = 60000) {\n    this.cache = new Map();\n    this.ttl = ttlMs;\n    // Clean expired items periodically\n    setInterval(() => this.cleanup(), Math.min(ttlMs, 60000));\n  }\n\n  get(key: string): number {\n    const now = Date.now();\n    const item = this.cache.get(key);\n    if (!item || item.expires < now) return 0;\n    return item.count;\n  }\n\n  set(key: string, count: number, windowMs: number): void {\n    this.cache.set(key, {\n      count,\n      expires: Date.now() + Math.min(windowMs, this.ttl),\n    });\n  }\n\n  increment(key: string, windowMs: number): number {\n    const now = Date.now();\n    const item = this.cache.get(key) || { count: 0, expires: now + windowMs };\n    if (item.expires < now) {\n      item.count = 1;\n      item.expires = now + windowMs;\n    } else {\n      item.count++;\n    }\n    this.cache.set(key, item);\n    return item.count;\n  }\n\n  cleanup(): void {\n    const now = Date.now();\n    for (const [key, item] of this.cache.entries()) {\n      if (item.expires < now) {\n        this.cache.delete(key);\n      }\n    }\n  }\n}\n\n// Main rate limiter class\nexport class Ratelimit {\n  private redisPromise: Promise<RedisClientType>;\n  private limiter: LimiterType;\n  private limiterType: string;\n  private prefix: string;\n  private analytics: boolean;\n  private timeout: number;\n  private ephemeralCache?: EphemeralCache;\n\n  constructor(config: RatelimitConfig) {\n    // Handle both direct client and promise\n    this.redisPromise = Promise.resolve(config.redis);\n    this.limiter = config.limiter;\n    this.limiterType = getLimiterType(config.limiter);\n    this.prefix = config.prefix || 'ratelimit';\n    this.analytics = config.analytics ?? false;\n    this.timeout = config.timeout ?? 1000;\n\n    // Create ephemeral cache if requested\n    if (config.ephemeralCache) {\n      this.ephemeralCache = new EphemeralCache(config.ephemeralCacheTTL);\n    }\n  }\n\n  // Get the Redis client with timeout protection\n  private async getRedis(): Promise<RedisClientType> {\n    try {\n      const timeoutPromise = new Promise<never>((_, reject) => {\n        setTimeout(\n          () => reject(new RatelimitError(`Redis connection timed out after ${this.timeout}ms`)),\n          this.timeout\n        );\n      });\n\n      const redis = await Promise.race([this.redisPromise, timeoutPromise]);\n\n      // Check if Redis is connected\n      if (!redis.isOpen || !(await redis.ping())) {\n        await redis.connect();\n      }\n\n      return redis;\n    } catch (error) {\n      throw new RatelimitError(\n        `Failed to connect to Redis: ${error instanceof Error ? error.message : String(error)}`\n      );\n    }\n  }\n\n  // Apply rate limit\n  async limit(identifier: string): Promise<RatelimitResponse> {\n    const now = Date.now();\n    const key = `${this.prefix}:${identifier}`;\n\n    // Try ephemeral cache first if available\n    if (this.ephemeralCache && this.limiterType === 'slidingWindow') {\n      const windowMs = (this.limiter as SlidingWindowOptions).interval;\n      const limit = (this.limiter as SlidingWindowOptions).limit;\n\n      try {\n        // Try Redis first\n        return await this.applySlidingWindowLimit(key, now);\n      } catch (error) {\n        // Fall back to ephemeral cache on Redis failure\n        console.warn(\n          `Redis error, using ephemeral cache: ${\n            error instanceof Error ? error.message : String(error)\n          }`\n        );\n\n        const count = this.ephemeralCache.increment(key, windowMs);\n        const success = count <= limit;\n\n        return {\n          success,\n          limit,\n          remaining: Math.max(0, limit - count),\n          reset: now + windowMs,\n          retryAfter: success ? 0 : Math.ceil(windowMs / 1000),\n        };\n      }\n    }\n\n    // Apply appropriate limiter\n    try {\n      switch (this.limiterType) {\n        case 'slidingWindow':\n          return await this.applySlidingWindowLimit(key, now);\n        case 'fixedWindow':\n          return await this.applyFixedWindowLimit(key, now);\n        case 'tokenBucket':\n          return await this.applyTokenBucketLimit(key, now);\n        default:\n          throw new RatelimitError(`Unknown limiter type: ${this.limiterType}`);\n      }\n    } catch (error) {\n      // If Redis fails and no ephemeral cache, fail open with a warning\n      console.error(\n        `Rate limiting error: ${error instanceof Error ? error.message : String(error)}`\n      );\n\n      // Get limit based on limiter type\n      const limit = 'limit' in this.limiter ? this.limiter.limit : 10;\n\n      return {\n        success: true, // Fail open\n        limit,\n        remaining: limit - 1,\n        reset: now + 60000, // Arbitrary 1-minute reset\n      };\n    }\n  }\n\n  // Sliding window implementation\n  private async applySlidingWindowLimit(key: string, now: number): Promise<RatelimitResponse> {\n    const redis = await this.getRedis();\n    const { limit, interval } = this.limiter as SlidingWindowOptions;\n    const windowStart = now - interval;\n\n    const luaScript = `\n        local key = KEYS[1]\n        local analyticsKey = KEYS[2]\n        local now = tonumber(ARGV[1])\n        local windowMs = tonumber(ARGV[2])\n        local maxRequests = tonumber(ARGV[3])\n        local doAnalytics = tonumber(ARGV[4])\n        local windowStart = now - windowMs\n        \n        -- Remove counts older than the current window\n        redis.call('ZREMRANGEBYSCORE', key, 0, windowStart)\n        \n        -- Get current count\n        local count = redis.call('ZCARD', key)\n        \n        local success = count < maxRequests\n        \n        -- Add current timestamp if successful\n        if success then\n          redis.call('ZADD', key, now, now .. ':' .. math.random())\n          count = count + 1\n        end\n        \n        -- Set expiration to keep memory usage bounded\n        redis.call('PEXPIRE', key, windowMs * 2)\n        \n        -- Analytics if requested\n        if doAnalytics == 1 then\n          -- Record request timestamp for analytics\n          redis.call('ZADD', analyticsKey, now, now)\n          redis.call('PEXPIRE', analyticsKey, windowMs * 2)\n        end\n        \n        -- Calculate when the oldest request expires\n        local oldestTimestamp = 0\n        if count >= maxRequests then\n          local oldest = redis.call('ZRANGE', key, 0, 0, 'WITHSCORES')\n          if #oldest >= 2 then\n            oldestTimestamp = tonumber(oldest[2])\n          end\n        end\n        \n        -- Calculate pending and throughput if analytics enabled\n        local pending = 0\n        local throughput = 0\n        if doAnalytics == 1 then\n          pending = count\n          -- Calculate requests in the last second\n          local secondAgo = now - 1000\n          throughput = redis.call('ZCOUNT', analyticsKey, secondAgo, '+inf')\n        end\n        \n        -- Return results\n        return {\n          success and 1 or 0, \n          maxRequests, \n          math.max(0, maxRequests - count), \n          now + windowMs,\n          oldestTimestamp > 0 and math.ceil((oldestTimestamp + windowMs - now) / 1000) or 0,\n          pending,\n          throughput\n        }\n      `;\n\n    try {\n      const analyticsKey = `${key}:analytics`;\n      const result = await redis.eval(luaScript, {\n        keys: [key, analyticsKey],\n        arguments: [\n          now.toString(),\n          interval.toString(),\n          limit.toString(),\n          this.analytics ? '1' : '0',\n        ],\n      });\n\n      if (!Array.isArray(result)) {\n        throw new RatelimitError('Invalid response from Redis');\n      }\n\n      const response: RatelimitResponse = {\n        success: Boolean(result[0]),\n        limit: Number(result[1]),\n        remaining: Number(result[2]),\n        reset: Number(result[3]),\n      };\n\n      // Add conditional properties\n      const retryAfter = Number(result[4]);\n      if (retryAfter > 0) response.retryAfter = retryAfter;\n\n      if (this.analytics) {\n        response.pending = Number(result[5]);\n        response.throughput = Number(result[6]);\n      }\n\n      // Store in ephemeral cache if available\n      if (this.ephemeralCache) {\n        this.ephemeralCache.set(key, limit - response.remaining, interval);\n      }\n\n      return response;\n    } catch (error) {\n      throw new RatelimitError(\n        `Sliding window limit error: ${error instanceof Error ? error.message : String(error)}`\n      );\n    }\n  }\n\n  // Fixed window implementation\n  private async applyFixedWindowLimit(key: string, now: number): Promise<RatelimitResponse> {\n    const redis = await this.getRedis();\n    const { limit, interval } = this.limiter as FixedWindowOptions;\n\n    // Create window key with fixed time boundary\n    const windowKey = `${key}:${Math.floor(now / interval)}`;\n\n    const luaScript = `\n        local key = KEYS[1]\n        local analyticsKey = KEYS[2]\n        local limit = tonumber(ARGV[1])\n        local windowMs = tonumber(ARGV[2])\n        local doAnalytics = tonumber(ARGV[3])\n        local now = tonumber(ARGV[4])\n        \n        -- Increment counter for this window\n        local count = redis.call('INCR', key)\n        \n        -- Set expiration if this is first request in window\n        if count == 1 then\n          redis.call('PEXPIRE', key, windowMs * 2)\n        end\n        \n        local success = count <= limit\n        \n        -- Analytics\n        if doAnalytics == 1 then\n          redis.call('ZADD', analyticsKey, now, now)\n          redis.call('PEXPIRE', analyticsKey, windowMs)\n        end\n        \n        -- Calculate remaining time in window\n        local ttl = redis.call('PTTL', key)\n        if ttl < 0 then ttl = windowMs end\n        \n        -- Calculate throughput if analytics enabled\n        local throughput = 0\n        if doAnalytics == 1 then\n          -- Calculate requests in the last second\n          local secondAgo = now - 1000\n          throughput = redis.call('ZCOUNT', analyticsKey, secondAgo, '+inf')\n        end\n        \n        return {\n          success and 1 or 0,\n          limit,\n          math.max(0, limit - count),\n          now + ttl,\n          success and 0 or math.ceil(ttl / 1000),\n          count,\n          throughput\n        }\n      `;\n\n    try {\n      const analyticsKey = `${key}:analytics`;\n      const result = await redis.eval(luaScript, {\n        keys: [windowKey, analyticsKey],\n        arguments: [\n          limit.toString(),\n          interval.toString(),\n          this.analytics ? '1' : '0',\n          now.toString(),\n        ],\n      });\n\n      if (!Array.isArray(result)) {\n        throw new RatelimitError('Invalid response from Redis');\n      }\n\n      const response: RatelimitResponse = {\n        success: Boolean(result[0]),\n        limit: Number(result[1]),\n        remaining: Number(result[2]),\n        reset: Number(result[3]),\n      };\n\n      const retryAfter = Number(result[4]);\n      if (retryAfter > 0) response.retryAfter = retryAfter;\n\n      if (this.analytics) {\n        response.pending = Number(result[5]);\n        response.throughput = Number(result[6]);\n      }\n\n      return response;\n    } catch (error) {\n      throw new RatelimitError(\n        `Fixed window limit error: ${error instanceof Error ? error.message : String(error)}`\n      );\n    }\n  }\n\n  // Token bucket implementation\n  private async applyTokenBucketLimit(key: string, now: number): Promise<RatelimitResponse> {\n    const redis = await this.getRedis();\n    const { limit, refillRate, interval } = this.limiter as TokenBucketOptions;\n\n    const luaScript = `\n        local key = KEYS[1]\n        local analyticsKey = KEYS[2]\n        local now = tonumber(ARGV[1])\n        local refillRate = tonumber(ARGV[2])\n        local refillInterval = tonumber(ARGV[3])\n        local bucketCapacity = tonumber(ARGV[4])\n        local doAnalytics = tonumber(ARGV[5])\n        \n        -- Get current bucket state\n        local bucketInfo = redis.call('HMGET', key, 'tokens', 'lastRefill')\n        local tokens = tonumber(bucketInfo[1]) or bucketCapacity\n        local lastRefill = tonumber(bucketInfo[2]) or 0\n        \n        -- Calculate token refill\n        local elapsedTime = now - lastRefill\n        local tokensToAdd = math.floor(elapsedTime * (refillRate / refillInterval))\n        \n        if tokensToAdd > 0 then\n          -- Add tokens based on elapsed time\n          tokens = math.min(bucketCapacity, tokens + tokensToAdd)\n          lastRefill = now\n        end\n        \n        -- Try to consume a token\n        local success = tokens >= 1\n        if success then\n          tokens = tokens - 1\n        end\n        \n        -- Save updated bucket state\n        redis.call('HMSET', key, 'tokens', tokens, 'lastRefill', lastRefill)\n        redis.call('PEXPIRE', key, refillInterval * 2)\n        \n        -- Analytics\n        if doAnalytics == 1 then\n          redis.call('ZADD', analyticsKey, now, now)\n          redis.call('PEXPIRE', analyticsKey, refillInterval)\n        end\n        \n        -- Calculate time until next token refill\n        local timeToNextToken = success and 0 or math.ceil((1 - tokens) * (refillInterval / refillRate))\n        \n        -- Calculate throughput if analytics enabled\n        local throughput = 0\n        if doAnalytics == 1 then\n          -- Calculate requests in the last second\n          local secondAgo = now - 1000\n          throughput = redis.call('ZCOUNT', analyticsKey, secondAgo, '+inf')\n        end\n        \n        return {\n          success and 1 or 0,\n          bucketCapacity,\n          tokens,\n          now + (refillInterval / refillRate),\n          timeToNextToken,\n          bucketCapacity - tokens,\n          throughput\n        }\n      `;\n\n    try {\n      const analyticsKey = `${key}:analytics`;\n      const result = await redis.eval(luaScript, {\n        keys: [key, analyticsKey],\n        arguments: [\n          now.toString(),\n          refillRate.toString(),\n          interval.toString(),\n          limit.toString(),\n          this.analytics ? '1' : '0',\n        ],\n      });\n\n      if (!Array.isArray(result)) {\n        throw new RatelimitError('Invalid response from Redis');\n      }\n\n      const response: RatelimitResponse = {\n        success: Boolean(result[0]),\n        limit: Number(result[1]),\n        remaining: Number(result[2]),\n        reset: Number(result[3]),\n      };\n\n      const retryAfter = Number(result[4]);\n      if (retryAfter > 0) response.retryAfter = retryAfter;\n\n      if (this.analytics) {\n        response.pending = Number(result[5]);\n        response.throughput = Number(result[6]);\n      }\n\n      return response;\n    } catch (error) {\n      throw new RatelimitError(\n        `Token bucket limit error: ${error instanceof Error ? error.message : String(error)}`\n      );\n    }\n  }\n  // Modified block method to prevent infinite loop\n  async block(identifier: string, maxWaitMs = 5000): Promise<RatelimitResponse> {\n    const startTime = Date.now();\n    let attempts = 0;\n    const maxAttempts = 100; // Safeguard against infinite loops\n\n    while (attempts < maxAttempts) {\n      attempts++;\n      const response = await this.limit(identifier);\n\n      if (response.success) {\n        return response;\n      }\n\n      const currentTime = Date.now();\n      if (currentTime - startTime >= maxWaitMs) {\n        throw new RatelimitError(\n          `Rate limit exceeded for ${identifier} after waiting ${currentTime - startTime}ms`\n        );\n      }\n\n      const waitTime = Math.max(\n        50,\n        Math.min(500, response.retryAfter ? (response.retryAfter * 1000) / 2 : 100)\n      );\n\n      await new Promise((resolve) => setTimeout(resolve, waitTime));\n    }\n\n    // This is a safeguard in case we reach max attempts\n    throw new RatelimitError(`Rate limit exceeded for ${identifier} after ${maxAttempts} attempts`);\n  }\n  // Reset rate limit for an identifier\n  async reset(identifier: string): Promise<void> {\n    try {\n      const redis = await this.getRedis();\n      const key = `${this.prefix}:${identifier}`;\n\n      await redis.del(key);\n      await redis.del(`${key}:analytics`);\n\n      // Also clear ephemeral cache if available\n      if (this.ephemeralCache) {\n        this.ephemeralCache.set(key, 0, 0);\n      }\n    } catch (error) {\n      throw new RatelimitError(\n        `Failed to reset rate limit: ${error instanceof Error ? error.message : String(error)}`\n      );\n    }\n  }\n}\n","import { RatelimitError } from './index';\nimport { TimeWindow } from './types';\n\n// Parse time window to milliseconds with proper error handling\nexport const parseTimeWindow = (window: TimeWindow): number => {\n  try {\n    const [valueStr, unit] = window.split(' ');\n    const value = Number.parseInt(valueStr || 's', 10);\n\n    if (Number.isNaN(value) || value <= 0) {\n      throw new RatelimitError(`Invalid time value: ${valueStr}`);\n    }\n\n    switch (unit) {\n      case 'ms':\n        return value;\n      case 's':\n        return value * 1000;\n      case 'm':\n        return value * 60 * 1000;\n      case 'h':\n        return value * 60 * 60 * 1000;\n      case 'd':\n        return value * 24 * 60 * 60 * 1000;\n      default:\n        throw new RatelimitError(`Invalid time unit: ${unit}`);\n    }\n  } catch (error) {\n    if (error instanceof RatelimitError) throw error;\n    throw new RatelimitError(`Failed to parse time window: ${window}`);\n  }\n};\n"],"mappings":"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","names":["createClient","getLimiterType","limiter","EphemeralCache","ttlMs","key","now","item","count","windowMs","Ratelimit","config","timeoutPromise","_","reject","RatelimitError","redis","error","identifier","limit","success","interval","windowStart","luaScript","analyticsKey","result","response","retryAfter","windowKey","refillRate","maxWaitMs","startTime","attempts","maxAttempts","currentTime","waitTime","resolve","parseTimeWindow","window","valueStr","unit","value","RatelimitError","error","RatelimitError","message","fixedWindow","limit","window","parseTimeWindow","slidingWindow","tokenBucket","refillRate","interval","redisClient","getRedisSingleClient","envRedisKey","createClient","retries","err","error","createSingletonRateLimiter","props","redis","createRateLimiter","Ratelimit"]}