import type { AuthConfig, RateLimitConfig } from '../types.js'; import { type RateLimitKey } from './security-defaults.js'; export interface RateLimitResult { allowed: boolean; remaining: number; retryAfterMs: number; } export interface RateLimitEntry { count: number; resetAt: number; } /** * Storage interface for the rate-limiter. Implementations may be synchronous * (default in-memory Map) or asynchronous (Redis, Prisma, Upstash, etc.). * * Methods are permitted to return plain values or Promises; `createRateLimiter` * awaits the result regardless, so an in-memory store stays on the fast path * while a remote store stays correct. */ export interface RateLimitStore { get(key: string): RateLimitEntry | undefined | Promise; set(key: string, entry: RateLimitEntry): void | Promise; delete(key: string): void | Promise; } /** * Public rate-limiter interface. `check` and `reset` may return synchronously * or asynchronously depending on the underlying store — callers should `await` * the result either way. */ export interface RateLimiter { check(identifier: string): RateLimitResult | Promise; /** * Hand back `amount` slots (default 1) that `check` took in the current * window — a success returning exactly what its own request cost. Clamps at * zero and does nothing once the window has rolled. On an atomic store that * means it can never buy budget that was not taken; on an async store it is * a read-modify-write like `check` and shares its interleaving caveat (see * `createRateLimiter`). */ refund(identifier: string, amount?: number): void | Promise; reset(identifier: string): void | Promise; } /** * Default in-memory rate-limit store. Uses a Map and periodically prunes * expired entries. Intended for single-process deployments; switch to a * persistent adapter for multi-instance setups. */ export declare function createInMemoryRateLimitStore(options?: { cleanupIntervalMs?: number; }): RateLimitStore; /** * Build a rate-limiter for the given config. Uses `config.store` when * provided (e.g. a Prisma- or Redis-backed implementation) and falls back * to the in-memory store for the single-process default. * * **Atomicity caveat:** `check()` and `refund()` are read-modify-writes (`get` * → increment / decrement → `set`). With the default single-process in-memory * store this is atomic — there is no await between read and write that another * request can interleave through. With an **async/remote** store (Redis, Prisma, * Upstash) the get and set are two round-trips, so concurrent requests can each * read the same count and under-count the limit by the in-flight concurrency — * and a `refund` racing a `check` can land a count of 1 where 2 were taken. For * a strict limit under multi-instance load, back the store with a server-side * atomic increment (e.g. Redis `INCR` + `EXPIRE`) and have `get`/`set` reflect * it, rather than relying on this read-modify-write. */ export declare function createRateLimiter(config: RateLimitConfig): RateLimiter; /** * Build a rate-limiter from an optional config slice, or `null` when the slice * is absent. Lets handlers write `enforceRateLimit(makeRateLimiter(cfg), key)` * without repeating the ternary. */ export declare function makeRateLimiter(config: RateLimitConfig | undefined): RateLimiter | null; /** * The rate-limiter for one endpoint key, shared by every handler factory built * for the same `jwt.secret` and the same resolved limit. Handlers use this * instead of `makeRateLimiter(config.rateLimit?.key)`: it applies the secure * default (see `rateLimitFor`) and it puts two factories reading one key on * one counter. */ export declare function sharedLimiter(config: AuthConfig, key: RateLimitKey): RateLimiter | null; /** * Enforce a rate limit at the top of a handler. Returns a ready-to-return 429 * `Response` (with a `Retry-After` header) when the limit is exceeded, or * `null` when the request may proceed (including when `limiter` is `null`, * i.e. limiting is disabled). Consolidates the identical 429 block that was * duplicated across login/register/forgot-password. */ export declare function enforceRateLimit(limiter: RateLimiter | null, key: string, message?: string): Promise;