{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @jfungus/ratelimit-hono - Rate limiting middleware for Hono\n *\n * @module\n */\n\nimport {\n  type Algorithm,\n  MemoryStore,\n  type RateLimitInfo,\n  type RateLimitStore,\n  checkRateLimit,\n} from '@jfungus/ratelimit'\nimport type { Context, Env, MiddlewareHandler } from 'hono'\n\n// Re-export core types\nexport {\n  type Algorithm,\n  MemoryStore,\n  type RateLimitInfo,\n  type RateLimitStore,\n  type CheckRateLimitOptions,\n  type CheckRateLimitResult,\n  type StoreResult,\n  checkRateLimit,\n  createRateLimiter,\n} from '@jfungus/ratelimit'\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Quota unit for IETF standard headers.\n * @see https://datatracker.ietf.org/doc/draft-ietf-httpapi-ratelimit-headers/\n */\nexport type QuotaUnit = 'requests' | 'content-bytes' | 'concurrent-requests'\n\n/**\n * Header format options.\n *\n * ## \"legacy\" (default)\n * Common X-RateLimit-* headers used by GitHub, Twitter, and most APIs:\n * - `X-RateLimit-Limit`: max requests in window\n * - `X-RateLimit-Remaining`: remaining requests\n * - `X-RateLimit-Reset`: Unix timestamp (seconds) when window resets\n *\n * ## \"draft-6\"\n * IETF draft-06 format with individual RateLimit-* headers:\n * - `RateLimit-Policy`: policy description (e.g., `100;w=60`)\n * - `RateLimit-Limit`: max requests\n * - `RateLimit-Remaining`: remaining requests\n * - `RateLimit-Reset`: seconds until reset\n *\n * ## \"draft-7\"\n * IETF draft-07 format with combined RateLimit header:\n * - `RateLimit-Policy`: policy description\n * - `RateLimit`: combined (e.g., `limit=100, remaining=50, reset=30`)\n *\n * ## \"standard\"\n * Current IETF draft-08+ format with structured field values (RFC 9651):\n * - `RateLimit-Policy`: `\"name\";q=100;w=60`\n * - `RateLimit`: `\"name\";r=50;t=30`\n *\n * ## false\n * Disable all rate limit headers.\n *\n * @see https://datatracker.ietf.org/doc/draft-ietf-httpapi-ratelimit-headers/\n */\nexport type HeadersFormat =\n  | 'legacy' // X-RateLimit-* headers (GitHub/Twitter style)\n  | 'draft-6' // IETF draft-06: individual RateLimit-* headers\n  | 'draft-7' // IETF draft-07: combined RateLimit header\n  | 'standard' // IETF draft-08+: structured field format (current)\n  | false // Disable headers\n\n/**\n * Store access interface exposed in context\n */\nexport type RateLimitStoreAccess = {\n  /** Get rate limit info for a key. Returns undefined if key doesn't exist. */\n  getKey: (\n    key: string,\n  ) =>\n    | Promise<{ count: number; reset: number } | undefined>\n    | { count: number; reset: number }\n    | undefined\n  /** Reset rate limit for a key */\n  resetKey: (key: string) => void | Promise<void>\n  /** Reset all rate limit entries (if supported by store) */\n  resetAll?: () => void | Promise<void>\n}\n\n/**\n * Options for rate limit middleware\n */\nexport type RateLimitOptions<E extends Env = Env> = {\n  /**\n   * Maximum requests allowed in the time window.\n   * @default 100\n   */\n  limit?: number | ((c: Context<E>) => number | Promise<number>)\n\n  /**\n   * Time window in milliseconds.\n   * @default 60000 (1 minute)\n   */\n  windowMs?: number\n\n  /**\n   * Rate limiting algorithm.\n   * @default 'sliding-window'\n   */\n  algorithm?: Algorithm\n\n  /**\n   * Storage backend for rate limit state.\n   * @default MemoryStore\n   */\n  store?: RateLimitStore\n\n  /**\n   * Generate unique key for each client.\n   * @default IP address from headers\n   */\n  keyGenerator?: (c: Context<E>) => string | Promise<string>\n\n  /**\n   * Handler called when rate limit is exceeded.\n   */\n  handler?: (c: Context<E>, info: RateLimitInfo) => Response | Promise<Response>\n\n  /**\n   * HTTP header format to use.\n   *\n   * - \"legacy\": X-RateLimit-* headers (GitHub/Twitter style, default)\n   * - \"draft-6\": IETF draft-06 individual headers\n   * - \"draft-7\": IETF draft-07 combined header\n   * - \"standard\": IETF draft-08+ structured fields (current spec)\n   * - false: Disable headers\n   *\n   * @default 'legacy'\n   */\n  headers?: HeadersFormat\n\n  /**\n   * Policy identifier for IETF headers (draft-6+).\n   * Used in RateLimit and RateLimit-Policy headers.\n   * @default 'default'\n   */\n  identifier?: string\n\n  /**\n   * Quota unit for IETF standard headers.\n   * Only included in \"standard\" format when not \"requests\".\n   * @default 'requests'\n   */\n  quotaUnit?: QuotaUnit\n\n  /**\n   * Skip rate limiting for certain requests.\n   */\n  skip?: (c: Context<E>) => boolean | Promise<boolean>\n\n  /**\n   * Don't count successful (2xx) requests against limit.\n   * @default false\n   */\n  skipSuccessfulRequests?: boolean\n\n  /**\n   * Don't count failed (4xx, 5xx) requests against limit.\n   * @default false\n   */\n  skipFailedRequests?: boolean\n\n  /**\n   * Callback when a request is rate limited.\n   */\n  onRateLimited?: (c: Context<E>, info: RateLimitInfo) => void | Promise<void>\n\n  /**\n   * Behavior when store operations fail.\n   *\n   * - 'allow': Allow the request through (fail-open, default)\n   * - 'deny': Block the request with 500 error (fail-closed)\n   * - Function: Custom handler returning true to allow, false to deny\n   *\n   * @default 'allow'\n   */\n  onStoreError?: 'allow' | 'deny' | ((error: Error, c: Context<E>) => boolean | Promise<boolean>)\n\n  /**\n   * Dry-run mode: track rate limits but don't block requests.\n   * Useful for monitoring and testing before enforcing limits.\n   * Headers and callbacks are still set/called normally.\n   * @default false\n   */\n  dryRun?: boolean\n}\n\n/**\n * Cloudflare Rate Limiting binding interface\n */\nexport type RateLimitBinding = {\n  limit: (options: { key: string }) => Promise<{ success: boolean }>\n}\n\n/**\n * Options for Cloudflare Rate Limiting binding\n */\nexport type CloudflareRateLimitOptions<E extends Env = Env> = {\n  /**\n   * Cloudflare Rate Limiting binding from env\n   */\n  binding: RateLimitBinding | ((c: Context<E>) => RateLimitBinding)\n\n  /**\n   * Generate unique key for each client.\n   */\n  keyGenerator: (c: Context<E>) => string | Promise<string>\n\n  /**\n   * Handler called when rate limit is exceeded.\n   */\n  handler?: (c: Context<E>) => Response | Promise<Response>\n\n  /**\n   * Skip rate limiting for certain requests.\n   */\n  skip?: (c: Context<E>) => boolean | Promise<boolean>\n}\n\n// ============================================================================\n// Context Variable Type Extension\n// ============================================================================\n\ndeclare module 'hono' {\n  interface ContextVariableMap {\n    rateLimit?: RateLimitInfo\n    rateLimitStore?: RateLimitStoreAccess\n  }\n}\n\n// ============================================================================\n// Singleton Default Store\n// ============================================================================\n\nlet defaultStore: MemoryStore | undefined\n\n/**\n * Shutdown the default memory store.\n * Call this during graceful shutdown to clean up timers.\n *\n * @example\n * ```ts\n * import { shutdownDefaultStore } from '@jfungus/ratelimit-hono'\n *\n * process.on('SIGTERM', () => {\n *   shutdownDefaultStore()\n *   process.exit(0)\n * })\n * ```\n */\nexport function shutdownDefaultStore(): void {\n  if (defaultStore) {\n    defaultStore.shutdown()\n    defaultStore = undefined\n  }\n}\n\n// ============================================================================\n// Header Generation\n// ============================================================================\n\n/**\n * Sanitize identifier for RFC 9651 structured field compliance.\n */\nfunction sanitizeIdentifier(id: string): string {\n  if (!id || typeof id !== 'string') {\n    return 'default'\n  }\n  // RFC 9651 tokens: Only allow alphanumeric, underscore, hyphen, dot, colon, asterisk, slash\n  // Must start with a letter\n  const sanitized = id.replace(/[^a-zA-Z0-9_\\-.:*/]/g, '-')\n  if (!sanitized || !/^[a-zA-Z]/.test(sanitized)) {\n    return 'default'\n  }\n  return sanitized\n}\n\n/**\n * Set rate limit response headers based on the configured format.\n */\nfunction setHeaders(\n  c: Context,\n  info: RateLimitInfo,\n  format: HeadersFormat,\n  windowMs: number,\n  identifier: string,\n  quotaUnit: QuotaUnit,\n): void {\n  if (format === false) {\n    return\n  }\n\n  const windowSeconds = Math.ceil(windowMs / 1000)\n  const resetSeconds = Math.max(0, Math.ceil((info.reset - Date.now()) / 1000))\n  const safeId = sanitizeIdentifier(identifier)\n\n  switch (format) {\n    case 'standard':\n      // IETF draft-08+ (current): Structured field values per RFC 9651\n      {\n        let policy = `\"${safeId}\";q=${info.limit};w=${windowSeconds}`\n        if (quotaUnit !== 'requests') {\n          policy += `;qu=\"${quotaUnit}\"`\n        }\n        c.header('RateLimit-Policy', policy)\n        c.header('RateLimit', `\"${safeId}\";r=${info.remaining};t=${resetSeconds}`)\n      }\n      break\n\n    case 'draft-7':\n      // IETF draft-07: Combined RateLimit header with comma-separated values\n      c.header('RateLimit-Policy', `${info.limit};w=${windowSeconds}`)\n      c.header(\n        'RateLimit',\n        `limit=${info.limit}, remaining=${info.remaining}, reset=${resetSeconds}`,\n      )\n      break\n\n    case 'draft-6':\n      // IETF draft-06: Individual RateLimit-* headers\n      c.header('RateLimit-Policy', `${info.limit};w=${windowSeconds}`)\n      c.header('RateLimit-Limit', String(info.limit))\n      c.header('RateLimit-Remaining', String(info.remaining))\n      c.header('RateLimit-Reset', String(resetSeconds))\n      break\n    default:\n      // Common X-RateLimit-* headers (GitHub, Twitter, most APIs)\n      // Uses Unix timestamp for reset (seconds since epoch)\n      c.header('X-RateLimit-Limit', String(info.limit))\n      c.header('X-RateLimit-Remaining', String(info.remaining))\n      c.header('X-RateLimit-Reset', String(Math.ceil(info.reset / 1000)))\n      break\n  }\n}\n\n/**\n * Build headers object for rate limit responses.\n */\nfunction buildRateLimitHeaders(\n  info: RateLimitInfo,\n  format: HeadersFormat,\n  windowMs: number,\n  identifier: string,\n  quotaUnit: QuotaUnit,\n): Record<string, string> {\n  const headers: Record<string, string> = {\n    'Content-Type': 'text/plain',\n    'Retry-After': String(Math.max(0, Math.ceil((info.reset - Date.now()) / 1000))),\n  }\n\n  if (format === false) {\n    return headers\n  }\n\n  const windowSeconds = Math.ceil(windowMs / 1000)\n  const resetSeconds = Math.max(0, Math.ceil((info.reset - Date.now()) / 1000))\n  const safeId = sanitizeIdentifier(identifier)\n\n  switch (format) {\n    case 'standard': {\n      let policy = `\"${safeId}\";q=${info.limit};w=${windowSeconds}`\n      if (quotaUnit !== 'requests') {\n        policy += `;qu=\"${quotaUnit}\"`\n      }\n      headers['RateLimit-Policy'] = policy\n      headers.RateLimit = `\"${safeId}\";r=${info.remaining};t=${resetSeconds}`\n      break\n    }\n    case 'draft-7':\n      headers['RateLimit-Policy'] = `${info.limit};w=${windowSeconds}`\n      headers.RateLimit = `limit=${info.limit}, remaining=${info.remaining}, reset=${resetSeconds}`\n      break\n    case 'draft-6':\n      headers['RateLimit-Policy'] = `${info.limit};w=${windowSeconds}`\n      headers['RateLimit-Limit'] = String(info.limit)\n      headers['RateLimit-Remaining'] = String(info.remaining)\n      headers['RateLimit-Reset'] = String(resetSeconds)\n      break\n    default:\n      headers['X-RateLimit-Limit'] = String(info.limit)\n      headers['X-RateLimit-Remaining'] = String(info.remaining)\n      headers['X-RateLimit-Reset'] = String(Math.ceil(info.reset / 1000))\n      break\n  }\n\n  return headers\n}\n\n// ============================================================================\n// Default Key Generator\n// ============================================================================\n\n/**\n * Extract client IP address from request headers.\n *\n * Checks headers in order of reliability:\n * 1. `CF-Connecting-IP` - Cloudflare's true client IP\n * 2. `X-Real-IP` - Common proxy header (nginx)\n * 3. `X-Forwarded-For` - Standard proxy header (first IP only)\n *\n * @param c - Hono context\n * @returns Client IP address or 'unknown' if not found\n *\n * @warning These headers can be spoofed. Only trust them behind a reverse proxy.\n */\nexport function getClientIP(c: Context): string {\n  // Platform-specific headers (most reliable)\n  const cfIP = c.req.header('cf-connecting-ip')\n  if (cfIP) {\n    return cfIP\n  }\n\n  const xRealIP = c.req.header('x-real-ip')\n  if (xRealIP) {\n    return xRealIP\n  }\n\n  // X-Forwarded-For - take first IP\n  const xff = c.req.header('x-forwarded-for')\n  if (xff) {\n    return xff.split(',')[0].trim()\n  }\n\n  return 'unknown'\n}\n\n// ============================================================================\n// Default Handler\n// ============================================================================\n\n/**\n * Create the default 429 response for rate-limited requests.\n */\nfunction createDefaultResponse(\n  info: RateLimitInfo,\n  format: HeadersFormat,\n  windowMs: number,\n  identifier: string,\n  quotaUnit: QuotaUnit,\n): Response {\n  const headers = buildRateLimitHeaders(info, format, windowMs, identifier, quotaUnit)\n\n  return new Response('Rate limit exceeded', {\n    status: 429,\n    headers,\n  })\n}\n\n// ============================================================================\n// Main Middleware\n// ============================================================================\n\n/**\n * Rate Limit Middleware for Hono.\n *\n * @param {RateLimitOptions} [options] - Configuration options\n * @returns {MiddlewareHandler} Middleware handler\n *\n * @example\n * ```ts\n * import { Hono } from 'hono'\n * import { rateLimiter } from '@jfungus/ratelimit-hono'\n *\n * const app = new Hono()\n *\n * // Basic usage - 60 requests per minute\n * app.use(rateLimiter())\n *\n * // Custom configuration\n * app.use('/api/*', rateLimiter({\n *   limit: 100,\n *   windowMs: 60 * 1000,\n * }))\n * ```\n */\nexport const rateLimiter = <E extends Env = Env>(\n  options?: RateLimitOptions<E>,\n): MiddlewareHandler<E> => {\n  // Merge with defaults\n  const opts = {\n    limit: 100 as number | ((c: Context<E>) => number | Promise<number>),\n    windowMs: 60_000,\n    algorithm: 'sliding-window' as Algorithm,\n    store: undefined as RateLimitStore | undefined,\n    keyGenerator: getClientIP as (c: Context<E>) => string | Promise<string>,\n    handler: undefined as\n      | ((c: Context<E>, info: RateLimitInfo) => Response | Promise<Response>)\n      | undefined,\n    headers: 'legacy' as HeadersFormat,\n    identifier: 'default',\n    quotaUnit: 'requests' as QuotaUnit,\n    skip: undefined as ((c: Context<E>) => boolean | Promise<boolean>) | undefined,\n    skipSuccessfulRequests: false,\n    skipFailedRequests: false,\n    onRateLimited: undefined as\n      | ((c: Context<E>, info: RateLimitInfo) => void | Promise<void>)\n      | undefined,\n    onStoreError: 'allow' as\n      | 'allow'\n      | 'deny'\n      | ((error: Error, c: Context<E>) => boolean | Promise<boolean>),\n    dryRun: false,\n    ...options,\n  }\n\n  // Validate configuration\n  if (typeof opts.limit === 'number' && opts.limit <= 0) {\n    throw new Error(`[@jfungus/ratelimit-hono] limit must be a positive number, got: ${opts.limit}`)\n  }\n  if (opts.windowMs <= 0) {\n    throw new Error(\n      `[@jfungus/ratelimit-hono] windowMs must be a positive number, got: ${opts.windowMs}`,\n    )\n  }\n\n  // Use default store if none provided\n  const store = opts.store ?? (defaultStore ??= new MemoryStore())\n\n  // Track initialization\n  let initPromise: Promise<void> | null = null\n\n  /**\n   * Handle store errors based on configuration.\n   * @returns true to allow request, false to deny\n   */\n  async function handleStoreError(error: Error, c: Context<E>): Promise<boolean> {\n    if (typeof opts.onStoreError === 'function') {\n      return opts.onStoreError(error, c)\n    }\n    // Default: fail-open (allow request through)\n    return opts.onStoreError === 'allow'\n  }\n\n  return async function rateLimiterMiddleware(c, next) {\n    // Initialize store on first request (with proper locking)\n    if (!initPromise && store.init) {\n      const result = store.init(opts.windowMs)\n      // Handle both sync and async init\n      initPromise = result instanceof Promise ? result : Promise.resolve()\n    }\n    if (initPromise) {\n      try {\n        await initPromise\n      } catch (error) {\n        const shouldAllow = await handleStoreError(\n          error instanceof Error ? error : new Error(String(error)),\n          c,\n        )\n        if (shouldAllow) {\n          return next()\n        }\n        return new Response('Rate limiter initialization failed', {\n          status: 500,\n        })\n      }\n    }\n\n    // Check if should skip\n    if (opts.skip) {\n      const shouldSkip = await opts.skip(c)\n      if (shouldSkip) {\n        return next()\n      }\n    }\n\n    // Generate key\n    const key = await opts.keyGenerator(c)\n\n    // Get limit (may be dynamic)\n    const limit = typeof opts.limit === 'function' ? await opts.limit(c) : opts.limit\n\n    // Check rate limit with error handling\n    let allowed: boolean\n    let info: RateLimitInfo\n\n    try {\n      const result = await checkRateLimit({\n        store,\n        key,\n        limit,\n        windowMs: opts.windowMs,\n        algorithm: opts.algorithm,\n      })\n      allowed = result.allowed\n      info = result.info\n    } catch (error) {\n      const shouldAllow = await handleStoreError(\n        error instanceof Error ? error : new Error(String(error)),\n        c,\n      )\n      if (shouldAllow) {\n        return next()\n      }\n      return new Response('Rate limiter error', { status: 500 })\n    }\n\n    // Set context variable for downstream middleware\n    c.set('rateLimit', info)\n\n    // Expose store access in context\n    c.set('rateLimitStore', {\n      getKey: store.get?.bind(store) ?? (() => undefined),\n      resetKey: store.resetKey.bind(store),\n      resetAll: store.resetAll?.bind(store),\n    })\n\n    // Set headers\n    setHeaders(c, info, opts.headers, opts.windowMs, opts.identifier, opts.quotaUnit)\n\n    // Handle rate limited\n    if (!allowed) {\n      // Fire callback (always, even in dry-run mode for monitoring)\n      if (opts.onRateLimited) {\n        await opts.onRateLimited(c, info)\n      }\n\n      // In dry-run mode, allow the request through but still set headers\n      if (!opts.dryRun) {\n        // Custom handler or default\n        if (opts.handler) {\n          return opts.handler(c, info)\n        }\n        return createDefaultResponse(\n          info,\n          opts.headers,\n          opts.windowMs,\n          opts.identifier,\n          opts.quotaUnit,\n        )\n      }\n    }\n\n    // Capture the window key BEFORE calling next() to ensure we decrement\n    // the same window we incremented, even if next() takes a long time\n    let windowKeyForDecrement: string | undefined\n    if (opts.skipSuccessfulRequests || opts.skipFailedRequests) {\n      const windowStart = Math.floor(Date.now() / opts.windowMs) * opts.windowMs\n      windowKeyForDecrement = `${key}:${windowStart}`\n    }\n\n    // Continue\n    await next()\n\n    // Handle skip options after response\n    if (windowKeyForDecrement && store.decrement) {\n      const status = c.res.status\n      const shouldDecrement =\n        (opts.skipSuccessfulRequests && status >= 200 && status < 300) ||\n        (opts.skipFailedRequests && status >= 400)\n\n      if (shouldDecrement) {\n        try {\n          await store.decrement(windowKeyForDecrement)\n        } catch {\n          // Ignore decrement errors - request already processed\n        }\n      }\n    }\n  }\n}\n\n// ============================================================================\n// Cloudflare Rate Limiting Binding Middleware\n// ============================================================================\n\n/**\n * Rate limiter using Cloudflare's built-in Rate Limiting binding.\n *\n * This uses Cloudflare's globally distributed rate limiting infrastructure,\n * which is ideal for high-traffic applications.\n *\n * @example\n * ```ts\n * import { cloudflareRateLimiter } from '@jfungus/ratelimit-hono'\n *\n * type Bindings = { RATE_LIMITER: RateLimitBinding }\n *\n * const app = new Hono<{ Bindings: Bindings }>()\n *\n * app.use(cloudflareRateLimiter({\n *   binding: (c) => c.env.RATE_LIMITER,\n *   keyGenerator: (c) => c.req.header('cf-connecting-ip') ?? 'unknown',\n * }))\n * ```\n */\nexport const cloudflareRateLimiter = <E extends Env = Env>(\n  options: CloudflareRateLimitOptions<E>,\n): MiddlewareHandler<E> => {\n  const { binding, keyGenerator, handler, skip } = options\n\n  return async function cloudflareRateLimiterMiddleware(c, next) {\n    // Check if should skip\n    if (skip) {\n      const shouldSkip = await skip(c)\n      if (shouldSkip) {\n        return next()\n      }\n    }\n\n    // Get binding (may be dynamic)\n    const rateLimitBinding = typeof binding === 'function' ? binding(c) : binding\n\n    // Generate key\n    const key = await keyGenerator(c)\n\n    // Check rate limit\n    const { success } = await rateLimitBinding.limit({ key })\n\n    if (!success) {\n      if (handler) {\n        return handler(c)\n      }\n      return new Response('Rate limit exceeded', {\n        status: 429,\n        headers: { 'Content-Type': 'text/plain' },\n      })\n    }\n\n    return next()\n  }\n}\n"],"mappings":"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","names":["import_ratelimit"]}