{"version":3,"sources":["../../../src/stores/MemoryStore.ts","../../../src/core/blocking.ts","../../../src/utils/helpers.ts","../../../src/core/RateLimiter.ts","../../../src/adapters/fastify/helpers.ts","../../../src/adapters/fastify/middleware.ts","../../../src/adapters/fastify/factories.ts"],"names":["createRateLimiter"],"mappings":"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","file":"index.mjs","sourcesContent":["import type { BlockRecord, RateLimitStore, StoreEntry } from './types'\n\nexport interface MemoryStoreOptions {\n  /**\n   * Maximum unique keys held in memory at once.\n   * When the cap is reached the oldest entry is evicted (LRU-style).\n   * Set to `0` to disable the cap.\n   * @default 10_000\n   */\n  maxKeys?: number\n}\n\n/**\n * Default in-process storage backend.\n *\n * Uses two plain Maps — one for rate-limit trackers, one for block records.\n * Map insertion order provides free LRU eviction: the first key is always\n * the oldest when we need to make room for a new one.\n *\n * Not suitable for multi-process deployments; implement RateLimitStore with\n * a shared backend (Redis, Valkey, …) for that case.\n */\nexport class MemoryStore implements RateLimitStore {\n  private readonly entries = new Map<string, StoreEntry>()\n  private readonly blocks = new Map<string, BlockRecord>()\n  private readonly maxKeys: number\n\n  constructor({ maxKeys = 10_000 }: MemoryStoreOptions = {}) {\n    this.maxKeys = maxKeys\n  }\n\n  // ── Tracker ─────────────────────────────────────────────────────────────────\n\n  getEntry(key: string): StoreEntry | null {\n    return this.entries.get(key) ?? null\n  }\n\n  setEntry(key: string, entry: StoreEntry): void {\n    this.evictIfFull(this.entries)\n    this.entries.set(key, entry)\n  }\n\n  deleteEntry(key: string): void {\n    this.entries.delete(key)\n  }\n\n  // ── Block ────────────────────────────────────────────────────────────────────\n\n  getBlock(key: string): BlockRecord | null {\n    return this.blocks.get(key) ?? null\n  }\n\n  setBlock(key: string, record: BlockRecord): void {\n    this.blocks.set(key, record)\n  }\n\n  deleteBlock(key: string): void {\n    this.blocks.delete(key)\n  }\n\n  // ── Bulk / lifecycle ─────────────────────────────────────────────────────────\n\n  resetKey(key: string): void {\n    this.entries.delete(key)\n    this.blocks.delete(key)\n  }\n\n  resetAll(): void {\n    this.entries.clear()\n    this.blocks.clear()\n  }\n\n  entryCount(): number {\n    return this.entries.size\n  }\n  blockedCount(): number {\n    return this.blocks.size\n  }\n\n  cleanup(windowMs: number): void {\n    const now = Date.now()\n\n    for (const [key, entry] of this.entries) {\n      if (entry.type === 'fixed') {\n        if (entry.resetTime <= now) this.entries.delete(key)\n      } else {\n        const fresh = entry.timestamps.filter(ts => ts > now - windowMs)\n        if (fresh.length === 0) {\n          this.entries.delete(key)\n        } else {\n          entry.timestamps = fresh\n        }\n      }\n    }\n\n    for (const [key, block] of this.blocks) {\n      if (block.unblockTime <= now) this.blocks.delete(key)\n    }\n  }\n\n  destroy(): void {\n    this.entries.clear()\n    this.blocks.clear()\n  }\n\n  // ── Private ──────────────────────────────────────────────────────────────────\n\n  private evictIfFull(map: Map<string, unknown>): void {\n    if (this.maxKeys > 0 && map.size >= this.maxKeys) {\n      const oldest = map.keys().next().value as string | undefined\n      if (oldest !== undefined) map.delete(oldest)\n    }\n  }\n}\n","import type { BlockRecord } from '../stores/types'\n\n/**\n * Progressive block-duration escalation based on violation history in the\n * last 30 days:\n *\n * | Recent offenses                  | Block duration |\n * |----------------------------------|----------------|\n * | ≥ 3 month-length blocks          | 1 year         |\n * | ≥ 3 week-length blocks           | 30 days        |\n * | ≥ 3 day-length blocks            | 7 days         |\n * | ≥ 5 short blocks                 | 1 day          |\n * | otherwise                        | initialBlockMs |\n */\nexport const calculateBlockDuration = (\n  history: BlockRecord['blockHistory'],\n  initialBlockMs: number\n): number => {\n  const cutoff = Date.now() - 30 * 24 * 60 * 60_000\n  const recent = history.filter(b => b.timestamp > cutoff)\n\n  if (recent.length === 0) return initialBlockMs\n\n  const tally = { initial: 0, day: 0, week: 0, month: 0 }\n\n  for (const b of recent) {\n    if (b.duration >= 30 * 24 * 60 * 60_000) tally.month++\n    else if (b.duration >= 7 * 24 * 60 * 60_000) tally.week++\n    else if (b.duration >= 24 * 60 * 60_000) tally.day++\n    else tally.initial++\n  }\n\n  if (tally.month >= 3) return 365 * 24 * 60 * 60_000\n  if (tally.week >= 3) return 30 * 24 * 60 * 60_000\n  if (tally.day >= 3) return 7 * 24 * 60 * 60_000\n  if (tally.initial >= 5) return 24 * 60 * 60_000\n  return initialBlockMs\n}\n","// ── Formatting ─────────────────────────────────────────────────────────────────\n\nexport const formatDuration = (ms: number): string => {\n  const mins = Math.floor(ms / 60_000)\n  const hrs = Math.floor(mins / 60)\n  const days = Math.floor(hrs / 24)\n  if (days >= 365) return '1 year'\n  if (days >= 30) return '30 days'\n  if (days >= 7) return '7 days'\n  if (days >= 1) return `${days} day${days > 1 ? 's' : ''}`\n  if (hrs >= 1) return `${hrs} hour${hrs > 1 ? 's' : ''}`\n  return `${mins} minute${mins !== 1 ? 's' : ''}`\n}\n\n// ── Safe callback execution ────────────────────────────────────────────────────\n\n/** Runs a sync callback; swallows and logs any error so it never crashes the middleware. */\nexport const safeExec = (label: string, fn: () => void): void => {\n  try {\n    fn()\n  } catch (err) {\n    console.error(`[rate-shield] Unhandled error in ${label}:`, err)\n  }\n}\n\n/** Async variant of safeExec. */\nexport const safeExecAsync = async (\n  label: string,\n  fn: () => Promise<void> | void\n): Promise<void> => {\n  try {\n    await fn()\n  } catch (err) {\n    console.error(`[rate-shield] Unhandled error in ${label}:`, err)\n  }\n}\n","/* eslint-disable no-console */\nimport type { RateShieldContext } from '../context'\nimport type {\n  BlockRecord,\n  FixedWindowEntry,\n  SlidingWindowEntry\n} from '../stores/types'\nimport { MemoryStore } from '../stores/MemoryStore'\nimport type {\n  AllowedEvent,\n  BlockedEvent,\n  LimitReachedEvent,\n  RateLimitInfo,\n  RateLimitOptions,\n  RateLimitStats,\n  RateLimiterCore,\n  RecordData\n} from '../types'\nimport { calculateBlockDuration } from './blocking'\nimport { formatDuration, safeExecAsync } from '../utils/helpers'\n\n// ── Internal result type ───────────────────────────────────────────────────────\n\ninterface BlockResult {\n  unblockTime: number\n  blockCount: number\n  duration: number\n}\n\n// ── Factory ────────────────────────────────────────────────────────────────────\n\nexport const createRateLimiter = <\n  TReq extends object = Record<string, unknown>\n>(\n  options: RateLimitOptions<TReq> = {}\n): RateLimiterCore<TReq> => {\n  const {\n    strategy = 'sliding',\n    windowMs = 60_000,\n    maxRequests = 15,\n    initialBlockMs = 20 * 60_000,\n    message = {\n      success: false,\n      message: 'Too many requests. You are temporarily blocked.'\n    },\n    keyGenerator = () => 'unknown',\n    enableLogger = false,\n    skip = () => false,\n    maxKeys = 10_000,\n    whitelist,\n    dryRun = false,\n    headers = true,\n    store: customStore,\n    onRecord,\n    extractRequestData,\n    onBlocked,\n    onLimitReached,\n    onRequestAllowed\n  } = options\n\n  const store = customStore ?? new MemoryStore({ maxKeys })\n\n  // Prevents double-counting when the same instance is mounted at multiple\n  // router levels (e.g. app.use('/api', limiter) + router.use(limiter)).\n  const checkedRequests = new WeakMap<object, true>()\n\n  let destroyed = false\n\n  const log = (...args: unknown[]): void => {\n    if (enableLogger) console.log(`[rate-shield:${strategy}]`, ...args)\n  }\n\n  // ── Whitelist ──────────────────────────────────────────────────────────────\n\n  const isWhitelisted = (key: string, req: TReq): boolean => {\n    if (!whitelist) return false\n    if (Array.isArray(whitelist)) return whitelist.includes(key)\n    try {\n      return whitelist(key, req)\n    } catch {\n      return false\n    }\n  }\n\n  // ── Response helpers ───────────────────────────────────────────────────────\n\n  const buildPayload = (durationMs: number): unknown => {\n    if (typeof message === 'string') return message\n    return {\n      ...message,\n      message: `Too many requests. You are blocked for ${formatDuration(durationMs)}.`,\n      retryAfter: Math.ceil(durationMs / 1000)\n    }\n  }\n\n  const writeHeaders = (\n    ctx: RateShieldContext<TReq>,\n    remaining: number,\n    resetEpochSec: number,\n    retryAfterSec?: number\n  ): void => {\n    if (!headers || ctx.isResponseSent()) return\n    ctx.setHeader('X-RateLimit-Limit', maxRequests)\n    ctx.setHeader('X-RateLimit-Remaining', Math.max(0, remaining))\n    ctx.setHeader('X-RateLimit-Reset', resetEpochSec)\n    ctx.setHeader('X-RateLimit-Window', Math.ceil(windowMs / 1000))\n    ctx.setHeader('X-RateLimit-Strategy', strategy)\n    if (dryRun) ctx.setHeader('X-RateLimit-DryRun', 'true')\n    if (retryAfterSec !== undefined)\n      ctx.setHeader('Retry-After', Math.ceil(retryAfterSec))\n  }\n\n  // ── Block enforcement ──────────────────────────────────────────────────────\n\n  const enforceBlock = async (\n    ctx: RateShieldContext<TReq>,\n    key: string,\n    now: number,\n    existingBlock: BlockRecord | null\n  ): Promise<BlockResult> => {\n    const history = [...(existingBlock?.blockHistory ?? [])]\n    const duration = calculateBlockDuration(history, initialBlockMs)\n    const unblockTime = now + duration\n    const blockCount = (existingBlock?.blockCount ?? 0) + 1\n\n    history.push({ duration, timestamp: now })\n    // Keep only the last 30 days of violations to bound store growth.\n    const trimmedHistory = history.filter(\n      b => now - b.timestamp < 30 * 24 * 60 * 60_000\n    )\n\n    await store.setBlock(key, {\n      unblockTime,\n      blockCount,\n      lastBlockDuration: duration,\n      blockHistory: trimmedHistory\n    })\n    // Remove tracker so a fresh window begins after unblock.\n    await store.deleteEntry(key)\n\n    log(\n      `BLOCKED \"${key}\" for ${formatDuration(duration)} (offense #${trimmedHistory.length})`\n    )\n\n    if (onBlocked) {\n      await safeExecAsync('onBlocked', () =>\n        onBlocked!({\n          key,\n          req: ctx.req,\n          blockDuration: duration,\n          blockCount,\n          unblockTime\n        } as BlockedEvent<TReq>)\n      )\n    }\n\n    const retryAfterSec = Math.ceil(duration / 1000)\n    writeHeaders(ctx, 0, Math.ceil(unblockTime / 1000), retryAfterSec)\n\n    if (dryRun) {\n      log(`[DryRun] Block recorded for \"${key}\" — request allowed through.`)\n    } else {\n      if (!ctx.isResponseSent()) ctx.block(429, buildPayload(duration))\n    }\n\n    return { unblockTime, blockCount, duration }\n  }\n\n  // ── onRecord helpers ───────────────────────────────────────────────────────\n\n  const emitRecord = async (data: RecordData<TReq>): Promise<void> => {\n    if (!onRecord) return\n    await safeExecAsync('onRecord', () => onRecord!(data))\n  }\n\n  // With exactOptionalPropertyTypes, we must omit `requestData` entirely rather\n  // than set it to `undefined` when extractRequestData is not configured.\n  const withRequestData = (\n    base: Omit<RecordData<TReq>, 'requestData'>\n  ): RecordData<TReq> =>\n    extractRequestData\n      ? { ...base, requestData: extractRequestData(base.req) }\n      : (base as RecordData<TReq>)\n\n  // ── Strategy: fixed window ─────────────────────────────────────────────────\n\n  const handleFixed = async (\n    key: string,\n    now: number,\n    ctx: RateShieldContext<TReq>\n  ): Promise<void> => {\n    const raw = await store.getEntry(key)\n    let entry: FixedWindowEntry\n\n    if (!raw || raw.type !== 'fixed' || raw.resetTime <= now) {\n      entry = { type: 'fixed', count: 0, resetTime: now + windowMs }\n      log(`New fixed window for \"${key}\"`)\n    } else {\n      entry = raw\n    }\n\n    entry.count++\n    await store.setEntry(key, entry)\n\n    const remaining = maxRequests - entry.count\n    log(`[Fixed] \"${key}\": ${entry.count}/${maxRequests}`)\n\n    writeHeaders(ctx, remaining, Math.ceil(entry.resetTime / 1000))\n\n    if (entry.count > maxRequests) {\n      const existingBlock = await store.getBlock(key)\n      const result = await enforceBlock(ctx, key, now, existingBlock)\n\n      await emitRecord(\n        withRequestData({\n          key,\n          req: ctx.req,\n          strategy,\n          count: entry.count,\n          remaining: 0,\n          resetTime: result.unblockTime,\n          isBlocked: true,\n          dryRunBlocked: dryRun,\n          blockCount: result.blockCount,\n          blockExpiresAt: result.unblockTime,\n          timestamp: now\n        })\n      )\n\n      if (!dryRun) return\n      return ctx.pass()\n    }\n\n    if (onRequestAllowed) {\n      await safeExecAsync('onRequestAllowed', () =>\n        onRequestAllowed!({\n          key,\n          req: ctx.req,\n          count: entry.count,\n          remaining: Math.max(0, remaining)\n        } as AllowedEvent<TReq>)\n      )\n    }\n\n    // remaining === 0 → this was the last slot; the next request will block.\n    if (onLimitReached && remaining === 0) {\n      await safeExecAsync('onLimitReached', () =>\n        onLimitReached!({\n          key,\n          req: ctx.req,\n          count: entry.count,\n          limit: maxRequests\n        } as LimitReachedEvent<TReq>)\n      )\n    }\n\n    await emitRecord(\n      withRequestData({\n        key,\n        req: ctx.req,\n        strategy,\n        count: entry.count,\n        remaining: Math.max(0, remaining),\n        resetTime: entry.resetTime,\n        isBlocked: false,\n        dryRunBlocked: false,\n        timestamp: now\n      })\n    )\n\n    ctx.pass()\n  }\n\n  // ── Strategy: sliding window ───────────────────────────────────────────────\n\n  const handleSliding = async (\n    key: string,\n    now: number,\n    ctx: RateShieldContext<TReq>\n  ): Promise<void> => {\n    const raw = await store.getEntry(key)\n    let entry: SlidingWindowEntry\n\n    if (!raw || raw.type !== 'sliding') {\n      entry = { type: 'sliding', timestamps: [] }\n    } else {\n      entry = raw\n    }\n\n    // Slide: discard timestamps outside the rolling window.\n    entry.timestamps = entry.timestamps.filter(ts => ts > now - windowMs)\n\n    const count = entry.timestamps.length\n    const remaining = maxRequests - count\n    const resetTime =\n      count > 0 ? entry.timestamps[0]! + windowMs : now + windowMs\n\n    log(`[Sliding] \"${key}\": ${count}/${maxRequests} in last ${windowMs}ms`)\n\n    writeHeaders(ctx, remaining, Math.ceil(resetTime / 1000))\n\n    if (count >= maxRequests) {\n      const existingBlock = await store.getBlock(key)\n      const result = await enforceBlock(ctx, key, now, existingBlock)\n\n      await emitRecord(\n        withRequestData({\n          key,\n          req: ctx.req,\n          strategy,\n          count,\n          remaining: 0,\n          resetTime: result.unblockTime,\n          isBlocked: true,\n          dryRunBlocked: dryRun,\n          blockCount: result.blockCount,\n          blockExpiresAt: result.unblockTime,\n          timestamp: now\n        })\n      )\n\n      if (!dryRun) return\n      return ctx.pass()\n    }\n\n    entry.timestamps.push(now)\n    await store.setEntry(key, entry)\n\n    if (onRequestAllowed) {\n      await safeExecAsync('onRequestAllowed', () =>\n        onRequestAllowed!({\n          key,\n          req: ctx.req,\n          count: count + 1,\n          remaining: Math.max(0, remaining - 1)\n        } as AllowedEvent<TReq>)\n      )\n    }\n\n    // remaining === 1 before push → 0 after → next request blocks.\n    if (onLimitReached && remaining === 1) {\n      await safeExecAsync('onLimitReached', () =>\n        onLimitReached!({\n          key,\n          req: ctx.req,\n          count: count + 1,\n          limit: maxRequests\n        } as LimitReachedEvent<TReq>)\n      )\n    }\n\n    await emitRecord(\n      withRequestData({\n        key,\n        req: ctx.req,\n        strategy,\n        count: count + 1,\n        remaining: Math.max(0, remaining - 1),\n        resetTime,\n        isBlocked: false,\n        dryRunBlocked: false,\n        timestamp: now\n      })\n    )\n\n    ctx.pass()\n  }\n\n  // ── Cleanup timer ──────────────────────────────────────────────────────────\n\n  const runCleanup = (): void => {\n    if (destroyed) return\n    void (async () => {\n      try {\n        await store.cleanup?.(windowMs)\n        log('Cleanup complete.')\n      } catch (err) {\n        console.error('[rate-shield] Cleanup error:', err)\n      }\n    })()\n  }\n\n  const cleanupInterval = setInterval(runCleanup, 2 * 60_000)\n  // .unref() prevents the interval from keeping the Node.js event loop alive,\n  // which would cause Jest / Vitest to hang after tests complete.\n  if (typeof cleanupInterval.unref === 'function') cleanupInterval.unref()\n\n  const onProcessExit = (): void => clearInterval(cleanupInterval)\n  process.once('exit', onProcessExit)\n\n  // ── Core instance ──────────────────────────────────────────────────────────\n\n  return {\n    async handle(ctx: RateShieldContext<TReq>): Promise<void> {\n      if (destroyed) {\n        console.warn(\n          '[rate-shield] This instance has been destroyed — call createRateLimiter() to get a new one.'\n        )\n        return ctx.pass()\n      }\n\n      let shouldSkip = false\n      try {\n        shouldSkip = await skip(ctx.req)\n      } catch (err) {\n        console.error('[rate-shield] Error in skip():', err)\n      }\n      if (shouldSkip) return ctx.pass()\n\n      if (checkedRequests.has(ctx.req)) {\n        log('Duplicate check on same request — skipping.')\n        return ctx.pass()\n      }\n      checkedRequests.set(ctx.req, true)\n\n      let key = 'unknown'\n      try {\n        key = keyGenerator(ctx.req)\n      } catch (err) {\n        console.error('[rate-shield] Error in keyGenerator():', err)\n      }\n\n      const now = Date.now()\n      log(`Request from \"${key}\"`)\n\n      if (isWhitelisted(key, ctx.req)) {\n        log(`\"${key}\" is whitelisted — bypassing.`)\n        return ctx.pass()\n      }\n\n      // ── Active block check ────────────────────────────────────────────────\n      const blockRecord = await store.getBlock(key)\n\n      if (blockRecord && blockRecord.unblockTime > now) {\n        const remainingMs = blockRecord.unblockTime - now\n        log(`\"${key}\" is blocked — ${formatDuration(remainingMs)} remaining.`)\n\n        await emitRecord(\n          withRequestData({\n            key,\n            req: ctx.req,\n            strategy,\n            count: maxRequests,\n            remaining: 0,\n            resetTime: blockRecord.unblockTime,\n            isBlocked: true,\n            dryRunBlocked: dryRun,\n            blockCount: blockRecord.blockCount,\n            blockExpiresAt: blockRecord.unblockTime,\n            timestamp: now\n          })\n        )\n\n        if (dryRun) {\n          log(`[DryRun] Would reject \"${key}\" — allowing through.`)\n          return ctx.pass()\n        }\n\n        writeHeaders(\n          ctx,\n          0,\n          Math.ceil(blockRecord.unblockTime / 1000),\n          Math.ceil(remainingMs / 1000)\n        )\n        if (!ctx.isResponseSent()) ctx.block(429, buildPayload(remainingMs))\n        return\n      }\n\n      if (blockRecord) {\n        await store.deleteBlock(key)\n        log(`Block expired — \"${key}\" is now unblocked.`)\n      }\n\n      if (strategy === 'sliding') {\n        return handleSliding(key, now, ctx)\n      }\n      return handleFixed(key, now, ctx)\n    },\n\n    async destroy(): Promise<void> {\n      if (destroyed) return\n      destroyed = true\n      clearInterval(cleanupInterval)\n      process.removeListener('exit', onProcessExit)\n      await store.destroy?.()\n      log('Instance destroyed — all state cleared.')\n    },\n\n    async reset(key: string): Promise<void> {\n      await store.resetKey(key)\n      log(`State reset for \"${key}\".`)\n    },\n\n    async resetAll(): Promise<void> {\n      await store.resetAll()\n      log('All state cleared.')\n    },\n\n    async getInfo(key: string): Promise<RateLimitInfo | null> {\n      const now = Date.now()\n      const block = await store.getBlock(key)\n\n      if (block && block.unblockTime > now) {\n        return {\n          key,\n          strategy,\n          count: maxRequests,\n          remaining: 0,\n          resetTime: block.unblockTime,\n          isBlocked: true,\n          blockExpiresAt: block.unblockTime,\n          blockCount: block.blockCount\n        }\n      }\n\n      const entry = await store.getEntry(key)\n      if (!entry) return null\n\n      if (entry.type === 'fixed') {\n        return {\n          key,\n          strategy,\n          count: entry.count,\n          remaining: Math.max(0, maxRequests - entry.count),\n          resetTime: entry.resetTime,\n          isBlocked: false\n        }\n      }\n\n      const fresh = entry.timestamps.filter(ts => ts > now - windowMs)\n      return {\n        key,\n        strategy,\n        count: fresh.length,\n        remaining: Math.max(0, maxRequests - fresh.length),\n        resetTime: fresh.length > 0 ? fresh[0]! + windowMs : now + windowMs,\n        isBlocked: false\n      }\n    },\n\n    async getStats(): Promise<RateLimitStats> {\n      return {\n        strategy,\n        trackedKeys: await store.entryCount(),\n        blockedKeys: await store.blockedCount(),\n        windowMs,\n        maxRequests\n      }\n    }\n  }\n}\n","import type { FastifyRequest } from 'fastify'\n\n/**\n * Default key generator for Fastify.\n * Respects `trustProxy` config — when enabled, `request.ip` already\n * reflects the real client IP from X-Forwarded-For.\n * Falls back to raw X-Forwarded-For header when trustProxy is off.\n */\nexport const extractIp = (req: FastifyRequest): string => {\n  const fwd = req.headers['x-forwarded-for']\n  if (typeof fwd === 'string') return fwd.split(',')[0]!.trim()\n  if (Array.isArray(fwd) && fwd.length > 0) return fwd[0]!.trim()\n  return req.ip ?? req.socket.remoteAddress ?? 'unknown'\n}\n","/* eslint-disable no-console */\nimport type { FastifyReply, FastifyRequest } from 'fastify'\nimport { createRateLimiter as createCore } from '../../core/RateLimiter'\nimport type { RateShieldContext } from '../../context'\nimport type {\n  RateLimitInfo,\n  RateLimitOptions,\n  RateLimitStats\n} from '../../types'\nimport { extractIp } from './helpers'\n\n// ── Fastify-specific instance type ─────────────────────────────────────────────\n\n/**\n * A rate-limiter instance that can be registered directly as a Fastify\n * `onRequest` hook:\n *\n * ```ts\n * const limiter = createRateLimiter({ maxRequests: 100 })\n * app.addHook('onRequest', limiter)\n * // or on a single route:\n * app.get('/api/data', { onRequest: limiter }, handler)\n * ```\n */\nexport interface FastifyRateLimiterInstance {\n  (request: FastifyRequest, reply: FastifyReply): Promise<void>\n  destroy(): void | Promise<void>\n  reset(key: string): void | Promise<void>\n  resetAll(): void | Promise<void>\n  getInfo(key: string): Promise<RateLimitInfo | null>\n  getStats(): Promise<RateLimitStats>\n}\n\n// ── Fastify factory ────────────────────────────────────────────────────────────\n\n/**\n * Create a rate-limiter instance that works as a Fastify `onRequest` hook.\n *\n * @example\n * ```ts\n * import Fastify from 'fastify'\n * import { createRateLimiter } from 'rate-shield/fastify'\n *\n * const app    = Fastify({ trustProxy: true })\n * const limiter = createRateLimiter({ maxRequests: 100, windowMs: 60_000 })\n *\n * app.addHook('onRequest', limiter)\n * ```\n */\nexport const createRateLimiter = (\n  options: RateLimitOptions<FastifyRequest> = {}\n): FastifyRateLimiterInstance => {\n  const core = createCore<FastifyRequest>({\n    keyGenerator: extractIp,\n    ...options\n  })\n\n  const hook = async (\n    request: FastifyRequest,\n    reply: FastifyReply\n  ): Promise<void> => {\n    const ctx: RateShieldContext<FastifyRequest> = {\n      req: request,\n      setHeader: (name, value) => {\n        if (!reply.sent) reply.header(name, String(value))\n      },\n      isResponseSent: () => reply.sent,\n      block: (_statusCode, body) => {\n        if (!reply.sent) void reply.status(429).send(body)\n      },\n      // Fastify hooks continue automatically when the async function returns\n      // without sending a response — no explicit pass call needed.\n      pass: () => {}\n    }\n\n    await core.handle(ctx)\n  }\n\n  return Object.assign(hook, {\n    destroy: () => core.destroy(),\n    reset: (key: string) => core.reset(key),\n    resetAll: () => core.resetAll(),\n    getInfo: (key: string) => core.getInfo(key),\n    getStats: () => core.getStats()\n  }) as FastifyRateLimiterInstance\n}\n","import type { FastifyRequest } from 'fastify'\nimport { extractIp } from './helpers'\nimport { createRateLimiter } from './middleware'\nimport type { FastifyRateLimiterInstance } from './middleware'\nimport type { RateLimitOptions } from '../../types'\n\n// ── Strategy aliases ───────────────────────────────────────────────────────────\n\nexport const createFixedRateLimiter = (\n  options: Omit<RateLimitOptions<FastifyRequest>, 'strategy'> = {}\n): FastifyRateLimiterInstance =>\n  createRateLimiter({ ...options, strategy: 'fixed' })\n\nexport const createSlidingRateLimiter = (\n  options: Omit<RateLimitOptions<FastifyRequest>, 'strategy'> = {}\n): FastifyRateLimiterInstance =>\n  createRateLimiter({ ...options, strategy: 'sliding' })\n\n// ── Endpoint-scoped factories ──────────────────────────────────────────────────\n\n/**\n * Limiter whose key is prefixed with the endpoint name for per-route isolation.\n *\n * @example\n * ```ts\n * app.post('/api/login', {\n *   onRequest: createEndpointRateLimiter('/api/login', { maxRequests: 5 })\n * }, loginHandler)\n * ```\n */\nexport const createEndpointRateLimiter = (\n  endpoint: string,\n  options: RateLimitOptions<FastifyRequest> = {}\n): FastifyRateLimiterInstance =>\n  createRateLimiter({\n    ...options,\n    keyGenerator: (req: FastifyRequest) => {\n      const base = options.keyGenerator\n        ? options.keyGenerator(req)\n        : extractIp(req)\n      return `${endpoint}:${base}`\n    }\n  })\n\nexport const createSlidingEndpointRateLimiter = (\n  endpoint: string,\n  options: Omit<RateLimitOptions<FastifyRequest>, 'strategy'> = {}\n): FastifyRateLimiterInstance =>\n  createRateLimiter({\n    ...options,\n    strategy: 'sliding',\n    keyGenerator: (req: FastifyRequest) => {\n      const base = options.keyGenerator\n        ? options.keyGenerator(req)\n        : extractIp(req)\n      return `${endpoint}:${base}`\n    }\n  })\n"]}