nextjs-secure 0.1.1 → 0.2.0

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/errors.ts","../src/utils/time.ts","../src/utils/ip.ts","../src/middleware/rate-limit/stores/memory.ts","../src/middleware/rate-limit/algorithms/sliding-window.ts","../src/middleware/rate-limit/algorithms/fixed-window.ts","../src/middleware/rate-limit/algorithms/token-bucket.ts","../src/middleware/rate-limit/middleware.ts","../src/index.ts"],"names":["response","info"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Custom error classes for next-secure\n */\n\n/**\n * Base error class for all next-secure errors\n */\nexport class SecureError extends Error {\n /**\n * HTTP status code\n */\n public readonly statusCode: number\n\n /**\n * Error code for programmatic handling\n */\n public readonly code: string\n\n /**\n * Additional error details\n */\n public readonly details?: Record<string, unknown>\n\n constructor(\n message: string,\n options: {\n statusCode?: number\n code?: string\n details?: Record<string, unknown>\n cause?: Error\n } = {}\n ) {\n super(message, { cause: options.cause })\n this.name = 'SecureError'\n this.statusCode = options.statusCode ?? 500\n this.code = options.code ?? 'SECURE_ERROR'\n this.details = options.details\n\n // Maintains proper stack trace for where error was thrown\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor)\n }\n }\n\n /**\n * Convert error to JSON response\n */\n toJSON(): Record<string, unknown> {\n return {\n error: this.name,\n message: this.message,\n code: this.code,\n ...(this.details && { details: this.details }),\n }\n }\n\n /**\n * Create a Response object from this error\n */\n toResponse(headers?: HeadersInit): Response {\n return new Response(JSON.stringify(this.toJSON()), {\n status: this.statusCode,\n headers: {\n 'Content-Type': 'application/json',\n ...headers,\n },\n })\n }\n}\n\n/**\n * Rate limit exceeded error\n */\nexport class RateLimitError extends SecureError {\n /**\n * Seconds until rate limit resets\n */\n public readonly retryAfter: number\n\n /**\n * Unix timestamp when limit resets\n */\n public readonly resetAt: number\n\n constructor(\n options: {\n retryAfter: number\n resetAt: number\n message?: string\n details?: Record<string, unknown>\n }\n ) {\n super(options.message ?? 'Too Many Requests', {\n statusCode: 429,\n code: 'RATE_LIMIT_EXCEEDED',\n details: options.details,\n })\n this.name = 'RateLimitError'\n this.retryAfter = options.retryAfter\n this.resetAt = options.resetAt\n }\n\n override toJSON(): Record<string, unknown> {\n return {\n ...super.toJSON(),\n retryAfter: this.retryAfter,\n }\n }\n\n override toResponse(headers?: HeadersInit): Response {\n return new Response(JSON.stringify(this.toJSON()), {\n status: this.statusCode,\n headers: {\n 'Content-Type': 'application/json',\n 'Retry-After': String(this.retryAfter),\n ...headers,\n },\n })\n }\n}\n\n/**\n * Authentication error\n */\nexport class AuthenticationError extends SecureError {\n constructor(\n message = 'Authentication required',\n options: {\n code?: string\n details?: Record<string, unknown>\n cause?: Error\n } = {}\n ) {\n super(message, {\n statusCode: 401,\n code: options.code ?? 'AUTHENTICATION_REQUIRED',\n details: options.details,\n cause: options.cause,\n })\n this.name = 'AuthenticationError'\n }\n}\n\n/**\n * Authorization error (authenticated but not permitted)\n */\nexport class AuthorizationError extends SecureError {\n constructor(\n message = 'Access denied',\n options: {\n code?: string\n details?: Record<string, unknown>\n cause?: Error\n } = {}\n ) {\n super(message, {\n statusCode: 403,\n code: options.code ?? 'ACCESS_DENIED',\n details: options.details,\n cause: options.cause,\n })\n this.name = 'AuthorizationError'\n }\n}\n\n/**\n * Validation error\n */\nexport class ValidationError extends SecureError {\n /**\n * Field-level validation errors\n */\n public readonly errors: Array<{\n field: string\n message: string\n code?: string\n }>\n\n constructor(\n errors: Array<{ field: string; message: string; code?: string }>,\n message = 'Validation failed'\n ) {\n super(message, {\n statusCode: 400,\n code: 'VALIDATION_ERROR',\n details: { errors },\n })\n this.name = 'ValidationError'\n this.errors = errors\n }\n\n override toJSON(): Record<string, unknown> {\n return {\n ...super.toJSON(),\n errors: this.errors,\n }\n }\n}\n\n/**\n * CSRF token error\n */\nexport class CsrfError extends SecureError {\n constructor(\n message = 'Invalid or missing CSRF token',\n options: {\n details?: Record<string, unknown>\n } = {}\n ) {\n super(message, {\n statusCode: 403,\n code: 'CSRF_TOKEN_INVALID',\n details: options.details,\n })\n this.name = 'CsrfError'\n }\n}\n\n/**\n * Configuration error\n */\nexport class ConfigurationError extends SecureError {\n constructor(\n message: string,\n options: {\n details?: Record<string, unknown>\n cause?: Error\n } = {}\n ) {\n super(message, {\n statusCode: 500,\n code: 'CONFIGURATION_ERROR',\n details: options.details,\n cause: options.cause,\n })\n this.name = 'ConfigurationError'\n }\n}\n\n/**\n * Check if an error is a SecureError\n */\nexport function isSecureError(error: unknown): error is SecureError {\n return error instanceof SecureError\n}\n\n/**\n * Convert unknown error to SecureError\n */\nexport function toSecureError(error: unknown): SecureError {\n if (error instanceof SecureError) {\n return error\n }\n\n if (error instanceof Error) {\n return new SecureError(error.message, {\n cause: error,\n })\n }\n\n return new SecureError(String(error))\n}\n","/**\n * Time parsing and manipulation utilities\n */\n\nimport type { Duration } from '../core/types'\n\n/**\n * Time unit multipliers in milliseconds\n */\nconst TIME_UNITS: Record<string, number> = {\n ms: 1,\n s: 1000,\n m: 60 * 1000,\n h: 60 * 60 * 1000,\n d: 24 * 60 * 60 * 1000,\n}\n\n/**\n * Extended time unit names\n */\nconst TIME_UNIT_ALIASES: Record<string, string> = {\n millisecond: 'ms',\n milliseconds: 'ms',\n second: 's',\n seconds: 's',\n sec: 's',\n secs: 's',\n minute: 'm',\n minutes: 'm',\n min: 'm',\n mins: 'm',\n hour: 'h',\n hours: 'h',\n hr: 'h',\n hrs: 'h',\n day: 'd',\n days: 'd',\n}\n\n/**\n * Parse a duration string or number to milliseconds\n *\n * @example\n * ```typescript\n * parseDuration('15m') // 900000 (15 minutes)\n * parseDuration('1h') // 3600000 (1 hour)\n * parseDuration('30s') // 30000 (30 seconds)\n * parseDuration('1d') // 86400000 (1 day)\n * parseDuration(60000) // 60000 (already in ms)\n * parseDuration('2 hours') // 7200000 (2 hours)\n * parseDuration('1h 30m') // 5400000 (1.5 hours)\n * ```\n *\n * @param duration - Duration string (e.g., '15m', '1h', '30s') or number in milliseconds\n * @returns Duration in milliseconds\n * @throws Error if the duration format is invalid\n */\nexport function parseDuration(duration: Duration | string): number {\n // If it's already a number, return as-is\n if (typeof duration === 'number') {\n if (duration < 0) {\n throw new Error(`Invalid duration: ${duration}. Duration must be non-negative.`)\n }\n return duration\n }\n\n // Trim and lowercase the string\n const input = duration.trim().toLowerCase()\n\n if (!input) {\n throw new Error('Invalid duration: empty string')\n }\n\n // Try to parse as a simple number (assume milliseconds)\n const numericValue = Number(input)\n if (!isNaN(numericValue)) {\n if (numericValue < 0) {\n throw new Error(`Invalid duration: ${duration}. Duration must be non-negative.`)\n }\n return numericValue\n }\n\n // Handle compound durations like \"1h 30m\" or \"1h30m\"\n let totalMs = 0\n const regex = /(\\d+(?:\\.\\d+)?)\\s*([a-z]+)/g\n let match: RegExpExecArray | null\n let hasMatch = false\n\n while ((match = regex.exec(input)) !== null) {\n hasMatch = true\n const value = parseFloat(match[1])\n let unit = match[2]\n\n // Resolve unit aliases\n if (unit in TIME_UNIT_ALIASES) {\n unit = TIME_UNIT_ALIASES[unit]\n }\n\n // Get multiplier\n const multiplier = TIME_UNITS[unit]\n if (multiplier === undefined) {\n throw new Error(\n `Invalid duration unit: \"${unit}\" in \"${duration}\". ` +\n `Valid units: s, m, h, d (or seconds, minutes, hours, days)`\n )\n }\n\n totalMs += value * multiplier\n }\n\n if (!hasMatch) {\n throw new Error(\n `Invalid duration format: \"${duration}\". ` +\n `Expected format like \"15m\", \"1h\", \"30s\", \"1d\", or \"1h 30m\"`\n )\n }\n\n return Math.floor(totalMs)\n}\n\n/**\n * Format milliseconds to a human-readable duration string\n *\n * @example\n * ```typescript\n * formatDuration(900000) // \"15m\"\n * formatDuration(3600000) // \"1h\"\n * formatDuration(5400000) // \"1h 30m\"\n * formatDuration(86400000) // \"1d\"\n * formatDuration(90061000) // \"1d 1h 1m 1s\"\n * ```\n *\n * @param ms - Duration in milliseconds\n * @param options - Formatting options\n * @returns Human-readable duration string\n */\nexport function formatDuration(\n ms: number,\n options: {\n /**\n * Use long unit names (e.g., \"minutes\" instead of \"m\")\n */\n long?: boolean\n /**\n * Maximum number of units to include\n */\n maxUnits?: number\n /**\n * Separator between units\n */\n separator?: string\n } = {}\n): string {\n const { long = false, maxUnits = 4, separator = ' ' } = options\n\n if (ms < 0) {\n return `-${formatDuration(-ms, options)}`\n }\n\n if (ms === 0) {\n return long ? '0 seconds' : '0s'\n }\n\n const units: Array<{ value: number; short: string; long: string; longPlural: string }> = [\n { value: 86400000, short: 'd', long: 'day', longPlural: 'days' },\n { value: 3600000, short: 'h', long: 'hour', longPlural: 'hours' },\n { value: 60000, short: 'm', long: 'minute', longPlural: 'minutes' },\n { value: 1000, short: 's', long: 'second', longPlural: 'seconds' },\n { value: 1, short: 'ms', long: 'millisecond', longPlural: 'milliseconds' },\n ]\n\n const parts: string[] = []\n let remaining = ms\n\n for (const unit of units) {\n if (parts.length >= maxUnits) break\n if (remaining >= unit.value) {\n const count = Math.floor(remaining / unit.value)\n remaining = remaining % unit.value\n\n if (long) {\n parts.push(`${count} ${count === 1 ? unit.long : unit.longPlural}`)\n } else {\n parts.push(`${count}${unit.short}`)\n }\n }\n }\n\n return parts.join(separator)\n}\n\n/**\n * Get the current timestamp in seconds (Unix timestamp)\n */\nexport function nowInSeconds(): number {\n return Math.floor(Date.now() / 1000)\n}\n\n/**\n * Get the current timestamp in milliseconds\n */\nexport function nowInMs(): number {\n return Date.now()\n}\n\n/**\n * Calculate reset time for a fixed window\n *\n * @param windowMs - Window size in milliseconds\n * @returns Unix timestamp (seconds) when the window resets\n */\nexport function getWindowReset(windowMs: number): number {\n const now = Date.now()\n const windowStart = Math.floor(now / windowMs) * windowMs\n const windowEnd = windowStart + windowMs\n return Math.floor(windowEnd / 1000)\n}\n\n/**\n * Get the start of the current window\n *\n * @param windowMs - Window size in milliseconds\n * @returns Timestamp (ms) of window start\n */\nexport function getWindowStart(windowMs: number): number {\n return Math.floor(Date.now() / windowMs) * windowMs\n}\n\n/**\n * Sleep for a specified duration\n *\n * @param duration - Duration to sleep\n * @returns Promise that resolves after the duration\n */\nexport function sleep(duration: Duration | string): Promise<void> {\n const ms = parseDuration(duration)\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\n/**\n * Check if a timestamp has expired\n *\n * @param timestampMs - Timestamp in milliseconds\n * @param ttlMs - Time-to-live in milliseconds\n * @returns true if expired\n */\nexport function isExpired(timestampMs: number, ttlMs: number): boolean {\n return Date.now() > timestampMs + ttlMs\n}\n\n/**\n * Calculate time until expiration\n *\n * @param expiresAt - Expiration timestamp in milliseconds\n * @returns Milliseconds until expiration (0 if already expired)\n */\nexport function timeUntilExpiry(expiresAt: number): number {\n return Math.max(0, expiresAt - Date.now())\n}\n\n/**\n * Convert seconds to milliseconds\n */\nexport function secondsToMs(seconds: number): number {\n return seconds * 1000\n}\n\n/**\n * Convert milliseconds to seconds\n */\nexport function msToSeconds(ms: number): number {\n return Math.floor(ms / 1000)\n}\n","/**\n * IP address extraction and validation utilities\n */\n\nimport type { NextRequest } from '../core/types'\n\n/**\n * Headers to check for client IP (in order of priority)\n */\nconst IP_HEADERS = [\n // Cloudflare\n 'cf-connecting-ip',\n // Vercel\n 'x-real-ip',\n // Standard forwarded header (RFC 7239)\n 'x-forwarded-for',\n // AWS ELB\n 'x-client-ip',\n // Azure\n 'client-ip',\n // Fastly\n 'fastly-client-ip',\n // Akamai\n 'true-client-ip',\n // Google Cloud\n 'x-appengine-user-ip',\n // Fly.io\n 'fly-client-ip',\n] as const\n\n/**\n * Localhost/private IP patterns\n */\nconst PRIVATE_IP_PATTERNS = [\n /^127\\./, // IPv4 loopback\n /^10\\./, // Private class A\n /^172\\.(1[6-9]|2[0-9]|3[01])\\./, // Private class B\n /^192\\.168\\./, // Private class C\n /^::1$/, // IPv6 loopback\n /^fe80:/i, // IPv6 link-local\n /^fc00:/i, // IPv6 unique local\n /^fd[0-9a-f]{2}:/i, // IPv6 unique local\n]\n\n/**\n * IPv4 validation regex\n */\nconst IPV4_REGEX = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/\n\n/**\n * IPv6 validation regex (simplified)\n */\nconst IPV6_REGEX = /^(?:[a-fA-F0-9]{1,4}:){7}[a-fA-F0-9]{1,4}$|^::1$|^::$|^(?:[a-fA-F0-9]{1,4}:)*:(?:[a-fA-F0-9]{1,4}:)*[a-fA-F0-9]{1,4}$/\n\n/**\n * Options for IP extraction\n */\nexport interface GetIpOptions {\n /**\n * Trust proxy headers (default: true)\n * Set to false in direct-to-client setups\n */\n trustProxy?: boolean\n\n /**\n * Additional headers to check (checked first)\n */\n customHeaders?: string[]\n\n /**\n * Fallback IP when none found\n */\n fallback?: string\n}\n\n/**\n * Extract client IP address from request\n *\n * @example\n * ```typescript\n * // Basic usage\n * const ip = getClientIp(request)\n *\n * // With options\n * const ip = getClientIp(request, {\n * trustProxy: true,\n * customHeaders: ['my-custom-ip-header'],\n * fallback: '0.0.0.0'\n * })\n * ```\n *\n * @param request - Next.js request object\n * @param options - Extraction options\n * @returns Client IP address or fallback\n */\nexport function getClientIp(request: NextRequest, options: GetIpOptions = {}): string {\n const { trustProxy = true, customHeaders = [], fallback = '127.0.0.1' } = options\n\n // First, check if Next.js has already extracted the IP\n if (request.ip) {\n return normalizeIp(request.ip)\n }\n\n if (!trustProxy) {\n return fallback\n }\n\n // Check custom headers first\n for (const header of customHeaders) {\n const value = request.headers.get(header)\n if (value) {\n const ip = parseIpFromHeader(value)\n if (ip) return ip\n }\n }\n\n // Check standard headers\n for (const header of IP_HEADERS) {\n const value = request.headers.get(header)\n if (value) {\n const ip = parseIpFromHeader(value)\n if (ip) return ip\n }\n }\n\n return fallback\n}\n\n/**\n * Parse IP from header value\n * Handles comma-separated lists (x-forwarded-for)\n */\nfunction parseIpFromHeader(headerValue: string): string | null {\n // x-forwarded-for can have multiple IPs: \"client, proxy1, proxy2\"\n // The first one is the client IP\n const ips = headerValue.split(',').map((ip) => ip.trim())\n\n for (const ip of ips) {\n const normalized = normalizeIp(ip)\n if (isValidIp(normalized)) {\n return normalized\n }\n }\n\n return null\n}\n\n/**\n * Normalize an IP address\n * - Removes IPv6 brackets\n * - Removes port numbers\n * - Trims whitespace\n */\nexport function normalizeIp(ip: string): string {\n let normalized = ip.trim()\n\n // Remove IPv6 brackets: [::1] -> ::1\n if (normalized.startsWith('[') && normalized.includes(']')) {\n normalized = normalized.slice(1, normalized.indexOf(']'))\n }\n\n // Remove port: 192.168.1.1:8080 -> 192.168.1.1\n // For IPv4 with port\n if (normalized.includes(':') && !normalized.includes('::')) {\n const lastColon = normalized.lastIndexOf(':')\n const potentialPort = normalized.slice(lastColon + 1)\n if (/^\\d+$/.test(potentialPort)) {\n normalized = normalized.slice(0, lastColon)\n }\n }\n\n // Handle IPv4-mapped IPv6: ::ffff:192.168.1.1 -> 192.168.1.1\n if (normalized.toLowerCase().startsWith('::ffff:')) {\n const ipv4Part = normalized.slice(7)\n if (isValidIpv4(ipv4Part)) {\n return ipv4Part\n }\n }\n\n return normalized\n}\n\n/**\n * Check if an IP address is valid\n */\nexport function isValidIp(ip: string): boolean {\n return isValidIpv4(ip) || isValidIpv6(ip)\n}\n\n/**\n * Check if an IPv4 address is valid\n */\nexport function isValidIpv4(ip: string): boolean {\n return IPV4_REGEX.test(ip)\n}\n\n/**\n * Check if an IPv6 address is valid\n */\nexport function isValidIpv6(ip: string): boolean {\n return IPV6_REGEX.test(ip) || ip === '::1' || ip === '::'\n}\n\n/**\n * Check if an IP is a private/local address\n */\nexport function isPrivateIp(ip: string): boolean {\n return PRIVATE_IP_PATTERNS.some((pattern) => pattern.test(ip))\n}\n\n/**\n * Check if an IP is localhost\n */\nexport function isLocalhost(ip: string): boolean {\n return ip === '127.0.0.1' || ip === '::1' || ip === 'localhost'\n}\n\n/**\n * Create a rate limit key from IP\n * Normalizes and optionally hashes the IP\n */\nexport function createIpKey(\n ip: string,\n options: {\n prefix?: string\n hash?: boolean\n } = {}\n): string {\n const { prefix = 'rl', hash = false } = options\n const normalizedIp = normalizeIp(ip)\n\n if (hash) {\n // Simple hash for privacy (not cryptographic)\n const hashCode = simpleHash(normalizedIp)\n return `${prefix}:ip:${hashCode}`\n }\n\n return `${prefix}:ip:${normalizedIp}`\n}\n\n/**\n * Simple non-cryptographic hash (for key generation)\n */\nfunction simpleHash(str: string): string {\n let hash = 0\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i)\n hash = ((hash << 5) - hash) + char\n hash = hash & hash // Convert to 32-bit integer\n }\n return Math.abs(hash).toString(36)\n}\n\n/**\n * Anonymize an IP address (for logging)\n * IPv4: 192.168.1.100 -> 192.168.1.xxx\n * IPv6: 2001:db8::1 -> 2001:db8::xxx\n */\nexport function anonymizeIp(ip: string): string {\n const normalized = normalizeIp(ip)\n\n if (isValidIpv4(normalized)) {\n const parts = normalized.split('.')\n parts[3] = 'xxx'\n return parts.join('.')\n }\n\n if (isValidIpv6(normalized)) {\n const parts = normalized.split(':')\n if (parts.length > 0) {\n parts[parts.length - 1] = 'xxxx'\n }\n return parts.join(':')\n }\n\n return 'xxx.xxx.xxx.xxx'\n}\n\n/**\n * Get geolocation info from request (if available)\n * Works with Vercel Edge and Cloudflare\n */\nexport function getGeoInfo(request: NextRequest): {\n country?: string\n city?: string\n region?: string\n latitude?: string\n longitude?: string\n} {\n // Vercel provides geo info on the request\n if (request.geo) {\n return {\n country: request.geo.country,\n city: request.geo.city,\n region: request.geo.region,\n latitude: request.geo.latitude,\n longitude: request.geo.longitude,\n }\n }\n\n // Cloudflare headers\n return {\n country: request.headers.get('cf-ipcountry') ?? undefined,\n city: request.headers.get('cf-ipcity') ?? undefined,\n region: request.headers.get('cf-region') ?? undefined,\n latitude: request.headers.get('cf-iplat') ?? undefined,\n longitude: request.headers.get('cf-iplong') ?? undefined,\n }\n}\n","/**\n * In-memory rate limit store\n *\n * Suitable for:\n * - Development\n * - Single-instance deployments\n * - Testing\n *\n * Not suitable for:\n * - Multi-instance/distributed deployments (use Redis/Upstash)\n * - Serverless (data lost between invocations)\n */\n\nimport type { RateLimitStore, MemoryStoreOptions } from '../types'\nimport { msToSeconds } from '../../../utils/time'\n\n/**\n * Entry stored in memory\n */\ninterface MemoryEntry {\n count: number\n reset: number // Unix timestamp (seconds)\n createdAt: number // Timestamp (ms)\n}\n\n/**\n * LRU-style memory store for rate limiting\n *\n * Features:\n * - Automatic cleanup of expired entries\n * - LRU eviction when max keys exceeded\n * - Zero dependencies\n * - Edge Runtime compatible\n *\n * @example\n * ```typescript\n * import { MemoryStore } from 'next-secure/rate-limit'\n *\n * const store = new MemoryStore({\n * cleanupInterval: 60000, // 1 minute\n * maxKeys: 10000\n * })\n * ```\n */\nexport class MemoryStore implements RateLimitStore {\n public readonly name = 'memory'\n\n private store: Map<string, MemoryEntry>\n private cleanupTimer: ReturnType<typeof setInterval> | null = null\n private readonly maxKeys: number\n private readonly cleanupInterval: number\n\n constructor(options: MemoryStoreOptions = {}) {\n const { cleanupInterval = 60000, maxKeys = 10000 } = options\n\n this.store = new Map()\n this.maxKeys = maxKeys\n this.cleanupInterval = cleanupInterval\n\n // Start cleanup timer (only in long-running environments)\n if (typeof setInterval !== 'undefined' && cleanupInterval > 0) {\n this.startCleanupTimer()\n }\n }\n\n /**\n * Increment the counter for a key\n *\n * Note: The key should already include window information if needed.\n * This store is algorithm-agnostic - algorithms handle windowing logic.\n */\n async increment(\n key: string,\n windowMs: number\n ): Promise<{ count: number; reset: number }> {\n const now = Date.now()\n const defaultReset = msToSeconds(now + windowMs)\n\n const existing = this.store.get(key)\n\n if (existing) {\n // Increment existing entry\n existing.count++\n // Move to end (LRU update)\n this.store.delete(key)\n this.store.set(key, existing)\n return { count: existing.count, reset: existing.reset }\n }\n\n // New entry\n const entry: MemoryEntry = {\n count: 1,\n reset: defaultReset,\n createdAt: now,\n }\n\n // Check if we need to evict\n if (this.store.size >= this.maxKeys) {\n this.evictOldest()\n }\n\n this.store.set(key, entry)\n return { count: 1, reset: defaultReset }\n }\n\n /**\n * Get the current count for a key\n */\n async get(key: string): Promise<{ count: number; reset: number } | null> {\n const entry = this.store.get(key)\n\n if (!entry) {\n return null\n }\n\n // Check if expired\n const now = Math.floor(Date.now() / 1000)\n if (entry.reset <= now) {\n this.store.delete(key)\n return null\n }\n\n return { count: entry.count, reset: entry.reset }\n }\n\n /**\n * Reset the counter for a key\n */\n async reset(key: string): Promise<void> {\n this.store.delete(key)\n }\n\n /**\n * Check if the store is healthy\n */\n async isHealthy(): Promise<boolean> {\n return true\n }\n\n /**\n * Cleanup expired entries\n */\n async cleanup(): Promise<void> {\n const now = Math.floor(Date.now() / 1000)\n const keysToDelete: string[] = []\n\n for (const [key, entry] of this.store) {\n if (entry.reset <= now) {\n keysToDelete.push(key)\n }\n }\n\n for (const key of keysToDelete) {\n this.store.delete(key)\n }\n }\n\n /**\n * Close the store (stop cleanup timer)\n */\n async close(): Promise<void> {\n this.stopCleanupTimer()\n this.store.clear()\n }\n\n /**\n * Get the current size of the store\n */\n get size(): number {\n return this.store.size\n }\n\n /**\n * Clear all entries\n */\n clear(): void {\n this.store.clear()\n }\n\n /**\n * Start the cleanup timer\n */\n private startCleanupTimer(): void {\n if (this.cleanupTimer) return\n\n this.cleanupTimer = setInterval(() => {\n void this.cleanup()\n }, this.cleanupInterval)\n\n // Unref to not keep the process alive\n if (typeof this.cleanupTimer === 'object' && 'unref' in this.cleanupTimer) {\n (this.cleanupTimer as NodeJS.Timeout).unref()\n }\n }\n\n /**\n * Stop the cleanup timer\n */\n private stopCleanupTimer(): void {\n if (this.cleanupTimer) {\n clearInterval(this.cleanupTimer)\n this.cleanupTimer = null\n }\n }\n\n /**\n * Evict oldest entries when max keys exceeded\n */\n private evictOldest(): void {\n // Map maintains insertion order, so first key is oldest\n const keysToDelete = Math.ceil(this.maxKeys * 0.1) // Delete 10%\n\n let deleted = 0\n for (const key of this.store.keys()) {\n if (deleted >= keysToDelete) break\n this.store.delete(key)\n deleted++\n }\n }\n}\n\n/**\n * Create a memory store with default options\n */\nexport function createMemoryStore(options?: MemoryStoreOptions): MemoryStore {\n return new MemoryStore(options)\n}\n\n/**\n * Global memory store instance (singleton)\n * Useful for serverless environments where you want to reuse across requests\n */\nlet globalStore: MemoryStore | null = null\n\n/**\n * Get or create the global memory store\n */\nexport function getGlobalMemoryStore(options?: MemoryStoreOptions): MemoryStore {\n if (!globalStore) {\n globalStore = new MemoryStore(options)\n }\n return globalStore\n}\n\n/**\n * Reset the global memory store (useful for testing)\n */\nexport function resetGlobalMemoryStore(): void {\n if (globalStore) {\n void globalStore.close()\n globalStore = null\n }\n}\n","/**\n * Sliding Window Rate Limiting Algorithm\n *\n * This algorithm provides a smoother rate limiting experience compared to fixed windows.\n * It uses a weighted calculation based on the previous and current window counts.\n *\n * How it works:\n * 1. Divide time into fixed windows (e.g., 1 minute each)\n * 2. Track request counts for current and previous windows\n * 3. Calculate weighted count based on position within current window\n *\n * Example (100 req/min limit):\n * - Previous window: 80 requests\n * - Current window: 30 requests\n * - 30 seconds into current window (50% through)\n * - Weighted count = 30 + (80 * 0.5) = 70\n * - Since 70 < 100, request is allowed\n *\n * Pros:\n * - Smoother than fixed window\n * - Prevents burst attacks at window boundaries\n * - Memory efficient (only stores 2 counters per key)\n *\n * Cons:\n * - Slightly more complex than fixed window\n * - Not perfectly accurate (approximation)\n */\n\nimport type { RateLimitStore, RateLimitAlgorithmImpl } from '../types'\nimport type { RateLimitInfo } from '../../../core/types'\nimport { msToSeconds } from '../../../utils/time'\n\n/**\n * Sliding window counter algorithm implementation\n */\nexport class SlidingWindowAlgorithm implements RateLimitAlgorithmImpl {\n public readonly name = 'sliding-window' as const\n\n /**\n * Check if the request should be rate limited\n */\n async check(\n store: RateLimitStore,\n key: string,\n limit: number,\n windowMs: number\n ): Promise<RateLimitInfo> {\n const now = Date.now()\n\n // Calculate window boundaries\n const windowStart = Math.floor(now / windowMs) * windowMs\n const windowEnd = windowStart + windowMs\n const previousWindowStart = windowStart - windowMs\n\n // Position within current window (0 to 1)\n const windowPosition = (now - windowStart) / windowMs\n\n // Keys for current and previous windows\n const currentKey = `${key}:${windowStart}`\n const previousKey = `${key}:${previousWindowStart}`\n\n // Get counts from both windows\n const [currentData, previousData] = await Promise.all([\n store.get(currentKey),\n store.get(previousKey),\n ])\n\n const currentCount = currentData?.count ?? 0\n const previousCount = previousData?.count ?? 0\n\n // Calculate weighted count using sliding window formula\n // Weight of previous window decreases as we move through current window\n const previousWeight = 1 - windowPosition\n const weightedCount = currentCount + Math.floor(previousCount * previousWeight)\n\n // Calculate reset time (end of current window)\n const reset = msToSeconds(windowEnd)\n\n // Check if limit exceeded\n if (weightedCount >= limit) {\n // Calculate retry time based on when enough requests will \"expire\"\n const retryAfter = this.calculateRetryAfter(\n currentCount,\n previousCount,\n limit,\n windowMs,\n windowPosition\n )\n\n return {\n limit,\n remaining: 0,\n reset,\n limited: true,\n retryAfter,\n }\n }\n\n // Increment current window counter\n await store.increment(currentKey, windowMs)\n\n // Calculate remaining\n const remaining = Math.max(0, limit - weightedCount - 1)\n\n return {\n limit,\n remaining,\n reset,\n limited: false,\n }\n }\n\n /**\n * Calculate how long until the client can make another request\n */\n private calculateRetryAfter(\n currentCount: number,\n previousCount: number,\n limit: number,\n windowMs: number,\n windowPosition: number\n ): number {\n // If previous window is empty, wait until current window resets\n if (previousCount === 0) {\n return Math.ceil((1 - windowPosition) * windowMs / 1000)\n }\n\n // Calculate when the weighted count will drop below limit\n // We need: currentCount + previousCount * (1 - newPosition) < limit\n // Solving for newPosition: newPosition > 1 - (limit - currentCount) / previousCount\n\n const requiredPosition = 1 - (limit - currentCount) / previousCount\n\n if (requiredPosition <= windowPosition) {\n // Should already be under limit, but we got here so add small delay\n return 1\n }\n\n if (requiredPosition >= 1) {\n // Need to wait until next window\n const remainingInCurrentWindow = (1 - windowPosition) * windowMs\n return Math.ceil(remainingInCurrentWindow / 1000)\n }\n\n // Calculate time until we reach required position\n const timeToWait = (requiredPosition - windowPosition) * windowMs\n return Math.ceil(timeToWait / 1000)\n }\n}\n\n/**\n * Create a sliding window algorithm instance\n */\nexport function createSlidingWindowAlgorithm(): SlidingWindowAlgorithm {\n return new SlidingWindowAlgorithm()\n}\n\n/**\n * Sliding Window Log Algorithm (more accurate but uses more memory)\n *\n * This stores individual request timestamps instead of just counters.\n * More accurate but not recommended for high-traffic scenarios.\n */\nexport class SlidingWindowLogAlgorithm implements RateLimitAlgorithmImpl {\n public readonly name = 'sliding-window' as const\n\n /**\n * In-memory log of request timestamps per key\n * For production, this should be stored externally (Redis sorted sets, etc.)\n */\n private logs: Map<string, number[]> = new Map()\n\n /**\n * Maximum log size before cleanup\n */\n private readonly maxLogSize = 10000\n\n async check(\n _store: RateLimitStore,\n key: string,\n limit: number,\n windowMs: number\n ): Promise<RateLimitInfo> {\n const now = Date.now()\n const windowStart = now - windowMs\n\n // Get or create log for this key\n let log = this.logs.get(key) ?? []\n\n // Remove expired entries\n log = log.filter((timestamp) => timestamp > windowStart)\n\n // Calculate reset (when oldest entry expires)\n const oldestTimestamp = log[0] ?? now\n const reset = msToSeconds(oldestTimestamp + windowMs)\n\n // Check if limit exceeded\n if (log.length >= limit) {\n const retryAfter = Math.ceil((oldestTimestamp + windowMs - now) / 1000)\n\n return {\n limit,\n remaining: 0,\n reset,\n limited: true,\n retryAfter: Math.max(1, retryAfter),\n }\n }\n\n // Add current request\n log.push(now)\n this.logs.set(key, log)\n\n // Cleanup if too many keys\n if (this.logs.size > this.maxLogSize) {\n this.cleanup()\n }\n\n return {\n limit,\n remaining: Math.max(0, limit - log.length),\n reset,\n limited: false,\n }\n }\n\n /**\n * Remove oldest entries when log size exceeded\n */\n private cleanup(): void {\n const keysToDelete: string[] = []\n const now = Date.now()\n\n for (const [key, log] of this.logs) {\n // Delete empty or very old logs\n if (log.length === 0 || log[log.length - 1]! < now - 3600000) {\n keysToDelete.push(key)\n }\n }\n\n for (const key of keysToDelete) {\n this.logs.delete(key)\n }\n }\n\n /**\n * Clear all logs\n */\n clear(): void {\n this.logs.clear()\n }\n}\n","/**\n * Fixed Window Rate Limiting Algorithm\n *\n * The simplest rate limiting algorithm. Divides time into fixed windows\n * and counts requests within each window.\n *\n * How it works:\n * 1. Divide time into fixed windows (e.g., every minute starting at :00)\n * 2. Count requests within the current window\n * 3. Reset counter when new window starts\n *\n * Example (100 req/min limit):\n * - Window 1 (12:00:00 - 12:00:59): 80 requests -> allowed\n * - Window 2 (12:01:00 - 12:01:59): 0 requests (fresh start)\n *\n * Pros:\n * - Simple to implement\n * - Memory efficient (only 1 counter per key)\n * - Fast (O(1) operations)\n *\n * Cons:\n * - Burst problem: 200 requests possible in 2 seconds at window boundary\n * - 100 requests at 12:00:59 (end of window 1)\n * - 100 requests at 12:01:00 (start of window 2)\n * - Not smooth\n *\n * Use when:\n * - Simplicity is preferred\n * - Burst at boundaries is acceptable\n * - Memory/CPU is very constrained\n */\n\nimport type { RateLimitStore, RateLimitAlgorithmImpl } from '../types'\nimport type { RateLimitInfo } from '../../../core/types'\nimport { msToSeconds } from '../../../utils/time'\n\n/**\n * Fixed window algorithm implementation\n */\nexport class FixedWindowAlgorithm implements RateLimitAlgorithmImpl {\n public readonly name = 'fixed-window' as const\n\n /**\n * Check if the request should be rate limited\n */\n async check(\n store: RateLimitStore,\n key: string,\n limit: number,\n windowMs: number\n ): Promise<RateLimitInfo> {\n const now = Date.now()\n\n // Calculate window boundaries\n const windowStart = Math.floor(now / windowMs) * windowMs\n const windowEnd = windowStart + windowMs\n const reset = msToSeconds(windowEnd)\n\n // Create window-specific key\n const windowKey = `${key}:${windowStart}`\n\n // Get current count\n const data = await store.get(windowKey)\n const currentCount = data?.count ?? 0\n\n // Check if limit exceeded\n if (currentCount >= limit) {\n const retryAfter = Math.ceil((windowEnd - now) / 1000)\n\n return {\n limit,\n remaining: 0,\n reset,\n limited: true,\n retryAfter: Math.max(1, retryAfter),\n }\n }\n\n // Increment counter\n const { count } = await store.increment(windowKey, windowMs)\n\n // Double-check after increment (race condition protection)\n if (count > limit) {\n const retryAfter = Math.ceil((windowEnd - now) / 1000)\n\n return {\n limit,\n remaining: 0,\n reset,\n limited: true,\n retryAfter: Math.max(1, retryAfter),\n }\n }\n\n return {\n limit,\n remaining: Math.max(0, limit - count),\n reset,\n limited: false,\n }\n }\n}\n\n/**\n * Create a fixed window algorithm instance\n */\nexport function createFixedWindowAlgorithm(): FixedWindowAlgorithm {\n return new FixedWindowAlgorithm()\n}\n\n/**\n * Fixed window with burst protection\n *\n * Adds a secondary limit to prevent bursts at window boundaries.\n * For example: 100 req/min with max 20 req/10sec burst.\n */\nexport class FixedWindowWithBurstProtection implements RateLimitAlgorithmImpl {\n public readonly name = 'fixed-window' as const\n\n constructor(\n private readonly burstLimit: number,\n private readonly burstWindowMs: number\n ) {}\n\n async check(\n store: RateLimitStore,\n key: string,\n limit: number,\n windowMs: number\n ): Promise<RateLimitInfo> {\n const now = Date.now()\n\n // Check burst limit first (smaller window)\n const burstWindowStart = Math.floor(now / this.burstWindowMs) * this.burstWindowMs\n const burstKey = `${key}:burst:${burstWindowStart}`\n const burstData = await store.get(burstKey)\n const burstCount = burstData?.count ?? 0\n\n if (burstCount >= this.burstLimit) {\n const burstWindowEnd = burstWindowStart + this.burstWindowMs\n const retryAfter = Math.ceil((burstWindowEnd - now) / 1000)\n\n return {\n limit: this.burstLimit,\n remaining: 0,\n reset: msToSeconds(burstWindowEnd),\n limited: true,\n retryAfter: Math.max(1, retryAfter),\n }\n }\n\n // Check main limit\n const windowStart = Math.floor(now / windowMs) * windowMs\n const windowEnd = windowStart + windowMs\n const windowKey = `${key}:${windowStart}`\n const data = await store.get(windowKey)\n const currentCount = data?.count ?? 0\n\n if (currentCount >= limit) {\n const retryAfter = Math.ceil((windowEnd - now) / 1000)\n\n return {\n limit,\n remaining: 0,\n reset: msToSeconds(windowEnd),\n limited: true,\n retryAfter: Math.max(1, retryAfter),\n }\n }\n\n // Increment both counters\n await Promise.all([\n store.increment(windowKey, windowMs),\n store.increment(burstKey, this.burstWindowMs),\n ])\n\n return {\n limit,\n remaining: Math.max(0, limit - currentCount - 1),\n reset: msToSeconds(windowEnd),\n limited: false,\n }\n }\n}\n\n/**\n * Create a fixed window with burst protection\n *\n * @example\n * ```typescript\n * // 100 req/min with max 20 req/10sec burst\n * const algorithm = createFixedWindowWithBurstProtection(20, 10000)\n * ```\n */\nexport function createFixedWindowWithBurstProtection(\n burstLimit: number,\n burstWindowMs: number\n): FixedWindowWithBurstProtection {\n return new FixedWindowWithBurstProtection(burstLimit, burstWindowMs)\n}\n","/**\n * Token Bucket Rate Limiting Algorithm\n *\n * A bucket holds tokens that are consumed by requests. Tokens are refilled\n * at a constant rate. This allows for controlled bursts while maintaining\n * an average rate.\n *\n * How it works:\n * 1. Bucket starts full with 'limit' tokens\n * 2. Each request consumes 1 token (or more for weighted requests)\n * 3. Tokens are refilled at 'limit / window' rate\n * 4. Request is allowed if tokens >= 1\n *\n * Example (100 tokens, refill 100/min = 1.67/sec):\n * - Initial: 100 tokens\n * - 50 requests instantly: 50 tokens remaining (burst allowed)\n * - Wait 30 seconds: 50 + (50 * 1.67) = 100 tokens (refilled)\n * - 100 requests instantly: 0 tokens\n * - Next request: denied until tokens refill\n *\n * Pros:\n * - Allows controlled bursts\n * - Smooth average rate\n * - Good for APIs with sporadic traffic\n *\n * Cons:\n * - More complex state management\n * - Requires storing last refill time\n *\n * Use when:\n * - You want to allow bursts\n * - Traffic is sporadic\n * - User experience matters (can handle burst then wait)\n */\n\nimport type { RateLimitStore, RateLimitAlgorithmImpl, TokenBucketState } from '../types'\nimport type { RateLimitInfo } from '../../../core/types'\nimport { msToSeconds } from '../../../utils/time'\n\n/**\n * Token bucket algorithm implementation\n */\nexport class TokenBucketAlgorithm implements RateLimitAlgorithmImpl {\n public readonly name = 'token-bucket' as const\n\n /**\n * In-memory bucket states\n * For distributed systems, this should be stored in Redis\n */\n private buckets: Map<string, TokenBucketState> = new Map()\n\n /**\n * Maximum number of buckets to store before cleanup\n */\n private readonly maxBuckets = 10000\n\n /**\n * Check if the request should be rate limited\n *\n * @param store - Not used directly, state stored in memory\n * @param key - Rate limit key\n * @param limit - Maximum tokens (bucket capacity)\n * @param windowMs - Time to refill bucket completely\n */\n async check(\n _store: RateLimitStore,\n key: string,\n limit: number,\n windowMs: number\n ): Promise<RateLimitInfo> {\n const now = Date.now()\n\n // Get or create bucket state\n let bucket = this.buckets.get(key)\n\n if (!bucket) {\n // New bucket, start full\n bucket = {\n tokens: limit,\n lastRefill: now,\n }\n } else {\n // Refill tokens based on time elapsed\n bucket = this.refillTokens(bucket, limit, windowMs, now)\n }\n\n // Calculate reset time (when bucket would be full again)\n const tokensNeeded = limit - bucket.tokens\n const refillRate = limit / windowMs // tokens per ms\n const timeToFull = tokensNeeded / refillRate\n const reset = msToSeconds(now + timeToFull)\n\n // Check if we have tokens\n if (bucket.tokens < 1) {\n // Calculate when we'll have 1 token\n const timeToOneToken = (1 - bucket.tokens) / refillRate\n const retryAfter = Math.ceil(timeToOneToken / 1000)\n\n return {\n limit,\n remaining: 0,\n reset,\n limited: true,\n retryAfter: Math.max(1, retryAfter),\n }\n }\n\n // Consume a token\n bucket.tokens -= 1\n this.buckets.set(key, bucket)\n\n // Cleanup if too many buckets\n if (this.buckets.size > this.maxBuckets) {\n this.cleanup()\n }\n\n return {\n limit,\n remaining: Math.floor(bucket.tokens),\n reset,\n limited: false,\n }\n }\n\n /**\n * Refill tokens based on time elapsed\n */\n private refillTokens(\n bucket: TokenBucketState,\n limit: number,\n windowMs: number,\n now: number\n ): TokenBucketState {\n const elapsed = now - bucket.lastRefill\n const refillRate = limit / windowMs // tokens per ms\n const tokensToAdd = elapsed * refillRate\n\n return {\n tokens: Math.min(limit, bucket.tokens + tokensToAdd),\n lastRefill: now,\n }\n }\n\n /**\n * Remove old buckets\n */\n private cleanup(): void {\n const now = Date.now()\n const staleThreshold = 3600000 // 1 hour\n\n const keysToDelete: string[] = []\n\n for (const [key, bucket] of this.buckets) {\n if (now - bucket.lastRefill > staleThreshold) {\n keysToDelete.push(key)\n }\n }\n\n for (const key of keysToDelete) {\n this.buckets.delete(key)\n }\n }\n\n /**\n * Get current bucket state (for testing/debugging)\n */\n getBucketState(key: string): TokenBucketState | undefined {\n return this.buckets.get(key)\n }\n\n /**\n * Clear all buckets\n */\n clear(): void {\n this.buckets.clear()\n }\n}\n\n/**\n * Create a token bucket algorithm instance\n */\nexport function createTokenBucketAlgorithm(): TokenBucketAlgorithm {\n return new TokenBucketAlgorithm()\n}\n\n/**\n * Leaky Bucket Algorithm (variation of token bucket)\n *\n * Instead of refilling tokens, requests \"leak\" out of the bucket\n * at a constant rate. This enforces a strict output rate.\n *\n * Think of it as a bucket with a hole at the bottom:\n * - Requests are added to the bucket\n * - Requests leak out at a constant rate\n * - If bucket overflows, request is rejected\n *\n * Use when:\n * - You need strict rate enforcement\n * - Bursts should be queued, not rejected\n * - Output rate must be constant\n */\nexport class LeakyBucketAlgorithm implements RateLimitAlgorithmImpl {\n public readonly name = 'token-bucket' as const // Grouped with token bucket\n\n /**\n * In-memory bucket states\n * Stores the \"water level\" and last leak time\n */\n private buckets: Map<string, { level: number; lastLeak: number }> = new Map()\n\n /**\n * Maximum number of buckets\n */\n private readonly maxBuckets = 10000\n\n async check(\n _store: RateLimitStore,\n key: string,\n limit: number,\n windowMs: number\n ): Promise<RateLimitInfo> {\n const now = Date.now()\n\n // Get or create bucket\n let bucket = this.buckets.get(key) ?? { level: 0, lastLeak: now }\n\n // Calculate how much has leaked since last check\n const elapsed = now - bucket.lastLeak\n const leakRate = limit / windowMs // requests per ms\n const leaked = elapsed * leakRate\n\n // Update level (can't go below 0)\n bucket.level = Math.max(0, bucket.level - leaked)\n bucket.lastLeak = now\n\n // Calculate reset time\n const timeToEmpty = bucket.level / leakRate\n const reset = msToSeconds(now + timeToEmpty)\n\n // Check if bucket would overflow\n if (bucket.level + 1 > limit) {\n // Calculate when there's room for 1 more request\n const overflow = bucket.level + 1 - limit\n const timeToRoom = overflow / leakRate\n const retryAfter = Math.ceil(timeToRoom / 1000)\n\n return {\n limit,\n remaining: 0,\n reset,\n limited: true,\n retryAfter: Math.max(1, retryAfter),\n }\n }\n\n // Add request to bucket\n bucket.level += 1\n this.buckets.set(key, bucket)\n\n // Cleanup if needed\n if (this.buckets.size > this.maxBuckets) {\n this.cleanup(now)\n }\n\n return {\n limit,\n remaining: Math.floor(limit - bucket.level),\n reset,\n limited: false,\n }\n }\n\n private cleanup(now: number): void {\n const staleThreshold = 3600000\n\n for (const [key, bucket] of this.buckets) {\n if (now - bucket.lastLeak > staleThreshold) {\n this.buckets.delete(key)\n }\n }\n }\n\n clear(): void {\n this.buckets.clear()\n }\n}\n\n/**\n * Create a leaky bucket algorithm instance\n */\nexport function createLeakyBucketAlgorithm(): LeakyBucketAlgorithm {\n return new LeakyBucketAlgorithm()\n}\n","/**\n * Rate Limiting Middleware for Next.js App Router\n *\n * @example\n * ```typescript\n * // Basic usage\n * import { withRateLimit } from 'next-secure/rate-limit'\n *\n * export const GET = withRateLimit(\n * async (req) => Response.json({ data: [] }),\n * { limit: 100, window: '15m' }\n * )\n *\n * // With custom identifier\n * export const GET = withRateLimit(handler, {\n * limit: 100,\n * window: '15m',\n * identifier: (req) => req.headers.get('x-api-key') ?? 'anonymous'\n * })\n *\n * // With Redis store\n * import { createRedisStore } from 'next-secure/rate-limit'\n *\n * const store = createRedisStore({ client: redis })\n *\n * export const GET = withRateLimit(handler, {\n * limit: 100,\n * window: '15m',\n * store\n * })\n * ```\n */\n\nimport type { NextRequest, RateLimitInfo, SecureContext } from '../../core/types'\nimport { RateLimitError } from '../../core/errors'\nimport { parseDuration } from '../../utils/time'\nimport { getClientIp } from '../../utils/ip'\nimport type { RateLimitConfig, RateLimitStore, RateLimitAlgorithmImpl } from './types'\nimport { MemoryStore } from './stores/memory'\nimport { SlidingWindowAlgorithm } from './algorithms/sliding-window'\nimport { FixedWindowAlgorithm } from './algorithms/fixed-window'\nimport { TokenBucketAlgorithm } from './algorithms/token-bucket'\n\n/**\n * Default configuration values\n */\nconst DEFAULT_CONFIG: Partial<RateLimitConfig> = {\n algorithm: 'sliding-window',\n identifier: 'ip',\n headers: true,\n prefix: 'rl',\n message: 'Too Many Requests',\n statusCode: 429,\n debug: false,\n}\n\n/**\n * Global default store (shared across handlers)\n */\nlet defaultStore: RateLimitStore | null = null\n\n/**\n * Get or create the default memory store\n */\nfunction getDefaultStore(): RateLimitStore {\n if (!defaultStore) {\n defaultStore = new MemoryStore()\n }\n return defaultStore\n}\n\n/**\n * Get algorithm instance by name\n */\nfunction getAlgorithm(name: RateLimitConfig['algorithm']): RateLimitAlgorithmImpl {\n switch (name) {\n case 'fixed-window':\n return new FixedWindowAlgorithm()\n case 'token-bucket':\n return new TokenBucketAlgorithm()\n case 'sliding-window':\n default:\n return new SlidingWindowAlgorithm()\n }\n}\n\n/**\n * Create rate limit headers\n */\nfunction createRateLimitHeaders(info: RateLimitInfo): Headers {\n const headers = new Headers()\n\n headers.set('X-RateLimit-Limit', String(info.limit))\n headers.set('X-RateLimit-Remaining', String(info.remaining))\n headers.set('X-RateLimit-Reset', String(info.reset))\n\n if (info.limited && info.retryAfter) {\n headers.set('Retry-After', String(info.retryAfter))\n }\n\n return headers\n}\n\n/**\n * Merge headers from multiple sources\n */\nfunction mergeHeaders(target: Headers, source: Headers): void {\n source.forEach((value, key) => {\n target.set(key, value)\n })\n}\n\n/**\n * Get client identifier from request\n */\nasync function getIdentifier(\n request: NextRequest,\n identifier: RateLimitConfig['identifier'],\n prefix: string,\n context?: SecureContext\n): Promise<string> {\n if (typeof identifier === 'function') {\n const id = await identifier(request)\n return `${prefix}:custom:${id}`\n }\n\n if (identifier === 'user') {\n // Requires auth middleware to have run first\n const userId = context?.user\n ? (context.user as { id?: string }).id ?? 'anonymous'\n : 'anonymous'\n return `${prefix}:user:${userId}`\n }\n\n // Default: IP-based\n const ip = getClientIp(request)\n return `${prefix}:ip:${ip}`\n}\n\n/**\n * Rate limiting middleware wrapper\n *\n * @example\n * ```typescript\n * // Simple usage\n * export const GET = withRateLimit(\n * async (req) => Response.json({ ok: true }),\n * { limit: 100, window: '15m' }\n * )\n *\n * // With all options\n * export const POST = withRateLimit(\n * async (req, ctx) => {\n * // ctx.rateLimit contains info\n * return Response.json({ remaining: ctx.rateLimit?.remaining })\n * },\n * {\n * limit: 10,\n * window: '1m',\n * algorithm: 'sliding-window',\n * identifier: 'ip',\n * headers: true,\n * onLimit: (req, info) => new Response(\n * JSON.stringify({ error: 'Slow down!' }),\n * { status: 429 }\n * ),\n * skip: (req) => req.headers.get('x-bypass') === 'secret'\n * }\n * )\n * ```\n */\nexport function withRateLimit<TUser = unknown>(\n handler: (\n request: NextRequest,\n context: SecureContext<TUser> & { rateLimit?: RateLimitInfo }\n ) => Promise<Response> | Response,\n config: RateLimitConfig\n): (request: NextRequest, context?: SecureContext<TUser>) => Promise<Response> {\n // Merge config with defaults\n const finalConfig: Required<RateLimitConfig> = {\n ...DEFAULT_CONFIG,\n ...config,\n store: config.store ?? getDefaultStore(),\n } as Required<RateLimitConfig>\n\n // Parse window duration once\n const windowMs = parseDuration(finalConfig.window)\n\n // Get algorithm once\n const algorithm = getAlgorithm(finalConfig.algorithm)\n\n // Debug logging\n const debug = finalConfig.debug\n ? (msg: string, data?: unknown) => {\n // eslint-disable-next-line no-console\n console.log(`[next-secure:rate-limit] ${msg}`, data ?? '')\n }\n : () => {}\n\n debug('Initialized', {\n limit: finalConfig.limit,\n window: finalConfig.window,\n algorithm: finalConfig.algorithm,\n })\n\n return async (\n request: NextRequest,\n context?: SecureContext<TUser>\n ): Promise<Response> => {\n // Create context if not provided\n const ctx: SecureContext<TUser> & { rateLimit?: RateLimitInfo } = context ?? {\n user: null,\n requestId: crypto.randomUUID(),\n ip: getClientIp(request),\n userAgent: request.headers.get('user-agent') ?? '',\n startTime: Date.now(),\n metadata: {},\n }\n\n try {\n // Check if we should skip rate limiting\n if (finalConfig.skip) {\n const shouldSkip = await finalConfig.skip(request)\n if (shouldSkip) {\n debug('Skipping rate limit check')\n return handler(request, ctx)\n }\n }\n\n // Get identifier\n const key = await getIdentifier(\n request,\n finalConfig.identifier,\n finalConfig.prefix,\n ctx\n )\n debug('Rate limit key', key)\n\n // Check rate limit\n const info = await algorithm.check(\n finalConfig.store,\n key,\n finalConfig.limit,\n windowMs\n )\n debug('Rate limit info', info)\n\n // Add rate limit info to context\n ctx.rateLimit = info\n\n // Check if limited\n if (info.limited) {\n debug('Request rate limited')\n\n // Custom handler\n if (finalConfig.onLimit) {\n const response = await finalConfig.onLimit(request, info)\n\n // Add headers to custom response\n if (finalConfig.headers) {\n const rateLimitHeaders = createRateLimitHeaders(info)\n mergeHeaders(response.headers, rateLimitHeaders)\n }\n\n return response\n }\n\n // Default rate limit response\n const error = new RateLimitError({\n retryAfter: info.retryAfter ?? 60,\n resetAt: info.reset * 1000,\n message: finalConfig.message,\n })\n\n const response = error.toResponse()\n\n if (finalConfig.headers) {\n const rateLimitHeaders = createRateLimitHeaders(info)\n mergeHeaders(response.headers, rateLimitHeaders)\n }\n\n return response\n }\n\n // Call the handler\n const response = await handler(request, ctx)\n\n // Add rate limit headers to successful response\n if (finalConfig.headers) {\n // Clone response to modify headers\n const newResponse = new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers: new Headers(response.headers),\n })\n\n const rateLimitHeaders = createRateLimitHeaders(info)\n mergeHeaders(newResponse.headers, rateLimitHeaders)\n\n return newResponse\n }\n\n return response\n } catch (error) {\n debug('Error in rate limit middleware', error)\n\n // Re-throw RateLimitError\n if (error instanceof RateLimitError) {\n throw error\n }\n\n // For other errors, let the request through (fail open)\n // This prevents rate limiting from blocking all requests on errors\n // eslint-disable-next-line no-console\n console.error('[next-secure:rate-limit] Error:', error)\n return handler(request, ctx)\n }\n }\n}\n\n/**\n * Create a rate limiter instance for reuse\n *\n * @example\n * ```typescript\n * const apiLimiter = createRateLimiter({\n * limit: 100,\n * window: '15m'\n * })\n *\n * export const GET = apiLimiter(async (req) => Response.json({ ok: true }))\n * export const POST = apiLimiter(async (req) => Response.json({ ok: true }))\n * ```\n */\nexport function createRateLimiter(config: RateLimitConfig) {\n return <TUser = unknown>(\n handler: (\n request: NextRequest,\n context: SecureContext<TUser> & { rateLimit?: RateLimitInfo }\n ) => Promise<Response> | Response\n ) => withRateLimit(handler, config)\n}\n\n/**\n * Check rate limit without wrapping a handler\n * Useful for checking rate limit in existing code\n *\n * @example\n * ```typescript\n * export async function GET(req: NextRequest) {\n * const result = await checkRateLimit(req, {\n * limit: 100,\n * window: '15m'\n * })\n *\n * if (!result.success) {\n * return result.response\n * }\n *\n * // Continue with normal logic\n * return Response.json({ ok: true })\n * }\n * ```\n */\nexport async function checkRateLimit(\n request: NextRequest,\n config: RateLimitConfig\n): Promise<{\n success: boolean\n info: RateLimitInfo\n response?: Response\n headers: Headers\n}> {\n const finalConfig = {\n ...DEFAULT_CONFIG,\n ...config,\n store: config.store ?? getDefaultStore(),\n } as Required<RateLimitConfig>\n\n const windowMs = parseDuration(finalConfig.window)\n const algorithm = getAlgorithm(finalConfig.algorithm)\n\n // Check if should skip\n if (finalConfig.skip) {\n const shouldSkip = await finalConfig.skip(request)\n if (shouldSkip) {\n const info: RateLimitInfo = {\n limit: finalConfig.limit,\n remaining: finalConfig.limit,\n reset: Math.floor(Date.now() / 1000) + Math.floor(windowMs / 1000),\n limited: false,\n }\n return { success: true, info, headers: new Headers() }\n }\n }\n\n const key = await getIdentifier(request, finalConfig.identifier, finalConfig.prefix)\n const info = await algorithm.check(finalConfig.store, key, finalConfig.limit, windowMs)\n const headers = finalConfig.headers ? createRateLimitHeaders(info) : new Headers()\n\n if (info.limited) {\n let response: Response\n\n if (finalConfig.onLimit) {\n response = await finalConfig.onLimit(request, info)\n } else {\n const error = new RateLimitError({\n retryAfter: info.retryAfter ?? 60,\n resetAt: info.reset * 1000,\n message: finalConfig.message,\n })\n response = error.toResponse()\n }\n\n if (finalConfig.headers) {\n mergeHeaders(response.headers, headers)\n }\n\n return { success: false, info, response, headers }\n }\n\n return { success: true, info, headers }\n}\n\n/**\n * Reset rate limit for a specific key\n *\n * @example\n * ```typescript\n * // Reset rate limit for an IP\n * await resetRateLimit('ip', '192.168.1.1')\n *\n * // Reset for a user\n * await resetRateLimit('user', 'user-123')\n * ```\n */\nexport async function resetRateLimit(\n type: 'ip' | 'user' | 'custom',\n identifier: string,\n options?: {\n store?: RateLimitStore\n prefix?: string\n }\n): Promise<void> {\n const store = options?.store ?? getDefaultStore()\n const prefix = options?.prefix ?? 'rl'\n const key = `${prefix}:${type}:${identifier}`\n\n await store.reset(key)\n}\n\n/**\n * Get current rate limit status for a key (without incrementing)\n */\nexport async function getRateLimitStatus(\n type: 'ip' | 'user' | 'custom',\n identifier: string,\n options?: {\n store?: RateLimitStore\n prefix?: string\n }\n): Promise<{ count: number; reset: number } | null> {\n const store = options?.store ?? getDefaultStore()\n const prefix = options?.prefix ?? 'rl'\n const key = `${prefix}:${type}:${identifier}`\n\n return store.get(key)\n}\n\n/**\n * Clear all rate limits (useful for testing)\n */\nexport function clearAllRateLimits(): void {\n if (defaultStore && 'clear' in defaultStore) {\n (defaultStore as MemoryStore).clear()\n }\n}\n","/**\n * next-secure\n *\n * Production-ready security middleware for Next.js 13+ App Router.\n *\n * @example\n * ```typescript\n * import { withRateLimit, withAuth, secure } from 'next-secure'\n *\n * // Simple rate limiting\n * export const GET = withRateLimit(\n * async (req) => Response.json({ ok: true }),\n * { limit: 100, window: '15m' }\n * )\n *\n * // Builder pattern\n * export const POST = secure()\n * .rateLimit({ limit: 10, window: '1m' })\n * .auth({ roles: ['admin'] })\n * .handle(async (req, ctx) => {\n * return Response.json({ user: ctx.user })\n * })\n * ```\n *\n * @packageDocumentation\n */\n\n// =============================================================================\n// Core\n// =============================================================================\n\nexport type {\n NextRequest,\n SecureContext,\n SecureHandler,\n Middleware,\n ErrorResponse,\n RateLimitInfo,\n Duration,\n RateLimitAlgorithm,\n RateLimitIdentifier,\n} from './core/types'\n\nexport {\n SecureError,\n RateLimitError,\n AuthenticationError,\n AuthorizationError,\n ValidationError,\n CsrfError,\n ConfigurationError,\n isSecureError,\n toSecureError,\n} from './core/errors'\n\n// =============================================================================\n// Rate Limiting\n// =============================================================================\n\nexport {\n withRateLimit,\n createRateLimiter,\n checkRateLimit,\n resetRateLimit,\n getRateLimitStatus,\n clearAllRateLimits,\n} from './middleware/rate-limit'\n\nexport type {\n RateLimitConfig,\n RateLimitStore,\n MemoryStoreOptions,\n RedisStoreOptions,\n UpstashStoreOptions,\n} from './middleware/rate-limit'\n\nexport {\n MemoryStore,\n createMemoryStore,\n getGlobalMemoryStore,\n} from './middleware/rate-limit'\n\n// =============================================================================\n// Utilities\n// =============================================================================\n\nexport {\n parseDuration,\n formatDuration,\n nowInSeconds,\n nowInMs,\n sleep,\n} from './utils/time'\n\nexport {\n getClientIp,\n normalizeIp,\n isValidIp,\n isPrivateIp,\n isLocalhost,\n anonymizeIp,\n getGeoInfo,\n} from './utils/ip'\n\n// =============================================================================\n// Version\n// =============================================================================\n\n/**\n * Package version\n */\nexport const VERSION = '0.1.0'\n"]}
1
+ {"version":3,"sources":["../src/core/errors.ts","../src/utils/time.ts","../src/utils/ip.ts","../src/middleware/rate-limit/stores/memory.ts","../src/middleware/rate-limit/algorithms/sliding-window.ts","../src/middleware/rate-limit/algorithms/fixed-window.ts","../src/middleware/rate-limit/algorithms/token-bucket.ts","../src/middleware/rate-limit/middleware.ts","../src/middleware/csrf/token.ts","../src/middleware/csrf/middleware.ts","../src/index.ts"],"names":["response","info","webcrypto","DEFAULT_CONFIG"],"mappings":";;;;;AAOO,IAAM,WAAA,GAAN,cAA0B,KAAA,CAAM;AAAA;AAAA;AAAA;AAAA,EAIrB,UAAA;AAAA;AAAA;AAAA;AAAA,EAKA,IAAA;AAAA;AAAA;AAAA;AAAA,EAKA,OAAA;AAAA,EAEhB,WAAA,CACE,OAAA,EACA,OAAA,GAKI,EAAC,EACL;AACA,IAAA,KAAA,CAAM,OAAA,EAAS,EAAE,KAAA,EAAO,OAAA,CAAQ,OAAO,CAAA;AACvC,IAAA,IAAA,CAAK,IAAA,GAAO,aAAA;AACZ,IAAA,IAAA,CAAK,UAAA,GAAa,QAAQ,UAAA,IAAc,GAAA;AACxC,IAAA,IAAA,CAAK,IAAA,GAAO,QAAQ,IAAA,IAAQ,cAAA;AAC5B,IAAA,IAAA,CAAK,UAAU,OAAA,CAAQ,OAAA;AAGvB,IAAA,IAAI,MAAM,iBAAA,EAAmB;AAC3B,MAAA,KAAA,CAAM,iBAAA,CAAkB,IAAA,EAAM,IAAA,CAAK,WAAW,CAAA;AAAA,IAChD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAA,GAAkC;AAChC,IAAA,OAAO;AAAA,MACL,OAAO,IAAA,CAAK,IAAA;AAAA,MACZ,SAAS,IAAA,CAAK,OAAA;AAAA,MACd,MAAM,IAAA,CAAK,IAAA;AAAA,MACX,GAAI,IAAA,CAAK,OAAA,IAAW,EAAE,OAAA,EAAS,KAAK,OAAA;AAAQ,KAC9C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,OAAA,EAAiC;AAC1C,IAAA,OAAO,IAAI,QAAA,CAAS,IAAA,CAAK,UAAU,IAAA,CAAK,MAAA,EAAQ,CAAA,EAAG;AAAA,MACjD,QAAQ,IAAA,CAAK,UAAA;AAAA,MACb,OAAA,EAAS;AAAA,QACP,cAAA,EAAgB,kBAAA;AAAA,QAChB,GAAG;AAAA;AACL,KACD,CAAA;AAAA,EACH;AACF;AAKO,IAAM,cAAA,GAAN,cAA6B,WAAA,CAAY;AAAA;AAAA;AAAA;AAAA,EAI9B,UAAA;AAAA;AAAA;AAAA;AAAA,EAKA,OAAA;AAAA,EAEhB,YACE,OAAA,EAMA;AACA,IAAA,KAAA,CAAM,OAAA,CAAQ,WAAW,mBAAA,EAAqB;AAAA,MAC5C,UAAA,EAAY,GAAA;AAAA,MACZ,IAAA,EAAM,qBAAA;AAAA,MACN,SAAS,OAAA,CAAQ;AAAA,KAClB,CAAA;AACD,IAAA,IAAA,CAAK,IAAA,GAAO,gBAAA;AACZ,IAAA,IAAA,CAAK,aAAa,OAAA,CAAQ,UAAA;AAC1B,IAAA,IAAA,CAAK,UAAU,OAAA,CAAQ,OAAA;AAAA,EACzB;AAAA,EAES,MAAA,GAAkC;AACzC,IAAA,OAAO;AAAA,MACL,GAAG,MAAM,MAAA,EAAO;AAAA,MAChB,YAAY,IAAA,CAAK;AAAA,KACnB;AAAA,EACF;AAAA,EAES,WAAW,OAAA,EAAiC;AACnD,IAAA,OAAO,IAAI,QAAA,CAAS,IAAA,CAAK,UAAU,IAAA,CAAK,MAAA,EAAQ,CAAA,EAAG;AAAA,MACjD,QAAQ,IAAA,CAAK,UAAA;AAAA,MACb,OAAA,EAAS;AAAA,QACP,cAAA,EAAgB,kBAAA;AAAA,QAChB,aAAA,EAAe,MAAA,CAAO,IAAA,CAAK,UAAU,CAAA;AAAA,QACrC,GAAG;AAAA;AACL,KACD,CAAA;AAAA,EACH;AACF;AAKO,IAAM,mBAAA,GAAN,cAAkC,WAAA,CAAY;AAAA,EACnD,WAAA,CACE,OAAA,GAAU,yBAAA,EACV,OAAA,GAII,EAAC,EACL;AACA,IAAA,KAAA,CAAM,OAAA,EAAS;AAAA,MACb,UAAA,EAAY,GAAA;AAAA,MACZ,IAAA,EAAM,QAAQ,IAAA,IAAQ,yBAAA;AAAA,MACtB,SAAS,OAAA,CAAQ,OAAA;AAAA,MACjB,OAAO,OAAA,CAAQ;AAAA,KAChB,CAAA;AACD,IAAA,IAAA,CAAK,IAAA,GAAO,qBAAA;AAAA,EACd;AACF;AAKO,IAAM,kBAAA,GAAN,cAAiC,WAAA,CAAY;AAAA,EAClD,WAAA,CACE,OAAA,GAAU,eAAA,EACV,OAAA,GAII,EAAC,EACL;AACA,IAAA,KAAA,CAAM,OAAA,EAAS;AAAA,MACb,UAAA,EAAY,GAAA;AAAA,MACZ,IAAA,EAAM,QAAQ,IAAA,IAAQ,eAAA;AAAA,MACtB,SAAS,OAAA,CAAQ,OAAA;AAAA,MACjB,OAAO,OAAA,CAAQ;AAAA,KAChB,CAAA;AACD,IAAA,IAAA,CAAK,IAAA,GAAO,oBAAA;AAAA,EACd;AACF;AAKO,IAAM,eAAA,GAAN,cAA8B,WAAA,CAAY;AAAA;AAAA;AAAA;AAAA,EAI/B,MAAA;AAAA,EAMhB,WAAA,CACE,MAAA,EACA,OAAA,GAAU,mBAAA,EACV;AACA,IAAA,KAAA,CAAM,OAAA,EAAS;AAAA,MACb,UAAA,EAAY,GAAA;AAAA,MACZ,IAAA,EAAM,kBAAA;AAAA,MACN,OAAA,EAAS,EAAE,MAAA;AAAO,KACnB,CAAA;AACD,IAAA,IAAA,CAAK,IAAA,GAAO,iBAAA;AACZ,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AAAA,EAChB;AAAA,EAES,MAAA,GAAkC;AACzC,IAAA,OAAO;AAAA,MACL,GAAG,MAAM,MAAA,EAAO;AAAA,MAChB,QAAQ,IAAA,CAAK;AAAA,KACf;AAAA,EACF;AACF;AAKO,IAAM,SAAA,GAAN,cAAwB,WAAA,CAAY;AAAA,EACzC,WAAA,CACE,OAAA,GAAU,+BAAA,EACV,OAAA,GAEI,EAAC,EACL;AACA,IAAA,KAAA,CAAM,OAAA,EAAS;AAAA,MACb,UAAA,EAAY,GAAA;AAAA,MACZ,IAAA,EAAM,oBAAA;AAAA,MACN,SAAS,OAAA,CAAQ;AAAA,KAClB,CAAA;AACD,IAAA,IAAA,CAAK,IAAA,GAAO,WAAA;AAAA,EACd;AACF;AAKO,IAAM,kBAAA,GAAN,cAAiC,WAAA,CAAY;AAAA,EAClD,WAAA,CACE,OAAA,EACA,OAAA,GAGI,EAAC,EACL;AACA,IAAA,KAAA,CAAM,OAAA,EAAS;AAAA,MACb,UAAA,EAAY,GAAA;AAAA,MACZ,IAAA,EAAM,qBAAA;AAAA,MACN,SAAS,OAAA,CAAQ,OAAA;AAAA,MACjB,OAAO,OAAA,CAAQ;AAAA,KAChB,CAAA;AACD,IAAA,IAAA,CAAK,IAAA,GAAO,oBAAA;AAAA,EACd;AACF;AAKO,SAAS,cAAc,KAAA,EAAsC;AAClE,EAAA,OAAO,KAAA,YAAiB,WAAA;AAC1B;AAKO,SAAS,cAAc,KAAA,EAA6B;AACzD,EAAA,IAAI,iBAAiB,WAAA,EAAa;AAChC,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,IAAI,iBAAiB,KAAA,EAAO;AAC1B,IAAA,OAAO,IAAI,WAAA,CAAY,KAAA,CAAM,OAAA,EAAS;AAAA,MACpC,KAAA,EAAO;AAAA,KACR,CAAA;AAAA,EACH;AAEA,EAAA,OAAO,IAAI,WAAA,CAAY,MAAA,CAAO,KAAK,CAAC,CAAA;AACtC;;;AC5PA,IAAM,UAAA,GAAqC;AAAA,EACzC,EAAA,EAAI,CAAA;AAAA,EACJ,CAAA,EAAG,GAAA;AAAA,EACH,GAAG,EAAA,GAAK,GAAA;AAAA,EACR,CAAA,EAAG,KAAK,EAAA,GAAK,GAAA;AAAA,EACb,CAAA,EAAG,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK;AACpB,CAAA;AAKA,IAAM,iBAAA,GAA4C;AAAA,EAChD,WAAA,EAAa,IAAA;AAAA,EACb,YAAA,EAAc,IAAA;AAAA,EACd,MAAA,EAAQ,GAAA;AAAA,EACR,OAAA,EAAS,GAAA;AAAA,EACT,GAAA,EAAK,GAAA;AAAA,EACL,IAAA,EAAM,GAAA;AAAA,EACN,MAAA,EAAQ,GAAA;AAAA,EACR,OAAA,EAAS,GAAA;AAAA,EACT,GAAA,EAAK,GAAA;AAAA,EACL,IAAA,EAAM,GAAA;AAAA,EACN,IAAA,EAAM,GAAA;AAAA,EACN,KAAA,EAAO,GAAA;AAAA,EACP,EAAA,EAAI,GAAA;AAAA,EACJ,GAAA,EAAK,GAAA;AAAA,EACL,GAAA,EAAK,GAAA;AAAA,EACL,IAAA,EAAM;AACR,CAAA;AAoBO,SAAS,cAAc,QAAA,EAAqC;AAEjE,EAAA,IAAI,OAAO,aAAa,QAAA,EAAU;AAChC,IAAA,IAAI,WAAW,CAAA,EAAG;AAChB,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,kBAAA,EAAqB,QAAQ,CAAA,gCAAA,CAAkC,CAAA;AAAA,IACjF;AACA,IAAA,OAAO,QAAA;AAAA,EACT;AAGA,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,IAAA,EAAK,CAAE,WAAA,EAAY;AAE1C,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,MAAM,IAAI,MAAM,gCAAgC,CAAA;AAAA,EAClD;AAGA,EAAA,MAAM,YAAA,GAAe,OAAO,KAAK,CAAA;AACjC,EAAA,IAAI,CAAC,KAAA,CAAM,YAAY,CAAA,EAAG;AACxB,IAAA,IAAI,eAAe,CAAA,EAAG;AACpB,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,kBAAA,EAAqB,QAAQ,CAAA,gCAAA,CAAkC,CAAA;AAAA,IACjF;AACA,IAAA,OAAO,YAAA;AAAA,EACT;AAGA,EAAA,IAAI,OAAA,GAAU,CAAA;AACd,EAAA,MAAM,KAAA,GAAQ,6BAAA;AACd,EAAA,IAAI,KAAA;AACJ,EAAA,IAAI,QAAA,GAAW,KAAA;AAEf,EAAA,OAAA,CAAQ,KAAA,GAAQ,KAAA,CAAM,IAAA,CAAK,KAAK,OAAO,IAAA,EAAM;AAC3C,IAAA,QAAA,GAAW,IAAA;AACX,IAAA,MAAM,KAAA,GAAQ,UAAA,CAAW,KAAA,CAAM,CAAC,CAAC,CAAA;AACjC,IAAA,IAAI,IAAA,GAAO,MAAM,CAAC,CAAA;AAGlB,IAAA,IAAI,QAAQ,iBAAA,EAAmB;AAC7B,MAAA,IAAA,GAAO,kBAAkB,IAAI,CAAA;AAAA,IAC/B;AAGA,IAAA,MAAM,UAAA,GAAa,WAAW,IAAI,CAAA;AAClC,IAAA,IAAI,eAAe,MAAA,EAAW;AAC5B,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,wBAAA,EAA2B,IAAI,CAAA,MAAA,EAAS,QAAQ,CAAA,6DAAA;AAAA,OAElD;AAAA,IACF;AAEA,IAAA,OAAA,IAAW,KAAA,GAAQ,UAAA;AAAA,EACrB;AAEA,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,6BAA6B,QAAQ,CAAA,6DAAA;AAAA,KAEvC;AAAA,EACF;AAEA,EAAA,OAAO,IAAA,CAAK,MAAM,OAAO,CAAA;AAC3B;AAkBO,SAAS,cAAA,CACd,EAAA,EACA,OAAA,GAaI,EAAC,EACG;AACR,EAAA,MAAM,EAAE,IAAA,GAAO,KAAA,EAAO,WAAW,CAAA,EAAG,SAAA,GAAY,KAAI,GAAI,OAAA;AAExD,EAAA,IAAI,KAAK,CAAA,EAAG;AACV,IAAA,OAAO,CAAA,CAAA,EAAI,cAAA,CAAe,CAAC,EAAA,EAAI,OAAO,CAAC,CAAA,CAAA;AAAA,EACzC;AAEA,EAAA,IAAI,OAAO,CAAA,EAAG;AACZ,IAAA,OAAO,OAAO,WAAA,GAAc,IAAA;AAAA,EAC9B;AAEA,EAAA,MAAM,KAAA,GAAmF;AAAA,IACvF,EAAE,OAAO,KAAA,EAAU,KAAA,EAAO,KAAK,IAAA,EAAM,KAAA,EAAO,YAAY,MAAA,EAAO;AAAA,IAC/D,EAAE,OAAO,IAAA,EAAS,KAAA,EAAO,KAAK,IAAA,EAAM,MAAA,EAAQ,YAAY,OAAA,EAAQ;AAAA,IAChE,EAAE,OAAO,GAAA,EAAO,KAAA,EAAO,KAAK,IAAA,EAAM,QAAA,EAAU,YAAY,SAAA,EAAU;AAAA,IAClE,EAAE,OAAO,GAAA,EAAM,KAAA,EAAO,KAAK,IAAA,EAAM,QAAA,EAAU,YAAY,SAAA,EAAU;AAAA,IACjE,EAAE,OAAO,CAAA,EAAG,KAAA,EAAO,MAAM,IAAA,EAAM,aAAA,EAAe,YAAY,cAAA;AAAe,GAC3E;AAEA,EAAA,MAAM,QAAkB,EAAC;AACzB,EAAA,IAAI,SAAA,GAAY,EAAA;AAEhB,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,IAAI,KAAA,CAAM,UAAU,QAAA,EAAU;AAC9B,IAAA,IAAI,SAAA,IAAa,KAAK,KAAA,EAAO;AAC3B,MAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,SAAA,GAAY,KAAK,KAAK,CAAA;AAC/C,MAAA,SAAA,GAAY,YAAY,IAAA,CAAK,KAAA;AAE7B,MAAA,IAAI,IAAA,EAAM;AACR,QAAA,KAAA,CAAM,IAAA,CAAK,CAAA,EAAG,KAAK,CAAA,CAAA,EAAI,KAAA,KAAU,IAAI,IAAA,CAAK,IAAA,GAAO,IAAA,CAAK,UAAU,CAAA,CAAE,CAAA;AAAA,MACpE,CAAA,MAAO;AACL,QAAA,KAAA,CAAM,KAAK,CAAA,EAAG,KAAK,CAAA,EAAG,IAAA,CAAK,KAAK,CAAA,CAAE,CAAA;AAAA,MACpC;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO,KAAA,CAAM,KAAK,SAAS,CAAA;AAC7B;AAKO,SAAS,YAAA,GAAuB;AACrC,EAAA,OAAO,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,GAAA,KAAQ,GAAI,CAAA;AACrC;AAKO,SAAS,OAAA,GAAkB;AAChC,EAAA,OAAO,KAAK,GAAA,EAAI;AAClB;AA+BO,SAAS,MAAM,QAAA,EAA4C;AAChE,EAAA,MAAM,EAAA,GAAK,cAAc,QAAQ,CAAA;AACjC,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,YAAY,UAAA,CAAW,OAAA,EAAS,EAAE,CAAC,CAAA;AACzD;AAiCO,SAAS,YAAY,EAAA,EAAoB;AAC9C,EAAA,OAAO,IAAA,CAAK,KAAA,CAAM,EAAA,GAAK,GAAI,CAAA;AAC7B;;;ACvQA,IAAM,UAAA,GAAa;AAAA;AAAA,EAEjB,kBAAA;AAAA;AAAA,EAEA,WAAA;AAAA;AAAA,EAEA,iBAAA;AAAA;AAAA,EAEA,aAAA;AAAA;AAAA,EAEA,WAAA;AAAA;AAAA,EAEA,kBAAA;AAAA;AAAA,EAEA,gBAAA;AAAA;AAAA,EAEA,qBAAA;AAAA;AAAA,EAEA;AACF,CAAA;AAKA,IAAM,mBAAA,GAAsB;AAAA,EAC1B,QAAA;AAAA;AAAA,EACA,OAAA;AAAA;AAAA,EACA,+BAAA;AAAA;AAAA,EACA,aAAA;AAAA;AAAA,EACA,OAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA;AAAA;AACF,CAAA;AAKA,IAAM,UAAA,GAAa,6FAAA;AAKnB,IAAM,UAAA,GAAa,uHAAA;AA2CZ,SAAS,WAAA,CAAY,OAAA,EAAsB,OAAA,GAAwB,EAAC,EAAW;AACpF,EAAA,MAAM,EAAE,aAAa,IAAA,EAAM,aAAA,GAAgB,EAAC,EAAG,QAAA,GAAW,aAAY,GAAI,OAAA;AAG1E,EAAA,IAAI,QAAQ,EAAA,EAAI;AACd,IAAA,OAAO,WAAA,CAAY,QAAQ,EAAE,CAAA;AAAA,EAC/B;AAEA,EAAA,IAAI,CAAC,UAAA,EAAY;AACf,IAAA,OAAO,QAAA;AAAA,EACT;AAGA,EAAA,KAAA,MAAW,UAAU,aAAA,EAAe;AAClC,IAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,MAAM,CAAA;AACxC,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,MAAM,EAAA,GAAK,kBAAkB,KAAK,CAAA;AAClC,MAAA,IAAI,IAAI,OAAO,EAAA;AAAA,IACjB;AAAA,EACF;AAGA,EAAA,KAAA,MAAW,UAAU,UAAA,EAAY;AAC/B,IAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,MAAM,CAAA;AACxC,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,MAAM,EAAA,GAAK,kBAAkB,KAAK,CAAA;AAClC,MAAA,IAAI,IAAI,OAAO,EAAA;AAAA,IACjB;AAAA,EACF;AAEA,EAAA,OAAO,QAAA;AACT;AAMA,SAAS,kBAAkB,WAAA,EAAoC;AAG7D,EAAA,MAAM,GAAA,GAAM,WAAA,CAAY,KAAA,CAAM,GAAG,CAAA,CAAE,IAAI,CAAC,EAAA,KAAO,EAAA,CAAG,IAAA,EAAM,CAAA;AAExD,EAAA,KAAA,MAAW,MAAM,GAAA,EAAK;AACpB,IAAA,MAAM,UAAA,GAAa,YAAY,EAAE,CAAA;AACjC,IAAA,IAAI,SAAA,CAAU,UAAU,CAAA,EAAG;AACzB,MAAA,OAAO,UAAA;AAAA,IACT;AAAA,EACF;AAEA,EAAA,OAAO,IAAA;AACT;AAQO,SAAS,YAAY,EAAA,EAAoB;AAC9C,EAAA,IAAI,UAAA,GAAa,GAAG,IAAA,EAAK;AAGzB,EAAA,IAAI,WAAW,UAAA,CAAW,GAAG,KAAK,UAAA,CAAW,QAAA,CAAS,GAAG,CAAA,EAAG;AAC1D,IAAA,UAAA,GAAa,WAAW,KAAA,CAAM,CAAA,EAAG,UAAA,CAAW,OAAA,CAAQ,GAAG,CAAC,CAAA;AAAA,EAC1D;AAIA,EAAA,IAAI,UAAA,CAAW,SAAS,GAAG,CAAA,IAAK,CAAC,UAAA,CAAW,QAAA,CAAS,IAAI,CAAA,EAAG;AAC1D,IAAA,MAAM,SAAA,GAAY,UAAA,CAAW,WAAA,CAAY,GAAG,CAAA;AAC5C,IAAA,MAAM,aAAA,GAAgB,UAAA,CAAW,KAAA,CAAM,SAAA,GAAY,CAAC,CAAA;AACpD,IAAA,IAAI,OAAA,CAAQ,IAAA,CAAK,aAAa,CAAA,EAAG;AAC/B,MAAA,UAAA,GAAa,UAAA,CAAW,KAAA,CAAM,CAAA,EAAG,SAAS,CAAA;AAAA,IAC5C;AAAA,EACF;AAGA,EAAA,IAAI,UAAA,CAAW,WAAA,EAAY,CAAE,UAAA,CAAW,SAAS,CAAA,EAAG;AAClD,IAAA,MAAM,QAAA,GAAW,UAAA,CAAW,KAAA,CAAM,CAAC,CAAA;AACnC,IAAA,IAAI,WAAA,CAAY,QAAQ,CAAA,EAAG;AACzB,MAAA,OAAO,QAAA;AAAA,IACT;AAAA,EACF;AAEA,EAAA,OAAO,UAAA;AACT;AAKO,SAAS,UAAU,EAAA,EAAqB;AAC7C,EAAA,OAAO,WAAA,CAAY,EAAE,CAAA,IAAK,WAAA,CAAY,EAAE,CAAA;AAC1C;AAKO,SAAS,YAAY,EAAA,EAAqB;AAC/C,EAAA,OAAO,UAAA,CAAW,KAAK,EAAE,CAAA;AAC3B;AAKO,SAAS,YAAY,EAAA,EAAqB;AAC/C,EAAA,OAAO,WAAW,IAAA,CAAK,EAAE,CAAA,IAAK,EAAA,KAAO,SAAS,EAAA,KAAO,IAAA;AACvD;AAKO,SAAS,YAAY,EAAA,EAAqB;AAC/C,EAAA,OAAO,oBAAoB,IAAA,CAAK,CAAC,YAAY,OAAA,CAAQ,IAAA,CAAK,EAAE,CAAC,CAAA;AAC/D;AAKO,SAAS,YAAY,EAAA,EAAqB;AAC/C,EAAA,OAAO,EAAA,KAAO,WAAA,IAAe,EAAA,KAAO,KAAA,IAAS,EAAA,KAAO,WAAA;AACtD;AA2CO,SAAS,YAAY,EAAA,EAAoB;AAC9C,EAAA,MAAM,UAAA,GAAa,YAAY,EAAE,CAAA;AAEjC,EAAA,IAAI,WAAA,CAAY,UAAU,CAAA,EAAG;AAC3B,IAAA,MAAM,KAAA,GAAQ,UAAA,CAAW,KAAA,CAAM,GAAG,CAAA;AAClC,IAAA,KAAA,CAAM,CAAC,CAAA,GAAI,KAAA;AACX,IAAA,OAAO,KAAA,CAAM,KAAK,GAAG,CAAA;AAAA,EACvB;AAEA,EAAA,IAAI,WAAA,CAAY,UAAU,CAAA,EAAG;AAC3B,IAAA,MAAM,KAAA,GAAQ,UAAA,CAAW,KAAA,CAAM,GAAG,CAAA;AAClC,IAAA,IAAI,KAAA,CAAM,SAAS,CAAA,EAAG;AACpB,MAAA,KAAA,CAAM,KAAA,CAAM,MAAA,GAAS,CAAC,CAAA,GAAI,MAAA;AAAA,IAC5B;AACA,IAAA,OAAO,KAAA,CAAM,KAAK,GAAG,CAAA;AAAA,EACvB;AAEA,EAAA,OAAO,iBAAA;AACT;AAMO,SAAS,WAAW,OAAA,EAMzB;AAEA,EAAA,IAAI,QAAQ,GAAA,EAAK;AACf,IAAA,OAAO;AAAA,MACL,OAAA,EAAS,QAAQ,GAAA,CAAI,OAAA;AAAA,MACrB,IAAA,EAAM,QAAQ,GAAA,CAAI,IAAA;AAAA,MAClB,MAAA,EAAQ,QAAQ,GAAA,CAAI,MAAA;AAAA,MACpB,QAAA,EAAU,QAAQ,GAAA,CAAI,QAAA;AAAA,MACtB,SAAA,EAAW,QAAQ,GAAA,CAAI;AAAA,KACzB;AAAA,EACF;AAGA,EAAA,OAAO;AAAA,IACL,OAAA,EAAS,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,cAAc,CAAA,IAAK,MAAA;AAAA,IAChD,IAAA,EAAM,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,WAAW,CAAA,IAAK,MAAA;AAAA,IAC1C,MAAA,EAAQ,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,WAAW,CAAA,IAAK,MAAA;AAAA,IAC5C,QAAA,EAAU,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,UAAU,CAAA,IAAK,MAAA;AAAA,IAC7C,SAAA,EAAW,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,WAAW,CAAA,IAAK;AAAA,GACjD;AACF;;;ACxQO,IAAM,cAAN,MAA4C;AAAA,EACjC,IAAA,GAAO,QAAA;AAAA,EAEf,KAAA;AAAA,EACA,YAAA,GAAsD,IAAA;AAAA,EAC7C,OAAA;AAAA,EACA,eAAA;AAAA,EAEjB,WAAA,CAAY,OAAA,GAA8B,EAAC,EAAG;AAC5C,IAAA,MAAM,EAAE,eAAA,GAAkB,GAAA,EAAO,OAAA,GAAU,KAAM,GAAI,OAAA;AAErD,IAAA,IAAA,CAAK,KAAA,uBAAY,GAAA,EAAI;AACrB,IAAA,IAAA,CAAK,OAAA,GAAU,OAAA;AACf,IAAA,IAAA,CAAK,eAAA,GAAkB,eAAA;AAGvB,IAAA,IAAI,OAAO,WAAA,KAAgB,WAAA,IAAe,eAAA,GAAkB,CAAA,EAAG;AAC7D,MAAA,IAAA,CAAK,iBAAA,EAAkB;AAAA,IACzB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,SAAA,CACJ,GAAA,EACA,QAAA,EAC2C;AAC3C,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,IAAA,MAAM,YAAA,GAAe,WAAA,CAAY,GAAA,GAAM,QAAQ,CAAA;AAE/C,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,GAAG,CAAA;AAEnC,IAAA,IAAI,QAAA,EAAU;AAEZ,MAAA,QAAA,CAAS,KAAA,EAAA;AAET,MAAA,IAAA,CAAK,KAAA,CAAM,OAAO,GAAG,CAAA;AACrB,MAAA,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,GAAA,EAAK,QAAQ,CAAA;AAC5B,MAAA,OAAO,EAAE,KAAA,EAAO,QAAA,CAAS,KAAA,EAAO,KAAA,EAAO,SAAS,KAAA,EAAM;AAAA,IACxD;AAGA,IAAA,MAAM,KAAA,GAAqB;AAAA,MACzB,KAAA,EAAO,CAAA;AAAA,MACP,KAAA,EAAO,YAAA;AAAA,MACP,SAAA,EAAW;AAAA,KACb;AAGA,IAAA,IAAI,IAAA,CAAK,KAAA,CAAM,IAAA,IAAQ,IAAA,CAAK,OAAA,EAAS;AACnC,MAAA,IAAA,CAAK,WAAA,EAAY;AAAA,IACnB;AAEA,IAAA,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,GAAA,EAAK,KAAK,CAAA;AACzB,IAAA,OAAO,EAAE,KAAA,EAAO,CAAA,EAAG,KAAA,EAAO,YAAA,EAAa;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,IAAI,GAAA,EAA+D;AACvE,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,GAAG,CAAA;AAEhC,IAAA,IAAI,CAAC,KAAA,EAAO;AACV,MAAA,OAAO,IAAA;AAAA,IACT;AAGA,IAAA,MAAM,MAAM,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,GAAA,KAAQ,GAAI,CAAA;AACxC,IAAA,IAAI,KAAA,CAAM,SAAS,GAAA,EAAK;AACtB,MAAA,IAAA,CAAK,KAAA,CAAM,OAAO,GAAG,CAAA;AACrB,MAAA,OAAO,IAAA;AAAA,IACT;AAEA,IAAA,OAAO,EAAE,KAAA,EAAO,KAAA,CAAM,KAAA,EAAO,KAAA,EAAO,MAAM,KAAA,EAAM;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,MAAM,GAAA,EAA4B;AACtC,IAAA,IAAA,CAAK,KAAA,CAAM,OAAO,GAAG,CAAA;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SAAA,GAA8B;AAClC,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAA,GAAyB;AAC7B,IAAA,MAAM,MAAM,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,GAAA,KAAQ,GAAI,CAAA;AACxC,IAAA,MAAM,eAAyB,EAAC;AAEhC,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,CAAA,IAAK,KAAK,KAAA,EAAO;AACrC,MAAA,IAAI,KAAA,CAAM,SAAS,GAAA,EAAK;AACtB,QAAA,YAAA,CAAa,KAAK,GAAG,CAAA;AAAA,MACvB;AAAA,IACF;AAEA,IAAA,KAAA,MAAW,OAAO,YAAA,EAAc;AAC9B,MAAA,IAAA,CAAK,KAAA,CAAM,OAAO,GAAG,CAAA;AAAA,IACvB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,KAAA,GAAuB;AAC3B,IAAA,IAAA,CAAK,gBAAA,EAAiB;AACtB,IAAA,IAAA,CAAK,MAAM,KAAA,EAAM;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,IAAA,GAAe;AACjB,IAAA,OAAO,KAAK,KAAA,CAAM,IAAA;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAKA,KAAA,GAAc;AACZ,IAAA,IAAA,CAAK,MAAM,KAAA,EAAM;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAA,GAA0B;AAChC,IAAA,IAAI,KAAK,YAAA,EAAc;AAEvB,IAAA,IAAA,CAAK,YAAA,GAAe,YAAY,MAAM;AACpC,MAAA,KAAK,KAAK,OAAA,EAAQ;AAAA,IACpB,CAAA,EAAG,KAAK,eAAe,CAAA;AAGvB,IAAA,IAAI,OAAO,IAAA,CAAK,YAAA,KAAiB,QAAA,IAAY,OAAA,IAAW,KAAK,YAAA,EAAc;AACzE,MAAC,IAAA,CAAK,aAAgC,KAAA,EAAM;AAAA,IAC9C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAA,GAAyB;AAC/B,IAAA,IAAI,KAAK,YAAA,EAAc;AACrB,MAAA,aAAA,CAAc,KAAK,YAAY,CAAA;AAC/B,MAAA,IAAA,CAAK,YAAA,GAAe,IAAA;AAAA,IACtB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,WAAA,GAAoB;AAE1B,IAAA,MAAM,YAAA,GAAe,IAAA,CAAK,IAAA,CAAK,IAAA,CAAK,UAAU,GAAG,CAAA;AAEjD,IAAA,IAAI,OAAA,GAAU,CAAA;AACd,IAAA,KAAA,MAAW,GAAA,IAAO,IAAA,CAAK,KAAA,CAAM,IAAA,EAAK,EAAG;AACnC,MAAA,IAAI,WAAW,YAAA,EAAc;AAC7B,MAAA,IAAA,CAAK,KAAA,CAAM,OAAO,GAAG,CAAA;AACrB,MAAA,OAAA,EAAA;AAAA,IACF;AAAA,EACF;AACF;AAKO,SAAS,kBAAkB,OAAA,EAA2C;AAC3E,EAAA,OAAO,IAAI,YAAY,OAAO,CAAA;AAChC;AAMA,IAAI,WAAA,GAAkC,IAAA;AAK/B,SAAS,qBAAqB,OAAA,EAA2C;AAC9E,EAAA,IAAI,CAAC,WAAA,EAAa;AAChB,IAAA,WAAA,GAAc,IAAI,YAAY,OAAO,CAAA;AAAA,EACvC;AACA,EAAA,OAAO,WAAA;AACT;;;AC/MO,IAAM,yBAAN,MAA+D;AAAA,EACpD,IAAA,GAAO,gBAAA;AAAA;AAAA;AAAA;AAAA,EAKvB,MAAM,KAAA,CACJ,KAAA,EACA,GAAA,EACA,OACA,QAAA,EACwB;AACxB,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AAGrB,IAAA,MAAM,WAAA,GAAc,IAAA,CAAK,KAAA,CAAM,GAAA,GAAM,QAAQ,CAAA,GAAI,QAAA;AACjD,IAAA,MAAM,YAAY,WAAA,GAAc,QAAA;AAChC,IAAA,MAAM,sBAAsB,WAAA,GAAc,QAAA;AAG1C,IAAA,MAAM,cAAA,GAAA,CAAkB,MAAM,WAAA,IAAe,QAAA;AAG7C,IAAA,MAAM,UAAA,GAAa,CAAA,EAAG,GAAG,CAAA,CAAA,EAAI,WAAW,CAAA,CAAA;AACxC,IAAA,MAAM,WAAA,GAAc,CAAA,EAAG,GAAG,CAAA,CAAA,EAAI,mBAAmB,CAAA,CAAA;AAGjD,IAAA,MAAM,CAAC,WAAA,EAAa,YAAY,CAAA,GAAI,MAAM,QAAQ,GAAA,CAAI;AAAA,MACpD,KAAA,CAAM,IAAI,UAAU,CAAA;AAAA,MACpB,KAAA,CAAM,IAAI,WAAW;AAAA,KACtB,CAAA;AAED,IAAA,MAAM,YAAA,GAAe,aAAa,KAAA,IAAS,CAAA;AAC3C,IAAA,MAAM,aAAA,GAAgB,cAAc,KAAA,IAAS,CAAA;AAI7C,IAAA,MAAM,iBAAiB,CAAA,GAAI,cAAA;AAC3B,IAAA,MAAM,aAAA,GAAgB,YAAA,GAAe,IAAA,CAAK,KAAA,CAAM,gBAAgB,cAAc,CAAA;AAG9E,IAAA,MAAM,KAAA,GAAQ,YAAY,SAAS,CAAA;AAGnC,IAAA,IAAI,iBAAiB,KAAA,EAAO;AAE1B,MAAA,MAAM,aAAa,IAAA,CAAK,mBAAA;AAAA,QACtB,YAAA;AAAA,QACA,aAAA;AAAA,QACA,KAAA;AAAA,QACA,QAAA;AAAA,QACA;AAAA,OACF;AAEA,MAAA,OAAO;AAAA,QACL,KAAA;AAAA,QACA,SAAA,EAAW,CAAA;AAAA,QACX,KAAA;AAAA,QACA,OAAA,EAAS,IAAA;AAAA,QACT;AAAA,OACF;AAAA,IACF;AAGA,IAAA,MAAM,KAAA,CAAM,SAAA,CAAU,UAAA,EAAY,QAAQ,CAAA;AAG1C,IAAA,MAAM,YAAY,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,KAAA,GAAQ,gBAAgB,CAAC,CAAA;AAEvD,IAAA,OAAO;AAAA,MACL,KAAA;AAAA,MACA,SAAA;AAAA,MACA,KAAA;AAAA,MACA,OAAA,EAAS;AAAA,KACX;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAAA,CACN,YAAA,EACA,aAAA,EACA,KAAA,EACA,UACA,cAAA,EACQ;AAER,IAAA,IAAI,kBAAkB,CAAA,EAAG;AACvB,MAAA,OAAO,IAAA,CAAK,IAAA,CAAA,CAAM,CAAA,GAAI,cAAA,IAAkB,WAAW,GAAI,CAAA;AAAA,IACzD;AAMA,IAAA,MAAM,gBAAA,GAAmB,CAAA,GAAA,CAAK,KAAA,GAAQ,YAAA,IAAgB,aAAA;AAEtD,IAAA,IAAI,oBAAoB,cAAA,EAAgB;AAEtC,MAAA,OAAO,CAAA;AAAA,IACT;AAEA,IAAA,IAAI,oBAAoB,CAAA,EAAG;AAEzB,MAAA,MAAM,wBAAA,GAAA,CAA4B,IAAI,cAAA,IAAkB,QAAA;AACxD,MAAA,OAAO,IAAA,CAAK,IAAA,CAAK,wBAAA,GAA2B,GAAI,CAAA;AAAA,IAClD;AAGA,IAAA,MAAM,UAAA,GAAA,CAAc,mBAAmB,cAAA,IAAkB,QAAA;AACzD,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,UAAA,GAAa,GAAI,CAAA;AAAA,EACpC;AACF,CAAA;;;AC7GO,IAAM,uBAAN,MAA6D;AAAA,EAClD,IAAA,GAAO,cAAA;AAAA;AAAA;AAAA;AAAA,EAKvB,MAAM,KAAA,CACJ,KAAA,EACA,GAAA,EACA,OACA,QAAA,EACwB;AACxB,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AAGrB,IAAA,MAAM,WAAA,GAAc,IAAA,CAAK,KAAA,CAAM,GAAA,GAAM,QAAQ,CAAA,GAAI,QAAA;AACjD,IAAA,MAAM,YAAY,WAAA,GAAc,QAAA;AAChC,IAAA,MAAM,KAAA,GAAQ,YAAY,SAAS,CAAA;AAGnC,IAAA,MAAM,SAAA,GAAY,CAAA,EAAG,GAAG,CAAA,CAAA,EAAI,WAAW,CAAA,CAAA;AAGvC,IAAA,MAAM,IAAA,GAAO,MAAM,KAAA,CAAM,GAAA,CAAI,SAAS,CAAA;AACtC,IAAA,MAAM,YAAA,GAAe,MAAM,KAAA,IAAS,CAAA;AAGpC,IAAA,IAAI,gBAAgB,KAAA,EAAO;AACzB,MAAA,MAAM,UAAA,GAAa,IAAA,CAAK,IAAA,CAAA,CAAM,SAAA,GAAY,OAAO,GAAI,CAAA;AAErD,MAAA,OAAO;AAAA,QACL,KAAA;AAAA,QACA,SAAA,EAAW,CAAA;AAAA,QACX,KAAA;AAAA,QACA,OAAA,EAAS,IAAA;AAAA,QACT,UAAA,EAAY,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,UAAU;AAAA,OACpC;AAAA,IACF;AAGA,IAAA,MAAM,EAAE,KAAA,EAAM,GAAI,MAAM,KAAA,CAAM,SAAA,CAAU,WAAW,QAAQ,CAAA;AAG3D,IAAA,IAAI,QAAQ,KAAA,EAAO;AACjB,MAAA,MAAM,UAAA,GAAa,IAAA,CAAK,IAAA,CAAA,CAAM,SAAA,GAAY,OAAO,GAAI,CAAA;AAErD,MAAA,OAAO;AAAA,QACL,KAAA;AAAA,QACA,SAAA,EAAW,CAAA;AAAA,QACX,KAAA;AAAA,QACA,OAAA,EAAS,IAAA;AAAA,QACT,UAAA,EAAY,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,UAAU;AAAA,OACpC;AAAA,IACF;AAEA,IAAA,OAAO;AAAA,MACL,KAAA;AAAA,MACA,SAAA,EAAW,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,QAAQ,KAAK,CAAA;AAAA,MACpC,KAAA;AAAA,MACA,OAAA,EAAS;AAAA,KACX;AAAA,EACF;AACF,CAAA;;;AC3DO,IAAM,uBAAN,MAA6D;AAAA,EAClD,IAAA,GAAO,cAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMf,OAAA,uBAA6C,GAAA,EAAI;AAAA;AAAA;AAAA;AAAA,EAKxC,UAAA,GAAa,GAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAU9B,MAAM,KAAA,CACJ,MAAA,EACA,GAAA,EACA,OACA,QAAA,EACwB;AACxB,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AAGrB,IAAA,IAAI,MAAA,GAAS,IAAA,CAAK,OAAA,CAAQ,GAAA,CAAI,GAAG,CAAA;AAEjC,IAAA,IAAI,CAAC,MAAA,EAAQ;AAEX,MAAA,MAAA,GAAS;AAAA,QACP,MAAA,EAAQ,KAAA;AAAA,QACR,UAAA,EAAY;AAAA,OACd;AAAA,IACF,CAAA,MAAO;AAEL,MAAA,MAAA,GAAS,IAAA,CAAK,YAAA,CAAa,MAAA,EAAQ,KAAA,EAAO,UAAU,GAAG,CAAA;AAAA,IACzD;AAGA,IAAA,MAAM,YAAA,GAAe,QAAQ,MAAA,CAAO,MAAA;AACpC,IAAA,MAAM,aAAa,KAAA,GAAQ,QAAA;AAC3B,IAAA,MAAM,aAAa,YAAA,GAAe,UAAA;AAClC,IAAA,MAAM,KAAA,GAAQ,WAAA,CAAY,GAAA,GAAM,UAAU,CAAA;AAG1C,IAAA,IAAI,MAAA,CAAO,SAAS,CAAA,EAAG;AAErB,MAAA,MAAM,cAAA,GAAA,CAAkB,CAAA,GAAI,MAAA,CAAO,MAAA,IAAU,UAAA;AAC7C,MAAA,MAAM,UAAA,GAAa,IAAA,CAAK,IAAA,CAAK,cAAA,GAAiB,GAAI,CAAA;AAElD,MAAA,OAAO;AAAA,QACL,KAAA;AAAA,QACA,SAAA,EAAW,CAAA;AAAA,QACX,KAAA;AAAA,QACA,OAAA,EAAS,IAAA;AAAA,QACT,UAAA,EAAY,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,UAAU;AAAA,OACpC;AAAA,IACF;AAGA,IAAA,MAAA,CAAO,MAAA,IAAU,CAAA;AACjB,IAAA,IAAA,CAAK,OAAA,CAAQ,GAAA,CAAI,GAAA,EAAK,MAAM,CAAA;AAG5B,IAAA,IAAI,IAAA,CAAK,OAAA,CAAQ,IAAA,GAAO,IAAA,CAAK,UAAA,EAAY;AACvC,MAAA,IAAA,CAAK,OAAA,EAAQ;AAAA,IACf;AAEA,IAAA,OAAO;AAAA,MACL,KAAA;AAAA,MACA,SAAA,EAAW,IAAA,CAAK,KAAA,CAAM,MAAA,CAAO,MAAM,CAAA;AAAA,MACnC,KAAA;AAAA,MACA,OAAA,EAAS;AAAA,KACX;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,YAAA,CACN,MAAA,EACA,KAAA,EACA,QAAA,EACA,GAAA,EACkB;AAClB,IAAA,MAAM,OAAA,GAAU,MAAM,MAAA,CAAO,UAAA;AAC7B,IAAA,MAAM,aAAa,KAAA,GAAQ,QAAA;AAC3B,IAAA,MAAM,cAAc,OAAA,GAAU,UAAA;AAE9B,IAAA,OAAO;AAAA,MACL,QAAQ,IAAA,CAAK,GAAA,CAAI,KAAA,EAAO,MAAA,CAAO,SAAS,WAAW,CAAA;AAAA,MACnD,UAAA,EAAY;AAAA,KACd;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,OAAA,GAAgB;AACtB,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,IAAA,MAAM,cAAA,GAAiB,IAAA;AAEvB,IAAA,MAAM,eAAyB,EAAC;AAEhC,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,MAAM,CAAA,IAAK,KAAK,OAAA,EAAS;AACxC,MAAA,IAAI,GAAA,GAAM,MAAA,CAAO,UAAA,GAAa,cAAA,EAAgB;AAC5C,QAAA,YAAA,CAAa,KAAK,GAAG,CAAA;AAAA,MACvB;AAAA,IACF;AAEA,IAAA,KAAA,MAAW,OAAO,YAAA,EAAc;AAC9B,MAAA,IAAA,CAAK,OAAA,CAAQ,OAAO,GAAG,CAAA;AAAA,IACzB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,eAAe,GAAA,EAA2C;AACxD,IAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,GAAA,CAAI,GAAG,CAAA;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA,EAKA,KAAA,GAAc;AACZ,IAAA,IAAA,CAAK,QAAQ,KAAA,EAAM;AAAA,EACrB;AACF,CAAA;;;AClIA,IAAM,cAAA,GAA2C;AAAA,EAC/C,SAAA,EAAW,gBAAA;AAAA,EACX,UAAA,EAAY,IAAA;AAAA,EACZ,OAAA,EAAS,IAAA;AAAA,EACT,MAAA,EAAQ,IAAA;AAAA,EACR,OAAA,EAAS,mBAAA;AAAA,EACT,UAAA,EAAY,GAAA;AAAA,EACZ,KAAA,EAAO;AACT,CAAA;AAKA,IAAI,YAAA,GAAsC,IAAA;AAK1C,SAAS,eAAA,GAAkC;AACzC,EAAA,IAAI,CAAC,YAAA,EAAc;AACjB,IAAA,YAAA,GAAe,IAAI,WAAA,EAAY;AAAA,EACjC;AACA,EAAA,OAAO,YAAA;AACT;AAKA,SAAS,aAAa,IAAA,EAA4D;AAChF,EAAA,QAAQ,IAAA;AAAM,IACZ,KAAK,cAAA;AACH,MAAA,OAAO,IAAI,oBAAA,EAAqB;AAAA,IAClC,KAAK,cAAA;AACH,MAAA,OAAO,IAAI,oBAAA,EAAqB;AAAA,IAClC,KAAK,gBAAA;AAAA,IACL;AACE,MAAA,OAAO,IAAI,sBAAA,EAAuB;AAAA;AAExC;AAKA,SAAS,uBAAuB,IAAA,EAA8B;AAC5D,EAAA,MAAM,OAAA,GAAU,IAAI,OAAA,EAAQ;AAE5B,EAAA,OAAA,CAAQ,GAAA,CAAI,mBAAA,EAAqB,MAAA,CAAO,IAAA,CAAK,KAAK,CAAC,CAAA;AACnD,EAAA,OAAA,CAAQ,GAAA,CAAI,uBAAA,EAAyB,MAAA,CAAO,IAAA,CAAK,SAAS,CAAC,CAAA;AAC3D,EAAA,OAAA,CAAQ,GAAA,CAAI,mBAAA,EAAqB,MAAA,CAAO,IAAA,CAAK,KAAK,CAAC,CAAA;AAEnD,EAAA,IAAI,IAAA,CAAK,OAAA,IAAW,IAAA,CAAK,UAAA,EAAY;AACnC,IAAA,OAAA,CAAQ,GAAA,CAAI,aAAA,EAAe,MAAA,CAAO,IAAA,CAAK,UAAU,CAAC,CAAA;AAAA,EACpD;AAEA,EAAA,OAAO,OAAA;AACT;AAKA,SAAS,YAAA,CAAa,QAAiB,MAAA,EAAuB;AAC5D,EAAA,MAAA,CAAO,OAAA,CAAQ,CAAC,KAAA,EAAO,GAAA,KAAQ;AAC7B,IAAA,MAAA,CAAO,GAAA,CAAI,KAAK,KAAK,CAAA;AAAA,EACvB,CAAC,CAAA;AACH;AAKA,eAAe,aAAA,CACb,OAAA,EACA,UAAA,EACA,MAAA,EACA,OAAA,EACiB;AACjB,EAAA,IAAI,OAAO,eAAe,UAAA,EAAY;AACpC,IAAA,MAAM,EAAA,GAAK,MAAM,UAAA,CAAW,OAAO,CAAA;AACnC,IAAA,OAAO,CAAA,EAAG,MAAM,CAAA,QAAA,EAAW,EAAE,CAAA,CAAA;AAAA,EAC/B;AAEA,EAAA,IAAI,eAAe,MAAA,EAAQ;AAEzB,IAAA,MAAM,SAAS,OAAA,EAAS,IAAA,GACnB,OAAA,CAAQ,IAAA,CAAyB,MAAM,WAAA,GACxC,WAAA;AACJ,IAAA,OAAO,CAAA,EAAG,MAAM,CAAA,MAAA,EAAS,MAAM,CAAA,CAAA;AAAA,EACjC;AAGA,EAAA,MAAM,EAAA,GAAK,YAAY,OAAO,CAAA;AAC9B,EAAA,OAAO,CAAA,EAAG,MAAM,CAAA,IAAA,EAAO,EAAE,CAAA,CAAA;AAC3B;AAkCO,SAAS,aAAA,CACd,SAIA,MAAA,EAC6E;AAE7E,EAAA,MAAM,WAAA,GAAyC;AAAA,IAC7C,GAAG,cAAA;AAAA,IACH,GAAG,MAAA;AAAA,IACH,KAAA,EAAO,MAAA,CAAO,KAAA,IAAS,eAAA;AAAgB,GACzC;AAGA,EAAA,MAAM,QAAA,GAAW,aAAA,CAAc,WAAA,CAAY,MAAM,CAAA;AAGjD,EAAA,MAAM,SAAA,GAAY,YAAA,CAAa,WAAA,CAAY,SAAS,CAAA;AAGpD,EAAA,MAAM,KAAA,GAAQ,WAAA,CAAY,KAAA,GACtB,CAAC,KAAa,IAAA,KAAmB;AAE/B,IAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,yBAAA,EAA4B,GAAG,CAAA,CAAA,EAAI,QAAQ,EAAE,CAAA;AAAA,EAC3D,IACA,MAAM;AAAA,EAAC,CAAA;AAEX,EAAA,KAAA,CAAM,aAAA,EAAe;AAAA,IACnB,OAAO,WAAA,CAAY,KAAA;AAAA,IACnB,QAAQ,WAAA,CAAY,MAAA;AAAA,IACpB,WAAW,WAAA,CAAY;AAAA,GACxB,CAAA;AAED,EAAA,OAAO,OACL,SACA,OAAA,KACsB;AAEtB,IAAA,MAAM,MAA4D,OAAA,IAAW;AAAA,MAC3E,IAAA,EAAM,IAAA;AAAA,MACN,SAAA,EAAW,OAAO,UAAA,EAAW;AAAA,MAC7B,EAAA,EAAI,YAAY,OAAO,CAAA;AAAA,MACvB,SAAA,EAAW,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,YAAY,CAAA,IAAK,EAAA;AAAA,MAChD,SAAA,EAAW,KAAK,GAAA,EAAI;AAAA,MACpB,UAAU;AAAC,KACb;AAEA,IAAA,IAAI;AAEF,MAAA,IAAI,YAAY,IAAA,EAAM;AACpB,QAAA,MAAM,UAAA,GAAa,MAAM,WAAA,CAAY,IAAA,CAAK,OAAO,CAAA;AACjD,QAAA,IAAI,UAAA,EAAY;AACd,UAAA,KAAA,CAAM,2BAA2B,CAAA;AACjC,UAAA,OAAO,OAAA,CAAQ,SAAS,GAAG,CAAA;AAAA,QAC7B;AAAA,MACF;AAGA,MAAA,MAAM,MAAM,MAAM,aAAA;AAAA,QAChB,OAAA;AAAA,QACA,WAAA,CAAY,UAAA;AAAA,QACZ,WAAA,CAAY,MAAA;AAAA,QACZ;AAAA,OACF;AACA,MAAA,KAAA,CAAM,kBAAkB,GAAG,CAAA;AAG3B,MAAA,MAAM,IAAA,GAAO,MAAM,SAAA,CAAU,KAAA;AAAA,QAC3B,WAAA,CAAY,KAAA;AAAA,QACZ,GAAA;AAAA,QACA,WAAA,CAAY,KAAA;AAAA,QACZ;AAAA,OACF;AACA,MAAA,KAAA,CAAM,mBAAmB,IAAI,CAAA;AAG7B,MAAA,GAAA,CAAI,SAAA,GAAY,IAAA;AAGhB,MAAA,IAAI,KAAK,OAAA,EAAS;AAChB,QAAA,KAAA,CAAM,sBAAsB,CAAA;AAG5B,QAAA,IAAI,YAAY,OAAA,EAAS;AACvB,UAAA,MAAMA,SAAAA,GAAW,MAAM,WAAA,CAAY,OAAA,CAAQ,SAAS,IAAI,CAAA;AAGxD,UAAA,IAAI,YAAY,OAAA,EAAS;AACvB,YAAA,MAAM,gBAAA,GAAmB,uBAAuB,IAAI,CAAA;AACpD,YAAA,YAAA,CAAaA,SAAAA,CAAS,SAAS,gBAAgB,CAAA;AAAA,UACjD;AAEA,UAAA,OAAOA,SAAAA;AAAA,QACT;AAGA,QAAA,MAAM,KAAA,GAAQ,IAAI,cAAA,CAAe;AAAA,UAC/B,UAAA,EAAY,KAAK,UAAA,IAAc,EAAA;AAAA,UAC/B,OAAA,EAAS,KAAK,KAAA,GAAQ,GAAA;AAAA,UACtB,SAAS,WAAA,CAAY;AAAA,SACtB,CAAA;AAED,QAAA,MAAMA,SAAAA,GAAW,MAAM,UAAA,EAAW;AAElC,QAAA,IAAI,YAAY,OAAA,EAAS;AACvB,UAAA,MAAM,gBAAA,GAAmB,uBAAuB,IAAI,CAAA;AACpD,UAAA,YAAA,CAAaA,SAAAA,CAAS,SAAS,gBAAgB,CAAA;AAAA,QACjD;AAEA,QAAA,OAAOA,SAAAA;AAAA,MACT;AAGA,MAAA,MAAM,QAAA,GAAW,MAAM,OAAA,CAAQ,OAAA,EAAS,GAAG,CAAA;AAG3C,MAAA,IAAI,YAAY,OAAA,EAAS;AAEvB,QAAA,MAAM,WAAA,GAAc,IAAI,QAAA,CAAS,QAAA,CAAS,IAAA,EAAM;AAAA,UAC9C,QAAQ,QAAA,CAAS,MAAA;AAAA,UACjB,YAAY,QAAA,CAAS,UAAA;AAAA,UACrB,OAAA,EAAS,IAAI,OAAA,CAAQ,QAAA,CAAS,OAAO;AAAA,SACtC,CAAA;AAED,QAAA,MAAM,gBAAA,GAAmB,uBAAuB,IAAI,CAAA;AACpD,QAAA,YAAA,CAAa,WAAA,CAAY,SAAS,gBAAgB,CAAA;AAElD,QAAA,OAAO,WAAA;AAAA,MACT;AAEA,MAAA,OAAO,QAAA;AAAA,IACT,SAAS,KAAA,EAAO;AACd,MAAA,KAAA,CAAM,kCAAkC,KAAK,CAAA;AAG7C,MAAA,IAAI,iBAAiB,cAAA,EAAgB;AACnC,QAAA,MAAM,KAAA;AAAA,MACR;AAKA,MAAA,OAAA,CAAQ,KAAA,CAAM,mCAAmC,KAAK,CAAA;AACtD,MAAA,OAAO,OAAA,CAAQ,SAAS,GAAG,CAAA;AAAA,IAC7B;AAAA,EACF,CAAA;AACF;AAgBO,SAAS,kBAAkB,MAAA,EAAyB;AACzD,EAAA,OAAO,CACL,OAAA,KAIG,aAAA,CAAc,OAAA,EAAS,MAAM,CAAA;AACpC;AAuBA,eAAsB,cAAA,CACpB,SACA,MAAA,EAMC;AACD,EAAA,MAAM,WAAA,GAAc;AAAA,IAClB,GAAG,cAAA;AAAA,IACH,GAAG,MAAA;AAAA,IACH,KAAA,EAAO,MAAA,CAAO,KAAA,IAAS,eAAA;AAAgB,GACzC;AAEA,EAAA,MAAM,QAAA,GAAW,aAAA,CAAc,WAAA,CAAY,MAAM,CAAA;AACjD,EAAA,MAAM,SAAA,GAAY,YAAA,CAAa,WAAA,CAAY,SAAS,CAAA;AAGpD,EAAA,IAAI,YAAY,IAAA,EAAM;AACpB,IAAA,MAAM,UAAA,GAAa,MAAM,WAAA,CAAY,IAAA,CAAK,OAAO,CAAA;AACjD,IAAA,IAAI,UAAA,EAAY;AACd,MAAA,MAAMC,KAAAA,GAAsB;AAAA,QAC1B,OAAO,WAAA,CAAY,KAAA;AAAA,QACnB,WAAW,WAAA,CAAY,KAAA;AAAA,QACvB,KAAA,EAAO,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,GAAA,EAAI,GAAI,GAAI,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,QAAA,GAAW,GAAI,CAAA;AAAA,QACjE,OAAA,EAAS;AAAA,OACX;AACA,MAAA,OAAO,EAAE,SAAS,IAAA,EAAM,IAAA,EAAAA,OAAM,OAAA,EAAS,IAAI,SAAQ,EAAE;AAAA,IACvD;AAAA,EACF;AAEA,EAAA,MAAM,MAAM,MAAM,aAAA,CAAc,SAAS,WAAA,CAAY,UAAA,EAAY,YAAY,MAAM,CAAA;AACnF,EAAA,MAAM,IAAA,GAAO,MAAM,SAAA,CAAU,KAAA,CAAM,YAAY,KAAA,EAAO,GAAA,EAAK,WAAA,CAAY,KAAA,EAAO,QAAQ,CAAA;AACtF,EAAA,MAAM,UAAU,WAAA,CAAY,OAAA,GAAU,uBAAuB,IAAI,CAAA,GAAI,IAAI,OAAA,EAAQ;AAEjF,EAAA,IAAI,KAAK,OAAA,EAAS;AAChB,IAAA,IAAI,QAAA;AAEJ,IAAA,IAAI,YAAY,OAAA,EAAS;AACvB,MAAA,QAAA,GAAW,MAAM,WAAA,CAAY,OAAA,CAAQ,OAAA,EAAS,IAAI,CAAA;AAAA,IACpD,CAAA,MAAO;AACL,MAAA,MAAM,KAAA,GAAQ,IAAI,cAAA,CAAe;AAAA,QAC/B,UAAA,EAAY,KAAK,UAAA,IAAc,EAAA;AAAA,QAC/B,OAAA,EAAS,KAAK,KAAA,GAAQ,GAAA;AAAA,QACtB,SAAS,WAAA,CAAY;AAAA,OACtB,CAAA;AACD,MAAA,QAAA,GAAW,MAAM,UAAA,EAAW;AAAA,IAC9B;AAEA,IAAA,IAAI,YAAY,OAAA,EAAS;AACvB,MAAA,YAAA,CAAa,QAAA,CAAS,SAAS,OAAO,CAAA;AAAA,IACxC;AAEA,IAAA,OAAO,EAAE,OAAA,EAAS,KAAA,EAAO,IAAA,EAAM,UAAU,OAAA,EAAQ;AAAA,EACnD;AAEA,EAAA,OAAO,EAAE,OAAA,EAAS,IAAA,EAAM,IAAA,EAAM,OAAA,EAAQ;AACxC;AAcA,eAAsB,cAAA,CACpB,IAAA,EACA,UAAA,EACA,OAAA,EAIe;AACf,EAAA,MAAM,KAAA,GAAQ,OAAA,EAAS,KAAA,IAAS,eAAA,EAAgB;AAChD,EAAA,MAAM,MAAA,GAAS,SAAS,MAAA,IAAU,IAAA;AAClC,EAAA,MAAM,MAAM,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,IAAI,IAAI,UAAU,CAAA,CAAA;AAE3C,EAAA,MAAM,KAAA,CAAM,MAAM,GAAG,CAAA;AACvB;AAKA,eAAsB,kBAAA,CACpB,IAAA,EACA,UAAA,EACA,OAAA,EAIkD;AAClD,EAAA,MAAM,KAAA,GAAQ,OAAA,EAAS,KAAA,IAAS,eAAA,EAAgB;AAChD,EAAA,MAAM,MAAA,GAAS,SAAS,MAAA,IAAU,IAAA;AAClC,EAAA,MAAM,MAAM,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,IAAI,IAAI,UAAU,CAAA,CAAA;AAE3C,EAAA,OAAO,KAAA,CAAM,IAAI,GAAG,CAAA;AACtB;AAKO,SAAS,kBAAA,GAA2B;AACzC,EAAA,IAAI,YAAA,IAAgB,WAAW,YAAA,EAAc;AAC3C,IAAC,aAA6B,KAAA,EAAM;AAAA,EACtC;AACF;AC1dA,IAAM,OAAA,GAAU,IAAI,WAAA,EAAY;AAKzB,SAAS,YAAY,MAAA,EAAwB;AAClD,EAAA,MAAM,KAAA,GAAQ,IAAI,UAAA,CAAW,MAAM,CAAA;AACnC,EAAAC,kBAAA,CAAU,gBAAgB,KAAK,CAAA;AAC/B,EAAA,OAAO,MAAM,IAAA,CAAK,KAAK,CAAA,CACpB,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,QAAA,CAAS,EAAE,EAAE,QAAA,CAAS,CAAA,EAAG,GAAG,CAAC,CAAA,CAC1C,KAAK,EAAE,CAAA;AACZ;AAKA,eAAe,eAAA,CAAgB,MAAc,MAAA,EAAiC;AAC5E,EAAA,MAAM,GAAA,GAAM,MAAMA,kBAAA,CAAU,MAAA,CAAO,SAAA;AAAA,IACjC,KAAA;AAAA,IACA,OAAA,CAAQ,OAAO,MAAM,CAAA;AAAA,IACrB,EAAE,IAAA,EAAM,MAAA,EAAQ,IAAA,EAAM,SAAA,EAAU;AAAA,IAChC,KAAA;AAAA,IACA,CAAC,MAAM;AAAA,GACT;AAEA,EAAA,MAAM,GAAA,GAAM,MAAMA,kBAAA,CAAU,MAAA,CAAO,IAAA,CAAK,QAAQ,GAAA,EAAK,OAAA,CAAQ,MAAA,CAAO,IAAI,CAAC,CAAA;AACzE,EAAA,OAAO,KAAA,CAAM,KAAK,IAAI,UAAA,CAAW,GAAG,CAAC,CAAA,CAClC,IAAI,CAAC,CAAA,KAAM,EAAE,QAAA,CAAS,EAAE,EAAE,QAAA,CAAS,CAAA,EAAG,GAAG,CAAC,CAAA,CAC1C,KAAK,EAAE,CAAA;AACZ;AAKA,SAAS,WAAA,CAAY,GAAW,CAAA,EAAoB;AAClD,EAAA,IAAI,CAAA,CAAE,MAAA,KAAW,CAAA,CAAE,MAAA,EAAQ,OAAO,KAAA;AAElC,EAAA,IAAI,MAAA,GAAS,CAAA;AACb,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,CAAE,QAAQ,CAAA,EAAA,EAAK;AACjC,IAAA,MAAA,IAAU,EAAE,UAAA,CAAW,CAAC,CAAA,GAAI,CAAA,CAAE,WAAW,CAAC,CAAA;AAAA,EAC5C;AACA,EAAA,OAAO,MAAA,KAAW,CAAA;AACpB;AAKA,eAAsB,WAAA,CACpB,MAAA,EACA,MAAA,GAAiB,EAAA,EACA;AACjB,EAAA,MAAM,IAAA,GAAO,YAAY,MAAM,CAAA;AAC/B,EAAA,MAAM,GAAA,GAAM,MAAM,eAAA,CAAgB,IAAA,EAAM,MAAM,CAAA;AAC9C,EAAA,OAAO,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,GAAG,CAAA,CAAA;AACvB;AAKA,eAAsB,WAAA,CACpB,OACA,MAAA,EACkB;AAClB,EAAA,IAAI,CAAC,KAAA,IAAS,OAAO,KAAA,KAAU,UAAU,OAAO,KAAA;AAEhD,EAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,KAAA,CAAM,GAAG,CAAA;AAC7B,EAAA,IAAI,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG,OAAO,KAAA;AAE/B,EAAA,MAAM,CAAC,IAAA,EAAM,GAAG,CAAA,GAAI,KAAA;AACpB,EAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,GAAA,EAAK,OAAO,KAAA;AAE1B,EAAA,IAAI;AACF,IAAA,MAAM,QAAA,GAAW,MAAM,eAAA,CAAgB,IAAA,EAAM,MAAM,CAAA;AACnD,IAAA,OAAO,WAAA,CAAY,KAAK,QAAQ,CAAA;AAAA,EAClC,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AAKO,SAAS,WAAA,CAAY,GAAW,CAAA,EAAoB;AACzD,EAAA,IAAI,CAAC,CAAA,IAAK,CAAC,CAAA,EAAG,OAAO,KAAA;AACrB,EAAA,OAAO,WAAA,CAAY,GAAG,CAAC,CAAA;AACzB;;;ACjFA,IAAM,cAAA,GAAoC;AAAA,EACxC,IAAA,EAAM,QAAA;AAAA,EACN,IAAA,EAAM,GAAA;AAAA,EACN,QAAA,EAAU,IAAA;AAAA,EACV,MAAA,EAAQ,OAAA,CAAQ,GAAA,CAAI,QAAA,KAAa,YAAA;AAAA,EACjC,QAAA,EAAU,QAAA;AAAA,EACV,MAAA,EAAQ;AAAA;AACV,CAAA;AAEA,IAAMC,eAAAA,GAAiE;AAAA,EAErE,UAAA,EAAY,cAAA;AAAA,EACZ,SAAA,EAAW,OAAA;AAAA,EAEX,WAAA,EAAa,EAAA;AAAA,EACb,gBAAA,EAAkB,CAAC,MAAA,EAAQ,KAAA,EAAO,SAAS,QAAQ;AACrD,CAAA;AAEA,SAAS,UAAU,MAAA,EAA4B;AAC7C,EAAA,MAAM,MAAA,GAAS,MAAA,CAAO,MAAA,IAAU,OAAA,CAAQ,GAAA,CAAI,WAAA;AAC5C,EAAA,IAAI,CAAC,MAAA,EAAQ;AACX,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AACA,EAAA,OAAO,MAAA;AACT;AAEA,SAAS,iBAAA,CAAkB,IAAA,EAAc,KAAA,EAAe,IAAA,EAAiC;AACvF,EAAA,IAAI,MAAA,GAAS,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,KAAK,CAAA,CAAA;AAE7B,EAAA,IAAI,IAAA,CAAK,IAAA,EAAM,MAAA,IAAU,CAAA,OAAA,EAAU,KAAK,IAAI,CAAA,CAAA;AAC5C,EAAA,IAAI,IAAA,CAAK,MAAA,EAAQ,MAAA,IAAU,CAAA,SAAA,EAAY,KAAK,MAAM,CAAA,CAAA;AAClD,EAAA,IAAI,IAAA,CAAK,MAAA,EAAQ,MAAA,IAAU,CAAA,UAAA,EAAa,KAAK,MAAM,CAAA,CAAA;AACnD,EAAA,IAAI,IAAA,CAAK,UAAU,MAAA,IAAU,YAAA;AAC7B,EAAA,IAAI,IAAA,CAAK,QAAQ,MAAA,IAAU,UAAA;AAC3B,EAAA,IAAI,IAAA,CAAK,QAAA,EAAU,MAAA,IAAU,CAAA,WAAA,EAAc,KAAK,QAAQ,CAAA,CAAA;AAExD,EAAA,OAAO,MAAA;AACT;AAKA,eAAe,YAAA,CACb,GAAA,EACA,UAAA,EACA,SAAA,EACwB;AAExB,EAAA,MAAM,WAAA,GAAc,GAAA,CAAI,OAAA,CAAQ,GAAA,CAAI,UAAU,CAAA;AAC9C,EAAA,IAAI,aAAa,OAAO,WAAA;AAGxB,EAAA,MAAM,WAAA,GAAc,GAAA,CAAI,OAAA,CAAQ,GAAA,CAAI,cAAc,CAAA,IAAK,EAAA;AAEvD,EAAA,IAAI,WAAA,CAAY,QAAA,CAAS,mCAAmC,CAAA,EAAG;AAC7D,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,IAAI,KAAA,EAAM;AACzB,MAAA,MAAM,QAAA,GAAW,MAAM,MAAA,CAAO,QAAA,EAAS;AACvC,MAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,GAAA,CAAI,SAAS,CAAA;AACpC,MAAA,IAAI,OAAO,KAAA,KAAU,QAAA,EAAU,OAAO,KAAA;AAAA,IACxC,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF;AAEA,EAAA,IAAI,WAAA,CAAY,QAAA,CAAS,kBAAkB,CAAA,EAAG;AAC5C,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,IAAI,KAAA,EAAM;AACzB,MAAA,MAAM,IAAA,GAAO,MAAM,MAAA,CAAO,IAAA,EAAK;AAC/B,MAAA,IAAI,IAAA,IAAQ,OAAO,IAAA,CAAK,SAAS,MAAM,QAAA,EAAU;AAC/C,QAAA,OAAO,KAAK,SAAS,CAAA;AAAA,MACvB;AAAA,IACF,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF;AAEA,EAAA,OAAO,IAAA;AACT;AAEA,SAAS,oBAAA,CAAqB,MAAmB,MAAA,EAA0B;AACzE,EAAA,OAAO,IAAI,SAAS,IAAA,CAAK,SAAA,CAAU,EAAE,KAAA,EAAO,wBAAA,EAA0B,MAAA,EAAQ,CAAA,EAAG;AAAA,IAC/E,MAAA,EAAQ,GAAA;AAAA,IACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA;AAAmB,GAC/C,CAAA;AACH;AAUO,SAAS,QAAA,CAAS,OAAA,EAAuB,MAAA,GAAqB,EAAC,EAAiB;AACrF,EAAA,MAAM,MAAA,GAAS,UAAU,MAAM,CAAA;AAC/B,EAAA,MAAM,aAAa,EAAE,GAAG,cAAA,EAAgB,GAAG,OAAO,MAAA,EAAO;AACzD,EAAA,MAAM,UAAA,GAAa,MAAA,CAAO,UAAA,IAAcA,eAAAA,CAAe,UAAA;AACvD,EAAA,MAAM,SAAA,GAAY,MAAA,CAAO,SAAA,IAAaA,eAAAA,CAAe,SAAA;AACrD,EAAA,MAAM,gBAAA,GAAmB,MAAA,CAAO,gBAAA,IAAoBA,eAAAA,CAAe,gBAAA;AACnE,EAAA,MAAM,OAAA,GAAU,OAAO,OAAA,IAAW,oBAAA;AAElC,EAAA,OAAO,OAAO,GAAA,KAAwC;AACpD,IAAA,MAAM,MAAA,GAAS,GAAA,CAAI,MAAA,CAAO,WAAA,EAAY;AAGtC,IAAA,IAAI,CAAC,gBAAA,CAAiB,QAAA,CAAS,MAAM,CAAA,EAAG;AACtC,MAAA,OAAO,QAAQ,GAAG,CAAA;AAAA,IACpB;AAGA,IAAA,IAAI,OAAO,IAAA,EAAM;AACf,MAAA,MAAM,UAAA,GAAa,MAAM,MAAA,CAAO,IAAA,CAAK,GAAG,CAAA;AACxC,MAAA,IAAI,UAAA,EAAY,OAAO,OAAA,CAAQ,GAAG,CAAA;AAAA,IACpC;AAEA,IAAA,MAAM,UAAA,GAAa,WAAW,IAAA,IAAQ,QAAA;AACtC,IAAA,MAAM,WAAA,GAAc,GAAA,CAAI,OAAA,CAAQ,GAAA,CAAI,UAAU,CAAA,EAAG,KAAA;AAGjD,IAAA,IAAI,CAAC,WAAA,EAAa;AAChB,MAAA,OAAO,OAAA,CAAQ,KAAK,gBAAgB,CAAA;AAAA,IACtC;AAGA,IAAA,MAAM,WAAA,GAAc,MAAM,WAAA,CAAY,WAAA,EAAa,MAAM,CAAA;AACzD,IAAA,IAAI,CAAC,WAAA,EAAa;AAChB,MAAA,OAAO,OAAA,CAAQ,KAAK,gBAAgB,CAAA;AAAA,IACtC;AAGA,IAAA,MAAM,YAAA,GAAe,MAAM,YAAA,CAAa,GAAA,EAAK,YAAY,SAAS,CAAA;AAClE,IAAA,IAAI,CAAC,YAAA,EAAc;AACjB,MAAA,OAAO,OAAA,CAAQ,KAAK,eAAe,CAAA;AAAA,IACrC;AAGA,IAAA,IAAI,CAAC,WAAA,CAAY,WAAA,EAAa,YAAY,CAAA,EAAG;AAC3C,MAAA,OAAO,OAAA,CAAQ,KAAK,gBAAgB,CAAA;AAAA,IACtC;AAEA,IAAA,OAAO,QAAQ,GAAG,CAAA;AAAA,EACpB,CAAA;AACF;AAMA,eAAsB,YAAA,CAAa,MAAA,GAAqB,EAAC,EAGtD;AACD,EAAA,MAAM,MAAA,GAAS,UAAU,MAAM,CAAA;AAC/B,EAAA,MAAM,aAAa,EAAE,GAAG,cAAA,EAAgB,GAAG,OAAO,MAAA,EAAO;AACzD,EAAA,MAAM,WAAA,GAAc,MAAA,CAAO,WAAA,IAAeA,eAAAA,CAAe,WAAA;AACzD,EAAA,MAAM,UAAA,GAAa,WAAW,IAAA,IAAQ,QAAA;AAEtC,EAAA,MAAM,KAAA,GAAQ,MAAM,WAAA,CAAY,MAAA,EAAQ,WAAW,CAAA;AACnD,EAAA,MAAM,YAAA,GAAe,iBAAA,CAAkB,UAAA,EAAY,KAAA,EAAO,UAAU,CAAA;AAEpE,EAAA,OAAO,EAAE,OAAO,YAAA,EAAa;AAC/B;AAMA,eAAsB,YAAA,CACpB,GAAA,EACA,MAAA,GAAqB,EAAC,EACwB;AAC9C,EAAA,MAAM,MAAA,GAAS,UAAU,MAAM,CAAA;AAC/B,EAAA,MAAM,aAAa,EAAE,GAAG,cAAA,EAAgB,GAAG,OAAO,MAAA,EAAO;AACzD,EAAA,MAAM,UAAA,GAAa,MAAA,CAAO,UAAA,IAAcA,eAAAA,CAAe,UAAA;AACvD,EAAA,MAAM,SAAA,GAAY,MAAA,CAAO,SAAA,IAAaA,eAAAA,CAAe,SAAA;AACrD,EAAA,MAAM,UAAA,GAAa,WAAW,IAAA,IAAQ,QAAA;AAEtC,EAAA,MAAM,WAAA,GAAc,GAAA,CAAI,OAAA,CAAQ,GAAA,CAAI,UAAU,CAAA,EAAG,KAAA;AACjD,EAAA,IAAI,CAAC,WAAA,EAAa;AAChB,IAAA,OAAO,EAAE,KAAA,EAAO,KAAA,EAAO,MAAA,EAAQ,gBAAA,EAAiB;AAAA,EAClD;AAEA,EAAA,MAAM,WAAA,GAAc,MAAM,WAAA,CAAY,WAAA,EAAa,MAAM,CAAA;AACzD,EAAA,IAAI,CAAC,WAAA,EAAa;AAChB,IAAA,OAAO,EAAE,KAAA,EAAO,KAAA,EAAO,MAAA,EAAQ,gBAAA,EAAiB;AAAA,EAClD;AAEA,EAAA,MAAM,YAAA,GAAe,MAAM,YAAA,CAAa,GAAA,EAAK,YAAY,SAAS,CAAA;AAClE,EAAA,IAAI,CAAC,YAAA,EAAc;AACjB,IAAA,OAAO,EAAE,KAAA,EAAO,KAAA,EAAO,MAAA,EAAQ,eAAA,EAAgB;AAAA,EACjD;AAEA,EAAA,IAAI,CAAC,WAAA,CAAY,WAAA,EAAa,YAAY,CAAA,EAAG;AAC3C,IAAA,OAAO,EAAE,KAAA,EAAO,KAAA,EAAO,MAAA,EAAQ,gBAAA,EAAiB;AAAA,EAClD;AAEA,EAAA,OAAO,EAAE,OAAO,IAAA,EAAK;AACvB;;;AC7EO,IAAM,OAAA,GAAU","file":"index.cjs","sourcesContent":["/**\n * Custom error classes for next-secure\n */\n\n/**\n * Base error class for all next-secure errors\n */\nexport class SecureError extends Error {\n /**\n * HTTP status code\n */\n public readonly statusCode: number\n\n /**\n * Error code for programmatic handling\n */\n public readonly code: string\n\n /**\n * Additional error details\n */\n public readonly details?: Record<string, unknown>\n\n constructor(\n message: string,\n options: {\n statusCode?: number\n code?: string\n details?: Record<string, unknown>\n cause?: Error\n } = {}\n ) {\n super(message, { cause: options.cause })\n this.name = 'SecureError'\n this.statusCode = options.statusCode ?? 500\n this.code = options.code ?? 'SECURE_ERROR'\n this.details = options.details\n\n // Maintains proper stack trace for where error was thrown\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor)\n }\n }\n\n /**\n * Convert error to JSON response\n */\n toJSON(): Record<string, unknown> {\n return {\n error: this.name,\n message: this.message,\n code: this.code,\n ...(this.details && { details: this.details }),\n }\n }\n\n /**\n * Create a Response object from this error\n */\n toResponse(headers?: HeadersInit): Response {\n return new Response(JSON.stringify(this.toJSON()), {\n status: this.statusCode,\n headers: {\n 'Content-Type': 'application/json',\n ...headers,\n },\n })\n }\n}\n\n/**\n * Rate limit exceeded error\n */\nexport class RateLimitError extends SecureError {\n /**\n * Seconds until rate limit resets\n */\n public readonly retryAfter: number\n\n /**\n * Unix timestamp when limit resets\n */\n public readonly resetAt: number\n\n constructor(\n options: {\n retryAfter: number\n resetAt: number\n message?: string\n details?: Record<string, unknown>\n }\n ) {\n super(options.message ?? 'Too Many Requests', {\n statusCode: 429,\n code: 'RATE_LIMIT_EXCEEDED',\n details: options.details,\n })\n this.name = 'RateLimitError'\n this.retryAfter = options.retryAfter\n this.resetAt = options.resetAt\n }\n\n override toJSON(): Record<string, unknown> {\n return {\n ...super.toJSON(),\n retryAfter: this.retryAfter,\n }\n }\n\n override toResponse(headers?: HeadersInit): Response {\n return new Response(JSON.stringify(this.toJSON()), {\n status: this.statusCode,\n headers: {\n 'Content-Type': 'application/json',\n 'Retry-After': String(this.retryAfter),\n ...headers,\n },\n })\n }\n}\n\n/**\n * Authentication error\n */\nexport class AuthenticationError extends SecureError {\n constructor(\n message = 'Authentication required',\n options: {\n code?: string\n details?: Record<string, unknown>\n cause?: Error\n } = {}\n ) {\n super(message, {\n statusCode: 401,\n code: options.code ?? 'AUTHENTICATION_REQUIRED',\n details: options.details,\n cause: options.cause,\n })\n this.name = 'AuthenticationError'\n }\n}\n\n/**\n * Authorization error (authenticated but not permitted)\n */\nexport class AuthorizationError extends SecureError {\n constructor(\n message = 'Access denied',\n options: {\n code?: string\n details?: Record<string, unknown>\n cause?: Error\n } = {}\n ) {\n super(message, {\n statusCode: 403,\n code: options.code ?? 'ACCESS_DENIED',\n details: options.details,\n cause: options.cause,\n })\n this.name = 'AuthorizationError'\n }\n}\n\n/**\n * Validation error\n */\nexport class ValidationError extends SecureError {\n /**\n * Field-level validation errors\n */\n public readonly errors: Array<{\n field: string\n message: string\n code?: string\n }>\n\n constructor(\n errors: Array<{ field: string; message: string; code?: string }>,\n message = 'Validation failed'\n ) {\n super(message, {\n statusCode: 400,\n code: 'VALIDATION_ERROR',\n details: { errors },\n })\n this.name = 'ValidationError'\n this.errors = errors\n }\n\n override toJSON(): Record<string, unknown> {\n return {\n ...super.toJSON(),\n errors: this.errors,\n }\n }\n}\n\n/**\n * CSRF token error\n */\nexport class CsrfError extends SecureError {\n constructor(\n message = 'Invalid or missing CSRF token',\n options: {\n details?: Record<string, unknown>\n } = {}\n ) {\n super(message, {\n statusCode: 403,\n code: 'CSRF_TOKEN_INVALID',\n details: options.details,\n })\n this.name = 'CsrfError'\n }\n}\n\n/**\n * Configuration error\n */\nexport class ConfigurationError extends SecureError {\n constructor(\n message: string,\n options: {\n details?: Record<string, unknown>\n cause?: Error\n } = {}\n ) {\n super(message, {\n statusCode: 500,\n code: 'CONFIGURATION_ERROR',\n details: options.details,\n cause: options.cause,\n })\n this.name = 'ConfigurationError'\n }\n}\n\n/**\n * Check if an error is a SecureError\n */\nexport function isSecureError(error: unknown): error is SecureError {\n return error instanceof SecureError\n}\n\n/**\n * Convert unknown error to SecureError\n */\nexport function toSecureError(error: unknown): SecureError {\n if (error instanceof SecureError) {\n return error\n }\n\n if (error instanceof Error) {\n return new SecureError(error.message, {\n cause: error,\n })\n }\n\n return new SecureError(String(error))\n}\n","/**\n * Time parsing and manipulation utilities\n */\n\nimport type { Duration } from '../core/types'\n\n/**\n * Time unit multipliers in milliseconds\n */\nconst TIME_UNITS: Record<string, number> = {\n ms: 1,\n s: 1000,\n m: 60 * 1000,\n h: 60 * 60 * 1000,\n d: 24 * 60 * 60 * 1000,\n}\n\n/**\n * Extended time unit names\n */\nconst TIME_UNIT_ALIASES: Record<string, string> = {\n millisecond: 'ms',\n milliseconds: 'ms',\n second: 's',\n seconds: 's',\n sec: 's',\n secs: 's',\n minute: 'm',\n minutes: 'm',\n min: 'm',\n mins: 'm',\n hour: 'h',\n hours: 'h',\n hr: 'h',\n hrs: 'h',\n day: 'd',\n days: 'd',\n}\n\n/**\n * Parse a duration string or number to milliseconds\n *\n * @example\n * ```typescript\n * parseDuration('15m') // 900000 (15 minutes)\n * parseDuration('1h') // 3600000 (1 hour)\n * parseDuration('30s') // 30000 (30 seconds)\n * parseDuration('1d') // 86400000 (1 day)\n * parseDuration(60000) // 60000 (already in ms)\n * parseDuration('2 hours') // 7200000 (2 hours)\n * parseDuration('1h 30m') // 5400000 (1.5 hours)\n * ```\n *\n * @param duration - Duration string (e.g., '15m', '1h', '30s') or number in milliseconds\n * @returns Duration in milliseconds\n * @throws Error if the duration format is invalid\n */\nexport function parseDuration(duration: Duration | string): number {\n // If it's already a number, return as-is\n if (typeof duration === 'number') {\n if (duration < 0) {\n throw new Error(`Invalid duration: ${duration}. Duration must be non-negative.`)\n }\n return duration\n }\n\n // Trim and lowercase the string\n const input = duration.trim().toLowerCase()\n\n if (!input) {\n throw new Error('Invalid duration: empty string')\n }\n\n // Try to parse as a simple number (assume milliseconds)\n const numericValue = Number(input)\n if (!isNaN(numericValue)) {\n if (numericValue < 0) {\n throw new Error(`Invalid duration: ${duration}. Duration must be non-negative.`)\n }\n return numericValue\n }\n\n // Handle compound durations like \"1h 30m\" or \"1h30m\"\n let totalMs = 0\n const regex = /(\\d+(?:\\.\\d+)?)\\s*([a-z]+)/g\n let match: RegExpExecArray | null\n let hasMatch = false\n\n while ((match = regex.exec(input)) !== null) {\n hasMatch = true\n const value = parseFloat(match[1])\n let unit = match[2]\n\n // Resolve unit aliases\n if (unit in TIME_UNIT_ALIASES) {\n unit = TIME_UNIT_ALIASES[unit]\n }\n\n // Get multiplier\n const multiplier = TIME_UNITS[unit]\n if (multiplier === undefined) {\n throw new Error(\n `Invalid duration unit: \"${unit}\" in \"${duration}\". ` +\n `Valid units: s, m, h, d (or seconds, minutes, hours, days)`\n )\n }\n\n totalMs += value * multiplier\n }\n\n if (!hasMatch) {\n throw new Error(\n `Invalid duration format: \"${duration}\". ` +\n `Expected format like \"15m\", \"1h\", \"30s\", \"1d\", or \"1h 30m\"`\n )\n }\n\n return Math.floor(totalMs)\n}\n\n/**\n * Format milliseconds to a human-readable duration string\n *\n * @example\n * ```typescript\n * formatDuration(900000) // \"15m\"\n * formatDuration(3600000) // \"1h\"\n * formatDuration(5400000) // \"1h 30m\"\n * formatDuration(86400000) // \"1d\"\n * formatDuration(90061000) // \"1d 1h 1m 1s\"\n * ```\n *\n * @param ms - Duration in milliseconds\n * @param options - Formatting options\n * @returns Human-readable duration string\n */\nexport function formatDuration(\n ms: number,\n options: {\n /**\n * Use long unit names (e.g., \"minutes\" instead of \"m\")\n */\n long?: boolean\n /**\n * Maximum number of units to include\n */\n maxUnits?: number\n /**\n * Separator between units\n */\n separator?: string\n } = {}\n): string {\n const { long = false, maxUnits = 4, separator = ' ' } = options\n\n if (ms < 0) {\n return `-${formatDuration(-ms, options)}`\n }\n\n if (ms === 0) {\n return long ? '0 seconds' : '0s'\n }\n\n const units: Array<{ value: number; short: string; long: string; longPlural: string }> = [\n { value: 86400000, short: 'd', long: 'day', longPlural: 'days' },\n { value: 3600000, short: 'h', long: 'hour', longPlural: 'hours' },\n { value: 60000, short: 'm', long: 'minute', longPlural: 'minutes' },\n { value: 1000, short: 's', long: 'second', longPlural: 'seconds' },\n { value: 1, short: 'ms', long: 'millisecond', longPlural: 'milliseconds' },\n ]\n\n const parts: string[] = []\n let remaining = ms\n\n for (const unit of units) {\n if (parts.length >= maxUnits) break\n if (remaining >= unit.value) {\n const count = Math.floor(remaining / unit.value)\n remaining = remaining % unit.value\n\n if (long) {\n parts.push(`${count} ${count === 1 ? unit.long : unit.longPlural}`)\n } else {\n parts.push(`${count}${unit.short}`)\n }\n }\n }\n\n return parts.join(separator)\n}\n\n/**\n * Get the current timestamp in seconds (Unix timestamp)\n */\nexport function nowInSeconds(): number {\n return Math.floor(Date.now() / 1000)\n}\n\n/**\n * Get the current timestamp in milliseconds\n */\nexport function nowInMs(): number {\n return Date.now()\n}\n\n/**\n * Calculate reset time for a fixed window\n *\n * @param windowMs - Window size in milliseconds\n * @returns Unix timestamp (seconds) when the window resets\n */\nexport function getWindowReset(windowMs: number): number {\n const now = Date.now()\n const windowStart = Math.floor(now / windowMs) * windowMs\n const windowEnd = windowStart + windowMs\n return Math.floor(windowEnd / 1000)\n}\n\n/**\n * Get the start of the current window\n *\n * @param windowMs - Window size in milliseconds\n * @returns Timestamp (ms) of window start\n */\nexport function getWindowStart(windowMs: number): number {\n return Math.floor(Date.now() / windowMs) * windowMs\n}\n\n/**\n * Sleep for a specified duration\n *\n * @param duration - Duration to sleep\n * @returns Promise that resolves after the duration\n */\nexport function sleep(duration: Duration | string): Promise<void> {\n const ms = parseDuration(duration)\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\n/**\n * Check if a timestamp has expired\n *\n * @param timestampMs - Timestamp in milliseconds\n * @param ttlMs - Time-to-live in milliseconds\n * @returns true if expired\n */\nexport function isExpired(timestampMs: number, ttlMs: number): boolean {\n return Date.now() > timestampMs + ttlMs\n}\n\n/**\n * Calculate time until expiration\n *\n * @param expiresAt - Expiration timestamp in milliseconds\n * @returns Milliseconds until expiration (0 if already expired)\n */\nexport function timeUntilExpiry(expiresAt: number): number {\n return Math.max(0, expiresAt - Date.now())\n}\n\n/**\n * Convert seconds to milliseconds\n */\nexport function secondsToMs(seconds: number): number {\n return seconds * 1000\n}\n\n/**\n * Convert milliseconds to seconds\n */\nexport function msToSeconds(ms: number): number {\n return Math.floor(ms / 1000)\n}\n","/**\n * IP address extraction and validation utilities\n */\n\nimport type { NextRequest } from '../core/types'\n\n/**\n * Headers to check for client IP (in order of priority)\n */\nconst IP_HEADERS = [\n // Cloudflare\n 'cf-connecting-ip',\n // Vercel\n 'x-real-ip',\n // Standard forwarded header (RFC 7239)\n 'x-forwarded-for',\n // AWS ELB\n 'x-client-ip',\n // Azure\n 'client-ip',\n // Fastly\n 'fastly-client-ip',\n // Akamai\n 'true-client-ip',\n // Google Cloud\n 'x-appengine-user-ip',\n // Fly.io\n 'fly-client-ip',\n] as const\n\n/**\n * Localhost/private IP patterns\n */\nconst PRIVATE_IP_PATTERNS = [\n /^127\\./, // IPv4 loopback\n /^10\\./, // Private class A\n /^172\\.(1[6-9]|2[0-9]|3[01])\\./, // Private class B\n /^192\\.168\\./, // Private class C\n /^::1$/, // IPv6 loopback\n /^fe80:/i, // IPv6 link-local\n /^fc00:/i, // IPv6 unique local\n /^fd[0-9a-f]{2}:/i, // IPv6 unique local\n]\n\n/**\n * IPv4 validation regex\n */\nconst IPV4_REGEX = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/\n\n/**\n * IPv6 validation regex (simplified)\n */\nconst IPV6_REGEX = /^(?:[a-fA-F0-9]{1,4}:){7}[a-fA-F0-9]{1,4}$|^::1$|^::$|^(?:[a-fA-F0-9]{1,4}:)*:(?:[a-fA-F0-9]{1,4}:)*[a-fA-F0-9]{1,4}$/\n\n/**\n * Options for IP extraction\n */\nexport interface GetIpOptions {\n /**\n * Trust proxy headers (default: true)\n * Set to false in direct-to-client setups\n */\n trustProxy?: boolean\n\n /**\n * Additional headers to check (checked first)\n */\n customHeaders?: string[]\n\n /**\n * Fallback IP when none found\n */\n fallback?: string\n}\n\n/**\n * Extract client IP address from request\n *\n * @example\n * ```typescript\n * // Basic usage\n * const ip = getClientIp(request)\n *\n * // With options\n * const ip = getClientIp(request, {\n * trustProxy: true,\n * customHeaders: ['my-custom-ip-header'],\n * fallback: '0.0.0.0'\n * })\n * ```\n *\n * @param request - Next.js request object\n * @param options - Extraction options\n * @returns Client IP address or fallback\n */\nexport function getClientIp(request: NextRequest, options: GetIpOptions = {}): string {\n const { trustProxy = true, customHeaders = [], fallback = '127.0.0.1' } = options\n\n // First, check if Next.js has already extracted the IP\n if (request.ip) {\n return normalizeIp(request.ip)\n }\n\n if (!trustProxy) {\n return fallback\n }\n\n // Check custom headers first\n for (const header of customHeaders) {\n const value = request.headers.get(header)\n if (value) {\n const ip = parseIpFromHeader(value)\n if (ip) return ip\n }\n }\n\n // Check standard headers\n for (const header of IP_HEADERS) {\n const value = request.headers.get(header)\n if (value) {\n const ip = parseIpFromHeader(value)\n if (ip) return ip\n }\n }\n\n return fallback\n}\n\n/**\n * Parse IP from header value\n * Handles comma-separated lists (x-forwarded-for)\n */\nfunction parseIpFromHeader(headerValue: string): string | null {\n // x-forwarded-for can have multiple IPs: \"client, proxy1, proxy2\"\n // The first one is the client IP\n const ips = headerValue.split(',').map((ip) => ip.trim())\n\n for (const ip of ips) {\n const normalized = normalizeIp(ip)\n if (isValidIp(normalized)) {\n return normalized\n }\n }\n\n return null\n}\n\n/**\n * Normalize an IP address\n * - Removes IPv6 brackets\n * - Removes port numbers\n * - Trims whitespace\n */\nexport function normalizeIp(ip: string): string {\n let normalized = ip.trim()\n\n // Remove IPv6 brackets: [::1] -> ::1\n if (normalized.startsWith('[') && normalized.includes(']')) {\n normalized = normalized.slice(1, normalized.indexOf(']'))\n }\n\n // Remove port: 192.168.1.1:8080 -> 192.168.1.1\n // For IPv4 with port\n if (normalized.includes(':') && !normalized.includes('::')) {\n const lastColon = normalized.lastIndexOf(':')\n const potentialPort = normalized.slice(lastColon + 1)\n if (/^\\d+$/.test(potentialPort)) {\n normalized = normalized.slice(0, lastColon)\n }\n }\n\n // Handle IPv4-mapped IPv6: ::ffff:192.168.1.1 -> 192.168.1.1\n if (normalized.toLowerCase().startsWith('::ffff:')) {\n const ipv4Part = normalized.slice(7)\n if (isValidIpv4(ipv4Part)) {\n return ipv4Part\n }\n }\n\n return normalized\n}\n\n/**\n * Check if an IP address is valid\n */\nexport function isValidIp(ip: string): boolean {\n return isValidIpv4(ip) || isValidIpv6(ip)\n}\n\n/**\n * Check if an IPv4 address is valid\n */\nexport function isValidIpv4(ip: string): boolean {\n return IPV4_REGEX.test(ip)\n}\n\n/**\n * Check if an IPv6 address is valid\n */\nexport function isValidIpv6(ip: string): boolean {\n return IPV6_REGEX.test(ip) || ip === '::1' || ip === '::'\n}\n\n/**\n * Check if an IP is a private/local address\n */\nexport function isPrivateIp(ip: string): boolean {\n return PRIVATE_IP_PATTERNS.some((pattern) => pattern.test(ip))\n}\n\n/**\n * Check if an IP is localhost\n */\nexport function isLocalhost(ip: string): boolean {\n return ip === '127.0.0.1' || ip === '::1' || ip === 'localhost'\n}\n\n/**\n * Create a rate limit key from IP\n * Normalizes and optionally hashes the IP\n */\nexport function createIpKey(\n ip: string,\n options: {\n prefix?: string\n hash?: boolean\n } = {}\n): string {\n const { prefix = 'rl', hash = false } = options\n const normalizedIp = normalizeIp(ip)\n\n if (hash) {\n // Simple hash for privacy (not cryptographic)\n const hashCode = simpleHash(normalizedIp)\n return `${prefix}:ip:${hashCode}`\n }\n\n return `${prefix}:ip:${normalizedIp}`\n}\n\n/**\n * Simple non-cryptographic hash (for key generation)\n */\nfunction simpleHash(str: string): string {\n let hash = 0\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i)\n hash = ((hash << 5) - hash) + char\n hash = hash & hash // Convert to 32-bit integer\n }\n return Math.abs(hash).toString(36)\n}\n\n/**\n * Anonymize an IP address (for logging)\n * IPv4: 192.168.1.100 -> 192.168.1.xxx\n * IPv6: 2001:db8::1 -> 2001:db8::xxx\n */\nexport function anonymizeIp(ip: string): string {\n const normalized = normalizeIp(ip)\n\n if (isValidIpv4(normalized)) {\n const parts = normalized.split('.')\n parts[3] = 'xxx'\n return parts.join('.')\n }\n\n if (isValidIpv6(normalized)) {\n const parts = normalized.split(':')\n if (parts.length > 0) {\n parts[parts.length - 1] = 'xxxx'\n }\n return parts.join(':')\n }\n\n return 'xxx.xxx.xxx.xxx'\n}\n\n/**\n * Get geolocation info from request (if available)\n * Works with Vercel Edge and Cloudflare\n */\nexport function getGeoInfo(request: NextRequest): {\n country?: string\n city?: string\n region?: string\n latitude?: string\n longitude?: string\n} {\n // Vercel provides geo info on the request\n if (request.geo) {\n return {\n country: request.geo.country,\n city: request.geo.city,\n region: request.geo.region,\n latitude: request.geo.latitude,\n longitude: request.geo.longitude,\n }\n }\n\n // Cloudflare headers\n return {\n country: request.headers.get('cf-ipcountry') ?? undefined,\n city: request.headers.get('cf-ipcity') ?? undefined,\n region: request.headers.get('cf-region') ?? undefined,\n latitude: request.headers.get('cf-iplat') ?? undefined,\n longitude: request.headers.get('cf-iplong') ?? undefined,\n }\n}\n","/**\n * In-memory rate limit store\n *\n * Suitable for:\n * - Development\n * - Single-instance deployments\n * - Testing\n *\n * Not suitable for:\n * - Multi-instance/distributed deployments (use Redis/Upstash)\n * - Serverless (data lost between invocations)\n */\n\nimport type { RateLimitStore, MemoryStoreOptions } from '../types'\nimport { msToSeconds } from '../../../utils/time'\n\n/**\n * Entry stored in memory\n */\ninterface MemoryEntry {\n count: number\n reset: number // Unix timestamp (seconds)\n createdAt: number // Timestamp (ms)\n}\n\n/**\n * LRU-style memory store for rate limiting\n *\n * Features:\n * - Automatic cleanup of expired entries\n * - LRU eviction when max keys exceeded\n * - Zero dependencies\n * - Edge Runtime compatible\n *\n * @example\n * ```typescript\n * import { MemoryStore } from 'next-secure/rate-limit'\n *\n * const store = new MemoryStore({\n * cleanupInterval: 60000, // 1 minute\n * maxKeys: 10000\n * })\n * ```\n */\nexport class MemoryStore implements RateLimitStore {\n public readonly name = 'memory'\n\n private store: Map<string, MemoryEntry>\n private cleanupTimer: ReturnType<typeof setInterval> | null = null\n private readonly maxKeys: number\n private readonly cleanupInterval: number\n\n constructor(options: MemoryStoreOptions = {}) {\n const { cleanupInterval = 60000, maxKeys = 10000 } = options\n\n this.store = new Map()\n this.maxKeys = maxKeys\n this.cleanupInterval = cleanupInterval\n\n // Start cleanup timer (only in long-running environments)\n if (typeof setInterval !== 'undefined' && cleanupInterval > 0) {\n this.startCleanupTimer()\n }\n }\n\n /**\n * Increment the counter for a key\n *\n * Note: The key should already include window information if needed.\n * This store is algorithm-agnostic - algorithms handle windowing logic.\n */\n async increment(\n key: string,\n windowMs: number\n ): Promise<{ count: number; reset: number }> {\n const now = Date.now()\n const defaultReset = msToSeconds(now + windowMs)\n\n const existing = this.store.get(key)\n\n if (existing) {\n // Increment existing entry\n existing.count++\n // Move to end (LRU update)\n this.store.delete(key)\n this.store.set(key, existing)\n return { count: existing.count, reset: existing.reset }\n }\n\n // New entry\n const entry: MemoryEntry = {\n count: 1,\n reset: defaultReset,\n createdAt: now,\n }\n\n // Check if we need to evict\n if (this.store.size >= this.maxKeys) {\n this.evictOldest()\n }\n\n this.store.set(key, entry)\n return { count: 1, reset: defaultReset }\n }\n\n /**\n * Get the current count for a key\n */\n async get(key: string): Promise<{ count: number; reset: number } | null> {\n const entry = this.store.get(key)\n\n if (!entry) {\n return null\n }\n\n // Check if expired\n const now = Math.floor(Date.now() / 1000)\n if (entry.reset <= now) {\n this.store.delete(key)\n return null\n }\n\n return { count: entry.count, reset: entry.reset }\n }\n\n /**\n * Reset the counter for a key\n */\n async reset(key: string): Promise<void> {\n this.store.delete(key)\n }\n\n /**\n * Check if the store is healthy\n */\n async isHealthy(): Promise<boolean> {\n return true\n }\n\n /**\n * Cleanup expired entries\n */\n async cleanup(): Promise<void> {\n const now = Math.floor(Date.now() / 1000)\n const keysToDelete: string[] = []\n\n for (const [key, entry] of this.store) {\n if (entry.reset <= now) {\n keysToDelete.push(key)\n }\n }\n\n for (const key of keysToDelete) {\n this.store.delete(key)\n }\n }\n\n /**\n * Close the store (stop cleanup timer)\n */\n async close(): Promise<void> {\n this.stopCleanupTimer()\n this.store.clear()\n }\n\n /**\n * Get the current size of the store\n */\n get size(): number {\n return this.store.size\n }\n\n /**\n * Clear all entries\n */\n clear(): void {\n this.store.clear()\n }\n\n /**\n * Start the cleanup timer\n */\n private startCleanupTimer(): void {\n if (this.cleanupTimer) return\n\n this.cleanupTimer = setInterval(() => {\n void this.cleanup()\n }, this.cleanupInterval)\n\n // Unref to not keep the process alive\n if (typeof this.cleanupTimer === 'object' && 'unref' in this.cleanupTimer) {\n (this.cleanupTimer as NodeJS.Timeout).unref()\n }\n }\n\n /**\n * Stop the cleanup timer\n */\n private stopCleanupTimer(): void {\n if (this.cleanupTimer) {\n clearInterval(this.cleanupTimer)\n this.cleanupTimer = null\n }\n }\n\n /**\n * Evict oldest entries when max keys exceeded\n */\n private evictOldest(): void {\n // Map maintains insertion order, so first key is oldest\n const keysToDelete = Math.ceil(this.maxKeys * 0.1) // Delete 10%\n\n let deleted = 0\n for (const key of this.store.keys()) {\n if (deleted >= keysToDelete) break\n this.store.delete(key)\n deleted++\n }\n }\n}\n\n/**\n * Create a memory store with default options\n */\nexport function createMemoryStore(options?: MemoryStoreOptions): MemoryStore {\n return new MemoryStore(options)\n}\n\n/**\n * Global memory store instance (singleton)\n * Useful for serverless environments where you want to reuse across requests\n */\nlet globalStore: MemoryStore | null = null\n\n/**\n * Get or create the global memory store\n */\nexport function getGlobalMemoryStore(options?: MemoryStoreOptions): MemoryStore {\n if (!globalStore) {\n globalStore = new MemoryStore(options)\n }\n return globalStore\n}\n\n/**\n * Reset the global memory store (useful for testing)\n */\nexport function resetGlobalMemoryStore(): void {\n if (globalStore) {\n void globalStore.close()\n globalStore = null\n }\n}\n","/**\n * Sliding Window Rate Limiting Algorithm\n *\n * This algorithm provides a smoother rate limiting experience compared to fixed windows.\n * It uses a weighted calculation based on the previous and current window counts.\n *\n * How it works:\n * 1. Divide time into fixed windows (e.g., 1 minute each)\n * 2. Track request counts for current and previous windows\n * 3. Calculate weighted count based on position within current window\n *\n * Example (100 req/min limit):\n * - Previous window: 80 requests\n * - Current window: 30 requests\n * - 30 seconds into current window (50% through)\n * - Weighted count = 30 + (80 * 0.5) = 70\n * - Since 70 < 100, request is allowed\n *\n * Pros:\n * - Smoother than fixed window\n * - Prevents burst attacks at window boundaries\n * - Memory efficient (only stores 2 counters per key)\n *\n * Cons:\n * - Slightly more complex than fixed window\n * - Not perfectly accurate (approximation)\n */\n\nimport type { RateLimitStore, RateLimitAlgorithmImpl } from '../types'\nimport type { RateLimitInfo } from '../../../core/types'\nimport { msToSeconds } from '../../../utils/time'\n\n/**\n * Sliding window counter algorithm implementation\n */\nexport class SlidingWindowAlgorithm implements RateLimitAlgorithmImpl {\n public readonly name = 'sliding-window' as const\n\n /**\n * Check if the request should be rate limited\n */\n async check(\n store: RateLimitStore,\n key: string,\n limit: number,\n windowMs: number\n ): Promise<RateLimitInfo> {\n const now = Date.now()\n\n // Calculate window boundaries\n const windowStart = Math.floor(now / windowMs) * windowMs\n const windowEnd = windowStart + windowMs\n const previousWindowStart = windowStart - windowMs\n\n // Position within current window (0 to 1)\n const windowPosition = (now - windowStart) / windowMs\n\n // Keys for current and previous windows\n const currentKey = `${key}:${windowStart}`\n const previousKey = `${key}:${previousWindowStart}`\n\n // Get counts from both windows\n const [currentData, previousData] = await Promise.all([\n store.get(currentKey),\n store.get(previousKey),\n ])\n\n const currentCount = currentData?.count ?? 0\n const previousCount = previousData?.count ?? 0\n\n // Calculate weighted count using sliding window formula\n // Weight of previous window decreases as we move through current window\n const previousWeight = 1 - windowPosition\n const weightedCount = currentCount + Math.floor(previousCount * previousWeight)\n\n // Calculate reset time (end of current window)\n const reset = msToSeconds(windowEnd)\n\n // Check if limit exceeded\n if (weightedCount >= limit) {\n // Calculate retry time based on when enough requests will \"expire\"\n const retryAfter = this.calculateRetryAfter(\n currentCount,\n previousCount,\n limit,\n windowMs,\n windowPosition\n )\n\n return {\n limit,\n remaining: 0,\n reset,\n limited: true,\n retryAfter,\n }\n }\n\n // Increment current window counter\n await store.increment(currentKey, windowMs)\n\n // Calculate remaining\n const remaining = Math.max(0, limit - weightedCount - 1)\n\n return {\n limit,\n remaining,\n reset,\n limited: false,\n }\n }\n\n /**\n * Calculate how long until the client can make another request\n */\n private calculateRetryAfter(\n currentCount: number,\n previousCount: number,\n limit: number,\n windowMs: number,\n windowPosition: number\n ): number {\n // If previous window is empty, wait until current window resets\n if (previousCount === 0) {\n return Math.ceil((1 - windowPosition) * windowMs / 1000)\n }\n\n // Calculate when the weighted count will drop below limit\n // We need: currentCount + previousCount * (1 - newPosition) < limit\n // Solving for newPosition: newPosition > 1 - (limit - currentCount) / previousCount\n\n const requiredPosition = 1 - (limit - currentCount) / previousCount\n\n if (requiredPosition <= windowPosition) {\n // Should already be under limit, but we got here so add small delay\n return 1\n }\n\n if (requiredPosition >= 1) {\n // Need to wait until next window\n const remainingInCurrentWindow = (1 - windowPosition) * windowMs\n return Math.ceil(remainingInCurrentWindow / 1000)\n }\n\n // Calculate time until we reach required position\n const timeToWait = (requiredPosition - windowPosition) * windowMs\n return Math.ceil(timeToWait / 1000)\n }\n}\n\n/**\n * Create a sliding window algorithm instance\n */\nexport function createSlidingWindowAlgorithm(): SlidingWindowAlgorithm {\n return new SlidingWindowAlgorithm()\n}\n\n/**\n * Sliding Window Log Algorithm (more accurate but uses more memory)\n *\n * This stores individual request timestamps instead of just counters.\n * More accurate but not recommended for high-traffic scenarios.\n */\nexport class SlidingWindowLogAlgorithm implements RateLimitAlgorithmImpl {\n public readonly name = 'sliding-window' as const\n\n /**\n * In-memory log of request timestamps per key\n * For production, this should be stored externally (Redis sorted sets, etc.)\n */\n private logs: Map<string, number[]> = new Map()\n\n /**\n * Maximum log size before cleanup\n */\n private readonly maxLogSize = 10000\n\n async check(\n _store: RateLimitStore,\n key: string,\n limit: number,\n windowMs: number\n ): Promise<RateLimitInfo> {\n const now = Date.now()\n const windowStart = now - windowMs\n\n // Get or create log for this key\n let log = this.logs.get(key) ?? []\n\n // Remove expired entries\n log = log.filter((timestamp) => timestamp > windowStart)\n\n // Calculate reset (when oldest entry expires)\n const oldestTimestamp = log[0] ?? now\n const reset = msToSeconds(oldestTimestamp + windowMs)\n\n // Check if limit exceeded\n if (log.length >= limit) {\n const retryAfter = Math.ceil((oldestTimestamp + windowMs - now) / 1000)\n\n return {\n limit,\n remaining: 0,\n reset,\n limited: true,\n retryAfter: Math.max(1, retryAfter),\n }\n }\n\n // Add current request\n log.push(now)\n this.logs.set(key, log)\n\n // Cleanup if too many keys\n if (this.logs.size > this.maxLogSize) {\n this.cleanup()\n }\n\n return {\n limit,\n remaining: Math.max(0, limit - log.length),\n reset,\n limited: false,\n }\n }\n\n /**\n * Remove oldest entries when log size exceeded\n */\n private cleanup(): void {\n const keysToDelete: string[] = []\n const now = Date.now()\n\n for (const [key, log] of this.logs) {\n // Delete empty or very old logs\n if (log.length === 0 || log[log.length - 1]! < now - 3600000) {\n keysToDelete.push(key)\n }\n }\n\n for (const key of keysToDelete) {\n this.logs.delete(key)\n }\n }\n\n /**\n * Clear all logs\n */\n clear(): void {\n this.logs.clear()\n }\n}\n","/**\n * Fixed Window Rate Limiting Algorithm\n *\n * The simplest rate limiting algorithm. Divides time into fixed windows\n * and counts requests within each window.\n *\n * How it works:\n * 1. Divide time into fixed windows (e.g., every minute starting at :00)\n * 2. Count requests within the current window\n * 3. Reset counter when new window starts\n *\n * Example (100 req/min limit):\n * - Window 1 (12:00:00 - 12:00:59): 80 requests -> allowed\n * - Window 2 (12:01:00 - 12:01:59): 0 requests (fresh start)\n *\n * Pros:\n * - Simple to implement\n * - Memory efficient (only 1 counter per key)\n * - Fast (O(1) operations)\n *\n * Cons:\n * - Burst problem: 200 requests possible in 2 seconds at window boundary\n * - 100 requests at 12:00:59 (end of window 1)\n * - 100 requests at 12:01:00 (start of window 2)\n * - Not smooth\n *\n * Use when:\n * - Simplicity is preferred\n * - Burst at boundaries is acceptable\n * - Memory/CPU is very constrained\n */\n\nimport type { RateLimitStore, RateLimitAlgorithmImpl } from '../types'\nimport type { RateLimitInfo } from '../../../core/types'\nimport { msToSeconds } from '../../../utils/time'\n\n/**\n * Fixed window algorithm implementation\n */\nexport class FixedWindowAlgorithm implements RateLimitAlgorithmImpl {\n public readonly name = 'fixed-window' as const\n\n /**\n * Check if the request should be rate limited\n */\n async check(\n store: RateLimitStore,\n key: string,\n limit: number,\n windowMs: number\n ): Promise<RateLimitInfo> {\n const now = Date.now()\n\n // Calculate window boundaries\n const windowStart = Math.floor(now / windowMs) * windowMs\n const windowEnd = windowStart + windowMs\n const reset = msToSeconds(windowEnd)\n\n // Create window-specific key\n const windowKey = `${key}:${windowStart}`\n\n // Get current count\n const data = await store.get(windowKey)\n const currentCount = data?.count ?? 0\n\n // Check if limit exceeded\n if (currentCount >= limit) {\n const retryAfter = Math.ceil((windowEnd - now) / 1000)\n\n return {\n limit,\n remaining: 0,\n reset,\n limited: true,\n retryAfter: Math.max(1, retryAfter),\n }\n }\n\n // Increment counter\n const { count } = await store.increment(windowKey, windowMs)\n\n // Double-check after increment (race condition protection)\n if (count > limit) {\n const retryAfter = Math.ceil((windowEnd - now) / 1000)\n\n return {\n limit,\n remaining: 0,\n reset,\n limited: true,\n retryAfter: Math.max(1, retryAfter),\n }\n }\n\n return {\n limit,\n remaining: Math.max(0, limit - count),\n reset,\n limited: false,\n }\n }\n}\n\n/**\n * Create a fixed window algorithm instance\n */\nexport function createFixedWindowAlgorithm(): FixedWindowAlgorithm {\n return new FixedWindowAlgorithm()\n}\n\n/**\n * Fixed window with burst protection\n *\n * Adds a secondary limit to prevent bursts at window boundaries.\n * For example: 100 req/min with max 20 req/10sec burst.\n */\nexport class FixedWindowWithBurstProtection implements RateLimitAlgorithmImpl {\n public readonly name = 'fixed-window' as const\n\n constructor(\n private readonly burstLimit: number,\n private readonly burstWindowMs: number\n ) {}\n\n async check(\n store: RateLimitStore,\n key: string,\n limit: number,\n windowMs: number\n ): Promise<RateLimitInfo> {\n const now = Date.now()\n\n // Check burst limit first (smaller window)\n const burstWindowStart = Math.floor(now / this.burstWindowMs) * this.burstWindowMs\n const burstKey = `${key}:burst:${burstWindowStart}`\n const burstData = await store.get(burstKey)\n const burstCount = burstData?.count ?? 0\n\n if (burstCount >= this.burstLimit) {\n const burstWindowEnd = burstWindowStart + this.burstWindowMs\n const retryAfter = Math.ceil((burstWindowEnd - now) / 1000)\n\n return {\n limit: this.burstLimit,\n remaining: 0,\n reset: msToSeconds(burstWindowEnd),\n limited: true,\n retryAfter: Math.max(1, retryAfter),\n }\n }\n\n // Check main limit\n const windowStart = Math.floor(now / windowMs) * windowMs\n const windowEnd = windowStart + windowMs\n const windowKey = `${key}:${windowStart}`\n const data = await store.get(windowKey)\n const currentCount = data?.count ?? 0\n\n if (currentCount >= limit) {\n const retryAfter = Math.ceil((windowEnd - now) / 1000)\n\n return {\n limit,\n remaining: 0,\n reset: msToSeconds(windowEnd),\n limited: true,\n retryAfter: Math.max(1, retryAfter),\n }\n }\n\n // Increment both counters\n await Promise.all([\n store.increment(windowKey, windowMs),\n store.increment(burstKey, this.burstWindowMs),\n ])\n\n return {\n limit,\n remaining: Math.max(0, limit - currentCount - 1),\n reset: msToSeconds(windowEnd),\n limited: false,\n }\n }\n}\n\n/**\n * Create a fixed window with burst protection\n *\n * @example\n * ```typescript\n * // 100 req/min with max 20 req/10sec burst\n * const algorithm = createFixedWindowWithBurstProtection(20, 10000)\n * ```\n */\nexport function createFixedWindowWithBurstProtection(\n burstLimit: number,\n burstWindowMs: number\n): FixedWindowWithBurstProtection {\n return new FixedWindowWithBurstProtection(burstLimit, burstWindowMs)\n}\n","/**\n * Token Bucket Rate Limiting Algorithm\n *\n * A bucket holds tokens that are consumed by requests. Tokens are refilled\n * at a constant rate. This allows for controlled bursts while maintaining\n * an average rate.\n *\n * How it works:\n * 1. Bucket starts full with 'limit' tokens\n * 2. Each request consumes 1 token (or more for weighted requests)\n * 3. Tokens are refilled at 'limit / window' rate\n * 4. Request is allowed if tokens >= 1\n *\n * Example (100 tokens, refill 100/min = 1.67/sec):\n * - Initial: 100 tokens\n * - 50 requests instantly: 50 tokens remaining (burst allowed)\n * - Wait 30 seconds: 50 + (50 * 1.67) = 100 tokens (refilled)\n * - 100 requests instantly: 0 tokens\n * - Next request: denied until tokens refill\n *\n * Pros:\n * - Allows controlled bursts\n * - Smooth average rate\n * - Good for APIs with sporadic traffic\n *\n * Cons:\n * - More complex state management\n * - Requires storing last refill time\n *\n * Use when:\n * - You want to allow bursts\n * - Traffic is sporadic\n * - User experience matters (can handle burst then wait)\n */\n\nimport type { RateLimitStore, RateLimitAlgorithmImpl, TokenBucketState } from '../types'\nimport type { RateLimitInfo } from '../../../core/types'\nimport { msToSeconds } from '../../../utils/time'\n\n/**\n * Token bucket algorithm implementation\n */\nexport class TokenBucketAlgorithm implements RateLimitAlgorithmImpl {\n public readonly name = 'token-bucket' as const\n\n /**\n * In-memory bucket states\n * For distributed systems, this should be stored in Redis\n */\n private buckets: Map<string, TokenBucketState> = new Map()\n\n /**\n * Maximum number of buckets to store before cleanup\n */\n private readonly maxBuckets = 10000\n\n /**\n * Check if the request should be rate limited\n *\n * @param store - Not used directly, state stored in memory\n * @param key - Rate limit key\n * @param limit - Maximum tokens (bucket capacity)\n * @param windowMs - Time to refill bucket completely\n */\n async check(\n _store: RateLimitStore,\n key: string,\n limit: number,\n windowMs: number\n ): Promise<RateLimitInfo> {\n const now = Date.now()\n\n // Get or create bucket state\n let bucket = this.buckets.get(key)\n\n if (!bucket) {\n // New bucket, start full\n bucket = {\n tokens: limit,\n lastRefill: now,\n }\n } else {\n // Refill tokens based on time elapsed\n bucket = this.refillTokens(bucket, limit, windowMs, now)\n }\n\n // Calculate reset time (when bucket would be full again)\n const tokensNeeded = limit - bucket.tokens\n const refillRate = limit / windowMs // tokens per ms\n const timeToFull = tokensNeeded / refillRate\n const reset = msToSeconds(now + timeToFull)\n\n // Check if we have tokens\n if (bucket.tokens < 1) {\n // Calculate when we'll have 1 token\n const timeToOneToken = (1 - bucket.tokens) / refillRate\n const retryAfter = Math.ceil(timeToOneToken / 1000)\n\n return {\n limit,\n remaining: 0,\n reset,\n limited: true,\n retryAfter: Math.max(1, retryAfter),\n }\n }\n\n // Consume a token\n bucket.tokens -= 1\n this.buckets.set(key, bucket)\n\n // Cleanup if too many buckets\n if (this.buckets.size > this.maxBuckets) {\n this.cleanup()\n }\n\n return {\n limit,\n remaining: Math.floor(bucket.tokens),\n reset,\n limited: false,\n }\n }\n\n /**\n * Refill tokens based on time elapsed\n */\n private refillTokens(\n bucket: TokenBucketState,\n limit: number,\n windowMs: number,\n now: number\n ): TokenBucketState {\n const elapsed = now - bucket.lastRefill\n const refillRate = limit / windowMs // tokens per ms\n const tokensToAdd = elapsed * refillRate\n\n return {\n tokens: Math.min(limit, bucket.tokens + tokensToAdd),\n lastRefill: now,\n }\n }\n\n /**\n * Remove old buckets\n */\n private cleanup(): void {\n const now = Date.now()\n const staleThreshold = 3600000 // 1 hour\n\n const keysToDelete: string[] = []\n\n for (const [key, bucket] of this.buckets) {\n if (now - bucket.lastRefill > staleThreshold) {\n keysToDelete.push(key)\n }\n }\n\n for (const key of keysToDelete) {\n this.buckets.delete(key)\n }\n }\n\n /**\n * Get current bucket state (for testing/debugging)\n */\n getBucketState(key: string): TokenBucketState | undefined {\n return this.buckets.get(key)\n }\n\n /**\n * Clear all buckets\n */\n clear(): void {\n this.buckets.clear()\n }\n}\n\n/**\n * Create a token bucket algorithm instance\n */\nexport function createTokenBucketAlgorithm(): TokenBucketAlgorithm {\n return new TokenBucketAlgorithm()\n}\n\n/**\n * Leaky Bucket Algorithm (variation of token bucket)\n *\n * Instead of refilling tokens, requests \"leak\" out of the bucket\n * at a constant rate. This enforces a strict output rate.\n *\n * Think of it as a bucket with a hole at the bottom:\n * - Requests are added to the bucket\n * - Requests leak out at a constant rate\n * - If bucket overflows, request is rejected\n *\n * Use when:\n * - You need strict rate enforcement\n * - Bursts should be queued, not rejected\n * - Output rate must be constant\n */\nexport class LeakyBucketAlgorithm implements RateLimitAlgorithmImpl {\n public readonly name = 'token-bucket' as const // Grouped with token bucket\n\n /**\n * In-memory bucket states\n * Stores the \"water level\" and last leak time\n */\n private buckets: Map<string, { level: number; lastLeak: number }> = new Map()\n\n /**\n * Maximum number of buckets\n */\n private readonly maxBuckets = 10000\n\n async check(\n _store: RateLimitStore,\n key: string,\n limit: number,\n windowMs: number\n ): Promise<RateLimitInfo> {\n const now = Date.now()\n\n // Get or create bucket\n let bucket = this.buckets.get(key) ?? { level: 0, lastLeak: now }\n\n // Calculate how much has leaked since last check\n const elapsed = now - bucket.lastLeak\n const leakRate = limit / windowMs // requests per ms\n const leaked = elapsed * leakRate\n\n // Update level (can't go below 0)\n bucket.level = Math.max(0, bucket.level - leaked)\n bucket.lastLeak = now\n\n // Calculate reset time\n const timeToEmpty = bucket.level / leakRate\n const reset = msToSeconds(now + timeToEmpty)\n\n // Check if bucket would overflow\n if (bucket.level + 1 > limit) {\n // Calculate when there's room for 1 more request\n const overflow = bucket.level + 1 - limit\n const timeToRoom = overflow / leakRate\n const retryAfter = Math.ceil(timeToRoom / 1000)\n\n return {\n limit,\n remaining: 0,\n reset,\n limited: true,\n retryAfter: Math.max(1, retryAfter),\n }\n }\n\n // Add request to bucket\n bucket.level += 1\n this.buckets.set(key, bucket)\n\n // Cleanup if needed\n if (this.buckets.size > this.maxBuckets) {\n this.cleanup(now)\n }\n\n return {\n limit,\n remaining: Math.floor(limit - bucket.level),\n reset,\n limited: false,\n }\n }\n\n private cleanup(now: number): void {\n const staleThreshold = 3600000\n\n for (const [key, bucket] of this.buckets) {\n if (now - bucket.lastLeak > staleThreshold) {\n this.buckets.delete(key)\n }\n }\n }\n\n clear(): void {\n this.buckets.clear()\n }\n}\n\n/**\n * Create a leaky bucket algorithm instance\n */\nexport function createLeakyBucketAlgorithm(): LeakyBucketAlgorithm {\n return new LeakyBucketAlgorithm()\n}\n","/**\n * Rate Limiting Middleware for Next.js App Router\n *\n * @example\n * ```typescript\n * // Basic usage\n * import { withRateLimit } from 'next-secure/rate-limit'\n *\n * export const GET = withRateLimit(\n * async (req) => Response.json({ data: [] }),\n * { limit: 100, window: '15m' }\n * )\n *\n * // With custom identifier\n * export const GET = withRateLimit(handler, {\n * limit: 100,\n * window: '15m',\n * identifier: (req) => req.headers.get('x-api-key') ?? 'anonymous'\n * })\n *\n * // With Redis store\n * import { createRedisStore } from 'next-secure/rate-limit'\n *\n * const store = createRedisStore({ client: redis })\n *\n * export const GET = withRateLimit(handler, {\n * limit: 100,\n * window: '15m',\n * store\n * })\n * ```\n */\n\nimport type { NextRequest, RateLimitInfo, SecureContext } from '../../core/types'\nimport { RateLimitError } from '../../core/errors'\nimport { parseDuration } from '../../utils/time'\nimport { getClientIp } from '../../utils/ip'\nimport type { RateLimitConfig, RateLimitStore, RateLimitAlgorithmImpl } from './types'\nimport { MemoryStore } from './stores/memory'\nimport { SlidingWindowAlgorithm } from './algorithms/sliding-window'\nimport { FixedWindowAlgorithm } from './algorithms/fixed-window'\nimport { TokenBucketAlgorithm } from './algorithms/token-bucket'\n\n/**\n * Default configuration values\n */\nconst DEFAULT_CONFIG: Partial<RateLimitConfig> = {\n algorithm: 'sliding-window',\n identifier: 'ip',\n headers: true,\n prefix: 'rl',\n message: 'Too Many Requests',\n statusCode: 429,\n debug: false,\n}\n\n/**\n * Global default store (shared across handlers)\n */\nlet defaultStore: RateLimitStore | null = null\n\n/**\n * Get or create the default memory store\n */\nfunction getDefaultStore(): RateLimitStore {\n if (!defaultStore) {\n defaultStore = new MemoryStore()\n }\n return defaultStore\n}\n\n/**\n * Get algorithm instance by name\n */\nfunction getAlgorithm(name: RateLimitConfig['algorithm']): RateLimitAlgorithmImpl {\n switch (name) {\n case 'fixed-window':\n return new FixedWindowAlgorithm()\n case 'token-bucket':\n return new TokenBucketAlgorithm()\n case 'sliding-window':\n default:\n return new SlidingWindowAlgorithm()\n }\n}\n\n/**\n * Create rate limit headers\n */\nfunction createRateLimitHeaders(info: RateLimitInfo): Headers {\n const headers = new Headers()\n\n headers.set('X-RateLimit-Limit', String(info.limit))\n headers.set('X-RateLimit-Remaining', String(info.remaining))\n headers.set('X-RateLimit-Reset', String(info.reset))\n\n if (info.limited && info.retryAfter) {\n headers.set('Retry-After', String(info.retryAfter))\n }\n\n return headers\n}\n\n/**\n * Merge headers from multiple sources\n */\nfunction mergeHeaders(target: Headers, source: Headers): void {\n source.forEach((value, key) => {\n target.set(key, value)\n })\n}\n\n/**\n * Get client identifier from request\n */\nasync function getIdentifier(\n request: NextRequest,\n identifier: RateLimitConfig['identifier'],\n prefix: string,\n context?: SecureContext\n): Promise<string> {\n if (typeof identifier === 'function') {\n const id = await identifier(request)\n return `${prefix}:custom:${id}`\n }\n\n if (identifier === 'user') {\n // Requires auth middleware to have run first\n const userId = context?.user\n ? (context.user as { id?: string }).id ?? 'anonymous'\n : 'anonymous'\n return `${prefix}:user:${userId}`\n }\n\n // Default: IP-based\n const ip = getClientIp(request)\n return `${prefix}:ip:${ip}`\n}\n\n/**\n * Rate limiting middleware wrapper\n *\n * @example\n * ```typescript\n * // Simple usage\n * export const GET = withRateLimit(\n * async (req) => Response.json({ ok: true }),\n * { limit: 100, window: '15m' }\n * )\n *\n * // With all options\n * export const POST = withRateLimit(\n * async (req, ctx) => {\n * // ctx.rateLimit contains info\n * return Response.json({ remaining: ctx.rateLimit?.remaining })\n * },\n * {\n * limit: 10,\n * window: '1m',\n * algorithm: 'sliding-window',\n * identifier: 'ip',\n * headers: true,\n * onLimit: (req, info) => new Response(\n * JSON.stringify({ error: 'Slow down!' }),\n * { status: 429 }\n * ),\n * skip: (req) => req.headers.get('x-bypass') === 'secret'\n * }\n * )\n * ```\n */\nexport function withRateLimit<TUser = unknown>(\n handler: (\n request: NextRequest,\n context: SecureContext<TUser> & { rateLimit?: RateLimitInfo }\n ) => Promise<Response> | Response,\n config: RateLimitConfig\n): (request: NextRequest, context?: SecureContext<TUser>) => Promise<Response> {\n // Merge config with defaults\n const finalConfig: Required<RateLimitConfig> = {\n ...DEFAULT_CONFIG,\n ...config,\n store: config.store ?? getDefaultStore(),\n } as Required<RateLimitConfig>\n\n // Parse window duration once\n const windowMs = parseDuration(finalConfig.window)\n\n // Get algorithm once\n const algorithm = getAlgorithm(finalConfig.algorithm)\n\n // Debug logging\n const debug = finalConfig.debug\n ? (msg: string, data?: unknown) => {\n // eslint-disable-next-line no-console\n console.log(`[next-secure:rate-limit] ${msg}`, data ?? '')\n }\n : () => {}\n\n debug('Initialized', {\n limit: finalConfig.limit,\n window: finalConfig.window,\n algorithm: finalConfig.algorithm,\n })\n\n return async (\n request: NextRequest,\n context?: SecureContext<TUser>\n ): Promise<Response> => {\n // Create context if not provided\n const ctx: SecureContext<TUser> & { rateLimit?: RateLimitInfo } = context ?? {\n user: null,\n requestId: crypto.randomUUID(),\n ip: getClientIp(request),\n userAgent: request.headers.get('user-agent') ?? '',\n startTime: Date.now(),\n metadata: {},\n }\n\n try {\n // Check if we should skip rate limiting\n if (finalConfig.skip) {\n const shouldSkip = await finalConfig.skip(request)\n if (shouldSkip) {\n debug('Skipping rate limit check')\n return handler(request, ctx)\n }\n }\n\n // Get identifier\n const key = await getIdentifier(\n request,\n finalConfig.identifier,\n finalConfig.prefix,\n ctx\n )\n debug('Rate limit key', key)\n\n // Check rate limit\n const info = await algorithm.check(\n finalConfig.store,\n key,\n finalConfig.limit,\n windowMs\n )\n debug('Rate limit info', info)\n\n // Add rate limit info to context\n ctx.rateLimit = info\n\n // Check if limited\n if (info.limited) {\n debug('Request rate limited')\n\n // Custom handler\n if (finalConfig.onLimit) {\n const response = await finalConfig.onLimit(request, info)\n\n // Add headers to custom response\n if (finalConfig.headers) {\n const rateLimitHeaders = createRateLimitHeaders(info)\n mergeHeaders(response.headers, rateLimitHeaders)\n }\n\n return response\n }\n\n // Default rate limit response\n const error = new RateLimitError({\n retryAfter: info.retryAfter ?? 60,\n resetAt: info.reset * 1000,\n message: finalConfig.message,\n })\n\n const response = error.toResponse()\n\n if (finalConfig.headers) {\n const rateLimitHeaders = createRateLimitHeaders(info)\n mergeHeaders(response.headers, rateLimitHeaders)\n }\n\n return response\n }\n\n // Call the handler\n const response = await handler(request, ctx)\n\n // Add rate limit headers to successful response\n if (finalConfig.headers) {\n // Clone response to modify headers\n const newResponse = new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers: new Headers(response.headers),\n })\n\n const rateLimitHeaders = createRateLimitHeaders(info)\n mergeHeaders(newResponse.headers, rateLimitHeaders)\n\n return newResponse\n }\n\n return response\n } catch (error) {\n debug('Error in rate limit middleware', error)\n\n // Re-throw RateLimitError\n if (error instanceof RateLimitError) {\n throw error\n }\n\n // For other errors, let the request through (fail open)\n // This prevents rate limiting from blocking all requests on errors\n // eslint-disable-next-line no-console\n console.error('[next-secure:rate-limit] Error:', error)\n return handler(request, ctx)\n }\n }\n}\n\n/**\n * Create a rate limiter instance for reuse\n *\n * @example\n * ```typescript\n * const apiLimiter = createRateLimiter({\n * limit: 100,\n * window: '15m'\n * })\n *\n * export const GET = apiLimiter(async (req) => Response.json({ ok: true }))\n * export const POST = apiLimiter(async (req) => Response.json({ ok: true }))\n * ```\n */\nexport function createRateLimiter(config: RateLimitConfig) {\n return <TUser = unknown>(\n handler: (\n request: NextRequest,\n context: SecureContext<TUser> & { rateLimit?: RateLimitInfo }\n ) => Promise<Response> | Response\n ) => withRateLimit(handler, config)\n}\n\n/**\n * Check rate limit without wrapping a handler\n * Useful for checking rate limit in existing code\n *\n * @example\n * ```typescript\n * export async function GET(req: NextRequest) {\n * const result = await checkRateLimit(req, {\n * limit: 100,\n * window: '15m'\n * })\n *\n * if (!result.success) {\n * return result.response\n * }\n *\n * // Continue with normal logic\n * return Response.json({ ok: true })\n * }\n * ```\n */\nexport async function checkRateLimit(\n request: NextRequest,\n config: RateLimitConfig\n): Promise<{\n success: boolean\n info: RateLimitInfo\n response?: Response\n headers: Headers\n}> {\n const finalConfig = {\n ...DEFAULT_CONFIG,\n ...config,\n store: config.store ?? getDefaultStore(),\n } as Required<RateLimitConfig>\n\n const windowMs = parseDuration(finalConfig.window)\n const algorithm = getAlgorithm(finalConfig.algorithm)\n\n // Check if should skip\n if (finalConfig.skip) {\n const shouldSkip = await finalConfig.skip(request)\n if (shouldSkip) {\n const info: RateLimitInfo = {\n limit: finalConfig.limit,\n remaining: finalConfig.limit,\n reset: Math.floor(Date.now() / 1000) + Math.floor(windowMs / 1000),\n limited: false,\n }\n return { success: true, info, headers: new Headers() }\n }\n }\n\n const key = await getIdentifier(request, finalConfig.identifier, finalConfig.prefix)\n const info = await algorithm.check(finalConfig.store, key, finalConfig.limit, windowMs)\n const headers = finalConfig.headers ? createRateLimitHeaders(info) : new Headers()\n\n if (info.limited) {\n let response: Response\n\n if (finalConfig.onLimit) {\n response = await finalConfig.onLimit(request, info)\n } else {\n const error = new RateLimitError({\n retryAfter: info.retryAfter ?? 60,\n resetAt: info.reset * 1000,\n message: finalConfig.message,\n })\n response = error.toResponse()\n }\n\n if (finalConfig.headers) {\n mergeHeaders(response.headers, headers)\n }\n\n return { success: false, info, response, headers }\n }\n\n return { success: true, info, headers }\n}\n\n/**\n * Reset rate limit for a specific key\n *\n * @example\n * ```typescript\n * // Reset rate limit for an IP\n * await resetRateLimit('ip', '192.168.1.1')\n *\n * // Reset for a user\n * await resetRateLimit('user', 'user-123')\n * ```\n */\nexport async function resetRateLimit(\n type: 'ip' | 'user' | 'custom',\n identifier: string,\n options?: {\n store?: RateLimitStore\n prefix?: string\n }\n): Promise<void> {\n const store = options?.store ?? getDefaultStore()\n const prefix = options?.prefix ?? 'rl'\n const key = `${prefix}:${type}:${identifier}`\n\n await store.reset(key)\n}\n\n/**\n * Get current rate limit status for a key (without incrementing)\n */\nexport async function getRateLimitStatus(\n type: 'ip' | 'user' | 'custom',\n identifier: string,\n options?: {\n store?: RateLimitStore\n prefix?: string\n }\n): Promise<{ count: number; reset: number } | null> {\n const store = options?.store ?? getDefaultStore()\n const prefix = options?.prefix ?? 'rl'\n const key = `${prefix}:${type}:${identifier}`\n\n return store.get(key)\n}\n\n/**\n * Clear all rate limits (useful for testing)\n */\nexport function clearAllRateLimits(): void {\n if (defaultStore && 'clear' in defaultStore) {\n (defaultStore as MemoryStore).clear()\n }\n}\n","import { webcrypto } from 'node:crypto'\n\nconst encoder = new TextEncoder()\n\n/**\n * Generate random bytes as hex string\n */\nexport function randomBytes(length: number): string {\n const bytes = new Uint8Array(length)\n webcrypto.getRandomValues(bytes)\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('')\n}\n\n/**\n * Create HMAC signature\n */\nasync function createSignature(data: string, secret: string): Promise<string> {\n const key = await webcrypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign']\n )\n\n const sig = await webcrypto.subtle.sign('HMAC', key, encoder.encode(data))\n return Array.from(new Uint8Array(sig))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('')\n}\n\n/**\n * Constant-time string comparison to prevent timing attacks\n */\nfunction safeCompare(a: string, b: string): boolean {\n if (a.length !== b.length) return false\n\n let result = 0\n for (let i = 0; i < a.length; i++) {\n result |= a.charCodeAt(i) ^ b.charCodeAt(i)\n }\n return result === 0\n}\n\n/**\n * Create a signed CSRF token\n */\nexport async function createToken(\n secret: string,\n length: number = 32\n): Promise<string> {\n const data = randomBytes(length)\n const sig = await createSignature(data, secret)\n return `${data}.${sig}`\n}\n\n/**\n * Verify a signed CSRF token\n */\nexport async function verifyToken(\n token: string,\n secret: string\n): Promise<boolean> {\n if (!token || typeof token !== 'string') return false\n\n const parts = token.split('.')\n if (parts.length !== 2) return false\n\n const [data, sig] = parts\n if (!data || !sig) return false\n\n try {\n const expected = await createSignature(data, secret)\n return safeCompare(sig, expected)\n } catch {\n return false\n }\n}\n\n/**\n * Compare two tokens (constant-time)\n */\nexport function tokensMatch(a: string, b: string): boolean {\n if (!a || !b) return false\n return safeCompare(a, b)\n}\n","import type { NextRequest } from 'next/server'\nimport type { CSRFConfig, CSRFCookieOptions } from './types'\nimport { createToken, verifyToken, tokensMatch } from './token'\n\ntype RouteHandler = (req: NextRequest) => Response | Promise<Response>\n\nconst DEFAULT_COOKIE: CSRFCookieOptions = {\n name: '__csrf',\n path: '/',\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'strict',\n maxAge: 86400, // 24h\n}\n\nconst DEFAULT_CONFIG: Required<Omit<CSRFConfig, 'skip' | 'onError'>> = {\n cookie: DEFAULT_COOKIE,\n headerName: 'x-csrf-token',\n fieldName: '_csrf',\n secret: '',\n tokenLength: 32,\n protectedMethods: ['POST', 'PUT', 'PATCH', 'DELETE'],\n}\n\nfunction getSecret(config: CSRFConfig): string {\n const secret = config.secret || process.env.CSRF_SECRET\n if (!secret) {\n throw new Error(\n 'CSRF secret is required. Set config.secret or CSRF_SECRET env variable.'\n )\n }\n return secret\n}\n\nfunction buildCookieString(name: string, value: string, opts: CSRFCookieOptions): string {\n let cookie = `${name}=${value}`\n\n if (opts.path) cookie += `; Path=${opts.path}`\n if (opts.domain) cookie += `; Domain=${opts.domain}`\n if (opts.maxAge) cookie += `; Max-Age=${opts.maxAge}`\n if (opts.httpOnly) cookie += '; HttpOnly'\n if (opts.secure) cookie += '; Secure'\n if (opts.sameSite) cookie += `; SameSite=${opts.sameSite}`\n\n return cookie\n}\n\n/**\n * Extract token from request (header or body)\n */\nasync function extractToken(\n req: NextRequest,\n headerName: string,\n fieldName: string\n): Promise<string | null> {\n // check header first\n const headerToken = req.headers.get(headerName)\n if (headerToken) return headerToken\n\n // try to get from form data\n const contentType = req.headers.get('content-type') || ''\n\n if (contentType.includes('application/x-www-form-urlencoded')) {\n try {\n const cloned = req.clone()\n const formData = await cloned.formData()\n const token = formData.get(fieldName)\n if (typeof token === 'string') return token\n } catch {\n // ignore parse errors\n }\n }\n\n if (contentType.includes('application/json')) {\n try {\n const cloned = req.clone()\n const body = await cloned.json()\n if (body && typeof body[fieldName] === 'string') {\n return body[fieldName]\n }\n } catch {\n // ignore parse errors\n }\n }\n\n return null\n}\n\nfunction defaultErrorResponse(_req: NextRequest, reason: string): Response {\n return new Response(JSON.stringify({ error: 'CSRF validation failed', reason }), {\n status: 403,\n headers: { 'Content-Type': 'application/json' },\n })\n}\n\n/**\n * CSRF protection middleware\n *\n * Uses double submit cookie pattern:\n * 1. Server sets a signed token in a cookie\n * 2. Client sends the same token in header/body\n * 3. Server compares both values\n */\nexport function withCSRF(handler: RouteHandler, config: CSRFConfig = {}): RouteHandler {\n const secret = getSecret(config)\n const cookieOpts = { ...DEFAULT_COOKIE, ...config.cookie }\n const headerName = config.headerName || DEFAULT_CONFIG.headerName\n const fieldName = config.fieldName || DEFAULT_CONFIG.fieldName\n const protectedMethods = config.protectedMethods || DEFAULT_CONFIG.protectedMethods\n const onError = config.onError || defaultErrorResponse\n\n return async (req: NextRequest): Promise<Response> => {\n const method = req.method.toUpperCase()\n\n // skip unprotected methods\n if (!protectedMethods.includes(method)) {\n return handler(req)\n }\n\n // custom skip logic\n if (config.skip) {\n const shouldSkip = await config.skip(req)\n if (shouldSkip) return handler(req)\n }\n\n const cookieName = cookieOpts.name || '__csrf'\n const cookieToken = req.cookies.get(cookieName)?.value\n\n // no cookie = first request, reject\n if (!cookieToken) {\n return onError(req, 'missing_cookie')\n }\n\n // verify cookie token is valid (signed by us)\n const cookieValid = await verifyToken(cookieToken, secret)\n if (!cookieValid) {\n return onError(req, 'invalid_cookie')\n }\n\n // get token from request\n const requestToken = await extractToken(req, headerName, fieldName)\n if (!requestToken) {\n return onError(req, 'missing_token')\n }\n\n // compare tokens\n if (!tokensMatch(cookieToken, requestToken)) {\n return onError(req, 'token_mismatch')\n }\n\n return handler(req)\n }\n}\n\n/**\n * Generate a new CSRF token and cookie header\n * Use this in GET routes to set the initial token\n */\nexport async function generateCSRF(config: CSRFConfig = {}): Promise<{\n token: string\n cookieHeader: string\n}> {\n const secret = getSecret(config)\n const cookieOpts = { ...DEFAULT_COOKIE, ...config.cookie }\n const tokenLength = config.tokenLength || DEFAULT_CONFIG.tokenLength\n const cookieName = cookieOpts.name || '__csrf'\n\n const token = await createToken(secret, tokenLength)\n const cookieHeader = buildCookieString(cookieName, token, cookieOpts)\n\n return { token, cookieHeader }\n}\n\n/**\n * Validate a CSRF token without middleware\n * Useful for custom validation flows\n */\nexport async function validateCSRF(\n req: NextRequest,\n config: CSRFConfig = {}\n): Promise<{ valid: boolean; reason?: string }> {\n const secret = getSecret(config)\n const cookieOpts = { ...DEFAULT_COOKIE, ...config.cookie }\n const headerName = config.headerName || DEFAULT_CONFIG.headerName\n const fieldName = config.fieldName || DEFAULT_CONFIG.fieldName\n const cookieName = cookieOpts.name || '__csrf'\n\n const cookieToken = req.cookies.get(cookieName)?.value\n if (!cookieToken) {\n return { valid: false, reason: 'missing_cookie' }\n }\n\n const cookieValid = await verifyToken(cookieToken, secret)\n if (!cookieValid) {\n return { valid: false, reason: 'invalid_cookie' }\n }\n\n const requestToken = await extractToken(req, headerName, fieldName)\n if (!requestToken) {\n return { valid: false, reason: 'missing_token' }\n }\n\n if (!tokensMatch(cookieToken, requestToken)) {\n return { valid: false, reason: 'token_mismatch' }\n }\n\n return { valid: true }\n}\n","/**\n * next-secure\n *\n * Production-ready security middleware for Next.js 13+ App Router.\n *\n * @example\n * ```typescript\n * import { withRateLimit, withAuth, secure } from 'next-secure'\n *\n * // Simple rate limiting\n * export const GET = withRateLimit(\n * async (req) => Response.json({ ok: true }),\n * { limit: 100, window: '15m' }\n * )\n *\n * // Builder pattern\n * export const POST = secure()\n * .rateLimit({ limit: 10, window: '1m' })\n * .auth({ roles: ['admin'] })\n * .handle(async (req, ctx) => {\n * return Response.json({ user: ctx.user })\n * })\n * ```\n *\n * @packageDocumentation\n */\n\n// =============================================================================\n// Core\n// =============================================================================\n\nexport type {\n NextRequest,\n SecureContext,\n SecureHandler,\n Middleware,\n ErrorResponse,\n RateLimitInfo,\n Duration,\n RateLimitAlgorithm,\n RateLimitIdentifier,\n} from './core/types'\n\nexport {\n SecureError,\n RateLimitError,\n AuthenticationError,\n AuthorizationError,\n ValidationError,\n CsrfError,\n ConfigurationError,\n isSecureError,\n toSecureError,\n} from './core/errors'\n\n// =============================================================================\n// Rate Limiting\n// =============================================================================\n\nexport {\n withRateLimit,\n createRateLimiter,\n checkRateLimit,\n resetRateLimit,\n getRateLimitStatus,\n clearAllRateLimits,\n} from './middleware/rate-limit'\n\nexport type {\n RateLimitConfig,\n RateLimitStore,\n MemoryStoreOptions,\n RedisStoreOptions,\n UpstashStoreOptions,\n} from './middleware/rate-limit'\n\nexport {\n MemoryStore,\n createMemoryStore,\n getGlobalMemoryStore,\n} from './middleware/rate-limit'\n\n// =============================================================================\n// CSRF Protection\n// =============================================================================\n\nexport {\n withCSRF,\n generateCSRF,\n validateCSRF,\n createToken as createCSRFToken,\n verifyToken as verifyCSRFToken,\n tokensMatch,\n} from './middleware/csrf'\n\nexport type {\n CSRFConfig,\n CSRFCookieOptions,\n CSRFToken,\n} from './middleware/csrf'\n\n// =============================================================================\n// Utilities\n// =============================================================================\n\nexport {\n parseDuration,\n formatDuration,\n nowInSeconds,\n nowInMs,\n sleep,\n} from './utils/time'\n\nexport {\n getClientIp,\n normalizeIp,\n isValidIp,\n isPrivateIp,\n isLocalhost,\n anonymizeIp,\n getGeoInfo,\n} from './utils/ip'\n\n// =============================================================================\n// Version\n// =============================================================================\n\n/**\n * Package version\n */\nexport const VERSION = '0.2.0'\n"]}
package/dist/index.d.cts CHANGED
@@ -1,5 +1,7 @@
1
- import { D as Duration, N as NextRequest } from './memory-g9zyQPy5.cjs';
2
- export { E as ErrorResponse, l as MemoryStore, j as MemoryStoreOptions, M as Middleware, b as RateLimitAlgorithm, h as RateLimitConfig, c as RateLimitIdentifier, R as RateLimitInfo, i as RateLimitStore, k as RedisStoreOptions, S as SecureContext, a as SecureHandler, U as UpstashStoreOptions, e as checkRateLimit, f as clearAllRateLimits, m as createMemoryStore, d as createRateLimiter, n as getGlobalMemoryStore, g as getRateLimitStatus, r as resetRateLimit, w as withRateLimit } from './memory-g9zyQPy5.cjs';
1
+ import { D as Duration, N as NextRequest } from './memory-Dauy-IH3.cjs';
2
+ export { E as ErrorResponse, k as MemoryStore, M as MemoryStoreOptions, q as Middleware, n as RateLimitAlgorithm, f as RateLimitConfig, s as RateLimitIdentifier, b as RateLimitInfo, a as RateLimitStore, j as RedisStoreOptions, o as SecureContext, p as SecureHandler, U as UpstashStoreOptions, d as checkRateLimit, e as clearAllRateLimits, l as createMemoryStore, c as createRateLimiter, m as getGlobalMemoryStore, g as getRateLimitStatus, r as resetRateLimit, w as withRateLimit } from './memory-Dauy-IH3.cjs';
3
+ export { CSRFConfig, CSRFCookieOptions, CSRFToken, createToken as createCSRFToken, generateCSRF, tokensMatch, validateCSRF, verifyToken as verifyCSRFToken, withCSRF } from './csrf.cjs';
4
+ import 'next/server';
3
5
 
4
6
  /**
5
7
  * Custom error classes for next-secure
@@ -300,6 +302,6 @@ declare function getGeoInfo(request: NextRequest): {
300
302
  /**
301
303
  * Package version
302
304
  */
303
- declare const VERSION = "0.1.0";
305
+ declare const VERSION = "0.2.0";
304
306
 
305
307
  export { AuthenticationError, AuthorizationError, ConfigurationError, CsrfError, Duration, NextRequest, RateLimitError, SecureError, VERSION, ValidationError, anonymizeIp, formatDuration, getClientIp, getGeoInfo, isLocalhost, isPrivateIp, isSecureError, isValidIp, normalizeIp, nowInMs, nowInSeconds, parseDuration, sleep, toSecureError };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
- import { D as Duration, N as NextRequest } from './memory-g9zyQPy5.js';
2
- export { E as ErrorResponse, l as MemoryStore, j as MemoryStoreOptions, M as Middleware, b as RateLimitAlgorithm, h as RateLimitConfig, c as RateLimitIdentifier, R as RateLimitInfo, i as RateLimitStore, k as RedisStoreOptions, S as SecureContext, a as SecureHandler, U as UpstashStoreOptions, e as checkRateLimit, f as clearAllRateLimits, m as createMemoryStore, d as createRateLimiter, n as getGlobalMemoryStore, g as getRateLimitStatus, r as resetRateLimit, w as withRateLimit } from './memory-g9zyQPy5.js';
1
+ import { D as Duration, N as NextRequest } from './memory-Dauy-IH3.js';
2
+ export { E as ErrorResponse, k as MemoryStore, M as MemoryStoreOptions, q as Middleware, n as RateLimitAlgorithm, f as RateLimitConfig, s as RateLimitIdentifier, b as RateLimitInfo, a as RateLimitStore, j as RedisStoreOptions, o as SecureContext, p as SecureHandler, U as UpstashStoreOptions, d as checkRateLimit, e as clearAllRateLimits, l as createMemoryStore, c as createRateLimiter, m as getGlobalMemoryStore, g as getRateLimitStatus, r as resetRateLimit, w as withRateLimit } from './memory-Dauy-IH3.js';
3
+ export { CSRFConfig, CSRFCookieOptions, CSRFToken, createToken as createCSRFToken, generateCSRF, tokensMatch, validateCSRF, verifyToken as verifyCSRFToken, withCSRF } from './csrf.js';
4
+ import 'next/server';
3
5
 
4
6
  /**
5
7
  * Custom error classes for next-secure
@@ -300,6 +302,6 @@ declare function getGeoInfo(request: NextRequest): {
300
302
  /**
301
303
  * Package version
302
304
  */
303
- declare const VERSION = "0.1.0";
305
+ declare const VERSION = "0.2.0";
304
306
 
305
307
  export { AuthenticationError, AuthorizationError, ConfigurationError, CsrfError, Duration, NextRequest, RateLimitError, SecureError, VERSION, ValidationError, anonymizeIp, formatDuration, getClientIp, getGeoInfo, isLocalhost, isPrivateIp, isSecureError, isValidIp, normalizeIp, nowInMs, nowInSeconds, parseDuration, sleep, toSecureError };